@sugarcube/plugin-twitter
Version:
Query the Twitter API as a SugarCube plugin.
139 lines (120 loc) • 4.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.search = exports.friends = exports.followers = exports.feed = exports.tweets = exports.parseApiErrors = void 0;
var _fp = require("lodash/fp");
var _dashp = require("dashp");
var _core = require("@sugarcube/core");
var _utils = require("./utils");
var _entities = require("./entities");
// The requests within a 15 minutes window in milliseconds.
const rateLimit = requests => 15 * 60 / requests * 1000; // FIXME: Refactor to uncouple everything from the logger.
const apiErrors = (0, _fp.curry)((log, user, e) => {
if (/401/.test(e.message)) {
log.warn(`Failed to fetch ${user}: ${e.message}`);
return [];
}
if (e[0] && e[0].code === 34) {
log.warn(`Failed to fetch ${user}: ${e[0].message}`);
return [];
}
throw e;
});
const parseApiErrors = e => {
if (e[0] && e[0].message != null) return e[0].message;
if (e[0]) return JSON.stringify(e[0]);
if (e.message != null) return e.message;
return JSON.stringify(e);
};
exports.parseApiErrors = parseApiErrors;
const tweets = (0, _fp.curry)((cfg, tweetIds) => {
const delay = rateLimit(300);
const op = (0, _utils.throttle)(delay, (0, _utils.request)(cfg, "statuses/lookup.json"));
const params = {
id: tweetIds.join(","),
map: true,
tweet_mode: "extended"
};
return (0, _dashp.flowP)([op], params);
});
exports.tweets = tweets;
const feed = (0, _fp.curry)((cfg, user) => {
const count = cfg.twitter.tweet_count;
const retweets = cfg.twitter.tweet_count;
const delay = rateLimit(1500);
const op = (0, _utils.throttle)(delay, (0, _utils.request)(cfg, "statuses/user_timeline.json"));
const params = {
count,
include_rts: retweets,
[(0, _fp.isNaN)(Number(user)) ? "screen_name" : "user_id"]: user
};
const decisions = (0, _core.createFeatureDecisions)();
const transform = decisions.canNcube() ? _entities.tweetNcube : _entities.tweetLegacy;
return (0, _dashp.flowP)([op, (0, _fp.map)(transform)], params);
});
exports.feed = feed;
const followers = (cfg, log, users) => {
const recurseDepth = cfg.twitter.recurse_depth;
const delay = rateLimit(15);
const op = (0, _utils.recurse)(recurseDepth, "screen_name", (0, _utils.cursorify)((0, _utils.throttle)(delay, (0, _utils.request)(cfg, "followers/list.json"))));
return (0, _dashp.foldP)((memo, user) => {
const params = {
screen_name: user,
count: 200,
include_user_entities: true
};
return (0, _dashp.flowP)([op, (0, _dashp.tapP)(rs => log.info(`Fetched ${(0, _fp.size)(rs)} followers of ${user}.`)), (0, _dashp.caughtP)(apiErrors(log, user)), (0, _fp.flow)([_entities.followersTransform, (0, _fp.concat)(memo)])], params);
}, [], users);
};
exports.followers = followers;
const friends = (cfg, log, users) => {
const recurseDepth = cfg.twitter.recurse_depth;
const delay = rateLimit(15);
const op = (0, _utils.recurse)(recurseDepth, "screen_name", (0, _utils.cursorify)((0, _utils.throttle)(delay, (0, _utils.request)(cfg, "friends/list.json"))));
return (0, _dashp.foldP)((memo, user) => {
const params = {
screen_name: user,
count: 200,
include_user_entities: true
};
return (0, _dashp.flowP)([op, (0, _dashp.tapP)(rs => log.info(`Fetched ${(0, _fp.size)(rs)} friends of ${user}.`)), (0, _dashp.caughtP)(apiErrors(log, user)), (0, _fp.flow)([_entities.friendsTransform, (0, _fp.concat)(memo)])], params);
}, [], users);
};
exports.friends = friends;
const search = (0, _fp.curry)((cfg, log, queries) => {
const modifiers = ["twitter.language", "twitter.geocode"].reduce((memo, key) => {
switch (key) {
case "twitter.language":
return (0, _fp.get)(key, cfg) ? (0, _fp.merge)(memo, {
lang: (0, _fp.get)(key, cfg)
}) : memo;
case "twitter.geocode":
return (0, _fp.get)(key, cfg) ? (0, _fp.merge)(memo, {
geocode: (0, _fp.get)(key, cfg)
}) : memo;
default:
return memo;
}
}, {});
const delay = rateLimit(180);
const op = (0, _utils.throttle)(delay, (0, _utils.request)(cfg, "search/tweets.json"));
return (0, _dashp.foldP)((memo, query) => {
const q = (0, _fp.flow)([(0, _fp.split)(" "), (0, _fp.map)(encodeURIComponent), (0, _fp.join)("+")])(query);
const params = (0, _fp.merge)({
count: 100,
q
}, modifiers);
return (0, _dashp.flowP)([op, (0, _dashp.tapP)(rs => log.info(`Fetched ${(0, _fp.size)(rs.statuses)} tweets for the term ${query}`)), (0, _fp.flow)([(0, _fp.property)("statuses"), _entities.searchTransform, (0, _fp.map)((0, _fp.merge)({
query
})), (0, _fp.concat)(memo)])], params);
}, [], queries);
});
exports.search = search;
var _default = {
feed,
followers,
friends,
search
};
exports.default = _default;