fix(route/instagram): fix cookie route (#19517)
This commit is contained in:
parent
c81c64fafa
commit
fd8463ae4a
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
Loading…
Reference in New Issue