command.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.query = exports.view = exports.unsubAll = exports.unsub = exports.list = exports.sub = exports.parseCmd = void 0;
  4. const fs = require("fs");
  5. const path = require("path");
  6. const datetime_1 = require("./datetime");
  7. const loggers_1 = require("./loggers");
  8. const twitter_1 = require("./twitter");
  9. const logger = (0, loggers_1.getLogger)('command');
  10. function parseCmd(message) {
  11. message = message.trim();
  12. message = message.replace('\\\\', '\\0x5c');
  13. message = message.replace('\\\"', '\\0x22');
  14. message = message.replace('\\\'', '\\0x27');
  15. const strs = message.match(/'[\s\S]*?'|(?:\S+=)?"[\s\S]*?"|\S+/mg);
  16. const cmd = (strs === null || strs === void 0 ? void 0 : strs.length) ? strs[0].length ? strs[0].substring(0, 1) === '/' ? strs[0].substring(1) : '' : '' : '';
  17. const args = (strs !== null && strs !== void 0 ? strs : []).slice(1).map(arg => {
  18. arg = arg.replace(/^(\S+=)?["']+(?!.*=)|["']+$/g, '$1');
  19. arg = arg.replace('\\0x27', '\\\'');
  20. arg = arg.replace('\\0x22', '\\\"');
  21. arg = arg.replace('\\0x5c', '\\\\');
  22. return arg;
  23. });
  24. return {
  25. cmd,
  26. args,
  27. };
  28. }
  29. exports.parseCmd = parseCmd;
  30. function linkFinder(userName, chat, lock) {
  31. const normalizedLink = (0, twitter_1.linkBuilder)({ userName });
  32. const link = Object.keys(lock.threads).find(realLink => normalizedLink === realLink.replace(/\/@/, '/').toLowerCase());
  33. if (!link)
  34. return [null, -1];
  35. const index = lock.threads[link].subscribers.findIndex(({ chatID, chatType }) => chat.chatID === chatID && chat.chatType === chatType);
  36. return [link, index];
  37. }
  38. function sub(chat, args, reply, lock, lockfile) {
  39. if (chat.chatType === "temp") {
  40. return reply('请先添加机器人为好友。');
  41. }
  42. if (args.length === 0) {
  43. return reply('找不到要订阅 Instagram 限时动态的链接。');
  44. }
  45. const matched = (0, twitter_1.parseLink)(args[0]);
  46. if (!matched) {
  47. return reply(`订阅链接格式错误:
  48. 示例:
  49. https://www.instagram.com/tomoyo_kurosawa_/`);
  50. }
  51. const subscribeTo = (link, config = {}) => {
  52. const { id, msg = `已为此聊天订阅 ${link} 的 Instagram 限时动态` } = config;
  53. if (id) {
  54. lock.feed.push(link);
  55. lock.threads[link] = {
  56. id,
  57. offset: '0',
  58. subscribers: [],
  59. updatedAt: '',
  60. };
  61. }
  62. lock.threads[link].subscribers.push(chat);
  63. logger.warn(`chat ${JSON.stringify(chat)} has subscribed ${link}`);
  64. fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
  65. reply(msg);
  66. };
  67. const tryFindSub = (userName) => {
  68. const [realLink, index] = linkFinder(userName, chat, lock);
  69. if (index > -1) {
  70. reply('此聊天已订阅此链接的 Instagram 限时动态。');
  71. return true;
  72. }
  73. if (realLink) {
  74. subscribeTo(realLink);
  75. return true;
  76. }
  77. return false;
  78. };
  79. const newSub = (userName) => {
  80. const link = (0, twitter_1.linkBuilder)(matched);
  81. subscribeTo(link, { id: Number(userName.split(':')[1]) });
  82. };
  83. if (!tryFindSub(matched.userName)) {
  84. twitter_1.ScreenNameNormalizer.normalizeLive(matched.userName).then(userName => {
  85. if (!userName)
  86. return reply(`找不到用户 ${matched.userName.replace(/^@?(.*)$/, '@$1')}。`);
  87. else
  88. newSub(userName);
  89. });
  90. }
  91. }
  92. exports.sub = sub;
  93. function unsubAll(chat, args, reply, lock, lockfile) {
  94. if (chat.chatType === "temp") {
  95. return reply('请先添加机器人为好友。');
  96. }
  97. Object.entries(lock.threads).forEach(([link, { subscribers }]) => {
  98. const index = subscribers.indexOf(chat);
  99. if (index === -1)
  100. return;
  101. subscribers.splice(index, 1);
  102. fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
  103. logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
  104. });
  105. return reply(`已为此聊天退订所有 Instagram 限时动态链接。`);
  106. }
  107. exports.unsubAll = unsubAll;
  108. function unsub(chat, args, reply, lock, lockfile) {
  109. var _a;
  110. if (chat.chatType === "temp") {
  111. return reply('请先添加机器人为好友。');
  112. }
  113. if (args.length === 0) {
  114. return reply('找不到要退订 Instagram 限时动态的链接。');
  115. }
  116. const match = (_a = (0, twitter_1.parseLink)(args[0])) === null || _a === void 0 ? void 0 : _a.userName;
  117. if (!match) {
  118. return reply('链接格式有误。');
  119. }
  120. const [link, index] = linkFinder(match, chat, lock);
  121. if (index === -1)
  122. return list(chat, args, msg => reply('您没有订阅此链接的 Instagram 限时动态。\n' + msg), lock);
  123. else {
  124. lock.threads[link].subscribers.splice(index, 1);
  125. fs.writeFileSync(path.resolve(lockfile), JSON.stringify(lock));
  126. logger.warn(`chat ${JSON.stringify(chat)} has unsubscribed ${link}`);
  127. return reply(`已为此聊天退订 ${link} 的 Instagram 限时动态`);
  128. }
  129. }
  130. exports.unsub = unsub;
  131. function list(chat, _, reply, lock) {
  132. if (chat.chatType === "temp") {
  133. return reply('请先添加机器人为好友。');
  134. }
  135. const links = [];
  136. Object.keys(lock.threads).forEach(key => {
  137. if (lock.threads[key].subscribers.find(({ chatID, chatType }) => chat.chatID === chatID && chat.chatType === chatType))
  138. links.push(`${key} ${(0, datetime_1.relativeDate)(lock.threads[key].updatedAt)}`);
  139. });
  140. return reply('此聊天中订阅的 Instagram 限时动态动态链接:\n' + links.join('\n'));
  141. }
  142. exports.list = list;
  143. function view(chat, args, reply) {
  144. const promptOnError = (func) => (...args) => {
  145. try {
  146. func(...args);
  147. }
  148. catch (e) {
  149. reply('机器人尚未加载完毕,请稍后重试。');
  150. }
  151. };
  152. if (args.length === 0) {
  153. return reply('找不到要查看 Instagram 限时动态的链接。');
  154. }
  155. const match = (0, twitter_1.parseLink)(args[0]);
  156. if (!(match === null || match === void 0 ? void 0 : match.userName)) {
  157. return reply('链接格式有误。');
  158. }
  159. if (match === null || match === void 0 ? void 0 : match.storyId) {
  160. return promptOnError(twitter_1.sendStory)(match.userName, match.storyId, chat);
  161. }
  162. const conf = {};
  163. const confZH = {
  164. count: '最大查看数量',
  165. skip: '跳过数量',
  166. };
  167. for (const arg of args.slice(1)) {
  168. const optMatch = /^(count|skip)=(.*)/.exec(arg);
  169. if (!optMatch)
  170. return reply(`未定义的查看参数:${arg}。`);
  171. const optKey = optMatch[1];
  172. if (optMatch.length === 1 || !/^-?\d*$/.test(optMatch[2]))
  173. return reply(`${confZH[optKey]}参数应为数值。`);
  174. if (optMatch[2] === '')
  175. return reply(`${confZH[optKey]}参数值不可为空。`);
  176. conf[optKey] = Number(optMatch[2]);
  177. }
  178. promptOnError(twitter_1.sendAllStories)(match.userName, chat, conf.skip, conf.count);
  179. }
  180. exports.view = view;
  181. function query(chat, args, reply) {
  182. var _a;
  183. if (args.length === 0) {
  184. return reply('找不到要查询 Instagram 限时动态的用户。');
  185. }
  186. const match = (_a = (0, twitter_1.parseLink)(args[0])) === null || _a === void 0 ? void 0 : _a.userName;
  187. if (!match) {
  188. return reply('链接格式有误。');
  189. }
  190. try {
  191. (0, twitter_1.sendTimeline)(match, chat);
  192. }
  193. catch (e) {
  194. logger.error(`error querying timeline, error: ${e}`);
  195. reply('机器人尚未加载完毕,请稍后重试。');
  196. }
  197. }
  198. exports.query = query;