import { Plugin, Configurable, EventListener, DependencyLoad } from '@squeebot/core/lib/plugin'; import { logger } from '@squeebot/core/lib/core'; import Twitter from 'twitter-lite'; import { IMessage } from '@squeebot/core/lib/types'; import { sanitizeEscapedText } from '@squeebot/core/lib/common'; interface TwitterUser { id: number; name: string; screen_name: string; description: string; followers_count: number; statuses_count: number; } interface TwitterTweet { id: number; full_text: string; text?: string; user: TwitterUser; retweet_count: number; favorite_count: number; } @Configurable({ consumer_key: null, consumer_secret: null, access_token_key: null, access_token_secret: null, }) class TwitterURLPlugin extends Plugin { public twitter: Twitter | null = null; public me: TwitterUser | null = null; @EventListener('pluginUnload') public unloadEventHandler(plugin: string | Plugin): void { if (plugin === this.name || plugin === this) { this.config.save().then(() => this.emit('pluginUnloaded', this)); } } async handleTweet(data: TwitterTweet, msg: IMessage): Promise { const keys = []; const end = sanitizeEscapedText(data.text || data.full_text); keys.push(['field', 'Twitter', { color: 'cyan', type: 'title' }]); keys.push(['field', data.favorite_count.toString(), { color: 'red', label: ['♥', 'Likes'], type: 'metric' }]); keys.push(['field', data.retweet_count.toString(), { color: 'green', label: ['↱↲', 'Retweets'], type: 'metric' }]); keys.push(['bold', '@' + data.user.screen_name + ':']); keys.push(['field', end, { type: 'content' }]); msg.resolve(keys); return true; } async getStatusById(id: string): Promise { if (!this.twitter) { return null; } return this.twitter.get('statuses/show', {id, tweet_mode: 'extended'}); } async twitterURLHandler(url: URL, msg: IMessage): Promise { const det = url.href.match(/\/\w+\/status\/(\d+[^&#?\s/])/i); if (!det) { return false; } try { const tweet = await this.getStatusById(det[1]); if (tweet) { return this.handleTweet(tweet as TwitterTweet, msg); } } catch (e) { msg.resolve('No such tweet!'); } return false; } @DependencyLoad('urlreply') addURLHandler(urlreply: any): void { urlreply.registerHandler(this.name, 'twitter.com/', (url: URL, msg: IMessage) => { return this.twitterURLHandler(url, msg); }); urlreply.registerHandler(this.name, 'nitter.net/', (url: URL, msg: IMessage) => { return this.twitterURLHandler(url, msg); }); } initialize(): void { const consumerKey = this.config.get('consumer_key'); const consumerSecret = this.config.get('consumer_secret'); const accessToken = this.config.get('access_token_key'); const accessTokenSecret = this.config.get('access_token_secret'); if (!consumerKey || !consumerSecret || !accessToken || !accessTokenSecret) { logger.error('[%s] Twitter credentials missing!', this.name); return; } this.twitter = new Twitter({ consumer_key: consumerKey, consumer_secret: consumerSecret, access_token_key: accessToken, access_token_secret: accessTokenSecret, }); this.twitter.get('account/verify_credentials').then((data: TwitterUser) => { this.me = data; }, (err) => { logger.error('[%s] Twitter threw an error!', this.name, err.stack); }); } } module.exports = TwitterURLPlugin;