Browse Source

switch to query from own browser inspection

Mike L 3 years ago
parent
commit
1a77b63e2a
8 changed files with 56 additions and 55 deletions
  1. 10 10
      dist/command.js
  2. 6 6
      dist/koishi.js
  3. 1 1
      dist/loggers.js
  4. 6 6
      dist/main.js
  5. 16 17
      dist/twitter.js
  6. 1 1
      dist/utils.js
  7. 9 9
      dist/webshot.js
  8. 7 5
      src/twitter.ts

+ 10 - 10
dist/command.js

@@ -7,7 +7,7 @@ const datetime_1 = require("./datetime");
 const loggers_1 = require("./loggers");
 const twitter_1 = require("./twitter");
 const utils_1 = require("./utils");
-const logger = loggers_1.getLogger('command');
+const logger = (0, loggers_1.getLogger)('command');
 function parseCmd(message) {
     message = message.trim();
     message = message.replace('\\\\', '\\0x5c');
@@ -29,7 +29,7 @@ function parseCmd(message) {
 }
 exports.parseCmd = parseCmd;
 function linkFinder(userName, chat, lock) {
-    const normalizedLink = twitter_1.linkBuilder({ userName });
+    const normalizedLink = (0, twitter_1.linkBuilder)({ userName });
     const link = Object.keys(lock.threads).find(realLink => normalizedLink === realLink.replace(/\/@/, '/').toLowerCase());
     if (!link)
         return [null, -1];
@@ -43,7 +43,7 @@ function sub(chat, args, reply, lock, lockfile) {
     if (args.length === 0) {
         return reply('找不到要订阅的链接。');
     }
-    const matched = twitter_1.parseLink(args[0]);
+    const matched = (0, twitter_1.parseLink)(args[0]);
     if (!matched) {
         return reply(`订阅链接格式错误:
 示例:
@@ -81,7 +81,7 @@ https://www.instagram.com/tomoyo_kurosawa_/p/B6GHRSmgV-7/`);
         return false;
     };
     const newSub = (userName) => {
-        const link = twitter_1.linkBuilder(matched);
+        const link = (0, twitter_1.linkBuilder)(matched);
         let msg;
         if (offset !== '0') {
             msg = `已为此聊天订阅 ${link} 并回溯到 ${args[0].replace(/\?.*/, '')}(含)之后的第一条动态`;
@@ -89,8 +89,8 @@ https://www.instagram.com/tomoyo_kurosawa_/p/B6GHRSmgV-7/`);
         return subscribeTo(link, { id: Number(userName.split(':')[1]), msg });
     };
     if (matched.postUrlSegment) {
-        offset = utils_1.BigNumOps.plus(twitter_1.urlSegmentToId(matched.postUrlSegment), '-1');
-        twitter_1.getPostOwner(matched.postUrlSegment).then(userName => {
+        offset = utils_1.BigNumOps.plus((0, twitter_1.urlSegmentToId)(matched.postUrlSegment), '-1');
+        (0, twitter_1.getPostOwner)(matched.postUrlSegment).then(userName => {
             matched.userName = userName.split(':')[0];
             if (!tryFindSub(matched.userName))
                 newSub(userName);
@@ -131,7 +131,7 @@ function unsub(chat, args, reply, lock, lockfile) {
     if (args.length === 0) {
         return reply('找不到要退订的链接。');
     }
-    const match = (_a = twitter_1.parseLink(args[0])) === null || _a === void 0 ? void 0 : _a.userName;
+    const match = (_a = (0, twitter_1.parseLink)(args[0])) === null || _a === void 0 ? void 0 : _a.userName;
     if (!match) {
         return reply('链接格式有误。');
     }
@@ -153,7 +153,7 @@ function list(chat, _, reply, lock) {
     const links = [];
     Object.keys(lock.threads).forEach(key => {
         if (lock.threads[key].subscribers.find(({ chatID, chatType }) => chat.chatID === chatID && chat.chatType === chatType))
-            links.push(`${key} ${datetime_1.relativeDate(lock.threads[key].updatedAt)}`);
+            links.push(`${key} ${(0, datetime_1.relativeDate)(lock.threads[key].updatedAt)}`);
     });
     return reply('此聊天中订阅的 Instagram 动态链接:\n' + links.join('\n'));
 }
@@ -163,12 +163,12 @@ function view(chat, args, reply) {
     if (args.length === 0) {
         return reply('找不到要查看的链接。');
     }
-    const match = (_a = twitter_1.parseLink(args[0])) === null || _a === void 0 ? void 0 : _a.postUrlSegment;
+    const match = (_a = (0, twitter_1.parseLink)(args[0])) === null || _a === void 0 ? void 0 : _a.postUrlSegment;
     if (!match) {
         return reply('链接格式有误。');
     }
     try {
-        twitter_1.sendPost(match, chat);
+        (0, twitter_1.sendPost)(match, chat);
     }
     catch (e) {
         reply('机器人尚未加载完毕,请稍后重试。');

+ 6 - 6
dist/koishi.js

@@ -15,7 +15,7 @@ require("koishi-adapter-onebot");
 const command_1 = require("./command");
 const loggers_1 = require("./loggers");
 const utils_1 = require("./utils");
-const logger = loggers_1.getLogger('qqbot');
+const logger = (0, loggers_1.getLogger)('qqbot');
 const cqUrlFix = (factory) => (...args) => factory(...args).replace(/(?<=\[CQ:.*)url=(?=(base64|file|https?):\/\/)/, 'file=');
 exports.Message = {
     Image: cqUrlFix(koishi_1.segment.image),
@@ -51,7 +51,7 @@ class default_1 {
             var _a, _b;
             let wasEmpty = false;
             const queue = (_a = this.messageQueues)[_b = `${type}:${id}`] || (_a[_b] = (() => { wasEmpty = true; return []; })());
-            queue.push(() => koishi_1.sleep(200).then(resolver));
+            queue.push(() => (0, koishi_1.sleep)(200).then(resolver));
             logger.debug(`no. of message currently queued for ${type}:${id}: ${queue.length}`);
             if (wasEmpty)
                 this.next(type, id);
@@ -139,7 +139,7 @@ class default_1 {
                             .then(() => { logger.info(`accepted friend request from ${userString} (from group ${groupString})`); })
                             .catch(error => { logger.error(`error accepting friend request from ${userString}, error: ${error}`); });
                     }
-                    utils_1.chainPromises(groupList.map(groupItem => (done) => Promise.resolve(done ||
+                    (0, utils_1.chainPromises)(groupList.map(groupItem => (done) => Promise.resolve(done ||
                         this.bot.getGroupMember(groupItem.groupId, session.userId).then(() => {
                             groupString = `${groupItem.groupName}(${groupItem.groupId})`;
                             return session.bot.handleFriendRequest(session.messageId, true)
@@ -170,7 +170,7 @@ class default_1 {
             }));
             this.app.middleware((session) => __awaiter(this, void 0, void 0, function* () {
                 const chat = yield this.getChat(session);
-                const cmdObj = command_1.parseCmd(session.content);
+                const cmdObj = (0, command_1.parseCmd)(session.content);
                 const reply = (msg) => __awaiter(this, void 0, void 0, function* () {
                     const userString = `${session.username}(${session.userId})`;
                     return (chat.chatType === "group" ? this.sendToGroup : this.sendToUser)(chat.chatID.toString(), msg)
@@ -179,7 +179,7 @@ class default_1 {
                 switch (cmdObj.cmd) {
                     case 'instagram_view':
                     case 'instagram_get':
-                        command_1.view(chat, cmdObj.args, reply);
+                        (0, command_1.view)(chat, cmdObj.args, reply);
                         break;
                     case 'instagram_sub':
                     case 'instagram_subscribe':
@@ -217,7 +217,7 @@ ${chat.chatType === "temp" ?
             }
             catch (err) {
                 logger.error(`error connecting to bot provider at ${this.app.options.server}, will retry in 2.5s...`);
-                yield koishi_1.sleep(2500);
+                yield (0, koishi_1.sleep)(2500);
                 yield this.listen('retry connecting...');
             }
         });

+ 1 - 1
dist/loggers.js

@@ -4,7 +4,7 @@ exports.setLogLevels = exports.getLogger = void 0;
 const log4js_1 = require("log4js");
 const loggers = [];
 function getLogger(category) {
-    const l = log4js_1.getLogger(category);
+    const l = (0, log4js_1.getLogger)(category);
     l.level = 'info';
     loggers.push(l);
     return l;

+ 6 - 6
dist/main.js

@@ -9,7 +9,7 @@ const command_1 = require("./command");
 const loggers_1 = require("./loggers");
 const koishi_1 = require("./koishi");
 const twitter_1 = require("./twitter");
-const logger = loggers_1.getLogger();
+const logger = (0, loggers_1.getLogger)();
 const sections = [
     {
         header: 'GoCQHTTP Instagram Bot',
@@ -78,7 +78,7 @@ if (!config[k] || config[k] < 2048 || config[k] > 65536) {
     logger.warn(`invalid value of config.${k}, use ${exampleConfig[k]} as default`);
     config[k] = exampleConfig[k];
 }
-loggers_1.setLogLevels(config.loglevel);
+(0, loggers_1.setLogLevels)(config.loglevel);
 let lock;
 if (fs.existsSync(path.resolve(config.lockfile))) {
     try {
@@ -123,10 +123,10 @@ const qq = new koishi_1.default({
     host: config.cq_ws_host,
     port: config.cq_ws_port,
     bot_id: config.cq_bot_qq,
-    list: (c, a, cb) => command_1.list(c, a, cb, lock),
-    sub: (c, a, cb) => command_1.sub(c, a, cb, lock, config.lockfile),
-    unsub: (c, a, cb) => command_1.unsub(c, a, cb, lock, config.lockfile),
-    unsubAll: (c, a, cb) => command_1.unsubAll(c, a, cb, lock, config.lockfile),
+    list: (c, a, cb) => (0, command_1.list)(c, a, cb, lock),
+    sub: (c, a, cb) => (0, command_1.sub)(c, a, cb, lock, config.lockfile),
+    unsub: (c, a, cb) => (0, command_1.unsub)(c, a, cb, lock, config.lockfile),
+    unsubAll: (c, a, cb) => (0, command_1.unsubAll)(c, a, cb, lock, config.lockfile),
 });
 const worker = new twitter_1.default({
     sessionLockfile: config.ig_session_lockfile,

+ 16 - 17
dist/twitter.js

@@ -24,7 +24,7 @@ const loggers_1 = require("./loggers");
 const utils_1 = require("./utils");
 const webshot_1 = require("./webshot");
 const parseLink = (link) => {
-    let match = /instagram\.com\/(?:[^\/?#]+\/)?p\/([A-Za-z0-9\-_]+)/.exec(link);
+    let match = /instagram\.com\/(?:[^\/?#]+\/)?(?:p|tv)\/([A-Za-z0-9\-_]+)/.exec(link);
     if (match)
         return { postUrlSegment: match[1] };
     match =
@@ -42,11 +42,10 @@ const linkBuilder = (config) => {
         return `https://www.instagram.com/p/${config.postUrlSegment}/`;
 };
 exports.linkBuilder = linkBuilder;
-const graphqlLinkBuilder = ({ userId, first = '12', after }) => `https://www.instagram.com/graphql/query/\
-?query_id=17888483320059182&id=${userId}&first=${first}${after ? `&after=${after}` : ''}`;
+const graphqlLinkBuilder = ({ userId: id, first = '12', after }) => `https://www.instagram.com/graphql/query/?query_hash=8c2a529969ee035a5063f2fc8602a0fd&variables=${JSON.stringify({ id, first, after })}`;
 exports.graphqlLinkBuilder = graphqlLinkBuilder;
 const urlSegmentToId = (urlSegment) => urlSegment.length <= 28 ?
-    instagram_id_to_url_segment_1.urlSegmentToInstagramId(urlSegment) : instagram_id_to_url_segment_1.urlSegmentToInstagramId(urlSegment.slice(0, -28));
+    (0, instagram_id_to_url_segment_1.urlSegmentToInstagramId)(urlSegment) : (0, instagram_id_to_url_segment_1.urlSegmentToInstagramId)(urlSegment.slice(0, -28));
 exports.urlSegmentToId = urlSegmentToId;
 class SessionManager {
     constructor(client, file, credentials, codeServicePort) {
@@ -72,7 +71,7 @@ class SessionManager {
                     logger.warn('attempting to retry after 1 minute...');
                     if (fs.existsSync(filePath))
                         fs.unlinkSync(filePath);
-                    util_1.promisify(setTimeout)(60000).then(this.init);
+                    (0, util_1.promisify)(setTimeout)(60000).then(this.init);
                 });
             }
         };
@@ -82,7 +81,7 @@ class SessionManager {
             logger.info(`/confirm-2fa?code=<the code you received>&token=${token}`);
             let working;
             const server = http.createServer((req, res) => {
-                const { pathname, query } = url_1.parse(req.url, true);
+                const { pathname, query } = (0, url_1.parse)(req.url, true);
                 if (!working && pathname === '/confirm-2fa' && query.token === token &&
                     typeof (query.code) === 'string' && /^\d{6}$/.test(query.code)) {
                     const code = query.code;
@@ -165,7 +164,7 @@ let sendPost = (segmentId, receiver) => {
     throw Error();
 };
 exports.sendPost = sendPost;
-const logger = loggers_1.getLogger('instagram');
+const logger = (0, loggers_1.getLogger)('instagram');
 const maxTrials = 3;
 const retryInterval = 1500;
 const ordinal = (n) => {
@@ -226,7 +225,7 @@ class default_1 {
                                 .then(responseHandler);
                             const responseHandler = (res) => {
                                 if (res.status() !== 200) {
-                                    throw utils_1.customError('ResponseError')(`error navigating to user page, error was: ${res.status()} ${res.statusText()}`);
+                                    throw (0, utils_1.customError)('ResponseError')(`error navigating to user page, error was: ${res.status()} ${res.statusText()}`);
                                 }
                                 return res.json()
                                     .catch(redirectionHandler)
@@ -235,7 +234,7 @@ class default_1 {
                                     if (!json || !((_a = (json.graphql || json.data)) === null || _a === void 0 ? void 0 : _a.user)) {
                                         logger.warn('error parsing graphql response, returning empty object...');
                                         const data = { user: { edge_owner_to_timeline_media: { edges: [] } } };
-                                        return { graphql: data, data };
+                                        return { data };
                                     }
                                     return json;
                                 });
@@ -258,7 +257,7 @@ class default_1 {
                                 url = graphqlLinkBuilder({ userId, after: pageInfo.end_cursor });
                                 const nextPageDelay = this.webshotDelay * (0.4 + Math.random() * 0.1);
                                 timeout += nextPageDelay;
-                                return util_1.promisify(setTimeout)(nextPageDelay)
+                                return (0, util_1.promisify)(setTimeout)(nextPageDelay)
                                     .then(() => page.goto(url, { waitUntil: 'load', timeout: getTimeout() }))
                                     .then(responseHandler)
                                     .then(({ data }) => jsonHandler(data));
@@ -275,7 +274,7 @@ class default_1 {
                             else
                                 throw err;
                             return [];
-                        }).then(itemIds => util_1.promisify(setTimeout)(getTimeout()).then(() => itemIds.map(id => this.lazyGetMediaById(id))));
+                        }).then(itemIds => (0, util_1.promisify)(setTimeout)(getTimeout()).then(() => itemIds.map(id => this.lazyGetMediaById(id))));
                     })).finally(() => { page.close(); });
                 });
                 setTimeout(this.work, this.workInterval * 1000 / this.lock.feed.length);
@@ -364,9 +363,9 @@ class default_1 {
                 }
             });
             const queuedFeeds = lock.feed.slice(0, (lock.workon + 1) || undefined).reverse();
-            utils_1.chainPromises(utils_1.Arr.chunk(queuedFeeds, 5).map((arr, i) => () => Promise.all(arr.map((currentFeed, j) => {
+            (0, utils_1.chainPromises)(utils_1.Arr.chunk(queuedFeeds, 5).map((arr, i) => () => Promise.all(arr.map((currentFeed, j) => {
                 const promiseDelay = this.workInterval * (Math.random() + j + 10 - arr.length) * 125 / lock.feed.length;
-                const wait = (ms) => isWaitingForLogin ? utils_1.neverResolves() : util_1.promisify(setTimeout)(ms);
+                const wait = (ms) => isWaitingForLogin ? (0, utils_1.neverResolves)() : (0, util_1.promisify)(setTimeout)(ms);
                 const startTime = new Date().getTime();
                 const getTimerTime = () => new Date().getTime() - startTime;
                 const workon = (queuedFeeds.length - 1) - (i * 5 + j);
@@ -375,7 +374,7 @@ class default_1 {
                     logger.info(`skipped feed #${workon}: ${currentFeed}, last updated within an hour`);
                     return wait(promiseDelay * 3);
                 }
-                return util_1.promisify(setTimeout)(promiseDelay * 3).then(() => {
+                return (0, util_1.promisify)(setTimeout)(promiseDelay * 3).then(() => {
                     logger.info(`about to pull from feed #${workon}: ${currentFeed}`);
                     if (j === arr.length - 1)
                         logger.info(`timeout for this batch job: ${Math.trunc(promiseDelay)} ms`);
@@ -433,7 +432,7 @@ class default_1 {
             if (isWaitingForLogin !== true)
                 return;
             logger.warn('still waiting for login, pausing execution...');
-            return utils_1.neverResolves();
+            return (0, utils_1.neverResolves)();
         })
             .then(() => { isWaitingForLogin = true; logger.warn('blocked by login dialog, trying to log in manually...'); })
             .then(() => page.fill('input[name="password"]', opt.credentials[1], { timeout: 0 }))
@@ -481,7 +480,7 @@ class default_1 {
                 const lock = this.lock;
                 const feed = linkBuilder({ userName: mediaItem.user.username });
                 if (lock.feed.includes(feed) && lock.threads[feed].offset < mediaItem.pk) {
-                    logger.info(`post is newer than last offset of thread (${instagram_id_to_url_segment_1.instagramIdToUrlSegment(lock.threads[feed].offset)}), updating...`);
+                    logger.info(`post is newer than last offset of thread (${(0, instagram_id_to_url_segment_1.instagramIdToUrlSegment)(lock.threads[feed].offset)}), updating...`);
                     this.workOnFeed(feed);
                     if (lock.threads[feed].subscribers.some(subscriber => subscriber.chatID.toString() === receiver.chatID.toString() &&
                         subscriber.chatType === receiver.chatType))
@@ -494,7 +493,7 @@ class default_1 {
                 if (err instanceof instagram_private_api_1.IgLoginRequiredError || err instanceof instagram_private_api_1.IgCookieNotFoundError) {
                     logger.warn('login required, awaiting login...');
                     this.bot.sendTo(receiver, '等待登录中,稍后会处理请求,请稍候……');
-                    return this.session.login().then(() => exports.sendPost(segmentId, receiver));
+                    return this.session.login().then(() => (0, exports.sendPost)(segmentId, receiver));
                 }
                 ;
             });

+ 1 - 1
dist/utils.js

@@ -33,7 +33,7 @@ const chunkArray = (arr, size) => {
 };
 const rawRegExp = (...args) => RegExp(String.raw(...args));
 exports.rawRegExp = rawRegExp;
-const splitBigNumAt = (num, at) => num.replace(exports.rawRegExp `^([+-]?)(\d+)(\d{${at}})$`, '$1$2,$1$3')
+const splitBigNumAt = (num, at) => num.replace((0, exports.rawRegExp) `^([+-]?)(\d+)(\d{${at}})$`, '$1$2,$1$3')
     .replace(/^([^,]*)$/, '0,$1').split(',')
     .map(Number);
 const bigNumPlus = (num1, num2) => {

+ 9 - 9
dist/webshot.js

@@ -25,7 +25,7 @@ const typeInZH = {
     photo: ZHType('图片'),
     video: ZHType('视频'),
 };
-const logger = loggers_1.getLogger('webshot');
+const logger = (0, loggers_1.getLogger)('webshot');
 class Webshot extends CallableInstance {
     constructor(wsUrl, mode, getCookies, onready) {
         super('webshot');
@@ -46,7 +46,7 @@ class Webshot extends CallableInstance {
         this.reconnect = (error, onready) => {
             logger.error(`connection error, reason: ${error}`);
             logger.warn('trying to reconnect in 2.5s...');
-            return util_1.promisify(setTimeout)(2500)
+            return (0, util_1.promisify)(setTimeout)(2500)
                 .then(() => this.connect(onready));
         };
         this.performOnNewPage = (action, zoomFactor = 2, reconnectOnError = true) => this.browser.newPage({
@@ -89,7 +89,7 @@ class Webshot extends CallableInstance {
                             .then(() => page.goto(url, { waitUntil: 'load', timeout: getTimeout() }))
                             .then(next),
                         next(),
-                    ]))(() => util_1.promisify(setTimeout)(2000).then(() => page.waitForSelector('article', { timeout: getTimeout() }))))
+                    ]))(() => (0, util_1.promisify)(setTimeout)(2000).then(() => page.waitForSelector('article', { timeout: getTimeout() }))))
                         .catch((err) => {
                         if (err.name !== 'TimeoutError')
                             throw err;
@@ -118,8 +118,8 @@ class Webshot extends CallableInstance {
                                 element.style.display = 'none';
                         }
                     }))
-                        .then(() => utils_1.chainPromises(morePostProcessings.map(func => () => func(page))))
-                        .then(() => util_1.promisify(setTimeout)(getTimeout()))
+                        .then(() => (0, utils_1.chainPromises)(morePostProcessings.map(func => () => func(page))))
+                        .then(() => (0, util_1.promisify)(setTimeout)(getTimeout()))
                         .then(() => page.screenshot())
                         .then(screenshot => {
                         new pngjs_1.PNG({
@@ -178,7 +178,7 @@ class Webshot extends CallableInstance {
         };
         this.fetchMedia = (url) => new Promise((resolve, reject) => {
             logger.info(`fetching ${url}`);
-            axios_1.default({
+            (0, axios_1.default)({
                 method: 'get',
                 url,
                 responseType: 'arraybuffer',
@@ -198,7 +198,7 @@ class Webshot extends CallableInstance {
             });
         }).then(data => (ext => {
             const mediaTempFilePath = temp.path({ suffix: `.${ext}` });
-            fs_1.writeFileSync(mediaTempFilePath, Buffer.from(data));
+            (0, fs_1.writeFileSync)(mediaTempFilePath, Buffer.from(data));
             const path = `file://${mediaTempFilePath}`;
             switch (ext) {
                 case 'jpg':
@@ -234,7 +234,7 @@ class Webshot extends CallableInstance {
             if (this.mode > 0)
                 messageChain += (author + xmlEntities.decode(text));
             if (this.mode === 0) {
-                const url = twitter_1.linkBuilder({ postUrlSegment: item.code });
+                const url = (0, twitter_1.linkBuilder)({ postUrlSegment: item.code });
                 promise = promise.then(() => this.renderWebshot(url, 3840, webshotDelay, page => page.addStyleTag({ content: 'header>div>div+div{font-size:12px; line-height:15px; padding-top:0!important}' +
                         `header>div>div+div::before{content:"${item.user.full_name}"; color:#8e8e8e; font-weight:bold}`,
                 })))
@@ -264,7 +264,7 @@ class Webshot extends CallableInstance {
             if (1 - this.mode % 2)
                 promise = promise.then(() => {
                     if (item.carousel_media) {
-                        return utils_1.chainPromises(item.carousel_media.map(carouselItem => () => fetchBestCandidate(carouselItem.video_versions ||
+                        return (0, utils_1.chainPromises)(item.carousel_media.map(carouselItem => () => fetchBestCandidate(carouselItem.video_versions ||
                             carouselItem.image_versions2.candidates, type(carouselItem))));
                     }
                     else if (item.video_versions) {

+ 7 - 5
src/twitter.ts

@@ -23,7 +23,7 @@ import Webshot, { Cookies, Page } from './webshot';
 
 const parseLink = (link: string): { userName?: string, postUrlSegment?: string } => {
   let match =
-    /instagram\.com\/(?:[^\/?#]+\/)?p\/([A-Za-z0-9\-_]+)/.exec(link);
+    /instagram\.com\/(?:[^\/?#]+\/)?(?:p|tv)\/([A-Za-z0-9\-_]+)/.exec(link);
   if (match) return { postUrlSegment: match[1] };
   match =
     /instagram\.com\/([^\/?#]+)/.exec(link) ||
@@ -37,9 +37,11 @@ const linkBuilder = (config: ReturnType<typeof parseLink>): string => {
   if (config.postUrlSegment) return `https://www.instagram.com/p/${config.postUrlSegment}/`;
 };
 
-const graphqlLinkBuilder = ({userId, first = '12', after}: {userId: string, first?: string, after?: string}) =>
-  `https://www.instagram.com/graphql/query/\
-?query_id=17888483320059182&id=${userId}&first=${first}${after ? `&after=${after}` : ''}`;
+const graphqlLinkBuilder = (
+  {userId: id, first = '12', after}: {userId: string, first?: string, after?: string}
+) => `https://www.instagram.com/graphql/query/?query_hash=8c2a529969ee035a5063f2fc8602a0fd&variables=${
+  JSON.stringify({id, first, after})
+}`;
 
 const urlSegmentToId = (urlSegment: string) => urlSegment.length <= 28 ?
   pubUrlSegmentToId(urlSegment) : pubUrlSegmentToId(urlSegment.slice(0, -28));
@@ -446,7 +448,7 @@ export default class {
                       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 {data};
                       }
                       return json;
                     });