fix(route/instagram): fix cookie route (#19517)

This commit is contained in:
Tony 2025-07-02 16:05:32 +08:00 committed by GitHub
parent c81c64fafa
commit fd8463ae4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 76 deletions

View File

@ -3,7 +3,7 @@ import cache from '@/utils/cache';
import { CookieJar } from 'tough-cookie';
import { config } from '@/config';
import { renderItems } from '../common-utils';
import { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeedItems, getLoggedOutTagsFeedItems, renderGuestItems } from './utils';
import { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeed, renderGuestItems } from './utils';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import ConfigNotFoundError from '@/errors/types/config-not-found';
@ -35,7 +35,7 @@ You may need to setup cookie for a less restrictive rate limit and private profi
async function handler(ctx) {
// if (!config.instagram || !config.instagram.cookie) {
// throw Error('Instagram RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>');
// throw new ConfigNotFoundError('Instagram RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>');
// }
const availableCategories = ['user', 'tags'];
const { category, key } = ctx.req.param();
@ -45,7 +45,7 @@ async function handler(ctx) {
}
let cookieJar = await cache.get('instagram:cookieJar');
const wwwClaimV2 = await cache.get('instagram:wwwClaimV2');
// const wwwClaimV2 = await cache.get('instagram:wwwClaimV2');
const cacheMiss = !cookieJar;
if (cacheMiss) {
@ -59,7 +59,7 @@ async function handler(ctx) {
cookieJar = CookieJar.fromJSON(cookieJar);
}
if (!wwwClaimV2 && cookie && !(await checkLogin(cookieJar, cache))) {
if (/* !wwwClaimV2 &&*/ cookie && !(await checkLogin(cookieJar))) {
throw new ConfigNotFoundError('Invalid cookie');
}
@ -67,7 +67,7 @@ async function handler(ctx) {
let items;
switch (category) {
case 'user': {
const userInfo = await getUserInfo(key, cookieJar, cache);
const userInfo = await getUserInfo(key, cookieJar);
// User feed metadata
const biography = userInfo.biography;
@ -80,22 +80,26 @@ async function handler(ctx) {
feedLogo = userInfo.profile_pic_url_hd ?? userInfo.hd_profile_pic_url_info?.url ?? userInfo.profile_pic_url;
feedLink = `${baseUrl}/${username}`;
items = cookie ? await getUserFeedItems(id, username, cookieJar, cache) : [...userInfo.edge_felix_video_timeline.edges, ...userInfo.edge_owner_to_timeline_media.edges];
items = cookie ? await getUserFeedItems(id, username, cookieJar) : [...userInfo.edge_felix_video_timeline.edges, ...userInfo.edge_owner_to_timeline_media.edges];
break;
}
case 'tags': {
if (!config.instagram || !config.instagram.cookie) {
throw new ConfigNotFoundError('Instagram RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>');
}
const tag = key;
feedTitle = `#${tag} - Instagram`;
feedLink = `${baseUrl}/explore/tags/${tag}`;
feedLink = `${baseUrl}/explore/search/keyword/?q=%23${tag}`;
if (cookie) {
items = await getTagsFeedItems(tag, 'recent', cookieJar, cache.tryGet);
} else {
const hashTag = await getLoggedOutTagsFeedItems(tag, cookieJar);
items = [...hashTag.edge_hashtag_to_media.edges, ...hashTag.edge_hashtag_to_top_posts.edges];
}
const feedData = await getTagsFeed(tag, cookieJar);
feedLogo = feedData.profile_pic_url;
items = feedData.top.sections.flatMap((section) =>
// either media or clips
(section.feed_type === 'media' ? section.layout_content.medias : [...section.layout_content.one_by_two_item.clips.items, ...section.layout_content.fill_items]).map((item) => item.media)
);
break;
}

View File

@ -1,4 +1,5 @@
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
import { art } from '@/utils/render';
@ -6,7 +7,7 @@ import path from 'node:path';
import ConfigNotFoundError from '@/errors/types/config-not-found';
const baseUrl = 'https://www.instagram.com';
const COOKIE_URL = 'https://instagram.com';
const COOKIE_URL = baseUrl;
const getCSRFTokenFromJar = async (cookieJar) => {
const cookieString = await cookieJar.getCookieString(COOKIE_URL);
@ -14,45 +15,52 @@ const getCSRFTokenFromJar = async (cookieJar) => {
};
const getHeaders = async (cookieJar) => ({
'X-ASBD-ID': 198387,
'X-CSRFToken': await getCSRFTokenFromJar(cookieJar),
'X-IG-App-ID': 936_619_743_392_459,
'X-IG-WWW-Claim': undefined,
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'x-asbd-id': 359341,
'x-csrftoken': await getCSRFTokenFromJar(cookieJar),
'x-ig-app-id': 936_619_743_392_459,
'x-ig-www-claim': '0',
});
const checkLogin = async (cookieJar, cache) => {
const response = await got.post(`${baseUrl}/api/v1/web/fxcal/ig_sso_users/`, {
const checkLogin = async (cookieJar) => {
const response = await ofetch(`${baseUrl}/api/v1/web/fxcal/ig_sso_users/`, {
// cookieJar,
headers: {
cookie: await cookieJar.getCookieString(COOKIE_URL),
...(await getHeaders(cookieJar)),
'X-IG-WWW-Claim': '0',
'content-type': 'application/x-www-form-urlencoded',
cookie: (await cookieJar.getCookieString(COOKIE_URL)) as string,
...((await getHeaders(cookieJar)) as unknown as Record<string, string>),
// 'X-IG-WWW-Claim': '0',
},
method: 'POST',
});
const wwwClaimV2 = response.headers['x-ig-set-www-claim'];
// const wwwClaimV2 = response.headers['x-ig-set-www-claim'];
if (wwwClaimV2) {
cache.set('instagram:wwwClaimV2', wwwClaimV2);
}
// if (wwwClaimV2) {
// cache.set('instagram:wwwClaimV2', wwwClaimV2);
// }
return Boolean(response.data.status === 'ok');
return Boolean(response.status === 'ok');
};
const getUserInfo = async (username, cookieJar, cache) => {
const getUserInfo = async (username, cookieJar) => {
let webProfileInfo;
let id = await cache.get(`instagram:getIdByUsername:${username}`);
let userInfoCache = await cache.get(`instagram:userInfo:${id}`);
userInfoCache = userInfoCache && typeof userInfoCache === 'string' ? JSON.parse(userInfoCache) : userInfoCache;
if (!userInfoCache) {
try {
const response = await got(`${baseUrl}/api/v1/users/web_profile_info/`, {
cookieJar,
const response = await ofetch.raw(`${baseUrl}/api/v1/users/web_profile_info/`, {
// cookieJar,
headers: {
...(await getHeaders(cookieJar)),
'X-IG-WWW-Claim': (await cache.get('instagram:wwwClaimV2')) ?? undefined,
cookie: (await cookieJar.getCookieString(COOKIE_URL)) as string,
...((await getHeaders(cookieJar)) as unknown as Record<string, string>),
// 'X-IG-WWW-Claim': (await cache.get('instagram:wwwClaimV2')) ?? undefined,
},
searchParams: {
query: {
username,
},
});
@ -60,7 +68,7 @@ const getUserInfo = async (username, cookieJar, cache) => {
throw new ConfigNotFoundError('Invalid cookie');
}
webProfileInfo = response.data.data.user;
webProfileInfo = response._data.data.user;
id = webProfileInfo.id;
await cache.set(`instagram:getIdByUsername:${username}`, id, 31_536_000); // 1 year since it will never change
@ -73,24 +81,22 @@ const getUserInfo = async (username, cookieJar, cache) => {
}
}
userInfoCache = typeof userInfoCache === 'string' ? JSON.parse(userInfoCache) : userInfoCache;
return userInfoCache || webProfileInfo;
};
const getUserFeedItems = (id, username, cookieJar, cache) =>
const getUserFeedItems = (id, username, cookieJar) =>
cache.tryGet(
`instagram:feed:${id}`,
async () => {
const response = await got(`${baseUrl}/api/v1/feed/user/${username}/username/`, {
const response = await ofetch.raw(`${baseUrl}/api/v1/feed/user/${username}/username/`, {
// cookieJar,
headers: {
cookie: await cookieJar.getCookieString(COOKIE_URL),
...(await getHeaders(cookieJar)),
cookie: (await cookieJar.getCookieString(COOKIE_URL)) as string,
...((await getHeaders(cookieJar)) as unknown as Record<string, string>),
// 401 Unauthorized if cookie does not match with IP
'X-IG-WWW-Claim': await cache.get('instagram:wwwClaimV2'),
// 'X-IG-WWW-Claim': await cache.get('instagram:wwwClaimV2'),
},
searchParams: {
query: {
count: 30,
},
});
@ -99,56 +105,33 @@ const getUserFeedItems = (id, username, cookieJar, cache) =>
Please also check if your account is being blocked by Instagram.`);
}
return response.data.items;
return response._data.items;
},
config.cache.routeExpire,
false
);
const getTagsFeedItems = (tag, tab, cookieJar, tryGet) =>
tryGet(
const getTagsFeed = (tag, cookieJar) =>
cache.tryGet(
`instagram:tags:${tag}`,
async () => {
const response = await got(`${baseUrl}/api/v1/tags/web_info/`, {
const response = await ofetch(`${baseUrl}/api/v1/tags/web_info/`, {
// cookieJar, cookieJar is behaving weirdly here, so we use cookie header instead
headers: {
cookie: await cookieJar.getCookieString(COOKIE_URL),
...(await getHeaders(cookieJar)),
cookie: (await cookieJar.getCookieString(COOKIE_URL)) as string,
...((await getHeaders(cookieJar)) as unknown as Record<string, string>),
},
searchParams: {
query: {
tag_name: tag,
},
});
return response.data.data[tab].sections.flatMap((section) => section.layout_content.medias.map((media) => media.media));
return response.data;
},
config.cache.routeExpire,
false
);
const getLoggedOutTagsFeedItems = async (tag, cookieJar) => {
const response = await got(`${baseUrl}/api/v1/tags/logged_out_web_info/`, {
// cookieJar, cookieJar is behaving weirdly here, so we use cookie header instead
headers: {
cookie: await cookieJar.getCookieString(COOKIE_URL),
...(await getHeaders(cookieJar)),
'X-IG-WWW-Claim': '0',
},
searchParams: {
tag_name: tag,
},
});
const cookies = response.headers['set-cookie'];
if (cookies) {
for await (const cookie of cookies) {
await cookieJar.setCookie(cookie, COOKIE_URL);
}
}
return response.data.data.hashtag;
};
const renderGuestItems = (items) => {
const renderVideo = (node, summary) =>
art(path.join(__dirname, '../templates/video.art'), {
@ -212,4 +195,4 @@ const renderGuestItems = (items) => {
});
};
export { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeedItems, getLoggedOutTagsFeedItems, renderGuestItems };
export { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeed, renderGuestItems };