import os, io, time, random, logging, re, pytesseract, glob, pyautogui, traceback, cv2 import numpy as np from PIL import Image from dotenv import load_dotenv from selenium.webdriver.common.keys import Keys from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.chrome.options import Options import undetected_chromedriver as uc from pynput.mouse import Controller, Button from notifiers import get_notifier class TicketSwapBot: def __init__(self): load_dotenv() self.validate_env_vars() self.driver = uc.Chrome(headless=False,use_subprocess=False) self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") self.wait = WebDriverWait(self.driver, 10) self.refresh_wait_time = 5 self.pushbullet_api = os.getenv('PUSHBULLET_API') self.simplepush_api = os.getenv('SIMPLEPUSH_API') self.pushover_user = os.getenv('PUSHOVER_USERKEY') self.pushover_api = os.getenv('PUSHOVER_API') if self.simplepush_api != "": self.simplepush = get_notifier('simplepush') self.notifier_name = 'simplepush' elif self.pushover_api != "": self.pushover = get_notifier('pushover') self.notifier_name = 'pushover' elif self.pushbullet_api != "": self.pushbullet = get_notifier('pushbullet') self.notifier_name = 'pushbullet' else: logging.info("No notification service API key found in environment variables.") def validate_env_vars(self): required_env_vars = ['FACEBOOK_LOGIN', 'FACEBOOK_PASSWORD', 'EVENT_LINK'] for var in required_env_vars: if not os.getenv(var): raise EnvironmentError(f'Environment variable {var} is not set') def login(self): self.driver.get('https://www.ticketswap.com/login') button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[text()='Accept']"))) button.click() WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//button[@class='css-crruky e1dvqv261']"))).click() time.sleep(3) self.driver.switch_to.window(self.driver.window_handles[1]) cookie_popup_close_button = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//button[text()='Allow all cookies' or text()='Alle cookies toestaan']"))) cookie_popup_close_button.click() time.sleep(3) WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/input[@id='email']"))).send_keys(os.getenv('FACEBOOK_LOGIN')) WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/input[@id='pass']"))).send_keys(os.getenv('FACEBOOK_PASSWORD')) self.driver.find_element(By.XPATH, "//label[@id='loginbutton']").click() logging.info("Ticketswap login succesful") time.sleep(4) self.driver.switch_to.window(self.driver.window_handles[0]) time.sleep(4) self.cookies = self.driver.get_cookies() def search_event(self): url = os.getenv('EVENT_LINK') self.driver.get(url) for cookie in self.cookies: self.driver.add_cookie(cookie) logging.info(f"Starting to crawl: {url}") def refresh_event_page(self): # Loop to continuously check for the anti-bot button captcha_found = True while captcha_found: # Get the current window handle current_window = self.driver.current_window_handle # Switch to the current window (this will bring it to the foreground) self.driver.switch_to.window(current_window) while True: button = self.driver.find_elements(By.ID, "b") if button: logging.info("Anti-bot captcha found, trying to bypass") # Read the template image screenshot = self.driver.get_screenshot_as_png() pil_image = Image.open(io.BytesIO(screenshot)) np_image = np.array(pil_image) img_bgr = cv2.cvtColor(np_image, cv2.COLOR_RGB2BGR) # Convert the screenshot to grayscale gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # Apply Gaussian blur to reduce noise in the edge detection blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Apply Canny Edge Detection edges = cv2.Canny(blurred, threshold1=30, threshold2=100) # Find contours in the binary image contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) min_contour_area = 100 # adjust this value based on your needs contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_contour_area] # Load the template image and convert it to grayscale template = cv2.imread('template.png') if len(template.shape) == 3: # if the image has a channel dimension template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) # Detect edges in the template template_edges = cv2.Canny(template, threshold1=30, threshold2=100) # Find contours in the template template_contours, _ = cv2.findContours(template_edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Assume the largest contour in the template is the shape of the captcha template_contour = max(template_contours, key=cv2.contourArea) # Define the threshold threshold = 0.15 # Loop over the contours for contour in contours: match_score = cv2.matchShapes(template_contour, contour, cv2.CONTOURS_MATCH_I1, 0.0) # If the area is within a certain range, it might be the captcha if match_score < threshold: # Calculate the center of the contour M = cv2.moments(contour) x, y, w, h = cv2.boundingRect(contour) center_x = x + w // 2 center_y = y + h // 2 # Move the mouse to the center of the contour and click mouse = Controller() start_pos = mouse.position end_pos = (center_x, center_y) # the center of the button # move mouse to the button self.move_mouse_smoothly(mouse, mouse.position, (center_x, center_y)) # add a small random offset to the click position click_x = center_x + random.uniform(-5, 5) click_y = center_y + random.uniform(-5, 5) click_y = click_y + 110 # click the button mouse.position = (click_x, click_y) mouse.click(Button.left, 1) time.sleep(random.randint(1,2)) break else: # Exit the loop if the button is not found logging.info("Anti-bot captcha not found, proceeding") self.driver.refresh() # Random wait time between refreshes time.sleep(random.randint(5, 10)) break while True: try: tickets_element = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/h2[text()='Tickets']/following-sibling::span"))) tickets_text = tickets_element.text available_tickets = int(re.search(r'(\d+) available', tickets_text).group(1)) if available_tickets > 0: logging.info("Tickets available: " + str(available_tickets)) return # Exit the method if tickets are found elif available_tickets == 0: time.sleep(random.randint(5, 10)) self.driver.refresh() self.refresh_event_page() except Exception as e: logging.error(f"Error extracting tickets: {e}") logging.error(traceback.format_exc()) # Print the full traceback available_tickets = 0 while True: # Loop until the 403 error is resolved try: # Check the page title or content for signs of a 403 error page_title = self.driver.title if "403" in page_title: raise Exception("403 Forbidden error detected") else: break # Exit the loop if no 403 error is detected except Exception as e: if '403' in str(e): self.refresh_wait_time += 30 time.sleep(self.refresh_wait_time) self.driver.refresh() # Reload the page def move_mouse_smoothly(self, mouse, start_pos, end_pos): start_x, start_y = start_pos end_x, end_y = end_pos steps = 200 duration = 0.1 / 100 x_diff = end_x - start_x y_diff = end_y - start_y for i in range(steps): progress = i / steps x_offset = progress * x_diff y_offset = progress * y_diff noise = random.uniform(-5, 5) x = start_x + x_offset + noise y = start_y + y_offset + noise mouse.position = (int(x), int(y)) time.sleep(duration) def select_ticket(self): logging.error("Trying to select the first ticket") # Clicking on the ticket link ticket_link = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div/div/div/div/div/div[1]/div/a[1]/div"))) ticket_link.click() logging.error("Selected the first ticket") def buy_ticket(self): timeout = time.time() + 60 # 60-second timeout while True: if time.time() > timeout: logging.error("Timed out waiting for 'Buy ticket' button to disappear.") break buy_ticket_button = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "//button[text()='Buy ticket']"))) buy_button_text = buy_ticket_button.text if buy_button_text == 'Buy ticket': buy_ticket_button.click() # Call the click method with parentheses break def click_continue(self): try: continue_button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[text()='Continue']"))) continue_button.click() logging.info("Clicked 'Continue' button successfully.") except Exception as e: logging.error(f"Error clicking 'Continue' button: {e}") logging.error(traceback.format_exc()) def make_payment(self): try: # Click on credit card option credit_card_option = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[@id='STRIPE_MONEY_MACHINE_CREDITCARD']"))) credit_card_option.click() # Enter the credit card number cc_number_field = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//div[@id='card']"))) cc_number = os.getenv('CC_NUMBER') cc_number_field.send_keys(cc_number) # Enter the expiry date expiry_field = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//div[@id='expiry']"))) expiry_date = os.getenv('EXPIRY_DATE') expiry_field.send_keys(expiry_date) # Enter the CVV cvv_field = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//div[@id='cvc']/div[@class='__PrivateStripeElement']/iframe"))) cvv = os.getenv('CVV') cvv_field.send_keys(cvv) # Enter the name name_field = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[@id='ccname']"))) cc_name = os.getenv('CC_NAME') name_field.send_keys(cc_name) # Wait for 2 seconds time.sleep(2) # Click the Continue button continue_button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[text()='Continue']"))) continue_button.click() logging.info("Payment made successfully.") except Exception as e: logging.error(f"Error making payment: {e}") logging.error(traceback.format_exc()) def send_notification(self,purchase_status): # General message details message = f'Ticket has been {purchase_status} automatically' logging.info(message) url = self.driver.current_url # Check the notifier name and send the notification accordingly if self.notifier_name == 'simplepush': self.simplepush.notify(message=message + ' ' + url,key=self.simplepush_api,title=os.getenv('EVENT_NAME')) elif self.notifier_name == 'pushbullet': self.pushbullet.notify(message=message,token=self.pushbullet_api,title=os.getenv('EVENT_NAME'),url=url) elif self.notifier_name == 'pushover': self.pushover.notify(user=self.pushover_user,token=self.pushover_api,title=os.getenv('EVENT_NAME'),url=url,message=message) else: logging.warning("No valid notification service found.") def try_main(bot): try: while True: bot.refresh_event_page() bot.select_ticket() bot.buy_ticket() bot.click_continue() # Clicking the Continue button # Check if AUTO_PURCHASE is enabled auto_purchase = os.getenv('AUTO_PURCHASE') == 'True' if auto_purchase: bot.make_payment() # Making payment purchase_status = "purchased" else: purchase_status = "reserved" bot.send_notification(purchase_status) if not auto_purchase: time.sleep(1200) # Keep the browser open for 20 minutes for manual payment except Exception as e: logging.error(f"An error occurred: {e}") # If an exception occurs, restart the process try_main(bot) def main(): bot = TicketSwapBot() bot.login() bot.search_event() while True: bot.refresh_event_page() bot.select_ticket() try_main(bot) if __name__ == '__main__': logging.basicConfig(level=logging.INFO) main()