twitter.js 5.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.doSearch = void 0;
  4. const Twitter = require("twitter");
  5. const loggers_1 = require("./loggers");
  6. const utils_1 = require("./utils");
  7. let doSearch = (conf, receiver) => {
  8. throw Error();
  9. };
  10. exports.doSearch = doSearch;
  11. const TWITTER_EPOCH = 1288834974657;
  12. const snowflake = (epoch) => Number.isNaN(epoch) ? undefined :
  13. utils_1.BigNumOps.lShift(String(epoch - 1 - TWITTER_EPOCH), 22);
  14. const logger = (0, loggers_1.getLogger)('twitter');
  15. var AdaptiveSearch;
  16. (function (AdaptiveSearch) {
  17. ;
  18. AdaptiveSearch.isCursor = (entry) => entry.sortIndex === '0' || entry.sortIndex === '999999999';
  19. })(AdaptiveSearch || (AdaptiveSearch = {}));
  20. class default_1 {
  21. constructor(opt) {
  22. this.search = (conf) => {
  23. for (const key in conf) {
  24. if (!conf[key])
  25. delete conf[key];
  26. }
  27. const origQuery = conf.query;
  28. delete conf.query;
  29. const paramNames = { until: 'max_id', since: 'since_id' };
  30. logger.info(`performing custom query ${origQuery} with config: ${JSON.stringify(conf)}`);
  31. const query = origQuery.replace(new RegExp(`(${Object.values(paramNames).join('|')}):\d+`, 'g'), ($0, $1) => conf[$1] ? '' : $0).replace(/\(\)/g, '') +
  32. Object.keys(paramNames).map(k => conf[k] && `(${paramNames[k]}:${conf[k]})`).join();
  33. const doSearch = (q, tweets = [], cursor) => this.privateClient.get('https://api.twitter.com/2/search/adaptive.json', Object.assign({ q, tweet_mode: 'extended', count: 20, include_entities: true, query_source: 'typed_query' }, (cursor && { cursor }))).then(({ globalObjects: { tweets: tweetDict, users: userDict }, timeline: { instructions: { 0: { addEntries: { entries } } } } }) => {
  34. const newTweets = Object.values(tweetDict);
  35. let bottomCursor;
  36. if (newTweets.length) {
  37. logger.debug(`fetched tweets: ${JSON.stringify(newTweets)}`);
  38. entries.sort((e1, e2) => Number(e1.sortIndex) - Number(e2.sortIndex));
  39. if (AdaptiveSearch.isCursor(entries[0])) {
  40. bottomCursor = entries[0];
  41. logger.info(`custom query ${origQuery} yielded ${newTweets.length} new tweets, next query will follow tweet ${entries[1].entryId.replace(/^sq-I-t-/, '')}`);
  42. }
  43. else {
  44. logger.info(`custom query ${origQuery} ended after yielding ${newTweets.length} new tweets, last entry was tweet ${entries[0].entryId.replace(/^sq-I-t-/, '')}`);
  45. }
  46. newTweets.forEach(tweet => Object.assign(tweet, {
  47. user: userDict[tweet.user_id_str],
  48. }));
  49. tweets.push(...newTweets);
  50. }
  51. if (!bottomCursor || !newTweets.length || tweets.length >= conf.count) {
  52. logger.info(`custom query ${origQuery} finished successfully, ${tweets.length} tweets have been fetched`);
  53. return tweets.slice(0, conf.count);
  54. }
  55. return doSearch(query, tweets, bottomCursor === null || bottomCursor === void 0 ? void 0 : bottomCursor.content.operation.cursor.value);
  56. });
  57. return doSearch(query);
  58. };
  59. this.privateClient = new Twitter({
  60. bearer_token: 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA',
  61. request: {
  62. headers: {
  63. 'Content-Type': 'application/x-www-form-urlencoded',
  64. Cookie: `auth_token=${opt.privateAuthToken}; ct0=${opt.privateCsrfToken};`,
  65. 'X-CSRF-Token': opt.privateCsrfToken,
  66. },
  67. }
  68. });
  69. this.bot = opt.bot;
  70. exports.doSearch = ({ query, count, since, until }, receiver) => {
  71. const countNum = Number(count) || 15;
  72. this.search({
  73. query,
  74. count: Math.abs(countNum),
  75. since: utils_1.BigNumOps.parse(since) || snowflake(new Date(since).getTime()),
  76. until: utils_1.BigNumOps.parse(until) || snowflake(new Date(until).getTime()),
  77. })
  78. .then(tweets => (0, utils_1.chainPromises)(tweets.map(tweet => () => this.bot.sendTo(receiver, `\
  79. 用户:${tweet.user.name} (@${tweet.user.screen_name})
  80. 编号:${tweet.id_str}
  81. 时间:${tweet.created_at}
  82. 媒体:${tweet.extended_entities ? '有' : '无'}
  83. 正文:\n${tweet.full_text.replace(/^([\s\S\n]{50})[\s\S\n]+?( https:\/\/t.co\/.*)?$/, '$1…$2')}`))
  84. .concat(() => this.bot.sendTo(receiver, tweets.length ?
  85. '自定义查询完毕,使用 /twitter_view <编号> 查看推文详细内容。' :
  86. '自定义查询完毕,没有找到符合条件的推文。'))))
  87. .catch((err) => {
  88. var _a, _b;
  89. logger.warn(`error retrieving timeline: ${((_a = err[0]) === null || _a === void 0 ? void 0 : _a.message) || err}`);
  90. return this.bot.sendTo(receiver, `自定义查询时出现错误:${((_b = err[0]) === null || _b === void 0 ? void 0 : _b.message) || err}`);
  91. });
  92. };
  93. }
  94. }
  95. exports.default = default_1;