|
@@ -5,6 +5,7 @@ import TwitterTypes from 'twitter-d';
|
|
|
|
|
|
import { getLogger } from './loggers';
|
|
import { getLogger } from './loggers';
|
|
import QQBot from './koishi';
|
|
import QQBot from './koishi';
|
|
|
|
+import RedisSvc from './redis';
|
|
import { chainPromises, BigNumOps } from './utils';
|
|
import { chainPromises, BigNumOps } from './utils';
|
|
import Webshot from './webshot';
|
|
import Webshot from './webshot';
|
|
|
|
|
|
@@ -20,6 +21,7 @@ interface IWorkerOption {
|
|
accessTokenSecret: string;
|
|
accessTokenSecret: string;
|
|
mode: number;
|
|
mode: number;
|
|
wsUrl: string;
|
|
wsUrl: string;
|
|
|
|
+ redis?: IRedisConfig;
|
|
}
|
|
}
|
|
|
|
|
|
export class ScreenNameNormalizer {
|
|
export class ScreenNameNormalizer {
|
|
@@ -121,6 +123,7 @@ export default class {
|
|
private webshot: Webshot;
|
|
private webshot: Webshot;
|
|
private mode: number;
|
|
private mode: number;
|
|
private wsUrl: string;
|
|
private wsUrl: string;
|
|
|
|
+ private redis: RedisSvc;
|
|
|
|
|
|
constructor(opt: IWorkerOption) {
|
|
constructor(opt: IWorkerOption) {
|
|
this.client = new Twitter({
|
|
this.client = new Twitter({
|
|
@@ -136,9 +139,10 @@ export default class {
|
|
this.webshotDelay = opt.webshotDelay;
|
|
this.webshotDelay = opt.webshotDelay;
|
|
this.mode = opt.mode;
|
|
this.mode = opt.mode;
|
|
this.wsUrl = opt.wsUrl;
|
|
this.wsUrl = opt.wsUrl;
|
|
|
|
+ if (opt.redis) this.redis = new RedisSvc(opt.redis);
|
|
ScreenNameNormalizer._queryUser = this.queryUser;
|
|
ScreenNameNormalizer._queryUser = this.queryUser;
|
|
sendTweet = (id, receiver) => {
|
|
sendTweet = (id, receiver) => {
|
|
- this.getTweet(id, this.sendTweets(`tweet ${id}`, receiver))
|
|
|
|
|
|
+ this.getTweet(id, this.sendTweets({sourceInfo: `tweet ${id}`, reportOnSkip: true}, receiver))
|
|
.catch((err: {code: number, message: string}[]) => {
|
|
.catch((err: {code: number, message: string}[]) => {
|
|
if (err[0].code !== 144) {
|
|
if (err[0].code !== 144) {
|
|
logger.warn(`error retrieving tweet: ${err[0].message}`);
|
|
logger.warn(`error retrieving tweet: ${err[0].message}`);
|
|
@@ -258,10 +262,28 @@ export default class {
|
|
|
|
|
|
private workOnTweets = (
|
|
private workOnTweets = (
|
|
tweets: Tweets,
|
|
tweets: Tweets,
|
|
- sendTweets: (msg: string, text: string, author: string) => void
|
|
|
|
- ) => this.webshot(tweets, sendTweets, this.webshotDelay);
|
|
|
|
-
|
|
|
|
- public getTweet = (id: string, sender: (msg: string, text: string, author: string) => void) => {
|
|
|
|
|
|
+ sendTweets: (id: string, msg: string, text: string, author: string) => void
|
|
|
|
+ ) => Promise.all(tweets.map(tweet =>
|
|
|
|
+ (this.redis ? this.redis.getContent(`webshot/${tweet.id_str}`) : Promise.reject())
|
|
|
|
+ .then(content => {
|
|
|
|
+ if (content === null) throw Error();
|
|
|
|
+ logger.info(`retrieved cached webshot of tweet ${tweet.id_str} from redis database`);
|
|
|
|
+ const {msg, text, author} = JSON.parse(content) as {[key: string]: string};
|
|
|
|
+ sendTweets(tweet.retweeted_status ? tweet.retweeted_status.id_str : tweet.id_str, msg, text, author);
|
|
|
|
+ }).catch(() =>
|
|
|
|
+ this.webshot([tweet], (id: string, msg: string, text: string, author: string) => {
|
|
|
|
+ Promise.resolve()
|
|
|
|
+ .then(() => {
|
|
|
|
+ if (!this.redis) return;
|
|
|
|
+ logger.info(`caching webshot of tweet ${tweet.id_str} to redis database`);
|
|
|
|
+ this.redis.cacheContent(`webshot/${tweet.id_str}`, JSON.stringify({msg, text, author}));
|
|
|
|
+ })
|
|
|
|
+ .then(() => sendTweets(id, msg, text, author));
|
|
|
|
+ }, this.webshotDelay)
|
|
|
|
+ )
|
|
|
|
+ ));
|
|
|
|
+
|
|
|
|
+ public getTweet = (id: string, sender: (id: string, msg: string, text: string, author: string) => void) => {
|
|
const endpoint = 'statuses/show';
|
|
const endpoint = 'statuses/show';
|
|
const config = {
|
|
const config = {
|
|
id,
|
|
id,
|
|
@@ -274,10 +296,13 @@ export default class {
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
- private sendTweets = (source?: string, ...to: IChat[]) => (msg: string, text: string, author: string) => {
|
|
|
|
|
|
+ private sendTweets = (
|
|
|
|
+ config: {sourceInfo?: string, reportOnSkip?: boolean} = {reportOnSkip: false}, ...to: IChat[]
|
|
|
|
+ ) => (id: string, msg: string, text: string, author: string) => {
|
|
to.forEach(subscriber => {
|
|
to.forEach(subscriber => {
|
|
- logger.info(`pushing data${source ? ` of ${source}` : ''} to ${JSON.stringify(subscriber)}`);
|
|
|
|
- retryOnError(
|
|
|
|
|
|
+ const {sourceInfo: source, reportOnSkip} = config;
|
|
|
|
+ const targetStr = JSON.stringify(subscriber);
|
|
|
|
+ const send = () => retryOnError(
|
|
() => this.bot.sendTo(subscriber, msg),
|
|
() => this.bot.sendTo(subscriber, msg),
|
|
(_, count, terminate: (doNothing: Promise<void>) => void) => {
|
|
(_, count, terminate: (doNothing: Promise<void>) => void) => {
|
|
if (count <= maxTrials) {
|
|
if (count <= maxTrials) {
|
|
@@ -286,6 +311,23 @@ export default class {
|
|
logger.warn(`${count - 1} consecutive failures while sending message chain, trying plain text instead...`);
|
|
logger.warn(`${count - 1} consecutive failures while sending message chain, trying plain text instead...`);
|
|
terminate(this.bot.sendTo(subscriber, author + text, true));
|
|
terminate(this.bot.sendTo(subscriber, author + text, true));
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ ).then(() => {
|
|
|
|
+ if (this.redis) {
|
|
|
|
+ logger.info(`caching push status of this tweet (or its origin in case of a retweet) for ${targetStr}...`);
|
|
|
|
+ return this.redis.cacheForChat(id, subscriber);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ (this.redis ? this.redis.isCachedForChat(id, subscriber) : Promise.resolve(false))
|
|
|
|
+ .then(isCached => {
|
|
|
|
+ if (isCached) {
|
|
|
|
+ logger.info(`skipped subscriber ${targetStr} as this tweet or the origin of this retweet has been sent already`);
|
|
|
|
+ if (!reportOnSkip) return;
|
|
|
|
+ text = `[最近发送过的推文:${id}]`;
|
|
|
|
+ msg = author + text;
|
|
|
|
+ }
|
|
|
|
+ logger.info(`pushing data${source ? ` of ${source}` : ''} to ${targetStr}`);
|
|
|
|
+ return send();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
};
|
|
};
|
|
@@ -377,7 +419,7 @@ export default class {
|
|
if (currentThread.offset === '-1') { updateOffset(); return; }
|
|
if (currentThread.offset === '-1') { updateOffset(); return; }
|
|
if (currentThread.offset === '0') tweets.splice(1);
|
|
if (currentThread.offset === '0') tweets.splice(1);
|
|
|
|
|
|
- return this.workOnTweets(tweets, this.sendTweets(`thread ${currentFeed}`, ...currentThread.subscribers))
|
|
|
|
|
|
+ return this.workOnTweets(tweets, this.sendTweets({sourceInfo: `thread ${currentFeed}`}, ...currentThread.subscribers))
|
|
.then(updateDate).then(updateOffset);
|
|
.then(updateDate).then(updateOffset);
|
|
})
|
|
})
|
|
.then(() => {
|
|
.then(() => {
|