forked from 8ORUZ7/github-autofollow-bot
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathautofollow.py
156 lines (129 loc) · 5.74 KB
/
autofollow.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
import random
import time
import logging
import threading
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
from scripts.utils import (
load_credentials,
get_user_inputs,
set_delay,
click_button,
display_intro,
get_user_agreement,
ask_for_2fa_verification,
get_follow_or_unfollow,
handle_2fa # Ensure handle_2fa is imported if you are using it
)
# Specify the path to the EdgeDriver executable
driver_service = Service(r'C:\Path\To\Your\msedgedriver.exe')
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
LOGO = r"""
___ _ _ _ ___ _ _ ___ _
/ __(_) |_| |_ _ _ __ _ | __|__| | |_____ __ _____ _ _ | _ ) ___| |_
| (_ | | _| ' \ || / _` | | _/ _ \ | / _ \ V V / -_) '_| | _ \/ _ \ _|
\___|_|\__|_||_\_,_\__, | |_|\___/_|_\___/\_/\_/\___|_| |___/\___/\__|
|___/
"""
# Global variable to control the stop command
stop_thread = False
def github_login(driver, username, password):
"""Logs in to GitHub."""
driver.get("https://github.com/login")
time.sleep(2)
driver.find_element(By.ID, "login_field").send_keys(username)
driver.find_element(By.ID, "password").send_keys(password)
driver.find_element(By.NAME, "commit").click()
time.sleep(2)
logging.info("Logged in successfully.")
def handle_accounts(driver, account_url, action, focus, page, delay, count):
"""Handles the logic for following or unfollowing users based on focus."""
target_url = f"{account_url}/{'stargazers?page=' if focus == 'stargazers' else '?tab=followers&page='}{page}"
logging.info(f"Navigating to {target_url}")
driver.get(target_url)
try:
WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//input[@type='submit' and @name='commit']")))
except TimeoutException:
logging.error("Timed out waiting for the page to load or buttons to be present.")
with open("page_source.html", "w", encoding="utf-8") as file:
file.write(driver.page_source)
logging.debug("Page source saved to page_source.html")
return False, count
buttons_xpath = {
"follow": "//input[@type='submit' and @name='commit' and @value='Follow']",
"unfollow": "//input[@type='submit' and @name='commit' and @value='Unfollow']"
}.get(action)
ignore_buttons_xpath = {
"follow": "//input[@type='submit' and @name='commit' and @value='Unfollow']",
"unfollow": "//input[@type='submit' and @name='commit' and @value='Follow']"
}.get(action)
if not buttons_xpath:
logging.error(f"Unknown action type: {action}.")
return False, count
all_buttons = driver.find_elements(By.XPATH, buttons_xpath)
if not all_buttons:
logging.info(f"No {action} buttons found on page {page}.")
if not driver.find_elements(By.XPATH, "//a[@aria-label='Next']"):
logging.info("No pagination controls found. Exiting.")
return False, count
logging.info("Moving to the next page.")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(random.uniform(0.5, 2)) # Random delay to mimic human scrolling
return True, count
logging.info(f"Found {len(all_buttons)} {action} buttons on page {page}.")
for button in all_buttons:
try:
driver.execute_script("arguments[0].scrollIntoView(true);", button)
WebDriverWait(driver, 3).until(EC.element_to_be_clickable(button))
time.sleep(random.uniform(0.1, 0.5)) # Random delay to simulate human behavior
count = click_button(driver, button, delay, count, action)
except (StaleElementReferenceException, TimeoutException) as e:
logging.error(f"Error with button interaction: {e}")
except Exception as e:
logging.error(f"Unexpected error: {e}")
return True, count
def listen_for_stop():
"""Listens for user input to stop the script."""
global stop_thread
while not stop_thread:
if input().strip().lower() == 'stop':
stop_thread = True
def main():
"""Main function to run the script."""
global stop_thread
display_intro(LOGO)
get_user_agreement()
action = get_follow_or_unfollow()
account_url, start_page, speed_mode, focus = get_user_inputs()
delay = set_delay(speed_mode)
github_username, github_password = load_credentials()
# Initialize the WebDriver before checking for 2FA
driver = webdriver.Edge(service=driver_service)
github_login(driver, github_username, github_password)
# Check for 2FA
if ask_for_2fa_verification():
handle_2fa(driver) # Call the 2FA handling function here
logging.info("Starting now")
stop_listener = threading.Thread(target=listen_for_stop)
stop_listener.start()
page = start_page
count = 0
try:
while not stop_thread:
action_success, count = handle_accounts(driver, account_url, action, focus, page, delay, count)
if not action_success:
logging.info(f"No {action} buttons found or pagination controls missing. Exiting.")
break
page += 1
except KeyboardInterrupt:
logging.info("Program interrupted by user.")
finally:
logging.info(f"Total accounts processed ({action}ed): {count}")
driver.quit()
if __name__ == "__main__":
main()