2023-03-11, add new feature to pause MAXBOT. fix bugs for ibon.
parent
73ec0403e8
commit
316fc727d8
|
@ -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,6 +1732,7 @@ def tixcraft_area_auto_select(driver, url, config_dict):
|
|||
|
||||
# only when keyword#2 filled to query.
|
||||
if area_keyword_2_enable:
|
||||
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)
|
||||
|
@ -1729,6 +1744,7 @@ def tixcraft_area_auto_select(driver, url, config_dict):
|
|||
|
||||
# only when keyword#3 filled to query.
|
||||
if area_keyword_3_enable:
|
||||
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)
|
||||
|
@ -1740,6 +1756,7 @@ def tixcraft_area_auto_select(driver, url, config_dict):
|
|||
|
||||
# only when keyword#4 filled to query.
|
||||
if area_keyword_4_enable:
|
||||
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)
|
||||
|
@ -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 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,23 +5374,26 @@ 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 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 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 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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
captcha_password_inputbox = None
|
||||
try:
|
||||
captcha_password_inputbox = driver.find_element(By.CSS_SELECTOR, '')
|
||||
except Exception as exc:
|
||||
pass
|
||||
|
||||
if not captcha_password_inputbox is None:
|
||||
inferred_answer_string = None
|
||||
answer_list = []
|
||||
|
||||
if len(user_guess_string) > 0:
|
||||
inferred_answer_string = user_guess_string
|
||||
is_retry_user_single_answer = False
|
||||
|
||||
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,6 +6441,9 @@ def ibon_main(driver, url, config_dict, ibon_dict):
|
|||
if is_event_page:
|
||||
ibon_auto_signup(driver)
|
||||
|
||||
is_match_target_feature = False
|
||||
|
||||
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
|
||||
|
@ -6287,20 +6453,20 @@ def ibon_main(driver, url, config_dict, ibon_dict):
|
|||
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)
|
||||
|
||||
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:
|
||||
#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
|
||||
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 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
|
||||
|
@ -6312,6 +6478,7 @@ def ibon_main(driver, url, config_dict, ibon_dict):
|
|||
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:
|
||||
|
@ -6322,10 +6489,13 @@ def ibon_main(driver, url, config_dict, ibon_dict):
|
|||
#is_button_clicked = force_press_button(driver, By.CSS_SELECTOR, 'a.btn.btn-primary')
|
||||
driver.back()
|
||||
driver.refresh()
|
||||
else:
|
||||
|
||||
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
|
||||
|
@ -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) Don’t Forget、Candle Bb) Don’t Forget、Forever+1 Cc) Don’t Forget、Flowerbomb Dd) Don’t 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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
181
settings.py
181
settings.py
|
@ -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"
|
||||
|
@ -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'
|
||||
|
@ -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"] = '搶票'
|
||||
|
@ -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"] = '复制'
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue