├── LICENSE ├── README.md ├── chromedriver.exe ├── debug.log ├── follow_API.py ├── follow_selenium.py ├── requirements.txt ├── unfollow_API.py └── unfollow_selenium.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ashwin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub-Follow-Bot 2 | 3 | A bot done with python to follow and unfollow users in GitHub. 4 | 5 | 6 | ## Releases: 7 | 8 | There is 2 releases the [Selenium](https://github.com/TheDarkAssassins/Github-Follow-Bot/releases/tag/Selenium) and the [GithubAPI](https://github.com/TheDarkAssassins/Github-Follow-Bot/releases/tag/GitHubAPI) 9 | - The [Selenium](https://github.com/TheDarkAssassins/Github-Follow-Bot/releases/tag/Selenium) release it faster then the GitHubAPI but it uses more CPU and it's unsafe 10 | - The [GithubAPI](https://github.com/TheDarkAssassins/Github-Follow-Bot/releases/tag/GitHubAPI) release it can be run in the background and it's slow but safe 11 | 12 | 13 | ## Why I did this? 14 | 15 | Somewhere, I read: 16 | - One in ten people you follow will follow you back. 17 | - One in hundred people you follow will star your repos. 18 | - One in thousand people you follow will fork your repos. 19 | 20 | Might be true, might be not 🤷‍. 21 | -------------------------------------------------------------------------------- /chromedriver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bot20200722/github-bot/cdae2d8f2ca3be1e56f540ddca849d84ef36a4f3/chromedriver.exe -------------------------------------------------------------------------------- /debug.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bot20200722/github-bot/cdae2d8f2ca3be1e56f540ddca849d84ef36a4f3/debug.log -------------------------------------------------------------------------------- /follow_API.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import argparse 4 | import tqdm 5 | import time 6 | from base64 import b64encode 7 | from datetime import datetime 8 | 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('-t', '--token', help="Your GitHub Personal Access Token", required=True) 11 | parser.add_argument('-m', '--my-username', help="Your GitHub Username", required=True) 12 | parser.add_argument('-u', '--user-target', help="User to grab followers from") 13 | parser.add_argument('-f', '--file', help="Follow users from a File pre-generated") 14 | parser.add_argument('-mf', '--max-followers', help="Max Number of Followers to Follow") 15 | args = parser.parse_args() 16 | 17 | HEADERS = {"Authorization": "Basic " + b64encode(str(args.my_username + ":" + args.token).encode('utf-8')).decode('utf-8')} 18 | 19 | res = requests.get("https://api.github.com/user", headers=HEADERS) 20 | 21 | if res.status_code != 200: 22 | print("Failure to Authenticate, please check Personal Access Token and Username!") 23 | exit(1) 24 | else: 25 | print("Authentication Succeeded!") 26 | sesh = requests.session() 27 | sesh.headers.update(HEADERS) 28 | if not args.file: 29 | 30 | target = args.user_target 31 | res = sesh.get("https://api.github.com/users/" + target + "/followers") 32 | 33 | 34 | links_array = requests.utils.parse_header_links(res.headers['Link'].rstrip('>').replace('>,<', ',<')) 35 | last_link = links_array[1]['url'] 36 | last_page = last_link.split('=')[-1] 37 | users_to_follow = [] 38 | mnof = args.max_followers 39 | print('Grabbing People to Follow\nThis may take a while... there are ' + str(last_page) + ' pages to go through.') 40 | for page in tqdm.tqdm(range(1, int(last_page)), ncols=35, smoothing=True, bar_format='[PROGRESS] {n_fmt}/{total_fmt} | {bar}'): 41 | res = sesh.get('https://api.github.com/users/' + target + "/followers?limit=100&page=" + str(page)).json() 42 | for user in res: 43 | users_to_follow.append(user['login']) 44 | if mnof != None: 45 | if len(users_to_follow) >= int(mnof): 46 | break 47 | filename = str(datetime.now().strftime('%m-%d-%YT%H-%M-%S')) + "-" + str(len(users_to_follow)) + "_FOLLOWED.json" 48 | with open(filename, 'w+') as f: 49 | json.dump(users_to_follow, f, indent=4) 50 | else: 51 | filename = args.file 52 | 53 | with open(filename, 'r+') as f: 54 | obj = json.load(f) 55 | print("Starting to Follow Users... This WILL Take a while, we must avoid being rate-limited!") 56 | for user in tqdm.tqdm(obj, ncols=35, smoothing=True, bar_format='[PROGRESS] {n_fmt}/{total_fmt} | {bar}'): 57 | while True: 58 | time.sleep(2) 59 | res = sesh.put('https://api.github.com/user/following/' + user) 60 | if res.status_code != 204: 61 | print(res.status_code) 62 | print("We may have been rate-limited, waiting until it stops!") 63 | time.sleep(60) 64 | else: 65 | break 66 | -------------------------------------------------------------------------------- /follow_selenium.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | from selenium import webdriver 4 | from selenium.webdriver.common.by import By 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | 8 | # Initializing the headless chrome 9 | driver = webdriver.Chrome() 10 | driver.get("https://github.com/login") 11 | wait = WebDriverWait(driver, 10) 12 | 13 | # Locating username and password field 14 | username = wait.until(EC.presence_of_element_located((By.ID, "login_field"))) 15 | password = wait.until(EC.presence_of_element_located((By.ID, "password"))) 16 | 17 | # password and username need to go into these values 18 | username.send_keys("enter your email") 19 | password.send_keys("enter your password") 20 | 21 | # Clicking the sign in button 22 | login_form = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@value='Sign in']"))) 23 | login_form.click() 24 | 25 | # Go to the followers tab 26 | prepend = ["Anteste", "andrewsyc"] 27 | 28 | for user in prepend: 29 | for t in range(1, 50): 30 | string = "https://github.com/{}?page={}&tab=followers".format(user, t) 31 | driver.get(string) 32 | time.sleep(1) 33 | 34 | follow_button = driver.find_elements_by_xpath("//input[@aria-label='Follow this person']") 35 | 36 | try: 37 | for i in follow_button: 38 | i.submit() 39 | except: 40 | pass 41 | time.sleep(1) 42 | 43 | driver.close() 44 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tqdm==4.33.0 2 | requests==2.23.0 3 | -------------------------------------------------------------------------------- /unfollow_API.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import argparse 4 | import tqdm 5 | import time 6 | from base64 import b64encode 7 | from datetime import datetime 8 | 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('-t', '--token', help="Your GitHub Personal Access Token", required=True) 11 | parser.add_argument('-m', '--my-username', help="Your GitHub Username", required=True) 12 | parser.add_argument('-u', '--user-target', help="User to grab followers from") 13 | parser.add_argument('-f', '--file', help="unfollow users from a File pre-generated") 14 | parser.add_argument('-a', '--all', help="Unfollow all your followers", action="store_true") 15 | parser.add_argument('-mf', '--max-followers', help="Max Number of Followers to unfollow") 16 | args = parser.parse_args() 17 | 18 | HEADERS = {"Authorization": "Basic " + b64encode(str(args.my_username + ":" + args.token).encode('utf-8')).decode('utf-8')} 19 | 20 | res = requests.get("https://api.github.com/user", headers=HEADERS) 21 | 22 | if res.status_code != 200: 23 | print("Failure to Authenticate, please check Personal Access Token and Username!") 24 | exit(1) 25 | else: 26 | print("Authentication Succeeded!") 27 | sesh = requests.session() 28 | sesh.headers.update(HEADERS) 29 | if not args.file: 30 | 31 | target = args.my_username 32 | res = sesh.get("https://api.github.com/users/" + target + "/following") 33 | 34 | 35 | links_array = requests.utils.parse_header_links(res.headers['Link'].rstrip('>').replace('>,<', ',<')) 36 | last_link = links_array[1]['url'] 37 | last_page = last_link.split('=')[-1] 38 | users_to_follow = [] 39 | mnof = args.max_followers 40 | print('Grabbing People to unfollow\nThis may take a while... there are ' + str(last_page) + ' pages to go through.') 41 | for page in tqdm.tqdm(range(1, int(last_page)), ncols=35, smoothing=True, bar_format='[PROGRESS] {n_fmt}/{total_fmt} | {bar}'): 42 | res = sesh.get('https://api.github.com/users/' + target + "/following?limit=100&page=" + str(page)).json() 43 | for user in res: 44 | users_to_follow.append(user['login']) 45 | if mnof != None: 46 | if len(users_to_follow) >= int(mnof): 47 | break 48 | filename = str(datetime.now().strftime('%m-%d-%YT%H-%M-%S')) + "-" + str(len(users_to_follow)) + "_FOLLOWING.json" 49 | with open(filename, 'w+') as f: 50 | json.dump(users_to_follow, f, indent=4) 51 | else: 52 | filename = args.file 53 | 54 | with open(filename, 'r+') as f: 55 | obj = json.load(f) 56 | print("Starting to unfollow Users... This WILL Take a while, we must avoid being rate-limited!") 57 | for user in tqdm.tqdm(obj, ncols=35, smoothing=True, bar_format='[PROGRESS] {n_fmt}/{total_fmt} | {bar}'): 58 | while True: 59 | time.sleep(2) 60 | res = sesh.delete('https://api.github.com/user/following/' + user) 61 | if res.status_code != 204: 62 | print(res.status_code) 63 | print("We may have been rate-limited, waiting until it stops!") 64 | time.sleep(60) 65 | else: 66 | break 67 | -------------------------------------------------------------------------------- /unfollow_selenium.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | from selenium import webdriver 4 | from selenium.webdriver.common.by import By 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | 8 | # Initializing the headless chrome 9 | driver = webdriver.Chrome() 10 | driver.get("https://github.com/login") 11 | wait = WebDriverWait(driver, 10) 12 | 13 | # Locating username and password field 14 | username = wait.until(EC.presence_of_element_located((By.ID, "login_field"))) 15 | password = wait.until(EC.presence_of_element_located((By.ID, "password"))) 16 | 17 | # password and username need to go into these values 18 | username.send_keys("username") 19 | password.send_keys("password") 20 | 21 | # Clicking the sign in button 22 | login_form = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@value='Sign in']"))) 23 | login_form.click() 24 | 25 | 26 | prepend = ["yourusername"] 27 | 28 | 29 | for user in prepend: 30 | for i in range(0, 200): 31 | for t in range(1, 100): 32 | string = "https://github.com/{}?tab=following&page={}".format(user, t) 33 | driver.get(string) 34 | time.sleep(1) 35 | 36 | follow_button = driver.find_elements_by_xpath("//input[@aria-label='Unfollow this person']") 37 | 38 | # time.sleep(1) 39 | # print len(follow_button) 40 | try: 41 | for i in follow_button: 42 | i.submit() 43 | except: 44 | pass 45 | time.sleep(1) 46 | 47 | 48 | 49 | driver.close() 50 | 51 | time.sleep(3) 52 | 53 | time.sleep(3) 54 | 55 | driver.close() 56 | --------------------------------------------------------------------------------