2023-03-11, add new feature to pause MAXBOT. fix bugs for ibon.

master
CHUN YU YAO 2023-03-13 00:34:59 +08:00
parent 73ec0403e8
commit 316fc727d8
3 changed files with 467 additions and 133 deletions

View File

@ -53,7 +53,11 @@ import argparse
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
CONST_APP_VERSION = u"MaxBot (2023.03.08)"
CONST_APP_VERSION = u"MaxBot (2023.03.11)"
CONST_MAXBOT_CONFIG_FILE = "settings.json"
CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt"
CONST_MAXBOT_INT28_FILE = "MAXBOT_INT28_IDLE.txt"
CONST_HOMEPAGE_DEFAULT = "https://tixcraft.com"
URL_GOOGLE_OAUTH = 'https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?redirect_uri=https%3A%2F%2Fdevelopers.google.com%2Foauthplayground&prompt=consent&response_type=code&client_id=407408718192.apps.googleusercontent.com&scope=email&access_type=offline&flowName=GeneralOAuthFlow'
@ -84,6 +88,7 @@ CONST_WEBDRIVER_TYPE_SELENIUM = "selenium"
#CONST_WEBDRIVER_TYPE_STEALTH = "stealth"
CONST_WEBDRIVER_TYPE_UC = "undetected_chromedriver"
def t_or_f(arg):
ret = False
ua = str(arg).upper()
@ -120,9 +125,8 @@ def get_app_root():
return app_root
def get_config_dict(args):
config_json_filename = 'settings.json'
app_root = get_app_root()
config_filepath = os.path.join(app_root, config_json_filename)
config_filepath = os.path.join(app_root, CONST_MAXBOT_CONFIG_FILE)
# allow assign config by command line.
if not args.input is None:
@ -178,6 +182,16 @@ def get_config_dict(args):
config_dict["ocr_captcha"]["force_submit"] = True
return config_dict
def write_last_url_to_file(url):
with open(CONST_MAXBOT_LAST_URL_FILE, "w") as text_file:
text_file.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:
@ -1718,9 +1732,10 @@ def tixcraft_area_auto_select(driver, url, config_dict):
# only when keyword#2 filled to query.
if area_keyword_2_enable:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_2, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword2:", is_need_refresh)
if area_keyword_1 != area_keyword_2:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_2, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword2:", is_need_refresh)
if is_need_refresh:
if areas is None:
@ -1729,9 +1744,10 @@ def tixcraft_area_auto_select(driver, url, config_dict):
# only when keyword#3 filled to query.
if area_keyword_3_enable:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_3, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword3:", is_need_refresh)
if area_keyword_1 != area_keyword_3:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_3, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword3:", is_need_refresh)
if is_need_refresh:
if areas is None:
@ -1740,9 +1756,10 @@ def tixcraft_area_auto_select(driver, url, config_dict):
# only when keyword#4 filled to query.
if area_keyword_4_enable:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_4, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword4:", is_need_refresh)
if area_keyword_1 != area_keyword_4:
is_need_refresh, areas = get_tixcraft_target_area(el, area_keyword_4, area_auto_select_mode, pass_1_seat_remaining_enable)
if show_debug_message:
print("is_need_refresh for keyword4:", is_need_refresh)
area_target = None
if areas is not None:
@ -5133,10 +5150,15 @@ def ibon_activity_info(driver, config_dict):
return is_date_assign_by_bot
def ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_1, area_keyword_1_and):
def ibon_area_auto_select(driver, config_dict, area_keyword_1, area_keyword_1_and):
show_debug_message = True # debug.
show_debug_message = False # online
area_auto_select_mode = config_dict["tixcraft"]["area_auto_select"]["mode"]
if config_dict["advanced"]["verbose"]:
show_debug_message = True
is_price_assign_by_bot = False
is_need_refresh = False
@ -5179,9 +5201,22 @@ def ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_1, area_ke
if row_is_enabled:
try:
button_class_string = str(row.get_attribute('class'))
if len(button_class_string) > 1:
if 'disabled' in button_class_string:
row_is_enabled=False
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:
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
@ -5284,7 +5319,7 @@ def ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_1, area_ke
else:
is_need_refresh = True
if show_debug_message:
print("matched_blocks is empty.")
print("matched_blocks is empty, is_need_refresh")
if target_area is not None:
try:
@ -5309,13 +5344,15 @@ 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
auto_fill_ticket_number = True
if auto_fill_ticket_number:
# click price row.
area_auto_select_mode = config_dict["tixcraft"]["area_auto_select"]["mode"]
area_keyword_1 = config_dict["tixcraft"]["area_auto_select"]["area_keyword_1"].strip()
area_keyword_2 = config_dict["tixcraft"]["area_auto_select"]["area_keyword_2"].strip()
area_keyword_3 = config_dict["tixcraft"]["area_auto_select"]["area_keyword_3"].strip()
@ -5337,25 +5374,28 @@ def ibon_performance(driver, config_dict):
is_need_refresh = False
if not is_price_assign_by_bot:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_1, area_keyword_1_and)
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword_1, area_keyword_1_and)
if is_need_refresh:
if area_keyword_2_enable:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_2, area_keyword_2_and)
if show_debug_message:
print("is_need_refresh for keyword2:", is_need_refresh)
if area_keyword_1 != area_keyword_2:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword_2, area_keyword_2_and)
if show_debug_message:
print("is_need_refresh for keyword2:", is_need_refresh)
if is_need_refresh:
if area_keyword_3_enable:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_3, area_keyword_3_and)
if show_debug_message:
print("is_need_refresh for keyword3:", is_need_refresh)
if area_keyword_1 != area_keyword_3:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword_3, area_keyword_3_and)
if show_debug_message:
print("is_need_refresh for keyword3:", is_need_refresh)
if is_need_refresh:
if area_keyword_4_enable:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, area_auto_select_mode, area_keyword_4, area_keyword_4_and)
if show_debug_message:
print("is_need_refresh for keyword4:", is_need_refresh)
if area_keyword_1 != area_keyword_4:
is_need_refresh, is_price_assign_by_bot = ibon_area_auto_select(driver, config_dict, area_keyword_4, area_keyword_4_and)
if show_debug_message:
print("is_need_refresh for keyword4:", is_need_refresh)
if is_need_refresh:
try:
@ -6179,40 +6219,163 @@ def cityline_main(driver, url, config_dict):
if len(url.split('/'))>=5:
cityline_shows_goto_cta(driver)
def guess_ibon_question(driver):
show_debug_message = True # debug.
show_debug_message = False # online
inferred_answer_string = None
answer_list = []
form_select = None
try:
form_select = driver.find_element(By.CSS_SELECTOR, 'div.editor-box > div > div.form-group > label')
except Exception as exc:
print("find verify textbox fail")
pass
question_text = None
if form_select is not None:
try:
question_text = form_select.text
except Exception as exc:
print("get text fail")
is_options_in_question = False
if inferred_answer_string is None:
if not question_text is None:
inferred_answer_string, answer_list = get_answer_list_from_question_string(None, question_text)
return inferred_answer_string, answer_list
def ibon_verification_question(driver, answer_index, config_dict):
show_debug_message = True # debug.
show_debug_message = False # online
user_guess_string = config_dict["kktix"]["user_guess_string"]
if config_dict["advanced"]["verbose"]:
show_debug_message = True
# part 1: check div.
question_div = None
try:
question_div = driver.find_element(By.CSS_SELECTOR, '')
except Exception as exc:
pass
#print("find input fail:", exc)
presale_code = config_dict["tixcraft"]["presale_code"]
presale_code_delimiter = config_dict["tixcraft"]["presale_code_delimiter"]
#captcha_text_div_text
captcha_text_div_text = None
inferred_answer_string = None
answer_list = []
captcha_password_inputbox = None
try:
captcha_password_inputbox = driver.find_element(By.CSS_SELECTOR, '')
except Exception as exc:
pass
is_retry_user_single_answer = False
if not captcha_password_inputbox is None:
inferred_answer_string = None
if len(user_guess_string) > 0:
inferred_answer_string = user_guess_string
if len(presale_code) > 0:
if len(presale_code_delimiter) > 0:
if presale_code_delimiter in presale_code:
answer_list = presale_code.split(presale_code_delimiter)
if len(answer_list) > 0:
if answer_index < len(answer_list)-1:
inferred_answer_string = answer_list[answer_index+1]
else:
if not captcha_text_div_text is None:
inferred_answer_string, answer_list = get_answer_list_from_question_string(None, captcha_text_div_text)
is_retry_user_single_answer = True
if answer_index < 2:
inferred_answer_string = presale_code
if inferred_answer_string is None:
inferred_answer_string, answer_list = guess_ibon_question(driver)
if inferred_answer_string is None:
if not answer_list is None:
if len(answer_list) > 0:
if answer_index < len(answer_list)-1:
inferred_answer_string = answer_list[answer_index+1]
if show_debug_message:
print("answer_index:", answer_index)
print("inferred_answer_string:", inferred_answer_string)
print("answer_index:", answer_index)
print("is_retry_user_single_answer:", is_retry_user_single_answer)
form_input = None
try:
form_input = driver.find_element(By.CSS_SELECTOR, 'div.editor-box > div > div.form-group > input')
except Exception as exc:
print("find verify code fail")
pass
inputed_value = None
if form_input is not None:
try:
inputed_value = form_input.get_attribute('value')
except Exception as exc:
print("find verify code fail")
pass
if inputed_value is None:
inputed_value = ""
if not inferred_answer_string is None:
is_password_sent = False
if len(inputed_value)==0:
try:
# PS: sometime may send key twice...
form_input.clear()
form_input.send_keys(inferred_answer_string)
is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'div.editor-box > div > div.form-group > a.btn')
is_password_sent = True
# guess answer mode.
answer_index += 1
if show_debug_message:
print("sent password by bot:", inferred_answer_string)
except Exception as exc:
pass
else:
if inputed_value == inferred_answer_string:
if show_debug_message:
print("sent password by previous time.")
is_password_sent = True
try:
form_input.send_keys(Keys.ENTER)
except Exception as exc:
pass
if is_retry_user_single_answer:
# increase counter for waiting for stop retry.
answer_index += 1
else:
# guess answer mode.
if answer_index > -1:
# here not is first option.
inferred_answer_previous = None
if answer_index < len(answer_list)-1:
inferred_answer_previous = answer_list[answer_index]
if inputed_value == inferred_answer_previous:
try:
form_input.clear()
form_input.send_keys(inferred_answer_string)
is_button_clicked = force_press_button(driver, By.CSS_SELECTOR,'div.editor-box > div > div.form-group > a.btn')
is_password_sent = True
if show_debug_message:
print("sent password by bot:", inferred_answer_string, "at index:", answer_index+2)
answer_index += 1
except Exception as exc:
pass
if is_password_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:
if len(inputed_value)==0:
try:
form_input.click()
except Exception as exc:
pass
return answer_index
def ibon_ticket_agree(driver):
# check agree
form_checkbox = None
@ -6278,70 +6441,77 @@ def ibon_main(driver, url, config_dict, ibon_dict):
if is_event_page:
ibon_auto_signup(driver)
#https://ticket.ibon.com.tw/ActivityInfo/Details/0000?pattern=entertainment
if '/ActivityInfo/Details/' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
is_match_target_feature = False
if is_event_page:
date_auto_select_enable = config_dict["tixcraft"]["date_auto_select"]["enable"]
if date_auto_select_enable:
ibon_activity_info(driver, config_dict)
if not is_match_target_feature:
#https://ticket.ibon.com.tw/ActivityInfo/Details/0000?pattern=entertainment
if '/ActivityInfo/Details/' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
# validation question url:
# https://orders.ibon.com.tw/application/UTK02/UTK0201_0.aspx?rn=1180872370&PERFORMANCE_ID=B04M7XZT&PRODUCT_ID=B04KS88E&SHOW_PLACE_MAP=True
if '/application/UTK02/' in url and '.aspx?rn=' in url:
#PS: not sure, use 'kktix' block or 'tixcraft' block for this feature.
#auto_guess_options = config_dict["kktix"]["auto_guess_options"]
auto_guess_options = True
if auto_guess_options:
#ibon_dict["answer_index"] = ibon_verification_question(driver, ibon_dict["answer_index"], config_dict)
pass
else:
ibon_dict["answer_index"] = -1
if is_event_page:
date_auto_select_enable = config_dict["tixcraft"]["date_auto_select"]["enable"]
if date_auto_select_enable:
is_match_target_feature = True
ibon_activity_info(driver, config_dict)
# https://orders.ibon.com.tw/application/UTK02/UTK0201_000.aspx?PERFORMANCE_ID=0000
if '/application/UTK02/' in url and '.aspx?PERFORMANCE_ID=' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
if not is_match_target_feature:
# validation question url:
# https://orders.ibon.com.tw/application/UTK02/UTK0201_0.aspx?rn=1180872370&PERFORMANCE_ID=B04M7XZT&PRODUCT_ID=B04KS88E&SHOW_PLACE_MAP=True
if '/application/UTK02/' in url and '.aspx?rn=' in url:
is_match_target_feature = True
ibon_dict["answer_index"] = ibon_verification_question(driver, ibon_dict["answer_index"], config_dict)
else:
ibon_dict["answer_index"] = -1
if is_event_page:
area_auto_select_enable = config_dict["tixcraft"]["area_auto_select"]["enable"]
if area_auto_select_enable:
if 'PERFORMANCE_PRICE_AREA_ID=' in url:
# step 2: assign ticket number.
ticket_number = str(config_dict["ticket_number"])
is_ticket_number_assigned = ibon_ticket_number_auto_select(driver, ticket_number)
if is_ticket_number_assigned:
click_ret = ibon_purchase_button_press(driver)
else:
is_sold_out = ibon_check_sold_out(driver)
if is_sold_out:
#is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'a.btn.btn-primary')
driver.back()
driver.refresh()
else:
# step 1: select area.
ibon_performance(driver, config_dict)
#https://orders.ibon.com.tw/application/UTK02/UTK0206_.aspx
if 'orders.ibon.com.tw/application/UTK02/UTK020' in url and '.aspx' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
if not is_match_target_feature:
# https://orders.ibon.com.tw/application/UTK02/UTK0201_000.aspx?PERFORMANCE_ID=0000
if '/application/UTK02/' in url and '.aspx?PERFORMANCE_ID=' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
if is_event_page:
auto_check_agree = config_dict["auto_check_agree"]
if auto_check_agree:
is_finish_checkbox_click = False
for i in range(3):
is_finish_checkbox_click = ibon_ticket_agree(driver)
if is_event_page:
area_auto_select_enable = config_dict["tixcraft"]["area_auto_select"]["enable"]
if area_auto_select_enable:
if 'PERFORMANCE_PRICE_AREA_ID=' in url:
# step 2: assign ticket number.
is_match_target_feature = True
ticket_number = str(config_dict["ticket_number"])
is_ticket_number_assigned = ibon_ticket_number_auto_select(driver, ticket_number)
if is_ticket_number_assigned:
click_ret = ibon_purchase_button_press(driver)
else:
is_sold_out = ibon_check_sold_out(driver)
if is_sold_out:
#is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'a.btn.btn-primary')
driver.back()
driver.refresh()
if 'PRODUCT_ID=' in url:
# step 1: select area.
is_match_target_feature = True
ibon_performance(driver, config_dict)
if not is_match_target_feature:
#https://orders.ibon.com.tw/application/UTK02/UTK0206_.aspx
if 'orders.ibon.com.tw/application/UTK02/UTK020' in url and '.aspx' in url:
is_event_page = False
if len(url.split('/'))==6:
is_event_page = True
if is_event_page:
auto_check_agree = config_dict["auto_check_agree"]
if auto_check_agree:
is_finish_checkbox_click = False
for i in range(3):
is_finish_checkbox_click = ibon_ticket_agree(driver)
if is_finish_checkbox_click:
break
if is_finish_checkbox_click:
break
if is_finish_checkbox_click:
is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'a.btn.btn-pink.continue')
is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'a.btn.btn-pink.continue')
return ibon_dict
@ -8799,8 +8969,15 @@ def main(args):
if len(url) > 0 :
if url != last_url:
print(url)
write_last_url_to_file(url)
if os.path.exists(CONST_MAXBOT_INT28_FILE):
print("MAXBOT Paused.")
last_url = url
if os.path.exists(CONST_MAXBOT_INT28_FILE):
time.sleep(0.2)
continue
tixcraft_family = False
if 'tixcraft.com' in url:
tixcraft_family = True
@ -8916,7 +9093,8 @@ if __name__ == "__main__":
#captcha_text_div_text = "以下哪位不是LOVELYZ成員? (請以半形輸入選項內的英文及數字,大小寫須符合),範例:E5e。 (A1a)智愛 (B2b)美珠 (C3c)JON (D4d)叡仁"
#captcha_text_div_text = "題請問此次 RAVI的SOLO專輯名稱為?(請以半形輸入法作答,大小寫需要一模一樣,範例:Tt Aa [ BOOK] 、 Bb [OOK BOOK.R] 、 Cc [R.OOK BOOK] 、 Dd [OOK R. BOOK]"
#captcha_text_div_text = "請問下列哪個選項皆為河成雲的創作歌曲? Aa) Dont Forget、Candle Bb) Dont Forget、Forever+1 Cc) Dont Forget、Flowerbomb Dd) Dont Forget、One Love 請以半形輸入,大小寫含括號需一模一樣 【範例:答案為B需填入Bb)】"
captcha_text_div_text = "魏如萱得過什麼獎?(1) 金馬獎 最佳女主角(2) 金鐘獎 戲劇節目女主角(3) 金曲獎 最佳華語女歌手(4) 走鐘獎 好好聽音樂獎 (請輸入半形數字)"
#captcha_text_div_text = "魏如萱得過什麼獎?(1) 金馬獎 最佳女主角(2) 金鐘獎 戲劇節目女主角(3) 金曲獎 最佳華語女歌手(4) 走鐘獎 好好聽音樂獎 (請輸入半形數字)"
captcha_text_div_text = "Love in the Air 是由哪兩本小說改篇而成呢?(A)Love Strom & Love Sky (B)Love Rain & Love Cloud (C)Love Wind & Love Sun (D)Love Dry & Love Cold (請輸入選項大寫英文單字 範例E)"
inferred_answer_string, answer_list = get_answer_list_from_question_string(None, captcha_text_div_text)
print("inferred_answer_string:", inferred_answer_string)
print("answer_list:", answer_list)

View File

@ -20,9 +20,10 @@ import json
import webbrowser
import base64
CONST_APP_VERSION = u"MaxBot (2023.03.08)"
CONST_APP_VERSION = u"MaxBot (2023.03.11)"
CONST_LAUNCHER_CONFIG_FILENAME = "config_launcher.json"
CONST_MAXBOT_LAUNCHER_FILE = "config_launcher.json"
CONST_MAXBOT_CONFIG_FILE = "settings.json"
translate={}
@ -146,7 +147,7 @@ def get_app_root():
def get_default_config():
config_dict={}
config_dict["list"] = ["settings.json"]
config_dict["list"] = [CONST_MAXBOT_CONFIG_FILE]
config_dict["advanced"] = {}
config_dict["advanced"]["language"] = "English"
@ -158,7 +159,7 @@ def load_json():
app_root = get_app_root()
# overwrite config path.
config_filepath = os.path.join(app_root, CONST_LAUNCHER_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_LAUNCHER_FILE)
config_dict = None
if os.path.isfile(config_filepath):
@ -170,7 +171,7 @@ def load_json():
def btn_restore_defaults_clicked(language_code):
app_root = get_app_root()
config_filepath = os.path.join(app_root, CONST_LAUNCHER_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_LAUNCHER_FILE)
config_dict = get_default_config()
import json
@ -186,7 +187,7 @@ def btn_save_clicked(language_code):
def btn_save_act(language_code, slience_mode=True):
app_root = get_app_root()
config_filepath = os.path.join(app_root, CONST_LAUNCHER_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_LAUNCHER_FILE)
config_dict = get_default_config()

View File

@ -20,10 +20,14 @@ import json
import webbrowser
import pyperclip
import base64
import time
import threading
CONST_APP_VERSION = u"MaxBot (2023.03.08)"
CONST_APP_VERSION = u"MaxBot (2023.03.11)"
CONST_SETTINGS_CONFIG_FILENAME = "settings.json"
CONST_MAXBOT_CONFIG_FILE = "settings.json"
CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt"
CONST_MAXBOT_INT28_FILE = "MAXBOT_INT28_IDLE.txt"
CONST_FROM_TOP_TO_BOTTOM = u"from top to bottom"
CONST_FROM_BOTTOM_TO_TOP = u"from bottom to top"
@ -83,7 +87,7 @@ def load_translate():
en_us["date_keyword"] = 'Date Keyword'
en_us["pass_date_is_sold_out"] = 'Pass date is sold out'
en_us["auto_reload_coming_soon_page"] = 'Reload coming soon page'
en_us["area_auto_select"] = 'Area Auto Select'
#en_us["area_select_order"] = 'Area select order'
en_us["area_keyword_1"] = 'Area Keyword #1'
@ -98,10 +102,20 @@ def load_translate():
en_us["headless"] = 'Headless mode'
# Make the operation more talkative
en_us["verbose"] = 'Verbose mode'
en_us["running_status"] = 'Running Status'
en_us["running_url"] = 'Running URL'
en_us["status_idle"] = 'Idle'
en_us["status_paused"] = 'Paused'
en_us["status_enabled"] = 'Enabled'
en_us["status_running"] = 'Running'
en_us["idle"] = 'Idle'
en_us["resume"] = 'Resume'
en_us["preference"] = 'Preference'
en_us["advanced"] = 'Advanced'
en_us["autofill"] = 'Autofill'
en_us["runtime"] = 'Runtime'
en_us["about"] = 'About'
en_us["run"] = 'Run'
@ -128,7 +142,7 @@ def load_translate():
en_us["hkticketing_password"] = 'HKTICKETING password'
en_us["kham_password"] = 'KHAM password'
en_us["save_password_alert"] = 'Saving passwords to config file may expose your passwords.'
en_us["play_captcha_sound"] = 'Play sound when captcha'
en_us["captcha_sound_filename"] = 'captcha sound filename'
en_us["adblock_plus_enable"] = 'Adblock Plus Extension'
@ -178,10 +192,20 @@ def load_translate():
zh_tw["webdriver_type"] = 'WebDriver類別'
zh_tw["headless"] = '無圖形界面模式'
zh_tw["verbose"] = '輸出詳細除錯訊息'
zh_tw["running_status"] = '執行狀態'
zh_tw["running_url"] = '執行網址'
zh_tw["status_idle"] = '閒置中'
zh_tw["status_paused"] = '已暫停'
zh_tw["status_enabled"] = '已啟用'
zh_tw["status_running"] = '執行中'
zh_tw["idle"] = '暫停搶票'
zh_tw["resume"] = '接續搶票'
zh_tw["preference"] = '偏好設定'
zh_tw["advanced"] = '進階設定'
zh_tw["autofill"] = '自動填表單'
zh_tw["runtime"] = '執行階段'
zh_tw["about"] = '關於'
zh_tw["run"] = '搶票'
@ -208,7 +232,7 @@ def load_translate():
zh_tw["hkticketing_password"] = 'HKTICKETING 密碼'
zh_tw["kham_password"] = '寬宏 密碼'
zh_tw["save_password_alert"] = '將密碼保存到設定檔中可能會讓您的密碼被盜。'
zh_tw["play_captcha_sound"] = '輸入驗證碼時播放音效'
zh_tw["captcha_sound_filename"] = '驗證碼用音效檔'
zh_tw["adblock_plus_enable"] = 'Adblock 瀏覽器擴充功能'
@ -258,10 +282,20 @@ def load_translate():
zh_cn["webdriver_type"] = 'WebDriver类别'
zh_cn["headless"] = '无图形界面模式'
zh_cn["verbose"] = '输出详细除错讯息'
zh_cn["running_status"] = '執行狀態'
zh_cn["running_url"] = '執行網址'
zh_cn["status_idle"] = '閒置中'
zh_cn["status_paused"] = '已暫停'
zh_cn["status_enabled"] = '已启用'
zh_cn["status_running"] = '執行中'
zh_cn["idle"] = '暂停抢票'
zh_cn["resume"] = '接续抢票'
zh_cn["preference"] = '偏好设定'
zh_cn["advanced"] = '進階設定'
zh_cn["autofill"] = '自动填表单'
zh_cn["runtime"] = '运行'
zh_cn["about"] = '关于'
zh_cn["copy"] = '复制'
@ -289,7 +323,7 @@ def load_translate():
zh_cn["hkticketing_password"] = 'HKTICKETING 密码'
zh_cn["kham_password"] = '宽宏 密码'
zh_cn["save_password_alert"] = '將密碼保存到文件中可能會暴露您的密碼。'
zh_cn["play_captcha_sound"] = '输入验证码时播放音效'
zh_cn["captcha_sound_filename"] = '验证码用音效档'
zh_cn["adblock_plus_enable"] = 'Adblock 浏览器扩充功能'
@ -339,10 +373,20 @@ def load_translate():
ja_jp["webdriver_type"] = 'WebDriverタイプ'
ja_jp["headless"] = 'ヘッドレスモード'
ja_jp["verbose"] = '詳細モード'
ja_jp["running_status"] = 'スターテス'
ja_jp["running_url"] = '現在の URL'
ja_jp["status_idle"] = '閒置中'
ja_jp["status_paused"] = '一時停止'
ja_jp["status_enabled"] = '有効'
ja_jp["status_running"] = 'ランニング'
ja_jp["idle"] = 'アイドル'
ja_jp["resume"] = '再開する'
ja_jp["preference"] = '設定'
ja_jp["advanced"] = '高度な設定'
ja_jp["autofill"] = 'オートフィル'
ja_jp["runtime"] = 'ランタイム'
ja_jp["about"] = '情報'
ja_jp["run"] = 'チケットを取る'
@ -493,11 +537,17 @@ def get_default_config():
return config_dict
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 load_json():
app_root = get_app_root()
# overwrite config path.
config_filepath = os.path.join(app_root, CONST_SETTINGS_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_CONFIG_FILE)
config_dict = None
if os.path.isfile(config_filepath):
@ -509,7 +559,7 @@ def load_json():
def btn_restore_defaults_clicked(language_code):
app_root = get_app_root()
config_filepath = os.path.join(app_root, CONST_SETTINGS_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_CONFIG_FILE)
config_dict = get_default_config()
import json
@ -520,6 +570,27 @@ def btn_restore_defaults_clicked(language_code):
global root
load_GUI(root, config_dict)
def btn_idle_clicked(language_code):
app_root = get_app_root()
idle_filepath = os.path.join(app_root, CONST_MAXBOT_INT28_FILE)
with open(CONST_MAXBOT_INT28_FILE, "w") as text_file:
text_file.write("")
update_maxbot_runtime_status(language_code)
def btn_resume_clicked(language_code):
app_root = get_app_root()
idle_filepath = os.path.join(app_root, CONST_MAXBOT_INT28_FILE)
for i in range(10):
if os.path.exists(idle_filepath):
try:
os.remove(idle_filepath)
break
except Exception as exc:
pass
else:
break
update_maxbot_runtime_status(language_code)
def btn_launcher_clicked(language_code):
import subprocess
@ -574,7 +645,7 @@ def btn_save_clicked(language_code):
def btn_save_act(language_code, slience_mode=False):
app_root = get_app_root()
config_filepath = os.path.join(app_root, CONST_SETTINGS_CONFIG_FILENAME)
config_filepath = os.path.join(app_root, CONST_MAXBOT_CONFIG_FILE)
config_dict = get_default_config()
@ -744,7 +815,7 @@ def btn_save_act(language_code, slience_mode=False):
config_dict["advanced"]["adblock_plus_enable"] = bool(chk_state_adblock_plus.get())
config_dict["advanced"]["open_google_oauth_url"] = bool(chk_state_google_oauth.get())
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())
@ -759,7 +830,7 @@ def btn_save_act(language_code, slience_mode=False):
if not slience_mode:
#messagebox.showinfo(translate[language_code]["save"], translate[language_code]["done"])
file_to_save = asksaveasfilename(initialfile = CONST_SETTINGS_CONFIG_FILENAME, defaultextension=".json",filetypes=[("json Documents","*.json"),("All Files","*.*")])
file_to_save = asksaveasfilename(initialfile = CONST_MAXBOT_CONFIG_FILE, defaultextension=".json",filetypes=[("json Documents","*.json"),("All Files","*.*")])
if not file_to_save is None:
print("save as to:", file_to_save)
with open(str(file_to_save), 'w') as outfile:
@ -833,7 +904,6 @@ def btn_preview_sound_clicked():
play_mp3_async(new_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):
@ -981,7 +1051,7 @@ def applyNewLanguage():
lbl_kktix_area_keyword_2_and_text.config(text=translate[language_code]["and"])
lbl_auto_guess_options.config(text=translate[language_code]["auto_guess_options"])
lbl_user_guess_string.config(text=translate[language_code]["user_guess_string"])
lbl_date_auto_select.config(text=translate[language_code]["date_auto_select"])
lbl_date_auto_select_mode.config(text=translate[language_code]["date_select_order"])
lbl_date_keyword.config(text=translate[language_code]["date_keyword"])
@ -1028,7 +1098,8 @@ def applyNewLanguage():
tabControl.tab(0, text=translate[language_code]["preference"])
tabControl.tab(1, text=translate[language_code]["advanced"])
tabControl.tab(2, text=translate[language_code]["autofill"])
tabControl.tab(3, text=translate[language_code]["about"])
tabControl.tab(3, text=translate[language_code]["runtime"])
tabControl.tab(4, text=translate[language_code]["about"])
global lbl_tixcraft_sid
global lbl_ibon_ibonqware
@ -1385,11 +1456,11 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X):
,"https://ticketplus.com.tw/ (遠大)"
,"http://www.urbtix.hk/ (城市)"
,"https://www.cityline.com/ (買飛)"
,"https://premier.hkticketing.com/ (快達票)"
,"https://ticketing.galaxymacau.com/ (澳門銀河)"
]
# 目前機器人已失效, 因為官方的 reCaptcha 可以檢測出機器人。
'''
,"https://premier.hkticketing.com/ (快達票)"
'''
combo_homepage.set(homepage)
combo_homepage.bind("<<ComboboxSelected>>", callbackHomepageOnChange)
@ -2222,6 +2293,85 @@ def AutofillTab(root, config_dict, language_code, UI_PADDING_X):
frame_group_header.grid(column=0, row=row_count, padx=UI_PADDING_X)
def settings_timer(language_code):
while True:
update_maxbot_runtime_status(language_code)
time.sleep(0.5)
def update_maxbot_runtime_status(language_code):
is_paused = False
if os.path.exists(CONST_MAXBOT_INT28_FILE):
is_paused = True
try:
global lbl_maxbot_status_data
maxbot_status = translate[language_code]['status_enabled']
if is_paused:
maxbot_status = translate[language_code]['status_paused']
lbl_maxbot_status_data.config(text=maxbot_status)
global btn_idle
global btn_resume
if not is_paused:
btn_idle.grid(column=1, row=0)
btn_resume.grid_forget()
else:
btn_resume.grid(column=2, row=0)
btn_idle.grid_forget()
global lbl_maxbot_last_url_data
last_url = read_last_url_from_file()
lbl_maxbot_last_url_data.config(text=last_url)
except Exception as exc:
pass
def RuntimeTab(root, config_dict, language_code, UI_PADDING_X):
row_count = 0
frame_group_header = Frame(root)
group_row_count = 0
maxbot_status = ""
global lbl_maxbot_status
lbl_maxbot_status = Label(frame_group_header, text=translate[language_code]['running_status'])
lbl_maxbot_status.grid(column=0, row=group_row_count, sticky = E)
frame_maxbot_interrupt = Frame(frame_group_header)
global lbl_maxbot_status_data
lbl_maxbot_status_data = Label(frame_maxbot_interrupt, text=maxbot_status)
lbl_maxbot_status_data.grid(column=0, row=group_row_count, sticky = W)
global btn_idle
global btn_resume
btn_idle = ttk.Button(frame_maxbot_interrupt, text=translate[language_code]['idle'], command= lambda: btn_idle_clicked(language_code) )
btn_idle.grid(column=1, row=0)
btn_resume = ttk.Button(frame_maxbot_interrupt, text=translate[language_code]['resume'], command= lambda: btn_resume_clicked(language_code))
btn_resume.grid(column=2, row=0)
frame_maxbot_interrupt.grid(column=1, row=group_row_count, sticky = W)
group_row_count +=1
global lbl_maxbot_last_url
lbl_maxbot_last_url = Label(frame_group_header, text=translate[language_code]['running_url'])
lbl_maxbot_last_url.grid(column=0, row=group_row_count, sticky = E)
last_url = ""
global lbl_maxbot_last_url_data
lbl_maxbot_last_url_data = Label(frame_group_header, text=last_url)
lbl_maxbot_last_url_data.grid(column=1, row=group_row_count, sticky = W)
frame_group_header.grid(column=0, row=row_count, padx=UI_PADDING_X)
update_maxbot_runtime_status(language_code)
def AboutTab(root, language_code):
row_count = 0
@ -2336,8 +2486,6 @@ def get_action_bar(root, language_code):
btn_restore_defaults = ttk.Button(frame_action, text=translate[language_code]['restore_defaults'], command= lambda: btn_restore_defaults_clicked(language_code))
btn_restore_defaults.grid(column=3, row=0)
return frame_action
def clearFrame(frame):
@ -2367,7 +2515,11 @@ def load_GUI(root, config_dict):
tabControl.add(tab3, text=translate[language_code]['autofill'])
tab4 = Frame(tabControl)
tabControl.add(tab4, text=translate[language_code]['about'])
tabControl.add(tab4, text=translate[language_code]['runtime'])
tab5 = Frame(tabControl)
tabControl.add(tab5, text=translate[language_code]['about'])
tabControl.grid(column=0, row=row_count)
tabControl.select(tab1)
@ -2380,7 +2532,9 @@ def load_GUI(root, config_dict):
PreferenctTab(tab1, config_dict, language_code, UI_PADDING_X)
AdvancedTab(tab2, config_dict, language_code, UI_PADDING_X)
AutofillTab(tab3, config_dict, language_code, UI_PADDING_X)
AboutTab(tab4, language_code)
RuntimeTab(tab4, config_dict, language_code, UI_PADDING_X)
AboutTab(tab5, language_code)
threading.Thread(target=settings_timer, args=(language_code,), daemon=True).start()
def main():
@ -2402,6 +2556,7 @@ def main():
load_GUI(root, config_dict)
GUI_SIZE_WIDTH = 510
GUI_SIZE_HEIGHT = 619