├── README.txt └── bot.py /README.txt: -------------------------------------------------------------------------------- 1 | Web-based Snapsore Bot 2 | 3 | Selenium bot configued with undetectable chromedriver to increase Snapchat score. 4 | 5 | [[ This program is functional, but the Snapchat web version does not seem to be configured to handle mass 6 | snap send batches, resulting in extreme memory spikes. If I find a workaround to this issue I will 7 | update this code. ]] 8 | 9 | (in other words: slow memory leak on web.snapchat.com causing issues for continued use) 10 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Snapchat web snapscore bot. 3 | 4 | :author: Max Milazzo 5 | """ 6 | 7 | 8 | import undetected_chromedriver as uc 9 | from selenium.webdriver.common.by import By 10 | from time import time, sleep 11 | from random import random 12 | # manages imports 13 | 14 | 15 | MY_USERNAME = "EXAMPLE@gmail.com" 16 | MY_PASSWORD = "PASSWORD123" 17 | # username and password 18 | 19 | SLEEP_TIME = 1 20 | # sleep time between actions (multiplied by random number from 0-1 PLUS MIN_MULT) 21 | 22 | LOGIN_SLEEP = 1 23 | # sleep time between login actions 24 | 25 | SELECT_SLEEP = 0.01 26 | # sleep time for chat selection (multiplied by random numbr from 0-1 PLUS MIN_MULT) 27 | 28 | MIN_MULT = 0.05 29 | # minimum value to add to random multiplier for sleeps 30 | 31 | MIN_ADD = 0 32 | # minimum value to add to random sleep times 33 | 34 | CHAT_SELECTIONS = 100 35 | # maximum value of chat selection (will select chats from index 1 to CHAT_SELECTIONS value) 36 | # snaps will be sent to the FIRST "n" groups/people based on Snapchat's default (based on previoud send activity) 37 | # if CHAT_SELECTIONS = N, then N snaps will be sent per "batch" 38 | 39 | ERR_SEND_RATE_MULT = 20 40 | # estimation of number of additional "lost" snaps per failed batch 41 | # a previously failed batch can interfere with the next batch, and even if successful it can cause some sends to fail 42 | 43 | FIRST_SEND_SLEEP = 30 44 | # waits extra time after the first snap send 45 | 46 | USERNAME = '//*[@id="username"]' 47 | PASSWORD = '//*[@id="password"]' 48 | LOGIN = '//*[@id="loginTrigger"]' 49 | # login XPATH data 50 | 51 | CAMERA = '//*[@id="root"]/div[1]/div[2]/div/div/div/div/div[1]/div/div/div/div/div/div[2]/div/div/div[1]/button[1]' 52 | SEND_1 = '//*[@id="root"]/div[1]/div[2]/div/div/div/div/div[1]/div/div/div/div/div/button[2]' 53 | SEND_2 = '//*[@id="root"]/div[1]/div[2]/div/div/div/div/div[1]/div/div/div/div/div[1]/form/div[4]/button' 54 | RESTART_CAM = '//*[@id="root"]/div[1]/div[2]/div/div/div/div/div[1]/div/div/div/div/div/button' 55 | TEST_OPEN = '//*[@id="root"]/div[1]/div[1]/div[1]/div[3]/button' 56 | # element XPATH data 57 | # selection XPATH data stored and handled in "send_pic" function 58 | 59 | batch_num = 1 60 | # batch number count 61 | 62 | err_count = 0 63 | # error counter 64 | 65 | successful_batches = 0 66 | # successful batch count 67 | 68 | failed_batches = 0 69 | # failed batch count 70 | 71 | send_count = 0 72 | # snap send counter 73 | 74 | 75 | def init(): 76 | """ 77 | Creates driver and opens site. 78 | 79 | :return: driver 80 | :rtype: webdriver 81 | """ 82 | 83 | driver = uc.Chrome(use_subprocess=True) 84 | driver.get("http://web.snapchat.com/") 85 | 86 | input("Wait for site to load (login button MUST be yellow), then press enter to continue...") 87 | 88 | return driver 89 | 90 | 91 | def login(driver): 92 | """ 93 | Logs into site. 94 | 95 | :param driver: current webdriver in use 96 | :type driver: webdriver 97 | """ 98 | 99 | username_elem = driver.find_element(By.XPATH, USERNAME) 100 | username_elem.send_keys(MY_USERNAME) 101 | # enters username 102 | 103 | sleep(LOGIN_SLEEP) 104 | 105 | password_elem = driver.find_element(By.XPATH, PASSWORD) 106 | password_elem.send_keys(MY_PASSWORD) 107 | # enters password 108 | 109 | sleep(LOGIN_SLEEP) 110 | 111 | login_elem = driver.find_element(By.XPATH, LOGIN) 112 | login_elem.click() 113 | # clicks login button 114 | 115 | 116 | def send_pic(driver): 117 | """ 118 | Sends a single snap to chats. 119 | 120 | :param driver: current webdriver in use 121 | :type driver: webdriver 122 | """ 123 | 124 | global batch_num 125 | global err_count 126 | global send_count 127 | global successful_batches 128 | global failed_batches 129 | # global variables used 130 | 131 | selected_count = 0 132 | batch_err = 0 133 | batch_start = time() 134 | # temporary local variables used 135 | 136 | sleep(SLEEP_TIME * (random() + MIN_MULT) + 3) 137 | # sleep(SLEEP_TIME * (random() + MIN_MULT) + MIN_ADD) 138 | # [ custom sleep time used for better results ] 139 | # waits before after next iteration called in main 140 | 141 | try: 142 | camera_elem = driver.find_element(By.XPATH, CAMERA) 143 | camera_elem.click() 144 | print("[ camera click SUCCESS ]") 145 | # clicks camera 146 | 147 | except Exception as e: 148 | err_count += 1 149 | batch_err += 1 150 | 151 | print("[ camera click FAILURE ]") 152 | print(f"Error #{err_count}:\n----------") 153 | print(e) 154 | 155 | sleep(SLEEP_TIME * (random() + MIN_MULT) + MIN_ADD) 156 | # waits after clicking camera 157 | 158 | try: 159 | send_1_elem = driver.find_element(By.XPATH, SEND_1) 160 | send_1_elem.click() 161 | print("[ send (1/2) click SUCCESS ]") 162 | # clicks "send" 163 | 164 | except Exception as e: 165 | err_count += 1 166 | batch_err += 1 167 | 168 | print("[ send (1/2) click FAILURE ]") 169 | print(f"Error #{err_count}:\n----------") 170 | print(e) 171 | 172 | sleep(SLEEP_TIME * (random() + MIN_MULT) + 3) 173 | # sleep(SLEEP_TIME * (random() + MIN_MULT) + MIN_ADD) 174 | # [ custom sleep time used for better results ] 175 | # waits after "send" click 176 | 177 | try: 178 | for x in range(1, CHAT_SELECTIONS + 1): 179 | select_elem = driver.find_element(By.XPATH, f'//*[@id="root"]/div[1]/div[2]/div/div/div/div/div[1]/div/div/div/div/div[1]/form/div[3]/div/div[1]/div[{x}]/div[3]') 180 | # select button string 181 | # changes based on selection, so not included as global variable 182 | 183 | select_elem.click() 184 | 185 | selected_count += 1 186 | print(f"[ select ({x}/{CHAT_SELECTIONS}) click SUCCESS ]") 187 | 188 | sleep(SELECT_SLEEP * (random() + MIN_MULT) + MIN_ADD) 189 | # clicks select and waits 190 | 191 | except Exception as e: 192 | err_count += 1 193 | batch_err += 1 194 | 195 | print(f"[ select click FAILURE ]") 196 | print(f"Error #{err_count}:\n----------") 197 | print(e) 198 | 199 | sleep(SLEEP_TIME * (random() + MIN_MULT) + MIN_ADD) 200 | # waits additional time after selection 201 | 202 | try: 203 | send_2_elem = driver.find_element(By.XPATH, SEND_2) 204 | send_2_elem.click() 205 | send_count += selected_count 206 | print("[ send (2/2) click SUCCESS ]") 207 | # clicks final send 208 | 209 | except Exception as e: 210 | err_count += 1 211 | batch_err += 1 212 | 213 | print("[ send (2/2) click FAILURE ]") 214 | print(f"Error #{err_count}:\n----------") 215 | print(e) 216 | 217 | if batch_err == 0: # updates batch success/failure data 218 | successful_batches += 1 219 | else: 220 | failed_batches += 1 221 | 222 | cur_time = time() 223 | 224 | print("\n" + "=" * 80) 225 | print(f"Batch #{batch_num}:") 226 | print("=" * 80) 227 | print(f"BATCH COUNT: << {selected_count} snaps selected in batch >>") 228 | print(f"BASE TOTAL COUNT: << {send_count} snaps sent in total >>") 229 | print(f"EST. ACTUAL COUNT: << {round(send_count - failed_batches * ERR_SEND_RATE_MULT)} snaps sent in total >>") 230 | print(f"\nBATCH TIME: << {round(cur_time - batch_start, 3)} seconds elapsed in batch >>") 231 | print(f"TOTAL TIME: << {round(cur_time - START_TIME, 3)} seconds elapsed in total >>") 232 | print(f"\nBATCH ERRORS: << {batch_err} >>") 233 | print(f"TOTAL ERRORS: << {err_count} >>") 234 | print(f"\nCLEAN BATCHES: << {successful_batches} >>") 235 | print(f"ERRORED BATCHES: << {failed_batches} >>") 236 | print(f"\nBATCH RATE: << {round(selected_count / (cur_time - batch_start), 3)} snaps/second >>") 237 | print(f"CUM. BASE RATE: << {round(send_count / (cur_time - START_TIME), 3)} snaps/second >>") 238 | print(f"EST. CUM. ACTUAL RATE: << {round((send_count - failed_batches * ERR_SEND_RATE_MULT) / (cur_time - START_TIME), 3)} snaps/second >>") 239 | print("=" * 80 + "\n") 240 | # displays data 241 | 242 | if batch_num == 1: # extra sleep after first send 243 | sleep(FIRST_SEND_SLEEP) 244 | 245 | try: 246 | driver.find_element(By.XPATH, TEST_OPEN) 247 | # this data should always be present on page 248 | 249 | except: 250 | driver.refresh() 251 | sleep(5) 252 | # refresh if page error 253 | # some sleep value (can change based on user preference) 254 | 255 | try: 256 | start_cam = driver.find_element(By.XPATH, RESTART_CAM) 257 | start_cam.click() 258 | # attempts to click "cam restart" button if needed 259 | 260 | except: 261 | pass 262 | 263 | batch_num += 1 264 | 265 | 266 | def main(): 267 | """ 268 | Program entry point. 269 | """ 270 | 271 | global START_TIME 272 | 273 | driver = init() 274 | login(driver) 275 | # gets driver and logs in 276 | 277 | input("Complete camera setup and press enter to start...") 278 | print("<< PROGRAM RUNNING >>") 279 | 280 | START_TIME = time() 281 | 282 | while (True): # loops to send snaps indefinitely 283 | send_pic(driver) 284 | 285 | 286 | if __name__ == "__main__": 287 | main() --------------------------------------------------------------------------------