This repository has been archived by the owner on Apr 16, 2023. It is now read-only.
forked from raulrene/twitter-contest-js-bot
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
220 lines (183 loc) · 8.8 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
var API = require('./api-functions'),
RATE_LIMIT_EXCEEDED_TIMEOUT = 1000 * 60 * 10, // 10 minutes
RETWEET_TIMEOUT = 1000 * 15, // 15 seconds
RATE_SEARCH_TIMEOUT = 1000 * 30, // 30 seconds
searchQueries = [
"retweet to win -vote -filter:retweets OR RT to win -vote -filter:retweets",
"retweet 2 win -vote -filter:retweets OR RT 2 win -vote -filter:retweets"
],
// "Specifies what type of search results you would prefer to receive. The current default is “mixed.” Valid values include:"
// Default: "recent" (return only the most recent results in the response)
// "mixed" (Include both popular and real time results in the response)
// "popular" (return only the most popular results in the response)
RESULT_TYPE = "mixed",
// Minimum amount of retweets a tweet needs before we retweet it.
// - Significantly reduces the amount of fake contests retweeted and stops
// retweeting other bots that retweet retweets of other bots.
// Default: 10
MIN_RETWEETS_NEEDED = 10,
// Maxiumum amount of tweets a user can have before we do not retweet them.
// - Accounts with an extremely large amount of tweets are often bots,
// therefore we should ignore them and not retweet their tweets.
// Default: 20000
// 0 (disables)
MAX_USER_TWEETS = 20000,
// If option above is enabled, allow us to block them.
// - Blocking users do not prevent their tweets from appearing in search,
// but this will ensure you do not accidentally retweet them still.
// Default: false
// true (will block user)
MAX_USER_TWEETS_BLOCK = false;
// Main self-initializing function
(function() {
var last_tweet_id = 0,
searchResultsArr = [],
blockedUsers = [],
badTweetIds = [],
limitLockout = false;
/** The Callback function for the Search API */
var searchCallback = function(response) {
var payload = JSON.parse(response);
// Iterating through tweets returned by the Search
payload.statuses.forEach(function (searchItem) {
// Lots of checks to filter out bad tweets, other bots and contests that are likely not legitimate
// is not already a retweet
if (!searchItem.retweeted_status) {
// is not an ignored tweet
if (badTweetIds.indexOf(searchItem.id) === -1) {
// has enough retweets on the tweet for us to retweet it too (helps prove legitimacy)
if (searchItem.retweet_count >= MIN_RETWEETS_NEEDED) {
// user is not on our blocked list
if (blockedUsers.indexOf(searchItem.user.id) === -1) {
if (MAX_USER_TWEETS && searchItem.user.statuses_count < MAX_USER_TWEETS) { // should we ignore users with high amounts of tweets (likely bots)
// Save the search item in the Search Results array
searchResultsArr.push(searchItem);
}
else if (MAX_USER_TWEETS_BLOCK) { // may be a spam bot, do we want to block them?
blockedUsers.push(searchItem.user.id);
API.blockUser(searchItem.user.id);
console.log("Blocking possible bot user " + searchItem.user.id);
}
}
}
}
}
});
// If we have the next_results, search again for the rest (sort of a pagination)
if (payload.search_metadata.next_results) {
API.searchByStringParam(payload.search_metadata.next_results, searchCallback);
}
};
function unlock() {
console.log("Limit lockout time has passed, resuming program...");
limitLockout = false;
};
/** The error callback for the Search API */
var errorHandler = function (err) {
console.error("Error!", err.message);
try {
// If the error is "Rate limit exceeded", code 88 - try again after 10 minutes
if (JSON.parse(err.error).errors[0].code === 88) {
console.log("After " + RATE_LIMIT_EXCEEDED_TIMEOUT / 60000 + " minutes, I will try again to fetch some results...");
limitLockout = true; // suspend other functions from running while lockout is in effect
// queue unsuspend of program
setTimeout(function () {
unlock();
}, RATE_LIMIT_EXCEEDED_TIMEOUT);
}
}
catch (err) {
console.log("Possible unexpected token E" + err);
}
};
/** The Search function */
var search = function() {
// do not search if limit lockout is in effect
if (limitLockout)
return;
console.log("Searching for tweets...");
for (var i = 0; i < searchQueries.length; ++i) {
API.search({
// Without having the word "vote", and filtering out retweets - as much as possible
text: searchQueries[i],
result_type: RESULT_TYPE,
callback: searchCallback,
error_callback: errorHandler,
since_id: last_tweet_id
});
// we need to wait between search queries so we do not trigger rate limit lockout
sleepFor(RATE_SEARCH_TIMEOUT);
console.log("Sleeping between searches so we don't trigger rate limit...");
}
};
/** The Retweet worker - also performs Favorite and Follow actions if necessary */
var retweetWorker = function() {
// Check if we have elements in the Result Array
if (searchResultsArr.length) {
// Pop the first element (by doing a shift() operation)
var searchItem = searchResultsArr[0];
searchResultsArr.shift();
// Retweet
console.log("Retweeting", searchItem.id);
API.retweet(
searchItem.id_str,
function success() {
// On success, try to Favorite/Like and Follow
if (searchItem.text.toLowerCase().indexOf("fav") > -1 || searchItem.text.toLowerCase().indexOf("like") > -1) {
API.favorite(searchItem.id_str);
console.log("Favorite", searchItem.id);
}
if (searchItem.text.toLowerCase().indexOf("follow") > -1) {
API.follow(searchItem.user.id_str);
console.log("Follow", searchItem.user.screen_name);
}
// Then, re-queue the RT Worker
setTimeout(function () {
retweetWorker();
}, RETWEET_TIMEOUT);
},
function error(errorCallback) {
// Currently will only apply to rate limit errors
if (errorCallback)
errorHandler(errorCallback);
console.error("RT Failed for", searchItem.id, ". Likely has already been retweeted. Adding to blacklist.");
// If the RT fails, blacklist it
badTweetIds.push(searchItem.id);
// Then, re-start the RT Worker
setTimeout(function () {
retweetWorker();
}, RETWEET_TIMEOUT);
}
);
} else { // no search results left in array
if (limitLockout) {
// we must schedule this to rerun, or else the program will exit when a lockout occurs
setTimeout(function () {
retweetWorker();
}, RATE_SEARCH_TIMEOUT);
return;
}
console.log("No more results... will search and analyze again in " + RATE_SEARCH_TIMEOUT / 1000 + " seconds.");
// go fetch new results
search();
setTimeout(function () {
retweetWorker();
}, RATE_SEARCH_TIMEOUT);
}
}
function sleepFor(sleepDuration) {
var now = new Date().getTime();
while (new Date().getTime() < now + sleepDuration) { /* do nothing */ }
}
// Initializing function, begins the program.
// First, gets the blocked users
API.getBlockedUsers(function (blockedList) {
blockedUsers = blockedList;
// Start searching (the Search is in itself a worker, as the callback continues to fetch data)
search();
// Start the Retweet worker after short grace period for search results to come in
setTimeout(function () {
retweetWorker();
}, 8000);
});
}());