|
@@ -32,8 +32,6 @@ const parseLink = (link: string): { userName?: string, postUrlSegment?: string }
|
|
return;
|
|
return;
|
|
};
|
|
};
|
|
|
|
|
|
-const isValidUrlSegment = (input: string) => /^[A-Za-z0-9\-_]+$/.test(input);
|
|
|
|
-
|
|
|
|
const linkBuilder = (config: ReturnType<typeof parseLink>): string => {
|
|
const linkBuilder = (config: ReturnType<typeof parseLink>): string => {
|
|
if (config.userName) return `https://www.instagram.com/${config.userName}/`;
|
|
if (config.userName) return `https://www.instagram.com/${config.userName}/`;
|
|
if (config.postUrlSegment) return `https://www.instagram.com/p/${config.postUrlSegment}/`;
|
|
if (config.postUrlSegment) return `https://www.instagram.com/p/${config.postUrlSegment}/`;
|
|
@@ -46,7 +44,7 @@ const graphqlLinkBuilder = ({userId, first = '12', after}: {userId: string, firs
|
|
const urlSegmentToId = (urlSegment: string) => urlSegment.length <= 28 ?
|
|
const urlSegmentToId = (urlSegment: string) => urlSegment.length <= 28 ?
|
|
pubUrlSegmentToId(urlSegment) : pubUrlSegmentToId(urlSegment.slice(0, -28));
|
|
pubUrlSegmentToId(urlSegment) : pubUrlSegmentToId(urlSegment.slice(0, -28));
|
|
|
|
|
|
-export { graphqlLinkBuilder, linkBuilder, parseLink, isValidUrlSegment, idToUrlSegment, urlSegmentToId };
|
|
|
|
|
|
+export { graphqlLinkBuilder, linkBuilder, parseLink, idToUrlSegment, urlSegmentToId };
|
|
|
|
|
|
interface IWorkerOption {
|
|
interface IWorkerOption {
|
|
sessionLockfile: string;
|
|
sessionLockfile: string;
|
|
@@ -397,21 +395,21 @@ export default class {
|
|
return page.context().addCookies(this.webshotCookies)
|
|
return page.context().addCookies(this.webshotCookies)
|
|
.then(() => page.goto(url, {waitUntil: 'load', timeout: getTimeout()}))
|
|
.then(() => page.goto(url, {waitUntil: 'load', timeout: getTimeout()}))
|
|
.then(response => {
|
|
.then(response => {
|
|
|
|
+ const itemIds: string[] = [];
|
|
|
|
+ const redirectionHandler = () =>
|
|
|
|
+ acceptCookieConsent(page)
|
|
|
|
+ .then(() => browserLogin(page))
|
|
|
|
+ .catch((err: Error) => {
|
|
|
|
+ if (err.name === 'TimeoutError') {
|
|
|
|
+ logger.warn('navigation timed out, assuming login has failed');
|
|
|
|
+ isWaitingForLogin = false;
|
|
|
|
+ }
|
|
|
|
+ throw err;
|
|
|
|
+ })
|
|
|
|
+ .then(() => browserSaveCookies(page))
|
|
|
|
+ .then(() => page.goto(url, {waitUntil: 'load', timeout: getTimeout()}))
|
|
|
|
+ .then(responseHandler);
|
|
const responseHandler = (res: typeof response): ReturnType<typeof response.json> => {
|
|
const responseHandler = (res: typeof response): ReturnType<typeof response.json> => {
|
|
- if (res.status() === 302) {
|
|
|
|
- return acceptCookieConsent(page)
|
|
|
|
- .then(() => browserLogin(page))
|
|
|
|
- .catch((err: Error) => {
|
|
|
|
- if (err.name === 'TimeoutError') {
|
|
|
|
- logger.warn('navigation timed out, assuming login has failed');
|
|
|
|
- isWaitingForLogin = false;
|
|
|
|
- }
|
|
|
|
- throw err;
|
|
|
|
- })
|
|
|
|
- .then(() => browserSaveCookies(page))
|
|
|
|
- .then(() => page.goto(url, {waitUntil: 'load', timeout: getTimeout()}))
|
|
|
|
- .then(responseHandler);
|
|
|
|
- }
|
|
|
|
if (res.status() !== 200) {
|
|
if (res.status() !== 200) {
|
|
const err = new Error(
|
|
const err = new Error(
|
|
`error navigating to user page, error was: ${res.status()} ${res.statusText()}`
|
|
`error navigating to user page, error was: ${res.status()} ${res.statusText()}`
|
|
@@ -420,17 +418,26 @@ export default class {
|
|
value: 'ResponseError',
|
|
value: 'ResponseError',
|
|
});
|
|
});
|
|
}
|
|
}
|
|
- return res.json();
|
|
|
|
|
|
+ return res.json()
|
|
|
|
+ .catch(redirectionHandler)
|
|
|
|
+ .then((json: {[key: string]: {user: IgGraphQLUser}}) => {
|
|
|
|
+ if (!json || !(json.graphql || json.data)?.user) {
|
|
|
|
+ logger.warn('error parsing graphql response, returning empty object...');
|
|
|
|
+ const data = {user: {edge_owner_to_timeline_media: {edges: []}} as IgGraphQLUser};
|
|
|
|
+ return {graphql: data, data};
|
|
|
|
+ }
|
|
|
|
+ return json;
|
|
|
|
+ });
|
|
};
|
|
};
|
|
const jsonHandler = ({user}: {user: IgGraphQLUser}): string[] | Promise<string[]> => {
|
|
const jsonHandler = ({user}: {user: IgGraphQLUser}): string[] | Promise<string[]> => {
|
|
const pageInfo = user.edge_owner_to_timeline_media.page_info;
|
|
const pageInfo = user.edge_owner_to_timeline_media.page_info;
|
|
- const itemIds: string[] = [];
|
|
|
|
for (const {node} of user.edge_owner_to_timeline_media.edges) {
|
|
for (const {node} of user.edge_owner_to_timeline_media.edges) {
|
|
if (node.__typename === 'GraphVideo' && node.product_type === 'igtv') continue;
|
|
if (node.__typename === 'GraphVideo' && node.product_type === 'igtv') continue;
|
|
if (node.id && BigNumOps.compare(node.id, targetId) > 0) itemIds.push(node.id);
|
|
if (node.id && BigNumOps.compare(node.id, targetId) > 0) itemIds.push(node.id);
|
|
else return itemIds;
|
|
else return itemIds;
|
|
|
|
+ if (Number(targetId) < 1) return itemIds;
|
|
}
|
|
}
|
|
- if (!pageInfo.has_next_page) return itemIds;
|
|
|
|
|
|
+ if (!pageInfo?.has_next_page) return itemIds;
|
|
logger.info('unable to find a smaller id than target, trying on next page...');
|
|
logger.info('unable to find a smaller id than target, trying on next page...');
|
|
url = graphqlLinkBuilder({userId: user.id, after: pageInfo.end_cursor});
|
|
url = graphqlLinkBuilder({userId: user.id, after: pageInfo.end_cursor});
|
|
return page.goto(url, {waitUntil: 'load', timeout: getTimeout()})
|
|
return page.goto(url, {waitUntil: 'load', timeout: getTimeout()})
|
|
@@ -442,7 +449,7 @@ export default class {
|
|
}).catch((err: Error) => {
|
|
}).catch((err: Error) => {
|
|
if (err.name !== 'TimeoutError' && err.name !== 'ResponseError') throw err;
|
|
if (err.name !== 'TimeoutError' && err.name !== 'ResponseError') throw err;
|
|
if (err.name === 'ResponseError') {
|
|
if (err.name === 'ResponseError') {
|
|
- logger.warn(`error while fetching tweets for ${userName}: ${err.message}`);
|
|
|
|
|
|
+ logger.warn(`error while fetching posts by @${userName}: ${err.message}`);
|
|
} else logger.warn(`navigation timed out at ${getTimerTime()} ms`);
|
|
} else logger.warn(`navigation timed out at ${getTimerTime()} ms`);
|
|
return [] as string[];
|
|
return [] as string[];
|
|
}).then(itemIds => promisify(setTimeout)(getTimeout()).then(() =>
|
|
}).then(itemIds => promisify(setTimeout)(getTimeout()).then(() =>
|
|
@@ -554,7 +561,6 @@ export default class {
|
|
const updateOffset = () => currentThread.offset = topOfFeed;
|
|
const updateOffset = () => currentThread.offset = topOfFeed;
|
|
|
|
|
|
if (currentThread.offset === '-1') { updateOffset(); return; }
|
|
if (currentThread.offset === '-1') { updateOffset(); return; }
|
|
- if (currentThread.offset === '0') mediaItems.splice(1);
|
|
|
|
|
|
|
|
return this.workOnMedia(mediaItems, this.sendMedia(`thread ${currentFeed}`, ...currentThread.subscribers))
|
|
return this.workOnMedia(mediaItems, this.sendMedia(`thread ${currentFeed}`, ...currentThread.subscribers))
|
|
.then(updateDate).then(updateOffset);
|
|
.then(updateDate).then(updateOffset);
|