+2
-1
@@ -21,3 +21,4 @@ #!/usr/bin/env node | ||
| var remote = process.argv[2] | ||
| var confFile = process.argv[3] || path.join(userHome, '.config', 'tweetcat.json') | ||
| var confFile = path.join(process.cwd(), process.argv[3]) || | ||
| path.join(userHome, '.config', 'tweetcat.json') | ||
@@ -24,0 +25,0 @@ if (!remote) return error('Usage: tweetcat [username]') |
+46
-52
@@ -22,10 +22,9 @@ 'use strict' | ||
| }) | ||
| var ws = through2() | ||
| var proxy = duplexify() | ||
| var rs = through2() | ||
| var queue = [] | ||
| var maxDataLength = 140 - opts.screen_name.length - 2 | ||
| var synSent = false | ||
| var connected = false | ||
| var remoteId, lastMsgId | ||
| var maxChunkSize = 140 - opts.screen_name.length - 2 | ||
| var remoteId, lastTweetId | ||
| proxy.setReadable(rs) | ||
| getUserId(remote, function (err, id) { | ||
@@ -39,6 +38,8 @@ if (err) return rs.destroy(err) | ||
| .stream('statuses/filter', { follow: remoteId }) | ||
| .on('connected', syn) // TODO: Sub-optimal: This event doesn't fire for about 10 sec even though there is a connection | ||
| .on('tweet', ontweet) | ||
| ws.on('data', ondata) | ||
| // Speed things up: Start 2-way handshake even though we are not yet done | ||
| // setting up the `statuses/filter` stream. This might result in a syn-ack | ||
| // being sent back to us before we are ready, but yolo | ||
| syn() | ||
| }) | ||
@@ -55,13 +56,2 @@ | ||
| function ondata (buf) { | ||
| debug('received data on stream', buf) | ||
| // TODO: Implement back pressure | ||
| addToQueue(buf) | ||
| if (!connected) return syn() | ||
| sendQueue() | ||
| } | ||
| function ontweet (tweet) { | ||
@@ -88,3 +78,3 @@ // ignore tweets not created by the remote user (e.g. replies to the user, retweets etc) | ||
| debug('detected incoming syn pkg (id: %s)', tweet.id_str) | ||
| lastMsgId = tweet.id_str | ||
| lastTweetId = tweet.id_str | ||
| synAck() | ||
@@ -97,5 +87,4 @@ return | ||
| debug('detected incoming syn-ack pkg (id: %s)', tweet.id_str) | ||
| lastMsgId = tweet.id_str | ||
| connected = true | ||
| sendQueue() | ||
| lastTweetId = tweet.id_str | ||
| setWritable() | ||
| return | ||
@@ -106,18 +95,30 @@ } | ||
| if (!connected) return debug('ignoring tweet - not yet connected!') | ||
| if (!proxy._writable) return debug('ignoring tweet - not yet connected!') | ||
| lastMsgId = tweet.id_str | ||
| var msg = tweet.text.replace(incomingMention, '') | ||
| msg = new Buffer(msg, 'base64').toString('utf8') | ||
| lastTweetId = tweet.id_str | ||
| var data = tweet.text.replace(incomingMention, '') | ||
| data = new Buffer(data, 'base64').toString('utf8') | ||
| debug('relaying message (id: %s)', tweet.id_str, msg) | ||
| debug('relaying data (id: %s)', tweet.id_str, data) | ||
| // TODO: Handle back pressure | ||
| rs.write(msg) // write decoded message to writable stream | ||
| rs.write(data) // TODO: Handle back pressure | ||
| } | ||
| function addToQueue (buf) { | ||
| function setWritable () { | ||
| if (proxy._writable) return // no need to do this more than once | ||
| debug('connected') | ||
| proxy.setWritable(through2(function (chunk, encoding, cb) { | ||
| debug('received data on stream', chunk) | ||
| sendData(chunk, function (err) { | ||
| if (err) rs.destroy(err) | ||
| cb() | ||
| }) | ||
| })) | ||
| } | ||
| function sendData (buf, cb) { | ||
| var rest = new Buffer('') | ||
| var encoded = buf.toString('base64') | ||
| while (encoded.length > maxDataLength) { | ||
| while (encoded.length > maxChunkSize) { | ||
| rest = Buffer.concat([buf.slice(-1), rest]) | ||
@@ -127,15 +128,9 @@ buf = buf.slice(0, -1) | ||
| } | ||
| queue.push(encoded) | ||
| if (rest.length) addToQueue(rest) | ||
| } | ||
| function sendQueue () { | ||
| // TODO: Handle raise condition | ||
| var data = queue.shift() | ||
| if (!data) return | ||
| var msg = util.format('@%s %s', remote, data) | ||
| // send encoded tweet from readable stream | ||
| sendTweet(msg, { in_reply_to_status_id: lastMsgId }, function (err) { | ||
| if (err) return rs.destroy(err) | ||
| sendQueue() | ||
| var tweet = util.format('@%s %s', remote, encoded) | ||
| sendTweet(tweet, { in_reply_to_status_id: lastTweetId }, function (err) { | ||
| if (err) return cb(err) | ||
| if (rest.length) return sendData(rest, cb) | ||
| cb() | ||
| }) | ||
@@ -145,6 +140,5 @@ } | ||
| function syn () { | ||
| if (synSent) return | ||
| synSent = true | ||
| debug('preparing syn pkg...') | ||
| sendTweet(util.format(synTmpl, remote, Date.now()), function (err, id) { | ||
| var tweet = util.format(synTmpl, remote, Date.now()) | ||
| sendTweet(tweet, function (err, id) { | ||
| if (err) return rs.destroy(err) | ||
@@ -157,7 +151,7 @@ debug('syn pkg sent successfully (id: %s)', id) | ||
| debug('preparing syn-ack pkg...') | ||
| sendTweet(util.format(synAckTmpl, remote, Date.now()), function (err, id) { | ||
| var tweet = util.format(synAckTmpl, remote, Date.now()) | ||
| sendTweet(tweet, { in_reply_to_user_id_str: lastTweetId }, function (err, id) { | ||
| if (err) return rs.destroy(err) | ||
| debug('syn-ack pkg sent successfully (id: %s)', id) | ||
| connected = true | ||
| sendQueue() | ||
| setWritable() | ||
| }) | ||
@@ -176,3 +170,3 @@ } | ||
| debug('tweet sent successfully (id: %s)', data.id_str) | ||
| lastMsgId = data.id_str | ||
| lastTweetId = data.id_str | ||
| cb(null, data.id_str) | ||
@@ -182,3 +176,3 @@ }) | ||
| return duplexify(ws, rs) | ||
| return proxy | ||
| } |
+1
-1
| { | ||
| "name": "tweetcat", | ||
| "version": "0.0.2", | ||
| "version": "0.0.3", | ||
| "description": "p2p pipe across the internet using Twitter as a transport stream", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
+1
-0
@@ -31,2 +31,3 @@ # tweetcat | ||
| - [ ] Add tests | ||
| - [x] ~~Allow transfer of binary data~~ | ||
@@ -33,0 +34,0 @@ - [x] ~~Allow transfer of data larger than 140 chars~~ |
| {"user_id":"3253697520","screen_name":"tweetcat001","x_auth_expires":"0","token":"3253697520-FEPUQWobB1S6TpVf3SC5r3FEEks7D9pTvWbWQoP","secret":"zO57EosNmQIazpSJ2acMYjGygADhI941HzYp1NFV9X7ta"} |
| {"user_id":"3343336215","screen_name":"tweetcat003","x_auth_expires":"0","token":"3343336215-br02lCYPFZcdJCBRaUrgSWPX0FoFdzPZar38xkc","secret":"eIOWz7N3YE9AKQjYJyTFAHA1q9LonJi6tXvGrMcVver7r"} |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
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
41
2.5%10493
-3.24%6
-25%205
-2.38%