From 88dbe5841c101595ad25005df53c14ddd9ff3131 Mon Sep 17 00:00:00 2001 From: CHUN YU YAO Date: Sat, 25 Nov 2023 09:11:43 +0800 Subject: [PATCH] 2023-11-19, add headless mode, fix macOS import Pillow fail. --- chrome_tixcraft.py | 92 ++++++++++++++++++++++++++++++++++++++++++---- config_launcher.py | 2 +- pip-req.txt | 1 + settings.json | 2 +- settings.py | 29 ++++++++++++++- text_server.py | 2 +- 6 files changed, 116 insertions(+), 12 deletions(-) diff --git a/chrome_tixcraft.py b/chrome_tixcraft.py index 347a956..2a28730 100644 --- a/chrome_tixcraft.py +++ b/chrome_tixcraft.py @@ -42,10 +42,13 @@ ssl._create_default_https_context = ssl._create_unverified_context # ocr import base64 -# workaround for ddddocr with Pillow. -from PIL import Image -if not hasattr(Image, 'ANTIALIAS'): - setattr(Image, 'ANTIALIAS', Image.LANCZOS) +try: + # workaround for ddddocr with Pillow. + from PIL import Image + if not hasattr(Image, 'ANTIALIAS'): + setattr(Image, 'ANTIALIAS', Image.LANCZOS) +except Exception as exc: + pass try: import ddddocr @@ -60,7 +63,7 @@ import webbrowser import chromedriver_autoinstaller -CONST_APP_VERSION = "MaxBot (2023.11.18)" +CONST_APP_VERSION = "MaxBot (2023.11.19)" CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt" @@ -165,9 +168,15 @@ def get_config_dict(args): config_dict = None if os.path.isfile(config_filepath): + # start to overwrite config settings. with open(config_filepath) as json_data: config_dict = json.load(json_data) + 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 @@ -194,6 +203,19 @@ def get_config_dict(args): if len(args.proxy_server) > 2: config_dict["advanced"]["proxy_server_port"] = args.proxy_server + # 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): @@ -374,6 +396,10 @@ def get_chrome_options(webdriver_path, config_dict): if os.path.exists(ext): chrome_options.add_extension(ext) + if config_dict["advanced"]["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') @@ -525,6 +551,10 @@ def get_uc_options(uc, config_dict, webdriver_path): 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') @@ -579,7 +609,7 @@ def load_chromdriver_uc(config_dict): try: options = get_uc_options(uc, config_dict, webdriver_path) - driver = uc.Chrome(driver_executable_path=chromedriver_path, options=options) + 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) @@ -669,7 +699,8 @@ def get_driver_by_config(config_dict): #print(config_dict["tixcraft"]) #print("==[advanced config]==") - #print(config_dict["advanced"]) + if config_dict["advanced"]["verbose"]: + print(config_dict["advanced"]) print("webdriver_type:", config_dict["webdriver_type"]) # entry point @@ -714,6 +745,9 @@ def get_driver_by_config(config_dict): 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): @@ -7483,12 +7517,22 @@ def tixcraft_main(driver, url, config_dict, tixcraft_dict, ocr, Captcha_Browser) tixcraft_dict["done_time"] = time.time() 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 + if not tixcraft_dict["start_time"] is None: if not tixcraft_dict["done_time"] is None: bot_elapsed_time = tixcraft_dict["done_time"] - tixcraft_dict["start_time"] if tixcraft_dict["elapsed_time"] != bot_elapsed_time: print("bot elapsed time:", "{:.3f}".format(bot_elapsed_time)) tixcraft_dict["elapsed_time"] = bot_elapsed_time + else: + tixcraft_dict["is_popup_checkout"] = False return tixcraft_dict @@ -7527,6 +7571,7 @@ def kktix_main(driver, url, config_dict, kktix_dict): if not is_url_contain_sign_in: if '/registrations/new' in url: + kktix_dict["start_time"] = time.time() is_need_refresh = False is_finish_checkbox_click = False is_need_refresh, is_finish_checkbox_click = kktix_reg_auto_reload(driver, url, config_dict, kktix_dict["kktix_register_status_last"]) @@ -7540,6 +7585,7 @@ def kktix_main(driver, url, config_dict, kktix_dict): # 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) + kktix_dict["done_time"] = time.time() else: is_event_page = False if '/events/' in url: @@ -7558,6 +7604,31 @@ def kktix_main(driver, url, config_dict, kktix_dict): 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 not kktix_dict["start_time"] is None: + if not kktix_dict["done_time"] is None: + bot_elapsed_time = kktix_dict["done_time"] - kktix_dict["start_time"] + if kktix_dict["elapsed_time"] != bot_elapsed_time: + print("bot elapsed time:", "{:.3f}".format(bot_elapsed_time)) + kktix_dict["elapsed_time"] = bot_elapsed_time + + + 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) + kktix_dict["is_popup_checkout"] = True + else: + kktix_dict["is_popup_checkout"] = False + return kktix_dict def fami_login(driver, account, password): @@ -11755,6 +11826,7 @@ def main(args): tixcraft_dict["start_time"]=None tixcraft_dict["done_time"]=None tixcraft_dict["elapsed_time"]=None + tixcraft_dict["is_popup_checkout"] = False # for kktix kktix_dict = {} @@ -11764,6 +11836,7 @@ def main(args): kktix_dict["start_time"]=None kktix_dict["done_time"]=None kktix_dict["elapsed_time"]=None + kktix_dict["is_popup_checkout"] = False fami_dict = {} fami_dict["fail_list"] = [] @@ -11927,6 +12000,11 @@ def cli(): help="overwrite ibonqware field", type=str) + parser.add_argument("--headless", + help="headless mode", + default='False', + type=str) + parser.add_argument("--browser", help="overwrite browser setting", default='', diff --git a/config_launcher.py b/config_launcher.py index bc62842..54e2d97 100644 --- a/config_launcher.py +++ b/config_launcher.py @@ -22,7 +22,7 @@ import sys import threading import webbrowser -CONST_APP_VERSION = "MaxBot (2023.11.10)" +CONST_APP_VERSION = "MaxBot (2023.11.19)" CONST_MAXBOT_LAUNCHER_FILE = "config_launcher.json" CONST_MAXBOT_CONFIG_FILE = "settings.json" diff --git a/pip-req.txt b/pip-req.txt index b63f9ba..92efde0 100644 --- a/pip-req.txt +++ b/pip-req.txt @@ -2,6 +2,7 @@ certifi chardet cryptography idna +Pillow==9.5.0 selenium>=4.0.0 undetected-chromedriver playsound==1.2.2 diff --git a/settings.json b/settings.json index faef2ac..566046c 100644 --- a/settings.json +++ b/settings.json @@ -1 +1 @@ -{"homepage": "https://tixcraft.com", "browser": "chrome", "language": "\u7e41\u9ad4\u4e2d\u6587", "ticket_number": 2, "auto_check_agree": true, "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true}, "tixcraft": {"date_auto_select": {"enable": true, "date_keyword": "", "mode": "from top to bottom"}, "pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "area_auto_select": {"enable": true, "mode": "from top to bottom", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "advanced": {"play_captcha_sound": {"enable": true, "filename": "ding-dong.wav"}, "tixcraft_sid": "", "ibonqware": "", "facebook_account": "", "kktix_account": "", "fami_account": "", "cityline_account": "", "urbtix_account": "", "hkticketing_account": "", "kham_account": "", "ticket_account": "", "udn_account": "", "ticketplus_account": "", "facebook_password": "", "kktix_password": "", "fami_password": "", "urbtix_password": "", "cityline_password": "", "hkticketing_password": "", "kham_password": "", "ticket_password": "", "udn_password": "", "ticketplus_password": "", "adblock_plus_enable": false, "disable_adjacent_seat": false, "hide_some_image": true, "block_facebook_network": true, "verbose": false, "auto_guess_options": true, "user_guess_string": "", "online_dictionary_url": "", "auto_reload_page_interval": 1.0, "auto_reload_random_delay": false, "proxy_server_port": ""}} +{"homepage": "https://tixcraft.com", "browser": "chrome", "language": "\u7e41\u9ad4\u4e2d\u6587", "ticket_number": 2, "auto_check_agree": true, "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true}, "tixcraft": {"date_auto_select": {"enable": true, "date_keyword": "", "mode": "from top to bottom"}, "pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "area_auto_select": {"enable": true, "mode": "from top to bottom", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "advanced": {"play_captcha_sound": {"enable": true, "filename": "ding-dong.wav"}, "tixcraft_sid": "", "ibonqware": "", "facebook_account": "", "kktix_account": "", "fami_account": "", "cityline_account": "", "urbtix_account": "", "hkticketing_account": "", "kham_account": "", "ticket_account": "", "udn_account": "", "ticketplus_account": "", "facebook_password": "", "kktix_password": "", "fami_password": "", "urbtix_password": "", "cityline_password": "", "hkticketing_password": "", "kham_password": "", "ticket_password": "", "udn_password": "", "ticketplus_password": "", "adblock_plus_enable": false, "disable_adjacent_seat": false, "hide_some_image": true, "block_facebook_network": true, "headless": false, "verbose": false, "auto_guess_options": true, "user_guess_string": "", "online_dictionary_url": "", "auto_reload_page_interval": 1.0, "auto_reload_random_delay": false, "proxy_server_port": ""}} \ No newline at end of file diff --git a/settings.py b/settings.py index 3f05993..b976bec 100644 --- a/settings.py +++ b/settings.py @@ -34,7 +34,7 @@ import ssl ssl._create_default_https_context = ssl._create_unverified_context -CONST_APP_VERSION = "MaxBot (2023.11.18)" +CONST_APP_VERSION = "MaxBot (2023.11.19)" CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt" @@ -154,6 +154,7 @@ def load_translate(): en_us["ocr_captcha_image_source"] = 'OCR image source' en_us["ocr_captcha_not_support_arm"] = 'ddddocr only supports Intel CPU' en_us["webdriver_type"] = 'WebDriver type' + en_us["headless"] = 'Headless mode' # Make the operation more talkative en_us["verbose"] = 'Verbose mode' en_us["running_status"] = 'Running Status' @@ -260,6 +261,7 @@ def load_translate(): zh_tw["ocr_captcha_image_source"] = 'OCR圖片取得方式' zh_tw["ocr_captcha_not_support_arm"] = 'ocr 只支援 Intel CPU' zh_tw["webdriver_type"] = 'WebDriver類別' + zh_tw["headless"] = '無圖形界面模式' zh_tw["verbose"] = '輸出詳細除錯訊息' zh_tw["running_status"] = '執行狀態' zh_tw["running_url"] = '執行網址' @@ -365,6 +367,7 @@ def load_translate(): zh_cn["ocr_captcha_image_source"] = 'OCR图像源' zh_cn["ocr_captcha_not_support_arm"] = 'ddddocr 仅支持 Intel CPU' zh_cn["webdriver_type"] = 'WebDriver类别' + zh_cn["headless"] = '无图形界面模式' zh_cn["verbose"] = '输出详细除错讯息' zh_cn["running_status"] = '执行状态' zh_cn["running_url"] = '执行网址' @@ -471,6 +474,7 @@ def load_translate(): ja_jp["ocr_captcha_image_source"] = 'OCR 画像ソース' ja_jp["ocr_captcha_not_support_arm"] = 'Intel CPU のみをサポートします' ja_jp["webdriver_type"] = 'WebDriverタイプ' + ja_jp["headless"] = 'ヘッドレスモード' ja_jp["verbose"] = '詳細モード' ja_jp["running_status"] = 'スターテス' ja_jp["running_url"] = '現在の URL' @@ -684,6 +688,7 @@ def get_default_config(): config_dict["advanced"]["hide_some_image"] = True config_dict["advanced"]["block_facebook_network"] = True + config_dict["advanced"]["headless"] = False config_dict["advanced"]["verbose"] = False config_dict["advanced"]["auto_guess_options"] = True config_dict["advanced"]["user_guess_string"] = "" @@ -818,6 +823,7 @@ def btn_save_act(language_code, slience_mode=False): global chk_state_hide_some_image global chk_state_block_facebook_network + global chk_state_headless global chk_state_verbose global chk_state_auto_guess_options global combo_ocr_captcha_image_source @@ -989,6 +995,7 @@ def btn_save_act(language_code, slience_mode=False): config_dict["ocr_captcha"]["force_submit"] = False config_dict["webdriver_type"] = combo_webdriver_type.get().strip() + config_dict["advanced"]["headless"] = bool(chk_state_headless.get()) config_dict["advanced"]["verbose"] = bool(chk_state_verbose.get()) config_dict["advanced"]["auto_guess_options"] = bool(chk_state_auto_guess_options.get()) @@ -1252,6 +1259,7 @@ def applyNewLanguage(): global lbl_ocr_captcha_image_source global lbl_ocr_captcha_not_support_arm global lbl_webdriver_type + global lbl_headless global lbl_verbose global lbl_auto_guess_options @@ -1276,6 +1284,7 @@ def applyNewLanguage(): global chk_hide_some_image global chk_block_facebook_network + global chk_headless global chk_verbose global lbl_online_dictionary_url global lbl_online_dictionary_preview @@ -1334,6 +1343,7 @@ def applyNewLanguage(): lbl_proxy_server_port.config(text=translate[language_code]["proxy_server_port"]) lbl_auto_reload_random_delay.config(text=translate[language_code]["auto_reload_random_delay"]) + lbl_headless.config(text=translate[language_code]["headless"]) lbl_verbose.config(text=translate[language_code]["verbose"]) lbl_online_dictionary_url.config(text=translate[language_code]["online_dictionary_url"]) @@ -1359,6 +1369,7 @@ def applyNewLanguage(): chk_hide_some_image.config(text=translate[language_code]["enable"]) chk_block_facebook_network.config(text=translate[language_code]["enable"]) + chk_headless.config(text=translate[language_code]["enable"]) chk_verbose.config(text=translate[language_code]["enable"]) chk_auto_guess_options.config(text=translate[language_code]["enable"]) chk_auto_reload_random_delay.config(text=translate[language_code]["enable"]) @@ -2067,6 +2078,20 @@ def AdvancedTab(root, config_dict, language_code, UI_PADDING_X): group_row_count+=1 + global lbl_headless + lbl_headless = Label(frame_group_header, text=translate[language_code]['headless']) + lbl_headless.grid(column=0, row=group_row_count, sticky = E) + + global chk_state_headless + chk_state_headless = BooleanVar() + chk_state_headless.set(config_dict['advanced']["headless"]) + + global chk_headless + chk_headless = Checkbutton(frame_group_header, text=translate[language_code]['enable'], variable=chk_state_headless) + chk_headless.grid(column=1, row=group_row_count, sticky = W) + + group_row_count+=1 + global lbl_verbose lbl_verbose = Label(frame_group_header, text=translate[language_code]['verbose']) lbl_verbose.grid(column=0, row=group_row_count, sticky = E) @@ -2682,7 +2707,7 @@ def main(): load_GUI(root, config_dict) GUI_SIZE_WIDTH = 570 - GUI_SIZE_HEIGHT = 595 + GUI_SIZE_HEIGHT = 615 GUI_SIZE_MACOS = str(GUI_SIZE_WIDTH) + 'x' + str(GUI_SIZE_HEIGHT) GUI_SIZE_WINDOWS=str(GUI_SIZE_WIDTH-60) + 'x' + str(GUI_SIZE_HEIGHT-70) diff --git a/text_server.py b/text_server.py index f4261dd..a335e52 100644 --- a/text_server.py +++ b/text_server.py @@ -27,7 +27,7 @@ import pyperclip import tornado from tornado.web import Application -CONST_APP_VERSION = "MaxBot (2023.11.10)" +CONST_APP_VERSION = "MaxBot (2023.11.19)" CONST_MAXBOT_QUESTION_FILE = "MAXBOT_QUESTION.txt"