'use strict';
const Twitter = require('twitter');
const Feed = require('feed').Feed;
/**
* TwitterRSSFeed class.
*/
class TwitterRSSFeed {
/**
* Twitter credentials.
* @typedef {Object} Credentials
* @property {string} consumer_key - Consumer key
* @property {string} consumer_secret - Consumer secret
* @property {string} token - Access token
* @property {string} token_secret - Access secret
*/
/**
* Options.
* @typedef {Object} Options
* @property {function} format - Formatter function
* @property {function[]} filters - Array of filter function
*/
/**
* Creates a instance of TwitterRSSFeed.
* @param {Credentials} credentials - Twitter credentials
* @constructor
*/
constructor(credentials) {
this.t = new Twitter({
consumer_key: credentials.consumer_key,
consumer_secret: credentials.consumer_secret,
access_token_key: credentials.token,
access_token_secret: credentials.token_secret
});
}
/**
* Returns a response object of Twitter API.
* @param {string} path - Path of Twitter API (Ex. 'statuses/user_timeline').
* @param {Object} params - Parameters of Twitter API.
* @returns {Promise<Object>} A response object of Twitter API.
* @see {@link https://developer.twitter.com/en/docs/api-reference-index|API reference index — Twitter Developers}
*/
async get(path, params) {
return await this.t.get(path, params);
}
/**
* Returns a RSS feed of Twitter API (GET statuses/user_timeline).
* @param {Object} params - Parameters of Twitter API (GET statuses/user_timeline).
* @param {Object} info - A RSS information.
* @param {Options} opts - Options.
* @returns {Promise<string>} A RSS feed.
* @see {@link https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-user_timeline|GET statuses/user_timeline — Twitter Developers}
*/
async statuses_user_timeline(params, info, opts = {}) {
const tweets = await this.get('statuses/user_timeline', params);
return this.rss(tweets, info, opts);
}
/**
* Returns a RSS feed of Twitter API (GET favorites/list).
* @param {Object} params - Parameters of Twitter API (GET favorites/list).
* @param {Object} info - A RSS information.
* @param {Options} opts - Options.
* @returns {Promise<string>} A RSS feed.
* @see {@link https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/get-favorites-list|GET favorites/list — Twitter Developers}
*/
async favorites_list(params, info, opts = {}) {
const tweets = await this.get('favorites/list', params);
return this.rss(tweets, info, opts);
}
/**
* Returns a RSS feed of Twitter API (Standard search API).
* @param {Object} params - Parameters of Twitter API (Standard search API).
* @param {Object} info - A RSS information.
* @param {Options} opts - Options.
* @returns {Promise<string>} A RSS feed.
* @see {@link https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets|Standard search API — Twitter Developers}
*/
async search_tweets(params, info, opts = {}) {
const searched = await this.get('search/tweets', params);
const tweets = searched.statuses;
return this.rss(tweets, info, opts);
}
/**
* Returns a RSS feed of Tweet objects.
* @param {Object[]} tweets - An array of Tweet object.
* @param {Object} info - A RSS information.
* @param {Options} opts - Options.
* @returns {string} A RSS feed.
* @see {@link https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object|Tweet object — Twitter Developers}
*/
rss(tweets, info, opts = {}) {
const filtered_tweets = this._filter_tweets(tweets, opts.filters);
const rss = this._make_rss(info, filtered_tweets, opts.formatter);
return rss;
}
_make_rss(info, tweets, formatter) {
const feed = new Feed({
title: info.channel.title,
description: info.channel.description,
link: info.channel.link
});
if (!formatter) {
formatter = TwitterRSSFeed.standard_formatter();
}
tweets.forEach(tweet => {
const item = formatter(tweet);
feed.addItem(item);
});
return feed.rss2();
}
_filter_tweets(tweets, filters) {
if (!filters) {
return tweets;
}
for (let filter of filters) {
tweets = filter(tweets);
}
return tweets;
}
/**
* Returns a standard formatter function.
* @returns {function} A standard formatter function
*/
static standard_formatter() {
return function(tweet) {
const get_text = function(tweet) {
// Use full_text for more than 140 characters.
//
// ref. Tweet updates — Twitter Developers
// https://developer.twitter.com/en/docs/tweets/tweet-updates.html
if (tweet.retweeted_status) {
let text;
if (tweet.retweeted_status.full_text) {
text = tweet.retweeted_status.full_text;
} else {
text = tweet.retweeted_status.text;
}
const screen_name = tweet.retweeted_status.user.screen_name;
return 'RT @' + screen_name + ': ' + text;
} else {
return tweet.full_text ? tweet.full_text : tweet.text;
}
};
const text = get_text(tweet);
return {
title: tweet.user.name + ' (@' + tweet.user.screen_name + ') on Twitter: "' + text + '" / Twitter',
description: text,
link: 'https://twitter.com/' + tweet.user.screen_name + '/status/' + tweet.id_str,
date: new Date(tweet.created_at)
};
};
}
/**
* Returns a filter function that extracts only the public user's tweets.
* @returns {function} A filter function that extracts only the public user's tweets
*/
static public_users_filter() {
return function(tweets) {
return tweets.filter(tweet => !tweet.user.protected);
};
}
}
module.exports = TwitterRSSFeed;