Advertisement
Guest User

Untitled

a guest
Sep 18th, 2014
292
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.29 KB | None | 0 0
  1. /*
  2. * Twitter bot
  3. * github.com/henriquesa
  4. * MIT license
  5. *
  6. * Every account posts a status and every other retweets it;
  7. * You can have unlimited accounts;
  8. * You need to have an API with write privileges (get it on dev.twitter.com);
  9. * The default rate is way below the maximum barrier;
  10. * Each account will be performing [number of total accounts] actions every time;
  11. * Educational tool only, please do not use this bot;
  12. * Provided AS IS, not liable for anything done with this.
  13. *
  14. */
  15.  
  16. var util = require('util'),
  17. twttr = require('twit'),
  18. async = require('async'),
  19. OAuth = require('oauth').OAuth,
  20. prompt = require('prompt'),
  21. googl = require('goo.gl');
  22.  
  23. var clients = [], iterations, interval, twitter;
  24.  
  25. var config = {};
  26. /*
  27. * config = {
  28. * tokens: {
  29. * consumer_key: String,
  30. * consumer_secret: String,
  31. * },
  32. *
  33. * tweet: {
  34. * beforeNumber: String,
  35. * afterNumber: String
  36. * },
  37. *
  38. * iterations: Number,
  39. * rate: Number,
  40. * innerRate: Number
  41. * }
  42. */
  43. config.tokens = {
  44. consumer_key: 'ADD-YOUR-KEY!',
  45. consumer_secret: 'IT-HAS-TO-HAVE-WRITE-PRIVILEGE'
  46. };
  47.  
  48. addClients();
  49.  
  50. function addClients(){
  51. async.waterfall([getRequestToken, askPin, getAccessToken], function(err, result){
  52. if(err) throw err;
  53. clients.push(result);
  54. prompt.start();
  55. prompt.confirm('Add one more account?', function(err, result){
  56. if(err) throw err;
  57. if(result) addClients();
  58. else initialConfig();
  59. });
  60. });
  61. }
  62.  
  63.  
  64. function initialConfig(){
  65.  
  66. // Creates a twttr instance for each account, and pushes them to the twitter array
  67. twitter = [];
  68. clients.forEach(function(client){
  69. var clientObject = mergeObjects(client, config.tokens); // Merges client tokens with API tokens
  70. twitter.push(new twttr(clientObject));
  71. });
  72.  
  73. // Number of status posts from each account that will be retweeted (number of accounts - 1) times
  74. // default = 400
  75. config.iterations = config.iterations || 400;
  76.  
  77. // Limiting rate between operations in milliseconds
  78. // default = 1 minute * number of accounts
  79. config.rate = config.rate || clients.length * 1000 * 60;
  80.  
  81. // Limiting rate between every request in each update in milliseconds; does not affect regular rate
  82. // default = 5 seconds
  83. config.innerRate = config.innerRate || 5000;
  84.  
  85. // Gap between posting and RTing the statuses
  86. // default = 20 seconds
  87. config.delayRt = config.delayRt || 20000;
  88.  
  89. // Tweet components
  90. config.tweet = config.tweet || {};
  91. config.tweet.beforeNumber = config.tweet.beforeNumber || 'Ajudante Beta';
  92. config.tweet.afterNumber = config.tweet.afterNumber || 'Veja no GitHub: tim-beta barra twitter-bot';
  93.  
  94. // Set update trigger at the configured rate, but adjusts for the delay between each request
  95. iteration = 0;
  96. update();
  97. }
  98.  
  99. function update(){
  100. iteration++; // Number inside loop, used to place the tweet number in the status update
  101. if(iteration > config.iterations){ // Over the limit of iterations, end execution
  102. clearInterval(interval);
  103. console.log('Done without errors');
  104. } else {
  105. postStatuses(); // Inside the limit, post statuses
  106. }
  107.  
  108. function postStatuses(){
  109. var postArray = [];
  110. twitter.forEach(function(account){ // Places all posting functions for all accounts inside an array...
  111. postArray.push(function(callback){
  112. postStatus(account, callback);
  113. });
  114. });
  115. async.series(postArray, statusCallback); // ...and calls them serially afterwards with the help of the async lib.
  116. }
  117.  
  118. function postStatus(account, callback){ // Called on each status update
  119. var before = Date.now();
  120. process.stdout.write('(' + (twitter.indexOf(account)+1) + '/' + twitter.length + ') Posting tweet... ');
  121. account.post('statuses/update', {status: composeStatus(), trim_user: true}, function(err, data, response){ // Posts tweet
  122. if(err){
  123. callback(err, response); // Ends all execution for the iteration
  124. return;
  125. }
  126. process.stdout.write('#' + data.id_str + ' ');
  127. callbackAfterDelay(before, data.id_str, callback);
  128. });
  129. }
  130.  
  131. function statusCallback(err, statusArray){
  132. if(err){ // If there was an error, end execution
  133. console.log('Error while posting statuses.');
  134. console.log(statusArray);
  135. clearInterval(interval);
  136. throw err;
  137. }
  138. var startRT = function(){
  139. retweetStatuses(statusArray);
  140. }
  141. setTimeout(startRT, config.delayRt); // Every status was posted correctly, now retweet them
  142. }
  143.  
  144. function retweetStatuses(statusArray){
  145. var retweetArray = [];
  146. statusArray.forEach(function(statusId, statusIndex){ // Places all RTing functions for all statuses and accounts inside an array
  147. twitter.forEach(function(account, accountIndex){
  148. if(statusIndex !== accountIndex){ // Only retweet if the status isn't from the account
  149. retweetArray.push(function(callback){
  150. postRetweet(account, statusId, callback);
  151. });
  152. }
  153. });
  154. });
  155.  
  156. async.series(retweetArray, retweetCallback);
  157. }
  158.  
  159. function retweetCallback(err, statusArray){
  160. if(err){ // If there was an error, end execution
  161. console.log('Error while retweeting statuses.');
  162. console.log(statusArray);
  163. clearInterval(interval);
  164. throw err;
  165. }
  166. var intervalTime = (config.rate - clients.length * config.innerRate) - config.delayRt; // Compensate for all the delays
  167. console.log('---- ' + iteration + ' out of ' + config.iterations + ' done. Sleeping for ' + intervalTime/1000 + 's ----');
  168. interval = setInterval(update, intervalTime);
  169. }
  170.  
  171. function postRetweet(account, statusId, callback){ // Called on each RT
  172. var before = Date.now();
  173. process.stdout.write('Retweeting #' + statusId + ' (account ' + (twitter.indexOf(account)+1) + ')... ');
  174. account.post('statuses/retweet/' + statusId, {trim_user: true}, function(err, data, response){
  175. if(err){
  176. callback(err, response);
  177. return;
  178. }
  179. callbackAfterDelay(before, data.id, callback);
  180. });
  181. }
  182.  
  183. function callbackAfterDelay(before, data, callback){
  184. var timeSpan = Date.now() - before; // Calculates the time difference between now and prior to the AJAX call
  185. console.log('OK (' + timeSpan + 'ms)');
  186. var _callback = function(){
  187. callback(null, data);
  188. };
  189. // If the AJAX call took longer than the regular delay, run callback. Else, run the callback after the complete delay occurs
  190. timeSpan >= config.innerRate ? _callback() : setTimeout(_callback, config.innerRate - timeSpan);
  191. }
  192.  
  193. function composeStatus(){
  194. return config.tweet.beforeNumber + ' ' +
  195. Math.floor(Math.random()*10000) + ' ' +
  196. iteration + ' ' +
  197. config.tweet.afterNumber;
  198. }
  199. }
  200.  
  201. function mergeObjects(obj1, obj2){
  202. for(attrName in obj2){
  203. obj1[attrName] = obj2[attrName];
  204. }
  205. return obj1;
  206. }
  207.  
  208. /*
  209. * Functions by tanepiper: https://gist.github.com/tanepiper/575303
  210. * Heavily modified.
  211. */
  212.  
  213. function askPin(oa, oauth_token, oauth_token_secret, callback){
  214. prompt.start();
  215. prompt.get('token', function(err, result){
  216. if(err) callback(err);
  217. callback(null, oa, oauth_token, oauth_token_secret, result.token);
  218. });
  219. }
  220.  
  221. function getRequestToken(callback) {
  222. var oauth = new OAuth(
  223. "https://api.twitter.com/oauth/request_token",
  224. "https://api.twitter.com/oauth/access_token",
  225. config.tokens.consumer_key,
  226. config.tokens.consumer_secret,
  227. "1.0",
  228. "oob",
  229. "HMAC-SHA1"
  230. );
  231.  
  232. oauth.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){
  233. if(error) {
  234. callback([error.statusCode, error.data].join(': '));
  235. } else {
  236. var authUrl = 'https://twitter.com/oauth/authorize?oauth_token=';
  237. googl.shorten(authUrl + oauth_token)
  238. .then(function(shortUrl){
  239. console.log(('Access ' + shortUrl + ' on your browser and enter the token number.').underline.green);
  240. callback(null, oauth, oauth_token, oauth_token_secret);
  241. })
  242. .catch(function(){
  243. console.log('\tAccess \n' + authUrl + oauth_token + '\n\ton your browser and enter the token number.');
  244. callback(null, oauth, oauth_token, oauth_token_secret);
  245. });
  246. }
  247. });
  248. }
  249.  
  250. function getAccessToken(oa, oauth_token, oauth_token_secret, pin, callback) {
  251. oa.getOAuthAccessToken(oauth_token, oauth_token_secret, pin,
  252. function(error, oauth_access_token, oauth_access_token_secret, results2) {
  253. if (error) {
  254. if (parseInt(error.statusCode) == 401) {
  255. callback('The pin number you have entered is incorrect');
  256. }
  257. }
  258. callback(null, {access_token: oauth_access_token, access_token_secret: oauth_access_token_secret});
  259. });
  260. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement