├── README.md ├── auth.py ├── config.py ├── keyword_config.py ├── keywords.txt ├── main.py ├── plugin_config.py ├── proxy_config.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # TweetScript 2 | A undetected Twitter bot using Selenium & Python 3 | -------------------------------------------------------------------------------- /auth.py: -------------------------------------------------------------------------------- 1 | username = "twitter username" 2 | password = "twitter password" -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # Do you want to use a proxy? [true|false] 2 | use_proxy = 'false' 3 | 4 | ## LIKE MODULE 5 | 6 | # Like tweets from keywords [true|false] 7 | like_from_keywords = 'true' 8 | 9 | # Limits [number of max likes] 10 | 11 | # If you have max likes set to 10 12 | # and you have 5 keywords each 13 | # keyword will get 2 likes 14 | 15 | max_likes_from_keywords = 100 -------------------------------------------------------------------------------- /keyword_config.py: -------------------------------------------------------------------------------- 1 | try: 2 | with open('keywords.txt', 'r') as file: 3 | keywords = [ line.rstrip() for line in file.readlines()] 4 | except FileNotFoundError: 5 | raise Exception('keywords.txt not found.') -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | KEYWORD_1 2 | KEYWORD_2 3 | KEYWORD_3 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # 🚀 This Project is in it's early stages of Development. 2 | # 📌 Working on new features and main menu. 3 | # ⚠️ Any Questions or Suggestions please Mail to: hendriksdevmail@gmail.com 4 | # 🖥 Version: 0.1 Beta 5 | 6 | import csv 7 | import os 8 | import platform 9 | import random 10 | import string 11 | import subprocess 12 | import sys 13 | import warnings 14 | import zipfile 15 | from random import choice, uniform 16 | from time import sleep 17 | 18 | import requests 19 | import undetected_chromedriver as uc 20 | from fake_headers import Headers 21 | from faker import Faker 22 | from selenium import webdriver 23 | from selenium.webdriver.common.action_chains import ActionChains 24 | from selenium.common.exceptions import (InvalidSessionIdException, 25 | NoSuchElementException, 26 | WebDriverException) 27 | from selenium.webdriver.common.by import By 28 | from selenium.webdriver.support import expected_conditions as EC 29 | from selenium.webdriver.support.ui import WebDriverWait 30 | from twocaptcha import TwoCaptcha 31 | 32 | import config 33 | import auth 34 | import proxy_config 35 | import keyword_config 36 | from plugin_config import background_js, manifest_json 37 | from selenium.webdriver.common.keys import Keys 38 | 39 | 40 | def clear(): 41 | # clearing the screen 42 | os.system('cls' if os.name == 'nt' else 'echo -e \\\\033c') 43 | 44 | 45 | clear() 46 | 47 | print("Getting Chromedriver") 48 | print("Proxy: {}".format(config.use_proxy)) 49 | OSNAME = platform.system() 50 | 51 | if OSNAME == 'Linux': 52 | OSNAME = 'lin' 53 | with subprocess.Popen(['google-chrome', '--version'], stdout=subprocess.PIPE) as proc: 54 | version = proc.stdout.read().decode('utf-8').replace('Google Chrome', '').strip() 55 | elif OSNAME == 'Darwin': 56 | OSNAME = 'mac' 57 | process = subprocess.Popen( 58 | ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '--version'], stdout=subprocess.PIPE) 59 | version = process.communicate()[0].decode( 60 | 'UTF-8').replace('Google Chrome', '').strip() 61 | elif OSNAME == 'Windows': 62 | OSNAME = 'win' 63 | process = subprocess.Popen( 64 | ['reg', 'query', 'HKEY_CURRENT_USER\\Software\\Google\\Chrome\\BLBeacon', '/v', 'version'], 65 | stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL 66 | ) 67 | version = process.communicate()[0].decode('UTF-8').strip().split()[-1] 68 | else: 69 | print('{} OS is not supported.'.format(OSNAME)) 70 | sys.exit() 71 | 72 | major_version = version.split('.')[0] 73 | 74 | uc.TARGET_VERSION = major_version 75 | 76 | uc.install() 77 | 78 | 79 | def type_me(element, text): 80 | """ 81 | Type like a human 82 | """ 83 | for letter in text: 84 | element.send_keys(letter) 85 | sleep(uniform(.1, .3)) 86 | 87 | options = webdriver.ChromeOptions() 88 | 89 | if config.use_proxy == "true": 90 | proxy_split = proxy.split(":") 91 | PROXY_HOST = proxy_split[0] 92 | PROXY_PORT = proxy_split[1] 93 | PROXY_USER = proxy_split[2] 94 | PROXY_PASS = proxy_split[3] 95 | 96 | pluginfile = 'proxy_auth_plugin.zip' 97 | with zipfile.ZipFile(pluginfile, 'w') as zp: 98 | zp.writestr("manifest.json", manifest_json) 99 | zp.writestr("background.js", background_js % 100 | (PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)) 101 | options.add_extension(pluginfile) 102 | 103 | print('Proxy: {}'.format(proxy)) 104 | else: 105 | pass 106 | 107 | header = Headers( 108 | browser="chrome", 109 | os=OSNAME, 110 | headers=False 111 | ).generate() 112 | agent = header['User-Agent'] 113 | prefs = {"profile.default_content_setting_values.notifications": 2} 114 | options.add_experimental_option("prefs", prefs) 115 | options.add_experimental_option('prefs', { 116 | 'credentials_enable_service': False, 117 | 'profile': { 118 | 'password_manager_enabled': False 119 | } 120 | }) 121 | options.add_argument('--no-sandbox') 122 | options.add_argument('--disable-dev-shm-usage') 123 | options.add_argument("--disable-web-security") 124 | options.add_argument('--headless') 125 | options.add_argument('--disable-gpu') 126 | viewport = ['1920,1080'] 127 | options.add_argument(f"--window-size={choice(viewport)}") 128 | options.add_argument("--log-level=3") 129 | options.add_argument('--disable-blink-features=AutomationControlled') 130 | options.add_experimental_option( 131 | "excludeSwitches", ["enable-automation", "enable-logging"]) 132 | options.add_experimental_option('useAutomationExtension', False) 133 | options.add_argument(f"user-agent={agent}") 134 | driver = webdriver.Chrome(options=options) 135 | 136 | wait = WebDriverWait(driver, 40) 137 | 138 | warnings.filterwarnings("ignore", category=DeprecationWarning) 139 | 140 | url = 'https://twitter.com/login' 141 | 142 | 143 | 144 | is_site_loading = True 145 | site_loaded = 'success' 146 | 147 | while(is_site_loading): 148 | try: 149 | driver.get(url) 150 | driver.execute_script( 151 | "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") 152 | sleep(1) 153 | loading_yes = driver.find_element_by_xpath( 154 | '/html/body/div/div/div/div[2]/main/div/div/div[1]/h1/span') 155 | is_site_loading = False 156 | 157 | except (NoSuchElementException, WebDriverException, InvalidSessionIdException) as e: 158 | site_loaded = 'false' 159 | is_site_loading = False 160 | 161 | if site_loaded == 'success': 162 | 163 | # enter username 164 | username = wait.until(EC.element_to_be_clickable( 165 | (By.XPATH, '//*[@id="react-root"]/div/div/div[2]/main/div/div/div[2]/form/div/div[1]/label/div/div[2]/div/input'))) 166 | type_me(username, auth.username) 167 | 168 | # enter password 169 | password = wait.until(EC.element_to_be_clickable( 170 | (By.XPATH, '/html/body/div/div/div/div[2]/main/div/div/div[2]/form/div/div[2]/label/div/div[2]/div/input'))) 171 | type_me(password, auth.password) 172 | 173 | # close pop up 174 | wait.until(EC.element_to_be_clickable( 175 | (By.XPATH, '/html/body/div/div/div/div[1]/div/div/div/div/div/div[2]'))).click() 176 | 177 | # click login 178 | wait.until(EC.element_to_be_clickable( 179 | (By.XPATH, '/html/body/div/div/div/div[2]/main/div/div/div[2]/form/div/div[3]/div'))).click() 180 | 181 | 182 | try: 183 | wait.until(EC.element_to_be_clickable( 184 | (By.XPATH, '/html/body/div/div/div/div[2]/main/div/div/div/div/div/div[1]/div[1]/div/div/div/div/div[1]/div/h2/span'))) 185 | print("Login Sucess | Username: {}".format(auth.username)) 186 | except (NoSuchElementException, WebDriverException, InvalidSessionIdException) as e: 187 | print("Login Failed | Username: {}".format(auth.username)) 188 | 189 | if config.like_from_keywords == "true": 190 | # Calc likes per keyword 191 | likes_per_keyword = config.max_likes_from_keywords / len(keyword_config.keywords) 192 | likes_per_keyword = round(likes_per_keyword) 193 | 194 | for keyword in keyword_config.keywords: 195 | keyword = keyword + ' lang:en' 196 | search = wait.until(EC.element_to_be_clickable( 197 | (By.XPATH, '//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[2]/div/div[2]/div/div/div/div[1]/div/div/div/form/div[1]/div/div/label/div[2]/div/input'))) 198 | type_me(search, keyword) 199 | 200 | driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[2]/div/div[2]/div/div/div/div[1]/div/div/div/form/div[1]/div/div/label/div[2]/div/input').send_keys(Keys.RETURN) 201 | 202 | wait.until(EC.element_to_be_clickable( 203 | (By.XPATH, '//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[1]/div/div[1]/div[2]/nav/div/div[2]/div/div[2]/a'))).click() 204 | 205 | sleep(5) 206 | 207 | liked = 0 208 | liked_counter = 1 209 | while likes_per_keyword > liked: 210 | try: 211 | try: 212 | element = driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[4]/div/div[3]/div'.format(liked_counter)) 213 | 214 | actions = ActionChains(driver) 215 | actions.move_to_element(element).perform() 216 | like_status = driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[4]/div/div[3]/div'.format(liked_counter)).get_attribute("data-testid") 217 | except (NoSuchElementException, WebDriverException, InvalidSessionIdException) as e: 218 | element = driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[3]/div/div[3]/div'.format(liked_counter)) 219 | 220 | actions = ActionChains(driver) 221 | actions.move_to_element(element).perform() 222 | like_status = driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[3]/div/div[3]/div'.format(liked_counter)).get_attribute("data-testid") 223 | if like_status == "like": 224 | user = driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[1]/div/div/div[1]/div[1]/a/div/div[2]/div/span'.format(liked_counter)).text 225 | try: 226 | driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[3]/div/div[3]/div'.format(liked_counter)).click() 227 | except (NoSuchElementException, WebDriverException, InvalidSessionIdException) as e: 228 | driver.find_element_by_xpath('/html/body/div/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/section/div/div/div[{}]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[4]/div/div[3]/div'.format(liked_counter)).click() 229 | liked_counter += 1 230 | liked += 1 231 | print() 232 | print('Liked Tweet by: {}'.format(user)) 233 | print('Liked Tweet(s): {}'.format(liked)) 234 | sleep(8) 235 | else: 236 | liked_counter += 1 237 | pass 238 | except (NoSuchElementException, WebDriverException, InvalidSessionIdException) as e: 239 | print("Unknown Error! Moving to next Keyword") 240 | liked = likes_per_keyword + 1 241 | pass 242 | 243 | wait.until(EC.element_to_be_clickable( 244 | (By.XPATH, '/html/body/div/div/div/div[2]/main/div/div/div/div/div/div[1]/div[1]/div/div/div/div/div[1]/div'))).click() 245 | 246 | 247 | 248 | else: 249 | pass 250 | 251 | sleep(10) 252 | driver.close() 253 | 254 | else: 255 | print('\033[31m' + 'Trying next proxy...' + '\033[0m') 256 | os.remove('proxy_auth_plugin.zip') 257 | -------------------------------------------------------------------------------- /plugin_config.py: -------------------------------------------------------------------------------- 1 | manifest_json = """ 2 | { 3 | "version": "1.0.0", 4 | "manifest_version": 2, 5 | "name": "Chrome Proxy", 6 | "permissions": [ 7 | "proxy", 8 | "tabs", 9 | "unlimitedStorage", 10 | "storage", 11 | "", 12 | "webRequest", 13 | "webRequestBlocking" 14 | ], 15 | "background": { 16 | "scripts": ["background.js"] 17 | }, 18 | "minimum_chrome_version":"22.0.0" 19 | } 20 | """ 21 | 22 | background_js = """ 23 | var config = { 24 | mode: "fixed_servers", 25 | rules: { 26 | singleProxy: { 27 | scheme: "http", 28 | host: "%s", 29 | port: parseInt(%s) 30 | }, 31 | bypassList: ["localhost"] 32 | } 33 | }; 34 | 35 | chrome.proxy.settings.set({value: config, scope: "regular"}, function() {}); 36 | 37 | function callbackFn(details) { 38 | return { 39 | authCredentials: { 40 | username: "%s", 41 | password: "%s" 42 | } 43 | }; 44 | } 45 | 46 | chrome.webRequest.onAuthRequired.addListener( 47 | callbackFn, 48 | {urls: [""]}, 49 | ['blocking'] 50 | ); 51 | """ -------------------------------------------------------------------------------- /proxy_config.py: -------------------------------------------------------------------------------- 1 | try: 2 | with open('proxies.txt', 'r') as file: 3 | proxy = [ line.rstrip() for line in file.readlines()] 4 | except FileNotFoundError: 5 | raise Exception('proxies.txt not found.') -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2captcha-python==1.1.0 2 | beautifulsoup4==4.9.3 3 | bs4==0.0.1 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | colorama==0.4.4 7 | fake-headers==1.0.2 8 | Faker==8.1.1 9 | html5lib==1.1 10 | idna==2.10 11 | python-dateutil==2.8.1 12 | requests==2.25.1 13 | selenium==3.141.0 14 | six==1.15.0 15 | soupsieve==2.2.1 16 | text-unidecode==1.3 17 | undetected-chromedriver==2.2.1 18 | urllib3>=1.26.5 19 | webencodings==0.5.1 20 | --------------------------------------------------------------------------------