Comparing version 0.1.2 to 0.1.3
'use strict'; | ||
var credentials = require('./configure/local_configure.js'); | ||
var EventEmitter = require('events').EventEmitter; | ||
@@ -9,3 +8,2 @@ var objectAssign = require('object-assign'); | ||
/** | ||
@@ -32,2 +30,3 @@ * Initialize a new Birdwatch | ||
this.logReports = this.options.logReports || false; | ||
this.useTestData = this.options.useTestData || false; | ||
} | ||
@@ -34,0 +33,0 @@ |
{ | ||
"name": "birdwatch", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "Monitor and cache specific twitter feeds", | ||
"main": "index.js", | ||
"engines": { | ||
"node": ">=0.10.0" | ||
}, | ||
"scripts": { | ||
@@ -34,2 +37,3 @@ "test": "mocha" | ||
"each-async": "~1.1.1", | ||
"fs-access": "^1.0.0", | ||
"is-regexp": "~1.0.0", | ||
@@ -36,0 +40,0 @@ "native-promise-only": "~0.8.0-a", |
@@ -5,2 +5,4 @@ # Birdwatch :baby_chick::watch: | ||
[![Build Status](https://travis-ci.org/radiovisual/birdwatch.svg?branch=master)](https://travis-ci.org/radiovisual/birdwatch) | ||
Birdwatch will help you grab tweets from specific twitter accounts, and cache the tweets on-disk and in-memory on your server, | ||
@@ -71,2 +73,9 @@ thus avoiding any request limits set by the Twitter API, and giving you more control over the data that is saved. | ||
##### useTestData | ||
Type: `boolean`<br> | ||
Default: `false` | ||
Use the test tweet data instead of making a network requests. Useful for testing/debugging. | ||
# ![birdwatch](media/screenshot-v.0.0.1.png) | ||
@@ -73,0 +82,0 @@ |
@@ -9,11 +9,2 @@ 'use strict'; | ||
/* | ||
* TODO: Add more tests | ||
* | ||
* removes filtered tweets | ||
* removes retweets when remove_retweets:true | ||
* fails if filter_tags isn't valid regex | ||
* tweets are sorted | ||
* | ||
* */ | ||
@@ -94,2 +85,55 @@ describe('Birdwatch', function() { | ||
it('should fail when filter_tweets is not a valid regex', function(){ | ||
var birdwatch = new Birdwatch() | ||
.feed('MichaelWuergler', {filter_tags: ''}); | ||
birdwatch.start(function(err){ | ||
assertEqual(err.message.slice(0,38) , "You must supply a regex to filter_tags"); | ||
}); | ||
}); | ||
/* | ||
// Currently, we can't test filter_tags | ||
// See: https://github.com/radiovisual/birdwatch/issues/4 | ||
it('should return only filtered tweets with option `filter_tags`', function(){ | ||
var birdwatch = new Birdwatch({useTestData:true}) | ||
.feed('Twitterer', {filter_tags:/#09/} ); | ||
birdwatch.start(function(err){}); | ||
return birdwatch.getCachedTweets().then(function(tweetdata){ | ||
assert(tweetdata.length === 1); | ||
}); | ||
}); | ||
*/ | ||
/* | ||
// Currently, we can't test remove_retweets | ||
// See: https://github.com/radiovisual/birdwatch/issues/4 | ||
it('should remove retweets with option `remove_retweets`', function(){ | ||
var birdwatch = new Birdwatch({useTestData:true}) | ||
.feed('Twitterer', {remove_retweets:true} ); | ||
birdwatch.start(function(err){}); | ||
return birdwatch.getCachedTweets().then(function(tweetdata){ | ||
assert(tweetdata.length === 5); | ||
}); | ||
}); | ||
*/ | ||
/* | ||
// Currently, we can't test sorting | ||
// See: https://github.com/radiovisual/birdwatch/issues/4 | ||
it('should sort the tweets', function(){ | ||
}); | ||
*/ | ||
}); | ||
@@ -96,0 +140,0 @@ |
175
utils.js
'use strict'; | ||
var credentials = require('./configure/local_configure'); | ||
var test_tweets = require("./test/test_tweets.js"); | ||
var underscore = require('underscore'); | ||
var eachAsync = require('each-async'); | ||
var isRegexp = require('is-regexp'); | ||
var fsAccess = require('fs-access'); | ||
var report = require("./report"); | ||
@@ -12,13 +13,22 @@ var chalk = require('chalk'); | ||
/* | ||
* The credentials file to use for Twitter API authentication. | ||
* This will be dynamically set in setupCredentials() | ||
* | ||
* @type {object} - The json object from local_configure.js | ||
*/ | ||
var credentials; | ||
/* | ||
* The Twit object used to access the Twitter API. | ||
* This value will be dynamically assigned in setupCredentials() | ||
*/ | ||
var twit; | ||
// Polyfill Promise | ||
require("native-promise-only"); | ||
// Setup the Twit object | ||
var T = new Twit({ | ||
consumer_key: credentials.consumer_key, | ||
consumer_secret: credentials.consumer_secret, | ||
access_token: credentials.access_token, | ||
access_token_secret: credentials.access_token_secret | ||
}); | ||
/** | ||
@@ -29,3 +39,3 @@ * Temporarily hold the data that is returned from the promises | ||
* and on-disk caches. When the birdwatching cycle is over, this is emptied | ||
* and waits for the next birdwatching cycle to begin. | ||
* and waits for the next birdwatching cycle to begin. | ||
* @type {Array} | ||
@@ -43,5 +53,5 @@ */ | ||
*/ | ||
var in_memory_cache = []; | ||
/** | ||
@@ -57,2 +67,4 @@ * Process the feeds by starting the promise chain. | ||
var self = this; | ||
// Let's go Birdwatching! | ||
@@ -68,14 +80,16 @@ if(this.logReports){ | ||
var p = getTwitterData(screenname, bwoptions).then(function(tweetdata){ | ||
return filterTweets(tweetdata, screenname, feedoptions, bwoptions); | ||
var p = self.setupCredentials(bwoptions).then(function(testmode){ | ||
return self.getTwitterData(screenname, bwoptions, testmode); | ||
}).then(function(tweetdata){ | ||
return self.filterTweets(tweetdata, screenname, feedoptions, bwoptions); | ||
}).then(function(){ | ||
return processCache(feeds); | ||
return self.processCache(feeds); | ||
}).then(function(processedCacheObjects){ | ||
return sortTweets(processedCacheObjects); | ||
return self.sortTweets(processedCacheObjects); | ||
}).then(function(sorteddataobjects){ | ||
saveToCache(sorteddataobjects, bwoptions); | ||
self.saveToCache(sorteddataobjects, bwoptions); | ||
}); | ||
p.catch(function(error){ | ||
report.logError(error, false); | ||
p.catch(function(err){ | ||
report.logError(err, false); | ||
}); | ||
@@ -98,3 +112,55 @@ | ||
/** | ||
* Setup the credentials for the app. | ||
* | ||
* Do we have real credentials? Or are we testing? | ||
* Travis CI and Mocha shouldn't need valid credentials to test with, | ||
* so we use this promise to determine if we are in test mode or not. | ||
* This promise will return `true` if we are in test mode | ||
* (i.e, the local_configure is not set or we want to use the test_tweet data), | ||
* and `false` if we in are in normal app mode. | ||
* This function also sets var credentials when in normal app mode. | ||
* | ||
* @param {object} bwoptions - the birdwatch configuration options | ||
* @returns {Promise} | ||
*/ | ||
exports.setupCredentials = function(bwoptions){ | ||
return new Promise(function(resolve, reject){ | ||
if( bwoptions.useTestData ){ | ||
resolve(true); | ||
} else { | ||
fsAccess('./configure/local_configure.js', function(err){ | ||
if(err) { | ||
resolve(true); | ||
} else { | ||
credentials = require("./configure/local_configure.js"); | ||
twit = new Twit({ | ||
consumer_key: credentials.consumer_key, | ||
consumer_secret: credentials.consumer_secret, | ||
access_token: credentials.access_token, | ||
access_token_secret: credentials.access_token_secret | ||
}); | ||
resolve(false); | ||
} | ||
}); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Request twitter data from the Twitter API | ||
@@ -104,6 +170,7 @@ * | ||
* @param {Object} bwoptions - birdwatch configuration options | ||
* @param {Boolean} testmode - are we in test mode? If yes, serve test tweet data | ||
* @returns {Promise} | ||
*/ | ||
function getTwitterData(screenname, bwoptions){ | ||
exports.getTwitterData = function(screenname, bwoptions, testmode){ | ||
@@ -116,12 +183,20 @@ if(bwoptions.logReports){ | ||
T.get("statuses/user_timeline", {screen_name: screenname, count:200, include_rts:true}, function(err, reply){ | ||
if (err){ | ||
reject(Error(err)); | ||
} else { | ||
resolve(reply); | ||
} | ||
}); | ||
// Send test_tweets in testing environments | ||
if(testmode){ | ||
resolve(test_tweets); | ||
} else { | ||
twit.get("statuses/user_timeline", {screen_name: screenname, count:200, include_rts:true}, function(err, reply){ | ||
if (err){ | ||
reject(err); | ||
} else { | ||
resolve(reply); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
}; | ||
@@ -139,3 +214,3 @@ | ||
function filterTweets(tweetdata, screenname, feedoptions, bwoptions){ | ||
exports.filterTweets = function(tweetdata, screenname, feedoptions, bwoptions){ | ||
@@ -145,7 +220,8 @@ return new Promise(function (resolve, reject){ | ||
// return the unfiltered tweetdata if no filters are requested | ||
if( !feedoptions.hasOwnProperty("filter_tags") ){ | ||
// and if no need to remove the retweets | ||
if( !feedoptions.filter_tags && !feedoptions.remove_retweets){ | ||
returned_tweets.push(tweetdata); | ||
resolve(); | ||
// otherwise, return the filtered tweets | ||
// otherwise, let's filter the tweets | ||
} else { | ||
@@ -159,19 +235,15 @@ | ||
// is options.filter_tags a string or regex? | ||
// is options.filter_tags a regex? | ||
var isRegEx = isRegexp(feedoptions.filter_tags); | ||
var search_tags; | ||
if(!isRegEx){ | ||
if(feedoptions.filter_tags && !isRegEx){ | ||
reject(new Error(["You must supply a regex to filter_tags\nCheck your syntax on: "+feedoptions.filter_tags])); | ||
} | ||
var remove_retweets = feedoptions.remove_retweets; | ||
for (var i in tweetdata){ | ||
var tweet = tweetdata[i]; | ||
var isRetweet = tweet.hasOwnProperty("retweeted_status"); | ||
var isRetweet = tweet.retweeted_status; | ||
if(isRetweet && remove_retweets){ | ||
if(isRetweet && feedoptions.remove_retweets){ | ||
continue; | ||
@@ -182,4 +254,6 @@ } | ||
if(feedoptions.filter_tags.test(tweet.text)){ | ||
if(feedoptions.filter_tags && feedoptions.filter_tags.test(tweet.text)){ | ||
matches.push(tweet); | ||
} else if (!feedoptions.filter_tags) { | ||
matches.push(tweet); | ||
} | ||
@@ -194,4 +268,5 @@ } | ||
} | ||
}; | ||
/** | ||
@@ -204,3 +279,3 @@ * Break the data objects out of the returned_tweets array | ||
var processCache = function(feeds){ | ||
exports.processCache = function(feeds){ | ||
@@ -235,3 +310,3 @@ return new Promise(function(resolve, reject){ | ||
var sortTweets = function(tweetObjects, options){ | ||
exports.sortTweets = function(tweetObjects, options){ | ||
@@ -257,7 +332,9 @@ return new Promise(function(resolve, reject){ | ||
function saveToCache(dataToSave, bwoptions){ | ||
exports.saveToCache = function(dataToSave, bwoptions){ | ||
fs.writeFile('./cache/cached_tweets.json', JSON.stringify(dataToSave), function (err) { | ||
in_memory_cache.push(JSON.stringify(dataToSave)); | ||
fs.writeFileSync('./cache/cached_tweets.json', JSON.stringify(dataToSave), {flag:'w'}, function (err) { | ||
if (err) { | ||
report.logError(["Error saving cached_tweets.json in processCache()", error]); | ||
report.logError(["Error saving cached_tweets.json in saveToCache()", err]); | ||
} else { | ||
@@ -270,6 +347,5 @@ | ||
returned_tweets = []; | ||
in_memory_cache.push(JSON.stringify(dataToSave)); | ||
} | ||
}); | ||
} | ||
}; | ||
@@ -309,2 +385,8 @@ | ||
// TODO: make the process wait until in_memory cache is available. | ||
// This will allow the process to be properly tested. | ||
// Otherwise the on-disk cache file is always served in the test scenarios, | ||
// and we can't test sorting, and removing retweets, etc if this is the case | ||
// See https://github.com/radiovisual/birdwatch/issues/4 | ||
// Maybe this will help: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use | ||
if (in_memory_cache.length > 0){ | ||
@@ -326,2 +408,3 @@ | ||
}); | ||
}; | ||
@@ -328,0 +411,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34209
13
528
160
8
3
+ Addedfs-access@^1.0.0
+ Addedfs-access@1.0.1(transitive)
+ Addednull-check@1.0.0(transitive)