Something went wrong. Try again.
A Twitter-Bot that is going to increase your followers.
Something went wrong. Try again.
19 kB · 646 lines
JavaScript
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647var assert = require('assert') , http = require('http') , EventEmitter = require('events').EventEmitter , rewire = require('rewire') , sinon = require('sinon') , Twit = require('../lib/twitter') , config1 = require('../config1') , config2 = require('../config2') , colors = require('colors') , helpers = require('./helpers') , util = require('util') , zlib = require('zlib') , async = require('async') , restTest = require('./rest');
/** * Stop the stream and check the tweet we got back. * Call @done on completion. * * @param {object} stream object returned by twit.stream() * @param {Function} done completion callback */exports.checkStream = function (stream, done) { stream.on('connected', function () { console.log('\nconnected'.grey) });
stream.once('tweet', function (tweet) { stream.stop() assert.ok(tweet) assert.equal('string', typeof tweet.text) assert.equal('string', typeof tweet.id_str)
console.log(('\ntweet: '+tweet.text).grey)
done() });
stream.on('reconnecting', function (req, res, connectInterval) { console.log('Got disconnected. Scheduling reconnect! statusCode:', res.statusCode, 'connectInterval', connectInterval) });
stream.on('error', function (err) { console.log('Stream emitted an error', err) return done(err) })}
/** * Check the stream state is correctly set for a stopped stream. * * @param {object} stream object returned by twit.stream() */exports.checkStreamStopState = function (stream) { assert.strictEqual(stream._connectInterval, 0) assert.strictEqual(stream._usedFirstReconnect, false) assert.strictEqual(stream._scheduledReconnect, undefined) assert.strictEqual(stream._stallAbortTimeout, undefined)}
describe('Streaming API', function () {
it('statuses/sample', function (done) { var twit = new Twit(config1); var stream = twit.stream('statuses/sample')
exports.checkStream(stream, done) })
it('statuses/filter using `track`', function (done) { this.timeout(120000) var twit = new Twit(config2); var stream = twit.stream('statuses/filter', { track: 'fun' })
exports.checkStream(stream, done) })
it('statuses/filter using `locations` string', function (done) { var twit = new Twit(config1); var world = '-180,-90,180,90'; var stream = twit.stream('statuses/filter', { locations: world })
exports.checkStream(stream, done) })
it('statuses/filter using `locations` array for San Francisco and New York', function (done) { var twit = new Twit(config2); var params = { locations: [ '-122.75', '36.8', '121.75', '37.8', '-74', '40', '73', '41' ] }
var stream = twit.stream('statuses/filter', params)
exports.checkStream(stream, done) })
it('statuses/filter using `track` array', function (done) { var twit = new Twit(config1); var params = { track: [ 'twitter', ':)', 'fun' ] }
var stream = twit.stream('statuses/filter', params)
exports.checkStream(stream, done) })
it('statuses/filter using `track` and `language`', function (done) { var twit = new Twit(config1); var params = { track: [ 'twitter', '#apple', 'google', 'twitter', 'facebook', 'happy', 'party', ':)' ], language: 'en' }
var stream = twit.stream('statuses/filter', params)
exports.checkStream(stream, done) })
it('stopping & restarting the stream works', function (done) { var twit = new Twit(config2); var stream = twit.stream('statuses/sample')
//stop the stream after 2 seconds setTimeout(function () { stream.stop()
exports.checkStreamStopState(stream)
console.log('\nstopped stream') }, 2000)
//after 3 seconds, start the stream, and stop after 'connect' setTimeout(function () { stream.once('connected', function (req) { console.log('\nrestarted stream') stream.stop()
exports.checkStreamStopState(stream)
console.log('\nstopped stream') done() })
//restart the stream stream.start() }, 3000) })
it('stopping & restarting stream emits to previously assigned callbacks', function (done) { var twit = new Twit(config1); var stream = twit.stream('statuses/sample')
var started = false var numTweets = 0 stream.on('tweet', function (tweet) { process.stdout.write('.') if (!started) { started = true numTweets++ console.log('received tweet', numTweets)
console.log('stopping stream') stream.stop()
exports.checkStreamStopState(stream)
// we've successfully received a new tweet after restarting, test successful if (numTweets === 2) { done() } else { started = false console.log('restarting stream')
setTimeout(function () { stream.start() }, 1000) } } })
stream.on('limit', function (limitMsg) { console.log('limit', limitMsg) })
stream.on('disconnect', function (disconnMsg) { console.log('disconnect', disconnMsg) })
stream.on('reconnect', function (req, res, ival) { console.log('reconnect. statusCode:', res.statusCode, 'interval:', ival) })
stream.on('connect', function (req) { console.log('connect') })
})})
describe('streaming API direct message events', function () { var senderScreenName; var receiverScreenName; var twitSender; var twitReceiver;
// before we send direct messages the user receiving the DM // has to follow the sender. Make this so. before(function (done) { twitSender = new Twit(config1); twitReceiver = new Twit(config2);
// get sender/receiver names in parallel, then make the receiver follow the sender async.parallel({ // get sender screen name and set it for tests to use getSenderScreenName: function (parNext) { console.log('getting sender user screen_name')
twitSender.get('account/verify_credentials', { twit_options: { retry: true } }, function (err, reply) { assert(!err, err)
assert(reply) assert(reply.screen_name)
senderScreenName = reply.screen_name
return parNext() }) }, // get receiver screen name and set it for tests to use getReceiverScreenName: function (parNext) { console.log('getting receiver user screen_name') twitReceiver.get('account/verify_credentials', { twit_options: { retry: true } }, function (err, reply) { assert(!err, err)
assert(reply) assert(reply.screen_name)
receiverScreenName = reply.screen_name
return parNext() }) } }, function (err) { assert(!err, err)
var followParams = { screen_name: senderScreenName } console.log('making receiver user follow the sender user') // make receiver follow sender twitReceiver.post('friendships/create', followParams, function (err, reply) { assert(!err, err) assert(reply.following)
done() }) }) })
it('user_stream `direct_message` event', function (done) { // User A follows User B // User A connects to their user stream // User B posts a DM to User A // User A receives it in their user stream this.timeout(0);
// build out DM params function makeDmParams () { return { screen_name: receiverScreenName, text: helpers.generateRandomString(10) + ' direct message streaming event test! :-) ' + helpers.generateRandomString(20), twit_options: { retry: true } } }
var dmIdsReceived = [] var dmIdsSent = [] var sentDmFound = false
// start listening for user stream events var receiverStream = twitReceiver.stream('user')
console.log('\nlistening for DMs') // listen for direct_message event and check DM once it's received receiverStream.on('direct_message', function (directMsg) { if (sentDmFound) { // don't call `done` more than once return }
console.log('got DM event. id:', directMsg.direct_message.id_str) restTest.checkDm(directMsg.direct_message) dmIdsReceived.push(directMsg.direct_message.id_str)
// make sure one of the DMs sent was found // (we can send multiple DMs if our stream has to reconnect) sentDmFound = dmIdsSent.some(function (dmId) { return dmId == directMsg.direct_message.id_str })
if (!sentDmFound) { console.log('this DM doesnt match our test DMs - still waiting for a matching one.') console.log('dmIdsSent', dmIdsSent) return }
receiverStream.stop() return done() })
var lastTimeSent = 0 var msToWait = 0 var postDmInterval = null
receiverStream.on('connected', function () { var dmParams = makeDmParams()
console.log('sending a new DM:', dmParams.text) twitSender.post('direct_messages/new', dmParams, function (err, reply) { assert(!err, err) assert(reply) restTest.checkDm(reply) assert(reply.id_str) // we will check this dm against the reply recieved in the message event dmIdsSent.push(reply.id_str)
console.log('successfully posted DM:', reply.text, reply.id_str) if (dmIdsReceived.indexOf(reply.id_str) !== -1) { // our response to the DM posting lost the race against the direct_message // listener (we already got the event). So we can finish the test. done() } }) })
after(function (done) { console.log('cleaning up DMs:', dmIdsSent) // delete the DMs we posted var deleteDms = dmIdsSent.map(function (dmId) { return function (next) { assert.equal(typeof dmId, 'string') console.log('\ndeleting DM', dmId) var params = { id: dmId, twit_options: { retry: true } } twitSender.post('direct_messages/destroy', params, function (err, reply) { assert(!err, err) restTest.checkDm(reply) assert.equal(reply.id, dmId) return next() }) } }) async.parallel(deleteDms, done) }) })})
describe('streaming API friends preamble', function () { it('returns an array of strings if stringify_friend_ids is true', function (done) { var twit = new Twit(config1); var stream = twit.stream('user', { stringify_friend_ids: true }); stream.on('friends', function (friendsObj) { assert(friendsObj) assert(friendsObj.friends_str) if (friendsObj.friends_str.length) { assert.equal(typeof friendsObj.friends_str[0], 'string') } else { console.log('\nEmpty friends preamble:', friendsObj, '. Make some friends on Twitter! ^_^') } done() }) })})
describe('streaming API bad request', function (done) { it('emits an error for a 401 response', function (done) { var badCredentials = { consumer_key: 'a' , consumer_secret: 'b' , access_token: 'c' , access_token_secret: 'd' }
var twit = new Twit(badCredentials);
var stream = twit.stream('statuses/filter', { track : ['foo'] });
stream.on('error', function (err) { assert.equal(err.statusCode, 401) assert(err.twitterReply)
return done() }) })})
describe('streaming API `messages` event', function (done) { var request = require('request'); var originalPost = request.post; var RewiredTwit = rewire('../lib/twitter'); var RewiredStreamingApiConnection = rewire('../lib/streaming-api-connection'); var revertParser, revertTwit;
var MockParser = function () { var self = this; EventEmitter.call(self); process.nextTick(function () { self.emit('element', {scrub_geo: 'bar'}) self.emit('element', {limit: 'buzz'}) }); } util.inherits(MockParser, EventEmitter);
before(function () { revertTwit = RewiredTwit.__set__('StreamingAPIConnection', RewiredStreamingApiConnection); revertParser = RewiredStreamingApiConnection.__set__('Parser', MockParser);
request.post = function () { return new helpers.FakeRequest() } })
after(function () { request.post = originalPost; revertTwit(); revertParser(); })
it('is returned for 2 different event types', function (done) { var twit = new RewiredTwit(config1); var stream = twit.stream('statuses/sample'); var gotScrubGeo = false; var gotLimit = false; var numMessages = 0;
var maybeDone = function () { if (gotScrubGeo && gotLimit && numMessages == 2) { done() } }
stream.on('limit', function () { gotLimit = true; maybeDone(); }); stream.on('scrub_geo', function () { gotScrubGeo = true; maybeDone(); })
stream.on('message', function (msg) { numMessages++; maybeDone(); }) })})
describe('streaming reconnect', function (done) { it('correctly implements connection closing backoff', function (done) { var stubPost = function () { var fakeRequest = new helpers.FakeRequest() process.nextTick(function () { fakeRequest.emit('close') }) return fakeRequest }
var request = require('request') var stubPost = sinon.stub(request, 'post', stubPost)
var twit = new Twit(config1); var stream = twit.stream('statuses/filter', { track: [ 'fun', 'yolo']});
var reconnects = [0, 250, 500, 750] var reconnectCount = -1
var testDone = false
stream.on('reconnect', function () { if (testDone) { return } reconnectCount += 1 var expectedInterval = reconnects[reconnectCount]
// make sure our connect interval is correct assert.equal(stream._connectInterval, expectedInterval);
// simulate immediate reconnect by forcing a new connection (`self._connectInterval` parameter unchanged) stream._startPersistentConnection();
if (reconnectCount === reconnects.length -1) { // restore request.post stubPost.restore() testDone = true return done(); } }); });
it('correctly implements 420 backoff', function (done) { var stubPost = function () { var fakeRequest = new helpers.FakeRequest() process.nextTick(function () { var fakeResponse = new helpers.FakeResponse(420) fakeRequest.emit('response', fakeResponse) fakeRequest.emit('close') }) return fakeRequest }
var request = require('request') var stubPost = sinon.stub(request, 'post', stubPost)
var twit = new Twit(config1); var stream = twit.stream('statuses/filter', { track: [ 'fun', 'yolo']});
var reconnects = [60000, 120000, 240000, 480000] var reconnectCount = -1 var testComplete = false
stream.on('reconnect', function (req, res, connectInterval) { if (testComplete) { // prevent race between last connection attempt firing a reconnect and us validating the final // reconnect value in `reconnects` return }
reconnectCount += 1 var expectedInterval = reconnects[reconnectCount]
// make sure our connect interval is correct assert.equal(stream._connectInterval, connectInterval); assert.equal(stream._connectInterval, expectedInterval); // simulate immediate reconnect by forcing a new connection (`self._connectInterval` parameter unchanged) stream._startPersistentConnection();
if (reconnectCount === reconnects.length -1) { // restore request.post stubPost.restore() testComplete = true return done(); } }); });});
describe('Streaming API disconnect message', function (done) { it.skip('results in stopping the stream', function (done) { var stubPost = function () { var fakeRequest = new helpers.FakeRequest() process.nextTick(function () { var body = zlib.gzipSync(JSON.stringify({disconnect: true}) + '\r\n') var fakeResponse = new helpers.FakeResponse(200, body) fakeRequest.emit('response', fakeResponse); fakeResponse.emit('close') }); return fakeRequest }
var request = require('request') var origRequest = request.post var stubs = sinon.collection stubs.stub(request, 'post', stubPost)
var twit = new Twit(config1); var stream = twit.stream('statuses/filter', { track: ['fun']});
stream.on('disconnect', function (disconnMsg) { stream.stop(); // restore stub request.post = origRequest done(); }) })});
describe.skip('Streaming API Connection limit exceeded message', function (done) { it('results in an `error` event containing the message', function (done) { var errMsg = 'Exceeded connection limit for user';
var stubPost = function () { var fakeRequest = new helpers.FakeRequest(); process.nextTick(function () { var body = zlib.gzipSync(errMsg + '\r\n'); var fakeResponse = new helpers.FakeResponse(200, body); fakeRequest.emit('response', fakeResponse); fakeResponse.emit('close'); }); return fakeRequest }
var request = require('request'); var origRequest = request.post; var stubs = sinon.collection; stubs.stub(request, 'post', stubPost);
var twit = new Twit(config1); var stream = twit.stream('statuses/filter');
stream.on('error', function (err) { assert(err.toString().indexOf(errMsg) !== -1, 'Unexpected error msg:' + errMsg + '.');; stream.stop(); // restore stub request.post = origRequest; done(); }) })})
describe('Streaming API connection management', function () { it('.stop() works in all states', function (done) { var stubPost = function () { var fakeRequest = new helpers.FakeRequest(); process.nextTick(function () { var body = zlib.gzipSync('Foobar\r\n'); var fakeResponse = new helpers.FakeResponse(200, body); fakeRequest.emit('response', fakeResponse); }); return fakeRequest }
var request = require('request'); var origRequest = request.post; var stubs = sinon.collection; stubs.stub(request, 'post', stubPost);
var twit = new Twit(config1);
var stream = twit.stream('statuses/sample'); stream.stop(); console.log('\nStopped. Restarting..'); stream.start(); stream.once('connect', function(request) { console.log('Stream emitted `connect`. Stopping & starting stream..') stream.stop();
stream.once('connected', function () { console.log('Stream emitted `connected`. Stopping stream.'); stream.stop();
stubs.restore(); done(); }); stream.start(); }); })})