Compare commits

...

10 Commits

Author SHA1 Message Date
Your Name 8142d85ddc allow to sendkeys vis javascript to localhost. 2024-09-07 16:52:37 +08:00
Your Name 7d6ff4330f add access token when call resetful API. 2024-09-05 14:10:40 +08:00
Your Name cf444bc2e2 2024-04-24, set auto_guess_options default to Fasle, and allow to set on web GUI. 2024-09-04 11:46:02 +08:00
Your Name 312a26b84e let refresh ignore maxbot is paused, force to send refresh. 2024-08-29 11:48:23 +08:00
Your Name 75843b5f00 Merge branch 'master' of github.com:max32002/tixcraft_bot 2024-08-25 17:31:37 +08:00
Your Name 9b0d32918f 2024-04-23, add refrash at target time 2024-08-25 17:31:16 +08:00
Max 3d335b7410
Update README.md 2024-07-31 16:27:29 +08:00
Max 1a159aeb6c
Create README_EXTENSION.md 2024-07-31 16:16:57 +08:00
Your Name 7f5ca0ad4c update for ticketmaster issue #279 2024-07-30 14:27:48 +08:00
Your Name d499fa8af2 Trivial updates 2024-07-29 23:03:41 +08:00
22 changed files with 633 additions and 206 deletions

View File

@ -44,24 +44,24 @@ A從之前 MaxBot 執行秒數來看,較好的電腦花費秒數大約 8
如果說你想增加在伺服器上存取記錄的變化程度,可以使用秒數的關鍵字功能,讓 MaxBot 在特定秒數時啟動與暫停。參考影片https://youtu.be/u3YQCZZu6kE 如果說你想增加在伺服器上存取記錄的變化程度,可以使用秒數的關鍵字功能,讓 MaxBot 在特定秒數時啟動與暫停。參考影片https://youtu.be/u3YQCZZu6kE
MaxBot 的出發點是幫助大家在購票時,可以有效率地自動化在花時間、重覆又無聊的刷新網頁。如果有任何違法,必定立即修正。 MaxBot 的出發點是幫助大家在購票時,可以有效率地自動化在花時間、重覆又無聊的刷新網頁。
如果有任何違法,必定立即修正。
</details> </details>
## Download 蛋黃酥上車程式下載 ## Download 蛋黃酥上車程式下載
<details> 蛋黃酥上車程式下載:
<summary><code><b>蛋黃酥上車程式下載</b>(點我展開)</code></summary>
https://github.com/max32002/tixcraft_bot/releases https://github.com/max32002/tixcraft_bot/releases
下載說明: 下載說明:
- 目前有打包的「執行檔」,只有 Windows 平台,其他作業系統需要使用原始碼來執行。當然 Windows 平台也可以用原始碼執行 MaxBot. - 目前有打包的「執行檔」,只有 Windows 平台,其他作業系統需要使用原始碼來執行。當然 Windows 平台也可以用原始碼執行 MaxBot.
- 如果你是要用「原始碼」執行 MaxBot, 在透過 git clone 或在 github 按下載原始碼的 zip 檔,你的 python 版本可以使用 3.7 / 3.8 / 3.9 / 3.10 這 4 個版號。 - 如果你是要用「原始碼」執行 MaxBot, 在透過 git clone 或在 github 按下載原始碼的 zip 檔, python 版本使用 3.7 / 3.8 / 3.9 / 3.10 這 4 個版號測試功能正常
- 如果有辦法的話,建議使用原始碼來執行 MaxBot執行上的「效率」與「相容性」的問題會較少。 - 如果有辦法的話,建議使用原始碼來執行 MaxBot執行上的「效率」與「相容性」的問題會較少。
</details>
## Demo 示範影片 ## Demo 示範影片
@ -69,17 +69,13 @@ https://github.com/max32002/tixcraft_bot/releases
## How to Use 如何使用 ## How to Use 如何使用
<details> 如何使用網頁說明:
<summary><code><b>如何使用</b>(點我展開)</code></summary>
- tixcraft / indievox / ticketmaster: https://max-everyday.com/2018/03/tixcraft-bot/ - tixcraft / indievox / ticketmaster: https://max-everyday.com/2018/03/tixcraft-bot/
- kktix: https://max-everyday.com/2018/12/kktix-bot/ - kktix: https://max-everyday.com/2018/12/kktix-bot/
- cityline: https://max-everyday.com/2019/03/cityline-bot/ - cityline: https://max-everyday.com/2019/03/cityline-bot/
- urbtix: https://max-everyday.com/2019/02/urbtix-bot/ - urbtix: https://max-everyday.com/2019/02/urbtix-bot/
- hkticketing / galaxymacau: https://max-everyday.com/2023/01/hkticketing-bot/ - hkticketing / galaxymacau: https://max-everyday.com/2023/01/hkticketing-bot/
</details>
## How to Execute Source Code 透過原始碼的執行方法 ## How to Execute Source Code 透過原始碼的執行方法
<details> <details>
@ -176,99 +172,29 @@ https://zh-tw.emeditor.com/increase-virtual-memory/
</details> </details>
## File Description 檔案說明 ## File Description 檔案說明
主要的檔案說明:
<details>
<summary><code><b>檔案說明</b>(點我展開)</code></summary>
- chrome_tixcraft.py : 搶票機器人主程式,用來自動化網頁的操作,使用元件是 selenium。 - chrome_tixcraft.py : 搶票機器人主程式,用來自動化網頁的操作,使用元件是 selenium。
- nodriver_tixcraft.py : 也是搶票機器人主程式,用來自動化網頁的操作,使用的元件是 nodriver。 - nodriver_tixcraft.py : 也是搶票機器人主程式,用來自動化網頁的操作,使用的元件是 nodriver。
- settings.py : 編輯 settings.json 的 GUI 介面。提供圖片 OCR 功能給 chrome 擴充功能。支援定時啟用/停用 MaxBot。 - settings.py : 編輯 settings.json 的 GUI 介面。提供圖片 OCR 功能給 chrome 擴充功能。支援定時啟用/停用 MaxBot。
- settings_old.py : 舊版本的編輯 settings.json 的 GUI 介面,與 settings.py 的差別在介面一個是網頁形式old 的用的是視窗形式。 - settings_old.py : 舊版本的編輯 settings.json 的 GUI 介面,與 settings.py 的差別在介面一個是網頁形式old 的用的是視窗形式。
- config_launcher.py : 設定檔管理, 方便對多個設定檔案搶票。 - config_launcher.py : 設定檔管理, 方便對多個設定檔案搶票。
</details>
## Introduce the Implement 實作方法 ## Introduce the Implement 實作方法
[實作方法 點我查看](https://stackoverflow.max-everyday.com/2018/03/selenium-chrome-webdriver/) https://stackoverflow.max-everyday.com/2018/03/selenium-chrome-webdriver/
## Execute Suggestion 蛋黃酥上車相關建議
<details>
<summary><code><b>蛋黃酥上車相關建議</b>(點我展開)</code></summary>
please run this source code with high performance hardware computer and high speed + stable network.
門票的「限量」是很殘酷的,建議不要用破舊的電腦或連線不穩的手機網路來搶票,因為只要比別人慢個 0.1 秒,票可能就沒了。為了要搶到限量的票真心建議去一下網咖或找一個網路連線穩定且快的地方並使用硬體不差的電腦來搶票。
</details>
## TODO about Cpatcha 關於驗證碼 ## TODO about Cpatcha 關於驗證碼
<details>
<summary><code><b>關於驗證碼</b>(點我展開)</code></summary>
目前自動輸入驗證碼用的元件是: 目前自動輸入驗證碼用的元件是:
https://github.com/sml2h3/ddddocr https://github.com/sml2h3/ddddocr
附註:
- 由於 ddddocr 元件的因素python 版本要降到 3.10.11 版, 傳送門:
https://www.python.org/downloads/release/python-31011/
- 猜測驗證碼時比較容易出錯的是字英 f 和 t還有 q 和 g, v 和 u 還有 w.
想自動輸入驗證碼,除了可以使用 ddddocr 提供的 trainer, 也可以參考看看:實作基於 CNN 的台鐵訂票驗證碼辨識以及透過模仿及資料增強的訓練集產生器 (Simple captcha solver based on CNN and a training set generator by imitating the style of captcha and data augmentation)
https://github.com/JasonLiTW/simple-railway-captcha-solver
</details>
## Common Problems 常見問題整理 ## Common Problems 常見問題整理
<details> 整理大家在搶票時常遇到的問題:
<summary><code><b>常見問題整理</b>(點我展開)</code></summary> https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket
### 整理大家在搶票時常遇到的問題:
- [使用搶票程式有違法嗎?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#law)
- [沒講清楚成功後的報酬](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#reward)
- [買到太多票](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#too_many_ticket)
- [如何處理多的票?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#many_tickets)
- [讓票要注意的詐騙](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#ticket-fraud)
- [使用搶票程式會讓自己的帳號被鎖住嗎?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#account_locked)
- [如何恢復拓元的「購票權限」?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#unlock)
- [網頁刷新速度有推薦幾秒刷新一次嗎?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#refresh)
- [搶票的電腦需要多少的網路頻寬才夠?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#bandwidth)
- [使用VPN/代理伺服器(Proxy)來搶票會有用嗎?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#proxy)
- [同一個IP短時間重試被系統視為惡意程式而封鎖怎麼辦](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#ip_blocked)
- [Firefox和chrome搶票上有差距嗎我看大家基本上都用chrome 很少用Firefox.](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#firefox_vs_chrome)
- [為什麼要設計搶票的機制?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#race_game)
- [為什麼網頁會有驗證碼?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#captcha)
- [你的硬體設備該不該升級?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#hardware)
- [想組一台新的電腦,是不是可以給我一些建議呢?](https://max-everyday.com/2023/02/common-problem-when-you-buy-ticket/#new_compute_suggestion)
</details>
## Extension Privacy 擴充功能隱私權政策 ## Extension Privacy 擴充功能隱私權政策
<details> https://github.com/max32002/tixcraft_bot/blob/master/README_EXTENSION.md
<summary><code><b>MaxBot Plus擴充功能隱私權政策</b>(點我展開)</code></summary>
### 產品如何收集、使用及分享使用者資料
- 擴充取得會取得特定網頁內容, 並且自動輸入張數。
- 擴充功能會移除特定網頁內容裡已售完的網頁區塊。
- 擴充功能會取得特定網址資訊, 並置換為下一個新的網址。
- 擴充取得會取得特定網頁內容, 判斷為需要重新整理時, 自動刷新頁面。
### 使用者資料的所有分享對象。
- 擴充功能沒有分享使用者資料。
### 擴充功能主要功能:
- 特定的訂票網頁內容, 並且自動輸入張數。
- 移除特定的訂票網頁內容裡已售完的網頁區塊。
- 特定的訂票網址, 自動置換為下一步的新網址。
- 當訂票網頁內容已經無票或沒有符合的關鍵字時, 自動刷新網頁。
- 特定網頁支援驗證碼功能, 需要同時開啟 MaxBot 主程式。
</details>

20
README_EXTENSION.md Normal file
View File

@ -0,0 +1,20 @@
## Extension Privacy 擴充功能隱私權政策
### 產品如何收集、使用及分享使用者資料
- 擴充取得會取得特定網頁內容, 並且自動輸入張數。
- 擴充功能會移除特定網頁內容裡已售完的網頁區塊。
- 擴充功能會取得特定網址資訊, 並置換為下一個新的網址。
- 擴充取得會取得特定網頁內容, 判斷為需要重新整理時, 自動刷新頁面。
### 使用者資料的所有分享對象。
- 擴充功能沒有分享使用者資料。
### 擴充功能主要功能:
- 特定的訂票網頁內容, 並且自動輸入張數。
- 移除特定的訂票網頁內容裡已售完的網頁區塊。
- 特定的訂票網址, 自動置換為下一步的新網址。
- 當訂票網頁內容已經無票或沒有符合的關鍵字時, 自動刷新網頁。
- 特定網頁支援驗證碼功能, 需要同時開啟 MaxBot 主程式。

View File

@ -44,7 +44,7 @@ except Exception as exc:
print(exc) print(exc)
pass pass
CONST_APP_VERSION = "MaxBot (2024.04.21)" CONST_APP_VERSION = "MaxBot (2024.04.24)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt" CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_CONFIG_FILE = "settings.json"
@ -1923,12 +1923,13 @@ def tixcraft_change_captcha(driver,url):
def tixcraft_toast(driver, message): def tixcraft_toast(driver, message):
toast_element = None toast_element = None
try: try:
my_css_selector = ".remark-word" my_css_selector = "p.remark-word"
toast_element = driver.find_element(By.CSS_SELECTOR, my_css_selector) toast_element = driver.find_element(By.CSS_SELECTOR, my_css_selector)
if not toast_element is None: if not toast_element is None:
driver.execute_script("arguments[0].innerHTML='%s';" % message, toast_element) driver.execute_script("arguments[0].innerHTML='%s';" % message, toast_element)
except Exception as exc: except Exception as exc:
print("find toast element fail") #print("find toast element fail")
pass
def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False): def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False):
is_verifyCode_editing = False is_verifyCode_editing = False
@ -1962,8 +1963,12 @@ def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False):
is_visible = False is_visible = False
if is_visible: if is_visible:
is_text_clicked = False
if inputed_value == "":
try: try:
form_verifyCode.click() form_verifyCode.click()
is_text_clicked = True
is_verifyCode_editing = True is_verifyCode_editing = True
except Exception as exc: except Exception as exc:
print("click form_verifyCode fail, trying to use javascript.") print("click form_verifyCode fail, trying to use javascript.")
@ -1978,6 +1983,8 @@ def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False):
if len(answer) > 0: if len(answer) > 0:
#print("start to fill answer.") #print("start to fill answer.")
try: try:
if not is_text_clicked:
form_verifyCode.click()
form_verifyCode.clear() form_verifyCode.clear()
form_verifyCode.send_keys(answer) form_verifyCode.send_keys(answer)
@ -1987,6 +1994,7 @@ def tixcraft_keyin_captcha_code(driver, answer = "", auto_submit = False):
is_form_sumbited = True is_form_sumbited = True
else: else:
driver.execute_script("document.getElementById(\"TicketForm_verifyCode\").select();") driver.execute_script("document.getElementById(\"TicketForm_verifyCode\").select();")
# TODO: show text message on ticketmaster web page.
tixcraft_toast(driver, "※ 按 Enter 如果答案是: " + answer) tixcraft_toast(driver, "※ 按 Enter 如果答案是: " + answer)
except Exception as exc: except Exception as exc:
print("send_keys ocr answer fail.") print("send_keys ocr answer fail.")
@ -2118,7 +2126,6 @@ def tixcraft_auto_ocr(driver, ocr, away_from_keyboard_enable, previous_answer, C
else: else:
if not away_from_keyboard_enable: if not away_from_keyboard_enable:
tixcraft_keyin_captcha_code(driver) tixcraft_keyin_captcha_code(driver)
tixcraft_toast(driver, "※ OCR辨識失敗Q_Q驗證碼請手動輸入...")
else: else:
is_need_redo_ocr = True is_need_redo_ocr = True
if previous_answer != ocr_answer: if previous_answer != ocr_answer:
@ -10844,9 +10851,85 @@ def resize_window(driver, config_dict):
driver.set_window_size(int(size_array[0]), int(size_array[1])) driver.set_window_size(int(size_array[0]), int(size_array[1]))
driver.set_window_position(position_left, 30) driver.set_window_position(position_left, 30)
def check_refresh_datetime_occur(driver, target_time):
is_refresh_datetime_sent = False
system_clock_data = datetime.now()
current_time = system_clock_data.strftime('%H:%M:%S')
if target_time == current_time:
try:
driver.refresh()
is_refresh_datetime_sent = True
print("send refresh at time:", current_time)
except Exception as exc:
pass
return is_refresh_datetime_sent
def sendkey_to_browser(driver, config_dict):
tmp_filepath = ""
if "token" in config_dict:
app_root = util.get_app_root()
tmp_file = config_dict["token"] + ".tmp"
tmp_filepath = os.path.join(app_root, tmp_file)
if os.path.exists(tmp_filepath):
sendkey_to_browser_exist(driver, tmp_filepath)
def sendkey_to_browser_exist(driver, tmp_filepath):
sendkey_dict = None
try:
with open(tmp_filepath) as json_data:
sendkey_dict = json.load(json_data)
print(sendkey_dict)
except Exception as e:
print("error on open file")
print(e)
pass
if sendkey_dict:
all_command_done = True
if "command" in sendkey_dict:
for cmd_dict in sendkey_dict["command"]:
#print("cmd_dict", cmd_dict)
if cmd_dict["type"] == "sendkey":
print("sendkey")
target_text = cmd_dict["text"]
try:
form_input_1 = driver.find_element(By.CSS_SELECTOR, cmd_dict["selector"])
inputed_value_1 = form_input_1.get_attribute('value')
if not inputed_value_1 == target_text:
form_input_1.clear()
form_input_1.click()
form_input_1.send_keys(cmd_dict["text"])
except Exception as exc:
all_command_done = False
print("error on sendkey")
print(exc)
pass
if cmd_dict["type"] == "click":
print("click")
try:
form_input_1 = driver.find_element(By.CSS_SELECTOR, cmd_dict["selector"])
form_input_1.click()
except Exception as exc:
all_command_done = False
print("error on click")
print(exc)
pass
time.sleep(0.05)
# must all command success to delete tmp file.
if all_command_done:
try:
os.unlink(tmp_filepath)
except Exception as e:
pass
def main(args): def main(args):
config_dict = get_config_dict(args) config_dict = get_config_dict(args)
config_dict["token"] = util.get_token()
driver = None driver = None
if not config_dict is None: if not config_dict is None:
@ -10860,7 +10943,6 @@ def main(args):
else: else:
print("Load config error!") print("Load config error!")
# internal variable. 說明:這是一個內部變數,請略過。
url = "" url = ""
last_url = "" last_url = ""
@ -10878,6 +10960,8 @@ def main(args):
maxbot_last_reset_time = time.time() maxbot_last_reset_time = time.time()
is_quit_bot = False is_quit_bot = False
is_refresh_datetime_sent = False
while True: while True:
time.sleep(0.05) time.sleep(0.05)
@ -10904,6 +10988,9 @@ def main(args):
if len(url) == 0: if len(url) == 0:
continue continue
if not is_refresh_datetime_sent:
is_refresh_datetime_sent = check_refresh_datetime_occur(driver, config_dict["refresh_datetime"])
is_maxbot_paused = False is_maxbot_paused = False
if os.path.exists(CONST_MAXBOT_INT28_FILE): if os.path.exists(CONST_MAXBOT_INT28_FILE):
is_maxbot_paused = True is_maxbot_paused = True
@ -10923,6 +11010,9 @@ def main(args):
time.sleep(0.1) time.sleep(0.1)
continue continue
sendkey_to_browser(driver, config_dict)
# default is 0, not reset.
if config_dict["advanced"]["reset_browser_interval"] > 0: if config_dict["advanced"]["reset_browser_interval"] > 0:
maxbot_running_time = time.time() - maxbot_last_reset_time maxbot_running_time = time.time() - maxbot_last_reset_time
if maxbot_running_time > config_dict["advanced"]["reset_browser_interval"]: if maxbot_running_time > config_dict["advanced"]["reset_browser_interval"]:

View File

@ -24,7 +24,7 @@ import webbrowser
import util import util
CONST_APP_VERSION = "MaxBot (2024.04.21)" CONST_APP_VERSION = "MaxBot (2024.04.24)"
CONST_MAXBOT_LAUNCHER_FILE = "config_launcher.json" CONST_MAXBOT_LAUNCHER_FILE = "config_launcher.json"
CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_CONFIG_FILE = "settings.json"

View File

@ -1,11 +1,5 @@
# Demo (示範影片) # Demo (示範影片)
### 2024-04-14
網頁設定介面
https://youtu.be/VDQFbweMLxU
### 2024-04-11 ### 2024-04-11
信用卡活動登錄自動填身份證號碼 信用卡活動登錄自動填身份證號碼

View File

@ -32,7 +32,7 @@ except Exception as exc:
print(exc) print(exc)
pass pass
CONST_APP_VERSION = "MaxBot (2024.04.21)" CONST_APP_VERSION = "MaxBot (2024.04.24)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt" CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_CONFIG_FILE = "settings.json"
@ -2152,6 +2152,21 @@ def nodriver_overwrite_prefs(conf):
with open(state_filepath, 'w') as outfile: with open(state_filepath, 'w') as outfile:
outfile.write(json_str) outfile.write(json_str)
async def check_refresh_datetime_occur(tab, target_time):
is_refresh_datetime_sent = False
system_clock_data = datetime.now()
current_time = system_clock_data.strftime('%H:%M:%S')
if target_time == current_time:
try:
await tab.reload()
is_refresh_datetime_sent = True
print("send refresh at time:", current_time)
except Exception as exc:
pass
return is_refresh_datetime_sent
async def main(args): async def main(args):
config_dict = get_config_dict(args) config_dict = get_config_dict(args)
@ -2178,7 +2193,6 @@ async def main(args):
else: else:
print("Load config error!") print("Load config error!")
# internal variable. 說明:這是一個內部變數,請略過。
url = "" url = ""
last_url = "" last_url = ""
@ -2205,6 +2219,8 @@ async def main(args):
maxbot_last_reset_time = time.time() maxbot_last_reset_time = time.time()
is_quit_bot = False is_quit_bot = False
is_refresh_datetime_sent = False
while True: while True:
time.sleep(0.05) time.sleep(0.05)
@ -2231,6 +2247,9 @@ async def main(args):
if len(url) == 0: if len(url) == 0:
continue continue
if not is_refresh_datetime_sent:
is_refresh_datetime_sent = await check_refresh_datetime_occur(tab, config_dict["refresh_datetime"])
is_maxbot_paused = False is_maxbot_paused = False
if os.path.exists(CONST_MAXBOT_INT28_FILE): if os.path.exists(CONST_MAXBOT_INT28_FILE):
is_maxbot_paused = True is_maxbot_paused = True

View File

@ -3,6 +3,7 @@
"browser": "chrome", "browser": "chrome",
"language": "English", "language": "English",
"ticket_number": 2, "ticket_number": 2,
"refresh_datetime": "",
"ocr_captcha": { "ocr_captcha": {
"enable": true, "enable": true,
"beta": true, "beta": true,
@ -24,7 +25,7 @@
"kktix": { "kktix": {
"auto_press_next_step_button": true, "auto_press_next_step_button": true,
"auto_fill_ticket_number": true, "auto_fill_ticket_number": true,
"max_dwell_time": 60 "max_dwell_time": 90
}, },
"cityline": { "cityline": {
"cityline_queue_retry": true "cityline_queue_retry": true
@ -77,7 +78,7 @@
"block_facebook_network": false, "block_facebook_network": false,
"headless": false, "headless": false,
"verbose": false, "verbose": false,
"auto_guess_options": true, "auto_guess_options": false,
"user_guess_string": "", "user_guess_string": "",
"remote_url": "\"http://127.0.0.1:16888/\"", "remote_url": "\"http://127.0.0.1:16888/\"",
"auto_reload_page_interval": 0.1, "auto_reload_page_interval": 0.1,

View File

@ -39,7 +39,7 @@ try:
except Exception as exc: except Exception as exc:
pass pass
CONST_APP_VERSION = "MaxBot (2024.04.21)" CONST_APP_VERSION = "MaxBot (2024.04.24)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt" CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_CONFIG_FILE = "settings.json"
@ -104,6 +104,8 @@ def get_default_config():
config_dict["browser"] = "chrome" config_dict["browser"] = "chrome"
config_dict["language"] = "English" config_dict["language"] = "English"
config_dict["ticket_number"] = 2 config_dict["ticket_number"] = 2
config_dict["refresh_datetime"] = ""
config_dict["ocr_captcha"] = {} config_dict["ocr_captcha"] = {}
config_dict["ocr_captcha"]["enable"] = True config_dict["ocr_captcha"]["enable"] = True
config_dict["ocr_captcha"]["beta"] = True config_dict["ocr_captcha"]["beta"] = True
@ -125,7 +127,7 @@ def get_default_config():
config_dict['kktix']={} config_dict['kktix']={}
config_dict["kktix"]["auto_press_next_step_button"] = True config_dict["kktix"]["auto_press_next_step_button"] = True
config_dict["kktix"]["auto_fill_ticket_number"] = True config_dict["kktix"]["auto_fill_ticket_number"] = True
config_dict["kktix"]["max_dwell_time"] = 60 config_dict["kktix"]["max_dwell_time"] = 90
config_dict['cityline']={} config_dict['cityline']={}
config_dict["cityline"]["cityline_queue_retry"] = True config_dict["cityline"]["cityline_queue_retry"] = True
@ -183,7 +185,7 @@ def get_default_config():
config_dict["advanced"]["headless"] = False config_dict["advanced"]["headless"] = False
config_dict["advanced"]["verbose"] = False config_dict["advanced"]["verbose"] = False
config_dict["advanced"]["auto_guess_options"] = True config_dict["advanced"]["auto_guess_options"] = False
config_dict["advanced"]["user_guess_string"] = "" config_dict["advanced"]["user_guess_string"] = ""
# remote_url not under ocr, due to not only support ocr features. # remote_url not under ocr, due to not only support ocr features.
@ -382,6 +384,12 @@ def clean_tmp_file():
for filepath in remove_file_list: for filepath in remove_file_list:
util.force_remove_file(filepath) util.force_remove_file(filepath)
Root_Dir = util.get_app_root()
target_folder = os.listdir(Root_Dir)
for item in target_folder:
if item.endswith(".tmp"):
os.remove(os.path.join(Root_Dir, item))
class QuestionHandler(tornado.web.RequestHandler): class QuestionHandler(tornado.web.RequestHandler):
def get(self): def get(self):
global txt_question global txt_question
@ -477,6 +485,36 @@ class SaveJsonHandler(tornado.web.RequestHandler):
self.finish() self.finish()
class SendkeyHandler(tornado.web.RequestHandler):
def post(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
_body = None
is_pass_check = True
errorMessage = ""
errorCode = 0
if is_pass_check:
is_pass_check = False
try :
_body = json.loads(self.request.body)
is_pass_check = True
except Exception:
errorMessage = "wrong json format"
errorCode = 1001
pass
if is_pass_check:
app_root = util.get_app_root()
if "token" in _body:
tmp_file = _body["token"] + ".tmp"
config_filepath = os.path.join(app_root, tmp_file)
util.save_json(_body, config_filepath)
self.write({"return": True})
class OcrHandler(tornado.web.RequestHandler): class OcrHandler(tornado.web.RequestHandler):
def get(self): def get(self):
self.write({"answer": "1234"}) self.write({"answer": "1234"})
@ -558,6 +596,7 @@ async def main_server():
app = Application([ app = Application([
("/version", VersionHandler), ("/version", VersionHandler),
("/shutdown", ShutdownHandler), ("/shutdown", ShutdownHandler),
("/sendkey", SendkeyHandler),
# status api # status api
("/status", StatusHandler), ("/status", StatusHandler),

View File

@ -34,7 +34,7 @@ try:
except Exception as exc: except Exception as exc:
pass pass
CONST_APP_VERSION = "MaxBot (2024.04.21)" CONST_APP_VERSION = "MaxBot (2024.04.24)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt" CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json" CONST_MAXBOT_CONFIG_FILE = "settings.json"
@ -106,6 +106,7 @@ def load_translate():
en_us["browser"] = 'Browser' en_us["browser"] = 'Browser'
en_us["language"] = 'Language' en_us["language"] = 'Language'
en_us["ticket_number"] = 'Ticker Number' en_us["ticket_number"] = 'Ticker Number'
en_us["refresh_datetime"] = 'Refresh at specified time'
en_us["enable"] = 'Enable' en_us["enable"] = 'Enable'
en_us["recommand_enable"] = "Recommended to enable" en_us["recommand_enable"] = "Recommended to enable"
@ -226,6 +227,7 @@ def load_translate():
zh_tw["browser"] = '瀏覽器' zh_tw["browser"] = '瀏覽器'
zh_tw["language"] = '語言' zh_tw["language"] = '語言'
zh_tw["ticket_number"] = '門票張數' zh_tw["ticket_number"] = '門票張數'
zh_tw["refresh_datetime"] = '刷新在指定時間'
zh_tw["enable"] = '啟用' zh_tw["enable"] = '啟用'
zh_tw["recommand_enable"] = "建議啟用" zh_tw["recommand_enable"] = "建議啟用"
@ -344,6 +346,7 @@ def load_translate():
zh_cn["browser"] = '浏览器' zh_cn["browser"] = '浏览器'
zh_cn["language"] = '语言' zh_cn["language"] = '语言'
zh_cn["ticket_number"] = '门票张数' zh_cn["ticket_number"] = '门票张数'
zh_cn["refresh_datetime"] = '刷新在指定时间'
zh_cn["enable"] = '启用' zh_cn["enable"] = '启用'
zh_cn["recommand_enable"] = "建议启用" zh_cn["recommand_enable"] = "建议启用"
@ -464,6 +467,7 @@ def load_translate():
ja_jp["browser"] = 'ブラウザ' ja_jp["browser"] = 'ブラウザ'
ja_jp["language"] = '言語' ja_jp["language"] = '言語'
ja_jp["ticket_number"] = '枚数' ja_jp["ticket_number"] = '枚数'
ja_jp["refresh_datetime"] = '目標時間にリフレッシュ'
ja_jp["enable"] = '有効' ja_jp["enable"] = '有効'
ja_jp["recommand_enable"] = "有効化を推奨" ja_jp["recommand_enable"] = "有効化を推奨"
@ -591,6 +595,8 @@ def get_default_config():
config_dict["browser"] = "chrome" config_dict["browser"] = "chrome"
config_dict["language"] = "English" config_dict["language"] = "English"
config_dict["ticket_number"] = 2 config_dict["ticket_number"] = 2
config_dict["refresh_datetime"] = ""
config_dict["ocr_captcha"] = {} config_dict["ocr_captcha"] = {}
config_dict["ocr_captcha"]["enable"] = True config_dict["ocr_captcha"]["enable"] = True
config_dict["ocr_captcha"]["beta"] = True config_dict["ocr_captcha"]["beta"] = True
@ -612,7 +618,7 @@ def get_default_config():
config_dict['kktix']={} config_dict['kktix']={}
config_dict["kktix"]["auto_press_next_step_button"] = True config_dict["kktix"]["auto_press_next_step_button"] = True
config_dict["kktix"]["auto_fill_ticket_number"] = True config_dict["kktix"]["auto_fill_ticket_number"] = True
config_dict["kktix"]["max_dwell_time"] = 60 config_dict["kktix"]["max_dwell_time"] = 90
config_dict['cityline']={} config_dict['cityline']={}
config_dict["cityline"]["cityline_queue_retry"] = True config_dict["cityline"]["cityline_queue_retry"] = True
@ -670,7 +676,7 @@ def get_default_config():
config_dict["advanced"]["headless"] = False config_dict["advanced"]["headless"] = False
config_dict["advanced"]["verbose"] = False config_dict["advanced"]["verbose"] = False
config_dict["advanced"]["auto_guess_options"] = True config_dict["advanced"]["auto_guess_options"] = False
config_dict["advanced"]["user_guess_string"] = "" config_dict["advanced"]["user_guess_string"] = ""
config_dict["advanced"]["remote_url"] = "http://127.0.0.1:%d/" % (CONST_SERVER_PORT) config_dict["advanced"]["remote_url"] = "http://127.0.0.1:%d/" % (CONST_SERVER_PORT)
@ -1023,6 +1029,7 @@ def btn_save_act(slience_mode=False):
txt_resume_keyword_second.insert("1.0", config_dict["advanced"]["resume_keyword_second"].strip()) txt_resume_keyword_second.insert("1.0", config_dict["advanced"]["resume_keyword_second"].strip())
if is_all_data_correct: if is_all_data_correct:
config_dict["refresh_datetime"] = txt_refresh_datetime.get().strip()
config_dict["area_auto_select"]["enable"] = bool(chk_state_area_auto_select.get()) config_dict["area_auto_select"]["enable"] = bool(chk_state_area_auto_select.get())
config_dict["area_auto_select"]["mode"] = combo_area_auto_select_mode.get().strip() config_dict["area_auto_select"]["mode"] = combo_area_auto_select_mode.get().strip()
@ -1258,6 +1265,7 @@ def applyNewLanguage():
global lbl_browser global lbl_browser
global lbl_language global lbl_language
global lbl_ticket_number global lbl_ticket_number
global lbl_refresh_datetime
# for kktix # for kktix
global lbl_auto_press_next_step_button global lbl_auto_press_next_step_button
@ -1347,6 +1355,7 @@ def applyNewLanguage():
lbl_browser.config(text=translate[language_code]["browser"]) lbl_browser.config(text=translate[language_code]["browser"])
lbl_language.config(text=translate[language_code]["language"]) lbl_language.config(text=translate[language_code]["language"])
lbl_ticket_number.config(text=translate[language_code]["ticket_number"]) lbl_ticket_number.config(text=translate[language_code]["ticket_number"])
lbl_refresh_datetime.config(text=translate[language_code]["refresh_datetime"])
lbl_auto_press_next_step_button.config(text=translate[language_code]["auto_press_next_step_button"]) lbl_auto_press_next_step_button.config(text=translate[language_code]["auto_press_next_step_button"])
lbl_auto_fill_ticket_number.config(text=translate[language_code]["auto_fill_ticket_number"]) lbl_auto_fill_ticket_number.config(text=translate[language_code]["auto_fill_ticket_number"])
@ -1674,9 +1683,6 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X):
print("python version:", platform.python_version()) print("python version:", platform.python_version())
print("platform:", platform.platform()) print("platform:", platform.platform())
global lbl_homepage
global lbl_ticket_number
global lbl_kktix global lbl_kktix
global lbl_tixcraft global lbl_tixcraft
@ -1686,6 +1692,7 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X):
group_row_count = 0 group_row_count = 0
# first row need padding Y # first row need padding Y
global lbl_homepage
lbl_homepage = Label(frame_group_header, text=translate[language_code]['homepage']) lbl_homepage = Label(frame_group_header, text=translate[language_code]['homepage'])
lbl_homepage.grid(column=0, row=group_row_count, sticky = E) lbl_homepage.grid(column=0, row=group_row_count, sticky = E)
@ -1699,6 +1706,7 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X):
group_row_count+=1 group_row_count+=1
global lbl_ticket_number
lbl_ticket_number = Label(frame_group_header, text=translate[language_code]['ticket_number']) lbl_ticket_number = Label(frame_group_header, text=translate[language_code]['ticket_number'])
lbl_ticket_number.grid(column=0, row=group_row_count, sticky = E) lbl_ticket_number.grid(column=0, row=group_row_count, sticky = E)
@ -1719,6 +1727,17 @@ def PreferenctTab(root, config_dict, language_code, UI_PADDING_X):
frame_group_header.grid(column=0, row=row_count, sticky = W, padx=UI_PADDING_X) frame_group_header.grid(column=0, row=row_count, sticky = W, padx=UI_PADDING_X)
group_row_count+=1
global lbl_refresh_datetime
lbl_refresh_datetime = Label(frame_group_header, text=translate[language_code]['refresh_datetime'])
lbl_refresh_datetime.grid(column=0, row=group_row_count, sticky = E)
global txt_refresh_datetime
txt_refresh_datetime_value = StringVar(frame_group_header, value=str(config_dict["refresh_datetime"]))
txt_refresh_datetime = Entry(frame_group_header, width=30, textvariable = txt_refresh_datetime_value)
txt_refresh_datetime.grid(column=1, row=group_row_count, sticky = W)
row_count+=1 row_count+=1
# for sub group KKTix. # for sub group KKTix.
@ -3130,7 +3149,7 @@ def main_gui():
load_GUI(root, config_dict) load_GUI(root, config_dict)
GUI_SIZE_WIDTH = 590 GUI_SIZE_WIDTH = 610
GUI_SIZE_HEIGHT = 645 GUI_SIZE_HEIGHT = 645
GUI_SIZE_MACOS = str(GUI_SIZE_WIDTH) + 'x' + str(GUI_SIZE_HEIGHT) GUI_SIZE_MACOS = str(GUI_SIZE_WIDTH) + 'x' + str(GUI_SIZE_HEIGHT)
@ -3179,6 +3198,12 @@ def clean_tmp_file():
for filepath in remove_file_list: for filepath in remove_file_list:
util.force_remove_file(filepath) util.force_remove_file(filepath)
Root_Dir = util.get_app_root()
target_folder = os.listdir(Root_Dir)
for item in target_folder:
if item.endswith(".tmp"):
os.remove(os.path.join(Root_Dir, item))
def btn_copy_ip_clicked(): def btn_copy_ip_clicked():
local_ip = util.get_ip_address() local_ip = util.get_ip_address()
ip_address = "http://%s:%d/" % (local_ip,CONST_SERVER_PORT) ip_address = "http://%s:%d/" % (local_ip,CONST_SERVER_PORT)
@ -3227,6 +3252,36 @@ class VersionHandler(tornado.web.RequestHandler):
def get(self): def get(self):
self.write({"version":self.application.version}) self.write({"version":self.application.version})
class SendkeyHandler(tornado.web.RequestHandler):
def post(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
_body = None
is_pass_check = True
errorMessage = ""
errorCode = 0
if is_pass_check:
is_pass_check = False
try :
_body = json.loads(self.request.body)
is_pass_check = True
except Exception:
errorMessage = "wrong json format"
errorCode = 1001
pass
if is_pass_check:
app_root = util.get_app_root()
if "token" in _body:
tmp_file = _body["token"] + ".tmp"
config_filepath = os.path.join(app_root, tmp_file)
util.save_json(_body, config_filepath)
self.write({"return": True})
class OcrHandler(tornado.web.RequestHandler): class OcrHandler(tornado.web.RequestHandler):
def get(self): def get(self):
self.write({"answer": "1234"}) self.write({"answer": "1234"})
@ -3286,6 +3341,8 @@ async def main_server():
app = Application([ app = Application([
("/", MainHandler), ("/", MainHandler),
("/version", VersionHandler), ("/version", VersionHandler),
("/sendkey", SendkeyHandler),
("/ocr", OcrHandler), ("/ocr", OcrHandler),
("/query", MainHandler), ("/query", MainHandler),
("/question", QuestionHandler), ("/question", QuestionHandler),

View File

@ -12,6 +12,7 @@ import threading
from typing import Optional from typing import Optional
import requests import requests
import uuid
CONST_FROM_TOP_TO_BOTTOM = "from top to bottom" CONST_FROM_TOP_TO_BOTTOM = "from top to bottom"
CONST_FROM_BOTTOM_TO_TOP = "from bottom to top" CONST_FROM_BOTTOM_TO_TOP = "from bottom to top"
@ -2041,3 +2042,6 @@ def launch_maxbot(script_name="chrome_tixcraft", filename="", homepage="", kktix
msg=str(exc) msg=str(exc)
print("exeption:", msg) print("exeption:", msg)
pass pass
def get_token():
return str(uuid.uuid4().hex)

View File

@ -1 +1 @@
{"homepage": "about:blank", "browser": "chrome", "language": "English", "ticket_number": 2, "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "date_auto_select": {"enable": true, "date_keyword": "", "mode": "random"}, "area_auto_select": {"enable": true, "mode": "random", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "max_dwell_time": 60}, "cityline": {"cityline_queue_retry": true}, "tixcraft": {"pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "advanced": {"play_sound": {"ticket": true, "order": 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": "", "facebook_password_plaintext": "", "kktix_password_plaintext": "", "fami_password_plaintext": "", "urbtix_password_plaintext": "", "cityline_password_plaintext": "", "hkticketing_password_plaintext": "", "kham_password_plaintext": "", "ticket_password_plaintext": "", "udn_password_plaintext": "", "ticketplus_password_plaintext": "", "chrome_extension": true, "disable_adjacent_seat": false, "hide_some_image": false, "block_facebook_network": false, "headless": false, "verbose": false, "auto_guess_options": true, "user_guess_string": "", "remote_url": "\"http://127.0.0.1:16888/\"", "auto_reload_page_interval": 0.1, "auto_reload_overheat_count": 4, "auto_reload_overheat_cd": 1, "reset_browser_interval": 0, "proxy_server_port": "", "window_size": "480,1024,0", "idle_keyword": "", "resume_keyword": "", "idle_keyword_second": "", "resume_keyword_second": ""}, "domain_filter": ["*.doubleclick.net/*", "*.googlesyndication.com/*", "*.ssp.hinet.net/*", "*a.amnet.tw/*", "*adx.c.appier.net/*", "*cdn.cookielaw.org/*", "*cdnjs.cloudflare.com/ajax/libs/clipboard.js/*", "*clarity.ms/*", "*cloudfront.com/*", "*cms.analytics.yahoo.com/*", "*e2elog.fetnet.net/*", "*fundingchoicesmessages.google.com/*", "*ghtinc.com/*", "*google-analytics.com/*", "*googletagmanager.com/*", "*googletagservices.com/*", "*img.uniicreative.com/*", "*lndata.com/*", "*match.adsrvr.org/*", "*onead.onevision.com.tw/*", "*play.google.com/log?*", "*popin.cc/*", "*rollbar.com/*", "*sb.scorecardresearch.com/*", "*tagtoo.co/*", "*ticketmaster.sg/js/adblock*", "*ticketmaster.sg/js/adblock.js*", "*tixcraft.com/js/analytics.js*", "*tixcraft.com/js/common.js*", "*tixcraft.com/js/custom.js*", "*treasuredata.com/*", "*www.youtube.com/youtubei/v1/player/heartbeat*"]} {"homepage": "about:blank", "browser": "chrome", "language": "English", "ticket_number": 2, "refresh_datetime": "", "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "date_auto_select": {"enable": true, "date_keyword": "", "mode": "random"}, "area_auto_select": {"enable": true, "mode": "random", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "max_dwell_time": 90}, "cityline": {"cityline_queue_retry": true}, "tixcraft": {"pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "advanced": {"play_sound": {"ticket": true, "order": 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": "", "facebook_password_plaintext": "", "kktix_password_plaintext": "", "fami_password_plaintext": "", "urbtix_password_plaintext": "", "cityline_password_plaintext": "", "hkticketing_password_plaintext": "", "kham_password_plaintext": "", "ticket_password_plaintext": "", "udn_password_plaintext": "", "ticketplus_password_plaintext": "", "chrome_extension": true, "disable_adjacent_seat": false, "hide_some_image": false, "block_facebook_network": false, "headless": false, "verbose": false, "auto_guess_options": false, "user_guess_string": "", "remote_url": "\"http://127.0.0.1:16888/\"", "auto_reload_page_interval": 0.1, "auto_reload_overheat_count": 4, "auto_reload_overheat_cd": 1, "reset_browser_interval": 0, "proxy_server_port": "", "window_size": "480,1024,1", "idle_keyword": "", "resume_keyword": "", "idle_keyword_second": "", "resume_keyword_second": ""}, "token": "3fbc8123327e468b8ac6e9280cf0653a", "domain_filter": ["*.doubleclick.net/*", "*.googlesyndication.com/*", "*.ssp.hinet.net/*", "*a.amnet.tw/*", "*adx.c.appier.net/*", "*cdn.cookielaw.org/*", "*cdnjs.cloudflare.com/ajax/libs/clipboard.js/*", "*clarity.ms/*", "*cloudfront.com/*", "*cms.analytics.yahoo.com/*", "*e2elog.fetnet.net/*", "*fundingchoicesmessages.google.com/*", "*ghtinc.com/*", "*google-analytics.com/*", "*googletagmanager.com/*", "*googletagservices.com/*", "*img.uniicreative.com/*", "*lndata.com/*", "*match.adsrvr.org/*", "*onead.onevision.com.tw/*", "*play.google.com/log?*", "*popin.cc/*", "*rollbar.com/*", "*sb.scorecardresearch.com/*", "*tagtoo.co/*", "*ticketmaster.sg/js/adblock*", "*ticketmaster.sg/js/adblock.js*", "*tixcraft.com/js/analytics.js*", "*tixcraft.com/js/common.js*", "*tixcraft.com/js/custom.js*", "*treasuredata.com/*", "*www.youtube.com/youtubei/v1/player/heartbeat*"]}

View File

@ -153,6 +153,48 @@ async function ocr(data_url, image_data, tabId)
); );
} }
async function post(data_url, post_body, tabId)
{
//console.log("data_url:"+data_url);
fetch(data_url,{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: post_body
})
.then(response =>
{
if (response.ok)
{
return response.json();
}
else if (response.status === 404)
{
let result_json={"answer": "", "fail": 'error 404'};
//console.log(result_json);
//sendResponse(result_json);
return Promise.reject('error 404')
}
}
)
.then((data) =>
{
if (data)
{
let result_json=data;
console.log(result_json);
chrome.tabs.sendMessage(tabId, result_json);
}
}
)
.catch(error =>
{
console.log('error is', error)
}
);
}
// for avoid overheat. // for avoid overheat.
chrome.storage.local.set( chrome.storage.local.set(
{ {
@ -179,6 +221,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
ocr(request_json.data.url, request_json.data.image_data, tabId); ocr(request_json.data.url, request_json.data.image_data, tabId);
} }
if(request_json.action=="post") {
const tabId = sender.tab.id;
post(request_json.data.url, request_json.data.post_data, tabId);
}
if(request_json.action=="status") { if(request_json.action=="status") {
result_json={"status": answer}; result_json={"status": answer};
const tabId = sender.tab.id; const tabId = sender.tab.id;

View File

@ -1 +1 @@
{"homepage": "about:blank", "browser": "chrome", "language": "English", "ticket_number": 2, "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "date_auto_select": {"enable": true, "date_keyword": "", "mode": "random"}, "area_auto_select": {"enable": true, "mode": "random", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "max_dwell_time": 60}, "cityline": {"cityline_queue_retry": true}, "tixcraft": {"pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "advanced": {"play_sound": {"ticket": true, "order": 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": "", "facebook_password_plaintext": "", "kktix_password_plaintext": "", "fami_password_plaintext": "", "urbtix_password_plaintext": "", "cityline_password_plaintext": "", "hkticketing_password_plaintext": "", "kham_password_plaintext": "", "ticket_password_plaintext": "", "udn_password_plaintext": "", "ticketplus_password_plaintext": "", "chrome_extension": true, "disable_adjacent_seat": false, "hide_some_image": false, "block_facebook_network": false, "headless": false, "verbose": false, "auto_guess_options": true, "user_guess_string": "", "remote_url": "\"http://127.0.0.1:16888/\"", "auto_reload_page_interval": 0.1, "auto_reload_overheat_count": 4, "auto_reload_overheat_cd": 1, "reset_browser_interval": 0, "proxy_server_port": "", "window_size": "480,1024,0", "idle_keyword": "", "resume_keyword": "", "idle_keyword_second": "", "resume_keyword_second": ""}} {"homepage": "about:blank", "browser": "chrome", "language": "English", "ticket_number": 2, "refresh_datetime": "", "ocr_captcha": {"enable": true, "beta": true, "force_submit": true, "image_source": "canvas"}, "webdriver_type": "undetected_chromedriver", "date_auto_select": {"enable": true, "date_keyword": "", "mode": "random"}, "area_auto_select": {"enable": true, "mode": "random", "area_keyword": ""}, "keyword_exclude": "\"\u8f2a\u6905\",\"\u8eab\u969c\",\"\u8eab\u5fc3 \u969c\u7919\",\"Restricted View\",\"\u71c8\u67f1\u906e\u853d\",\"\u8996\u7dda\u4e0d\u5b8c\u6574\"", "kktix": {"auto_press_next_step_button": true, "auto_fill_ticket_number": true, "max_dwell_time": 90}, "cityline": {"cityline_queue_retry": true}, "tixcraft": {"pass_date_is_sold_out": true, "auto_reload_coming_soon_page": true}, "advanced": {"play_sound": {"ticket": true, "order": 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": "", "facebook_password_plaintext": "", "kktix_password_plaintext": "", "fami_password_plaintext": "", "urbtix_password_plaintext": "", "cityline_password_plaintext": "", "hkticketing_password_plaintext": "", "kham_password_plaintext": "", "ticket_password_plaintext": "", "udn_password_plaintext": "", "ticketplus_password_plaintext": "", "chrome_extension": true, "disable_adjacent_seat": false, "hide_some_image": false, "block_facebook_network": false, "headless": false, "verbose": false, "auto_guess_options": false, "user_guess_string": "", "remote_url": "\"http://127.0.0.1:16888/\"", "auto_reload_page_interval": 0.1, "auto_reload_overheat_count": 4, "auto_reload_overheat_cd": 1, "reset_browser_interval": 0, "proxy_server_port": "", "window_size": "480,1024,1", "idle_keyword": "", "resume_keyword": "", "idle_keyword_second": "", "resume_keyword_second": ""}, "token": "3fbc8123327e468b8ac6e9280cf0653a"}

View File

@ -0,0 +1,218 @@
const storage = chrome.storage.local;
var settings = null;
$("footer").remove();
function ibon_assign_ticket_number(ticket_number) {
let $main_table = $("div > table.table[id] > tbody");
if ($main_table.length > 0) {
//console.log("found main table");
let $ticket_tr = $main_table.find("tr");
if ($ticket_tr.length) {
let $ticket_options = $main_table.find("select:first option");
if ($ticket_options.length) {
let is_ticket_number_assign = false;
if (ticket_number > 0) {
console.log("target ticket_number:" + ticket_number);
$ticket_options.each(function() {
if ($(this).val() == ticket_number) {
$(this).prop('selected', true);
$(this).trigger("change");
is_ticket_number_assign = true;
return false;
}
});
}
//console.log("is_ticket_number_assign:"+is_ticket_number_assign);
if (!is_ticket_number_assign) {
$ticket_options.last().prop('selected', true);
$ticket_options.trigger("change");
is_ticket_number_assign = true;
}
if (is_ticket_number_assign) {
let select_tag = document.querySelector("div > table.table[id] > tbody select");
if(select_tag) {
//console.log("trigger select click");
select_tag.dispatchEvent(new Event('change'));
}
start_ibon_ocr();
}
} else {
//console.log("target option empty");
}
} else {
setTimeout(function() {
ibon_assign_ticket_number(ticket_number)
}, 200);
}
}
}
function ibon_assign_adjacent_seat(flag) {
//console.log("disable_adjacent_seat flag:"+flag);
if (flag) {
$('input[type=checkbox]').each(function() {
$(this).prop('checked', true);
});
}
}
function ibon_focus_on_captcha() {
$("div.editor-box > div > input[type='text']").focus();
}
var myInterval = null;
function ibon_get_ocr_image() {
//console.log("get_ocr_image");
let image_data = "";
// PS: tixcraft have different domain to use the same content script.
const currentUrl = window.location.href;
const domain = currentUrl.split('/')[2];
let canvas = document.querySelector("div.editor-box > div > canvas");
if (canvas != null) {
let img_data = canvas.toDataURL();
if (img_data) {
image_data = img_data.split(",")[1];
//console.log(image_data);
}
}
return image_data;
}
chrome.runtime.onMessage.addListener((message) => {
//console.log('sent from background', message);
if(message.answer) {
ibon_set_ocr_answer(message.answer);
}
});
function ibon_set_ocr_answer(answer) {
console.log("answer:" + answer);
if (answer.length > 0) {
//$("div.editor-box > div > input[type='text']").val(answer);
let input_tag = document.querySelector("div.editor-box > div > input[type='text']");
if(input_tag) {
//console.log("click on captcha input.")
//input_tag.click();
//input_tag.value=answer;
//input_tag.dispatchEvent(new Event('change'));
if(input_tag.value != answer) {
console.log("set new answer as:" + answer);
ibon_set_ocr_answer_api(answer);
}
}
}
}
async function ibon_set_ocr_answer_api(answer) {
let api_url = get_remote_url(settings);
if(api_url.indexOf("127.0.0.")>-1) {
let body = {
token: settings.token,
command: [
{type: 'sendkey', selector: "div.editor-box > div > input[type='text']", text: answer},
{type: 'click', selector: 'div#ticket-wrap a.btn.btn-primary[href]'}
]};
body = JSON.stringify(body);
let bundle = {
action: 'post',
data: {
'url': api_url + 'sendkey',
'post_data': body,
}
};
let bundle_string = JSON.stringify(bundle);
const return_answer = await chrome.runtime.sendMessage(bundle);
//console.log(return_answer);
}
}
async function ibon_get_ocr_answer(api_url, image_data) {
let bundle = {
action: 'ocr',
data: {
'url': api_url + 'ocr',
'image_data': image_data,
}
};
let bundle_string = JSON.stringify(bundle);
const return_answer = await chrome.runtime.sendMessage(bundle);
//console.log(return_answer);
}
function ibon_orc_image_ready(api_url) {
let ret = false;
let image_data = ibon_get_ocr_image();
if (image_data.length > 0) {
ret = true;
if (myInterval) clearInterval(myInterval);
ibon_get_ocr_answer(api_url, image_data);
}
console.log("ibon_orc_image_ready:" + ret);
return ret;
}
storage.get('settings', function(items) {
if (items.settings) {
settings = items.settings;
} else {
console.log('no settings found');
}
});
storage.get('settings', function(items) {
if (items.settings) {
settings = items.settings;
}
});
function get_remote_url(settings) {
let remote_url_string = "";
if (settings) {
let remote_url_array = [];
if (settings.advanced.remote_url.length > 0) {
remote_url_array = JSON.parse('[' + settings.advanced.remote_url + ']');
}
if (remote_url_array.length) {
remote_url_string = remote_url_array[0];
}
}
return remote_url_string;
}
function start_ibon_ocr() {
// ocr
if (settings.ocr_captcha.enable) {
let remote_url_string = get_remote_url(settings);
if (!ibon_orc_image_ready(remote_url_string)) {
myInterval = setInterval(() => {
ibon_orc_image_ready(remote_url_string);
}, 100);
}
} else {
// no orc, just focus;
ibon_focus_on_captcha();
}
}
storage.get('status', function(items) {
if (items.status && items.status == 'ON') {
//console.log("ticket_number:"+ settings.ticket_number);
// ajax.
setTimeout(function() {
ibon_assign_ticket_number(settings.ticket_number);
ibon_assign_adjacent_seat(settings.advanced.disable_adjacent_seat);
}, 100);
} else {
console.log('no status found');
}
});

View File

@ -1,5 +1,4 @@
var myInterval = null; var myInterval = null;
function dom_ready() function dom_ready()
{ {
let ret=false; let ret=false;
@ -8,15 +7,29 @@ function dom_ready()
ret=true; ret=true;
if(myInterval) clearInterval(myInterval); if(myInterval) clearInterval(myInterval);
(function () { (function () {
$("div#ticket-wrap a[onclick]").click(); var btn_e = document.createEvent('MouseEvents');
btn_e.initEvent('click', true, true );
let btn1 = document.querySelector("div#ticket-wrap > a[onclick]");
if(btn1 > 0) {
console.log("trigger btn1 click");
//btn1.click();
let btn1 = document.querySelector("div#ticket-wrap > a[onclick]");
btn1.dispatchEvent(btn_e);
} else {
let btn2 = document.querySelector("div#ticket-wrap > a[href]");
if(btn2) {
console.log("trigger btn2 click");
//btn2.click();
btn2.dispatchEvent(btn_e);
}
}
})(); })();
} }
//console.log("dom_ready:"+ret); console.log("dom_ready:"+ret);
return ret; return ret;
} }
if(!dom_ready()) {
myInterval = setInterval(() => { myInterval = setInterval(() => {
dom_ready(); dom_ready();
}, 100); }, 1000);
}

View File

@ -1,8 +1,5 @@
const storage = chrome.storage.local; const storage = chrome.storage.local;
$("div.description").remove();
$("footer").remove();
function kktix_event_redirect() function kktix_event_redirect()
{ {
const currentUrl = window.location.href; const currentUrl = window.location.href;

View File

@ -1,5 +0,0 @@
$("div.headlines").remove();
$("section.app-intro").remove();
$("section.copywriting").remove();
$("section.partner-venues").remove();
$("footer").remove();

View File

@ -1,6 +1,4 @@
var myInterval = null; var myInterval = null;
var checkboxInterval = null;
//console.log("assign appear");
function kktix_verification_conditions(settings) function kktix_verification_conditions(settings)
{ {
@ -35,14 +33,6 @@ function kktix_verification_conditions(settings)
return is_text_sent; return is_text_sent;
} }
function kktix_agree()
{
$('input[type=checkbox]:not(:checked)').each(function() {
$(this).click();
if(checkboxInterval) clearInterval(checkboxInterval);
});
}
function kktix_area_keyword(settings) function kktix_area_keyword(settings)
{ {
let area_keyword_array = []; let area_keyword_array = [];
@ -91,20 +81,6 @@ function kktix_area_keyword(settings)
let ticket_number = settings.ticket_number; let ticket_number = settings.ticket_number;
if(ticket_number>0) { if(ticket_number>0) {
/*
// trigger events by jQuery.
let target_input = target_area.find("input");
target_input.click();
target_input.prop("value", ticket_number);
let down = $.Event('keydown');
down.key=""+ticket_number;
target_input.trigger(down);
let up = $.Event('keyup');
up.key=""+ticket_number;
target_input.trigger(up);
*/
//console.log(base_info); //console.log(base_info);
let is_verification_conditions_popup = false; let is_verification_conditions_popup = false;
@ -176,20 +152,8 @@ function dom_ready()
return ret; return ret;
} }
const rootElement = document.documentElement;
if(rootElement) {
if(!dom_ready()) { if(!dom_ready()) {
myInterval = setInterval(() => { myInterval = setInterval(() => {
dom_ready(); dom_ready();
}, 200); }, 200);
checkboxInterval= setInterval(() => {
//console.log("kktix_agree")
kktix_agree();
}, 200);
} }
$("footer").remove();
$("div.banner-wrapper div.img-wrapper img").remove();
}

View File

@ -1,6 +1,19 @@
const storage = chrome.storage.local; const storage = chrome.storage.local;
var settings = null; var settings = null;
var myInterval = null; var checkboxInterval = null;
var notNowInterval = null;
function kktix_agree()
{
$('input[type=checkbox]:not(:checked)').each(function() {
$(this).click();
});
}
function kktix_not_now()
{
$("#guestModal.modal.in > div.modal-dialog > div > div.modal-footer > button.btn-default.pull-right").click();
}
function kktix_clean_exclude(settings) function kktix_clean_exclude(settings)
{ {
@ -42,7 +55,7 @@ function kktix_force_auto_reload_by_timer()
if(max_dwell_time <= 10) { if(max_dwell_time <= 10) {
max_dwell_time = 10; max_dwell_time = 10;
} }
console.log('We are going to force reload after few seconeds.'); console.log('We are going to force reload after '+ max_dwell_time +' seconeds.');
setTimeout(function () { setTimeout(function () {
location.reload(); location.reload();
}, max_dwell_time * 1000); }, max_dwell_time * 1000);
@ -62,6 +75,13 @@ storage.get('status', function (items)
{ {
if (items.status && items.status=='ON') if (items.status && items.status=='ON')
{ {
checkboxInterval = setInterval(() => {
kktix_agree();
}, 100);
notNowInterval = setInterval(() => {
kktix_not_now();
}, 200);
kktix_force_auto_reload_by_timer(); kktix_force_auto_reload_by_timer();
setTimeout(function () { setTimeout(function () {

View File

@ -291,6 +291,16 @@
"js/ibon_ticket_next.js" "js/ibon_ticket_next.js"
] ]
}, },
{
"matches": [
"https://ticket.ibon.com.tw/EventBuy/*/*/*"
],
"run_at": "document_end",
"js": [
"jquery.min.js",
"js/ibon_eventbuy.js"
]
},
{ {
"matches": [ "matches": [
"https://orders.ibon.com.tw/application/utk02/UTK0201_0.aspx?*PERFORMANCE_ID=*PRODUCT_ID=*", "https://orders.ibon.com.tw/application/utk02/UTK0201_0.aspx?*PERFORMANCE_ID=*PRODUCT_ID=*",
@ -415,16 +425,6 @@
"js/ticketplus_order.js" "js/ticketplus_order.js"
] ]
}, },
{
"matches": [
"https://kktix.com/"
],
"run_at": "document_end",
"js": [
"jquery.min.js",
"js/kktix_home.js"
]
},
{ {
"matches": [ "matches": [
"https://*.kktix.cc/events/*" "https://*.kktix.cc/events/*"
@ -435,17 +435,6 @@
"js/kktix_events.js" "js/kktix_events.js"
] ]
}, },
{
"matches": [
"https://kktix.com/events/*/registrations/new"
],
"world": "MAIN",
"run_at": "document_end",
"js": [
"js/common.js",
"js/kktix_registrations_assign.js"
]
},
{ {
"matches": [ "matches": [
"https://kktix.com/events/*/registrations/new" "https://kktix.com/events/*/registrations/new"
@ -478,5 +467,5 @@
} }
], ],
"version": "1.0.28" "version": "1.0.29"
} }

View File

@ -21,7 +21,7 @@
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="false">偏好設定</button> <button class="nav-link" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="false" onclick="home_tab_clicked();">偏好設定</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
@ -176,6 +176,19 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-3">
<label for="refresh_datetime" class="col-sm-2 col-form-label">刷新在指定時間</label>
<div class="col-sm-10">
<input class="form-control" id="refresh_datetime" value="" />
<div class="bd-callout bd-callout-info">
<strong>提示:</strong>
時間格式 <code>HH:MM:SS</code> (小時:分鐘:秒數)。在設定介面修改並存檔,並不會套用在「已經」開出的瀏覽器,只會套用在存檔之後按「搶票」所開出的瀏覽器。
</div>
</div>
</div>
<div class="row mb-3"> <div class="row mb-3">
<label for="date_select_mode" class="col-sm-2 col-form-label">日期排序方式</label> <label for="date_select_mode" class="col-sm-2 col-form-label">日期排序方式</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -419,6 +432,14 @@
<textarea class="form-control" id="user_guess_string" rows="3"></textarea> <textarea class="form-control" id="user_guess_string" rows="3"></textarea>
</div> </div>
</div> </div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label form-check-label" for="auto_guess_options">自動猜測驗證問題</label>
<div class="col-sm-10">
<input class="form-check-input" type="checkbox" id="auto_guess_options" />
</div>
</div>
</div> </div>
<!-- tab 5 --> <!-- tab 5 -->

View File

@ -10,6 +10,7 @@ const resume_button = document.querySelector('#resume_btn');
// preference // preference
const homepage = document.querySelector('#homepage'); const homepage = document.querySelector('#homepage');
const ticket_number = document.querySelector('#ticket_number'); const ticket_number = document.querySelector('#ticket_number');
const refresh_datetime = document.querySelector('#refresh_datetime');
const date_select_mode = document.querySelector('#date_select_mode'); const date_select_mode = document.querySelector('#date_select_mode');
const date_keyword = document.querySelector('#date_keyword'); const date_keyword = document.querySelector('#date_keyword');
const area_select_mode = document.querySelector('#area_select_mode'); const area_select_mode = document.querySelector('#area_select_mode');
@ -51,6 +52,7 @@ const PUBLIC_SERVER_URL = "http://maxbot.dropboxlike.com:16888/";
// dictionary // dictionary
const user_guess_string = document.querySelector('#user_guess_string'); const user_guess_string = document.querySelector('#user_guess_string');
const auto_guess_options = document.querySelector('#auto_guess_options');
// auto fill // auto fill
@ -95,6 +97,7 @@ function load_settins_to_form(settings)
// preference // preference
homepage.value = settings.homepage; homepage.value = settings.homepage;
ticket_number.value = settings.ticket_number; ticket_number.value = settings.ticket_number;
refresh_datetime.value = settings.refresh_datetime;
date_select_mode.value = settings.date_auto_select.mode; date_select_mode.value = settings.date_auto_select.mode;
date_keyword.value = settings.date_auto_select.date_keyword; date_keyword.value = settings.date_auto_select.date_keyword;
if(date_keyword.value=='""') { if(date_keyword.value=='""') {
@ -156,6 +159,7 @@ function load_settins_to_form(settings)
if(user_guess_string.value=='""') { if(user_guess_string.value=='""') {
user_guess_string.value=''; user_guess_string.value='';
} }
auto_guess_options.checked = settings.advanced.auto_guess_options;
// auto fill // auto fill
tixcraft_sid.value = settings.advanced.tixcraft_sid; tixcraft_sid.value = settings.advanced.tixcraft_sid;
@ -235,6 +239,7 @@ function maxbot_reset_api()
settings = data; settings = data;
load_settins_to_form(data); load_settins_to_form(data);
check_unsaved_fields(); check_unsaved_fields();
run_message("已重設為預設值");
}) })
.fail(function() { .fail(function() {
//alert( "error" ); //alert( "error" );
@ -327,6 +332,7 @@ function save_changes_to_dict(silent_flag)
// preference // preference
settings.homepage = homepage.value; settings.homepage = homepage.value;
settings.ticket_number = ticket_number_value; settings.ticket_number = ticket_number_value;
settings.refresh_datetime = refresh_datetime.value;
settings.date_auto_select.mode = date_select_mode.value; settings.date_auto_select.mode = date_select_mode.value;
let date_keyword_string = date_keyword.value; let date_keyword_string = date_keyword.value;
@ -401,6 +407,8 @@ function save_changes_to_dict(silent_flag)
} }
settings.advanced.user_guess_string = user_guess_string_string; settings.advanced.user_guess_string = user_guess_string_string;
settings.advanced.auto_guess_options = auto_guess_options.checked;
// auto fill // auto fill
settings.advanced.tixcraft_sid = tixcraft_sid.value; settings.advanced.tixcraft_sid = tixcraft_sid.value;
settings.advanced.ibonqware = ibonqware.value; settings.advanced.ibonqware = ibonqware.value;
@ -506,7 +514,7 @@ function maxbot_save()
function check_unsaved_fields() function check_unsaved_fields()
{ {
if(settings) { if(settings) {
const field_list_basic = ["homepage","ticket_number","browser","webdriver_type"]; const field_list_basic = ["homepage","ticket_number","refresh_datetime","browser","webdriver_type"];
field_list_basic.forEach(f => { field_list_basic.forEach(f => {
const field = document.querySelector('#'+f); const field = document.querySelector('#'+f);
if(field.value != settings[f]) { if(field.value != settings[f]) {
@ -699,3 +707,8 @@ function run_message(msg)
message.innerText = ''; message.innerText = '';
}, 3000); }, 3000);
} }
function home_tab_clicked() {
console.log("clicked");
document.getElementById("homepage").focus();
}