Browse Source

support viewing exact story id

Mike L 3 years ago
parent
commit
29b7f7b76e
5 changed files with 141 additions and 69 deletions
  1. 14 9
      dist/command.js
  2. 56 28
      dist/twitter.js
  3. 0 1
      package.json
  4. 14 8
      src/command.ts
  5. 57 23
      src/twitter.ts

+ 14 - 9
dist/command.js

@@ -126,14 +126,24 @@ function list(chat, _, reply, lock) {
 }
 exports.list = list;
 function view(chat, args, reply) {
-    var _a;
+    const promptOnError = (func) => (...args) => {
+        try {
+            func(...args);
+        }
+        catch (e) {
+            reply('机器人尚未加载完毕,请稍后重试。');
+        }
+    };
     if (args.length === 0) {
         return reply('找不到要查看 Instagram 限时动态的链接。');
     }
-    const match = (_a = twitter_1.parseLink(args[0])) === null || _a === void 0 ? void 0 : _a.userName;
-    if (!match) {
+    const match = twitter_1.parseLink(args[0]);
+    if (!(match === null || match === void 0 ? void 0 : match.userName)) {
         return reply('链接格式有误。');
     }
+    if (match === null || match === void 0 ? void 0 : match.storyId) {
+        return promptOnError(twitter_1.sendStory)(match.userName, match.storyId, chat);
+    }
     const conf = {};
     const confZH = {
         count: '最大查看数量',
@@ -150,11 +160,6 @@ function view(chat, args, reply) {
             return reply(`${confZH[optKey]}参数值不可为空。`);
         conf[optKey] = Number(optMatch[2]);
     }
-    try {
-        twitter_1.sendAllStories(match, chat, conf.skip, conf.count);
-    }
-    catch (e) {
-        reply('机器人尚未加载完毕,请稍后重试。');
-    }
+    promptOnError(twitter_1.sendAllStories)(match.userName, chat, conf.skip, conf.count);
 }
 exports.view = view;

+ 56 - 28
dist/twitter.js

@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
     });
 };
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.sendAllStories = exports.ScreenNameNormalizer = exports.SessionManager = exports.parseLink = exports.linkBuilder = void 0;
+exports.sendAllStories = exports.sendStory = exports.ScreenNameNormalizer = exports.SessionManager = exports.parseLink = exports.linkBuilder = void 0;
 const crypto = require("crypto");
 const fs = require("fs");
 const http = require("http");
@@ -140,7 +140,11 @@ class ScreenNameNormalizer {
 }
 exports.ScreenNameNormalizer = ScreenNameNormalizer;
 ScreenNameNormalizer.normalize = (username) => `${username.toLowerCase().replace(/^@/, '')}:`;
-let sendAllStories = (segmentId, receiver, startIndex, count) => {
+let sendStory = (username, storyId, receiver) => {
+    throw Error();
+};
+exports.sendStory = sendStory;
+let sendAllStories = (username, receiver, startIndex, count) => {
     throw Error();
 };
 exports.sendAllStories = sendAllStories;
@@ -381,14 +385,9 @@ class default_1 {
         this.webshotDelay = opt.webshotDelay;
         this.mode = opt.mode;
         this.wsUrl = opt.wsUrl;
-        ScreenNameNormalizer._queryUser = this.queryUser;
-        exports.sendAllStories = (rawUserName, receiver, startIndex = 0, count = 10) => {
-            if (startIndex < 0)
-                return this.bot.sendTo(receiver, '跳过数量参数值应为非负整数。');
-            if (count < 1)
-                return this.bot.sendTo(receiver, '最大查看数量参数值应为正整数。');
-            const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
-            this.queryUser(rawUserName)
+        const workNow = (config) => {
+            const { action, retryAction, reply, rawUserName } = config;
+            return this.queryUser(rawUserName)
                 .then(userNameId => {
                 var _a, _b;
                 const [userName, userId] = userNameId.split(':');
@@ -401,41 +400,70 @@ class default_1 {
                     .filter(item => !(item.pk in this.cache[userName].stories))
                     .map(item => this.webshot([Object.assign(Object.assign({}, item), { user: this.cache[userName].user })], (msgs, text, author) => this.cache[userName].stories[item.pk] = { pk: item.pk, msgs, text, author, original: item }, this.webshotDelay))).then(() => userName).finally(() => this.cache[userName].updated = new Date()));
             })
-                .then(userName => {
-                const storyItems = Object.values(this.cache[userName].stories)
-                    .sort((i1, i2) => -utils_1.BigNumOps.compare(i2.pk, i1.pk));
-                if (storyItems.length === 0)
-                    return this.bot.sendTo(receiver, `当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
-                if (startIndex + 1 > storyItems.length)
-                    return this.bot.sendTo(receiver, '跳过数量到达或超过当前用户可用的限时动态数量。');
-                const endIndex = Math.min(storyItems.length, startIndex + count);
-                const sendRangeText = `${startIndex + 1}${endIndex - startIndex > 1 ? `-${endIndex}` : ''}`;
-                return this.workOnMedia(storyItems.slice(startIndex, endIndex), sender)
-                    .then(() => this.bot.sendTo(receiver, `已显示当前用户 ${storyItems.length} 条可用限时动态中的第 ${sendRangeText} 条。`));
-            })
+                .then(action)
                 .catch((error) => {
                 if (error instanceof instagram_private_api_1.IgExactUserNotFoundError) {
-                    this.bot.sendTo(receiver, `找不到用户 ${rawUserName.replace(/^@?(.*)$/, '@$1')}。`);
+                    reply(`找不到用户 ${rawUserName.replace(/^@?(.*)$/, '@$1')}。`);
                 }
                 if (error instanceof instagram_private_api_1.IgNetworkError) {
                     if (error.cause.message === "Unexpected '<'") {
                         logger.warn('login required, logging in again...');
-                        return this.session.login().then(() => exports.sendAllStories(rawUserName, receiver, startIndex, count));
+                        return this.session.login().then(retryAction);
                     }
                     logger.warn(`error while fetching stories for ${rawUserName}: ${JSON.stringify(error.cause)}`);
-                    this.bot.sendTo(receiver, `获取 Stories 时出现错误:原因: ${error.cause}`);
+                    reply(`获取 Stories 时出现错误:原因: ${error.cause}`);
                 }
                 else if (error instanceof instagram_private_api_1.IgLoginRequiredError || error instanceof instagram_private_api_1.IgCookieNotFoundError) {
                     logger.warn('login required, logging in again...');
-                    this.bot.sendTo(receiver, '等待登陆中,稍后会处理请求,请稍候……');
-                    this.session.login().then(() => exports.sendAllStories(rawUserName, receiver, startIndex, count));
+                    reply('等待登陆中,稍后会处理请求,请稍候……');
+                    this.session.login().then(retryAction);
                 }
                 else {
                     logger.error(`unhandled error while fetching stories for ${rawUserName}: ${error}`);
-                    this.bot.sendTo(receiver, `获取 Stories 时发生未知错误: ${error}`);
+                    reply(`获取 Stories 时发生未知错误: ${error}`);
                 }
             });
         };
+        ScreenNameNormalizer._queryUser = this.queryUser;
+        exports.sendStory = (rawUserName, storyId, receiver) => {
+            const reply = msg => this.bot.sendTo(receiver, msg);
+            const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
+            workNow({
+                rawUserName,
+                action: userName => {
+                    if (!(storyId in this.cache[userName].stories))
+                        return reply('此动态不存在或已过期。');
+                    return this.workOnMedia([this.cache[userName].stories[storyId]], sender);
+                },
+                reply,
+                retryAction: () => exports.sendStory(rawUserName, storyId, receiver),
+            });
+        };
+        exports.sendAllStories = (rawUserName, receiver, startIndex = 0, count = 10) => {
+            const reply = msg => this.bot.sendTo(receiver, msg);
+            if (startIndex < 0)
+                return reply('跳过数量参数值应为非负整数。');
+            if (count < 1)
+                return reply('最大查看数量参数值应为正整数。');
+            const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
+            workNow({
+                rawUserName,
+                action: userName => {
+                    const storyItems = Object.values(this.cache[userName].stories)
+                        .sort((i1, i2) => -utils_1.BigNumOps.compare(i2.pk, i1.pk));
+                    if (storyItems.length === 0)
+                        return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
+                    if (startIndex + 1 > storyItems.length)
+                        return reply('跳过数量到达或超过当前用户可用的限时动态数量。');
+                    const endIndex = Math.min(storyItems.length, startIndex + count);
+                    const sendRangeText = `${startIndex + 1}${endIndex - startIndex > 1 ? `-${endIndex}` : ''}`;
+                    return this.workOnMedia(storyItems.slice(startIndex, endIndex), sender)
+                        .then(() => reply(`已显示当前用户 ${storyItems.length} 条可用限时动态中的第 ${sendRangeText} 条。`));
+                },
+                reply,
+                retryAction: () => exports.sendAllStories(rawUserName, receiver, startIndex, count)
+            });
+        };
     }
     get pullOrders() {
         const arr = [];

+ 0 - 1
package.json

@@ -33,7 +33,6 @@
     "callable-instance": "^2.0.0",
     "command-line-usage": "^5.0.5",
     "html-entities": "^1.3.1",
-    "instagram-id-to-url-segment": "github:CL-Jeremy/instagram-id-to-url-segment#built",
     "instagram-private-api": "^1.44.1",
     "koishi": "^3.10.0",
     "koishi-adapter-onebot": "^3.0.8",

+ 14 - 8
src/command.ts

@@ -7,7 +7,7 @@ import * as path from 'path';
 import { relativeDate } from './datetime';
 import { getLogger } from './loggers';
 import {
-  sendAllStories, ScreenNameNormalizer as normalizer,
+  sendStory, sendAllStories, ScreenNameNormalizer as normalizer,
   linkBuilder, parseLink
 } from './twitter';
 
@@ -134,13 +134,23 @@ function list(chat: IChat, _: string[], reply: (msg: string) => any, lock: ILock
 }
 
 function view(chat: IChat, args: string[], reply: (msg: string) => any): void {
+  const promptOnError = <T>(func: (...args: T[]) => void) => (...args: T[]): void => {
+    try {
+      func(...args);
+    } catch (e) {
+      reply('机器人尚未加载完毕,请稍后重试。');
+    }
+  }
   if (args.length === 0) {
     return reply('找不到要查看 Instagram 限时动态的链接。');
   }
-  const match = parseLink(args[0])?.userName;
-  if (!match) {
+  const match = parseLink(args[0]);
+  if (!match?.userName) {
     return reply('链接格式有误。');
   }
+  if (match?.storyId) {
+    return promptOnError(sendStory)(match.userName, match.storyId, chat);
+  }
   const conf: {
     skip?: number,
     count?: number,
@@ -157,11 +167,7 @@ function view(chat: IChat, args: string[], reply: (msg: string) => any): void {
     if (optMatch[2] === '') return reply(`${confZH[optKey]}参数值不可为空。`);
     conf[optKey] = Number(optMatch[2]);
   }
-  try {
-    sendAllStories(match, chat, conf.skip, conf.count);
-  } catch (e) {
-    reply('机器人尚未加载完毕,请稍后重试。');
-  }
+  promptOnError(sendAllStories)(match.userName, chat, conf.skip, conf.count);
 }
 
 export { parseCmd, sub, list, unsub, view };

+ 57 - 23
src/twitter.ts

@@ -16,6 +16,7 @@ import { SocksProxyAgent } from 'socks-proxy-agent';
 import { getLogger } from './loggers';
 import QQBot from './koishi';
 import { Arr, BigNumOps, chainPromises } from './utils';
+
 import Webshot from './webshot';
 
 const parseLink = (link: string): {userName?: string, storyId?: string} => {
@@ -164,7 +165,11 @@ export class ScreenNameNormalizer {
   }
 }
 
-export let sendAllStories = (segmentId: string, receiver: IChat, startIndex: number, count: number): void => {
+export let sendStory = (username: string, storyId: string, receiver: IChat): void => {
+  throw Error();
+}
+
+export let sendAllStories = (username: string, receiver: IChat, startIndex: number, count: number): void => {
   throw Error();
 };
 
@@ -242,12 +247,14 @@ export default class {
     this.mode = opt.mode;
     this.wsUrl = opt.wsUrl;
 
-    ScreenNameNormalizer._queryUser = this.queryUser;
-    sendAllStories = (rawUserName, receiver, startIndex = 0, count = 10) => {
-      if (startIndex < 0) return this.bot.sendTo(receiver, '跳过数量参数值应为非负整数。');
-      if (count < 1) return this.bot.sendTo(receiver, '最大查看数量参数值应为正整数。');
-      const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
-      this.queryUser(rawUserName)
+    const workNow = (config: {
+      rawUserName: string,
+      action: (userName: string) => void,
+      retryAction: () => void,
+      reply: (msg: string) => void
+    }) => {
+      const {action, retryAction, reply, rawUserName} = config;
+      return this.queryUser(rawUserName)
         .then(userNameId => {
           const [userName, userId] = userNameId.split(':');
           if (Date.now() - this.cache[userName]?.updated?.getTime() > this.workInterval * 1000 &&
@@ -265,37 +272,64 @@ export default class {
               ))
             ).then(() => userName).finally(() => this.cache[userName].updated = new Date()));
         })
-        .then(userName => {
-          const storyItems = Object.values(this.cache[userName].stories)
-            .sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
-          if (storyItems.length === 0) return this.bot.sendTo(receiver, `当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
-          if (startIndex + 1 > storyItems.length) return this.bot.sendTo(receiver, '跳过数量到达或超过当前用户可用的限时动态数量。');
-          const endIndex = Math.min(storyItems.length, startIndex + count);
-          const sendRangeText = `${startIndex + 1}${endIndex - startIndex > 1 ? `-${endIndex}` : ''}`;
-          return this.workOnMedia(storyItems.slice(startIndex, endIndex), sender)
-            .then(() => this.bot.sendTo(receiver, `已显示当前用户 ${storyItems.length} 条可用限时动态中的第 ${sendRangeText} 条。`));
-        })
+        .then(action)
         .catch((error: IgClientError & Partial<RequestError>) => {
           if (error instanceof IgExactUserNotFoundError) {
-            this.bot.sendTo(receiver, `找不到用户 ${rawUserName.replace(/^@?(.*)$/, '@$1')}。`);
+            reply(`找不到用户 ${rawUserName.replace(/^@?(.*)$/, '@$1')}。`);
           }
           if (error instanceof IgNetworkError) {
             if ((error.cause as Error).message === "Unexpected '<'") {
               logger.warn('login required, logging in again...');
-              return this.session.login().then(() => sendAllStories(rawUserName, receiver, startIndex, count));
+              return this.session.login().then(retryAction);
             }
             logger.warn(`error while fetching stories for ${rawUserName}: ${JSON.stringify(error.cause)}`);
-            this.bot.sendTo(receiver, `获取 Stories 时出现错误:原因: ${error.cause}`);
+            reply(`获取 Stories 时出现错误:原因: ${error.cause}`);
           } else if (error instanceof IgLoginRequiredError || error instanceof IgCookieNotFoundError) {
             logger.warn('login required, logging in again...');
-            this.bot.sendTo(receiver, '等待登陆中,稍后会处理请求,请稍候……');
-            this.session.login().then(() => sendAllStories(rawUserName, receiver, startIndex, count));
+            reply('等待登陆中,稍后会处理请求,请稍候……');
+            this.session.login().then(retryAction);
           } else {
             logger.error(`unhandled error while fetching stories for ${rawUserName}: ${error}`);
-            this.bot.sendTo(receiver, `获取 Stories 时发生未知错误: ${error}`); 
+            reply(`获取 Stories 时发生未知错误: ${error}`); 
           }
         });
     };
+
+    ScreenNameNormalizer._queryUser = this.queryUser;
+    sendStory = (rawUserName, storyId, receiver) => {
+      const reply = msg => this.bot.sendTo(receiver, msg);
+      const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
+      workNow({
+        rawUserName,
+        action: userName => {
+          if (!(storyId in this.cache[userName].stories)) return reply('此动态不存在或已过期。');
+          return this.workOnMedia([this.cache[userName].stories[storyId]], sender);
+        },
+        reply,
+        retryAction: () => sendStory(rawUserName, storyId, receiver),
+      });
+    }
+    sendAllStories = (rawUserName, receiver, startIndex = 0, count = 10) => {
+      const reply = msg => this.bot.sendTo(receiver, msg);
+      if (startIndex < 0) return reply('跳过数量参数值应为非负整数。');
+      if (count < 1) return reply('最大查看数量参数值应为正整数。');
+      const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
+      workNow({
+        rawUserName,
+        action: userName => {
+          const storyItems = Object.values(this.cache[userName].stories)
+            .sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
+          if (storyItems.length === 0) return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
+          if (startIndex + 1 > storyItems.length) return reply('跳过数量到达或超过当前用户可用的限时动态数量。');
+          const endIndex = Math.min(storyItems.length, startIndex + count);
+          const sendRangeText = `${startIndex + 1}${endIndex - startIndex > 1 ? `-${endIndex}` : ''}`;
+          return this.workOnMedia(storyItems.slice(startIndex, endIndex), sender)
+            .then(() => reply(`已显示当前用户 ${storyItems.length} 条可用限时动态中的第 ${sendRangeText} 条。`));
+        },
+        reply,
+        retryAction: () => sendAllStories(rawUserName, receiver, startIndex, count)
+      });
+    };
   }
 
   public launch = () => {