diff --git a/chrome_tixcraft.py b/chrome_tixcraft.py index feeecda..5253939 100644 --- a/chrome_tixcraft.py +++ b/chrome_tixcraft.py @@ -44,11 +44,12 @@ try: import ddddocr except Exception as exc: pass +from NonBrowser import NonBrowser import ssl ssl._create_default_https_context = ssl._create_unverified_context -CONST_APP_VERSION = u"MaxBot (2023.01.13) ver.3" +CONST_APP_VERSION = u"MaxBot (2023.01.14)" CONST_HOMEPAGE_DEFAULT = "https://tixcraft.com" @@ -296,14 +297,11 @@ def get_driver_by_config(config_dict, driver_type): area_auto_select_enable = None area_auto_select_mode = "" - debugMode = False - # read config. homepage = config_dict["homepage"] browser = config_dict["browser"] # output debug message in client side. - debugMode = config_dict["debug"] ticket_number = str(config_dict["ticket_number"]) pass_1_seat_remaining_enable = config_dict["pass_1_seat_remaining"] @@ -385,7 +383,6 @@ def get_driver_by_config(config_dict, driver_type): print("play_captcha_sound", config_dict["advanced"]["play_captcha_sound"]["enable"]) print("sound file path", config_dict["advanced"]["play_captcha_sound"]["filename"]) print("adblock_plus_enable", config_dict["advanced"]["adblock_plus_enable"]) - print("debug Mode", debugMode) # entry point if homepage is None: @@ -862,11 +859,15 @@ def get_answer_list_by_question(CONST_EXAMPLE_SYMBOL, captcha_text_div_text): # close some div on home url. def tixcraft_home(driver): + show_debug_message = True # debug. + show_debug_message = False # online + accept_all_cookies_btn = None try: accept_all_cookies_btn = driver.find_element(By.ID, 'onetrust-accept-btn-handler') except Exception as exc: - #print("find accept_all_cookies_btn fail") + if show_debug_message: + print("find accept_all_cookies_btn fail") pass if accept_all_cookies_btn is not None: @@ -878,6 +879,8 @@ def tixcraft_home(driver): 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: @@ -886,6 +889,9 @@ def tixcraft_home(driver): 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.") close_all_alert_btns = None try: @@ -1674,13 +1680,16 @@ def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False): return is_verifyCode_editing, is_form_sumbited -def tixcraft_reload_captcha(driver): +def tixcraft_reload_captcha(driver, domain_name): # manually keyin verify code. # start to input verify code. ret = False form_captcha = None try: - form_captcha = driver.find_element(By.ID, 'yw0') + image_id = 'yw0' + if 'indievox.com' in domain_name: + image_id = 'TicketForm_verifyCode-image' + form_captcha = driver.find_element(By.ID, image_id) if not form_captcha is None: form_captcha.click() ret = True @@ -1690,22 +1699,58 @@ def tixcraft_reload_captcha(driver): return ret #PS: credit to LinShihJhang's share -def tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer): +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") - from NonBrowser import NonBrowser + + CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER = "NonBrowser" + CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_CANVAS = "canvas" is_need_redo_ocr = False is_form_sumbited = False orc_answer = None if not ocr is None: - Non_Browser = NonBrowser() - Non_Browser.Set_cookies(driver.get_cookies()) - img_base64 = base64.b64decode(Non_Browser.Request_Captcha()) - try: - orc_answer = ocr.classification(img_base64) - except Exception as exc: - pass + 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() + + img_base64 = None + if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER: + img_base64 = base64.b64decode(Captcha_Browser.Request_Captcha()) + if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_CANVAS: + image_id = 'yw0' + if 'indievox.com' in domain_name: + image_id = 'TicketForm_verifyCode-image' + try: + form_verifyCode_base64 = driver.execute_async_script(""" + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + var img = document.getElementById('%s'); + canvas.height = img.naturalHeight; + canvas.width = img.naturalWidth; + context.drawImage(img, 0, 0); + callback = arguments[arguments.length - 1]; + callback(canvas.toDataURL()); + """ % (image_id)) + img_base64 = base64.b64decode(form_verifyCode_base64.split(',')[1]) + except Exception as exc: + if show_debug_message: + print("canvas exception:", str(exc)) + pass + if not img_base64 is None: + try: + orc_answer = ocr.classification(img_base64) + except Exception as exc: + pass + + ocr_done_time = time.time() + ocr_elapsed_time = ocr_done_time - ocr_start_time + print("ocr elapsed time:", "{:.3f}".format(ocr_elapsed_time)) else: print("ddddocr is None") @@ -1723,7 +1768,17 @@ def tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer): if previous_answer != orc_answer: previous_answer = orc_answer print("click captcha again") - tixcraft_reload_captcha(driver) + if True: + # selenium solution. + tixcraft_reload_captcha(driver, domain_name) + + if ocr_captcha_image_source == CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_CANVAS: + time.sleep(0.3) + else: + # Non_Browser solution. + new_captcha_url = Captcha_Browser.Request_Refresh_Captcha() #取得新的CAPTCHA + if new_captcha_url != "": + tixcraft_change_captcha(driver, new_captcha_url) #更改CAPTCHA圖 else: print("orc_answer is None") print("previous_answer:", previous_answer) @@ -1736,13 +1791,14 @@ def tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer): return is_need_redo_ocr, previous_answer, is_form_sumbited -def tixcraft_ticket_main(driver, config_dict, ocr): +def tixcraft_ticket_main(driver, config_dict, ocr, Captcha_Browser, domain_name): auto_check_agree = config_dict["auto_check_agree"] ocr_captcha_enable = config_dict["ocr_captcha"]["enable"] away_from_keyboard_enable = config_dict["ocr_captcha"]["force_submit"] if not ocr_captcha_enable: away_from_keyboard_enable = False + ocr_captcha_image_source = config_dict["ocr_captcha"]["image_source"] if auto_check_agree: tixcraft_ticket_agree(driver) @@ -1803,7 +1859,7 @@ def tixcraft_ticket_main(driver, config_dict, ocr): previous_answer = None is_verifyCode_editing = True 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) + 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. is_verifyCode_editing = False @@ -4955,12 +5011,16 @@ def list_all_cookies(driver): cookies_dict[cookie['name']] = cookie['value'] print(cookies_dict) -def tixcraft_main(driver, url, config_dict, is_verifyCode_editing, ocr): - if url == 'https://tixcraft.com/': - tixcraft_home(driver) +def tixcraft_main(driver, url, config_dict, is_verifyCode_editing, ocr, Captcha_Browser): + home_url_list = ['https://tixcraft.com/','https://www.tixcraft.com/','https://indievox.com/','https://www.indievox.com/'] + for each_url in home_url_list: + if each_url == url: + tixcraft_home(driver) - if url == 'https://indievox.com/': - tixcraft_home(driver) + domain_name = url.split('/')[2] + #PS: need set cookies once, if user change domain. + Captcha_Browser.Set_cookies(driver.get_cookies()) + Captcha_Browser.Set_Domain(domain_name) if "/activity/detail/" in url: is_redirected = tixcraft_redirect(driver, url) @@ -4984,7 +5044,8 @@ def tixcraft_main(driver, url, config_dict, is_verifyCode_editing, ocr): # main app, to select ticket number. if '/ticket/ticket/' in url: if not is_verifyCode_editing: - is_verifyCode_editing = tixcraft_ticket_main(driver, config_dict, ocr) + domain_name = url.split('/')[2] + is_verifyCode_editing = tixcraft_ticket_main(driver, config_dict, ocr, Captcha_Browser, domain_name) else: is_verifyCode_editing = False @@ -5198,18 +5259,13 @@ def main(): DISCONNECTED_MSG = 'Unable to evaluate script: no such window: target window already closed' - debugMode = False - if 'debug' in config_dict: - debugMode = config_dict["debug"] - if debugMode: - print("Start to looping, detect browser url...") - ocr = None try: if config_dict["ocr_captcha"]["enable"]: ocr = ddddocr.DdddOcr(show_ad=False, beta=True) except Exception as exc: pass + Captcha_Browser = NonBrowser() while True: time.sleep(0.1) @@ -5318,8 +5374,7 @@ def main(): continue # 說明:輸出目前網址,覺得吵的話,請註解掉這行。 - if debugMode: - print("url:", url) + #print("url:", url) if len(url) > 0 : if url != last_url: @@ -5338,7 +5393,7 @@ def main(): tixcraft_family = True if tixcraft_family: - is_verifyCode_editing = tixcraft_main(driver, url, config_dict, is_verifyCode_editing, ocr) + is_verifyCode_editing = tixcraft_main(driver, url, config_dict, is_verifyCode_editing, ocr, Captcha_Browser) # for kktix.cc and kktix.com if 'kktix.c' in url: diff --git a/settings.json b/settings.json index 9e0879b..7261cf7 100644 --- a/settings.json +++ b/settings.json @@ -1 +1 @@ -{"homepage": "https://tixcraft.com", "browser": "chrome", "language": "\u7e41\u9ad4\u4e2d\u6587", "ticket_number": 2, "pass_1_seat_remaining": true, "auto_check_agree": true, "ocr_captcha": {"enable": true, "force_submit": false}, "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "area_mode": "from top to bottom", "area_keyword_1": "", "area_keyword_1_and": "", "area_keyword_2": "", "area_keyword_2_and": "", "auto_guess_options": false, "user_guess_string": ""}, "tixcraft": {"date_auto_select": {"enable": true, "date_keyword": "", "mode": "from top to bottom"}, "area_auto_select": {"enable": true, "area_keyword_1": "", "area_keyword_2": "", "area_keyword_3": "", "area_keyword_4": "", "mode": "from top to bottom"}, "pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true, "presale_code": ""}, "advanced": {"play_captcha_sound": {"enable": true, "filename": "ding-dong.wav"}, "facebook_account": "", "kktix_account": "", "adblock_plus_enable": false}, "debug": false} \ No newline at end of file +{"homepage": "https://tixcraft.com", "browser": "chrome", "language": "\u7e41\u9ad4\u4e2d\u6587", "ticket_number": 2, "pass_1_seat_remaining": true, "auto_check_agree": true, "ocr_captcha": {"enable": true, "force_submit": false, "image_source": "canvas"}, "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "area_mode": "from top to bottom", "area_keyword_1": "", "area_keyword_1_and": "", "area_keyword_2": "", "area_keyword_2_and": "", "auto_guess_options": false, "user_guess_string": ""}, "tixcraft": {"date_auto_select": {"enable": true, "date_keyword": "", "mode": "from top to bottom"}, "area_auto_select": {"enable": true, "area_keyword_1": "", "area_keyword_2": "", "area_keyword_3": "", "area_keyword_4": "", "mode": "from top to bottom"}, "pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true, "presale_code": ""}, "advanced": {"play_captcha_sound": {"enable": true, "filename": "ding-dong.wav"}, "facebook_account": "", "kktix_account": "", "adblock_plus_enable": false}, "debug": false} \ No newline at end of file diff --git a/settings.py b/settings.py index e6006ad..645b1b4 100644 --- a/settings.py +++ b/settings.py @@ -19,7 +19,7 @@ import json import webbrowser import pyperclip -CONST_APP_VERSION = u"MaxBot (2023.01.13) ver.3" +CONST_APP_VERSION = u"MaxBot (2023.01.14)" CONST_FROM_TOP_TO_BOTTOM = u"from top to bottom" CONST_FROM_BOTTOM_TO_TOP = u"from bottom to top" @@ -78,6 +78,7 @@ def load_translate(): en_us["pass_1_seat_remaining"] = 'Pass 1 seat remaining' en_us["ocr_captcha"] = 'OCR captcha' en_us["ocr_captcha_force_submit"] = 'Away from keyboard' + en_us["ocr_captcha_image_source"] = 'OCR image source' en_us["preference"] = 'Preference' en_us["advanced"] = 'Advanced' @@ -135,6 +136,7 @@ def load_translate(): zh_tw["pass_1_seat_remaining"] = '避開「剩餘 1」的區域' zh_tw["ocr_captcha"] = '猜測驗證碼' zh_tw["ocr_captcha_force_submit"] = '掛機模式' + zh_tw["ocr_captcha_image_source"] = 'OCR圖片取得方式' zh_tw["preference"] = '偏好設定' zh_tw["advanced"] = '進階設定' @@ -192,6 +194,7 @@ def load_translate(): zh_cn["pass_1_seat_remaining"] = '避开“剩余 1”的区域' zh_cn["ocr_captcha"] = '猜测验证码' zh_cn["ocr_captcha_force_submit"] = '挂机模式' + zh_cn["ocr_captcha_image_source"] = 'OCR图像源' zh_cn["preference"] = '偏好设定' zh_cn["advanced"] = '進階設定' @@ -249,7 +252,8 @@ def load_translate(): ja_jp["area_keyword_4"] = 'エリアキーワード #4' ja_jp["pass_1_seat_remaining"] = '「1 席残り」エリアは避ける' ja_jp["ocr_captcha"] = 'キャプチャを推測する' - zh_cn["ocr_captcha_force_submit"] = 'キーボードから離れて' + ja_jp["ocr_captcha_force_submit"] = 'キーボードから離れて' + ja_jp["ocr_captcha_image_source"] = 'OCR 画像ソース' ja_jp["preference"] = '設定' ja_jp["advanced"] = '高度な設定' @@ -303,6 +307,7 @@ def get_default_config(): config_dict["ocr_captcha"] = {} config_dict["ocr_captcha"]["enable"] = True config_dict["ocr_captcha"]["force_submit"] = False + config_dict["ocr_captcha"]["image_source"] = "canvas" config_dict['kktix']={} config_dict["kktix"]["auto_press_next_step_button"] = True @@ -425,6 +430,7 @@ def btn_save_act(language_code, slience_mode=False): global chk_state_adblock_plus global chk_state_ocr_captcha global chk_state_ocr_captcha_force_submit + global combo_ocr_captcha_image_source is_all_data_correct = True @@ -500,6 +506,7 @@ def btn_save_act(language_code, slience_mode=False): config_dict["ocr_captcha"] = {} config_dict["ocr_captcha"]["enable"] = bool(chk_state_ocr_captcha.get()) config_dict["ocr_captcha"]["force_submit"] = bool(chk_state_ocr_captcha_force_submit.get()) + config_dict["ocr_captcha"]["image_source"] = combo_ocr_captcha_image_source.get().strip() # save config. if is_all_data_correct: @@ -661,6 +668,7 @@ def applyNewLanguage(): global lbl_presale_code global lbl_ocr_captcha global lbl_ocr_captcha_force_submit + global lbl_ocr_captcha_image_source # for checkbox global chk_pass_1_seat_remaining @@ -720,6 +728,7 @@ def applyNewLanguage(): lbl_presale_code.config(text=translate[language_code]["user_guess_string"]) lbl_ocr_captcha.config(text=translate[language_code]["ocr_captcha"]) lbl_ocr_captcha_force_submit.config(text=translate[language_code]["ocr_captcha_force_submit"]) + lbl_ocr_captcha_image_source.config(text=translate[language_code]["ocr_captcha_image_source"]) chk_pass_1_seat_remaining.config(text=translate[language_code]["enable"]) chk_auto_check_agree.config(text=translate[language_code]["enable"]) @@ -967,13 +976,10 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X): auto_reload_coming_soon_page_enable = True presale_code = "" - debugMode = False - # read config. homepage = config_dict["homepage"] browser = config_dict["browser"] language = config_dict["language"] - debugMode = config_dict["debug"] # default ticket number # 說明:自動選擇的票數 @@ -1059,8 +1065,6 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X): print("auto_reload_coming_soon_page", auto_reload_coming_soon_page_enable) print("presale_code", presale_code) - print("debug Mode", debugMode) - global lbl_homepage global lbl_ticket_number @@ -1559,7 +1563,6 @@ def AdvancedTab(root, config_dict, language_code, UI_PADDING_X): global combo_browser combo_browser = ttk.Combobox(frame_group_header, state="readonly") combo_browser['values']= ("chrome","firefox") - #combo_browser.current(0) combo_browser.set(config_dict['browser']) combo_browser.grid(column=1, row=group_row_count, sticky = W) @@ -1572,7 +1575,6 @@ def AdvancedTab(root, config_dict, language_code, UI_PADDING_X): global combo_language combo_language = ttk.Combobox(frame_group_header, state="readonly") combo_language['values']= ("English","繁體中文","簡体中文","日本語") - #combo_language.current(0) combo_language.set(config_dict['language']) combo_language.bind("<>", callbackLanguageOnChange) combo_language.grid(column=1, row=group_row_count, sticky = W) @@ -1599,6 +1601,21 @@ def AdvancedTab(root, config_dict, language_code, UI_PADDING_X): txt_kktix_account = Entry(frame_group_header, width=20, textvariable = txt_kktix_account_value) txt_kktix_account.grid(column=1, row=group_row_count, sticky = W) + group_row_count+=1 + + CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER = "NonBrowser" + CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_CANVAS = "canvas" + + global lbl_ocr_captcha_image_source + lbl_ocr_captcha_image_source = Label(frame_group_header, text=translate[language_code]['ocr_captcha_image_source']) + lbl_ocr_captcha_image_source.grid(column=0, row=group_row_count, sticky = E) + + global combo_ocr_captcha_image_source + combo_ocr_captcha_image_source = ttk.Combobox(frame_group_header, state="readonly") + combo_ocr_captcha_image_source['values']= (CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_BROWSER, CONST_OCR_CAPTCH_IMAGE_SOURCE_NON_CANVAS) + combo_ocr_captcha_image_source.set(config_dict["ocr_captcha"]["image_source"]) + combo_ocr_captcha_image_source.grid(column=1, row=group_row_count, sticky = W) + group_row_count +=1 global lbl_play_captcha_sound