diff --git a/lib/routes/instagram/web-api/index.ts b/lib/routes/instagram/web-api/index.ts
index f390861868..cf80edef45 100644
--- a/lib/routes/instagram/web-api/index.ts
+++ b/lib/routes/instagram/web-api/index.ts
@@ -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 relevant config');
+ // throw new ConfigNotFoundError('Instagram RSS is disabled due to the lack of relevant config');
// }
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 relevant config');
+ }
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;
}
diff --git a/lib/routes/instagram/web-api/utils.ts b/lib/routes/instagram/web-api/utils.ts
index cae877ac47..062025ff7d 100644
--- a/lib/routes/instagram/web-api/utils.ts
+++ b/lib/routes/instagram/web-api/utils.ts
@@ -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),
+ // '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),
+ // '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),
// 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),
},
- 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 };