├── .gitignore ├── README.md ├── index.js ├── lib └── user-stream.js ├── package.json └── test └── stream.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .project 3 | .settings 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | user-stream 2 | ============= 3 | [![NPM module](https://badge.fury.io/js/user-stream.png)](https://badge.fury.io/js/user-stream) 4 | 5 | Simple Node.js Twitter (API 1.1) user stream client (https://dev.twitter.com/docs/streaming-apis/streams/user) 6 | 7 | [![NPM](https://nodei.co/npm/user-stream.png?stars&downloads)](https://nodei.co/npm/user-stream/) 8 | 9 | Install 10 | ------- 11 | ```npm install user-stream``` 12 | 13 | Usage 14 | ------- 15 | ```javascript 16 | var Stream = require('user-stream'); 17 | var stream = new Stream({ 18 | consumer_key: '', 19 | consumer_secret: '', 20 | access_token_key: '', 21 | access_token_secret: '' 22 | }); 23 | 24 | //create stream 25 | stream.stream(); 26 | 27 | //listen stream data 28 | stream.on('data', function(json) { 29 | console.log(json); 30 | }); 31 | ``` 32 | 33 | Events 34 | ------- 35 | - ```data``` - stream data in JSON format 36 | - ```garbage``` - stream data who can't be parsed to JSON 37 | - ```close``` - stream close event (stream connection closed) 38 | - ```error``` - error event (request error, response error, response status code greater than 200) 39 | - ```connected``` - stream created 40 | - ```heartbeat``` - twitter emitted heartbeat 41 | 42 | Methods 43 | ------- 44 | - ```stream``` - create stream connection 45 | - ```destroy``` - destroy/close stream connection 46 | 47 | ### Streaming API request parameters [link](https://dev.twitter.com/docs/streaming-apis/parameters) ### 48 | ```javascript 49 | var params = { 50 | with: 'user' 51 | } 52 | //create stream 53 | stream.stream(params); 54 | ``` 55 | #### Reserved parameters for lib 56 | - ```delimited``` 57 | - ```stall_warnings``` 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/user-stream'); -------------------------------------------------------------------------------- /lib/user-stream.js: -------------------------------------------------------------------------------- 1 | var oauth = require('oauth'), 2 | events = require('events'), 3 | util = require("util"); 4 | 5 | var user_stream_url = 'https://userstream.twitter.com/1.1/user.json', 6 | request_token_url = 'https://api.twitter.com/oauth/request_token', 7 | access_token_url = 'https://api.twitter.com/oauth/access_token'; 8 | 9 | module.exports = Stream; 10 | 11 | function Stream(params) { 12 | 13 | if (!(this instanceof Stream)) { 14 | return new Stream(params); 15 | } 16 | 17 | events.EventEmitter.call(this); 18 | 19 | this.params = params; 20 | 21 | this.oauth = new oauth.OAuth( 22 | request_token_url, 23 | access_token_url, 24 | this.params.consumer_key, 25 | this.params.consumer_secret, 26 | '1.0', 27 | null, 28 | 'HMAC-SHA1', 29 | null, 30 | { 31 | 'Accept': '*/*', 32 | 'Connection' 33 | : 'close', 34 | 'User-Agent': 'user-stream.js' 35 | } 36 | ); 37 | 38 | } 39 | 40 | //inherit 41 | util.inherits(Stream, events.EventEmitter); 42 | 43 | /** 44 | * Create twitter use stream 45 | * 46 | * Events: 47 | * - data 48 | * - garbage 49 | * - close 50 | * - error 51 | * - connected 52 | * - heartbeat 53 | * 54 | */ 55 | Stream.prototype.stream = function(params) { 56 | 57 | var stream = this; 58 | 59 | if (typeof params != 'object') { 60 | params = {}; 61 | } 62 | 63 | //required params for lib 64 | params.stall_warnings = 'true'; 65 | 66 | var request = this.oauth.post( 67 | user_stream_url, 68 | this.params.access_token_key, 69 | this.params.access_token_secret, 70 | params, 71 | null 72 | ); 73 | 74 | /** 75 | * Destroy socket 76 | */ 77 | this.destroy = function() { 78 | 79 | request.abort(); 80 | 81 | } 82 | 83 | request.on('response', function(response) { 84 | 85 | // Any response code greater then 200 from steam API is an error 86 | if(response.statusCode > 200) { 87 | 88 | stream.emit('error', {type: 'response', data: {code:response.statusCode}}); 89 | 90 | } else { 91 | 92 | //emit connected event 93 | stream.emit('connected'); 94 | 95 | response.setEncoding('utf8'); 96 | var data = ''; 97 | 98 | response.on('data', function(chunk) { 99 | 100 | data += chunk.toString('utf8'); 101 | 102 | //is heartbeat? 103 | if (data == '\r\n') { 104 | stream.emit('heartbeat'); 105 | return; 106 | } 107 | 108 | var index, json; 109 | 110 | while((index = data.indexOf('\r\n')) > -1) { 111 | json = data.slice(0, index); 112 | data = data.slice(index + 2); 113 | if(json.length > 0) { 114 | try { 115 | stream.emit('data', JSON.parse(json)); 116 | } catch(e) { 117 | stream.emit('garbage', data); 118 | } 119 | } 120 | } 121 | }); 122 | 123 | response.on('error', function(error) { 124 | 125 | stream.emit('close', error); 126 | 127 | }); 128 | 129 | response.on('end', function() { 130 | 131 | stream.emit('close', 'socket end'); 132 | 133 | }); 134 | 135 | 136 | response.on('close', function() { 137 | 138 | request.abort(); 139 | 140 | }); 141 | } 142 | 143 | }); 144 | 145 | request.on('error', function(error) { 146 | 147 | stream.emit('error', {type: 'request', data: error}); 148 | 149 | }); 150 | 151 | request.end(); 152 | 153 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-stream", 3 | "version": "0.0.7", 4 | "description": "Simple twitter user stream client. Only one dependency (oauth).", 5 | "keywords": ["twitter","streaming"], 6 | "author": "Aivis Silins", 7 | "dependencies": { 8 | "oauth": "0.9.8" 9 | }, 10 | "repository" : { 11 | "type": "git", 12 | "url": "git@github.com:aivis/user-stream.git" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/stream.js: -------------------------------------------------------------------------------- 1 | var Stream = require('./../index'); 2 | var stream = new Stream({ 3 | consumer_key: 'xxx', 4 | consumer_secret: 'xxx', 5 | access_token_key: 'xxx', 6 | access_token_secret: 'xxx' 7 | }); 8 | 9 | // * - data 10 | // * - garbage 11 | // * - close 12 | // * - error 13 | // * - connected 14 | // * - heartbeat 15 | 16 | //create stream 17 | stream.stream(); 18 | 19 | //stream JSON data 20 | stream.on('data', function(data){ 21 | console.log('Data:'); 22 | console.log(data); 23 | }); 24 | 25 | //incorrect json strings (can't parse to json) 26 | stream.on('garbage', function(data){ 27 | console.log('Can\'t be formatted:'); 28 | console.log(data); 29 | }); 30 | 31 | //heartbeats 32 | stream.on('heartbeat', function(){ 33 | console.log('Heartbeat'); 34 | }); 35 | 36 | //connected 37 | stream.on('connected', function(){ 38 | console.log('Stream created'); 39 | }); 40 | 41 | //connection errors (request || response) 42 | stream.on('error', function(error){ 43 | console.log('Connection error:'); 44 | console.log(error); 45 | }); 46 | 47 | //stream close event 48 | stream.on('close', function(error){ 49 | console.log('Stream closed'); 50 | console.log(error); 51 | }); --------------------------------------------------------------------------------