123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const fs = require("fs");
- const path = require("path");
- const Twitter = require("twitter");
- const loggers_1 = require("./loggers");
- const mirai_1 = require("./mirai");
- const webshot_1 = require("./webshot");
- const logger = loggers_1.getLogger('twitter');
- class default_1 {
- constructor(opt) {
- this.launch = () => {
- this.webshot = new webshot_1.default(this.webshotOutDir, this.mode, () => setTimeout(this.work, this.workInterval * 1000));
- };
- this.work = () => {
- const lock = this.lock;
- if (this.workInterval < 1)
- this.workInterval = 1;
- if (lock.feed.length === 0) {
- setTimeout(() => {
- this.work();
- }, this.workInterval * 1000);
- return;
- }
- if (lock.workon >= lock.feed.length)
- lock.workon = 0;
- if (!lock.threads[lock.feed[lock.workon]] ||
- !lock.threads[lock.feed[lock.workon]].subscribers ||
- lock.threads[lock.feed[lock.workon]].subscribers.length === 0) {
- logger.warn(`nobody subscribes thread ${lock.feed[lock.workon]}, removing from feed`);
- delete lock.threads[lock.feed[lock.workon]];
- lock.feed.splice(lock.workon, 1);
- fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
- this.work();
- return;
- }
- logger.debug(`pulling feed ${lock.feed[lock.workon]}`);
- const promise = new Promise(resolve => {
- let match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)\/lists\/([^\/]+)/);
- let config;
- let endpoint;
- if (match) {
- config = {
- owner_screen_name: match[1],
- slug: match[2],
- tweet_mode: 'extended',
- };
- endpoint = 'lists/statuses';
- }
- else {
- match = lock.feed[lock.workon].match(/https:\/\/twitter.com\/([^\/]+)/);
- if (match) {
- config = {
- screen_name: match[1],
- exclude_replies: false,
- tweet_mode: 'extended',
- };
- endpoint = 'statuses/user_timeline';
- }
- }
- if (endpoint) {
- const offset = lock.threads[lock.feed[lock.workon]].offset;
- if (offset > 0)
- config.since_id = offset;
- this.client.get(endpoint, config, (error, tweets, response) => {
- if (error) {
- if (error instanceof Array && error.length > 0 && error[0].code === 34) {
- logger.warn(`error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
- lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
- logger.info(`sending notfound message of ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
- this.bot.sendTo(subscriber, `链接 ${lock.feed[lock.workon]} 指向的用户或列表不存在,请退订。`).catch();
- });
- }
- else {
- logger.error(`unhandled error on fetching tweets for ${lock.feed[lock.workon]}: ${JSON.stringify(error)}`);
- }
- resolve();
- }
- else
- resolve(tweets);
- });
- }
- });
- promise.then((tweets) => {
- logger.debug(`api returned ${JSON.stringify(tweets)} for feed ${lock.feed[lock.workon]}`);
- if (!tweets || tweets.length === 0) {
- lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
- return;
- }
- if (lock.threads[lock.feed[lock.workon]].offset === -1) {
- lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
- return;
- }
- if (lock.threads[lock.feed[lock.workon]].offset === 0)
- tweets.splice(1);
- const maxCount = 3;
- let sendTimeout = 10000;
- const retryTimeout = 1500;
- const ordinal = (n) => {
- switch ((~~(n / 10) % 10 === 1) ? 0 : n % 10) {
- case 1:
- return `${n}st`;
- case 2:
- return `${n}nd`;
- case 3:
- return `${n}rd`;
- default:
- return `${n}th`;
- }
- };
- const sendTweets = (msg, text, author) => {
- lock.threads[lock.feed[lock.workon]].subscribers.forEach(subscriber => {
- logger.info(`pushing data of thread ${lock.feed[lock.workon]} to ${JSON.stringify(subscriber)}`);
- const retry = (reason, count) => {
- if (count <= maxCount)
- sendTimeout *= (count + 2) / (count + 1);
- setTimeout(() => {
- msg.forEach((message, pos) => {
- if (count > maxCount && message.type === 'Image') {
- if (pos === 0) {
- logger.warn(`${count - 1} consecutive failures sending webshot, trying plain text instead...`);
- msg[pos] = mirai_1.MiraiMessage.Plain(author + text);
- }
- else {
- msg[pos] = mirai_1.MiraiMessage.Plain(`[失败的图片:${message.path}]`);
- }
- }
- });
- logger.warn(`retry sending to ${subscriber.chatID} for the ${ordinal(count)} time...`);
- this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, count + 1));
- }, retryTimeout);
- };
- this.bot.sendTo(subscriber, msg, sendTimeout).catch(error => retry(error, 1));
- });
- };
- return this.webshot(tweets, sendTweets, this.webshotDelay)
- .then(() => {
- lock.threads[lock.feed[lock.workon]].offset = tweets[0].id_str;
- lock.threads[lock.feed[lock.workon]].updatedAt = new Date().toString();
- });
- })
- .then(() => {
- lock.workon++;
- let timeout = this.workInterval * 1000 / lock.feed.length;
- if (timeout < 1000)
- timeout = 1000;
- fs.writeFileSync(path.resolve(this.lockfile), JSON.stringify(lock));
- setTimeout(() => {
- this.work();
- }, timeout);
- });
- };
- this.client = new Twitter({
- consumer_key: opt.consumer_key,
- consumer_secret: opt.consumer_secret,
- access_token_key: opt.access_token_key,
- access_token_secret: opt.access_token_secret,
- });
- this.lockfile = opt.lockfile;
- this.lock = opt.lock;
- this.workInterval = opt.workInterval;
- this.bot = opt.bot;
- this.webshotDelay = opt.webshotDelay;
- this.webshotOutDir = opt.webshotOutDir;
- this.mode = opt.mode;
- }
- }
- exports.default = default_1;
|