Skip to content
This repository was archived by the owner on Feb 13, 2018. It is now read-only.

Commit bb2ce15

Browse files
author
Tim Whitlock
committed
added rest client
0 parents  commit bb2ce15

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Twitter API 1.1 client for NodeJS
2+
3+
See examples for usage

examples/verify-credentials.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Example verifies OAuth credentials via REST API
3+
*/
4+
5+
6+
var client = require('../lib/twitter').createClient();
7+
8+
client.setAuth (
9+
'your consumer key',
10+
'your consumer secret',
11+
'some access key',
12+
'some access secret'
13+
);
14+
15+
client.get( 'account/verify_credentials', { skip_status: true }, function( user, error, status ){
16+
console.log( user ? 'Authenticated as @'+user.screen_name : 'Not authenticated' );
17+
} );
18+
19+

lib/twitter.js

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/**
2+
* node-twitter-api
3+
* Twitter API 1.1 client for Node JS
4+
*/
5+
6+
7+
// pseudo constants
8+
//
9+
var TWITTER_API_TIMEOUT = 5,
10+
TWITTER_API_USERAGENT = 'Node/'+process.version.substr(1),
11+
TWITTER_API_BASE = 'https://api.twitter.com/1.1';
12+
TWITTER_OAUTH_REQUEST_TOKEN_URL = 'https://twitter.com/oauth/request_token';
13+
TWITTER_OAUTH_AUTHORIZE_URL = 'https://twitter.com/oauth/authorize';
14+
TWITTER_OAUTH_AUTHENTICATE_URL = 'https://twitter.com/oauth/authenticate';
15+
TWITTER_OAUTH_ACCESS_TOKEN_URL = 'https://twitter.com/oauth/access_token';
16+
17+
18+
19+
20+
/**
21+
* Simple token object that holds key and secret
22+
*/
23+
function OAuthToken( key, secret ){
24+
if( ! key || ! secret ){
25+
throw new Error('OAuthToken params must not be empty');
26+
}
27+
this.key = key;
28+
this.secret = secret;
29+
}
30+
31+
OAuthToken.prototype.get_authorization_url = function(){
32+
return TWITTER_OAUTH_AUTHORIZE_URL+'?oauth_token='+encodeURIComponent(this.key);
33+
}
34+
35+
36+
37+
38+
39+
/**
40+
* Object for compiling, signing and serializing OAuth parameters
41+
*/
42+
function OAuthParams( args ){
43+
this.consumer_secret = '';
44+
this.token_secret = '';
45+
this.args = args || {};
46+
this.args.oauth_version = this.args.oauth_version || '1.0';
47+
48+
}
49+
50+
OAuthParams.prototype.setConsumer = function( token ){
51+
this.consumer_secret = token.secret||'';
52+
this.args.oauth_consumer_key = token.key||'';
53+
return this;
54+
}
55+
56+
OAuthParams.prototype.setAccess = function ( token ){
57+
this.token_secret = token.secret||'';
58+
this.args.oauth_token = token.key||'';
59+
return this;
60+
}
61+
62+
OAuthParams.prototype.normalize = function(){
63+
var i, k, keys = [], sorted = {};
64+
for( k in this.args ){
65+
keys.push(k);
66+
}
67+
keys.sort();
68+
for( i in keys ){
69+
k = keys[i];
70+
sorted[k] = this.args[k];
71+
}
72+
return this.args = sorted;
73+
}
74+
75+
OAuthParams.prototype.serialize = function(){
76+
return require('querystring').stringify( this.args );
77+
}
78+
79+
OAuthParams.prototype.sign = function( requestMethod, requestUri ){
80+
var ms = Date.now(),
81+
s = Math.round( ms / 1000 );
82+
this.args.oauth_signature_method = 'HMAC-SHA1';
83+
this.args.oauth_timestamp = String(s);
84+
this.args.oauth_nonce = String(ms);
85+
delete this.args.oauth_signature;
86+
// normalize, build and sign
87+
this.normalize();
88+
var str = requestMethod.toUpperCase() +'&'+ encodeURIComponent(requestUri) +'&'+ encodeURIComponent(this.serialize()),
89+
key = encodeURIComponent(this.consumer_secret) +'&'+ encodeURIComponent(this.token_secret),
90+
hash = require('crypto').createHmac('sha1',key).update(str);
91+
this.args.oauth_signature = hash.digest('base64');
92+
return this;
93+
}
94+
95+
OAuthParams.prototype.getHeader = function(){
96+
var a, args = {}, lines = [];
97+
for( a in this.args ){
98+
if( 0 === a.indexOf('oauth_') ){
99+
lines.push( encodeURIComponent(a) +'='+ encodeURIComponent(this.args[a]) );
100+
}
101+
else {
102+
args[a] = this.args[a];
103+
}
104+
}
105+
this.args = args;
106+
return 'OAuth '+lines.join(',\n ');
107+
}
108+
109+
110+
111+
112+
113+
114+
115+
/**
116+
* Twitter API 1.1 REST client
117+
*/
118+
function TwitterClient(){
119+
this.deAuth();
120+
}
121+
122+
TwitterClient.prototype.setAuth = function( consumerKey, consumerSecret, accessKey, accessSecret ){
123+
this.consumerToken = new OAuthToken( consumerKey, consumerSecret );
124+
if( accessKey || accessSecret ){
125+
this.accessToken = new OAuthToken( accessKey, accessSecret );
126+
}
127+
else {
128+
this.accessToken = null;
129+
}
130+
return this;
131+
}
132+
133+
TwitterClient.prototype.hasAuth = function(){
134+
return ( this.accessToken instanceof OAuthToken ) && ( this.consumerToken instanceof OAuthToken );
135+
}
136+
137+
TwitterClient.prototype.deAuth = function(){
138+
this.consumerToken = null;
139+
this.accessToken = null;
140+
return this;
141+
}
142+
143+
TwitterClient.prototype.get = function( requestPath, requestArgs, callback ){
144+
return this.call( 'GET', requestPath, requestArgs, callback );
145+
}
146+
147+
TwitterClient.prototype.post = function( requestPath, requestArgs, callback ){
148+
return this.call( 'POST', requestPath, requestArgs, callback );
149+
}
150+
151+
TwitterClient.prototype.call = function( requestMethod, requestPath, requestArgs, callback ){
152+
if( ! this.hasAuth() ){
153+
throw new Error('Twitter client not authenticated');
154+
}
155+
requestMethod = String( requestMethod||'GET' ).toUpperCase();
156+
var requestUri = TWITTER_API_BASE + '/' + requestPath + '.json';
157+
// build and sign request parameters
158+
var params = new OAuthParams( requestArgs )
159+
.setConsumer( this.consumerToken )
160+
.setAccess( this.accessToken )
161+
.sign( requestMethod, requestUri );
162+
// grab authorization header and any remaining params
163+
var oauth = params.getHeader(),
164+
query = params.serialize();
165+
// build http request starting with parsed endpoint
166+
var http = require('url').parse( requestUri );
167+
http.headers = {
168+
Authorization: oauth,
169+
'User-Agent': TWITTER_API_USERAGENT
170+
};
171+
if( 'POST' === requestMethod ){
172+
http.method = 'POST';
173+
http.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
174+
http.headers['Content-Length'] = query.length;
175+
}
176+
else if( query ){
177+
http.path += '?' + query;
178+
}
179+
var req = require('https').get( http, function( res ){
180+
// started to receive response from twitter
181+
res.setEncoding('utf8');
182+
var body = '';
183+
res.on('data', function( chunk ) {
184+
body += chunk;
185+
} );
186+
res.on('end', function(){
187+
try {
188+
var error = null,
189+
data = JSON.parse(body);
190+
if( 'object' !== typeof data ){
191+
throw { message: 'Malformed response from Twitter', code: 0 };
192+
}
193+
if( data.errors && data.errors.length ){
194+
throw data.errors.pop();
195+
}
196+
}
197+
catch( e ){
198+
error = e;
199+
data = null;
200+
console.error( 'Twitter responded status '+res.statusCode );
201+
console.error( e.message || String(e) || 'Unknown error' );
202+
}
203+
if( 'function' === typeof callback ){
204+
callback( data, error, res.statusCode );
205+
}
206+
} );
207+
} );
208+
if( 'POST' === requestMethod && query ){
209+
req.write( query );
210+
req.write( '\n' );
211+
}
212+
req.end();
213+
return this;
214+
}
215+
216+
217+
218+
219+
220+
221+
222+
// export access to utils module
223+
224+
exports.createOAuthParams = function( args ){
225+
return new OAuthParams( args );
226+
}
227+
228+
229+
exports.createOAuthToken = function( key, secret ){
230+
return new OAuthToken( key, secret );
231+
}
232+
233+
234+
exports.createClient = function(){
235+
return new TwitterClient;
236+
}

0 commit comments

Comments
 (0)