#!/usr/bin/env python3 #encoding=utf-8 #執行方式:python chrome_tixcraft.py 或 python3 chrome_tixcraft.py import json import logging import os import pathlib import platform import random import re import sys import time from datetime import datetime from selenium import webdriver from selenium.common.exceptions import (NoAlertPresentException, NoSuchWindowException, UnexpectedAlertPresentException, WebDriverException) from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import Select, WebDriverWait #from DrissionPage import ChromiumPage logging.basicConfig() logger = logging.getLogger('logger') import warnings # for check kktix reg_info import requests from urllib3.exceptions import InsecureRequestWarning warnings.simplefilter('ignore',InsecureRequestWarning) import ssl ssl._create_default_https_context = ssl._create_unverified_context # ocr import base64 try: import ddddocr #PS: python 3.11.1 raise PIL conflict. from NonBrowser import NonBrowser except Exception as exc: pass import argparse import webbrowser import chromedriver_autoinstaller CONST_APP_VERSION = "MaxBot (2023.09.09)" CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt" CONST_MAXBOT_INT28_FILE = "MAXBOT_INT28_IDLE.txt" CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt" CONST_MAXBOT_QUESTION_FILE = "MAXBOT_QUESTION.txt" CONST_HOMEPAGE_DEFAULT = "https://tixcraft.com" URL_CHROME_DRIVER = 'https://chromedriver.chromium.org/' CONST_CHROME_VERSION_NOT_MATCH_EN="Please download the WebDriver version to match your browser version." CONST_CHROME_VERSION_NOT_MATCH_TW="請下載與您瀏覽器相同版本的WebDriver版本,或更新您的瀏覽器版本。" CONST_KKTIX_SIGN_IN_URL = "https://kktix.com/users/sign_in?back_to=%s" CONST_CITYLINE_SIGN_IN_URL = "https://www.cityline.com/Login.html?targetUrl=https%3A%2F%2Fwww.cityline.com%2FEvents.html" CONST_URBTIX_SIGN_IN_URL = "https://www.urbtix.hk/member-login" CONST_KHAM_SIGN_IN_URL = "https://kham.com.tw/application/UTK13/UTK1306_.aspx" CONST_TICKET_SIGN_IN_URL = "https://ticket.com.tw/application/utk13/utk1306_.aspx" CONST_HKTICKETING_SIGN_IN_URL = "https://premier.hkticketing.com/Secure/ShowLogin.aspx" CONST_FROM_TOP_TO_BOTTOM = "from top to bottom" CONST_FROM_BOTTOM_TO_TOP = "from bottom to top" CONST_RANDOM = "random" CONST_SELECT_ORDER_DEFAULT = CONST_FROM_TOP_TO_BOTTOM CONT_STRING_1_SEATS_REMAINING = ['@1 seat(s) remaining','剩餘 1@','@1 席残り'] CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER = "NonBrowser" CONST_OCR_CAPTCH_IMAGE_SOURCE_CANVAS = "canvas" CONST_WEBDRIVER_TYPE_SELENIUM = "selenium" CONST_WEBDRIVER_TYPE_UC = "undetected_chromedriver" CONST_WEBDRIVER_TYPE_DP = "DrissionPage" CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND = 4 def t_or_f(arg): ret = False ua = str(arg).upper() if 'TRUE'.startswith(ua): ret = True elif 'YES'.startswith(ua): ret = True return ret def format_config_keyword_for_json(user_input): if len(user_input) > 0: if not ('\"' in user_input): user_input = '"' + user_input + '"' if user_input[:1]=="{" and user_input[-1:]=="}": user_input=user_input[1:] user_input=user_input[:-1] if user_input[:1]=="[" and user_input[-1:]=="]": user_input=user_input[1:] user_input=user_input[:-1] return user_input def sx(s1): key=18 return ''.join(chr(ord(a) ^ key) for a in s1) def decryptMe(b): s="" if(len(b)>0): s=sx(base64.b64decode(b).decode("UTF-8")) return s def encryptMe(s): data="" if(len(s)>0): data=base64.b64encode(sx(s).encode('UTF-8')).decode("UTF-8") return data def get_app_root(): # 讀取檔案裡的參數值 basis = "" if hasattr(sys, 'frozen'): basis = sys.executable else: basis = sys.argv[0] app_root = os.path.dirname(basis) return app_root def get_config_dict(args): app_root = get_app_root() config_filepath = os.path.join(app_root, CONST_MAXBOT_CONFIG_FILE) # allow assign config by command line. if not args.input is None: if len(args.input) > 0: config_filepath = args.input config_dict = None if os.path.isfile(config_filepath): with open(config_filepath) as json_data: config_dict = json.load(json_data) # start to overwrite config settings. if not args.headless is None: headless_flag = t_or_f(args.headless) if headless_flag: config_dict["advanced"]["headless"] = True if not args.homepage is None: if len(args.homepage) > 0: config_dict["homepage"] = args.homepage if not args.ticket_number is None: if args.homepage > 0: config_dict["ticket_number"] = args.ticket_number if not args.browser is None: if len(args.browser) > 0: config_dict["browser"] = args.browser if not args.tixcraft_sid is None: if len(args.tixcraft_sid) > 0: config_dict["advanced"]["tixcraft_sid"] = encryptMe(args.tixcraft_sid) if not args.kktix_account is None: if len(args.kktix_account) > 0: config_dict["advanced"]["kktix_account"] = args.kktix_account if not args.kktix_password is None: if len(args.kktix_password) > 0: config_dict["advanced"]["kktix_password"] = args.kktix_password if not args.ibonqware is None: if len(args.ibonqware) > 0: config_dict["advanced"]["ibonqware"] = encryptMe(args.ibonqware) # special case for headless to enable away from keyboard mode. is_headless_enable = False if config_dict["advanced"]["headless"]: # for tixcraft headless. if len(config_dict["advanced"]["tixcraft_sid"]) > 1: is_headless_enable = True else: print("If you are runnig headless mode on tixcraft, you need input your cookie SID.") if is_headless_enable: config_dict["ocr_captcha"]["enable"] = True config_dict["ocr_captcha"]["force_submit"] = True return config_dict def write_question_to_file(question_text): outfile = None if platform.system() == 'Windows': outfile = open(CONST_MAXBOT_QUESTION_FILE, 'w', encoding='UTF-8') else: outfile = open(CONST_MAXBOT_QUESTION_FILE, 'w') if not outfile is None: outfile.write("%s" % question_text) def write_last_url_to_file(url): outfile = None if platform.system() == 'Windows': outfile = open(CONST_MAXBOT_LAST_URL_FILE, 'w', encoding='UTF-8') else: outfile = open(CONST_MAXBOT_LAST_URL_FILE, 'w') if not outfile is None: outfile.write("%s" % url) def read_last_url_from_file(): ret = "" with open(CONST_MAXBOT_LAST_URL_FILE, "r") as text_file: ret = text_file.readline() return ret def format_keyword_string(keyword): if not keyword is None: if len(keyword) > 0: keyword = keyword.replace('/','/') keyword = keyword.replace(' ','') keyword = keyword.replace(',','') keyword = keyword.replace(',','') keyword = keyword.replace('$','') keyword = keyword.replace(' ','').lower() return keyword def full2half(keyword): n = "" if not keyword is None: if len(keyword) > 0: for char in keyword: num = ord(char) if num == 0x3000: num = 32 elif 0xFF01 <= num <= 0xFF5E: num -= 0xfee0 n += chr(num) return n def get_chinese_numeric(): my_dict = {} my_dict['0']=['0','0','zero','零'] my_dict['1']=['1','1','one','一','壹','①','❶','⑴'] my_dict['2']=['2','2','two','二','貳','②','❷','⑵'] my_dict['3']=['3','3','three','三','叁','③','❸','⑶'] my_dict['4']=['4','4','four','四','肆','④','❹','⑷'] my_dict['5']=['5','5','five','五','伍','⑤','❺','⑸'] my_dict['6']=['6','6','six','六','陸','⑥','❻','⑹'] my_dict['7']=['7','7','seven','七','柒','⑦','❼','⑺'] my_dict['8']=['8','8','eight','八','捌','⑧','❽','⑻'] my_dict['9']=['9','9','nine','九','玖','⑨','❾','⑼'] return my_dict # 同義字 def synonyms(keyword): ret = [] my_dict = get_chinese_numeric() if keyword in my_dict: ret = my_dict[keyword] else: ret.append(keyword) return ret def normalize_chinese_numeric(keyword): ret = None my_dict = get_chinese_numeric() for i in my_dict: for item in my_dict[i]: if keyword.lower() == item: ret = int(i) break if not ret is None: break return ret def find_continuous_number(text): chars = "0123456789" return find_continuous_pattern(chars, text) def find_continuous_text(text): chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" return find_continuous_pattern(chars, text) def find_continuous_pattern(allowed_char, text): ret = "" is_allowed_char_start = False for char in text: #print("char:", char) if char in allowed_char: if len(ret)==0 and not is_allowed_char_start: is_allowed_char_start = True if is_allowed_char_start: ret += char else: # make not continuous is_allowed_char_start = False return ret def is_all_alpha_or_numeric(text): ret = False alpha_count = 0 numeric_count = 0 for char in text: try: if char.encode('UTF-8').isalpha(): alpha_count += 1 except Exception as exc: pass #if char.isnumeric(): if char.isdigit(): numeric_count += 1 if (alpha_count + numeric_count) == len(text): ret = True #print("text/is_all_alpha_or_numeric:",text,ret) return ret def get_favoriate_extension_path(webdriver_path): print("webdriver_path:", webdriver_path) extension_list = [] extension_list.append(os.path.join(webdriver_path,"Adblock_3.18.1.0.crx")) return extension_list def get_chromedriver_path(webdriver_path): chromedriver_path = os.path.join(webdriver_path,"chromedriver") if platform.system().lower()=="windows": chromedriver_path = os.path.join(webdriver_path,"chromedriver.exe") return chromedriver_path def get_brave_bin_path(): brave_path = "" if platform.system() == 'Windows': brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" if not os.path.exists(brave_path): brave_path = os.path.expanduser('~') + "\\AppData\\Local\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" if not os.path.exists(brave_path): brave_path = "C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" if not os.path.exists(brave_path): brave_path = "D:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" if platform.system() == 'Linux': brave_path = "/usr/bin/brave-browser" if platform.system() == 'Darwin': brave_path = '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser' return brave_path def get_chrome_options(webdriver_path, adblock_plus_enable, browser="chrome", headless = False): chrome_options = webdriver.ChromeOptions() if browser=="edge": chrome_options = webdriver.EdgeOptions() if browser=="safari": chrome_options = webdriver.SafariOptions() # some windows cause: timed out receiving message from renderer if adblock_plus_enable: # PS: this is ocx version. extension_list = get_favoriate_extension_path(webdriver_path) for ext in extension_list: if os.path.exists(ext): chrome_options.add_extension(ext) if headless: #chrome_options.add_argument('--headless') chrome_options.add_argument('--headless=new') chrome_options.add_argument('--disable-features=TranslateUI') chrome_options.add_argument('--disable-translate') chrome_options.add_argument('--lang=zh-TW') chrome_options.add_argument('--disable-web-security') chrome_options.add_argument("--no-sandbox"); chrome_options.add_argument("--disable-popup-blocking") chrome_options.add_argument("--disable-notifications") # for navigator.webdriver chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) # Deprecated chrome option is ignored: useAutomationExtension #chrome_options.add_experimental_option('useAutomationExtension', False) chrome_options.add_experimental_option("prefs", {"credentials_enable_service": False, "profile.password_manager_enabled": False, "translate":{"enabled": False}}) if browser=="brave": brave_path = get_brave_bin_path() if os.path.exists(brave_path): chrome_options.binary_location = brave_path chrome_options.page_load_strategy = 'eager' #chrome_options.page_load_strategy = 'none' chrome_options.unhandled_prompt_behavior = "accept" return chrome_options def load_chromdriver_normal(config_dict, driver_type): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True driver = None Root_Dir = get_app_root() webdriver_path = os.path.join(Root_Dir, "webdriver") chromedriver_path = get_chromedriver_path(webdriver_path) if not os.path.exists(webdriver_path): os.mkdir(webdriver_path) if not os.path.exists(chromedriver_path): print("WebDriver not exist, try to download to:", webdriver_path) chromedriver_autoinstaller.install(path=webdriver_path, make_version_dir=False) if not os.path.exists(chromedriver_path): print("Please download chromedriver and extract zip to webdriver folder from this url:") print("請下在面的網址下載與你chrome瀏覽器相同版本的chromedriver,解壓縮後放到webdriver目錄裡:") print(URL_CHROME_DRIVER) else: chrome_service = Service(chromedriver_path) chrome_options = get_chrome_options(webdriver_path, config_dict["advanced"]["adblock_plus_enable"], browser=config_dict["browser"], headless=config_dict["advanced"]["headless"]) try: driver = webdriver.Chrome(service=chrome_service, options=chrome_options) except Exception as exc: error_message = str(exc) if show_debug_message: print(exc) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) if "This version of ChromeDriver only supports Chrome version" in error_message: print(CONST_CHROME_VERSION_NOT_MATCH_EN) print(CONST_CHROME_VERSION_NOT_MATCH_TW) # remove exist chromedriver, download again. try: print("Deleting exist and download ChromeDriver again.") os.unlink(chromedriver_path) except Exception as exc2: print(exc2) pass chromedriver_autoinstaller.install(path=webdriver_path, make_version_dir=False) chrome_service = Service(chromedriver_path) try: chrome_options = get_chrome_options(webdriver_path, config_dict["advanced"]["adblock_plus_enable"], browser=config_dict["browser"], headless=config_dict["advanced"]["headless"]) driver = webdriver.Chrome(service=chrome_service, options=chrome_options) except Exception as exc2: print("Selenium 4.11.0 Release with Chrome For Testing Browser.") try: chrome_options = get_chrome_options(webdriver_path, config_dict["advanced"]["adblock_plus_enable"], browser=config_dict["browser"], headless=config_dict["advanced"]["headless"]) driver = webdriver.Chrome(service=Service(), options=chrome_options) except Exception as exc3: print(exc3) pass if driver_type=="stealth": from selenium_stealth import stealth # Selenium Stealth settings stealth(driver, languages=["zh-TW", "zh"], vendor="Google Inc.", platform="Win32", webgl_vendor="Intel Inc.", renderer="Intel Iris OpenGL Engine", fix_hairline=True, ) #print("driver capabilities", driver.capabilities) return driver def clean_uc_exe_cache(): exe_name = "chromedriver%s" platform = sys.platform if platform.endswith("win32"): exe_name %= ".exe" if platform.endswith(("linux", "linux2")): exe_name %= "" if platform.endswith("darwin"): exe_name %= "" d = "" if platform.endswith("win32"): d = "~/appdata/roaming/undetected_chromedriver" elif "LAMBDA_TASK_ROOT" in os.environ: d = "/tmp/undetected_chromedriver" elif platform.startswith(("linux", "linux2")): d = "~/.local/share/undetected_chromedriver" elif platform.endswith("darwin"): d = "~/Library/Application Support/undetected_chromedriver" else: d = "~/.undetected_chromedriver" data_path = os.path.abspath(os.path.expanduser(d)) is_cache_exist = False p = pathlib.Path(data_path) files = list(p.rglob("*chromedriver*?")) for file in files: if os.path.exists(str(file)): is_cache_exist = True try: os.unlink(str(file)) except Exception as exc2: print(exc2) pass return is_cache_exist def get_uc_options(uc, config_dict, webdriver_path): options = uc.ChromeOptions() options.page_load_strategy = 'eager' #options.page_load_strategy = 'none' options.unhandled_prompt_behavior = "accept" #print("strategy", options.page_load_strategy) if config_dict["advanced"]["adblock_plus_enable"]: load_extension_path = "" extension_list = get_favoriate_extension_path(webdriver_path) for ext in extension_list: ext = ext.replace('.crx','') if os.path.exists(ext): load_extension_path += ("," + os.path.abspath(ext)) if len(load_extension_path) > 0: print('load-extension:', load_extension_path[1:]) options.add_argument('--load-extension=' + load_extension_path[1:]) if config_dict["advanced"]["headless"]: #options.add_argument('--headless') options.add_argument('--headless=new') options.add_argument('--disable-features=TranslateUI') options.add_argument('--disable-translate') options.add_argument('--lang=zh-TW') options.add_argument('--disable-web-security') options.add_argument("--no-sandbox"); options.add_argument("--disable-popup-blocking") options.add_argument("--disable-notifications") options.add_argument("--password-store=basic") options.add_experimental_option("prefs", {"credentials_enable_service": False, "profile.password_manager_enabled": False, "translate":{"enabled": False}}) if config_dict["browser"]=="brave": brave_path = get_brave_bin_path() if os.path.exists(brave_path): options.binary_location = brave_path return options def load_chromdriver_uc(config_dict): import undetected_chromedriver as uc show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True Root_Dir = get_app_root() webdriver_path = os.path.join(Root_Dir, "webdriver") chromedriver_path = get_chromedriver_path(webdriver_path) if not os.path.exists(webdriver_path): os.mkdir(webdriver_path) if not os.path.exists(chromedriver_path): print("ChromeDriver not exist, try to download to:", webdriver_path) try: chromedriver_autoinstaller.install(path=webdriver_path, make_version_dir=False) except Exception as exc: print(exc) else: print("ChromeDriver exist:", chromedriver_path) driver = None if os.path.exists(chromedriver_path): # use chromedriver_autodownload instead of uc auto download. is_cache_exist = clean_uc_exe_cache() try: options = get_uc_options(uc, config_dict, webdriver_path) driver = uc.Chrome(driver_executable_path=chromedriver_path, options=options, headless=config_dict["advanced"]["headless"]) except Exception as exc: print(exc) error_message = str(exc) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) if "This version of ChromeDriver only supports Chrome version" in error_message: print(CONST_CHROME_VERSION_NOT_MATCH_EN) print(CONST_CHROME_VERSION_NOT_MATCH_TW) # remove exist chromedriver, download again. try: print("Deleting exist and download ChromeDriver again.") os.unlink(chromedriver_path) except Exception as exc2: print(exc2) pass try: chromedriver_autoinstaller.install(path=webdriver_path, make_version_dir=False) options = get_uc_options(uc, config_dict, webdriver_path) driver = uc.Chrome(driver_executable_path=chromedriver_path, options=options, headless=config_dict["advanced"]["headless"]) except Exception as exc2: print(exc2) pass else: print("WebDriver not found at path:", chromedriver_path) if driver is None: print('WebDriver object is None..., try again..') try: options = get_uc_options(uc, config_dict, webdriver_path) driver = uc.Chrome(options=options, headless=config_dict["advanced"]["headless"]) except Exception as exc: print(exc) error_message = str(exc) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) if "This version of ChromeDriver only supports Chrome version" in error_message: print(CONST_CHROME_VERSION_NOT_MATCH_EN) print(CONST_CHROME_VERSION_NOT_MATCH_TW) pass if driver is None: print("create web drive object by undetected_chromedriver fail!") if os.path.exists(chromedriver_path): print("Unable to use undetected_chromedriver, ") print("try to use local chromedriver to launch chrome browser.") driver_type = "selenium" driver = load_chromdriver_normal(config_dict, driver_type) else: print("建議您自行下載 ChromeDriver 到 webdriver 的資料夾下") print("you need manually download ChromeDriver to webdriver folder.") return driver def close_browser_tabs(driver): if not driver is None: try: window_handles_count = len(driver.window_handles) if window_handles_count > 1: driver.switch_to.window(driver.window_handles[1]) driver.close() driver.switch_to.window(driver.window_handles[0]) except Exception as excSwithFail: pass def get_driver_by_config(config_dict): global driver # read config. homepage = config_dict["homepage"] # output config: print("maxbot app version:", CONST_APP_VERSION) print("python version:", platform.python_version()) print("platform:", platform.platform()) print("homepage:", homepage) print("browser:", config_dict["browser"]) print("ticket_number:", str(config_dict["ticket_number"])) print(config_dict["tixcraft"]) print("==[advanced config]==") print(config_dict["advanced"]) print("webdriver_type:", config_dict["webdriver_type"]) # entry point if homepage is None: homepage = "" if len(homepage) == 0: homepage = CONST_HOMEPAGE_DEFAULT Root_Dir = get_app_root() webdriver_path = os.path.join(Root_Dir, "webdriver") print("platform.system().lower():", platform.system().lower()) if config_dict["browser"] in ["chrome","brave"]: # method 6: Selenium Stealth if config_dict["webdriver_type"] == CONST_WEBDRIVER_TYPE_SELENIUM: driver = load_chromdriver_normal(config_dict, config_dict["webdriver_type"]) if config_dict["webdriver_type"] == CONST_WEBDRIVER_TYPE_UC: # method 5: uc # multiprocessing not work bug. if platform.system().lower()=="windows": if hasattr(sys, 'frozen'): from multiprocessing import freeze_support freeze_support() driver = load_chromdriver_uc(config_dict) if config_dict["webdriver_type"] == CONST_WEBDRIVER_TYPE_DP: #driver = ChromiumPage() pass if config_dict["browser"] == "firefox": # default os is linux/mac # download url: https://github.com/mozilla/geckodriver/releases chromedriver_path = os.path.join(webdriver_path,"geckodriver") if platform.system().lower()=="windows": chromedriver_path = os.path.join(webdriver_path,"geckodriver.exe") if "macos" in platform.platform().lower(): if "arm64" in platform.platform().lower(): chromedriver_path = os.path.join(webdriver_path,"geckodriver_arm") webdriver_service = Service(chromedriver_path) driver = None try: from selenium.webdriver.firefox.options import Options options = Options() if config_dict["advanced"]["headless"]: options.add_argument('--headless') #options.add_argument('--headless=new') if platform.system().lower()=="windows": binary_path = "C:\\Program Files\\Mozilla Firefox\\firefox.exe" if not os.path.exists(binary_path): binary_path = os.path.expanduser('~') + "\\AppData\\Local\\Mozilla Firefox\\firefox.exe" if not os.path.exists(binary_path): binary_path = "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe" if not os.path.exists(binary_path): binary_path = "D:\\Program Files\\Mozilla Firefox\\firefox.exe" options.binary_location = binary_path driver = webdriver.Firefox(service=webdriver_service, options=options) except Exception as exc: error_message = str(exc) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) else: print(exc) if config_dict["browser"] == "edge": # default os is linux/mac # download url: https://developer.microsoft.com/zh-tw/microsoft-edge/tools/webdriver/ chromedriver_path = os.path.join(webdriver_path,"msedgedriver") if platform.system().lower()=="windows": chromedriver_path = os.path.join(webdriver_path,"msedgedriver.exe") webdriver_service = Service(chromedriver_path) chrome_options = get_chrome_options(webdriver_path, config_dict["advanced"]["adblock_plus_enable"], browser="edge", headless=config_dict["advanced"]["headless"]) driver = None try: driver = webdriver.Edge(service=webdriver_service, options=chrome_options) except Exception as exc: error_message = str(exc) #print(error_message) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) if config_dict["browser"] == "safari": driver = None try: driver = webdriver.Safari() except Exception as exc: error_message = str(exc) #print(error_message) left_part = None if "Stacktrace:" in error_message: left_part = error_message.split("Stacktrace:")[0] print(left_part) if driver is None: print("create web driver object fail @_@;") else: try: if 'kktix.c' in homepage: if len(config_dict["advanced"]["kktix_account"])>0: if not 'https://kktix.com/users/sign_in?' in homepage: homepage = CONST_KKTIX_SIGN_IN_URL % (homepage) if 'famiticket.com' in homepage: pass if 'ibon.com' in homepage: pass if 'kham.com' in homepage: if len(config_dict["advanced"]["kham_account"])>0: homepage = CONST_KHAM_SIGN_IN_URL if 'ticket.com.tw' in homepage: if len(config_dict["advanced"]["ticket_account"])>0: homepage = CONST_TICKET_SIGN_IN_URL if 'urbtix.hk' in homepage: if len(config_dict["advanced"]["urbtix_account"])>0: homepage = CONST_URBTIX_SIGN_IN_URL if 'cityline.com' in homepage: if len(config_dict["advanced"]["cityline_account"])>0: homepage = CONST_CITYLINE_SIGN_IN_URL if 'hkticketing.com' in homepage: if len(config_dict["advanced"]["hkticketing_account"])>0: homepage = CONST_HKTICKETING_SIGN_IN_URL if 'galaxymacau.com' in homepage: pass print("goto url:", homepage) driver.get(homepage) time.sleep(3.0) tixcraft_family = False if 'tixcraft.com' in homepage: tixcraft_family = True if 'indievox.com' in homepage: tixcraft_family = True if 'ticketmaster.' in homepage: tixcraft_family = True if tixcraft_family: if len(config_dict["advanced"]["tixcraft_sid"]) > 1: tixcraft_sid = decryptMe(config_dict["advanced"]["tixcraft_sid"]) driver.delete_cookie("SID") driver.add_cookie({"name":"SID", "value": tixcraft_sid, "path" : "/", "secure":True}) if 'ibon.com' in homepage: if len(config_dict["advanced"]["ibonqware"]) > 1: ibonqware = decryptMe(config_dict["advanced"]["ibonqware"]) driver.delete_cookie("ibonqware") driver.add_cookie({"name":"ibonqware", "value": ibonqware, "domain" : "ibon.com.tw", "secure":True}) except WebDriverException as exce2: print('oh no not again, WebDriverException') print('WebDriverException:', exce2) except Exception as exce1: print('get URL Exception:', exce1) pass return driver # common functions. def find_between( s, first, last ): try: start = s.index( first ) + len( first ) end = s.index( last, start ) return s[start:end] except ValueError: return "" # convert web string to reg pattern def convert_string_to_pattern(my_str, dynamic_length=True): my_hint_anwser_length = len(my_str) my_formated = "" if my_hint_anwser_length > 0: my_anwser_symbols = "()[]<>{}-" for idx in range(my_hint_anwser_length): char = my_str[idx:idx+1] if char in my_anwser_symbols: my_formated += ('\\' + char) continue pattern = re.compile("[A-Z]") match_result = pattern.match(char) #print("match_result A:", match_result) if not match_result is None: my_formated += "[A-Z]" pattern = re.compile("[a-z]") match_result = pattern.match(char) #print("match_result a:", match_result) if not match_result is None: my_formated += "[a-z]" pattern = re.compile("[\d]") match_result = pattern.match(char) #print("match_result d:", match_result) if not match_result is None: my_formated += "[\d]" # for dynamic length if dynamic_length: for i in range(10): my_formated = my_formated.replace("[A-Z][A-Z]","[A-Z]") my_formated = my_formated.replace("[a-z][a-z]","[a-z]") my_formated = my_formated.replace("[\d][\d]","[\d]") my_formated = my_formated.replace("[A-Z]","[A-Z]+") my_formated = my_formated.replace("[a-z]","[a-z]+") my_formated = my_formated.replace("[\d]","[\d]+") return my_formated def guess_answer_list_from_multi_options(tmp_text): show_debug_message = True # debug. show_debug_message = False # online options_list = [] matched_pattern = "" if len(options_list) == 0: if '【' in tmp_text and '】' in tmp_text: pattern = '【.{1,4}】' options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if '(' in tmp_text and ')' in tmp_text: pattern = '\(.{1,4}\)' options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if '[' in tmp_text and ']' in tmp_text: pattern = '\[.{1,4}\]' options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if "\n" in tmp_text and ')' in tmp_text: pattern = "\\n.{1,4}\)" options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if "\n" in tmp_text and ']' in tmp_text: pattern = "\\n.{1,4}\]" options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if "\n" in tmp_text and '】' in tmp_text: pattern = "\\n.{1,4}】" options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if "\n" in tmp_text and ':' in tmp_text: pattern = "\\n.{1,4}:" options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: matched_pattern = pattern if len(options_list) == 0: if " " in tmp_text and '?' in tmp_text: if ('.' in tmp_text or ':' in tmp_text or ')' in tmp_text or ']' in tmp_text or '>' in tmp_text): pattern = "[ /\n\|;\.\?]{1}.{1}[\.:)\]>]{1}.{2,3}" options_list = re.findall(pattern, tmp_text) if len(options_list) <= 2: options_list = [] else: formated_list = [] for new_item in options_list: new_item = new_item.strip() if new_item[:1] == ".": new_item = new_item[1:] if new_item[:1] == "?": new_item = new_item[1:] if new_item[:1] == "|": new_item = new_item[1:] if new_item[:1] == ";": new_item = new_item[1:] if new_item[:1] == "/": new_item = new_item[1:] new_item = new_item.strip() new_item = new_item[:1] formated_list.append(new_item) options_list = formated_list matched_pattern = pattern if show_debug_message: print("matched pattern:", matched_pattern) # default remove quota is_trim_quota = not check_answer_keep_symbol(tmp_text) if show_debug_message: print("is_trim_quota:", is_trim_quota) return_list = [] if len(options_list) > 0: options_list_length = len(options_list) if show_debug_message: print("options_list_length:", options_list_length) print("options_list:", options_list) if options_list_length > 2: is_all_options_same_length = True options_length_count = {} for i in range(options_list_length-1): current_option_length = len(options_list[i]) next_option_length = len(options_list[i+1]) if current_option_length != next_option_length: is_all_options_same_length = False if current_option_length in options_length_count: options_length_count[current_option_length] += 1 else: options_length_count[current_option_length] = 1 if show_debug_message: print("is_all_options_same_length:", is_all_options_same_length) if is_all_options_same_length: return_list = [] for each_option in options_list: if len(each_option) > 2: if is_trim_quota: return_list.append(each_option[1:-1]) else: return_list.append(each_option) else: return_list.append(each_option) else: #print("options_length_count:", options_length_count) if len(options_length_count) > 0: target_option_length = 0 most_length_count = 0 for k in options_length_count.keys(): if options_length_count[k] > most_length_count: most_length_count = options_length_count[k] target_option_length = k #print("most_length_count:", most_length_count) #print("target_option_length:", target_option_length) if target_option_length > 0: return_list = [] for each_option in options_list: current_option_length = len(each_option) if current_option_length == target_option_length: if is_trim_quota: return_list.append(each_option[1:-1]) else: return_list.append(each_option) # something is wrong, give up when option equal 2 options. if len(return_list) <= 2: return_list = [] # remove chinese work options. if len(options_list) > 0: new_list = [] for item in return_list: if is_all_alpha_or_numeric(item): new_list.append(item) if len(new_list) >=3: return_list = new_list return return_list #PS: this may get a wrong answer list. XD def guess_answer_list_from_symbols(captcha_text_div_text): return_list = [] # need replace to space to get first options. tmp_text = captcha_text_div_text tmp_text = tmp_text.replace('?',' ') tmp_text = tmp_text.replace('?',' ') tmp_text = tmp_text.replace('。',' ') delimitor_symbols_left = [u"(","[","{", " ", " ", " ", " "] delimitor_symbols_right = [u")","]","}", ":", ".", ")", "-"] idx = -1 for idx in range(len(delimitor_symbols_left)): symbol_left = delimitor_symbols_left[idx] symbol_right = delimitor_symbols_right[idx] if symbol_left in tmp_text and symbol_right in tmp_text and '半形' in tmp_text: hint_list = re.findall('\\'+ symbol_left + '[\\w]+\\'+ symbol_right , tmp_text) #print("hint_list:", hint_list) if not hint_list is None: if len(hint_list) > 1: return_list = [] my_answer_delimitor = symbol_right for options in hint_list: if len(options) > 2: my_anwser = options[1:-1] #print("my_anwser:",my_anwser) if len(my_anwser) > 0: return_list.append(my_anwser) if len(return_list) > 0: break return return_list def get_offical_hint_string_from_symbol(symbol, tmp_text): show_debug_message = True # debug. show_debug_message = False # online offical_hint_string = "" if symbol in tmp_text: # start to guess offical hint if offical_hint_string == "": if '【' in tmp_text and '】' in tmp_text: hint_list = re.findall('【.*?】', tmp_text) if not hint_list is None: if show_debug_message: print("【.*?】hint_list:", hint_list) for hint in hint_list: if symbol in hint: offical_hint_string = hint[1:-1] break if offical_hint_string == "": if '(' in tmp_text and ')' in tmp_text: hint_list = re.findall('\(.*?\)', tmp_text) if not hint_list is None: if show_debug_message: print("\(.*?\)hint_list:", hint_list) for hint in hint_list: if symbol in hint: offical_hint_string = hint[1:-1] break if offical_hint_string == "": if '[' in tmp_text and ']' in tmp_text: hint_list = re.findall('[.*?]', tmp_text) if not hint_list is None: if show_debug_message: print("[.*?]hint_list:", hint_list) for hint in hint_list: if symbol in hint: offical_hint_string = hint[1:-1] break if offical_hint_string == "": offical_hint_string = tmp_text return offical_hint_string def guess_answer_list_from_hint(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text): show_debug_message = True # debug. show_debug_message = False # online tmp_text = format_question_string(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) my_question = "" my_options = "" offical_hint_string = "" offical_hint_string_anwser = "" my_anwser_formated = "" my_answer_delimitor = "" if my_question == "": if "?" in tmp_text: question_index = tmp_text.find("?") my_question = tmp_text[:question_index+1] if my_question == "": if "。" in tmp_text: question_index = tmp_text.find("。") my_question = tmp_text[:question_index+1] if my_question == "": my_question = tmp_text #print("my_question:", my_question) # ps: hint_list is not options list if offical_hint_string == "": # for: 若你覺得答案為 a,請輸入 a if '答案' in tmp_text and CONST_INPUT_SYMBOL in tmp_text: offical_hint_string = get_offical_hint_string_from_symbol(CONST_INPUT_SYMBOL, tmp_text) if len(offical_hint_string) > 0: right_part = offical_hint_string.split(CONST_INPUT_SYMBOL)[1] #print("right_part:", right_part) if len(offical_hint_string) == len(tmp_text): offical_hint_string = right_part new_hint = find_continuous_text(right_part) if len(new_hint) > 0: # TODO: 答案為B需填入Bb) #if '答案' in offical_hint_string and CONST_INPUT_SYMBOL in offical_hint_string: offical_hint_string_anwser = new_hint if offical_hint_string == "": offical_hint_string = get_offical_hint_string_from_symbol(CONST_EXAMPLE_SYMBOL, tmp_text) if len(offical_hint_string) > 0: right_part = offical_hint_string.split(CONST_EXAMPLE_SYMBOL)[1] if len(offical_hint_string) == len(tmp_text): offical_hint_string = right_part # PS: find first text will only get B char in this case: 答案為B需填入Bb) new_hint = find_continuous_text(right_part) if len(new_hint) > 0: offical_hint_string_anwser = new_hint # resize offical_hint_string_anwser for options contains in hint string. #print("offical_hint_string_anwser:", offical_hint_string_anwser) if len(offical_hint_string_anwser) > 0: offical_hint_string = offical_hint_string.split(offical_hint_string_anwser)[0] if show_debug_message: print("offical_hint_string:", offical_hint_string) # try rule4: # get hint from rule 3: without '(' & '), but use "*" if len(offical_hint_string) == 0: target_symbol = "*" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index + len(target_symbol)) offical_hint_string = tmp_text[star_index: space_index] # is need to merge next block if len(offical_hint_string) > 0: target_symbol = offical_hint_string + " " if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) next_block_index = star_index + len(target_symbol) space_index = tmp_text.find(" ", next_block_index) next_block = tmp_text[next_block_index: space_index] if CONST_EXAMPLE_SYMBOL in next_block: offical_hint_string += ' ' + next_block # try rule5: # get hint from rule 3: n個半形英文大寫 if len(offical_hint_string) == 0: target_symbol = "個半形英文大寫" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 offical_hint_string_anwser = 'A' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] target_symbol = "個英文大寫" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 offical_hint_string_anwser = 'A' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] target_symbol = "個半形英文小寫" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 offical_hint_string_anwser = 'a' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] target_symbol = "個英文小寫" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 offical_hint_string_anwser = 'a' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] target_symbol = "個英數半形字" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 my_anwser_formated = '[A-Za-z\d]' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] target_symbol = "個半形" if target_symbol in tmp_text : star_index = tmp_text.find(target_symbol) space_index = tmp_text.find(" ", star_index) answer_char_count = tmp_text[star_index-1:star_index] if answer_char_count.isnumeric(): answer_char_count = normalize_chinese_numeric(answer_char_count) if answer_char_count is None: answer_char_count = '0' star_index -= 1 my_anwser_formated = '[A-Za-z\d]' * int(answer_char_count) offical_hint_string = tmp_text[star_index: space_index] if len(offical_hint_string) > 0: if show_debug_message: print("offical_hint_string_anwser:", offical_hint_string_anwser) my_anwser_formated = convert_string_to_pattern(offical_hint_string_anwser) my_options = tmp_text if len(my_question) < len(tmp_text): my_options = my_options.replace(my_question,"") my_options = my_options.replace(offical_hint_string,"") # try rule7: # check is chinese/english in question, if match, apply my_options rule. if len(offical_hint_string) > 0: tmp_text_org = captcha_text_div_text if CONST_EXAMPLE_SYMBOL in tmp_text: tmp_text_org = tmp_text_org.replace('Ex:','ex:') target_symbol = "ex:" if target_symbol in tmp_text_org : star_index = tmp_text_org.find(target_symbol) my_options = tmp_text_org[star_index-1:] if show_debug_message: print("tmp_text:", tmp_text) print("my_options:", my_options) if len(my_anwser_formated) > 0: allow_delimitor_symbols = ")].: }" pattern = re.compile(my_anwser_formated) search_result = pattern.search(my_options) if not search_result is None: (span_start, span_end) = search_result.span() maybe_delimitor="" if len(my_options) > (span_end+1)+1: maybe_delimitor = my_options[span_end+0:span_end+1] if maybe_delimitor in allow_delimitor_symbols: my_answer_delimitor = maybe_delimitor if show_debug_message: print("my_answer_delimitor:", my_answer_delimitor) # default remove quota is_trim_quota = not check_answer_keep_symbol(tmp_text) if show_debug_message: print("is_trim_quota:", is_trim_quota) return_list = [] if len(my_anwser_formated) > 0: new_pattern = my_anwser_formated if len(my_answer_delimitor) > 0: new_pattern = my_anwser_formated + '\\' + my_answer_delimitor return_list = re.findall(new_pattern, my_options) if show_debug_message: print("my_anwser_formated:", my_anwser_formated) print("new_pattern:", new_pattern) print("return_list:" , return_list) if not return_list is None: if len(return_list) == 1: # re-sample for this case. return_list = re.findall(my_anwser_formated, my_options) if len(return_list) == 1: # if use pattern to find matched only one, means it is for example text. return_list = None if not return_list is None: # clean delimitor if is_trim_quota: return_list_length = len(return_list) if return_list_length >= 1: if len(my_answer_delimitor) > 0: for idx in range(return_list_length): return_list[idx]=return_list[idx].replace(my_answer_delimitor,'') if show_debug_message: print("cleaned return_list:" , return_list) if return_list is None: return_list = [] return return_list, offical_hint_string_anwser def format_question_string(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text): tmp_text = captcha_text_div_text tmp_text = tmp_text.replace(' ',' ') tmp_text = tmp_text.replace(':',':') # for hint tmp_text = tmp_text.replace('*','*') # stop word. tmp_text = tmp_text.replace('輸入法','') tmp_text = tmp_text.replace('請問','') tmp_text = tmp_text.replace('請將','') tmp_text = tmp_text.replace('請在','') tmp_text = tmp_text.replace('請以','') tmp_text = tmp_text.replace('請回答','') tmp_text = tmp_text.replace('請','') # replace ex. tmp_text = tmp_text.replace('例如', CONST_EXAMPLE_SYMBOL) tmp_text = tmp_text.replace('如:', CONST_EXAMPLE_SYMBOL) tmp_text = tmp_text.replace('如為', CONST_EXAMPLE_SYMBOL+'為') tmp_text = tmp_text.replace('舉例', CONST_EXAMPLE_SYMBOL) if not CONST_EXAMPLE_SYMBOL in tmp_text: tmp_text = tmp_text.replace('例', CONST_EXAMPLE_SYMBOL) # important, maybe 例 & ex occurs at same time. tmp_text = tmp_text.replace('ex:', CONST_EXAMPLE_SYMBOL) tmp_text = tmp_text.replace('Ex:', CONST_EXAMPLE_SYMBOL) #若你覺得 #PS:這個,可能會造成更多問題,呵呵。 SYMBOL_IF_LIST = ['假設','如果','若'] for symbol_if in SYMBOL_IF_LIST: if symbol_if in tmp_text and '答案' in tmp_text: tmp_text = tmp_text.replace('覺得', '') tmp_text = tmp_text.replace('認為', '') tmp_text = tmp_text.replace(symbol_if + '你答案', CONST_EXAMPLE_SYMBOL + '答案') tmp_text = tmp_text.replace(symbol_if + '答案', CONST_EXAMPLE_SYMBOL + '答案') tmp_text = tmp_text.replace('填入', CONST_INPUT_SYMBOL) #tmp_text = tmp_text.replace('[','(') #tmp_text = tmp_text.replace(']',')') tmp_text = tmp_text.replace('?','?') tmp_text = tmp_text.replace('(','(') tmp_text = tmp_text.replace(')',')') return tmp_text def permutations(iterable, r=None): pool = tuple(iterable) n = len(pool) r = n if r is None else r if r > n: return indices = list(range(n)) cycles = list(range(n, n-r, -1)) yield tuple(pool[i] for i in indices[:r]) while n: for i in reversed(range(r)): cycles[i] -= 1 if cycles[i] == 0: indices[i:] = indices[i+1:] + indices[i:i+1] cycles[i] = n - i else: j = cycles[i] indices[i], indices[-j] = indices[-j], indices[i] yield tuple(pool[i] for i in indices[:r]) break else: return def get_answer_list_by_question(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text): show_debug_message = True # debug. show_debug_message = False # online return_list = [] tmp_text = format_question_string(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) # guess answer list from multi-options: 【】() [] if len(return_list)==0: return_list = guess_answer_list_from_multi_options(tmp_text) if show_debug_message: print("captcha_text_div_text:", captcha_text_div_text) if len(return_list) > 0: print("found, guess_answer_list_from_multi_options:", return_list) offical_hint_string_anwser = "" if len(return_list)==0: return_list, offical_hint_string_anwser = guess_answer_list_from_hint(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) else: is_match_factorial = False mutiple = 0 return_list_2, offical_hint_string_anwser = guess_answer_list_from_hint(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) if return_list_2 is None: if len(offical_hint_string_anwser) >=3: if len(return_list) >=3: mutiple = int(len(offical_hint_string_anwser) / len(return_list[0])) if mutiple >=3 : is_match_factorial = True if show_debug_message: print("mutiple:", mutiple) print("is_match_factorial:", is_match_factorial) if is_match_factorial: is_match_factorial = False order_string_list = ['排列','排序','依序','順序','遞增','遞減','升冪','降冪','新到舊','舊到新','小到大','大到小','高到低','低到高'] for order_string in order_string_list: if order_string in tmp_text: is_match_factorial = True if is_match_factorial: new_array = permutations(return_list, mutiple) #print("new_array:", new_array) return_list = [] for item_tuple in new_array: return_list.append(''.join(item_tuple)) if show_debug_message: if len(return_list) > 0: print("found, guess_answer_list_from_hint:", return_list) if len(return_list)==0: return_list = guess_answer_list_from_symbols(captcha_text_div_text) if show_debug_message: if len(return_list) > 0: print("found, guess_answer_list_from_symbols:", return_list) return return_list def force_press_button_iframe(driver, f, select_by, select_query, force_submit=True): if not f: # ensure we are on main content frame try: driver.switch_to.default_content() except Exception as exc: pass else: try: driver.switch_to.frame(f) except Exception as exc: pass is_clicked = force_press_button(driver, select_by, select_query, force_submit) if f: # switch back to main content, otherwise we will get StaleElementReferenceException try: driver.switch_to.default_content() except Exception as exc: pass return is_clicked def force_press_button(driver, select_by, select_query, force_submit=True): ret = False next_step_button = None try: next_step_button = driver.find_element(select_by ,select_query) if not next_step_button is None: if next_step_button.is_enabled(): next_step_button.click() ret = True except Exception as exc: #print("find %s clickable Exception:" % (select_query)) #print(exc) pass if force_submit: if not next_step_button is None: is_visible = False try: if next_step_button.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: driver.set_script_timeout(1) driver.execute_script("arguments[0].click();", next_step_button) ret = True except Exception as exc: pass return ret # close some div on home url. def tixcraft_home_close_window(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online accept_all_cookies_btn = None try: accept_all_cookies_btn = driver.find_element(By.CSS_SELECTOR, '#onetrust-accept-btn-handler') except Exception as exc: #print(exc) if show_debug_message: print("find accept_all_cookies_btn fail") pass if not accept_all_cookies_btn is None: is_visible = False try: if accept_all_cookies_btn.is_enabled() and accept_all_cookies_btn.is_displayed(): is_visible = True except Exception as exc: #print(exc) pass if is_visible: if show_debug_message: print("accept_all_cookies_btn visible. start to press.") try: accept_all_cookies_btn.click() except Exception as exc: #print(exc) print("try to click accept_all_cookies_btn fail, force click by js.") try: driver.execute_script("arguments[0].click();", accept_all_cookies_btn) except Exception as exc: pass else: if show_debug_message: print("accept_all_cookies_btn invisible.") # from detail to game def tixcraft_redirect(driver, url): ret = False game_name = "" # get game_name from url url_split = url.split("/") if len(url_split) >= 6: game_name = url_split[5] if "/activity/detail/%s" % (game_name,) in url: # to support teamear entry_url = url.replace("/activity/detail/","/activity/game/") print("redirec to new url:", entry_url) try: driver.get(entry_url) except Exception as exec1: pass ret = True return ret def tixcraft_date_auto_select(driver, url, config_dict, domain_name): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. auto_select_mode = config_dict["tixcraft"]["date_auto_select"]["mode"] date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() pass_date_is_sold_out_enable = config_dict["tixcraft"]["pass_date_is_sold_out"] auto_reload_coming_soon_page_enable = config_dict["tixcraft"]["auto_reload_coming_soon_page"] # PS: for big events, check sold out text maybe not helpful, due to database is too busy. sold_out_text_list = ["選購一空","已售完","No tickets available","Sold out","空席なし","完売した"] # PS: "Start ordering" for indievox.com. find_ticket_text_list = ['立即訂購','Find tickets', 'Start ordering','お申込みへ進む'] game_name = "" if "/activity/game/" in url: url_split = url.split("/") if len(url_split) >= 6: game_name = url_split[5] if show_debug_message: print('get date game_name:', game_name) print("date_auto_select_mode:", auto_select_mode) print("date_keyword:", date_keyword) check_game_detail = False # choose date if "/activity/game/%s" % (game_name,) in url: if show_debug_message: if len(date_keyword) == 0: print("date keyword is empty.") else: print("date keyword:", date_keyword) check_game_detail = True area_list = None if check_game_detail: if show_debug_message: print("start to query #gameList info.") my_css_selector = '#gameList > table > tbody > tr' try: area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #gameList fail") if show_debug_message: print("end of query #gameList info.") is_coming_soon = False coming_soon_condictions_list_tw = ['開賣','剩餘','天','小時','分鐘','秒','0',':','/'] matched_blocks = None formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("date_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True try: row_text = "" # check buy button. if row_is_enabled: row_text = row.text if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" row_is_enabled=False if len(row_text) > 0: row_is_enabled=True if row_is_enabled: row_is_enabled=False for text_item in find_ticket_text_list: if text_item in row_text: row_is_enabled = True break # check sold out text. if row_is_enabled: if pass_date_is_sold_out_enable: for sold_out_item in sold_out_text_list: row_text_right_part = row_text[(len(sold_out_item)+5)*-1:] if show_debug_message: #print("check right part text:", row_text_right_part) pass if sold_out_item in row_text_right_part: row_is_enabled = False if show_debug_message: print("match sold out text: %s, skip this row." % (sold_out_item)) break # check is coming soon. if row_is_enabled: is_match_all_coming_soon_condiction = True for condiction_string in coming_soon_condictions_list_tw: if not condiction_string in row_text: is_match_all_coming_soon_condiction = False break if is_match_all_coming_soon_condiction: if show_debug_message: print("match coming soon condiction at row:", row_text) is_coming_soon = True break except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) if show_debug_message: print("formated_area_list count:", len(formated_area_list)) if len(date_keyword) == 0: matched_blocks = formated_area_list else: # match keyword. if show_debug_message: print("start to match formated keyword:", date_keyword) matched_blocks = get_matched_blocks_by_keyword(config_dict, auto_select_mode, date_keyword, formated_area_list) if show_debug_message: if not matched_blocks is None: print("after match keyword, found count:", len(matched_blocks)) else: print("not found date-time-position") pass else: print("date date-time-position is None") pass target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] is_date_clicked = False if not target_area is None: if show_debug_message: print("target_area got, start to press button.") is_date_clicked = force_press_button(target_area, By.CSS_SELECTOR,'button') if not is_date_clicked: if show_debug_message: print("press button fail, try to click hyperlink.") # for: ticketmaster.sg is_date_clicked = force_press_button(target_area, By.CSS_SELECTOR,'a') # [PS]: current reload condition only when if auto_reload_coming_soon_page_enable: if is_coming_soon: if show_debug_message: print("match is_coming_soon, start to reload page.") # case 2: match one row is coming soon. try: driver.refresh() except Exception as exc: pass else: if not is_date_clicked: if not formated_area_list is None: if len(formated_area_list) == 0: print('start to refresh page.') try: driver.refresh() time.sleep(0.3) except Exception as exc: pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) return is_date_clicked def ticketmaster_date_auto_select(driver, url, config_dict, domain_name): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. auto_select_mode = config_dict["tixcraft"]["date_auto_select"]["mode"] date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() # TODO: implement this feature. date_keyword_and = "" pass_date_is_sold_out_enable = config_dict["tixcraft"]["pass_date_is_sold_out"] auto_reload_coming_soon_page_enable = config_dict["tixcraft"]["auto_reload_coming_soon_page"] # PS: for big events, check sold out text maybe not helpful, due to database is too busy. sold_out_text_list = ["選購一空","已售完","No tickets available","Sold out","空席なし","完売した"] find_ticket_text_list = ['See Tickets'] area_list = None try: area_list = driver.find_elements(By.CSS_SELECTOR, '#list-view > div > div.event-listing > div.accordion-wrapper > div') except Exception as exc: print("find #gameList fail") matched_blocks = None formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("date_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True try: if not row.is_enabled(): row_is_enabled=False row_text = "" # check buy button. if row_is_enabled: row_text = row.text if row_text is None: row_text = "" row_is_enabled=False # must contains 'See Tickets' for text_item in find_ticket_text_list: if text_item in row_text: row_is_enabled = True break # check sold out text. if row_is_enabled: if pass_date_is_sold_out_enable: for sold_out_item in sold_out_text_list: if sold_out_item in row_text: row_is_enabled = False if show_debug_message: print("match sold out text: %s, skip this row." % (sold_out_item)) break except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) if show_debug_message: print("formated_area_list count:", len(formated_area_list)) if len(date_keyword) == 0: matched_blocks = formated_area_list else: # match keyword. date_keyword = format_keyword_string(date_keyword) if show_debug_message: print("start to match formated keyword:", date_keyword) matched_blocks = get_matched_blocks_by_keyword(config_dict, auto_select_mode, date_keyword, formated_area_list) if show_debug_message: if not matched_blocks is None: print("after match keyword, found count:", len(matched_blocks)) else: print("not found date-time-position") pass else: print("date date-time-position is None") pass target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] is_date_clicked = False if not target_area is None: is_date_clicked = force_press_button(target_area, By.CSS_SELECTOR,'a') if is_date_clicked: try: window_handles_count = len(driver.window_handles) if window_handles_count > 1: driver.switch_to.window(driver.window_handles[0]) driver.close() driver.switch_to.window(driver.window_handles[0]) time.sleep(0.2) except Exception as excSwithFail: pass # [PS]: current reload condition only when if auto_reload_coming_soon_page_enable: if not is_date_clicked: if not formated_area_list is None: if len(formated_area_list) == 0: print('start to refresh page.') try: driver.refresh() time.sleep(0.3) except Exception as exc: pass return is_date_clicked def get_matched_blocks_by_keyword_item_set(config_dict, auto_select_mode, keyword_item_set, formated_area_list): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True matched_blocks = [] for row in formated_area_list: row_text = "" try: row_text = row.text except Exception as exc: pass if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: if show_debug_message: print("row_text:", row_text) is_match_all = False if ' ' in keyword_item_set: keyword_item_array = keyword_item_set.split(' ') is_match_all = True for keyword_item in keyword_item_array: keyword_item = format_keyword_string(keyword_item) if not keyword_item in row_text: is_match_all = False else: exclude_item = format_keyword_string(keyword_item_set) if exclude_item in row_text: is_match_all = True if is_match_all: matched_blocks.append(row) # only need first row. if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: break return matched_blocks def get_matched_blocks_by_keyword(config_dict, auto_select_mode, keyword_string, formated_area_list): keyword_array = [] try: keyword_array = json.loads("["+ keyword_string +"]") except Exception as exc: keyword_array = [] matched_blocks = [] for keyword_item_set in keyword_array: matched_blocks = get_matched_blocks_by_keyword_item_set(config_dict, auto_select_mode, keyword_item_set, formated_area_list) if len(matched_blocks) > 0: break return matched_blocks def is_row_match_keyword(keyword_string, row_text): # clean stop word. row_text = format_keyword_string(row_text) is_match_keyword = False if len(keyword_string) > 0: area_keyword_exclude_array = [] try: area_keyword_exclude_array = json.loads("["+ keyword_string +"]") except Exception as exc: area_keyword_exclude_array = [] for exclude_item_list in area_keyword_exclude_array: if len(row_text) > 0: if ' ' in exclude_item_list: area_keyword_array = exclude_item_list.split(' ') is_match_all_exclude = True for exclude_item in area_keyword_array: exclude_item = format_keyword_string(exclude_item) if not exclude_item in row_text: is_match_all_exclude = False if is_match_all_exclude: row_text = "" is_match_keyword = True break else: exclude_item = format_keyword_string(exclude_item_list) if exclude_item in row_text: row_text = "" is_match_keyword = True break return is_match_keyword def reset_row_text_if_match_keyword_exclude(config_dict, row_text): area_keyword_exclude = config_dict["keyword_exclude"] return is_row_match_keyword(area_keyword_exclude, row_text) # PURPOSE: get target area list. # RETURN: # is_need_refresh # matched_blocks # PS: matched_blocks will be None, if length equals zero. def get_tixcraft_target_area(el, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_need_refresh = False matched_blocks = None area_list = None area_list_count = 0 if not el is None: try: area_list = el.find_elements(By.TAG_NAME, 'a') except Exception as exc: #print("find area list a tag fail") pass if not area_list is None: area_list_count = len(area_list) if area_list_count == 0: print("area list is empty, do refresh!") is_need_refresh = True else: print("area list is None, do refresh!") is_need_refresh = True if area_list_count > 0: matched_blocks = [] for row in area_list: row_is_enabled=False try: row_is_enabled = row.is_enabled() except Exception as exc: pass row_text = "" if row_is_enabled: try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: # clean stop word. row_text = format_keyword_string(row_text) is_append_this_row = False if len(area_keyword_item) > 0: # must match keyword. is_append_this_row = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_append_this_row = False break else: # without keyword. is_append_this_row = True if is_append_this_row: if config_dict["ticket_number"] > 1: area_item_font_el = None try: #print('try to find font tag at row:', row_text) area_item_font_el = row.find_element(By.TAG_NAME, 'font') if not area_item_font_el is None: font_el_text = area_item_font_el.text if font_el_text is None: font_el_text = "" font_el_text = "@%s@" % (font_el_text) if show_debug_message: print('font tag text:', font_el_text) pass for check_item in CONT_STRING_1_SEATS_REMAINING: if check_item in font_el_text: if show_debug_message: print("match pass 1 seats remaining 1 full text:", row_text) print("match pass 1 seats remaining 2 font text:", font_el_text) is_append_this_row = False else: #print("row withou font tag.") pass except Exception as exc: #print("find font text in a tag fail:", exc) pass if show_debug_message: print("is_append_this_row:", is_append_this_row) if is_append_this_row: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: print("only need first item, break area list loop.") break if len(matched_blocks) == 0: matched_blocks = None is_need_refresh = True return is_need_refresh, matched_blocks def get_ticketmaster_target_area(config_dict, area_keyword_item, zone_info): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_need_refresh = False matched_blocks = None area_list_count = len(zone_info) if show_debug_message: print("area_list_count:", area_list_count) if area_list_count > 0: matched_blocks = [] for row in zone_info: row_is_enabled=False if zone_info[row]["areaStatus"] != "UNAVAILABLE": row_is_enabled = True if zone_info[row]["areaStatus"] == "SINGLE SEATS": row_is_enabled = True if config_dict["ticket_number"] > 1: row_is_enabled = False row_text = "" if row_is_enabled: try: row_text = zone_info[row]["groupName"] row_text += " " + zone_info[row]["description"] if "price" in zone_info[row]: row_text += " " + zone_info[row]["price"][0]["ticketPrice"] except Exception as exc: if show_debug_message: print("get text fail:", exc, zone_info[row]) pass if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: # clean stop word. row_text = format_keyword_string(row_text) if show_debug_message: #print("row_text:", row_text) pass is_append_this_row = False if len(area_keyword_item) > 0: # must match keyword. is_append_this_row = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_append_this_row = False break else: # without keyword. is_append_this_row = True if show_debug_message: #print("is_append_this_row:", is_append_this_row) pass if is_append_this_row: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: print("only need first item, break area list loop.") break if len(matched_blocks) == 0: matched_blocks = None is_need_refresh = True return is_need_refresh, matched_blocks # PS: auto refresh condition 1: no keyword + no hyperlink. # PS: auto refresh condition 2: with keyword + no hyperlink. def tixcraft_area_auto_select(driver, url, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() area_auto_select_mode = config_dict["area_auto_select"]["mode"] ticket_number = config_dict["ticket_number"] if show_debug_message: print("area_keyword:", area_keyword) if '/ticket/area/' in url: #driver.switch_to.default_content() el = None try: el = driver.find_element(By.CSS_SELECTOR, '.zone') except Exception as exc: print("find .zone fail, do nothing.") if not el is None: is_need_refresh = False areas = None if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_need_refresh, areas = get_tixcraft_target_area(el, config_dict, area_keyword_item) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. is_need_refresh, areas = get_tixcraft_target_area(el, config_dict, "") area_target = None if not areas is None: #print("area_auto_select_mode", area_auto_select_mode) #print("len(areas)", len(areas)) if len(areas) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(areas)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(areas)-1) #print("target_row_index", target_row_index) area_target = areas[target_row_index] if not area_target is None: try: #print("area text:", area_target.text) area_target.click() except Exception as exc: print("click area a link fail, start to retry...") try: driver.execute_script("arguments[0].click();", area_target) except Exception as exc: print("click area a link fail, after reftry still fail.") print(exc) pass # auto refresh for area list page. if is_need_refresh: try: driver.refresh() except Exception as exc: pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) def ticketmaster_area_auto_select(driver, config_dict, zone_info): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() area_auto_select_mode = config_dict["area_auto_select"]["mode"] if show_debug_message: print("area_keyword:", area_keyword) is_need_refresh = False areas = None if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_need_refresh, areas = get_ticketmaster_target_area(config_dict, area_keyword_item, zone_info) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. is_need_refresh, areas = get_ticketmaster_target_area(config_dict, "", zone_info) area_target = None if not areas is None: #print("area_auto_select_mode", area_auto_select_mode) if show_debug_message: print("len(areas)", len(areas)) if len(areas) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(areas)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(areas)-1) #print("target_row_index", target_row_index) area_target = areas[target_row_index] if not area_target is None: if show_debug_message: #print("area_target:", area_target) pass try: #print("area text:", area_target.text) click_area_javascript = 'areaTicket("%s", "map");' % area_target if show_debug_message: #print("click_area_javascript:", click_area_javascript) pass driver.execute_script(click_area_javascript) except Exception as exc: if show_debug_message: print(exc) pass # auto refresh for area list page. if is_need_refresh: try: driver.refresh() except Exception as exc: pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) def tixcraft_ticket_agree(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True agree_checkbox = None try: my_css_selector = '#TicketForm_agree' agree_checkbox = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find TicketForm_agree fail") if show_debug_message: print(exc) pass is_finish_checkbox_click = force_check_checkbox(driver, agree_checkbox) return is_finish_checkbox_click def tixcraft_ticket_number_auto_fill(driver, select_obj, ticket_number): is_ticket_number_assigned = False if not select_obj is None: try: # target ticket number select_obj.select_by_visible_text(ticket_number) #select.select_by_value(ticket_number) #select.select_by_index(int(ticket_number)) is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text ticket_number fail") print(exc) try: # target ticket number select_obj.select_by_visible_text(ticket_number) #select.select_by_value(ticket_number) #select.select_by_index(int(ticket_number)) is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text ticket_number fail...2") print(exc) # try buy one ticket try: select_obj.select_by_visible_text("1") #select.select_by_value("1") #select.select_by_index(int(ticket_number)) is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text 1 fail") pass # Plan B. # if not is_ticket_number_assigned: if False: if not select is None: try: # target ticket number #select.select_by_visible_text(ticket_number) print("assign ticker number by jQuery:",ticket_number) driver.execute_script("$(\"input[type='select']\").val(\""+ ticket_number +"\");") is_ticket_number_assigned = True except Exception as exc: print("jQuery select_by_visible_text ticket_number fail (after click.)") print(exc) return is_ticket_number_assigned def get_div_text_by_selector(driver, my_css_selector): div_element = None try: div_element = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find verify textbox fail") pass question_text = "" if not div_element is None: try: question_text = div_element.text except Exception as exc: print("get text fail") if question_text is None: question_text = "" return question_text def guess_tixcraft_question(driver, question_text): show_debug_message = True # debug. show_debug_message = False # online answer_list = [] formated_html_text = "" if len(question_text) > 0: # format question text. formated_html_text = question_text formated_html_text = formated_html_text.replace('「','【') formated_html_text = formated_html_text.replace('〔','【') formated_html_text = formated_html_text.replace('[','【') formated_html_text = formated_html_text.replace('〖','【') formated_html_text = formated_html_text.replace('[','【') formated_html_text = formated_html_text.replace('」','】') formated_html_text = formated_html_text.replace('〕','】') formated_html_text = formated_html_text.replace(']','】') formated_html_text = formated_html_text.replace('〗','】') formated_html_text = formated_html_text.replace(']','】') if '【' in formated_html_text and '】' in formated_html_text: # PS: 這個太容易沖突,因為問題類型太多,不能直接使用。 #inferred_answer_string = find_between(formated_html_text, "【", "】") pass if show_debug_message: print("formated_html_text:", formated_html_text) # start to guess answer inferred_answer_string = None # 請輸入"YES",代表您已詳閱且瞭解並同意。 if inferred_answer_string is None: if '輸入"YES"' in formated_html_text: if '已詳閱' in formated_html_text or '請詳閱' in formated_html_text: if '同意' in formated_html_text: inferred_answer_string = 'YES' # 購票前請詳閱注意事項,並於驗證碼欄位輸入【同意】繼續購票流程。 if inferred_answer_string is None: if '驗證碼' in formated_html_text or '驗證欄位' in formated_html_text: if '已詳閱' in formated_html_text or '請詳閱' in formated_html_text: if '輸入【同意】' in formated_html_text: inferred_answer_string = '同意' if inferred_answer_string is None: if len(question_text) > 0: answer_list = get_answer_list_from_question_string(None, question_text) else: answer_list = [answer_list] return answer_list def fill_common_verify_form(driver, config_dict, inferred_answer_string, fail_list, input_text_css, next_step_button_css, submit_by_enter, check_input_interval): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True form_input_list = [] try: form_input_list = driver.find_elements(By.CSS_SELECTOR, input_text_css) except Exception as exc: if show_debug_message: print("find verify code input textbox fail") pass if form_input_list is None: form_input_list = [] form_input_count = len(form_input_list) if show_debug_message: print("input textbox count:", form_input_count) is_do_press_next_button = False form_input = None if form_input_count > 0: form_input = form_input_list[0] if form_input_count == 1: is_do_press_next_button = True inputed_value = None if not form_input is None: try: inputed_value = form_input.get_attribute('value') except Exception as exc: if show_debug_message: print("get_attribute of verify code fail") pass if inputed_value is None: inputed_value = "" is_answer_sent = False if not form_input is None: if len(inferred_answer_string) > 0: if inputed_value != inferred_answer_string: try: # PS: sometime may send key twice... form_input.clear() form_input.send_keys(inferred_answer_string) is_button_clicked = False if is_do_press_next_button: if submit_by_enter: form_input.send_keys(Keys.ENTER) is_button_clicked = True if len(next_step_button_css) > 0: is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, next_step_button_css) if is_button_clicked: is_answer_sent = True fail_list.append(inferred_answer_string) if show_debug_message: print("sent password by bot:", inferred_answer_string, " at #", len(fail_list)) except Exception as exc: if show_debug_message: print(exc) pass if is_answer_sent: for i in range(3): time.sleep(0.1) alert_ret = check_pop_alert(driver) if alert_ret: if show_debug_message: print("press accept button at time #", i+1) break else: # no answer to fill. if len(inputed_value)==0: try: # solution 1: js. driver.execute_script("if(!(document.activeElement === arguments[0])){arguments[0].focus();}", form_input) # solution 2: selenium. #form_input.click() time.sleep(check_input_interval) except Exception as exc: pass return is_answer_sent, fail_list def get_answer_list_from_user_guess_string(config_dict): local_array = [] online_array = [] user_guess_string = config_dict["advanced"]["user_guess_string"] if len(user_guess_string) > 0: user_guess_string = format_config_keyword_for_json(user_guess_string) try: local_array = json.loads("["+ user_guess_string +"]") except Exception as exc: local_array = [] # load from internet. user_guess_string = "" if len(config_dict["advanced"]["online_dictionary_url"]) > 0: if os.path.exists(CONST_MAXBOT_ANSWER_ONLINE_FILE): with open(CONST_MAXBOT_ANSWER_ONLINE_FILE, "r") as text_file: user_guess_string = text_file.readline() if len(user_guess_string) > 0: user_guess_string = format_config_keyword_for_json(user_guess_string) try: online_array = json.loads("["+ user_guess_string +"]") except Exception as exc: online_array = [] return local_array + online_array def ticketmaster_promo(driver, config_dict, fail_list): question_selector = '#promoBox' return tixcraft_input_check_code(driver, config_dict, fail_list, question_selector) def tixcraft_verify(driver, config_dict, fail_list): question_selector = '.zone-verify' return tixcraft_input_check_code(driver, config_dict, fail_list, question_selector) def tixcraft_input_check_code(driver, config_dict, fail_list, question_selector): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True answer_list = [] question_text = get_div_text_by_selector(driver, question_selector) if len(question_text) > 0: write_question_to_file(question_text) answer_list = get_answer_list_from_user_guess_string(config_dict) if len(answer_list)==0: if config_dict["advanced"]["auto_guess_options"]: answer_list = guess_tixcraft_question(driver, question_text) inferred_answer_string = "" for answer_item in answer_list: if not answer_item in fail_list: inferred_answer_string = answer_item break if show_debug_message: print("inferred_answer_string:", inferred_answer_string) print("answer_list:", answer_list) # PS: auto-focus() when empty inferred_answer_string with empty inputed text value. input_text_css = "input[name='checkCode']" next_step_button_css = "" submit_by_enter = True check_input_interval = 0.2 is_answer_sent, fail_list = fill_common_verify_form(driver, config_dict, inferred_answer_string, fail_list, input_text_css, next_step_button_css, submit_by_enter, check_input_interval) return fail_list def tixcraft_change_captcha(driver,url): try: driver.execute_script(f"document.querySelector('.verify-img').children[0].setAttribute('src','{url}');") except Exception as exc: print("edit captcha element fail") def tixcraft_toast(driver, message): toast_element = None try: my_css_selector = ".remark-word" toast_element = driver.find_element(By.CSS_SELECTOR, my_css_selector) if not toast_element is None: driver.execute_script("arguments[0].innerHTML='%s';" % message, toast_element) except Exception as exc: print("find toast element fail") def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False): is_verifyCode_editing = False is_form_sumbited = False # manually keyin verify code. # start to input verify code. form_verifyCode = None try: form_verifyCode = driver.find_element(By.CSS_SELECTOR, '#TicketForm_verifyCode') except Exception as exc: print("find form_verifyCode fail") if not form_verifyCode is None: is_visible = False try: if form_verifyCode.is_enabled(): is_visible = True except Exception as exc: pass inputed_value = None try: inputed_value = form_verifyCode.get_attribute('value') except Exception as exc: print("find verify code fail") pass if inputed_value is None: inputed_value = "" is_visible = False if is_visible: try: form_verifyCode.click() is_verifyCode_editing = True except Exception as exc: print("click form_verifyCode fail, trying to use javascript.") # plan B try: driver.execute_script("document.getElementById(\"TicketForm_verifyCode\").focus();") is_verifyCode_editing = True except Exception as exc: #print("click form_verifyCode fail.") pass if len(answer) > 0: #print("start to fill answer.") try: form_verifyCode.clear() form_verifyCode.send_keys(answer) if auto_submit: form_verifyCode.send_keys(Keys.ENTER) is_verifyCode_editing = False is_form_sumbited = True else: driver.execute_script("document.getElementById(\"TicketForm_verifyCode\").select();") tixcraft_toast(driver, "※ 按 Enter 如果答案是: " + answer) except Exception as exc: print("send_keys ocr answer fail.") return is_verifyCode_editing, is_form_sumbited def tixcraft_reload_captcha(driver, domain_name): # manually keyin verify code. # start to input verify code. ret = False form_captcha = None try: image_id = 'TicketForm_verifyCode-image' if 'indievox.com' in domain_name: image_id = 'TicketForm_verifyCode-image' form_captcha = driver.find_element(By.CSS_SELECTOR, "#" + image_id) if not form_captcha is None: form_captcha.click() ret = True except Exception as exc: print("find form_captcha fail") return ret def tixcraft_get_ocr_answer(driver, ocr, ocr_captcha_image_source, Captcha_Browser, domain_name): show_debug_message = True # debug. show_debug_message = False # online ocr_answer = None if not ocr is None: img_base64 = None if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER: if not Captcha_Browser is None: img_base64 = base64.b64decode(Captcha_Browser.Request_Captcha()) if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_CANVAS: image_id = 'TicketForm_verifyCode-image' image_element = None try: my_css_selector = "#" + image_id image_element = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not image_element is None: if 'indievox.com' in domain_name: #image_id = 'TicketForm_verifyCode-image' pass try: driver.set_script_timeout(1) form_verifyCode_base64 = driver.execute_async_script(""" var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var img = document.getElementById('%s'); if(img!=null) { canvas.height = img.naturalHeight; canvas.width = img.naturalWidth; context.drawImage(img, 0, 0); callback = arguments[arguments.length - 1]; callback(canvas.toDataURL()); } """ % (image_id)) if not form_verifyCode_base64 is None: img_base64 = base64.b64decode(form_verifyCode_base64.split(',')[1]) if img_base64 is None: if not Captcha_Browser is None: print("canvas get image fail, use plan_b: NonBrowser") img_base64 = base64.b64decode(Captcha_Browser.Request_Captcha()) except Exception as exc: if show_debug_message: print("canvas exception:", str(exc)) pass if not img_base64 is None: try: ocr_answer = ocr.classification(img_base64) except Exception as exc: pass return ocr_answer #PS: credit to LinShihJhang's share def tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer, Captcha_Browser, ocr_captcha_image_source, domain_name): show_debug_message = True # debug. show_debug_message = False # online print("start to ddddocr") is_need_redo_ocr = False is_form_sumbited = False is_input_box_exist = False if not ocr is None: form_verifyCode = None try: form_verifyCode = driver.find_element(By.CSS_SELECTOR, '#TicketForm_verifyCode') is_input_box_exist = True except Exception as exc: pass else: print("ddddocr component is not able to use, you may running in arm environment.") if is_input_box_exist: if show_debug_message: print("away_from_keyboard_enable:", away_from_keyboard_enable) print("previous_answer:", previous_answer) print("ocr_captcha_image_source:", ocr_captcha_image_source) ocr_start_time = time.time() ocr_answer = tixcraft_get_ocr_answer(driver, ocr, ocr_captcha_image_source, Captcha_Browser, domain_name) ocr_done_time = time.time() ocr_elapsed_time = ocr_done_time - ocr_start_time print("ocr elapsed time:", "{:.3f}".format(ocr_elapsed_time)) if ocr_answer is None: if away_from_keyboard_enable: # page is not ready, retry again. # PS: usually occur in async script get captcha image. is_need_redo_ocr = True time.sleep(0.1) else: tixcraft_keyin_captcha_code(driver) else: ocr_answer = ocr_answer.strip() print("ocr_answer:", ocr_answer) if len(ocr_answer)==4: who_care_var, is_form_sumbited = tixcraft_keyin_captcha_code(driver, answer = ocr_answer, auto_submit = away_from_keyboard_enable) else: if not away_from_keyboard_enable: tixcraft_keyin_captcha_code(driver) tixcraft_toast(driver, "※ OCR辨識失敗Q_Q,驗證碼請手動輸入...") else: is_need_redo_ocr = True if previous_answer != ocr_answer: previous_answer = ocr_answer print("click captcha again.") if True: # selenium solution. tixcraft_reload_captcha(driver, domain_name) if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_CANVAS: time.sleep(0.1) else: # Non_Browser solution. if not Captcha_Browser is None: new_captcha_url = Captcha_Browser.Request_Refresh_Captcha() #取得新的CAPTCHA if new_captcha_url != "": tixcraft_change_captcha(driver, new_captcha_url) #更改CAPTCHA圖 else: print("input box not exist, quit ocr...") return is_need_redo_ocr, previous_answer, is_form_sumbited def tixcraft_ticket_main_agree(driver, config_dict): if config_dict["auto_check_agree"]: for i in range(3): is_finish_checkbox_click = tixcraft_ticket_agree(driver, config_dict) if is_finish_checkbox_click: break def get_tixcraft_ticket_select_by_keyword(driver, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_need_refresh = False matched_blocks = None area_list = None area_list_count = 0 try: my_css_selector = "table#ticketPriceList > tbody > tr" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: #print("find area list a tag fail") pass if not area_list is None: area_list_count = len(area_list) if area_list_count == 0: print("area list is empty, do refresh!") is_need_refresh = True else: print("area list is None, do refresh!") is_need_refresh = True if area_list_count > 0: matched_blocks = [] for row in area_list: row_is_enabled=False try: row_is_enabled = row.is_enabled() except Exception as exc: pass row_text = "" if row_is_enabled: try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: # clean stop word. row_text = format_keyword_string(row_text) is_append_this_row = False if len(area_keyword_item) > 0: # must match keyword. is_append_this_row = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_append_this_row = False break else: # without keyword. is_append_this_row = True if show_debug_message: print("is_append_this_row:", is_append_this_row, row_text) if is_append_this_row: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: print("only need first item, break area list loop.") break if len(matched_blocks) == 0: matched_blocks = None is_need_refresh = True return is_need_refresh, matched_blocks def get_tixcraft_ticket_select(driver, config_dict): area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() form_select = None areas = None if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_need_refresh, areas = get_tixcraft_ticket_select_by_keyword(driver, config_dict, area_keyword_item) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. is_need_refresh, areas = get_tixcraft_target_area(driver, config_dict, "") area_auto_select_mode = config_dict["area_auto_select"]["mode"] area_target = None if not areas is None: #print("area_auto_select_mode", area_auto_select_mode) #print("len(areas)", len(areas)) if len(areas) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(areas)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(areas)-1) #print("target_row_index", target_row_index) area_target = areas[target_row_index] if not area_target is None: try: form_select = area_target.find_element(By.TAG_NAME, 'select') except Exception as exc: #print("find area list a tag fail") form_select = None pass return form_select def tixcraft_ticket_main(driver, config_dict, ocr, Captcha_Browser, domain_name): tixcraft_ticket_main_agree(driver, config_dict) # allow agree not enable to assign ticket number. form_select_list = None try: form_select_list = driver.find_elements(By.CSS_SELECTOR, '.mobile-select') except Exception as exc: print("find select fail") pass form_select = None form_select_count = 0 if not form_select_list is None: form_select_count = len(form_select_list) if form_select_count >= 1: form_select = form_select_list[0] # multi select box if form_select_count > 1: if config_dict["area_auto_select"]["enable"]: # for tixcraft form_select_temp = get_tixcraft_ticket_select(driver, config_dict) if not form_select_temp is None: form_select = form_select_temp # for ticketmaster if form_select is None: try: form_select = driver.find_element(By.CSS_SELECTOR, 'td > select.form-select') except Exception as exc: print("find form-select fail") pass select_obj = None if not form_select is None: is_visible = False try: if form_select.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: select_obj = Select(form_select) except Exception as exc: pass if not select_obj is None: is_ticket_number_assigned = False row_text = None try: selected_option = select_obj.first_selected_option row_text = selected_option.text except Exception as exc: pass if not row_text is None: if len(row_text) > 0: if row_text != "0": if row_text.isnumeric(): # ticket assign. is_ticket_number_assigned = True # must wait select object ready to assign ticket number. if not is_ticket_number_assigned: # only this case: "ticket number not changed by bot" to play sound! # PS: I assume each time assign ticket number will succufully changed, so let sound play first. check_and_play_sound_for_captcha(config_dict) ticket_number = str(config_dict["ticket_number"]) is_ticket_number_assigned = tixcraft_ticket_number_auto_fill(driver, select_obj, ticket_number) # must wait ticket number assign to focus captcha. if is_ticket_number_assigned: tixcraft_ticket_main_ocr(driver, config_dict, ocr, Captcha_Browser, domain_name) def tixcraft_ticket_main_ocr(driver, config_dict, ocr, Captcha_Browser, domain_name): away_from_keyboard_enable = config_dict["ocr_captcha"]["force_submit"] if not config_dict["ocr_captcha"]["enable"]: away_from_keyboard_enable = False ocr_captcha_image_source = config_dict["ocr_captcha"]["image_source"] if not config_dict["ocr_captcha"]["enable"]: tixcraft_keyin_captcha_code(driver) else: previous_answer = None last_url, is_quit_bot = get_current_url(driver) for redo_ocr in range(999): is_need_redo_ocr, previous_answer, is_form_sumbited = tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer, Captcha_Browser, ocr_captcha_image_source, domain_name) if is_form_sumbited: # start next loop. break if not away_from_keyboard_enable: break if not is_need_redo_ocr: break current_url, is_quit_bot = get_current_url(driver) if current_url != last_url: break def kktix_confirm_order_button(driver): ret = False wait = WebDriverWait(driver, 1) next_step_button = None try: # method #3 wait next_step_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'div.form-actions a.btn-primary'))) if not next_step_button is None: if next_step_button.is_enabled(): next_step_button.click() ret = True except Exception as exc: print("wait form-actions div wait to be clickable Exception:") #print(exc) pass if not next_step_button is None: is_visible = False try: if next_step_button.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: driver.execute_script("arguments[0].click();", next_step_button) ret = True except Exception as exc: pass return ret # PS: There are two "Next" button in kktix. # : 1: /events/xxx # : 2: /events/xxx/registrations/new # : This is ONLY for case-1, because case-2 lenght >5 def kktix_events_press_next_button(driver): is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'div.tickets a.btn-point') return is_button_clicked # : This is for case-2 next button. def kktix_press_next_button(driver): ret = False wait = WebDriverWait(driver, 1) next_step_button = None try: # method #1 #form_actions_div = None #form_actions_div = driver.find_element(By.CSS_SELECTOR, '#registrationsNewApp') #next_step_button = form_actions_div.find_element(By.CSS_SELECTOR, 'div.form-actions button.btn-primary') # method #2 # next_step_button = driver.find_element(By.CSS_SELECTOR, '#registrationsNewApp div.form-actions button.btn-primary') # method #3 wait next_step_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#registrationsNewApp div.form-actions button.btn-primary'))) if not next_step_button is None: if next_step_button.is_enabled(): next_step_button.click() ret = True except Exception as exc: print("wait form-actions div wait to be clickable Exception:") print(exc) #pass if not next_step_button is None: is_visible = False try: if next_step_button.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: driver.execute_script("arguments[0].click();", next_step_button) ret = True except Exception as exc: pass return ret def kktix_captcha_inputed_text(captcha_inner_div): ret = "" if not captcha_inner_div is None: try: captcha_password_text = captcha_inner_div.find_element(By.TAG_NAME, "input") if not captcha_password_text is None: ret = captcha_password_text.get_attribute('value') else: print("find captcha input field fail") except Exception as exc: print("find captcha_inner_div Exception:") #print(exc) pass return ret def kktix_input_captcha_text(captcha_password_input_element, inferred_answer_string, force_overwrite = False): show_debug_message = True # debug. show_debug_message = False # online is_cpatcha_sent = False inputed_captcha_text = "" if not captcha_password_input_element is None: if force_overwrite: try: captcha_password_input_element.send_keys(inferred_answer_string) print("send captcha keys:" + inferred_answer_string) is_cpatcha_sent = True except Exception as exc: pass else: # not force overwrite: inputed_captcha_text = None try: inputed_captcha_text = captcha_password_input_element.get_attribute('value') except Exception as exc: pass if inputed_captcha_text is None: inputed_captcha_text = "" if len(inputed_captcha_text) == 0: try: captcha_password_input_element.send_keys(inferred_answer_string) print("send captcha keys:" + inferred_answer_string) is_cpatcha_sent = True except Exception as exc: pass else: if inputed_captcha_text == inferred_answer_string: is_cpatcha_sent = True return is_cpatcha_sent def kktix_travel_price_list(driver, config_dict, kktix_area_keyword): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True ticket_number = config_dict["ticket_number"] areas = None is_ticket_number_assigned = False ticket_price_list = None try: ticket_price_list = driver.find_elements(By.CSS_SELECTOR, '.display-table-row') except Exception as exc: ticket_price_list = None print("find ticket-price span Exception:") print(exc) pass price_list_count = 0 if not ticket_price_list is None: price_list_count = len(ticket_price_list) if show_debug_message: print("found price count:", price_list_count) else: print("find ticket-price span fail") is_travel_interrupted = False if price_list_count > 0: areas = [] kktix_area_keyword_array = kktix_area_keyword.split(' ') kktix_area_keyword_1 = kktix_area_keyword_array[0] kktix_area_keyword_1_and = "" if len(kktix_area_keyword_array) > 1: kktix_area_keyword_1_and = kktix_area_keyword_array[1] # clean stop word. kktix_area_keyword_1 = format_keyword_string(kktix_area_keyword_1) kktix_area_keyword_1_and = format_keyword_string(kktix_area_keyword_1_and) if show_debug_message: print('kktix_area_keyword_1:', kktix_area_keyword_1) print('kktix_area_keyword_1_and:', kktix_area_keyword_1_and) row_index = 0 for row in ticket_price_list: row_index += 1 row_text = "" try: row_text = row.text if show_debug_message: print("get text:", row_text, ",at row:", row_index) except Exception as exc: row_text = "" is_travel_interrupted = True print("get text fail.") if row_text is None: row_text = "" if '已售完' in row_text: row_text = "" if 'Sold Out' in row_text: row_text = "" if '完売' in row_text: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: # clean stop word. row_text = format_keyword_string(row_text) # check ticket input textbox. ticket_price_input = None try: ticket_price_input = row.find_element(By.CSS_SELECTOR, "input[type='text']") except Exception as exc: pass if not ticket_price_input is None: current_ticket_number = "" is_visible = False try: current_ticket_number = str(ticket_price_input.get_attribute('value')).strip() is_visible = ticket_price_input.is_enabled() except Exception as exc: pass if len(current_ticket_number) > 0: if current_ticket_number != "0": is_ticket_number_assigned = True if is_ticket_number_assigned: # no need to travel break is_danger_notice = False if ticket_number > 1: # start to check danger notice. span_danger_popup = None try: span_danger_popup = row.find_element(By.CSS_SELECTOR, "span.danger") if span_danger_popup.is_displayed(): is_danger_notice = True except Exception as exc: pass if is_danger_notice: # PS: not ALL danger notice are "only 1 seat remaining"... # TODO: check real remaining value instead of check css style. continue if is_visible: is_match_area = False match_area_code = 0 if len(kktix_area_keyword_1) == 0: # keyword #1, empty, direct add to list. is_match_area = True match_area_code = 1 else: # MUST match keyword #1. if kktix_area_keyword_1 in row_text: #print('match keyword#1') # because of logic between keywords is AND! if len(kktix_area_keyword_1_and) == 0: #print('keyword#2 is empty, directly match.') # keyword #2 is empty, direct append. is_match_area = True match_area_code = 2 else: if kktix_area_keyword_1_and in row_text: #print('match keyword#2') is_match_area = True match_area_code = 3 else: #print('not match keyword#2') pass else: #print('not match keyword#1') pass if show_debug_message: print("is_match_area:", is_match_area) print("match_area_code:", match_area_code) if is_match_area: areas.append(ticket_price_input) if is_travel_interrupted: # not sure to break or continue..., maybe break better. break else: if show_debug_message: print("no any price list found.") pass # unknow issue... if is_travel_interrupted: pass return is_ticket_number_assigned, areas def kktix_assign_ticket_number(driver, config_dict, kktix_area_keyword): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True ticket_number_str = str(config_dict["ticket_number"]) ticket_number = config_dict["ticket_number"] kktix_area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_ticket_number_assigned, areas = kktix_travel_price_list(driver, config_dict, kktix_area_keyword) is_need_refresh = False ticket_price_input = None if not is_ticket_number_assigned: if not areas is None: if len(areas) > 0: target_row_index = 0 if kktix_area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if kktix_area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(areas)-1 if kktix_area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(areas)-1) if show_debug_message: print("target_row_index", target_row_index) ticket_price_input = areas[target_row_index] else: is_need_refresh = True current_ticket_number = "" is_visible = False if not ticket_price_input is None: if show_debug_message: print("try to get input box value.") try: current_ticket_number = str(ticket_price_input.get_attribute('value')).strip() is_visible = ticket_price_input.is_enabled() except Exception as exc: pass if is_visible and len(current_ticket_number) > 0: if current_ticket_number == "0": try: print("asssign ticket number:%s" % ticket_number_str) ticket_price_input.clear() ticket_price_input.send_keys(ticket_number_str) is_ticket_number_assigned = True except Exception as exc: print("asssign ticket number to ticket-price field Exception:") print(exc) try: ticket_price_input.clear() ticket_price_input.send_keys("1") is_ticket_number_assigned = True except Exception as exc2: pass else: if show_debug_message: print("value already assigned.") # already assigned. is_ticket_number_assigned = True return is_ticket_number_assigned, is_need_refresh def kktix_get_web_datetime(registrationsNewApp_div): show_debug_message = True # debug. show_debug_message = False # online web_datetime = None is_found_web_datetime = False el_web_datetime_list = None if not registrationsNewApp_div is None: try: el_web_datetime_list = registrationsNewApp_div.find_elements(By.TAG_NAME, 'td') except Exception as exc: if show_debug_message: print("find td.ng-binding Exception") print(exc) pass #print("is_found_web_datetime", is_found_web_datetime) if not el_web_datetime_list is None: el_web_datetime_list_count = len(el_web_datetime_list) if el_web_datetime_list_count > 0: el_web_datetime = None for el_web_datetime in el_web_datetime_list: el_web_datetime_text = None try: el_web_datetime_text = el_web_datetime.text if show_debug_message: print("el_web_datetime_text:", el_web_datetime_text) except Exception as exc: if show_debug_message: print('parse web datetime fail:') print(exc) pass if not el_web_datetime_text is None: if len(el_web_datetime_text) > 0: now = datetime.now() #print("now:", now) for guess_year in range(now.year,now.year+3): current_year = str(guess_year) if current_year in el_web_datetime_text: if '/' in el_web_datetime_text: web_datetime = el_web_datetime_text is_found_web_datetime = True break if is_found_web_datetime: break else: print("find td.ng-binding fail") if show_debug_message: print('is_found_web_datetime:', is_found_web_datetime) print('web_datetime:', web_datetime) return web_datetime def kktix_hide_blocks(driver): is_need_refresh = False elements = None try: elements = driver.find_elements(By.CSS_SELECTOR, "div[ng-controller='EventInfoCtrl']") except Exception as exc: print("find person_agree_terms checkbox Exception") pass if not elements is None: for element in elements: if not element is None: try: driver.execute_script("arguments[0].innerHTML='';", element); except Exception as exc: pass def kktix_check_agree_checkbox(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True is_need_refresh = False is_finish_checkbox_click = False kktix_hide_blocks(driver) agree_checkbox = None try: agree_checkbox = driver.find_element(By.CSS_SELECTOR, '#person_agree_terms') except Exception as exc: print("find person_agree_terms checkbox Exception") if show_debug_message: print(exc) pass if not agree_checkbox is None: is_finish_checkbox_click = force_check_checkbox(driver, agree_checkbox) else: is_need_refresh = True if is_need_refresh: print("find person_agree_terms checkbox fail, do refresh page.") return is_need_refresh, is_finish_checkbox_click def check_checkbox(driver, by, query): show_debug_message = True # debug. show_debug_message = False # online agree_checkbox = None try: agree_checkbox = driver.find_element(by, query) except Exception as exc: if show_debug_message: print(exc) pass is_checkbox_checked = False if not agree_checkbox is None: is_checkbox_checked = force_check_checkbox(driver, agree_checkbox) return is_checkbox_checked def force_check_checkbox(driver, agree_checkbox): is_finish_checkbox_click = False if not agree_checkbox is None: is_visible = False try: if agree_checkbox.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: is_checkbox_checked = False try: if agree_checkbox.is_selected(): is_checkbox_checked = True except Exception as exc: pass if not is_checkbox_checked: #print('send check to checkbox') try: agree_checkbox.click() is_finish_checkbox_click = True except Exception as exc: try: driver.execute_script("arguments[0].click();", agree_checkbox) is_finish_checkbox_click = True except Exception as exc: pass else: is_finish_checkbox_click = True return is_finish_checkbox_click def kktix_check_register_status(driver, url): #ex: https://xxx.kktix.cc/events/xxx prefix_list = ['.com/events/','.cc/events/'] postfix = '/registrations/new' is_match_event_code = False event_code = "" for prefix in prefix_list: event_code = find_between(url,prefix,postfix) if len(event_code) > 0: is_match_event_code = True #print('event_code:',event_code) break if is_match_event_code: js = ''' function load_kktix_register_code(){ let api_url = "https://kktix.com/g/events/%s/register_info"; fetch(api_url).then(function (response) { return response.json(); } ).then(function (data) { let reload=false; console.log(data.inventory.registerStatus); if(data.inventory.registerStatus=='OUT_OF_STOCK') {reload=true;} if(data.inventory.registerStatus=='COMING_SOON') {reload=true;} console.log(reload); if(reload) {location.reload();} } ).catch(function (err) { console.log(err); }); } if (!$.kkUser) { $.kkUser = {}; } if (typeof $.kkUser.checked_status_register_code === 'undefined') { $.kkUser.checked_status_register_code = true; load_kktix_register_code(); } ''' % (event_code) try: driver.execute_script(js) except Exception as exc: pass # use javascritp version only. is_match_event_code = False html_result = None if is_match_event_code: url = "https://kktix.com/g/events/%s/register_info" % (event_code) #print('event_code:',event_code) #print("url:", url) user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' headers = {"Accept-Language": "zh-TW,zh;q=0.5", 'User-Agent': user_agent} try: html_result = requests.get(url , headers=headers, timeout=0.7, allow_redirects=False) except Exception as exc: html_result = None print("send reg_info request fail:") print(exc) registerStatus = None if not html_result is None: status_code = html_result.status_code #print("status_code:",status_code) if status_code == 200: html_text = html_result.text #print("html_text:", html_text) try: jsLoads = json.loads(html_text) if 'inventory' in jsLoads: if 'registerStatus' in jsLoads['inventory']: registerStatus = jsLoads['inventory']['registerStatus'] except Exception as exc: print("load reg_info json fail:") print(exc) pass #print("registerStatus:", registerStatus) return registerStatus def get_answer_string_from_web_date(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, registrationsNewApp_div, captcha_text_div_text): show_debug_message = True # debug. show_debug_message = False # online inferred_answer_string = None is_need_parse_web_datetime = False # '半形阿拉伯數字' & '半形數字' if '半形' in captcha_text_div_text and '字' in captcha_text_div_text: if '演出日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '活動日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '表演日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '開始日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '演唱會日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '展覽日期' in captcha_text_div_text: is_need_parse_web_datetime = True if '音樂會日期' in captcha_text_div_text: is_need_parse_web_datetime = True if 'the date of the show you purchased' in captcha_text_div_text: is_need_parse_web_datetime = True if show_debug_message: print("is_need_parse_web_datetime:", is_need_parse_web_datetime) if is_need_parse_web_datetime: web_datetime = kktix_get_web_datetime(registrationsNewApp_div) if not web_datetime is None: if show_debug_message: print("web_datetime:", web_datetime) captcha_text_formatted = format_question_string(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) if show_debug_message: print("captcha_text_formatted", captcha_text_formatted) my_datetime_foramted = None # MMDD if my_datetime_foramted is None: if '4位半形' in captcha_text_formatted: my_datetime_foramted = "%m%d" # for "如為2月30日,請輸入0230" if my_datetime_foramted is None: right_part = "" if CONST_EXAMPLE_SYMBOL in captcha_text_formatted: right_part = captcha_text_formatted.split(CONST_EXAMPLE_SYMBOL)[1] if CONST_INPUT_SYMBOL in right_part: right_part = right_part.split(CONST_INPUT_SYMBOL)[1] number_text = find_continuous_number(right_part) my_anwser_formated = convert_string_to_pattern(number_text, dynamic_length=False) if my_anwser_formated == "[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]": my_datetime_foramted = "%Y%m%d" if my_anwser_formated == "[\\d][\\d][\\d][\\d]": my_datetime_foramted = "%m%d" #print("my_datetime_foramted:", my_datetime_foramted) if show_debug_message: print("my_datetime_foramted", my_datetime_foramted) if my_datetime_foramted is None: now = datetime.now() for guess_year in range(now.year-4,now.year+2): current_year = str(guess_year) if current_year in captcha_text_formatted: my_hint_index = captcha_text_formatted.find(current_year) my_hint_anwser = captcha_text_formatted[my_hint_index:] #print("my_hint_anwser:", my_hint_anwser) # get after. my_delimitor_symbol = CONST_EXAMPLE_SYMBOL if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[my_delimitor_index+len(my_delimitor_symbol):] #print("my_hint_anwser:", my_hint_anwser) # get before. my_delimitor_symbol = ',' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] my_delimitor_symbol = '。' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] # PS: space may not is delimitor... my_delimitor_symbol = ' ' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] #remove last char. remove_last_char_list = [')','(','.','。',')','(','[',']'] for check_char in remove_last_char_list: if my_hint_anwser[-1:]==check_char: my_hint_anwser = my_hint_anwser[:-1] my_anwser_formated = convert_string_to_pattern(my_hint_anwser, dynamic_length=False) if my_anwser_formated == "[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]": my_datetime_foramted = "%Y%m%d" if my_anwser_formated == "[\\d][\\d][\\d][\\d]/[\\d][\\d]/[\\d][\\d]": my_datetime_foramted = "%Y/%m/%d" if show_debug_message: print("my_hint_anwser:", my_hint_anwser) print("my_anwser_formated:", my_anwser_formated) print("my_datetime_foramted:", my_datetime_foramted) break if not my_datetime_foramted is None: my_delimitor_symbol = ' ' if my_delimitor_symbol in web_datetime: web_datetime = web_datetime[:web_datetime.find(my_delimitor_symbol)] date_time = datetime.strptime(web_datetime,"%Y/%m/%d") if show_debug_message: print("our web date_time:", date_time) ans = None try: if not date_time is None: ans = date_time.strftime(my_datetime_foramted) except Exception as exc: pass inferred_answer_string = ans if show_debug_message: print("web date_time anwser:", ans) return inferred_answer_string def get_answer_string_from_web_time(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, registrationsNewApp_div, captcha_text_div_text): show_debug_message = True # debug. show_debug_message = False # online inferred_answer_string = None # parse '演出時間' is_need_parse_web_time = False if '半形' in captcha_text_div_text: if '演出時間' in captcha_text_div_text: is_need_parse_web_time = True if '表演時間' in captcha_text_div_text: is_need_parse_web_time = True if '開始時間' in captcha_text_div_text: is_need_parse_web_time = True if '演唱會時間' in captcha_text_div_text: is_need_parse_web_time = True if '展覽時間' in captcha_text_div_text: is_need_parse_web_time = True if '音樂會時間' in captcha_text_div_text: is_need_parse_web_time = True if 'the time of the show you purchased' in captcha_text_div_text: is_need_parse_web_time = True #print("is_need_parse_web_time", is_need_parse_web_time) if is_need_parse_web_time: web_datetime = None if not registrationsNewApp_div is None: web_datetime = kktix_get_web_datetime(registrationsNewApp_div) if not web_datetime is None: tmp_text = format_question_string(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) my_datetime_foramted = None if my_datetime_foramted is None: my_hint_anwser = tmp_text my_delimitor_symbol = CONST_EXAMPLE_SYMBOL if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[my_delimitor_index+len(my_delimitor_symbol):] #print("my_hint_anwser:", my_hint_anwser) # get before. my_delimitor_symbol = ',' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] my_delimitor_symbol = '。' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] # PS: space may not is delimitor... my_delimitor_symbol = ' ' if my_delimitor_symbol in my_hint_anwser: my_delimitor_index = my_hint_anwser.find(my_delimitor_symbol) my_hint_anwser = my_hint_anwser[:my_delimitor_index] my_anwser_formated = convert_string_to_pattern(my_hint_anwser, dynamic_length=False) #print("my_hint_anwser:", my_hint_anwser) #print("my_anwser_formated:", my_anwser_formated) if my_anwser_formated == "[\\d][\\d][\\d][\\d]": my_datetime_foramted = "%H%M" if '12小時' in tmp_text: my_datetime_foramted = "%I%M" if my_anwser_formated == "[\\d][\\d]:[\\d][\\d]": my_datetime_foramted = "%H:%M" if '12小時' in tmp_text: my_datetime_foramted = "%I:%M" if not my_datetime_foramted is None: date_delimitor_symbol = '(' if date_delimitor_symbol in web_datetime: date_delimitor_symbol_index = web_datetime.find(date_delimitor_symbol) if date_delimitor_symbol_index > 8: web_datetime = web_datetime[:date_delimitor_symbol_index-1] date_time = datetime.strptime(web_datetime,"%Y/%m/%d %H:%M") #print("date_time:", date_time) ans = None try: ans = date_time.strftime(my_datetime_foramted) except Exception as exc: pass inferred_answer_string = ans #print("my_anwser:", ans) return inferred_answer_string def check_answer_keep_symbol(captcha_text_div_text): is_need_keep_symbol = False # format text keep_symbol_tmp = captcha_text_div_text keep_symbol_tmp = keep_symbol_tmp.replace('也','須') keep_symbol_tmp = keep_symbol_tmp.replace('必須','須') keep_symbol_tmp = keep_symbol_tmp.replace('全都','都') keep_symbol_tmp = keep_symbol_tmp.replace('全部都','都') keep_symbol_tmp = keep_symbol_tmp.replace('一致','相同') keep_symbol_tmp = keep_symbol_tmp.replace('一樣','相同') keep_symbol_tmp = keep_symbol_tmp.replace('相等','相同') if '符號須都相同' in keep_symbol_tmp: is_need_keep_symbol = True if '符號都相同' in keep_symbol_tmp: is_need_keep_symbol = True if '符號須相同' in keep_symbol_tmp: is_need_keep_symbol = True # for: 大小寫含括號需一模一樣 keep_symbol_tmp = keep_symbol_tmp.replace('含', '') keep_symbol_tmp = keep_symbol_tmp.replace('和', '') keep_symbol_tmp = keep_symbol_tmp.replace('與', '') keep_symbol_tmp = keep_symbol_tmp.replace('還有', '') keep_symbol_tmp = keep_symbol_tmp.replace('及', '') keep_symbol_tmp = keep_symbol_tmp.replace('以及', '') keep_symbol_tmp = keep_symbol_tmp.replace('需', '') keep_symbol_tmp = keep_symbol_tmp.replace('必須', '') keep_symbol_tmp = keep_symbol_tmp.replace('而且', '') keep_symbol_tmp = keep_symbol_tmp.replace('且', '') keep_symbol_tmp = keep_symbol_tmp.replace('一模', '') #print("keep_symbol_tmp:", keep_symbol_tmp) if '大小寫括號相同' in keep_symbol_tmp: is_need_keep_symbol = True return is_need_keep_symbol def get_answer_list_from_question_string(registrationsNewApp_div, captcha_text_div_text): show_debug_message = True # debug. show_debug_message = False # online inferred_answer_string = None answer_list = [] CONST_EXAMPLE_SYMBOL = "範例" CONST_INPUT_SYMBOL = "輸入" if captcha_text_div_text is None: captcha_text_div_text = "" # 請在下方空白處輸入引號內文字: # 請回答下列問題,請在下方空格輸入DELIGHT(請以半形輸入法作答,大小寫需要一模一樣) if inferred_answer_string is None: is_use_quota_message = False if "「" in captcha_text_div_text and "」" in captcha_text_div_text: # test for rule#1, it's seem very easy conflict... match_quota_text_items = ["下方","空白","輸入","引號","文字"] is_match_quota_text = True for each_quota_text in match_quota_text_items: if not each_quota_text in captcha_text_div_text: is_match_quota_text = False if is_match_quota_text: is_use_quota_message = True #print("is_use_quota_message:" , is_use_quota_message) if is_use_quota_message: inferred_answer_string = find_between(captcha_text_div_text, "「", "」") #print("find captcha text:" , inferred_answer_string) if inferred_answer_string is None: is_use_quota_message = False if "【" in captcha_text_div_text and "】" in captcha_text_div_text: if '下' in captcha_text_div_text and '空' in captcha_text_div_text and CONST_INPUT_SYMBOL in captcha_text_div_text and '引號' in captcha_text_div_text and '字' in captcha_text_div_text: is_use_quota_message = True if '半形' in captcha_text_div_text and CONST_INPUT_SYMBOL in captcha_text_div_text and '引號' in captcha_text_div_text and '字' in captcha_text_div_text: is_use_quota_message = True #print("is_use_quota_message:" , is_use_quota_message) if is_use_quota_message: inferred_answer_string = find_between(captcha_text_div_text, "【", "】") #print("find captcha text:" , inferred_answer_string) # parse '演出日期' if inferred_answer_string is None: inferred_answer_string = get_answer_string_from_web_date(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, registrationsNewApp_div, captcha_text_div_text) # parse '演出時間' if inferred_answer_string is None: inferred_answer_string = get_answer_string_from_web_time(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, registrationsNewApp_div, captcha_text_div_text) # name of event. if inferred_answer_string is None: if "name of event" in captcha_text_div_text: if '(' in captcha_text_div_text and ')' in captcha_text_div_text and 'ans:' in captcha_text_div_text.lower(): target_symbol = "(" star_index = captcha_text_div_text.find(target_symbol) target_symbol = ":" star_index = captcha_text_div_text.find(target_symbol, star_index) target_symbol = ")" end_index = captcha_text_div_text.find(target_symbol, star_index) inferred_answer_string = captcha_text_div_text[star_index+1:end_index] #print("inferred_answer_string:", inferred_answer_string) # 二題式,組合問題。 is_combine_two_question = False if "第一題" in captcha_text_div_text and "第二題" in captcha_text_div_text: is_combine_two_question = True if "Q1." in captcha_text_div_text and "Q2." in captcha_text_div_text: if "二題" in captcha_text_div_text: is_combine_two_question = True if "2題" in captcha_text_div_text: is_combine_two_question = True if "Q1:" in captcha_text_div_text and "Q2:" in captcha_text_div_text: if "二題" in captcha_text_div_text: is_combine_two_question = True if "2題" in captcha_text_div_text: is_combine_two_question = True if "Q1 " in captcha_text_div_text and "Q2 " in captcha_text_div_text: if "二題" in captcha_text_div_text: is_combine_two_question = True if "2題" in captcha_text_div_text: is_combine_two_question = True if is_combine_two_question: inferred_answer_string = None #print("is_combine_two_question:", is_combine_two_question) # still no answer. if inferred_answer_string is None: if not is_combine_two_question: answer_list = get_answer_list_by_question(CONST_EXAMPLE_SYMBOL, CONST_INPUT_SYMBOL, captcha_text_div_text) if show_debug_message: print("guess answer list:", answer_list) else: if show_debug_message: print("skip to guess answer because of combine question...") else: if show_debug_message: print("got an inferred_answer_string:", inferred_answer_string) answer_list = [inferred_answer_string] return answer_list def kktix_reg_captcha_question_text(captcha_inner_div): captcha_text_div = None try: captcha_text_div = captcha_inner_div.find_element(By.TAG_NAME, "p") except Exception as exc: pass print("find p tag(captcha_text_div) fail") print(exc) question_text = None if not captcha_text_div is None: try: question_text = captcha_text_div.text except Exception as exc: pass if question_text is None: question_text = "" return question_text def kktix_double_check_all_text_value(driver, ticket_number): is_do_press_next_button = False # double check ticket input textbox. ticket_price_input_list = None try: # PS: unable directly access text's value attribute via css selector or xpath on KKTix! my_css_selector = "input[type='text']" #print("my_css_selector:", my_css_selector) ticket_price_input_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not ticket_price_input_list is None: #print("bingo, found one of ticket number textbox.") row_index = 0 for ticket_price_input in ticket_price_input_list: row_index += 1 current_ticket_number = "" try: current_ticket_number = str(ticket_price_input.get_attribute('value')).strip() except Exception as exc: pass if current_ticket_number is None: current_ticket_number = "" if len(current_ticket_number) > 0: #print(row_index, "current_ticket_number:", current_ticket_number) if current_ticket_number == str(ticket_number): #print("bingo, match target ticket number.") # ONLY, this case to auto press next button. is_do_press_next_button = True break return is_do_press_next_button # 本票券需要符合以下任一資格才可以購買 def get_kktix_control_label_text(driver): question_text = "" captcha_inner_div = None try: captcha_inner_div = driver.find_element(By.CSS_SELECTOR, 'div.ticket-unit > div.code-input > div.control-group > label.control-label') if not captcha_inner_div is None: question_text = captcha_inner_div.text except Exception as exc: pass return question_text def get_kktix_question_text(driver): question_text = "" captcha_inner_div = None try: captcha_inner_div = driver.find_element(By.CSS_SELECTOR, 'div.custom-captcha-inner') except Exception as exc: pass if not captcha_inner_div is None: question_text = kktix_reg_captcha_question_text(captcha_inner_div) return question_text def kktix_reg_captcha(driver, config_dict, fail_list, captcha_sound_played, is_finish_checkbox_click, registrationsNewApp_div): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True answer_list = [] question_text = get_kktix_question_text(driver) if len(question_text) > 0: write_question_to_file(question_text) if len(fail_list)==0: # only play sound once. if not captcha_sound_played: captcha_sound_played = True try: check_and_play_sound_for_captcha(config_dict) except Exception as exc: pass answer_list = get_answer_list_from_user_guess_string(config_dict) if len(answer_list)==0: if config_dict["advanced"]["auto_guess_options"]: answer_list = get_answer_list_from_question_string(registrationsNewApp_div, question_text) inferred_answer_string = "" for answer_item in answer_list: if not answer_item in fail_list: inferred_answer_string = answer_item break if show_debug_message: print("inferred_answer_string:", inferred_answer_string) print("answer_list:", answer_list) print("fail_list:", fail_list) # PS: auto-focus() when empty inferred_answer_string with empty inputed text value. input_text_css = 'div.custom-captcha-inner > div > div > input' next_step_button_css = '#registrationsNewApp div.form-actions button.btn-primary' submit_by_enter = False check_input_interval = 0.2 is_answer_sent, fail_list = fill_common_verify_form(driver, config_dict, inferred_answer_string, fail_list, input_text_css, next_step_button_css, submit_by_enter, check_input_interval) else: # no captcha text popup, goto next page. control_text = get_kktix_control_label_text(driver) if show_debug_message: print("control_text:", control_text) if control_text == "": click_ret = kktix_press_next_button(driver) return fail_list, captcha_sound_played def kktix_reg_new_main(driver, config_dict, fail_list, captcha_sound_played, is_finish_checkbox_click): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True # read config. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() # part 1: check div. registrationsNewApp_div = None try: registrationsNewApp_div = driver.find_element(By.CSS_SELECTOR, '#registrationsNewApp') except Exception as exc: pass #print("find input fail:", exc) # part 2: assign ticket number is_ticket_number_assigned = False if not registrationsNewApp_div is None: is_need_refresh = False if len(area_keyword) > 0: for retry_index in range(2): area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_ticket_number_assigned, is_need_refresh = kktix_assign_ticket_number(driver, config_dict, area_keyword_item) if is_ticket_number_assigned: break else: print("is_need_refresh for keyword:", area_keyword_item) if is_ticket_number_assigned: break else: # empty keyword, match all. is_ticket_number_assigned, is_need_refresh = kktix_assign_ticket_number(driver, config_dict, "") # part 3: captcha if is_ticket_number_assigned: fail_list, captcha_sound_played = kktix_reg_captcha(driver, config_dict, fail_list, captcha_sound_played, is_finish_checkbox_click, registrationsNewApp_div) else: if is_need_refresh: try: print("no match any price, start to refresh page...") driver.refresh() except Exception as exc: #print("refresh fail") pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) return fail_list, captcha_sound_played def kktix_reg_auto_reload(driver, url, config_dict, kktix_register_status_last): registerStatus = kktix_register_status_last # auto refresh for area list page. is_need_refresh = False if not is_need_refresh: if registerStatus is None: # current version, change refresh event from selenium to javascript. registerStatus = kktix_check_register_status(driver, url) # for request solution, refresh on selenium. if not registerStatus is None: print("registerStatus:", registerStatus) # OUT_OF_STOCK if registerStatus != 'IN_STOCK': is_need_refresh = True is_finish_checkbox_click = False if config_dict["auto_check_agree"]: if not is_need_refresh: is_need_refresh, is_finish_checkbox_click = kktix_check_agree_checkbox(driver, config_dict) if not is_finish_checkbox_click: # retry again. is_need_refresh, is_finish_checkbox_click = kktix_check_agree_checkbox(driver, config_dict) if is_need_refresh: try: print("try to refresh page...") driver.refresh() except Exception as exc: #print("refresh fail") pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) return is_need_refresh, is_finish_checkbox_click # PURPOSE: get target area list. # PS: this is main block, use keyword to get rows. # PS: it seems use date_auto_select_mode instead of area_auto_select_mode def get_fami_target_area(driver, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() date_keyword = format_keyword_string(date_keyword) area_auto_select_mode = config_dict["area_auto_select"]["mode"] area_list = None try: my_css_selector = "table.session__list > tbody > tr" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #session date list fail") if show_debug_message: print(exc) #PS: some blocks are generate by ajax, not appear at first time. formated_area_list = None if not area_list is None: area_list_length = len(area_list) if show_debug_message: print("lenth of area rows:", area_list_length) if area_list_length > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True el_btn = None try: my_css_selector = "button" el_btn = row.find_element(By.TAG_NAME, my_css_selector) if not el_btn is None: if not el_btn.is_enabled(): #print("row's button disabled!") row_is_enabled=False except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) matched_blocks = None if not formated_area_list is None: if len(formated_area_list) > 0: matched_blocks = [] if len(date_keyword)==0 and len(area_keyword_item)==0: # select all. matched_blocks = formated_area_list else: # match keyword. row_index = 0 for row in formated_area_list: row_index += 1 #print("row index:", row_index) date_html_text = "" area_html_text = "" row_text = "" try: my_css_selector = "td:nth-child(1)" td_date = row.find_element(By.CSS_SELECTOR, my_css_selector) if not td_date is None: #print("date:", td_date.text) date_html_text = format_keyword_string(td_date.text) my_css_selector = "td:nth-child(2)" td_area = row.find_element(By.CSS_SELECTOR, my_css_selector) if not td_area is None: #print("area:", td_area.text) area_html_text = format_keyword_string(td_area.text) row_text = row.text except Exception as exc: print("get row text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: # check date. is_match_date = False if len(date_keyword) > 0: if date_keyword in date_html_text: #print("is_match_date") is_match_date = True else: is_match_date = True # check area. is_match_area = False if len(area_keyword_item) > 0: # must match keyword. is_match_area = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_match_area = False break else: # without keyword. is_match_area = True if is_match_date and is_match_area: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: print("only need first item, break area list loop.") break return_row_count = 0 if not matched_blocks is None: return_row_count = len(matched_blocks) if return_row_count==0: matched_blocks = None if show_debug_message: print("return_row_count:", return_row_count) return matched_blocks def fami_activity(driver): #print("fami_activity bingo") #--------------------------- # part 1: press "buy" button. #--------------------------- fami_start_to_buy_button = None try: fami_start_to_buy_button = driver.find_element(By.CSS_SELECTOR, '#buyWaiting') except Exception as exc: pass is_visible = False if not fami_start_to_buy_button is None: try: if fami_start_to_buy_button.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: fami_start_to_buy_button.click() except Exception as exc: print("click buyWaiting button fail...") #print(exc) #pass try: driver.execute_script("arguments[0].click();", fami_start_to_buy_button) except Exception as exc: pass def fami_home(driver, url, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True is_ticket_number_assigned = False ticket_number = str(config_dict["ticket_number"]) #--------------------------- # part 3: fill ticket number. #--------------------------- ticket_el = None try: my_css_selector = "tr.ticket > td > select" ticket_el = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass print("click buyWaiting button fail") #print(exc) is_select_box_visible = False if not ticket_el is None: try: if ticket_el.is_enabled(): is_select_box_visible = True except Exception as exc: pass is_ticket_number_assigned = False if is_select_box_visible: ticket_number_select = None try: ticket_number_select = Select(ticket_el) except Exception as exc: pass if not ticket_number_select is None: try: #print("get select ticket value:" + Select(ticket_number_select).first_selected_option.text) if ticket_number_select.first_selected_option.text=="0" or ticket_number_select.first_selected_option.text=="選擇張數": # target ticket number ticket_number_select.select_by_visible_text(ticket_number) is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text ticket_number fail") print(exc) try: # try target ticket number twice ticket_number_select.select_by_visible_text(ticket_number) is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text ticket_number fail...2") print(exc) # try buy one ticket try: ticket_number_select.select_by_visible_text("1") is_ticket_number_assigned = True except Exception as exc: print("select_by_visible_text 1 fail") pass #--------------------------- # part 4: press "next" button. #--------------------------- if is_ticket_number_assigned: fami_assign_site_button = None try: my_css_selector = "div.col > a.btn" fami_assign_site_button = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not fami_assign_site_button is None: is_visible = False try: if fami_assign_site_button.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: fami_assign_site_button.click() except Exception as exc: print("click buyWaiting button fail") #print(exc) try: driver.execute_script("arguments[0].click();", fami_assign_site_button) except Exception as exc: pass areas = None if not is_select_box_visible: #--------------------------- # part 2: select keywords #--------------------------- area_auto_select_mode = config_dict["area_auto_select"]["mode"] area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: areas = get_fami_target_area(driver, config_dict, area_keyword_item) if not areas is None: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. areas = get_fami_target_area(driver, config_dict, "") area_target = None if not areas is None: #print("area_auto_select_mode", area_auto_select_mode) #print("len(areas)", len(areas)) if len(areas) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(areas)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(areas)-1) #print("target_row_index", target_row_index) area_target = areas[target_row_index] if not area_target is None: el_btn = None is_visible = False try: my_css_selector = "button" el_btn = area_target.find_element(By.TAG_NAME, my_css_selector) if not el_btn is None: if el_btn.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: el_btn.click() except Exception as exc: print("click buy button fail, start to retry...") try: driver.execute_script("arguments[0].click();", el_btn) except Exception as exc: pass # purpose: date auto select def urbtix_date_auto_select(driver, auto_select_mode, date_keyword, auto_reload_coming_soon_page_enable): show_debug_message = True # debug. show_debug_message = False # online ret = False matched_blocks = None area_list = None try: #print("try to find cityline area block") my_css_selector = "div.conent-wrapper > div.list-wrapper > ul" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #date-time-position date list fail") print(exc) #PS: some blocks are generate by ajax, not appear at first time. formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("date_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True el_btn = None try: my_css_selector = "div.buy-icon" el_btn = row.find_element(By.CSS_SELECTOR, my_css_selector) if not el_btn is None: button_class_string = str(el_btn.get_attribute('class')) if len(button_class_string) > 1: if 'disabled' in button_class_string: if show_debug_message: print("found disabled activity at row:", row_index) row_is_enabled=False except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) if show_debug_message: print("formated_area_list count:", len(formated_area_list)) if len(date_keyword) == 0: matched_blocks = formated_area_list else: # match keyword. if show_debug_message: print("start to match keyword:", date_keyword) matched_blocks = [] row_index = 0 for row in formated_area_list: row_index += 1 #row_is_enabled=False row_is_enabled=True if row_is_enabled: row_text = "" try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if show_debug_message: print("row_text:", row_text) is_match_area = is_row_match_keyword(date_keyword, row_text) if is_match_area: matched_blocks.append(row) if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: print("only need first item, break area list loop.") break if show_debug_message: if not matched_blocks is None: print("after match keyword, found count:", len(matched_blocks)) else: print("not found date-time-position") pass else: print("date date-time-position is None") pass target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] if not target_area is None: el_btn = None try: #print("target_area text", target_area.text) my_css_selector = "div.buy-icon" el_btn = target_area.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not el_btn is None: is_button_enable = True try: if not el_btn.is_enabled(): is_button_enable = False else: # button enable, but class disable. button_class_string = str(el_btn.get_attribute('class')) if len(button_class_string) > 1: if 'disabled' in button_class_string: is_button_enable = False if is_button_enable: el_btn.click() ret = True print("buy icon pressed.") except Exception as exc: # use plan B try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_btn) ret = True except Exception as exc: pass else: # no target. if auto_reload_coming_soon_page_enable: # auto refresh for date list page. if not formated_area_list is None: if len(formated_area_list) == 0: try: driver.refresh() time.sleep(1.0) except Exception as exc: pass return ret def urbtix_purchase_ticket(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online date_auto_select_mode = config_dict["tixcraft"]["date_auto_select"]["mode"] date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() auto_reload_coming_soon_page_enable = config_dict["tixcraft"]["auto_reload_coming_soon_page"] if show_debug_message: print("date_keyword:", date_keyword) is_date_assign_by_bot = urbtix_date_auto_select(driver, date_auto_select_mode, date_keyword, auto_reload_coming_soon_page_enable) return is_date_assign_by_bot # purpose: area auto select def urbtix_area_auto_select(driver, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_price_assign_by_bot = False is_need_refresh = False matched_blocks = None area_list = None try: #print("try to find cityline area block") my_css_selector = "div.area-info" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #ticket-price-tbl date list fail") print(exc) formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("area_list_count:", area_list_count) print("area_keyword_item:", area_keyword_item) if area_list_count > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True row_text = "" if row_is_enabled: try: row_text = row.text except Exception as exc: pass if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if row_text == "": row_is_enabled=False button_class_string = None if row_is_enabled: try: button_class_string = str(row.get_attribute('class')) except Exception as exc: pass if button_class_string is None: button_class_string = "" if len(button_class_string) > 1: if 'disabled' in button_class_string: row_is_enabled=False if 'selected' in button_class_string: # someone is selected. skip this process. row_is_enabled=False is_price_assign_by_bot = True break if row_is_enabled: formated_area_list.append(row) else: if show_debug_message: print("area_list_count is empty.") pass else: if show_debug_message: print("area_list_count is None.") pass if is_price_assign_by_bot: formated_area_list = None if not formated_area_list is None: area_list_count = len(formated_area_list) if show_debug_message: print("formated_area_list count:", area_list_count) if area_list_count > 0: matched_blocks = [] if len(area_keyword_item) == 0: matched_blocks = formated_area_list else: # match keyword. row_index = 0 for row in formated_area_list: row_index += 1 row_is_enabled=True if row_is_enabled: row_text = "" try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: row_text = format_keyword_string(row_text) if show_debug_message: print("row_text:", row_text) is_match_area = False if len(area_keyword_item) > 0: # must match keyword. is_match_area = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_match_area = False break else: # without keyword. is_match_area = True if is_match_area: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: break if show_debug_message: print("after match keyword, found count:", len(matched_blocks)) if len(matched_blocks) == 0: matched_blocks = None is_need_refresh = True target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] if not target_area is None: try: if target_area.is_enabled(): target_area.click() is_price_assign_by_bot = True except Exception as exc: print("click target_area link fail") print(exc) # use plan B try: print("force to click by js.") driver.execute_script("arguments[0].click();", target_area) is_price_assign_by_bot = True except Exception as exc: pass return is_need_refresh, is_price_assign_by_bot def urbtix_ticket_number_auto_select(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True is_ticket_number_assigned = False ticket_number = config_dict["ticket_number"] ticket_number_str = str(ticket_number) # check ticket input textbox. ticket_price_input = None try: ticket_price_input = driver.find_element(By.CSS_SELECTOR, "input.ticket-count") except Exception as exc: pass if not ticket_price_input is None: current_ticket_number = "" is_visible = False try: current_ticket_number = str(ticket_price_input.get_attribute('value')).strip() is_visible = ticket_price_input.is_enabled() except Exception as exc: pass if len(current_ticket_number) > 0: if current_ticket_number == "0": try: print("asssign ticket number:%s" % ticket_number_str) ticket_price_input.clear() ticket_price_input.send_keys(ticket_number_str) is_ticket_number_assigned = True except Exception as exc: print("asssign ticket number to ticket-price field Exception:") print(exc) try: ticket_price_input.clear() ticket_price_input.send_keys("1") is_ticket_number_assigned = True except Exception as exc2: pass else: # already assigned. is_ticket_number_assigned = True ticket_count = 0 if is_ticket_number_assigned: el_ticket_count = None try: my_css_selector = "div.left-content > span.total-count" el_ticket_count = driver.find_element(By.CSS_SELECTOR, my_css_selector) if not el_ticket_count is None: tmp_ticket_count = el_ticket_count.text if len(tmp_ticket_count) > 0: if tmp_ticket_count.isdigit(): ticket_count = int(tmp_ticket_count) except Exception as exc: pass if is_ticket_number_assigned and ticket_count==0: el_btn = None try: # this is "confirm"(確認) button. my_css_selector = "div.footer > div > div" el_btn = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not el_btn is None: try: if el_btn.is_enabled(): el_btn.click() print("varify site icon pressed.") time.sleep(0.3) except Exception as exc: # use plan B if show_debug_message: print("varify site icon pressed fail:", exc) ''' try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_btn) ret = True except Exception as exc: pass ''' pass else: if show_debug_message: print("varify site icon is None.") if is_ticket_number_assigned and ticket_count>0: el_btn = None try: # this is "add to cart"(加入購物籃) my_css_selector = "div.button-inner > div > div.button-text" el_btn = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not el_btn is None: try: if el_btn.is_enabled(): el_btn.click() time.sleep(0.3) ret = True print("shopping-cart icon pressed.") except Exception as exc: # use plan B if show_debug_message: print("click on button fail:", exc) pass ''' try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_btn) ret = True except Exception as exc: pass ''' else: if show_debug_message: print("shopping-cart site icon is None.") return is_ticket_number_assigned def urbtix_uncheck_adjacent_seat(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True el_checkbox_icon = None is_checkbox_checked = False try: my_css_selector = "div.quantity-inner > div.header > div.right > div.checkbox-wrapper > div.checkbox-icon" el_checkbox_icon = driver.find_element(By.CSS_SELECTOR, my_css_selector) if not el_checkbox_icon is None: div_class_string = str(el_checkbox_icon.get_attribute('class')) if not div_class_string is None: if len(div_class_string) > 1: if 'checked' in div_class_string: is_checkbox_checked = True except Exception as exc: if show_debug_message: print(exc) pass try: if is_checkbox_checked: el_checkbox_icon.click() except Exception as exc: if show_debug_message: print(exc) # force to click when exception. try: driver.execute_script("arguments[0].click();", el_checkbox_icon) except Exception as exc2: pass pass def urbtix_performance(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True ret = False is_price_assign_by_bot = False is_need_refresh = False auto_fill_ticket_number = True if auto_fill_ticket_number: # click price row. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() if show_debug_message: print("area_keyword:", area_keyword) if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: print("area_keyword_item for keyword:", area_keyword_item) is_need_refresh, is_price_assign_by_bot = urbtix_area_auto_select(driver, config_dict, area_keyword_item) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. is_need_refresh, is_price_assign_by_bot = urbtix_area_auto_select(driver, config_dict, "") # un-tested. disable refresh for now. is_need_refresh = False if is_need_refresh: try: driver.refresh() except Exception as exc: pass if config_dict["advanced"]["disable_adjacent_seat"]: urbtix_uncheck_adjacent_seat(driver, config_dict) # choose ticket. is_ticket_number_assigned = urbtix_ticket_number_auto_select(driver, config_dict) if show_debug_message: print("is_ticket_number_assigned:", is_ticket_number_assigned) return ret # purpose: date auto select def cityline_date_auto_select(driver, auto_select_mode, date_keyword, auto_reload_coming_soon_page_enable): show_debug_message = True # debug. show_debug_message = False # online ret = False matched_blocks = None area_list = None try: #print("try to find cityline area block") my_css_selector = "button.date-time-position" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #date-time-position date list fail") print(exc) formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("date_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True try: if not row.is_enabled(): row_is_enabled=False except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) if show_debug_message: print("formated_area_list count:", len(formated_area_list)) if len(date_keyword) == 0: matched_blocks = formated_area_list else: # match keyword. if show_debug_message: print("start to match keyword:", date_keyword) matched_blocks = [] row_index = 0 for row in formated_area_list: row_index += 1 #row_is_enabled=False row_is_enabled=True if row_is_enabled: row_text = "" try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if show_debug_message: print("row_text:", row_text) is_match_area = is_row_match_keyword(date_keyword, row_text) if is_match_area: matched_blocks.append(row) if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: break if show_debug_message: if not matched_blocks is None: print("after match keyword, found count:", len(matched_blocks)) else: print("not found date-time-position") pass else: print("date date-time-position is None") pass target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] if not target_area is None: try: if target_area.is_enabled(): target_area.click() ret = True except Exception as exc: # use plan B try: print("force to click by js.") driver.execute_script("arguments[0].click();", target_area) ret = True except Exception as exc: pass else: # no target. if auto_reload_coming_soon_page_enable: # auto refresh for date list page. if not formated_area_list is None: if len(formated_area_list) == 0: try: driver.refresh() time.sleep(0.3) except Exception as exc: pass return ret # purpose: area auto select # return: # True: area block appear. # False: area block not appear. # ps: return is successfully click on the price radio. def cityline_area_auto_select(driver, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True area_auto_select_mode = config_dict["area_auto_select"]["mode"] is_price_assign_by_bot = False is_need_refresh = False matched_blocks = None area_list = None try: #print("try to find cityline area block") my_css_selector = "div.form-check" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #ticket-price-tbl date list fail") print(exc) formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("area_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True try: my_css_selector = "span.price-limited > span" span_price_limited = row.find_element(By.CSS_SELECTOR, my_css_selector) if not span_price_limited is None: #print("found span limited at idx:", row_index) span_i18n_string = str(span_price_limited.get_attribute('data-i18n')) if len(span_i18n_string) > 1: if 'soldout' in span_i18n_string: #print("found span limited soldout at idx:", row_index) row_is_enabled=False except Exception as exc: pass if row_is_enabled: formated_area_list.append(row) else: if show_debug_message: print("area_list_count is empty.") pass else: if show_debug_message: print("area_list_count is None.") pass if not formated_area_list is None: area_list_count = len(formated_area_list) if show_debug_message: print("formated_area_list count:", area_list_count) if area_list_count > 0: matched_blocks = [] if len(area_keyword_item) == 0: matched_blocks = formated_area_list else: # match keyword. row_index = 0 for row in formated_area_list: row_index += 1 #row_is_enabled=False row_is_enabled=True if row_is_enabled: row_text = "" try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" if len(row_text) > 0: row_text = format_keyword_string(row_text) if show_debug_message: print("row_text:", row_text) is_match_area = False if len(area_keyword_item) > 0: # must match keyword. is_match_area = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_match_area = False break else: # without keyword. is_match_area = True if is_match_area: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: break if show_debug_message: print("after match keyword, found count:", len(matched_blocks)) if len(matched_blocks) == 0: matched_blocks = None is_need_refresh = True target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] if not target_area is None: el_btn = None try: #print("target_area text", target_area.text) my_css_selector = "input[type=radio]" el_btn = target_area.find_element(By.CSS_SELECTOR, my_css_selector) if not el_btn is None: if el_btn.is_enabled(): if not el_btn.is_selected(): el_btn.click() is_price_assign_by_bot = True else: is_price_assign_by_bot = True #print("bingo, click target_area radio") except Exception as exc: print("click target_area radio a link fail") print(exc) pass return is_need_refresh, is_price_assign_by_bot #[TODO]: # double check selected radio matched by keyword/keyword_and. def cityline_area_selected_text(driver): ret = False return ret def cityline_ticket_number_auto_select(driver, config_dict): selector_string = 'select.select-num' by_method = By.CSS_SELECTOR return assign_ticket_number_by_select(driver, config_dict, by_method, selector_string) def ibon_ticket_number_appear(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True selector_string = 'table.table > tbody > tr > td > select' by_method = By.CSS_SELECTOR form_select = None try: form_select = driver.find_element(by_method, selector_string) except Exception as exc: if show_debug_message: print("find ticket_number select fail:") print(exc) pass is_visible = False if not form_select is None: try: is_visible = form_select.is_enabled() except Exception as exc: pass return is_visible def ibon_ticket_number_auto_select(driver, config_dict): selector_string = 'table.table > tbody > tr > td > select' by_method = By.CSS_SELECTOR return assign_ticket_number_by_select(driver, config_dict, by_method, selector_string) def assign_ticket_number_by_select(driver, config_dict, by_method, selector_string): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True ticket_number = str(config_dict["ticket_number"]) form_select = None try: form_select = driver.find_element(by_method, selector_string) except Exception as exc: if show_debug_message: print("find ticket_number select fail") print(exc) pass select_obj = None if not form_select is None: is_visible = False try: is_visible = form_select.is_enabled() except Exception as exc: pass if is_visible: try: select_obj = Select(form_select) except Exception as exc: pass is_ticket_number_assigned = False if not select_obj is None: row_text = None try: row_text = select_obj.first_selected_option.text except Exception as exc: pass if not row_text is None: if len(row_text) > 0: if row_text != "0": # ticket assign. is_ticket_number_assigned = True if not is_ticket_number_assigned: is_ticket_number_assigned = tixcraft_ticket_number_auto_fill(driver, select_obj, ticket_number) else: if show_debug_message: print("ticket_number assigned by previous action.") return is_ticket_number_assigned def cityline_purchase_button_press(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True date_auto_select_mode = config_dict["tixcraft"]["date_auto_select"]["mode"] date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() auto_reload_coming_soon_page_enable = config_dict["tixcraft"]["auto_reload_coming_soon_page"] if show_debug_message: print("date_keyword:", date_keyword) is_date_assign_by_bot = cityline_date_auto_select(driver, date_auto_select_mode, date_keyword, auto_reload_coming_soon_page_enable) is_button_clicked = False if is_date_assign_by_bot: is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'button.purchase-btn') return is_button_clicked def cityline_next_button_press(driver): ret = False el_nav = None el_btn = None try: el_nav = driver.find_element(By.CSS_SELECTOR, '.puchase-bottom') el_btn = driver.find_element(By.CSS_SELECTOR, '#expressPurchaseBtn') except Exception as exc: print("find next button fail...") print(exc) if not el_btn is None and not el_nav is None: print("bingo, found next button, start to press") try: if el_btn.is_enabled(): #el_btn.click() builder = ActionChains(driver) builder.move_to_element(el_nav) builder.click(el_btn) builder.perform() ret = True except Exception as exc: print("click next button fail...") print(exc) # use plan B try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_btn) ret = True except Exception as exc: pass return ret def cityline_performance(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True is_price_assign_by_bot = False is_need_refresh = False auto_fill_ticket_number = True if auto_fill_ticket_number: # click price row. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() if show_debug_message: print("area_keyword:", area_keyword) if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_need_refresh, is_price_assign_by_bot = cityline_area_auto_select(driver, config_dict, area_keyword_item) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. # PS: cityline price default value is selected at the first option. is_need_refresh, is_price_assign_by_bot = cityline_area_auto_select(driver, config_dict, "") # un-tested. disable refresh for now. is_need_refresh = False if is_need_refresh: try: driver.refresh() except Exception as exc: pass # choose ticket. is_ticket_number_assigned = cityline_ticket_number_auto_select(driver, config_dict) if show_debug_message: print("ticket_number:", config_dict["ticket_number"]) print("is_ticket_number_assigned:", is_ticket_number_assigned) if is_ticket_number_assigned: auto_press_next_step_button = True if auto_press_next_step_button: if not is_price_assign_by_bot: #[TODO]: # double check selected radio matched by keyword/keyword_and. # cityline_area_selected_text(driver) pass if is_price_assign_by_bot: for i in range(2): click_ret = cityline_next_button_press(driver) if click_ret: break def ibon_date_auto_select(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True auto_select_mode = config_dict["tixcraft"]["date_auto_select"]["mode"] date_keyword = config_dict["tixcraft"]["date_auto_select"]["date_keyword"].strip() auto_reload_coming_soon_page_enable = config_dict["tixcraft"]["auto_reload_coming_soon_page"] if show_debug_message: print("date_keyword:", date_keyword) print("auto_reload_coming_soon_page_enable:", auto_reload_coming_soon_page_enable) matched_blocks = None area_list = None try: my_css_selector = "div.single-content > div > div.row > div > div.tr" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find date-time rows fail") print(exc) #PS: some blocks are generate by ajax, not appear at first time. formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("date_list_count:", area_list_count) if area_list_count > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 # default is enabled. row_is_enabled=True el_btn = None try: my_css_selector = "button" el_btn = row.find_element(By.TAG_NAME, my_css_selector) if not el_btn is None: if not el_btn.is_enabled(): #print("row's button disabled!") row_is_enabled=False except Exception as exc: if show_debug_message: print(exc) pass if row_is_enabled: formated_area_list.append(row) if not formated_area_list is None: area_list_count = len(formated_area_list) if show_debug_message: print("formated_area_list count:", area_list_count) if area_list_count > 0: if len(date_keyword) == 0: matched_blocks = formated_area_list else: # match keyword. if show_debug_message: print("start to match keyword:", date_keyword) matched_blocks = get_matched_blocks_by_keyword(config_dict, auto_select_mode, date_keyword, formated_area_list) if show_debug_message: if not matched_blocks is None: print("after match keyword, found count:", len(matched_blocks)) else: print("not found date-time-position") pass else: print("date date-time-position is None") pass target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] is_date_assign_by_bot = False if not target_area is None: is_button_clicked = False for i in range(3): el_btn = None try: my_css_selector = "button.btn" el_btn = target_area.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass if not el_btn is None: try: if el_btn.is_enabled(): el_btn.click() print("buy icon pressed.") is_button_clicked = True except Exception as exc: pass # use plan B ''' try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_btn) ret = True except Exception as exc: pass ''' if is_button_clicked: break is_date_assign_by_bot = is_button_clicked else: # no target to click. if auto_reload_coming_soon_page_enable: # auto refresh for date list page. if not formated_area_list is None: if len(formated_area_list) == 0: try: driver.refresh() time.sleep(0.3) except Exception as exc: pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) return is_date_assign_by_bot def ibon_area_auto_select(driver, config_dict, area_keyword_item): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True area_auto_select_mode = config_dict["area_auto_select"]["mode"] #print("area_auto_select_mode:", area_auto_select_mode) # when avaiable seat under this count, check seat text content. CONST_DETECT_SEAT_ATTRIBUTE_UNDER_ROW_COUNT = 20 is_price_assign_by_bot = False is_need_refresh = False area_list = None try: #print("try to find cityline area block") #my_css_selector = "div.col-md-5 > table > tbody > tr[onclick=\"onTicketArea(this.id)\"]" my_css_selector = "div.col-md-5 > table > tbody > tr:not(.disabled)" area_list = driver.find_elements(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find #ticket-price-tbl date list fail") print(exc) formated_area_list = None if not area_list is None: area_list_count = len(area_list) if show_debug_message: print("area_list_count:", area_list_count) print("area_keyword_item:", area_keyword_item) if area_list_count > 0: formated_area_list = [] # filter list. row_index = 0 for row in area_list: row_index += 1 row_is_enabled=True row_text = "" if row_is_enabled: try: row_text = row.text except Exception as exc: pass if row_text is None: row_text = "" if '已售完' in row_text: row_is_enabled=False if len(row_text) > 0: if row_text == "座位已被選擇": row_text="" if row_text == "座位已售出": row_text="" if row_text == "舞台區域": row_text="" if len(row_text) > 0: if reset_row_text_if_match_keyword_exclude(config_dict, row_text): row_text = "" # check ticket count when amount is few, because of it spent a lot of time at parsing element. if len(row_text) > 0: is_seat_remaining_checking = False # PS: when user query with keyword, but when selected row is too many, not every row to check remaing. # may cause the matched keyword row ticket seat under user target ticket number. if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: if len(formated_area_list)==0: is_seat_remaining_checking = True if area_list_count <= CONST_DETECT_SEAT_ATTRIBUTE_UNDER_ROW_COUNT: is_seat_remaining_checking = True if is_seat_remaining_checking: try: area_seat_el = row.find_element(By.CSS_SELECTOR, 'td.action') if not area_seat_el is None: seat_text = area_seat_el.text if seat_text is None: seat_text = "" if seat_text.isdigit(): seat_int = int(seat_text) if seat_int < config_dict["ticket_number"]: # skip this row. if show_debug_message: print("skip not enought ticket number area at row_text:", row_text) row_text = "" except Exception as exc: if show_debug_message: print(exc) pass if row_text == "": row_is_enabled=False if row_is_enabled: try: button_class_string = str(row.get_attribute('class')) if not button_class_string is None: if len(button_class_string) > 1: if 'disabled' in button_class_string: row_is_enabled=False if 'sold-out' in button_class_string: row_is_enabled=False except Exception as exc: pass if row_is_enabled: pass # each row to check is too slow. ''' row_is_enabled = False try: row_id_string = str(row.get_attribute('id')) if not row_id_string is None: if len(row_id_string) > 1: row_is_enabled = True except Exception as exc: pass ''' if row_is_enabled: formated_area_list.append(row) else: if show_debug_message: print("area_list_count is empty.") pass else: if show_debug_message: print("area_list_count is None.") pass if is_price_assign_by_bot: formated_area_list = None matched_blocks = [] if not formated_area_list is None: area_list_count = len(formated_area_list) if show_debug_message: print("formated_area_list count:", area_list_count) if area_list_count > 0: if len(area_keyword_item) == 0: matched_blocks = formated_area_list else: row_index = 0 for row in formated_area_list: row_index += 1 row_is_enabled=True if row_is_enabled: row_text = "" try: row_text = row.text except Exception as exc: print("get text fail") break if row_text is None: row_text = "" if len(row_text) > 0: row_text = format_keyword_string(row_text) if show_debug_message: print("row_text:", row_text) is_match_area = False if len(area_keyword_item) > 0: # must match keyword. is_match_area = True area_keyword_array = area_keyword_item.split(' ') for area_keyword in area_keyword_array: area_keyword = format_keyword_string(area_keyword) if not area_keyword in row_text: is_match_area = False break else: # without keyword. is_match_area = True if is_match_area: matched_blocks.append(row) if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: break if show_debug_message: print("after match keyword, found count:", len(matched_blocks)) target_area = None if not matched_blocks is None: if len(matched_blocks) > 0: target_row_index = 0 if area_auto_select_mode == CONST_FROM_TOP_TO_BOTTOM: pass if area_auto_select_mode == CONST_FROM_BOTTOM_TO_TOP: target_row_index = len(matched_blocks)-1 if area_auto_select_mode == CONST_RANDOM: target_row_index = random.randint(0,len(matched_blocks)-1) target_area = matched_blocks[target_row_index] else: is_need_refresh = True if show_debug_message: print("matched_blocks is empty, is_need_refresh") if not target_area is None: try: if target_area.is_enabled(): target_area.click() is_price_assign_by_bot = True except Exception as exc: print("click target_area link fail") print(exc) # use plan B try: print("force to click by js.") driver.execute_script("arguments[0].click();", target_area) is_price_assign_by_bot = True except Exception as exc: pass return is_need_refresh, is_price_assign_by_bot def ibon_allow_not_adjacent_seat(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True agree_checkbox = None try: my_css_selector = 'div.not-consecutive > div.custom-control > span > input[type="checkbox"]' agree_checkbox = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: print("find ibon seat checkbox Exception") if show_debug_message: print(exc) pass is_finish_checkbox_click = force_check_checkbox(driver, agree_checkbox) return is_finish_checkbox_click def ibon_performance(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True is_price_assign_by_bot = False is_need_refresh = False # click price row. area_keyword = config_dict["area_auto_select"]["area_keyword"].strip() if show_debug_message: print("area_keyword:", area_keyword) is_need_refresh = False if len(area_keyword) > 0: area_keyword_array = [] try: area_keyword_array = json.loads("["+ area_keyword +"]") except Exception as exc: area_keyword_array = [] for area_keyword_item in area_keyword_array: is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword_item) if not is_need_refresh: break else: print("is_need_refresh for keyword:", area_keyword_item) else: # empty keyword, match all. is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword) if show_debug_message: print("is_need_refresh:", is_need_refresh) if is_need_refresh: try: driver.refresh() except Exception as exc: pass if config_dict["advanced"]["auto_reload_random_delay"]: time.sleep(random.randint(0,CONST_AUTO_RELOAD_RANDOM_DELAY_MAX_SECOND)) return is_price_assign_by_bot def ibon_purchase_button_press(driver): is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, '#ticket-wrap > a.btn') return is_button_clicked def assign_text(driver, by, query, val, overwrite = False, submit=False): show_debug_message = True # debug. show_debug_message = False # online if val is None: val = "" is_visible = False if len(val) > 0: el_text = None try: el_text = driver.find_element(by, query) except Exception as exc: if show_debug_message: print(exc) pass if not el_text is None: try: if el_text.is_enabled() and el_text.is_displayed(): is_visible = True except Exception as exc: if show_debug_message: print(exc) pass is_text_sent = False if is_visible: try: inputed_text = el_text.get_attribute('value') if not inputed_text is None: is_do_keyin = False if len(inputed_text) == 0: is_do_keyin = True else: if inputed_text == val: is_text_sent = True else: if overwrite: el_text.clear() is_do_keyin = True if is_do_keyin: el_text.click() el_text.send_keys(val) if submit: el_text.send_keys(Keys.ENTER) is_text_sent = True except Exception as exc: if show_debug_message: print(exc) pass return is_text_sent def facebook_login(driver, account, password): is_email_sent = assign_text(driver, By.CSS_SELECTOR, '#email', account) is_password_sent = False if is_email_sent: is_password_sent = assign_text(driver, By.CSS_SELECTOR, '#pass', password, submit=True) return is_password_sent def kktix_login(driver, account, password): ret = False el_email = None try: el_email = driver.find_element(By.CSS_SELECTOR, '#user_login') except Exception as exc: pass is_visible = False if not el_email is None: try: if el_email.is_enabled(): is_visible = True except Exception as exc: pass is_email_sent = False if is_visible: try: inputed_text = el_email.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_email.send_keys(account) is_email_sent = True else: if inputed_text == account: is_email_sent = True except Exception as exc: pass el_pass = None if is_email_sent: try: el_pass = driver.find_element(By.CSS_SELECTOR, '#user_password') except Exception as exc: pass is_password_sent = False if not el_pass is None: try: if el_pass.is_enabled(): inputed_text = el_pass.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_pass.click() if(len(password)>0): el_pass.send_keys(password) el_pass.send_keys(Keys.ENTER) is_password_sent = True time.sleep(0.1) except Exception as exc: pass ret = is_password_sent return ret def cityline_login(driver, account, password): is_email_sent = assign_text(driver, By.CSS_SELECTOR, 'input[type="text"]', account, submit=True) # press "click here" use password to login. if is_email_sent: is_click_here_pressed = force_press_button(driver, By.CSS_SELECTOR,'.otp-box > ul > li:nth-child(3) > a') is_password_sent = False if is_email_sent: is_password_sent = assign_text(driver, By.CSS_SELECTOR, 'div > input[type="password"]', password, submit=True) return is_password_sent def urbtix_login(driver, account, password): ret = False el_email = None try: el_email = driver.find_element(By.CSS_SELECTOR, 'input[name="loginId"]') except Exception as exc: #print("find #email fail") #print(exc) pass is_visible = False if not el_email is None: try: if el_email.is_enabled(): is_visible = True except Exception as exc: pass is_email_sent = False if is_visible: try: inputed_text = el_email.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_email.send_keys(account) is_email_sent = True else: if inputed_text == account: is_email_sent = True except Exception as exc: pass el_pass = None if is_email_sent: try: el_pass = driver.find_element(By.CSS_SELECTOR, 'input[name="password"]') except Exception as exc: pass is_password_sent = False if not el_pass is None: try: if el_pass.is_enabled(): inputed_text = el_pass.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_pass.click() if(len(password)>0): el_pass.send_keys(password) is_password_sent = True except Exception as exc: pass el_btn = None if is_password_sent: try: el_btn = driver.find_element(By.CSS_SELECTOR, '.login-button') if not el_btn is None: el_btn.click() except Exception as exc: pass return ret def kham_login(driver, account, password): ret = False el_email = None try: my_css_selector = '#ACCOUNT' el_email = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass is_visible = False if not el_email is None: try: if el_email.is_enabled(): is_visible = True except Exception as exc: pass is_email_sent = False if is_visible: try: inputed_text = el_email.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_email.send_keys(account) is_email_sent = True else: if inputed_text == account: is_email_sent = True except Exception as exc: pass el_pass = None if is_email_sent: try: my_css_selector = 'table.login > tbody > tr > td > input[type="password"]' el_pass = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass is_password_sent = False if not el_pass is None: try: if el_pass.is_enabled(): inputed_text = el_pass.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_pass.click() if(len(password)>0): el_pass.send_keys(password) is_password_sent = True time.sleep(0.1) except Exception as exc: pass if is_password_sent: is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'div.memberContent > p > a > button.red') ret = is_password_sent return ret def ticket_login(driver, account, password): ret = False el_email = None try: my_css_selector = 'input[cname="帳號"]' el_email = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass is_visible = False if not el_email is None: try: if el_email.is_enabled(): is_visible = True except Exception as exc: pass is_email_sent = False if is_visible: try: inputed_text = el_email.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_email.send_keys(account) is_email_sent = True else: if inputed_text == account: is_email_sent = True except Exception as exc: pass el_pass = None if is_email_sent: try: my_css_selector = 'input[cname="密碼"]' el_pass = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass is_password_sent = False if not el_pass is None: try: if el_pass.is_enabled(): inputed_text = el_pass.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_pass.click() if(len(password)>0): el_pass.send_keys(password) is_password_sent = True time.sleep(0.1) except Exception as exc: pass if is_password_sent: is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'input[value="登入"]') ret = is_password_sent return ret def hkticketing_login(driver, account, password): ret = False el_email = None try: my_css_selector = 'div#myTick2Col > div.formMod2Col > div.formModule > div.loginContentContainer > input.borInput' el_email = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: #print("find_element exception") #print(exc) pass is_visible = False if not el_email is None: try: if el_email.is_enabled(): is_visible = True else: el_email.click() except Exception as exc: pass else: #print("account field is None.") pass is_email_sent = False if is_visible: try: # PS: this is special case... el_email.click() inputed_text = el_email.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: #print("send account text:", account) el_email.send_keys(account) is_email_sent = True else: if inputed_text == account: is_email_sent = True except Exception as exc: pass el_pass = None if is_email_sent: try: my_css_selector = 'div.loginContentContainer > input[type="password"]' el_pass = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: pass is_password_sent = False if not el_pass is None: try: if el_pass.is_enabled(): el_pass.click() inputed_text = el_pass.get_attribute('value') if not inputed_text is None: if len(inputed_text) == 0: el_pass.click() if(len(password)>0): #print("send password text:", password) el_pass.send_keys(password) el_pass.send_keys(Keys.ENTER) is_password_sent = True time.sleep(0.1) else: el_pass.click() except Exception as exc: pass ret = is_password_sent return ret def check_and_play_sound_for_captcha(config_dict): if config_dict["advanced"]["play_captcha_sound"]["enable"]: app_root = get_app_root() captcha_sound_filename = os.path.join(app_root, config_dict["advanced"]["play_captcha_sound"]["filename"].strip()) play_mp3_async(captcha_sound_filename) def play_mp3_async(sound_filename): import threading threading.Thread(target=play_mp3, args=(sound_filename,), daemon=True).start() def play_mp3(sound_filename): try: from playsound import playsound playsound(sound_filename) except Exception as exc: msg=str(exc) #print("play sound exeption:", msg) if platform.system() == 'Windows': import winsound try: winsound.PlaySound(sound_filename, winsound.SND_FILENAME) except Exception as exc2: pass # purpose: check alert poped. # PS: current version not enable... def check_pop_alert(driver): is_alert_popup = False # https://stackoverflow.com/questions/57481723/is-there-a-change-in-the-handling-of-unhandled-alert-in-chromedriver-and-chrome default_close_alert_text = [""] if len(default_close_alert_text) > 0: try: alert = None if not driver is None: #alert = driver.switch_to.alert alert = WebDriverWait(driver, 0.2).until(EC.alert_is_present()) if not alert is None: alert_text = str(alert.text) if not alert_text is None: is_match_auto_close_text = False for txt in default_close_alert_text: if len(txt) > 0: if txt in alert.text: is_match_auto_close_text = True else: is_match_auto_close_text = True #print("is_match_auto_close_text:", is_match_auto_close_text) #print("alert3 text:", alert.text) if is_match_auto_close_text: alert.accept() print("alert3 accepted") is_alert_popup = True else: print("alert3 not detected") except NoAlertPresentException as exc1: #logger.error('NoAlertPresentException for alert') pass except NoSuchWindowException: pass except Exception as exc: #logger.error('Exception2 for alert') #logger.error(exc, exc_info=True) pass return is_alert_popup def list_all_cookies(driver): cookies_dict = {} if not driver is None: all_cookies=driver.get_cookies(); for cookie in all_cookies: cookies_dict[cookie['name']] = cookie['value'] print(cookies_dict) def set_non_browser_cookies(driver, url, Captcha_Browser): if not driver is None: domain_name = url.split('/')[2] #PS: need set cookies once, if user change domain. if not Captcha_Browser is None: Captcha_Browser.Set_cookies(driver.get_cookies()) Captcha_Browser.Set_Domain(domain_name) def ticketmaster_parse_zone_info(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True mapSelectArea = None try: my_css_selector = '#mapSelectArea' mapSelectArea = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: if show_debug_message: print('fail to find my_css_selector:', my_css_selector) #print("find table#ticketPriceList fail", exc) if not mapSelectArea is None: mapSelectArea_html = "" try: mapSelectArea_html = mapSelectArea.get_attribute('innerHTML') except Exception as exc: mapSelectArea_html = "" if show_debug_message: print(exc) zone_string = "" tag_start = "var zone =" tag_end = "fieldImageType" if tag_start in mapSelectArea_html and tag_end in mapSelectArea_html: if show_debug_message: print('found zone info!') zone_string = mapSelectArea_html.split(tag_start)[1] zone_string = zone_string.split(tag_end)[0] zone_string = zone_string.strip() if zone_string[-1:] == "\n": zone_string=zone_string[:-1] zone_string = zone_string.strip() if zone_string[-1:] == ",": zone_string=zone_string[:-1] if show_debug_message: #print('found zone info string:', zone_string) pass if len(zone_string) > 0: zone_info = {} try: zone_info = json.loads(zone_string) if show_debug_message: #print("zone_info", zone_info) pass if not zone_info is None: ticketmaster_area_auto_select(driver, config_dict, zone_info) except Exception as exc: if show_debug_message: print(exc) def ticketmaster_get_ticketPriceList(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True div_mapContainer = None try: my_css_selector = '#mapContainer' div_mapContainer = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: if show_debug_message: print('fail to find my_css_selector:', my_css_selector) #print("find table#ticketPriceList fail", exc) table_select = None if not div_mapContainer is None: is_loading = False # check is loading. div_loadingmap = None try: my_css_selector = '#loadingmap' div_loadingmap = driver.find_element(By.CSS_SELECTOR, my_css_selector) if not div_loadingmap is None: is_loading = True except Exception as exc: if show_debug_message: print('fail to find my_css_selector:', my_css_selector) #print("find table#ticketPriceList fail", exc) if not is_loading: try: my_css_selector = '#ticketPriceList' table_select = driver.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: if show_debug_message: print('fail to find my_css_selector:', my_css_selector) #print("find table#ticketPriceList fail", exc) if table_select is None: ticketmaster_parse_zone_info(driver, config_dict) return table_select def ticketmaster_assign_ticket_number(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True table_select = ticketmaster_get_ticketPriceList(driver, config_dict) select_obj = None if not table_select is None: form_select = None if show_debug_message: print('found table#ticketPriceList, start find select') try: my_css_selector = 'select' form_select = table_select.find_element(By.CSS_SELECTOR, my_css_selector) except Exception as exc: if show_debug_message: print('my_css_selector:', my_css_selector) print("find form-select fail", exc) pass if not form_select is None: if show_debug_message: print('found ticket number select.') is_visible = False try: if form_select.is_enabled(): is_visible = True except Exception as exc: pass if is_visible: try: select_obj = Select(form_select) except Exception as exc: pass is_ticket_number_assigned = False if not select_obj is None: row_text = None try: row_text = select_obj.first_selected_option.text except Exception as exc: pass if not row_text is None: if show_debug_message: print('row_text:', row_text) if len(row_text) > 0: if row_text != "0": if row_text.isnumeric(): # ticket assign. is_ticket_number_assigned = True if show_debug_message: print('is_ticket_number_assigned:', is_ticket_number_assigned) # must wait select object ready to assign ticket number. if not is_ticket_number_assigned: ticket_number = str(config_dict["ticket_number"]) is_ticket_number_assigned = tixcraft_ticket_number_auto_fill(driver, select_obj, ticket_number) # must wait ticket number assign to focus captcha. if is_ticket_number_assigned: is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'#autoMode') def ticketmaster_captcha(driver, config_dict, ocr, Captcha_Browser, domain_name): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True away_from_keyboard_enable = config_dict["ocr_captcha"]["force_submit"] if not config_dict["ocr_captcha"]["enable"]: away_from_keyboard_enable = False ocr_captcha_image_source = config_dict["ocr_captcha"]["image_source"] if config_dict["auto_check_agree"]: for i in range(3): is_finish_checkbox_click = tixcraft_ticket_agree(driver, config_dict) if is_finish_checkbox_click: break if not config_dict["ocr_captcha"]["enable"]: tixcraft_keyin_captcha_code(driver) else: previous_answer = None last_url, is_quit_bot = get_current_url(driver) for redo_ocr in range(999): is_need_redo_ocr, previous_answer, is_form_sumbited = tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer, Captcha_Browser, ocr_captcha_image_source, domain_name) if is_form_sumbited: # start next loop. break if not away_from_keyboard_enable: break if not is_need_redo_ocr: break current_url, is_quit_bot = get_current_url(driver) if current_url != last_url: break def tixcraft_ad_footer(driver, config_dict): ad_div = None try: ad_div = driver.find_element(By.CSS_SELECTOR, '#ad-footer') if not ad_div is None: driver.execute_script("arguments[0].innerHTML='';", ad_div) except Exception as exc: #print(exc) pass try: ad_div = driver.find_element(By.CSS_SELECTOR, 'footer.footer') if not ad_div is None: driver.execute_script("arguments[0].innerHTML='';", ad_div) except Exception as exc: #print(exc) pass def tixcraft_main(driver, url, config_dict, tixcraft_dict, ocr, Captcha_Browser): tixcraft_ad_footer(driver, config_dict) tixcraft_home_close_window(driver, config_dict) home_url_list = ['https://tixcraft.com/' ,'https://www.tixcraft.com/' ,'https://indievox.com/' ,'https://www.indievox.com/' ,'https://teamear.tixcraft.com/activity' ,'https://www.ticketmaster.sg/' ,'https://www.ticketmaster.com/' ] for each_url in home_url_list: if each_url == url: if config_dict["ocr_captcha"]["enable"]: set_non_browser_cookies(driver, url, Captcha_Browser) pass break if "/activity/detail/" in url: is_redirected = tixcraft_redirect(driver, url) is_date_selected = False if "/activity/game/" in url: date_auto_select_enable = config_dict["tixcraft"]["date_auto_select"]["enable"] if date_auto_select_enable: domain_name = url.split('/')[2] is_date_selected = tixcraft_date_auto_select(driver, url, config_dict, domain_name) if '/artist/' in url and 'ticketmaster.com' in url: is_event_page = False if len(url.split('/'))==6: is_event_page = True if is_event_page: date_auto_select_enable = config_dict["tixcraft"]["date_auto_select"]["enable"] if date_auto_select_enable: domain_name = url.split('/')[2] is_date_selected = ticketmaster_date_auto_select(driver, url, config_dict, domain_name) # choose area if '/ticket/area/' in url: domain_name = url.split('/')[2] if config_dict["area_auto_select"]["enable"]: if not 'ticketmaster' in domain_name: # for tixcraft tixcraft_area_auto_select(driver, url, config_dict) else: # area auto select is too difficult, skip in this version. tixcraft_dict["fail_promo_list"] = ticketmaster_promo(driver, config_dict, tixcraft_dict["fail_promo_list"]) ticketmaster_assign_ticket_number(driver, config_dict) else: tixcraft_dict["fail_promo_list"] = [] # https://ticketmaster.sg/ticket/check-captcha/23_blackpink/954/5/75 if '/ticket/check-captcha/' in url: domain_name = url.split('/')[2] ticketmaster_captcha(driver, config_dict, ocr, Captcha_Browser, domain_name) if '/ticket/verify/' in url: tixcraft_dict["fail_list"] = tixcraft_verify(driver, config_dict, tixcraft_dict["fail_list"]) else: tixcraft_dict["fail_list"] = [] # main app, to select ticket number. if '/ticket/ticket/' in url: domain_name = url.split('/')[2] tixcraft_ticket_main(driver, config_dict, ocr, Captcha_Browser, domain_name) if '/ticket/checkout' in url: if config_dict["advanced"]["headless"]: if not tixcraft_dict["is_popup_checkout"]: domain_name = url.split('/')[2] checkout_url = "https://%s/ticket/checkout" % (domain_name) print("搶票成功, 請前往該帳號訂單查看: %s" % (checkout_url)) webbrowser.open_new(checkout_url) tixcraft_dict["is_popup_checkout"] = True else: tixcraft_dict["is_popup_checkout"] = False return tixcraft_dict def kktix_main(driver, url, config_dict, kktix_dict): kktix_account = config_dict["advanced"]["kktix_account"] is_url_contain_sign_in = False # fix https://kktix.com/users/sign_in?back_to=https://kktix.com/events/xxxx and registerStatus: SOLD_OUT cause page refresh. if '/users/sign_in?' in url: if len(kktix_account) > 4: kktix_login(driver, kktix_account, decryptMe(config_dict["advanced"]["kktix_password"])) is_url_contain_sign_in = True if not is_url_contain_sign_in: if '/registrations/new' in url: is_need_refresh, is_finish_checkbox_click = kktix_reg_auto_reload(driver, url, config_dict, kktix_dict["kktix_register_status_last"]) if is_need_refresh: # reset answer fail list. kktix_dict["fail_list"] = [] kktix_dict["captcha_sound_played"] = False kktix_dict["kktix_register_status_last"] = None else: # check is able to buy. if config_dict["kktix"]["auto_fill_ticket_number"]: kktix_dict["fail_list"], kktix_dict["captcha_sound_played"] = kktix_reg_new_main(driver, config_dict, kktix_dict["fail_list"], kktix_dict["captcha_sound_played"], is_finish_checkbox_click) else: is_event_page = False if '/events/' in url: # ex: https://xxx.kktix.cc/events/xxx-copy-1 if len(url.split('/'))<=5: is_event_page = True if is_event_page: if config_dict["kktix"]["auto_press_next_step_button"]: # pass switch check. #print("should press next here.") kktix_events_press_next_button(driver) # reset answer fail list. kktix_dict["fail_list"] = [] kktix_dict["captcha_sound_played"] = False kktix_dict["kktix_register_status_last"] = None if '/events/' in url and '/registrations/' in url and "-" in url: if config_dict["advanced"]["headless"]: if not kktix_dict["is_popup_checkout"]: is_event_page = False if len(url.split('/'))==8: is_event_page = True if is_event_page: confirm_clicked = kktix_confirm_order_button(driver) if confirm_clicked: domain_name = url.split('/')[2] checkout_url = "https://%s/account/orders" % (domain_name) print("搶票成功, 請前往該帳號訂單查看: %s" % (checkout_url)) webbrowser.open_new(checkout_url) tixcraft_dict["is_popup_checkout"] = True else: kktix_dict["is_popup_checkout"] = False return kktix_dict def famiticket_main(driver, url, config_dict): try: window_handles_count = len(driver.window_handles) if window_handles_count > 1: driver.switch_to.window(driver.window_handles[0]) driver.close() driver.switch_to.window(driver.window_handles[0]) except Exception as excSwithFail: pass if '/Home/Activity/Info/' in url: fami_activity(driver) if '/Sales/Home/Index/' in url: fami_home(driver, url, config_dict) def urbtix_performance_confirm_dialog_popup(driver): ret = False el_div = None try: el_div = driver.find_element(By.CSS_SELECTOR, 'div.notification-confirm-btn > div.button-text') except Exception as exc: #print("find modal-dialog fail") #print(exc) pass if not el_div is None: print("bingo, found notification-confirm-btn") is_visible = False try: if el_div.is_enabled(): if el_div.is_displayed(): is_visible = True except Exception as exc: pass if is_visible: try: el_div.click() ret = True except Exception as exc: # use plan B ''' try: print("force to click by js.") driver.execute_script("arguments[0].click();", el_div) ret = True except Exception as exc: pass ''' pass if ret: time.sleep(0.4) return ret def get_urbtix_survey_answer_by_question(question_text): show_debug_message = True # debug. show_debug_message = False # online question_text = question_text.replace(' ',' ') question_text = full2half(question_text) seq = 0 if '第' in question_text and '個' in question_text: temp_string = question_text.split('第')[1] seq_string = temp_string.split('個')[0] if len(seq_string) > 0: if seq_string.isdigit(): seq = int(seq_string) else: tmp_seq = normalize_chinese_numeric(seq_string) if not tmp_seq is None: seq = tmp_seq if show_debug_message: print("seq:", seq) direction = "left" if '右起' in question_text: direction = "right" if '由右' in question_text: direction = "right" if '從右' in question_text: direction = "right" if '自右' in question_text: direction = "right" if '右算' in question_text: direction = "right" if '右邊' in question_text: direction = "right" if ' from the RIGHT' in question_text: direction = "right" if '有多少個' in question_text: direction = "count" if '有幾個' in question_text: direction = "count" if 'How many ' in question_text: direction = "count" if show_debug_message: print("direction:", direction) question_text_formated = question_text question_text_formated = question_text_formated.replace('「','') question_text_formated = question_text_formated.replace('」','') question_text_formated = question_text_formated.replace(' ','') question_text_formated = question_text_formated.replace('-','') question_text_formated = question_text_formated.replace('_','') # format question. question_text_formated = question_text_formated.replace(';','') question_text_formated = question_text_formated.replace('.','') question_text_formated = question_text_formated.replace(':','') question_text_formated = question_text_formated.replace(',','') question_answer_char = "" option_text_string = find_continuous_text(question_text_formated) if show_debug_message: print("option_text_string:", option_text_string) if direction in ['left','right']: if seq > 0: if len(option_text_string) > 1: if seq <= len(option_text_string): if direction == "left": question_answer_char = option_text_string[seq-1:seq] if direction == "right": question_answer_char = option_text_string[len(option_text_string)-seq:len(option_text_string)-seq+1] if direction == "count": if '個' in question_text_formated: count_target = None count_answer = 0 tmp_seq = question_text_formated.split('個')[1] if len(tmp_seq) > 0: count_target_string = tmp_seq[:1] if len(count_target_string) > 0: if count_target_string.isdigit(): count_target = int(count_target_string) else: count_target = normalize_chinese_numeric(count_target_string) if not count_target is None: for char in option_text_string: if char == str(count_target): count_answer += 1 question_answer_char = str(count_answer) if show_debug_message: print("count_target:", count_target) print("count_answer:", count_answer) if show_debug_message: print("question_answer_char text:", question_answer_char) return question_answer_char, direction # PS: due to open-ended question, disable this feature now. def urbtix_auto_survey(driver, config_dict): show_debug_message = True # debug. show_debug_message = False # online if config_dict["advanced"]["verbose"]: show_debug_message = True questions_div = None try: questions_div = driver.find_elements(By.CSS_SELECTOR, 'div.modal-content > div.content > div.questions > div.question-item') except Exception as exc: #print("find modal-dialog fail") #print(exc) pass is_radio_clicked = False fill_question_count = 0 if not questions_div is None: quetions_count = len(questions_div) if show_debug_message: print("quetions_count:", quetions_count) try: for each_question_div in questions_div: each_question_ask_div = each_question_div.find_element(By.CSS_SELECTOR, 'div.titles > div') if not each_question_ask_div is None: question_text = each_question_ask_div.text if question_text is None: question_text = "" if show_debug_message: print("questions_div text:", question_text) question_answer_char, question_direction = get_urbtix_survey_answer_by_question(question_text) each_option_items_div = each_question_div.find_elements(By.CSS_SELECTOR, 'div.options > div.option-item') if not each_option_items_div is None: question_answered = False for each_option_div in each_option_items_div: option_content_div = each_option_div.find_element(By.CSS_SELECTOR, 'div.content-list') if not option_content_div is None: option_content_div_text = option_content_div.text if option_content_div is None: option_content_div="" option_content_div_text = option_content_div_text.strip() option_content_div_text = full2half(option_content_div_text) if question_direction in ['left','right']: for answer_item in synonyms(question_answer_char): if answer_item in option_content_div_text: is_radio_clicked = force_press_button(each_option_div, By.CSS_SELECTOR, 'div.radio-wrapper') if is_radio_clicked: if show_debug_message: print("fill answer:", answer_item) question_answered = True break if question_direction == "count": for answer_item in synonyms(question_answer_char): if answer_item in option_content_div_text: is_radio_clicked = force_press_button(each_option_div, By.CSS_SELECTOR, 'div.radio-wrapper') if is_radio_clicked: if show_debug_message: print("fill answer:", answer_item) question_answered = True break if question_answer_char == '0': is_match_none = False if '沒有' in option_content_div_text: is_match_none = True if 'LESS THEN ONE' in option_content_div_text.upper(): is_match_none = True if is_match_none: is_radio_clicked = force_press_button(each_option_div, By.CSS_SELECTOR, 'div.radio-wrapper') if is_radio_clicked: if show_debug_message: print("fill answer:", '沒有') question_answered = True break int_answer_char = int(question_answer_char) if int_answer_char > 1: for i in range(int_answer_char-1): for answer_item in synonyms(str(i+1)): is_match_more_then = False if answer_item + '個或以上' in option_content_div_text: is_match_more_then = True if answer_item + '個以上' in option_content_div_text: is_match_more_then = True if '多於' in option_content_div_text and answer_item + '個' in option_content_div_text: is_match_more_then = True if '更多' in option_content_div_text and answer_item + '個' in option_content_div_text: is_match_more_then = True if 'MORE THEN' in option_content_div_text.upper() and answer_item + '個' in option_content_div_text: is_match_more_then = True if is_match_more_then: is_radio_clicked = force_press_button(each_option_div, By.CSS_SELECTOR, 'div.radio-wrapper') if is_radio_clicked: if show_debug_message: print("fill answer:", answer_item + '個或以上') question_answered = True break if question_answered: break if question_answered: fill_question_count += 1 break except Exception as exc: if show_debug_message: print(exc) pass if show_debug_message: print("fill_question_count:", fill_question_count) #if is_radio_clicked and fill_question_count>=3: if is_radio_clicked and fill_question_count>=2: questions_remain_div = None questions_remain_text = "" try: questions_remain_div = driver.find_element(By.CSS_SELECTOR, 'div.surplus-questions-number') if not questions_remain_div is None: questions_remain_text = questions_remain_div.text questions_remain_text = questions_remain_text.strip() except Exception as exc: if show_debug_message: print(exc) pass if show_debug_message: #print("questions_remain_text:", questions_remain_text) pass if questions_remain_text == "0" or questions_remain_text == "": is_button_clicked = False #is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'div.button-wrapper > div.button-text-multi-lines > div') # Message: Element