├── README.md └── sns.py /README.md: -------------------------------------------------------------------------------- 1 | # sns-cart 2 | 3 | very basic python script to cart on sns. 4 | can handle logging in, url input, captcha harvesting, and product countdown. 5 | 6 | TODO: 7 | - [x] captcha harvest 8 | - [ ] create config file 9 | - [ ] logging 10 | - [X] handle captcha page (manually right now) 11 | - [ ] autocheckout 12 | - [X] proxy support 13 | - [X] timeout support 14 | 15 | *moving project to different language* 16 | -------------------------------------------------------------------------------- /sns.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | from selenium import webdriver 4 | from selenium.webdriver.common.keys import Keys 5 | import time 6 | import threading 7 | import thread 8 | import sys 9 | import Tkinter as tk 10 | import json 11 | from random import randint 12 | import cfscrape 13 | 14 | start_time = time.time() 15 | atc_flag = False 16 | botcheck_flag = False 17 | botcheck_gate = False 18 | proxy_flag = False 19 | login_flag = False 20 | restock_flag = False 21 | autocheckout = False 22 | countdown_retry_delay = 0 23 | 24 | # TODO: change most of this stuff in config file later 25 | url = 'http://sneakersnstuff.com' 26 | sizes = ['10'] 27 | accounts = [ 28 | { 29 | 'username':'', 30 | 'password':'' 31 | } 32 | ] 33 | 34 | proxies = [ 35 | { 36 | "http": "", 37 | "https": "" 38 | } 39 | ] 40 | 41 | tokens = [] 42 | ThreadCount = 1 43 | threads = [] 44 | 45 | #2captcha 46 | API_KEY = '' # Your 2captcha API KEY 47 | site_key = '6LflVAkTAAAAABzDDFKRJdb6RphdNfRPitO3xz2c' # site-key, read the 2captcha docs on how to get this 48 | s = requests.Session() 49 | #anticaptcha 50 | API_KEY_ANTI = '' 51 | a = requests.Session() 52 | # domain to solve captchas from 53 | urlc = 'http://sneakersnstuff.com' 54 | 55 | def addToCart(id, autocheckout, size): 56 | global url 57 | if botcheck_flag: 58 | chromedriver = webdriver.Chrome() 59 | chromedriver.get(urlc) 60 | 61 | global botcheck_gate 62 | while not botcheck_gate: 63 | continue 64 | 65 | headers = { 66 | "User-Agent": 67 | "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" 68 | } 69 | c = cfscrape.create_scraper() 70 | #c = requests.Session() 71 | c.headers.update(headers) 72 | 73 | if botcheck_flag == True: 74 | browser_cookies = chromedriver.get_cookies() 75 | #c.cookies.update( {cookie['name']:cookie['value'] for cookie in browser_cookies} ) 76 | for cookie in browser_cookies: 77 | c.cookies.set(cookie['name'], cookie['value'])#, path='/', domain=cookie['domain']) 78 | 79 | #set proxy 80 | if proxy_flag: 81 | if (len(proxies) > 0): 82 | c.proxies.update(proxies.pop()) 83 | #login process\ 84 | if login_flag: 85 | homepage = c.get("http://sneakersnstuff.com") 86 | homepage_content = BeautifulSoup(homepage.content, "html.parser") 87 | login_csrf = c.cookies['AntiCsrfToken'] 88 | 89 | LOGIN_HEADERS = { 90 | 'origin': "https://www.sneakersnstuff.com", 91 | "referer": "https://www.sneakersnstuff.com/", 92 | "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", 93 | "x-anticsrftoken": login_csrf, 94 | "x-requested-with": "XMLHttpRequest" 95 | } 96 | 97 | account = accounts.pop() 98 | login_payload = { 99 | 'ReturnURL': "", 100 | 'username': account["username"], 101 | 'password': account["password"], 102 | '_AntiCsrfToken': login_csrf 103 | } 104 | 105 | try: 106 | printToConsole("Logging in as %s" % (account["username"]), id) 107 | login_response = c.post('https://www.sneakersnstuff.com/en/authentication/login?skip_layout=1', data=login_payload, headers=LOGIN_HEADERS) 108 | login_response.raise_for_status() 109 | sys.stdout.flush() 110 | except requests.exceptions.HTTPError as err: 111 | sys.stdout.flush() 112 | if login_response.json(): 113 | printToConsole("%s" % str(login_response.json()["Status"]), id) 114 | if err: 115 | print(err) 116 | sys.stdout.flush() 117 | else: 118 | printToConsole("Status: %s - Succesfully logged in as %s" % (str(login_response.json()["Status"]), account["username"]), id) 119 | 120 | printToConsole("Ready to ATC!", id) 121 | 122 | progress = 0 123 | if not restock_flag: 124 | global atc_flag 125 | while not atc_flag: 126 | # if progress%10000000 == 0: 127 | # printToConsole("Standing By", id) 128 | # progress += 1 129 | continue 130 | 131 | #get product page info for product id 132 | #TODO: if size doesn't exist 133 | ID = -1 134 | first_load = 0 135 | while (ID == -1) or (ID is None): 136 | # currently must have url inputted or error :S 137 | url = url_entry.get() 138 | try: 139 | if (first_load == 0): 140 | printToConsole("Loading Product Page", id) 141 | first_load = 1 142 | response = c.get(url, timeout=10) 143 | except requests.exceptions.Timeout: 144 | printToConsole("Product Page Timed Out, retrying..", id) 145 | continue 146 | except requests.exceptions.HTTPError as err: 147 | printToConsole(err, id) 148 | soup = BeautifulSoup(response.content, "html.parser") 149 | #pull post parameters CSRF, partial, productID 150 | if 'AntiCsrfToken' in c.cookies: 151 | CSRF_TOKEN = c.cookies['AntiCsrfToken'] 152 | PARTIAL = 'cart-summary' 153 | size_spans = soup.find_all("span", class_="size-type") 154 | for span in size_spans: 155 | size_text = span.string.replace('\r','').replace('\n','').replace('\t','').replace('US','').strip() 156 | if size_text == size: 157 | size_div = span.find_parent("div", class_="size-button") 158 | ID = size_div.attrs.get("data-productid") 159 | if ID is None: 160 | printToConsole("ProductID not available, product is not live, or size is not available. Retrying...", id) 161 | if countdown_retry_delay > 0: 162 | time.sleep(countdown_retry_delay) 163 | if (ID == -1): 164 | printToConsole("Size does not exist or page is not returning as expected. Retrying...", id) 165 | if countdown_retry_delay > 0: 166 | time.sleep(countdown_retry_delay) 167 | 168 | # get add to cart request ready 169 | HEADERS = { 170 | 'origin': "https://www.sneakersnstuff.com", 171 | "referer": url, 172 | "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", 173 | "x-anticsrftoken": CSRF_TOKEN, 174 | "x-requested-with": "XMLHttpRequest" 175 | } 176 | 177 | if restock_flag: 178 | while not atc_flag: 179 | continue 180 | # if progress%100000000 == 0: 181 | # printToConsole("Standing By", id) 182 | # progress += 1 183 | 184 | # format atc payload (move this inside try to pop a new captcha response each time) 185 | PARTIAL = 'cart-summary' 186 | post_payload = {} 187 | if (len(tokens) > 0): 188 | post_payload = { 189 | 'g-recaptcha-response': tokens.pop(), 190 | '_AntiCsrfToken': CSRF_TOKEN, 191 | 'partial': PARTIAL, 192 | 'id': ID 193 | } 194 | label.config(text="captchas: " + str(len(tokens))) 195 | else: 196 | post_payload = { 197 | '_AntiCsrfToken': CSRF_TOKEN, 198 | 'partial': PARTIAL, 199 | 'id': ID 200 | } 201 | 202 | # send atc post request, retry i times if httperror 203 | # TODO: handle errors better, add scheduling, and figure out better retry protocol 204 | for i in range(1,60): 205 | try: 206 | printToConsole("ATC Request sent, attempt #%d" % (i), id) 207 | atc_response = c.post('https://www.sneakersnstuff.com/en/cart/add', data=post_payload, headers=HEADERS, timeout=30) 208 | atc_response.raise_for_status() 209 | except requests.exceptions.Timeout: 210 | printToConsole("ATC Request Timed Out, retrying..", id) 211 | if (len(tokens) > 0): 212 | post_payload = { 213 | 'g-recaptcha-response': tokens.pop(), 214 | '_AntiCsrfToken': CSRF_TOKEN, 215 | 'partial': PARTIAL, 216 | 'id': ID 217 | } 218 | label.config(text="captchas: " + str(len(tokens))) 219 | else: 220 | post_payload = { 221 | '_AntiCsrfToken': CSRF_TOKEN, 222 | 'partial': PARTIAL, 223 | 'id': ID 224 | } 225 | continue 226 | except requests.exceptions.HTTPError as err: 227 | if atc_response.json(): 228 | printToConsole("%s" % str(atc_response.json()["Status"]), id) 229 | #printToConsole("error") 230 | if err: 231 | #print(err) 232 | #sys.stdout.flush() 233 | if (len(tokens) > 0): 234 | post_payload = { 235 | 'g-recaptcha-response': tokens.pop(), 236 | '_AntiCsrfToken': CSRF_TOKEN, 237 | 'partial': PARTIAL, 238 | 'id': ID 239 | } 240 | label.config(text="captchas: " + str(len(tokens))) 241 | else: 242 | post_payload = { 243 | '_AntiCsrfToken': CSRF_TOKEN, 244 | 'partial': PARTIAL, 245 | 'id': ID 246 | } 247 | time.sleep(0.25) 248 | #time.sleep(randint(10,20)) 249 | continue 250 | else: 251 | printToConsole("[[SUCCESS]] Added to cart successfully, SIZE:%s attempt #%d" % (size, i), id) 252 | #printToConsole(c.cookies) 253 | break 254 | else: 255 | printToConsole("[[FAILED]] All attempts failed", id) 256 | 257 | #checkout stuff here 258 | if (autocheckout): 259 | # TODO: implement autocheckout 260 | print("test") 261 | elif (not autocheckout): 262 | # open up chrome instance to checkout manually 263 | printToConsole("Autocheckout Disabled: Opening Cart in Chrome", id) 264 | chromedriver2 = webdriver.Chrome() 265 | chromedriver2.get(url) 266 | time.sleep(0.5) 267 | chromedriver2.delete_all_cookies() 268 | for cookie in c.cookies: 269 | chromedriver2.add_cookie({ 270 | 'name': cookie.name, 271 | 'value': cookie.value 272 | # 'path': '/', 273 | # 'domain': cookie.domain 274 | }) 275 | sys.stdout.flush() 276 | chromedriver2.get("https://www.sneakersnstuff.com/en/cart/view") 277 | raw_input("Press enter to exit ;)\n") 278 | 279 | def startThreads(): 280 | printToConsole("THREADS STARTED") 281 | sys.stdout.flush() 282 | for i in range(len(sizes)): 283 | t = threading.Thread(target=addToCart, args=(i+1, autocheckout, sizes[i])) 284 | #t.daemon = True 285 | threads.append(t) 286 | t.start() 287 | 288 | def printToConsole(string, id=-1): 289 | # TODO: write to log file 290 | if id > -1: 291 | sys.stdout.write("[%s](Thread #%d) %s\n" % (time.ctime(), id, string)) 292 | else: 293 | sys.stdout.write("[%s] %s\n" % (time.ctime(), string)) 294 | sys.stdout.flush() 295 | 296 | def harvestCaptcha(): 297 | # TODO: handle captcha errors 298 | def startThread(): 299 | printToConsole('Requesting a captcha response from 2captcha') 300 | captcha_id = s.post("http://2captcha.com/in.php?key={}&method=userrecaptcha&googlekey={}&pageurl={}".format(API_KEY, site_key, urlc)).text.split('|')[1] 301 | # then we parse gresponse from 2captcha response 302 | recaptcha_answer = s.get("http://2captcha.com/res.php?key={}&action=get&id={}".format(API_KEY, captcha_id)).text 303 | while 'CAPCHA_NOT_READY' in recaptcha_answer: 304 | #print("waiting for captcha to be solved") 305 | sys.stdout.flush() 306 | time.sleep(3) 307 | recaptcha_answer = s.get("http://2captcha.com/res.php?key={}&action=get&id={}".format(API_KEY, captcha_id)).text 308 | recaptcha_answer = recaptcha_answer.split('|')[1] 309 | tokens.append(recaptcha_answer) 310 | label.config(text="captchas: " + str(len(tokens))) 311 | printToConsole("Captcha response recieved from 2captcha") 312 | #sleep for 2 minutes, delete token 313 | time.sleep(110) 314 | for token in tokens: 315 | if (token == recaptcha_answer): 316 | tokens.remove(token) 317 | label.config(text="captchas: " + str(len(tokens))) 318 | t = threading.Thread(target=startThread) 319 | threads.append(t) 320 | t.start() 321 | 322 | def harvestCaptcha2(): 323 | # TODO: handle captcha errors 324 | def startThread(): 325 | printToConsole('Requesting a captcha response from AntiCaptcha') 326 | headers = {'content-type': 'application/json'} 327 | request_data = { 328 | "clientKey":API_KEY_ANTI, 329 | "task": 330 | { 331 | "type":"NoCaptchaTaskProxyless", 332 | "websiteURL":url, 333 | "websiteKey":site_key 334 | }, 335 | "languagePool":"en" 336 | } 337 | response = a.post("https://api.anti-captcha.com/createTask", data=json.dumps(request_data), headers=headers) 338 | json_data = json.loads(response.text) 339 | taskID = -1 340 | if (json_data["errorId"] == 0): 341 | taskID = json_data["taskId"] 342 | 343 | request_data2 = { 344 | "clientKey":API_KEY_ANTI, 345 | "taskId":taskID 346 | } 347 | if (taskID != -1): 348 | response2 = a.post("https://api.anti-captcha.com/getTaskResult",data=json.dumps(request_data2), headers=headers) 349 | json_data2 = json.loads(response2.text) 350 | if json_data2["errorId"] == 0: 351 | token_waiting = True 352 | while(token_waiting): 353 | if json_data2["status"] == "ready": 354 | g_response = json_data2["solution"]["gRecaptchaResponse"] 355 | tokens.append(g_response) 356 | label.config(text="captchas: " + str(len(tokens))) 357 | printToConsole('Captcha response recieved from AntiCaptcha') 358 | #sleep for 2 minutes, delete token 359 | token_waiting = False 360 | time.sleep(110) 361 | for token in tokens: 362 | if (token == g_response): 363 | tokens.remove(token) 364 | label.config(text="captchas: " + str(len(tokens))) 365 | else: 366 | time.sleep(3) 367 | response2 = a.post("https://api.anti-captcha.com/getTaskResult",data=json.dumps(request_data2), headers=headers) 368 | json_data2 = json.loads(response2.text) 369 | t = threading.Thread(target=startThread) 370 | threads.append(t) 371 | t.start() 372 | 373 | def toggleAtcGate(): 374 | global atc_flag 375 | atc_flag = not atc_flag 376 | label_atc.config(text="atc flag: " + str(atc_flag)) 377 | 378 | def toggleBotCheckFlag(): 379 | global botcheck_gate 380 | botcheck_gate = not botcheck_gate 381 | 382 | # UI Elements 383 | root = tk.Tk() 384 | root.title("SNS") 385 | root.geometry("400x500") 386 | 387 | label = tk.Label(root, fg="dark green") 388 | label_atc = tk.Label(root, fg="dark green") 389 | label_atc.config(text="atc flag: " + str(atc_flag)) 390 | label.config(text="captchas: " + str(len(tokens))) 391 | label.grid(row=0, column=0, padx=(20,0)) 392 | label_atc.grid(row=2, column=0, padx=(20,0)) 393 | 394 | url_entry = tk.Entry(root, width=60) 395 | url_entry.grid(row=5, column=0, columnspan=2, padx=(15,0), pady=(5,0)) 396 | 397 | captcha2 = tk.Button(root, text='AntiCaptcha', pady=10, width=25, command=harvestCaptcha2) 398 | captcha = tk.Button(root, text='2Captcha', pady=10, width=25, command=harvestCaptcha) 399 | start = tk.Button(root, text='Start Threads / Login', pady=10, width=25, command=startThreads) 400 | botcheck = tk.Button(root, text='Bot Check Passed', pady=10, width=25, command=toggleBotCheckFlag) 401 | atc = tk.Button(root, text='Open ATC Gate', pady=10, width=25, command=toggleAtcGate) 402 | captcha.grid(row=0, column=1, padx=(25,50), pady=(50,10)) 403 | captcha2.grid(row=1, column=1, padx=(25,50), pady=(10,10)) 404 | start.grid(row=2, column=1, padx=(25,50), pady=20) 405 | botcheck.grid(row=3, column=1, padx=(25,50), pady=20) 406 | atc.grid(row=4, column=1, padx=(25,50), pady=10) 407 | root.mainloop() 408 | --------------------------------------------------------------------------------