diff --git a/README.md b/README.md index daa5b64..96c6b8c 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,16 @@ Result: "400x400": "https://pbs.twimg.com/profile_images/776738772759277569/hfaM5zhA_400x400.jpg" } ``` + +## Requirements + +[Redis](https://redis.io/) is required if caching is wanted. You will also need Twitter credentials that can be created here: [https://apps.twitter.com/](https://apps.twitter.com/) + +Environment Variables: +``` +TWITTER_CONSUMER_KEY= +TWITTER_CONSUMER_SECRET= +TWITTER_ACCESS_TOKEN_KEY= +TWITTER_ACCESS_TOKEN_SECRET= +REDIS_URL= +``` diff --git a/cache.js b/cache.js new file mode 100644 index 0000000..b02dae8 --- /dev/null +++ b/cache.js @@ -0,0 +1,33 @@ +let redis, redisAvailable; + +if (process.env.REDIS_URL) { + redis = require("redis").createClient({ url: process.env.REDIS_URL }); + + redis.on('error', err => { + console.log(err); + redisAvailable = false; + }); + + redis.on('ready', () => { + console.log('Redis ready'); + redisAvailable = true; + }); +} + +const get = async key => { + if (!redisAvailable) return; + return new Promise(resolve => { + redis.get(key, (err, data) => { + return resolve(data ? JSON.parse(data) : data); + }); + }); +}; + +const save = (key, data) => { + if (!redisAvailable) return; + redis.set(key, JSON.stringify(data), 'EX', 86400); +}; + +module.exports = { + get, save +}; diff --git a/package.json b/package.json index c798d32..3447016 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "license": "ISC", "dependencies": { "dotenv": "^4.0.0", + "redis": "^2.8.0", "request": "^2.83.0", "restify": "^6.3.4", "restify-clients": "^1.5.2", diff --git a/twitter.js b/twitter.js index d2a7828..06a3f32 100644 --- a/twitter.js +++ b/twitter.js @@ -1,3 +1,4 @@ +const cache = require('./cache'); const Twitter = require('twitter'); const twitter = new Twitter({ consumer_key: process.env.TWITTER_CONSUMER_KEY, @@ -12,9 +13,7 @@ const defaultProfile = { const cleanHandle = handle => (handle[0] === '@') ? handle.slice(1) : handle; -const getProfileURLs = async handle => { - handle = cleanHandle(handle); - +const fetchFromTwitter = async handle => { return new Promise((resolve, reject) => { twitter.get('users/show', { user_id: handle, @@ -32,9 +31,16 @@ const getProfileURLs = async handle => { '200x200': user.profile_image_url_https.replace('_normal.', '_200x200.'), '400x400': user.profile_image_url_https.replace('_normal.', '_400x400.') } + // Try and mitigate hitting rate limit + cache.save(handle, profileURLs); return resolve(profileURLs); }); }); +} + +const getProfileURLs = async handle => { + handle = cleanHandle(handle); + return await cache.get(handle)|| await fetchFromTwitter(handle); }; module.exports = {