2023-11-19, add headless mode, fix macOS import Pillow fail.

master
CHUN YU YAO 2023-11-25 09:11:43 +08:00
parent e3328d46f4
commit 88dbe5841c
6 changed files with 116 additions and 12 deletions

View File

@ -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='',

View File

@ -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"

View File

@ -2,6 +2,7 @@ certifi
chardet
cryptography
idna
Pillow==9.5.0
selenium>=4.0.0
undetected-chromedriver
playsound==1.2.2

View File

@ -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": ""}}

View File

@ -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)

View File

@ -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"