+2
-1
@@ -17,3 +17,3 @@ // | ||
| return callback(new Error('tweet must be of type String')); | ||
| } else if(status.length > 140) { | ||
| } else if(status.length > 280) { | ||
| return callback(new Error('tweet is too long: ' + status.length)); | ||
@@ -75,2 +75,3 @@ } | ||
| // | ||
| // choose a random friend of one of your followers, and follow that user | ||
@@ -77,0 +78,0 @@ // |
+12
-1
@@ -28,2 +28,3 @@ var assert = require('assert'); | ||
| self._isFileStreamEnded = false; | ||
| self._isSharedMedia = !!params.shared; | ||
| } | ||
@@ -129,3 +130,11 @@ | ||
| var mediaFileSizeBytes = fs.statSync(self._file_path).size; | ||
| var shared = self._isSharedMedia; | ||
| var media_category = 'dm_image'; | ||
| if (mediaType.toLowerCase().indexOf('gif') > -1) { | ||
| media_category = 'dm_gif'; | ||
| } else if (mediaType.toLowerCase().indexOf('video') > -1) { | ||
| media_category = 'dm_video'; | ||
| } | ||
| // Check the file size - it should not go over 15MB for video. | ||
@@ -137,3 +146,5 @@ // See https://dev.twitter.com/rest/reference/post/media/upload-chunked | ||
| 'media_type': mediaType, | ||
| 'total_bytes': mediaFileSizeBytes | ||
| 'total_bytes': mediaFileSizeBytes, | ||
| 'shared': shared, | ||
| 'media_category': media_category | ||
| }, cb); | ||
@@ -140,0 +151,0 @@ } else { |
+2
-2
@@ -100,3 +100,3 @@ var querystring = require('querystring'); | ||
| request.post({ | ||
| url: endpoints.API_HOST + '/oauth2/token', | ||
| url: endpoints.API_HOST + 'oauth2/token', | ||
| headers: { | ||
@@ -114,3 +114,3 @@ 'Authorization': 'Basic ' + b64Credentials, | ||
| } | ||
| if ( !body ) { | ||
@@ -117,0 +117,0 @@ var error = exports.makeTwitError('Not valid reply from Twitter upon obtaining bearer token'); |
@@ -8,9 +8,3 @@ | ||
| var request = require('request'); | ||
| var zlib = require('zlib'); | ||
| var zlibOptions = { | ||
| flush: zlib.Z_SYNC_FLUSH, | ||
| finishFlush: zlib.Z_SYNC_FLUSH | ||
| } | ||
| var STATUS_CODES_TO_ABORT_ON = require('./settings').STATUS_CODES_TO_ABORT_ON | ||
@@ -75,2 +69,3 @@ | ||
| self._setOauthTimestamp(); | ||
| this.reqOpts.encoding = 'utf8' | ||
| self.request = request.post(this.reqOpts); | ||
@@ -90,15 +85,8 @@ self.emit('connect', self.request); | ||
| var body = ''; | ||
| var compressedBody = ''; | ||
| self.response.on('data', function (chunk) { | ||
| compressedBody += chunk.toString('utf8'); | ||
| self.request.on('data', function (chunk) { | ||
| body += chunk; | ||
| }) | ||
| var gunzip = zlib.createGunzip(zlibOptions); | ||
| self.response.pipe(gunzip); | ||
| gunzip.on('data', function (chunk) { | ||
| body += chunk.toString('utf8') | ||
| }) | ||
| gunzip.on('end', function () { | ||
| self.request.on('end', function () { | ||
| try { | ||
@@ -119,9 +107,6 @@ body = JSON.parse(body) | ||
| }); | ||
| gunzip.on('error', function (err) { | ||
| // If Twitter sends us back an uncompressed HTTP response, gzip will error out. | ||
| // Handle this by emitting an error with the uncompressed response body. | ||
| var errMsg = 'Gzip error: ' + err.message; | ||
| var twitErr = helpers.makeTwitError(errMsg); | ||
| self.request.on('error', function (err) { | ||
| var twitErr = helpers.makeTwitError(err.message); | ||
| twitErr.statusCode = self.response.statusCode; | ||
| helpers.attachBodyInfoToError(twitErr, compressedBody); | ||
| helpers.attachBodyInfoToError(twitErr, body); | ||
| self.emit('parser-error', twitErr); | ||
@@ -135,17 +120,10 @@ }); | ||
| // Read the body from the response and return to the user. | ||
| var gunzip = zlib.createGunzip(zlibOptions); | ||
| self.response.pipe(gunzip); | ||
| //pass all response data to parser | ||
| gunzip.on('data', function (chunk) { | ||
| self.request.on('data', function(data) { | ||
| self._connectInterval = 0 | ||
| // stop stall timer, and start a new one | ||
| self._resetStallAbortTimeout(); | ||
| self.parser.parse(chunk.toString('utf8')); | ||
| }); | ||
| self.parser.parse(data); | ||
| }) | ||
| gunzip.on('close', self._onClose.bind(self)) | ||
| gunzip.on('error', function (err) { | ||
| self.emit('error', err); | ||
| }) | ||
| self.response.on('close', self._onClose.bind(self)) | ||
| self.response.on('error', function (err) { | ||
@@ -152,0 +130,0 @@ // expose response errors on twit instance |
+32
-12
@@ -88,4 +88,5 @@ // | ||
| callback(err, null, null); | ||
| } else { | ||
| reject(err); | ||
| } | ||
| reject(err); | ||
| } | ||
@@ -105,4 +106,5 @@ | ||
| self._updateClockOffsetFromResponse(resp); | ||
| var peerCertificate = resp && resp.socket && resp.socket.getPeerCertificate(); | ||
| if (self.config.trusted_cert_fingerprints) { | ||
| if (self.config.trusted_cert_fingerprints && peerCertificate) { | ||
| if (!resp.socket.authorized) { | ||
@@ -115,3 +117,3 @@ // The peer certificate was not signed by one of the authorized CA's. | ||
| } | ||
| var fingerprint = resp.socket.getPeerCertificate().fingerprint; | ||
| var fingerprint = peerCertificate.fingerprint; | ||
| var trustedFingerprints = self.config.trusted_cert_fingerprints; | ||
@@ -129,5 +131,10 @@ if (trustedFingerprints.indexOf(fingerprint) === -1) { | ||
| callback(err, parsedBody, resp); | ||
| } else { | ||
| if (err) { | ||
| reject(err) | ||
| } else { | ||
| resolve({ data: parsedBody, resp: resp }); | ||
| } | ||
| } | ||
| resolve({ data: parsedBody, resp: resp }); | ||
| return; | ||
@@ -209,13 +216,20 @@ }) | ||
| if (typeof self.config.timeout_ms !== 'undefined') { | ||
| if (typeof self.config.timeout_ms !== 'undefined' && !isStreaming) { | ||
| reqOpts.timeout = self.config.timeout_ms; | ||
| } | ||
| try { | ||
| // finalize the `path` value by building it using user-supplied params | ||
| path = helpers.moveParamsIntoPath(finalParams, path) | ||
| } catch (e) { | ||
| callback(e, null, null) | ||
| return | ||
| if (typeof self.config.strictSSL !== 'undefined') { | ||
| reqOpts.strictSSL = self.config.strictSSL; | ||
| } | ||
| // finalize the `path` value by building it using user-supplied params | ||
| // when json parameters should not be in the payload | ||
| if (JSONPAYLOAD_PATHS.indexOf(path) === -1) { | ||
| try { | ||
| path = helpers.moveParamsIntoPath(finalParams, path) | ||
| } catch (e) { | ||
| callback(e, null, null) | ||
| return | ||
| } | ||
| } | ||
@@ -262,3 +276,5 @@ if (path.match(/^https?:\/\//i)) { | ||
| if (Object.keys(finalParams).length) { | ||
| if (isStreaming) { | ||
| reqOpts.form = finalParams | ||
| } else if (Object.keys(finalParams).length) { | ||
| // not all of the user's parameters were used to build the request path | ||
@@ -486,2 +502,6 @@ // add them as a query string | ||
| if (typeof config.strictSSL !== 'undefined' && typeof config.strictSSL !== 'boolean') { | ||
| throw new TypeError('Twit config `strictSSL` must be a Boolean. Got: ' + config.strictSSL + '.'); | ||
| } | ||
| if (config.app_only_auth) { | ||
@@ -488,0 +508,0 @@ var auth_type = 'app-only auth' |
+1
-1
| { | ||
| "name": "twit", | ||
| "description": "Twitter API client for node (REST & Streaming)", | ||
| "version": "2.2.9", | ||
| "version": "2.2.10", | ||
| "author": "Tolga Tezel", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
+10
-1
@@ -24,2 +24,3 @@ # twit | ||
| timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests. | ||
| strictSSL: true, // optional - requires SSL certificates to be valid. | ||
| }) | ||
@@ -491,3 +492,3 @@ | ||
| Go here to create an app and get OAuth credentials (if you haven't already): https://dev.twitter.com/apps/new | ||
| Go here to create an app and get OAuth credentials (if you haven't already): https://apps.twitter.com/app/new | ||
@@ -581,2 +582,10 @@ # Advanced | ||
| ### 2.2.10 | ||
| * Update maximum Tweet characters to 280 (thanks @maziyarpanahi) | ||
| * For streaming requests, use request body for sending params (thanks @raine) | ||
| * Fix getBearerToken endpoint (thanks @williamcoates) | ||
| * Shared Parameter Feature For Media Upload (thanks @haroonabbasi) | ||
| * Don't include params in path for jsonpayload paths (thanks @egtoney) | ||
| * Add support for strictSSL request option (thanks @zdoc01) | ||
| ### 2.2.9 | ||
@@ -583,0 +592,0 @@ * Use JSON payload in request body for new DM endpoints. |
@@ -35,3 +35,2 @@ var assert = require('assert') | ||
| assert(body.rate_limit_context) | ||
| assert(body.resources.users) | ||
| assert(body.resources.search) | ||
@@ -55,3 +54,29 @@ assert.equal(Object.keys(body.resources).length, 2) | ||
| }) | ||
| it('GET `users/show` { screen_name: twitter } multiple times', function (done) { | ||
| var index = 0 | ||
| var limit = 50 | ||
| var params = { screen_name: 'twitter' } | ||
| var isDone = false; | ||
| var getData = function () { | ||
| twit.get('users/show', params, function (err, data) { | ||
| if (data) { | ||
| assert.equal(783214, data.id) | ||
| } else { | ||
| throw new Error(err) | ||
| } | ||
| index++ | ||
| if (index >= limit && !isDone) { | ||
| isDone = true; | ||
| done(); | ||
| } | ||
| }) | ||
| } | ||
| for (var i = 0; i <= limit; i++) { | ||
| getData() | ||
| } | ||
| }) | ||
| }) | ||
@@ -88,4 +88,2 @@ var assert = require('assert'); | ||
| assert(bodyObj.size) | ||
| assert(bodyObj.video) | ||
| assert.equal(bodyObj.video.video_type, 'video/mp4') | ||
| } |
+50
-9
@@ -51,3 +51,3 @@ var assert = require('assert') | ||
| it('GET `account/verify_credentials` using promise API AND callback API', function (done) { | ||
| it.skip('GET `account/verify_credentials` using promise API AND callback API', function (done) { | ||
| var i = 0; | ||
@@ -446,2 +446,28 @@ | ||
| describe('Url construction', function () { | ||
| var twit = null | ||
| var parameters = { | ||
| elem1: 'hello world', | ||
| foo: 'bar' | ||
| } | ||
| before(function () { | ||
| twit = new Twit(config1) | ||
| }) | ||
| it('adds query parameters to url', function (done) { | ||
| var resp = twit._buildReqOpts('POST', 'account/verify_credentials', parameters, false, function (err, data) { | ||
| assert.equal(data.url, 'https://api.twitter.com/1.1/account/verify_credentials.json?elem1=hello%20world&foo=bar') | ||
| done() | ||
| }) | ||
| }) | ||
| it('does not add query parameters to url when json should be in the payload', function (done) { | ||
| var resp = twit._buildReqOpts('POST', 'direct_messages/welcome_messages/new', parameters, false, function (err, data) { | ||
| assert.equal(data.url, 'https://api.twitter.com/1.1/direct_messages/welcome_messages/new.json') | ||
| done() | ||
| }) | ||
| }) | ||
| }) | ||
| describe('Media Upload', function () { | ||
@@ -605,9 +631,9 @@ var twit = null | ||
| describe('handling errors from the twitter api', function () { | ||
| var twit = new Twit({ | ||
| consumer_key: 'a', | ||
| consumer_secret: 'b', | ||
| access_token: 'c', | ||
| access_token_secret: 'd' | ||
| }) | ||
| it('should callback with an Error object with all the info and a response object', function (done) { | ||
| var twit = new Twit({ | ||
| consumer_key: 'a', | ||
| consumer_secret: 'b', | ||
| access_token: 'c', | ||
| access_token_secret: 'd' | ||
| }) | ||
| twit.get('account/verify_credentials', function (err, reply, res) { | ||
@@ -626,2 +652,17 @@ assert(err instanceof Error) | ||
| }) | ||
| it('should return a rejected promise with the Twitter API errors', function() { | ||
| twit.get('account/verify_credentials') | ||
| .then(result => { | ||
| throw Error("Twitter API exception was not caught in the promise API") | ||
| }) | ||
| .catch(err => { | ||
| assert(err instanceof Error) | ||
| assert(err.statusCode === 401) | ||
| assert(err.code > 0) | ||
| assert(err.message.match(/token/)) | ||
| assert(err.twitterReply) | ||
| assert(err.allErrors) | ||
| return; | ||
| }) | ||
| }) | ||
| }) | ||
@@ -676,3 +717,3 @@ describe('handling other errors', function () { | ||
| describe('Twit agent_options config', function () { | ||
| it('config.trusted_cert_fingerprints works against cert fingerprint for api.twitter.com:443', function (done) { | ||
| it.skip('config.trusted_cert_fingerprints works against cert fingerprint for api.twitter.com:443', function (done) { | ||
| // TODO: mock getPeerCertificate so we don't have to pin here | ||
@@ -696,3 +737,3 @@ config1.trusted_cert_fingerprints = [ | ||
| it('config.trusted_cert_fingerprints responds with Error when fingerprint mismatch occurs', function (done) { | ||
| it.skip('config.trusted_cert_fingerprints responds with Error when fingerprint mismatch occurs', function (done) { | ||
| config1.trusted_cert_fingerprints = [ | ||
@@ -699,0 +740,0 @@ 'AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA' |
@@ -388,3 +388,3 @@ var assert = require('assert') | ||
| stream.on('parser-error', function (err) { | ||
| stream.on('error', function (err) { | ||
| assert.equal(err.statusCode, 401) | ||
@@ -548,3 +548,3 @@ assert(err.twitterReply) | ||
| describe('Streaming API disconnect message', function (done) { | ||
| it('results in stopping the stream', function (done) { | ||
| it.skip('results in stopping the stream', function (done) { | ||
| var stubPost = function () { | ||
@@ -578,3 +578,3 @@ var fakeRequest = new helpers.FakeRequest() | ||
| describe('Streaming API Connection limit exceeded message', function (done) { | ||
| describe.skip('Streaming API Connection limit exceeded message', function (done) { | ||
| it('results in an `error` event containing the message', function (done) { | ||
@@ -581,0 +581,0 @@ var errMsg = 'Exceeded connection limit for user'; |
+14
-0
@@ -66,2 +66,16 @@ var assert = require('assert') | ||
| }) | ||
| it('throws when config provides invalid strictSSL option', function (done) { | ||
| assert.throws(function () { | ||
| var twit = new Twit({ | ||
| consumer_key: 'a' | ||
| , consumer_secret: 'a' | ||
| , access_token: 'a' | ||
| , access_token_secret: 'a' | ||
| , strictSSL: 'a' | ||
| }) | ||
| }, Error) | ||
| done() | ||
| }) | ||
| }) | ||
@@ -68,0 +82,0 @@ |
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
640984
0.49%2863
2.8%675
1.35%25
-3.85%