-
Notifications
You must be signed in to change notification settings - Fork 21
/
twitterbot.py
executable file
·434 lines (390 loc) · 14.2 KB
/
twitterbot.py
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 7 10:12:13 2019
@author: edoardottt
version = 1.3.3.3
This is the main file.
This file must be execute from the command line because
It has to read the input arguments (via getopt).
It searches for tweet links found in your personal Feed
via search input field.
It can: Put likes on tweets and retweets them.
It defines a class called TwitterBot.
TwitterBot elements are built with 6 elements:
username, password, likes ,retweets, keywords, followers.
This file is under MIT License.
"""
# ------- REQUIRED LIBRARIES -------
import time
import getopt
import socket
import getpass
import random
import datetime
import check_user
import add_result
import analyze_stat
import sys
import usage
# ------- VARIABLES -------
email_email = "" # user's username
email_password = "" # user's password
password_flag = False # True if the password has been entered
keywords = [] # the list of words you want to search in the input field
connection = 3 # time to wait the website load the content completely
keywords_flag = False # True if the -k option has been entered
stat_flag = False # True if the -s option has been entered
my_flag = False # True if the -m option has been entered
info_flag = False # True if the -i option has been entered
help_flag = False # True if the -h option has been entered
follow_flag = False # True if the -f option has been entered
limit = 1000 # Limit of the links crawled
website = "https://github.com/edoardottt/twitterBot"
version = "v1.3.3.3"
def intro(website, version):
logo = """ _ _ _ _ ____ _
| |___ _(_) |_| |_ ___ _ __| __ ) ___ | |_
| __\ \ /\ / / | __| __/ _ \ '__| _ \ / _ \| __|
| |_ \ V V /| | |_| || __/ | | |_) | (_) | |_
\__| \_/\_/ |_|\__|\__\___|_| |____/ \___/ \__|
"""
print(logo)
print(website)
print(version)
# write logs into log file
def write_log(ex):
f = open("twitterBot_log.txt", "a+")
f.write("-----------" + str(datetime.datetime.now()) + "----------\n")
f.write(str(ex) + "\r\n")
f.close()
# Function that prints dinamically
def print_wait_links():
for i in range(5):
print("Collecting links" + "." * i, end="\r")
time.sleep(1)
print("")
# ------- IMPORT (OR DOWNLOAD) EXTERNAL LIBRARIES -------
try:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
except Exception as ex:
write_log(ex)
usage.print_usage(3)
# ------- READ INPUT FROM CLI -------
intro(website, version)
try:
options, remainder = getopt.getopt(
sys.argv[1:],
"u:k:smihf:",
["username", "keywords", "stat", "mine", "info", "help", "follow"],
) # all the options allowed
for opt, arg in options:
if opt in ("-u", "--username"):
email_email = arg
elif opt in ("-k", "--keywords"):
try:
keywords_flag = True
keywords = arg.split(
","
) # if more than one keyword have been entered, split them and put into keywords
except Exception as ex:
write_log(ex)
usage.print_usage(0)
elif opt in ("-s", "--stat"):
stat_flag = True
elif opt in ("-m", "--mine"):
my_flag = True
elif opt in ("-i", "--info"):
info_flag = True
elif opt in ("-h", "--help"):
help_flag = True
elif opt in ("-f", "--follow"):
follow_flag = True
follow_user = arg
except Exception as ex:
write_log(ex)
usage.print_usage(0)
def check_connection(host="8.8.8.8", port=53):
"""
Check internet connection status
Host: 8.8.8.8 (google-public-dns-a.google.com)
OpenPort: 53/tcp
Service: domain (DNS/TCP)
"""
try:
socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
return True
except Exception as ex:
write_log(ex)
return False
# ------- TWITTERBOT CLASS -------
class TwitterBot:
def __init__(self, username, password, likes, retweets, keywords, followers):
self.username = username
self.password = password
self.likes = 0
self.retweets = 0
self.followers = 0
self.keywords = keywords
self.links = []
self.bot = webdriver.Firefox()
# It returns a random value between 5 and 8. That number indicates
# the seconds to be wait
def generate_random(self):
rand = random.randint(5, 8)
return rand
# Like a coin toss. 1/2 True - 1/2 False
def generate_mid_random(self):
rand = random.randint(1, 2)
if rand == 1:
return True
return False
def close(self):
self.bot.close()
# login
def login(self):
bot = self.bot
bot.get("https://twitter.com/") # Get the content of https://twitter.com/
time.sleep(
self.generate_random()
) # This line of code WHEREVER wait for n [5-8] seconds.
try:
email = bot.find_element_by_class_name(
"email-input"
) # Get the email input field.
password = bot.find_element_by_name(
"session[password]"
) # Get the password input field.
email.clear() # clear the email input field
password.clear() # clear the password input field
email.send_keys(self.username) # confirm
password.send_keys(self.password)
password.send_keys(Keys.RETURN) # send the inputs entered
time.sleep(self.generate_random())
auth_flag = None
try:
auth_flag = bot.find_element_by_css_selector(
"h1.Icon.Icon--bird.bird-topbar-etched"
)
# auth_flag = get the bird icon on top of the page if the
# authentication is Okay
except Exception as ex:
write_log(ex)
usage.print_usage(1)
if auth_flag is not None: # if the user is authorized
follower_elem = bot.find_element_by_css_selector(
"li.ProfileCardStats-stat:nth-child(3) > a:nth-child(1) > span:nth-child(2)"
)
self.followers = follower_elem.get_attribute(
"data-count"
) # get the followers count
return True
except Exception as ex:
write_log(ex)
usage.print_usage(2)
# add tweets links from search input field by typing the hashtags entered
def add_links(self):
print_wait_links()
bot = self.bot
for keyword in self.keywords:
bot.get(
"https://twitter.com/search?q=" + keyword + "&src=typd"
) # search the i-th hashtag
time.sleep(connection)
bot.execute_script(
"window.scroll(0,document.body.scrollHeight)"
) # scroll the page
time.sleep(connection)
tweets = bot.find_elements_by_class_name(
"tweet"
) # handle all the tweets shown
self.links += [
elem.get_attribute("data-permalink-path") for elem in tweets
] # get all the links of the tweets
random.shuffle(self.links)
print(str(min(len(self.links), limit)) + " links added!")
# add tweets links from the personal feed
def add_links_my_home(self):
print_wait_links()
bot = self.bot
for _ in range(7):
bot.execute_script(
"window.scroll(0,document.body.scrollHeight)"
) # scroll the page
time.sleep(connection)
time.sleep(connection)
# handle all the tweets shown
tweets = bot.find_elements_by_class_name("tweet")
self.links = [
elem.get_attribute("data-permalink-path") for elem in tweets
] # get all the links of the tweets
random.shuffle(self.links)
print(str(min(len(self.links), limit)) + " links added!")
# put likes and maybe rwtweets all the tweets reached
def crawl(self):
print("TwitterBot started at " + str(datetime.datetime.now())[:-7] + " !")
for link in self.links:
if (link is not None) and (self.likes < limit):
# if the tweets reached don't overcome the limit
# get the tweet page
self.bot.get("https://twitter.com" + link)
time.sleep(connection)
try:
self.bot.find_element_by_class_name(
"HeartAnimation"
).click() # like
self.likes += 1
time.sleep(1)
if self.generate_mid_random(): # True probability = 0.5
# retweet the tweet
self.bot.find_element_by_css_selector(
"button.ProfileTweet-actionButton.js-actionButton.js-actionRetweet"
).click()
time.sleep(2)
# confirm the retweet
self.bot.find_element_by_class_name(
"RetweetDialog-retweetActionLabel"
).click()
self.retweets += 1
# print the info
result = (
" | likes: "
+ str(self.likes)
+ " | "
+ "retweets: "
+ str(self.retweets)
)
print(str(datetime.datetime.now())[:-7] + result, end="\r")
time.sleep(self.generate_random())
except Exception as ex:
write_log(ex)
time.sleep(10)
print("")
print("Finished!")
def follow_people(self, user):
time_now = str(datetime.datetime.now())[:-7]
print("TwitterBot started at " + time_now + " !")
self.bot.get("https://twitter.com/" + user)
time.sleep(connection)
self.bot.find_element_by_css_selector(
"li.ProfileNav-item:nth-child(2) > a:nth-child(1) > span:nth-child(3)"
).click()
for i in range(10):
self.bot.execute_script(
"window.scroll(0,document.body.scrollHeight)"
) # scroll the page
time.sleep(connection)
people = self.bot.find_elements_by_class_name("u-linkComplex-target")
people = [elem.text for elem in people]
random.shuffle(people)
print(str(len(people)) + " accounts reached!")
i = 0
for elem in people:
self.bot.get("https://twitter.com/" + elem)
time.sleep(connection)
try:
i += 1
item = self.bot.find_element_by_css_selector(
"span.user-actions-follow-button:nth-child(2) > button:nth-child(1)"
)
item.click()
except Exception as ex:
write_log(ex)
continue
result = " | followed: " + str(i) + " |"
print(str(datetime.datetime.now())[:-7] + result, end="\r")
time.sleep(connection)
print("")
print("Finished!")
# ------- CREATE A BOT AND LOGIN -------
def build_Edo():
email_password = getpass.getpass(
"Insert password for " + email_email + ":"
) # password input via getpass
edoBot = TwitterBot(
email_email, email_password, 0, 0, keywords, 0
) # create the bot
network_status = check_connection()
if not network_status:
usage.print_usage(6) # if the network connection isn't active
authenticated = edoBot.login() # login
if authenticated:
if check_user.check_if_user_exists(edoBot.username, edoBot.password):
print("Welcome back, " + edoBot.username + " !")
else:
print("Logged in as " + edoBot.username + " !")
return edoBot
# ------- READ OPTIONS AND EXECUTE ACTIONS -------
# if -u and ( -m OR -k)
if (
(email_email != "")
and (not stat_flag)
and (not follow_flag)
and ((my_flag and (not keywords_flag)) or (keywords_flag and (not my_flag)))
):
edoBot = build_Edo()
if keywords_flag and (not my_flag):
edoBot.add_links()
elif my_flag and (not keywords_flag):
edoBot.add_links_my_home()
else:
usage.print_usage(8)
edoBot.crawl() # here start the bot
edoBot.close()
timee = datetime.datetime.now()
add_result.add_stat(
edoBot.username, timee, edoBot.likes, edoBot.retweets, edoBot.followers
)
# if -u and -s
elif (
(email_email != "")
and (not keywords_flag)
and stat_flag
and (not my_flag)
and (not info_flag)
and (not help_flag)
and (not follow_flag)
):
email_password = getpass.getpass(
"Insert password for " + email_email + ":"
) # password input via getpass
analyze_stat.check_stat(email_email, email_password)
# if -i
elif (
(email_email == "")
and (not stat_flag)
and (not my_flag)
and (not keywords_flag)
and (not help_flag)
and (info_flag)
and (not follow_flag)
):
usage.print_usage(7)
# if -h
elif (
(email_email == "")
and (not stat_flag)
and (not my_flag)
and (not keywords_flag)
and help_flag
and (not info_flag)
and (not follow_flag)
):
usage.print_usage(0)
# if -f
elif (
(email_email != "")
and (not keywords_flag)
and not (stat_flag)
and follow_flag
and (not my_flag)
and (not info_flag)
and (not help_flag)
):
if follow_user == "" or not follow_user:
usage.print_usage(8)
edoBot = build_Edo()
edoBot.follow_people(follow_user)
edoBot.close()
else:
usage.print_usage(8)