diff --git a/.gitignore b/.gitignore index fd1aeca..45da775 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ srlandpic.py venv inquirer agree-terms -share.json \ No newline at end of file +share.json +language diff --git a/README.md b/README.md index b7d51ce..3fde326 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # BHYG +## FriendshipEnder 新功能 + +1. 多个NTP地址 + +2. 多语言 + +3. 购票人信息打码 (用*号) 隐私保护 + +4. 修复原作者的一部分bug和优化体验 + +5. 没有啦 + ## 总述 这是一款购票辅助工具。 diff --git a/api.py b/api.py index 1476211..91407d5 100644 --- a/api.py +++ b/api.py @@ -12,8 +12,12 @@ from i18n import i18n from utils import save, load +from globals import * class BilibiliHyg: + global sdk + global i18n_lang + from globals import i18n_lang def __init__(self, config, sdk,client,session): self.waited = False self.sdk = sdk @@ -39,14 +43,14 @@ def __init__(self, config, sdk,client,session): if self.client != None: self.ip = self.client.tps_current_ip(sign_type="hmacsha1") if self.config["mode"] == 'time': - logger.info("当前为定时抢票模式") - logger.info("等待到达开票时间前15分钟以获取token...") + logger.info(i18n[i18n_lang]["now_mode_time_on"]) + logger.info(i18n[i18n_lang]["get_token_15min"]) while self.get_time() < self.config["time"]-900: time.sleep(10) - logger.info("距离开票时间还有{:.2f}秒".format((self.config["time"]-self.get_time()))) - logger.info("准备完毕,获取token中...") + logger.info(i18n[i18n_lang]["now_waiting_info"].format((self.config["time"]-self.get_time()))) + logger.info(i18n[i18n_lang]["get_token_finish"]) self.token = self.get_token() - logger.info("即将开始下单") + logger.info(i18n[i18n_lang]["will_pay_bill"]) def get_time(self): return float(time.time() + self.config["time_offset"]) @@ -63,11 +67,11 @@ def get_ticket_status(self): requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError, ): - logger.error("网络连接超时") + logger.error(i18n[i18n_lang]["network_timeout"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -76,13 +80,11 @@ def get_ticket_status(self): return -1, 0 try: if response.status_code == 412: - logger.error( - "可能被业务风控\n该种业务风控请及时暂停,否则可能会引起更大问题。" - ) + logger.error(i18n[i18n_lang]["wind_control"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -90,12 +92,10 @@ def get_ticket_status(self): return self.get_ticket_status() else: self.risk = True - logger.error( - "你也可以尝试更换网络环境,如重启流量(飞行模式开关)重新拨号(重启光猫)等" - ) - input("请确认排除问题后按三下回车继续") - input("请再按两下回车继续") - input("请再按一下回车继续") + logger.error(i18n[i18n_lang]["net_method"]) + input(i18n[i18n_lang]["res_3_returns"]) + input(i18n[i18n_lang]["res_2_returns"]) + input(i18n[i18n_lang]["res_1_return"]) return -1, 0 screens = response.json()["data"]["screen_list"] # 找到 字段id为screen_id的screen @@ -105,7 +105,7 @@ def get_ticket_status(self): screen = screens[i] break if screen == {}: - logger.error("未找到场次") + logger.error(i18n[i18n_lang]["no_found_screen"]) return -1, 0 # 找到 字段id为sku_id的sku skus = screen["ticket_list"] @@ -115,11 +115,11 @@ def get_ticket_status(self): sku = skus[i] break if sku == {}: - logger.error("未找到票档") + logger.error(i18n[i18n_lang]["no_found_sku"]) return -1, 0 return int(sku["sale_flag_number"]), sku["clickable"] except: - logger.error("可能被风控") + logger.error(i18n[i18n_lang]["may_wind_control"]) return -1, 0 def get_prepare(self): @@ -143,11 +143,11 @@ def get_prepare(self): data["act_id"] = self.config["act_id"] response = self.session.post(url, headers=self.headers, data=data) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -192,7 +192,7 @@ def phone_verify(self, token): if "phone" in self.config: phone = self.config["phone"] else: - phone = input("请输入手机号:") + phone = input(i18n[i18n_lang]["input_phone_num"]+": ") self.captcha_data = { "code": phone, } @@ -208,7 +208,7 @@ def phone_verify(self, token): data=self.captcha_data, ).json()["data"]["is_valid"] if not success: - logger.error("验证失败") + logger.error(i18n[i18n_lang]["input_verify_fail"]) if "phone" in self.config: self.config.pop("phone") return False @@ -234,34 +234,34 @@ def confirm_info(self, token): ) response = self.session.get(url, headers=self.headers) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) self.session.close() return self.confirm_info(token) response = response.json() - logger.info("信息已确认") + logger.info(i18n[i18n_lang]["info_confirmed"]) logger.debug(response) self.config["order_type"] = response["data"]["order_type"] if response["data"]["act"] is not None: - logger.info("检测到优惠活动") + logger.info(i18n[i18n_lang]["info_discount"]) self.config["act_id"] = response["data"]["act"]["act_id"] return def get_token(self): info = self.get_prepare() if info == {}: - logger.warning("未开放购票或被风控,请检查配置问题,休息1s") + logger.warning(i18n[i18n_lang]["info_no_ticket"]) time.sleep(1) self.get_token() if info["token"]: logger.success( - "成功准备订单" + i18n[i18n_lang]["info_bill_ok"] + "https://show.bilibili.com/platform/confirmOrder.html?token=" + info["token"] ) @@ -273,10 +273,10 @@ def get_token(self): try: self.confirm_info(info["token"]) except: - logger.error("确认订单失败") + logger.error(i18n[i18n_lang]["info_bill_fail"]) return info["token"] else: - logger.warning("触发风控。") + logger.warning(i18n[i18n_lang]["info_wind_control"]) self.sdk.add_breadcrumb( category="gaia", message="Gaia found", @@ -386,7 +386,7 @@ def create_order(self): if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -400,7 +400,7 @@ def create_order(self): if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -427,11 +427,11 @@ def fake_ticket(self, pay_token, order_id = None): logger.debug(url) response = self.session.get(url, headers=self.headers) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) @@ -477,11 +477,11 @@ def order_status(self, order_id): url = "https://show.bilibili.com/api/ticket/order/info?order_id=" + str(order_id) response = self.session.get(url, headers=self.headers) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if self.config["proxy"]: if self.ip == self.client.tps_current_ip(sign_type="hmacsha1"): logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( self.client.change_tps_ip(sign_type="hmacsha1") ) ) diff --git a/geetest.py b/geetest.py index 48c90ae..a94bfa1 100644 --- a/geetest.py +++ b/geetest.py @@ -1,3 +1,4 @@ +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder import json import time @@ -6,7 +7,7 @@ import bili_ticket_gt_python from loguru import logger - +from globals import * # REF: https://github.com/mikumifa/biliTickerBuy # REF: https://github.com/Amorter/biliTicker_gt # LICENSE: GPL-3.0 @@ -45,7 +46,7 @@ def run(gt, challenge, token, mode="local_gt", key=None): "appkey": key, "gt": gt, "challenge": challenge, - "referer": "https://show.bilibili.com" + "referer": "https://show.bilibili.com", } try: response = requests.post("http://api.rrocr.com/api/recognize.html", data=param).json() diff --git a/globals.py b/globals.py index dc9b88e..d1562e2 100644 --- a/globals.py +++ b/globals.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # Contains global variables - +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder import sys import os @@ -14,28 +14,31 @@ from login import * +from globals import * from utility import utility from utils import prompt, save, load import time - +from i18n import i18n +i18n_lang = "中文" def agree_terms(): + global i18n_lang while True: agree_prompt = input( - "欢迎使用BHYG软件,使用前请阅读EULA(https://github.com/biliticket/BHYG)。若您使用时遇到问题,请查阅biliticket文档(https://docs.bitf1a5h.eu.org/)\n特别提醒,根据EULA,严禁任何形式通过本软件盈利。若您同意本软件EULA,请键入:我已阅读并同意EULA,黄牛倒卖狗死妈\n") + i18n[i18n_lang]["eula"]) if "同意" in agree_prompt and "死妈" in agree_prompt and "黄牛" in agree_prompt and "不" not in agree_prompt: break else: - logger.error("输入不正确,请重试") + logger.error(i18n[i18n_lang]["wrong_input"]) with open("agree-terms", "w") as f: import machineid f.write(machineid.id()) - logger.info("已同意EULA") - + logger.info(i18n[i18n_lang]["agree_eula"]) def init(): + global i18n_lang logger.remove(handler_id=0) if sys.argv[0].endswith(".py"): level = "DEBUG" @@ -62,7 +65,7 @@ def init(): agree_terms() with open("agree-terms", "w") as f: f.write(machineid.id()) - version = "v0.8.3" + version = "v0.8.4" sentry_sdk.init( dsn="https://9c5cab8462254a2e1e6ea76ffb8a5e3d@sentry-inc.bitf1a5h.eu.org/3", @@ -86,6 +89,7 @@ def init(): def check_update(version): + global i18n_lang try: import requests data = requests.get("https://api.github.com/repos/biliticket/BHYG/releases/latest", @@ -112,39 +116,39 @@ def check_update(version): for distribution in data["assets"]: if distribution["name"] == name: logger.warning( - f"发现新版本{data['tag_name']},请前往 {distribution['browser_download_url']} 下载并替换软件本体,大小:{distribution['size'] / 1024 / 1024:.2f}MB") + i18n[i18n_lang]["new_version_1"].format(data['tag_name'], distribution['browser_download_url'], distribution['size'] / 1024 / 1024)) if data['body'] != "": - logger.warning(f"更新说明:{data['body']}") + logger.warning(i18n[i18n_lang]["new_version_notify"].format(data['body'])) if "force" in data["body"] or "强制" in data["body"]: force = True find = True break if not find: - logger.warning(f"发现新版本{data['tag_name']},请前往{data['html_url']}查看") + logger.warning(i18n[i18n_lang]["new_version_2"].format(data['tag_name'], data['html_url'])) if data['body'] != "": - logger.warning(f"更新说明:{data['body']}") + logger.warning(i18n[i18n_lang]["new_version_notify"].format(data['body'])) if "force" in data["body"] or "强制" in data["body"]: force = True find = True if force: - logger.warning("由于反滥用机制,该更新要求强制更新,更新后继续使用") - logger.info("你可以打开下载地址后关闭本窗口") + logger.warning(i18n[i18n_lang]["force_update_1"]) + logger.info(i18n[i18n_lang]["force_update_2"]) while True: pass except KeyboardInterrupt: - logger.error("更新检查被中断") + logger.error(i18n[i18n_lang]["update_interrupted"]) raise KeyboardInterrupt except: try: - logger.warning("更新检查失败") + logger.warning(i18n[i18n_lang]["update_fail"]) if not os.path.exists("skip-update"): - logger.error("程序禁止运行,请重试或更换网络环境") + logger.error(i18n[i18n_lang]["force_require_update"]) while True: pass else: - logger.warning("已跳过更新检查") + logger.warning(i18n[i18n_lang]["update_passed"]) except KeyboardInterrupt: - logger.error("更新检查被中断") + logger.error(i18n[i18n_lang]["update_interrupted"]) raise KeyboardInterrupt @@ -153,9 +157,10 @@ class HygException(Exception): def load_config(): + global i18n_lang go_utility = False if os.path.exists("config.json"): - logger.info("感谢您升级到最新版本!现在正在为您自动迁移...") + logger.info(i18n[i18n_lang]["welcome_new_version"]) if os.path.isdir("data"): import shutil shutil.rmtree("data") @@ -163,9 +168,9 @@ def load_config(): config = json.load(f) save(config) os.remove("config.json") - logger.info("迁移完成") + logger.info(i18n[i18n_lang]["new_version_ok"]) if os.path.exists("share.json"): - logger.info("检测到分享文件,正在导入") + logger.info(i18n[i18n_lang]["check_share"]) with open("share.json", "r", encoding="utf-8") as f: config = json.load(f) save(config) @@ -177,14 +182,19 @@ def load_config(): run_info = prompt([ inquirer.List( "run_info", - message="请选择运行设置", - choices=["延续上次启动所有配置", "保留登录信息重新配置", "全新启动", "进入账户实用工具", - "进入账户实用工具(重新登录)"], - default="延续上次启动所有配置" + message=i18n[i18n_lang]["select_setting"], + choices=[i18n[i18n_lang]["select_keep_all"], + i18n[i18n_lang]["select_keep_login"], + i18n[i18n_lang]["select_new_boot"], + i18n[i18n_lang]["select_tools"], + i18n[i18n_lang]["select_tools_relogin"], + i18n[i18n_lang]["select_reset"], + "语言设置/Language setting"], + default= i18n[i18n_lang]["select_keep_all"] )] )["run_info"] - if run_info == "全新启动": - logger.info("全新启动,但继承部分信息(若有)") + if run_info == i18n[i18n_lang]["select_new_boot"]: + logger.info(i18n[i18n_lang]["select_new_boot_msg"]) temp = load() config = {} if "pushplus" in temp: @@ -202,8 +212,8 @@ def load_config(): if "proxy_channel" in temp: config["proxy_channel"] = temp["proxy_channel"] use_login = False - elif run_info == "保留登录信息重新配置": - logger.info("只沿用登录信息") + elif run_info == i18n[i18n_lang]["select_keep_login"]: + logger.info(i18n[i18n_lang]["select_keep_login_msg"]) temp = load() config = {} if "gaia_vtoken" in temp: @@ -227,38 +237,80 @@ def load_config(): if "proxy_channel" in temp: config["proxy_channel"] = temp["proxy_channel"] use_login = True - elif run_info == "延续上次启动所有配置": - logger.info("使用上次的配置文件") + elif run_info == i18n[i18n_lang]["select_keep_all"]: + logger.info(i18n[i18n_lang]["select_keep_all_msg"]) config = load() use_login = True - elif run_info == "进入账户实用工具": - logger.info("进入账户实用工具") + elif run_info == i18n[i18n_lang]["select_tools"]: + logger.info(i18n[i18n_lang]["select_tools"]) go_utility = True use_login = True config = load() - elif run_info == "进入账户实用工具(重新登录)": - logger.info("进入账户实用工具(重新登录)") + elif run_info == i18n[i18n_lang]["select_tools_relogin"]: + logger.info(i18n[i18n_lang]["select_tools_relogin"]) go_utility = True use_login = False config = {} + elif run_info == i18n[i18n_lang]["select_reset"]: + choice = prompt([inquirer.List("again", message=i18n[i18n_lang]["select_reset_msg"], + choices=[i18n[i18n_lang]["no"], i18n[i18n_lang]["yes"]], default=i18n[i18n_lang]["no"])])[ + "again"] + if choice == i18n[i18n_lang]["yes"]: + os.remove("language") + os.remove("data") + os.remove("agree-terms") + config = {} + logger.info(i18n[i18n_lang]["select_reset_ok"]) + else: + logger.info(i18n[i18n_lang]["select_reset_cancel"]) + return + elif run_info == "语言设置/Language setting": + i18n_lang = inquirer.prompt([ + inquirer.List( + name="lang_select", + message="Please select language", + choices=["中文", "English", "中文(猫娘)"], + )] + )["lang_select"] + with open("language", "w", encoding="utf-8") as f: + f.write(i18n_lang) + f.close + config = load() + go_utility = True + use_login = True else: save({}) config = {} import ntplib c = ntplib.NTPClient() - skip = False - try: - response = c.request('ntp.tencent.com') - except Exception: - logger.error("时间同步出现错误,将跳过时间检查") - skip = True - if skip == False: + ntp_servers = ( + "ntp.ntsc.ac.cn", #//Zhejiang ping: 27.75 ms + "time.pool.aliyun.com", #//Zhejiang ping: 32.5 ms + "time1.cloud.tencent.com", #//Zhejiang ping: 35 ms + "asia.pool.ntp.org", #//Zhejiang ping: 37 ms + "edu.ntp.org.cn", #//Zhejiang ping: 41 ms + "cn.ntp.org.cn", #//Zhejiang ping: 41 ms | ipv6 | 有时候抽风 + "cn.pool.ntp.org", #//Zhejiang ping: 50 ms | 有时候抽风 + "ntp.tuna.tsinghua.edu.cn", #//Zhejiang ping: 55 ms | ipv6 + "time.asia.apple.com", #//Zhejiang ping: 78.75 ms + "time.windows.com", #//Zhejiang ping: 89 ms + ) + skip = 0 + for i in range(10): + try: + response = c.request(ntp_servers[i], timeout=2) + except Exception: + skip += 1 + else: + break + if skip >= 10: + logger.error(i18n[i18n_lang]["time_sync_fail"]) + config["time_offset"] = 0 + else: time_offset = response.offset if time_offset > 0.5: - logger.warning(f"当前时间偏移:{time_offset:.2f}秒,建议校准时间") + logger.warning(i18n[i18n_lang]["time_sync_delta"].format(time_offset)) config["time_offset"] = time_offset - else: - config["time_offset"] = 0 while True: if "cookie" not in config or not use_login: config["cookie"] = interactive_login(sentry_sdk) @@ -271,10 +323,9 @@ def load_config(): ) user = user.json() if user["data"]["isLogin"]: - logger.success("用户 " + user["data"]["uname"] + " 登录成功") + logger.success(i18n[i18n_lang]["user"] +' '+ user["data"]["uname"] +' '+ i18n[i18n_lang]["login_success"]) if user["data"]["vipStatus"] != 0: - logger.info( - f"用户为大会员,距离到期还有{(user['data']['vipDueDate'] / 1000 - time.time()) / 60 / 60 / 24:.2f}天") + logger.info(i18n[i18n_lang]["user_bigvip"].format((user['data']['vipDueDate'] / 1000 - time.time()) / 60 / 60 / 24)) import machineid sentry_sdk.set_user( { @@ -283,12 +334,12 @@ def load_config(): } ) if "hunter" in config: - logger.success("已启用猎手模式") - logger.info(f"战绩:{config['hunter']}张") + logger.success(i18n[i18n_lang]["hunter_mode"]) + logger.info(i18n[i18n_lang]["hunter_grade"].format(config['hunter'])) save(config) break else: - logger.error("登录失败") + logger.error(i18n[i18n_lang]["login_failure"]) use_login = False config.pop("cookie") save(config) diff --git a/i18n.py b/i18n.py index 47dc988..685cfd0 100644 --- a/i18n.py +++ b/i18n.py @@ -1,5 +1,6 @@ +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder i18n = { - "zh": { + "中文": { "data_error": "数据错误,运行环境不符", "migrate_share": "检测到分享文件,正在迁移", "has_destroyed": "已销毁原数据", @@ -47,7 +48,7 @@ "buyer_empty": "未找到购票人,请前往实名添加购票人", "select_buyer": "请选择购票人", "selected_buyer": "已选择购票人:{} {} {}", - "show_all_price_e_ticket": "共 {} 张 {} 票,单张价格为 {},总价为{}", + "show_all_price_e_ticket": "共 {} 张 {} 票,单张价格为 CN¥{:.2f},总价为 CN¥{:.2f}", "id_bind_single": "本项目只能购买一人票", "qr_login": "请使用Bilibili手机客户端扫描二维码", "login_success": "登录成功", @@ -56,5 +57,544 @@ "exit_manual": "已手动退出", "error_occured": "程序出现错误,错误信息:{} 错误追踪ID:{}", "exit_sleep_15s": "已安全退出,您可以关闭窗口(将在15秒后自动关闭)", + "not_begin": "未开放购票", + "has_end_buy": "已停售", + "cannot_buy": "不可售", + "has_end": "已结束", + "sold_out": "已售罄", + "pro_tem_sold_out": "暂时售罄,即将放票", + "free_not_supported": "免费票,程序尚未适配", + "show_all_price_paper_ticket": "共 {} 张 {} 票,单张价格为 CN¥{:.2f},纸质票,邮费为 CN¥{:.2f},总价为 CN¥{:.2f}", + "unk_status": "未知状态:", + "now_mode_time_on": "当前为定时抢票模式", + "now_waiting_time": "等待到达开票时间...", + "now_waiting_info": "等待中,距离开票时间还有{:.2f}秒", + "now_wake_up": "唤醒!即将开始抢票!", + "add_address": "没有收货地址,请先添加收货地址", + "please_select_address": "请选择收货地址", + "already_select_address": "已选择收货地址: {} {} {}", + "add_contact_info": "请添加联系人信息", + "add_contact_name": "联系人姓名:", + "add_contact_tel": "联系人手机号", + "add_buy_tickets": "请输入票数", + "input_phone_num": "请输入手机号", + "input_auto_verify": "请稍后,正在执行自动验证...", + "input_verify_fail": "验证失败,请重新验证", + "input_verify_success": "验证成功", + "sms_code_send_ok": "验证码发送成功", + "input_sms_code": "请输入验证码", + "beta_test_func": "该方法尚在测试中", + "input_user_name": "请输入用户名", + "input_user_password": "请输入密码", + "request_too_slow": "PS: 请求超时,请快一点", + "need_2nd_verify": "需要二次验证", + "need_2nd_verify": "需要二次验证", + "phone_banded": "已经绑定手机号", + "will_send_sms": "即将给该手机号发送验证码: ", + "choose_sns_login": "请选择第三方客户端登录方式", + "sns_micromessage": "微信", + "sns_qq": "QQ", + "sns_microblog": "微博", + "open_in_browser": "请在浏览器中打开上面的链接并登录, 然后复制重定向的链接(即提示'校验失败,请重试~'的网址)", + "input_redirect": "请输入重定向链接", + "connect_link_error": "链接错误,请重新登录", + "connect_no_account": "未绑定SNS账号", + "bi_login_method": "请选择登录方法", + "bi_login_cookie": "cookie", + "bi_login_qrcode": "扫描二维码", + "bi_login_user_pass": "输入用户名和密码", + "bi_login_web_sms": "网页版短信验证码", + "bi_login_app_sms": "手机APP版短信验证码", + "bi_login_sns": "第三方客户端", + "bi_input_cookie": "请输入cookie: ", + "bi_illegal_cookie": "cookie不合法", + "eula": "欢迎使用BHYG软件,使用前请阅读EULA(https://github.com/biliticket/BHYG)。若您使用时遇到问题,请查阅biliticket文档(https://docs.bitf1a5h.eu.org/)\n特别提醒,根据EULA,严禁任何形式通过本软件盈利。若您同意本软件EULA,请键入:我已阅读并同意EULA,黄牛倒卖狗死妈\n", + "wrong_input": "输入不正确,请重试", + "agree_eula": "已同意EULA", + "new_version_1": "发现新版本{},请前往 {} 下载并替换软件本体,大小:{:.2f}MB", + "new_version_notify": "更新说明:{}", + "new_version_2": "发现新版本{},请前往 {} 查看", + "force_update_1": "由于反滥用机制,该更新要求强制更新,更新后继续使用", + "force_update_2": "你可以打开下载地址后关闭本窗口", + "update_interrupted": "更新检查被中断", + "update_fail": "更新检查失败", + "force_require_update": "程序禁止运行,请重试或更换网络环境", + "update_passed": "已跳过更新检查", + "welcome_new_version": "感谢您升级到最新版本!现在正在为您自动迁移...", + "new_version_ok": "迁移完成", + "check_share": "检测到分享文件,正在导入", + "select_setting": "请选择运行设置", + "select_keep_all": "延续上次启动所有配置", + "select_keep_login": "保留登录信息重新配置", + "select_new_boot": "全新启动", + "select_tools": "进入账户实用工具", + "select_tools_relogin": "进入账户实用工具(重新登录)", + "select_reset": "恢复初始设置", + "select_new_boot_msg": "全新启动,但继承部分信息(若有)", + "select_keep_login_msg": "只沿用登录信息", + "select_keep_all_msg": "使用上次的配置文件", + "select_reset_msg": "此操作将会清除所有数据并恢复初始设置,不可恢复,是否继续?", + "select_reset_ok": "已清除所有数据并恢复初始设置", + "select_reset_cancel": "取消恢复初始设置,请再次启动本程序。", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "time_sync_delta": "当前时间偏移:{:.2f}秒,建议校准时间", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "user": "用户", + "user_bigvip": "用户为大会员,距离到期还有{:.2f}天", + "hunter_mode": "已启用猎手模式", + "hunter_grade": "战绩:{}张", + "login_failure": "登录失败", + "buyer_name": "请输入购票人姓名:", + "id_type": "请选择证件类型", + "id_idcard": "请选择证件类型", + "id_passport": "请选择证件类型", + "id_Hong_Kong": "请选择证件类型", #Hong Kong-Macau laissez-passer + "id_Taiwan": "请选择证件类型", #Mainland travel permit for Taiwan residents + "in_id_serial_number": "请输入购票人证件号码:", + "in_phone_number": "请输入购票人手机号码:", + "join_success": "添加成功", + "modify_ua": "请输入您要覆盖的UA:", + "modify_gaia_vtoken": "请输入您的gaia_vtoken:", + "hunter_mode_on": "猎手模式已开启(归零)", + "hunter_mode_off": "猎手模式已关闭", + "share_mode": "分享模式已启动", + "auto_quit": "自动退出中……", + "pushplus_token": "请输入您的PushPlus Token(留空关闭):", + "pushplus_off": "PushPlus推送已关闭", + "pushplus_on": "PushPlus推送已开启", + "input_your_phone": "请输入您的手机号码:", + "save_your_phone": "手机号码已保存", + "input_rrocr_key": "请输入RROCR KEY:", + "select_tool" : "请选择您要使用的实用工具", + "tool_add_buyer" : "添加购票人", + "tool_modify_ua" : "覆盖默认UA", + "tool_modify_gaia" : "覆盖gaia_vtoken", + "tool_hunter_mode" : "开启猎手模式(计数清零)", + "tool_hunter_off" : "关闭猎手模式", + "tool_share_mode" : "分享模式", + "tool_pushplus" : "PushPlus推送", + "tool_phone_prefill": "预填绑定手机号", + "tool_proxy_setting": "代理设置", + "tool_capacha_mode" : "选择验证码模式", + "back" : "返回", + "tool_not_supported": "暂不支持此功能", + "get_token_15min": "等待到达开票时间前15分钟以获取token...", + "get_token_finish": "准备完毕, 获取token中...", + "will_pay_bill": "即将开始下单", + "network_timeout": "网络连接超时", + + "wind_control": "可能被业务风控\n该种业务风控请及时暂停,否则可能会引起更大问题。", + "net_method": "你也可以尝试更换网络环境,如重启流量(飞行模式开关)重新拨号(重启光猫)等", + "res_3_returns": "请确认排除问题后按三下回车继续", + "res_2_returns": "请再按两下回车继续", + "res_1_return": "请再按一下回车继续", + "no_found_screen": "未找到场次", + "no_found_sku": "未找到票档", + "may_wind_control": "可能被风控", + "info_confirmed": "信息已确认", + "info_discount": "检测到优惠活动", + "info_no_ticket": "未开放购票或被风控,请检查配置问题,休息1s", + "info_bill_ok": "成功准备订单", + "info_bill_fail": "确认订单失败", + "info_wind_control": "触发风控。", + }, + "English": { + "data_error": "Data error! Environment is not OK!", + "migrate_share": "Shared-data detected. Migrating shared-data.", + "has_destroyed": "Original data has destroyed safety.", + "pay_success": "Paid successfully!", + "hunter_prompt": "You've robbed {} ticket(s).", + "choose_mode": "Choose your ticket robbing mode", + "start_up": "Homepage: https://github.com/biliticket/BHYG GPL-3.0", + "mode_time": "Schedule ticket robbing based on project invoicing time.", + "mode_direct": "Direct robbing", + "mode_detect": "Detect-remain robbing", + "mode_time_on": "Timed robbing is on.", + "mode_direct_on": "Direct robbing is on.", + "mode_detect_on": "Detect-remain robbing is on.", + "input_status_delay": "Input detect-delay (412 ERROR probability)(sec)", + "input_is_use_proxy": "Use proxy.", + "input_proxy": "Input proxy info.", + "input_proxy_channel": "Input proxy channel (0=Don't specify)", + "test_proxy": "Trying to visit bilibili. IP: {}", + "input_is_allow_again": "Allow repeat order.", + "common_project_id": "Common projects' ID:", + "empty": "Empty", + "input_project_id": "Input your project id", + "not_handled_412": "412 ERROR! Contact BHYG owner.", + "manual_change_ip": "Manual switch IP to: {}.", + "project_id_not_found": "Project ID not found!", + "server_no_response": "Server no response.", + "not_salable": "Unsalable.", + "project_name": "Project name: {}.", + "captcha_mode_not_supported": "Unsupported CAPTCHA code mode.", + "input_use_captcha_mode": "Select the CAPTCHA code mode.", + "local_gt": "Local Geetest module.", + "rrocr": "RROCR", + "manual": "Manual", + "no_proxy_by_default": "Default is no proxy.", + "captcha_mode_gt_by_default": "Default is using local Geetest module.", + "wrong_proxy_format": "Wrong format.", + "no_screen": "No screen.", + "yes": "Yes", + "no": "No", + "select_screen": "Select a session.", + "select_sku": "Select an SKU.", + "show_screen": "Session: {}.", + "show_sku": "SKU: {}.", + "show_act": "Promotional is on. Activity ID: {}.", + "buyer_empty": "No buyer found. Please add buyer.", + "select_buyer": "Select buyer.", + "selected_buyer": "Selected buyer: {} {} {}", + "show_all_price_e_ticket": "Totally {} piece(s) {} ticket(s). One piece price: CN¥{:.2f},Totally price: CN¥{:.2f}", + "id_bind_single": "This project can only purchase one person tickets", + "qr_login": "Scan QR with Bilibili mobile app.", + "login_success": "Login successfully.", + "login_failed": "Login failed. Please change your login method or try again later.", + "login_not_supported": "This way is unsupported.", + "exit_manual": "Manually quit.", + "error_occured": "Program ERROR! {} ERROR ID: {}", + "exit_sleep_15s": "Quit safely. This window will be auto closed in 15s.", + "not_begin": "Not begin", + "has_end_buy": "Finish buying", + "cannot_buy": "Cannot buy", + "has_end": "Has finished", + "sold_out": "Sold out", + "pro_tem_sold_out": "Temporarily sold out", + "free_not_supported": "Free ticket. Temporarily not supported", + "show_all_price_paper_ticket": "Totally {} piece(s) {} ticket(s). One piece price: CN¥{:.2f} Postage: CN¥{:.2f}, Totally price: CN¥{:.2f}", + "unk_status": "Unknown status:", + "now_mode_time_on": "Now is timed robbing mode", + "now_waiting_time": "Waiting for sale...", + "now_waiting_info": "Waiting for sale... {:.2f}sec left", + "now_wake_up": "Wake up! About to rob tickets!", + "add_address": "No delivery address. Please add one.", + "please_select_address": "Select a delivery address.", + "already_select_address": "Selected delivery address: {} {} {}", + "add_contact_info": "Please add a contact information.", + "add_contact_name": "contact name:", + "add_contact_tel": "contact phone number:", + "add_buy_tickets": "How many tickets to buy:", + "input_phone_num": "请输入手机号", + "input_auto_verify": "请稍后,正在执行自动验证...", + "input_verify_fail": "验证失败,请重新验证", + "input_verify_success": "验证成功", + "sms_code_send_ok": "验证码发送成功", + "input_sms_code": "请输入验证码", + "beta_test_func": "该方法尚在测试中", + "input_user_name": "请输入用户名", + "input_user_password": "请输入密码", + "request_too_slow": "PS: 请求超时,请快一点", + "need_2nd_verify": "需要二次验证", + "need_2nd_verify": "需要二次验证", + "phone_banded": "已经绑定手机号", + "will_send_sms": "即将给该手机号发送验证码: ", + "choose_sns_login": "请选择第三方客户端登录方式", + "sns_micromessage": "微信", + "sns_qq": "QQ", + "sns_microblog": "微博", + "open_in_browser": "请在浏览器中打开上面的链接并登录, 然后复制重定向的链接(即提示'校验失败,请重试~'的网址)", + "input_redirect": "请输入重定向链接", + "connect_link_error": "链接错误,请重新登录", + "connect_no_account": "未绑定SNS账号", + "bi_login_method": "请选择登录方法", + "bi_login_cookie": "cookie", + "bi_login_qrcode": "扫描二维码", + "bi_login_user_pass": "输入用户名和密码", + "bi_login_web_sms": "网页版短信验证码", + "bi_login_app_sms": "手机APP版短信验证码", + "bi_login_sns": "第三方客户端", + "bi_input_cookie": "请输入cookie: ", + "bi_illegal_cookie": "cookie不合法", + "eula": "Welcome to use BHYG software. Please read EULA(https://github.com/biliticket/BHYG) first.\n" + "If you encounter any problems while using it, please refer to the Biliticket documentation(https://docs.bitf1a5h.eu.org/)\n" + "Special reminder, according to EULA, it is strictly prohibited to make profits through this software in any form.\n" + "If you agree, please copy below (press Control+Insert to copy and Shift+Insert to paste): 我已阅读并同意EULA,黄牛倒卖狗死妈\n", + "wrong_input": "输入不正确,请重试", + "agree_eula": "已同意EULA", + "new_version_1": "发现新版本{},请前往 {} 下载并替换软件本体,大小:{:.2f}MB", + "new_version_notify": "更新说明:{}", + "new_version_2": "发现新版本{},请前往 {} 查看", + "force_update_1": "由于反滥用机制,该更新要求强制更新,更新后继续使用", + "force_update_2": "你可以打开下载地址后关闭本窗口", + "update_interrupted": "更新检查被中断", + "update_fail": "更新检查失败", + "force_require_update": "程序禁止运行,请重试或更换网络环境", + "update_passed": "已跳过更新检查", + "welcome_new_version": "感谢您升级到最新版本!现在正在为您自动迁移...", + "new_version_ok": "迁移完成", + "check_share": "检测到分享文件,正在导入", + "select_setting": "请选择运行设置", + "select_keep_all": "延续上次启动所有配置", + "select_keep_login": "保留登录信息重新配置", + "select_new_boot": "全新启动", + "select_tools": "进入账户实用工具", + "select_tools_relogin": "进入账户实用工具(重新登录)", + "select_reset": "恢复初始设置", + "select_new_boot_msg": "全新启动,但继承部分信息(若有)", + "select_keep_login_msg": "只沿用登录信息", + "select_keep_all_msg": "使用上次的配置文件", + "select_reset_msg": "此操作将会清除所有数据并恢复初始设置,不可恢复,是否继续?", + "select_reset_ok": "已清除所有数据并恢复初始设置", + "select_reset_cancel": "取消恢复初始设置,请再次启动本程序。", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "time_sync_delta": "当前时间偏移:{:.2f}秒,建议校准时间", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "user": "用户", + "user_bigvip": "用户为大会员,距离到期还有{:.2f}天", + "hunter_mode": "已启用猎手模式", + "hunter_grade": "战绩:{}张", + "login_failure": "登录失败", + "buyer_name": "请输入购票人姓名:", + "id_type": "请选择证件类型", + "id_idcard": "请选择证件类型", + "id_passport": "请选择证件类型", + "id_Hong_Kong": "请选择证件类型", #Hong Kong-Macau laissez-passer + "id_Taiwan": "请选择证件类型", #Mainland travel permit for Taiwan residents + "in_id_serial_number": "请输入购票人证件号码:", + "in_phone_number": "请输入购票人手机号码:", + "join_success": "添加成功", + "modify_ua": "请输入您要覆盖的UA:", + "modify_gaia_vtoken": "请输入您的gaia_vtoken:", + "hunter_mode_on": "猎手模式已开启(归零)", + "hunter_mode_off": "猎手模式已关闭", + "share_mode": "分享模式已启动", + "auto_quit": "自动退出中……", + "pushplus_token": "请输入您的PushPlus Token(留空关闭):", + "pushplus_off": "PushPlus推送已关闭", + "pushplus_on": "PushPlus推送已开启", + "input_your_phone": "请输入您的手机号码:", + "save_your_phone": "手机号码已保存", + "input_rrocr_key": "请输入RROCR KEY:", + "select_tool" : "请选择您要使用的实用工具", + "tool_add_buyer" : "添加购票人", + "tool_modify_ua" : "覆盖默认UA", + "tool_modify_gaia" : "覆盖gaia_vtoken", + "tool_hunter_mode" : "开启猎手模式(计数清零)", + "tool_hunter_off" : "关闭猎手模式", + "tool_share_mode" : "分享模式", + "tool_pushplus" : "PushPlus推送", + "tool_phone_prefill": "预填绑定手机号", + "tool_proxy_setting": "代理设置", + "tool_capacha_mode" : "选择验证码模式", + "back" : "返回", + "tool_not_supported": "暂不支持此功能", + "get_token_15min": "等待到达开票时间前15分钟以获取token...", + "get_token_finish": "准备完毕, 获取token中...", + "will_pay_bill": "即将开始下单", + "network_timeout": "网络连接超时", + + "wind_control": "可能被业务风控\n该种业务风控请及时暂停,否则可能会引起更大问题。", + "net_method": "你也可以尝试更换网络环境,如重启流量(飞行模式开关)重新拨号(重启光猫)等", + "res_3_returns": "请确认排除问题后按三下回车继续", + "res_2_returns": "请再按两下回车继续", + "res_1_return": "请再按一下回车继续", + "no_found_screen": "未找到场次", + "no_found_sku": "未找到票档", + "may_wind_control": "可能被风控", + "info_confirmed": "信息已确认", + "info_discount": "检测到优惠活动", + "info_no_ticket": "未开放购票或被风控,请检查配置问题,休息1s", + "info_bill_ok": "成功准备订单", + "info_bill_fail": "确认订单失败", + "info_wind_control": "触发风控。", + }, + "中文(猫娘)": { + "data_error": "数据错误,运行环境不符", + "migrate_share": "检测到分享文件,正在迁移", + "has_destroyed": "已销毁原数据", + "pay_success": "购票成功!", + "hunter_prompt": "猎手,你的战绩:{}张", + "choose_mode": "请选择抢票模式", + "start_up": "项目主页: https://github.com/biliticket/BHYG GPL-3.0 删除本信息或盗版必究。", + "mode_time": "根据项目开票时间定时抢票", + "mode_direct": "直接抢票", + "mode_detect": "检测详情界面余票后抢票", + "mode_time_on": "定时抢票已开启", + "mode_direct_on": "直接抢票已开启", + "mode_detect_on": "检测详情界面余票后抢票已开启", + "input_status_delay": "请输入票务信息检测时间间隔(该选项影响412风控概率)(秒)", + "input_is_use_proxy": "是否使用代理", + "input_proxy": "请输入代理认证信息", + "input_proxy_channel": "请输入代理通道(0则不指定)", + "test_proxy": "尝试访问B站,当前IP为:{}", + "input_is_allow_again": "是否允许重复下单", + "common_project_id": "常用项目id如下:", + "empty": "暂无", + "input_project_id": "请输入项目id", + "not_handled_412": "被412风控,请联系作者", + "manual_change_ip": "手动切换,当前IP为:{}", + "project_id_not_found": "未找到项目ID", + "server_no_response": "服务器无返回", + "not_salable": "项目不可售", + "project_name": "项目名称:{}", + "captcha_mode_not_supported": "暂不支持该验证码模式", + "input_use_captcha_mode": "请选择验证码模式", + "local_gt": "本地GT模块", + "rrocr": "RROCR", + "manual": "手动", + "no_proxy_by_default": "默认不使用代理", + "captcha_mode_gt_by_default": "默认使用本地GT模块", + "wrong_proxy_format": "输入格式错误,请重新输入", + "no_screen": "暂无票档信息", + "yes": "是", + "no": "否", + "select_screen": "请选择场次", + "select_sku": "请选择票档", + "show_screen": "场次:{}", + "show_sku": "票档:{}", + "show_act": "已开启优惠活动:活动ID {}", + "buyer_empty": "未找到购票人,请前往实名添加购票人", + "select_buyer": "请选择购票人", + "selected_buyer": "已选择购票人:{} {} {}", + "show_all_price_e_ticket": "共 {} 张 {} 票,单张价格为 CN¥{:.2f},总价为 CN¥{:.2f}", + "id_bind_single": "本项目只能购买一人票", + "qr_login": "请使用Bilibili手机客户端扫描二维码", + "login_success": "登录成功", + "login_failed": "登录时出现错误,可能是风控导致的。请更换登录方式或稍后再试", + "login_not_supported": "暂不支持该登录方式", + "exit_manual": "已手动退出", + "error_occured": "程序出现错误,错误信息:{} 错误追踪ID:{}", + "exit_sleep_15s": "已安全退出,您可以关闭窗口(将在15秒后自动关闭)", + "not_begin": "未开放购票", + "has_end_buy": "已停售", + "cannot_buy": "不可售", + "has_end": "已结束", + "sold_out": "已售罄", + "pro_tem_sold_out": "暂时售罄,即将放票", + "free_not_supported": "免费票,程序尚未适配", + "show_all_price_paper_ticket": "共 {} 张 {} 票,单张价格为 CN¥{:.2f},纸质票,邮费为 CN¥{:.2f},总价为 CN¥{:.2f}", + "unk_status": "未知状态:", + "now_mode_time_on": "当前为定时抢票模式", + "now_waiting_time": "等待到达开票时间...", + "now_waiting_info": "等待中,距离开票时间还有{:.2f}秒", + "now_wake_up": "唤醒!即将开始抢票!", + "add_address": "没有收货地址,请先添加收货地址", + "please_select_address": "请选择收货地址", + "already_select_address": "已选择收货地址: {} {} {}", + "add_contact_info": "请添加联系人信息", + "add_contact_name": "联系人姓名:", + "add_contact_tel": "联系人手机号", + "add_buy_tickets": "请输入票数", + "input_phone_num": "请输入手机号", + "input_auto_verify": "请稍后,正在执行自动验证...", + "input_verify_fail": "验证失败,请重新验证", + "input_verify_success": "验证成功", + "sms_code_send_ok": "验证码发送成功", + "input_sms_code": "请输入验证码", + "beta_test_func": "该方法尚在测试中", + "input_user_name": "请输入用户名", + "input_user_password": "请输入密码", + "request_too_slow": "PS: 请求超时,请快一点", + "need_2nd_verify": "需要二次验证", + "need_2nd_verify": "需要二次验证", + "phone_banded": "已经绑定手机号", + "will_send_sms": "即将给该手机号发送验证码: ", + "choose_sns_login": "请选择第三方客户端登录方式", + "sns_micromessage": "微信", + "sns_qq": "QQ", + "sns_microblog": "微博", + "open_in_browser": "请在浏览器中打开上面的链接并登录, 然后复制重定向的链接(即提示'校验失败,请重试~'的网址)", + "input_redirect": "请输入重定向链接", + "connect_link_error": "链接错误,请重新登录", + "connect_no_account": "未绑定SNS账号", + "bi_login_method": "请选择登录方法", + "bi_login_cookie": "cookie", + "bi_login_qrcode": "扫描二维码", + "bi_login_user_pass": "输入用户名和密码", + "bi_login_web_sms": "网页版短信验证码", + "bi_login_app_sms": "手机APP版短信验证码", + "bi_login_sns": "第三方客户端", + "bi_input_cookie": "请输入cookie: ", + "bi_illegal_cookie": "cookie不合法", + "eula": "欢迎使用BHYG软件,使用前请阅读EULA(https://github.com/biliticket/BHYG)。若您使用时遇到问题,请查阅biliticket文档(https://docs.bitf1a5h.eu.org/)\n特别提醒,根据EULA,严禁任何形式通过本软件盈利。若您同意本软件EULA,请键入:我已阅读并同意EULA,黄牛倒卖狗死妈\n", + "wrong_input": "输入不正确,请重试", + "agree_eula": "已同意EULA", + "new_version_1": "发现新版本{},请前往 {} 下载并替换软件本体,大小:{:.2f}MB", + "new_version_notify": "更新说明:{}", + "new_version_2": "发现新版本{},请前往 {} 查看", + "force_update_1": "由于反滥用机制,该更新要求强制更新,更新后继续使用", + "force_update_2": "你可以打开下载地址后关闭本窗口", + "update_interrupted": "更新检查被中断", + "update_fail": "更新检查失败", + "force_require_update": "程序禁止运行,请重试或更换网络环境", + "update_passed": "已跳过更新检查", + "welcome_new_version": "感谢您升级到最新版本!现在正在为您自动迁移...", + "new_version_ok": "迁移完成", + "check_share": "检测到分享文件,正在导入", + "select_setting": "请选择运行设置", + "select_keep_all": "延续上次启动所有配置", + "select_keep_login": "保留登录信息重新配置", + "select_new_boot": "全新启动", + "select_tools": "进入账户实用工具", + "select_tools_relogin": "进入账户实用工具(重新登录)", + "select_reset": "恢复初始设置", + "select_new_boot_msg": "全新启动,但继承部分信息(若有)", + "select_keep_login_msg": "只沿用登录信息", + "select_keep_all_msg": "使用上次的配置文件", + "select_reset_msg": "此操作将会清除所有数据并恢复初始设置,不可恢复,是否继续?", + "select_reset_ok": "已清除所有数据并恢复初始设置", + "select_reset_cancel": "取消恢复初始设置,请再次启动本程序。", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "time_sync_delta": "当前时间偏移:{:.2f}秒,建议校准时间", + "time_sync_fail": "时间同步出现错误,将跳过时间检查", + "user": "用户", + "user_bigvip": "用户为大会员,距离到期还有{:.2f}天", + "hunter_mode": "已启用猎手模式", + "hunter_grade": "战绩:{}张", + "login_failure": "登录失败", + "buyer_name": "请输入购票人姓名:", + "id_type": "请选择证件类型", + "id_idcard": "请选择证件类型", + "id_passport": "请选择证件类型", + "id_Hong_Kong": "请选择证件类型", #Hong Kong-Macau laissez-passer + "id_Taiwan": "请选择证件类型", #Mainland travel permit for Taiwan residents + "in_id_serial_number": "请输入购票人证件号码:", + "in_phone_number": "请输入购票人手机号码:", + "join_success": "添加成功", + "modify_ua": "请输入您要覆盖的UA:", + "modify_gaia_vtoken": "请输入您的gaia_vtoken:", + "hunter_mode_on": "猎手模式已开启(归零)", + "hunter_mode_off": "猎手模式已关闭", + "share_mode": "分享模式已启动", + "auto_quit": "自动退出中……", + "pushplus_token": "请输入您的PushPlus Token(留空关闭):", + "pushplus_off": "PushPlus推送已关闭", + "pushplus_on": "PushPlus推送已开启", + "input_your_phone": "请输入您的手机号码:", + "save_your_phone": "手机号码已保存", + "input_rrocr_key": "请输入RROCR KEY:", + "select_tool" : "请选择您要使用的实用工具", + "tool_add_buyer" : "添加购票人", + "tool_modify_ua" : "覆盖默认UA", + "tool_modify_gaia" : "覆盖gaia_vtoken", + "tool_hunter_mode" : "开启猎手模式(计数清零)", + "tool_hunter_off" : "关闭猎手模式", + "tool_share_mode" : "分享模式", + "tool_pushplus" : "PushPlus推送", + "tool_phone_prefill": "预填绑定手机号", + "tool_proxy_setting": "代理设置", + "tool_capacha_mode" : "选择验证码模式", + "back" : "返回", + "tool_not_supported": "暂不支持此功能", + "get_token_15min": "等待到达开票时间前15分钟以获取token...", + "get_token_finish": "准备完毕, 获取token中...", + "will_pay_bill": "即将开始下单", + "network_timeout": "网络连接超时", + + "wind_control": "可能被业务风控\n该种业务风控请及时暂停,否则可能会引起更大问题。", + "net_method": "你也可以尝试更换网络环境,如重启流量(飞行模式开关)重新拨号(重启光猫)等", + "res_3_returns": "请确认排除问题后按三下回车继续", + "res_2_returns": "请再按两下回车继续", + "res_1_return": "请再按一下回车继续", + "no_found_screen": "未找到场次", + "no_found_sku": "未找到票档", + "may_wind_control": "可能被风控", + "info_confirmed": "信息已确认", + "info_discount": "检测到优惠活动", + "info_no_ticket": "未开放购票或被风控,请检查配置问题,休息1s", + "info_bill_ok": "成功准备订单", + "info_bill_fail": "确认订单失败", + "info_wind_control": "触发风控。", } } diff --git a/login.py b/login.py index 16ad663..da98291 100644 --- a/login.py +++ b/login.py @@ -1,3 +1,4 @@ +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder import base64 import json import time @@ -11,7 +12,7 @@ import inquirer from i18n import i18n - +from globals import * def cookie(cookies): lst = [] @@ -50,6 +51,8 @@ def _verify(gt, challenge, token): def qr_login(session, headers): + global i18n_lang + from globals import i18n_lang generate = session.get( "https://passport.bilibili.com/x/passport-login/web/qrcode/generate", headers=headers, @@ -65,7 +68,7 @@ def qr_login(session, headers): qr.print_ascii(invert=True) img = qr.make_image() img.show() - logger.info(i18n["zh"]["qr_login"]) + logger.info(i18n[i18n_lang]["qr_login"]) while True: time.sleep(1) url = ( @@ -76,7 +79,7 @@ def qr_login(session, headers): # read as utf-8 check = req.json()["data"] if check["code"] == 0: - logger.success(i18n["zh"]["login_success"]) + logger.success(i18n[i18n_lang]["login_success"]) cookies = requests.utils.dict_from_cookiejar(session.cookies) break elif check["code"] == 86101: @@ -96,6 +99,8 @@ def qr_login(session, headers): def verify_code_login(session, headers): + global i18n_lang + from globals import i18n_lang # https://passport.bilibili.com/x/passport-login/captcha captcha = session.get( "https://passport.bilibili.com/x/passport-login/captcha", headers=headers @@ -103,11 +108,11 @@ def verify_code_login(session, headers): gt = captcha["data"]["geetest"]["gt"] challenge = captcha["data"]["geetest"]["challenge"] token = captcha["data"]["token"] - tel = prompt([inquirer.Text("tel", message="请输入手机号", validate=lambda _, x: len(x) == 11)])["tel"] - logger.info("请稍后,正在执行自动验证...") + tel = prompt([inquirer.Text("tel", message=i18n[i18n_lang]["input_phone_num"], validate=lambda _, x: len(x) == 11)])["tel"] + logger.info(i18n[i18n_lang]["input_auto_verify"]) cap_data = _verify(gt, challenge, token) while cap_data == False: - logger.error("验证失败,请重新验证") + logger.error(i18n[i18n_lang]["input_verify_fail"]) captcha = session.post( "https://passport.bilibili.com/x/passport-login/captcha", headers=headers, @@ -116,7 +121,7 @@ def verify_code_login(session, headers): challenge = captcha["data"]["geetest"]["challenge"] token = captcha["data"]["token"] cap_data = _verify(gt, challenge, token) - logger.success("验证完成") + logger.success(i18n[i18n_lang]["input_verify_success"]) data = { "cid": "86", "tel": tel, @@ -135,10 +140,10 @@ def verify_code_login(session, headers): logger.error(f"{send['code']}: {send['message']}") return verify_code_login(session, headers) else: - logger.success("验证码发送成功") + logger.success(i18n[i18n_lang]["sms_code_send_ok"]) send_token = send["data"]["captcha_key"] while True: - code = prompt([inquirer.Text("code", message="请输入验证码", validate=lambda _, x: len(x) == 6)])["code"] + code = prompt([inquirer.Text("code", message=i18n[i18n_lang]["input_sms_code"], validate=lambda _, x: len(x) == 6)])["code"] # https://passport.bilibili.com/x/passport-login/web/login/sms data = {"cid": "86", "tel": tel, "captcha_key": send_token, "code": code} login = session.post( @@ -149,13 +154,15 @@ def verify_code_login(session, headers): if login["code"] != 0: logger.error(f"{login['code']}: {login['message']}") else: - logger.success(i18n["zh"]["login_success"]) + logger.success(i18n[i18n_lang]["login_success"]) cookies = requests.utils.dict_from_cookiejar(session.cookies) return cookie(cookies) def verify_code_login_app(session, headers): - logger.warning("该方法尚在测试中") + global i18n_lang + from globals import i18n_lang + logger.warning(i18n[i18n_lang]["beta_test_func"]) import uuid def buvid(): import hashlib @@ -175,11 +182,11 @@ def buvid(): # gt = captcha["data"]["geetest"]["gt"] # challenge = captcha["data"]["geetest"]["challenge"] # token = captcha["data"]["token"] - tel = prompt([inquirer.Text("tel", message="请输入手机号", validate=lambda _, x: len(x) == 11)])["tel"] - # logger.info("请稍后,正在执行自动验证...") + tel = prompt([inquirer.Text("tel", message=i18n[i18n_lang]["input_phone_num"], validate=lambda _, x: len(x) == 11)])["tel"] + # logger.info(i18n[i18n_lang]["input_auto_verify"]) # cap_data = _verify(gt, challenge, token) # while cap_data == False: - # logger.error("验证失败,请重新验证") + # logger.error(i18n[i18n_lang]["input_verify_fail"]) # captcha = session.post( # "https://passport.bilibili.com/x/passport-login/captcha", # headers=headers, @@ -188,7 +195,7 @@ def buvid(): # challenge = captcha["data"]["geetest"]["challenge"] # token = captcha["data"]["token"] # cap_data = _verify(gt, challenge, token) - logger.success("验证完成") + logger.success(i18n[i18n_lang]["input_verify_success"]) session_id = uuid.uuid4().hex.upper() buvid = buvid() data = { @@ -216,10 +223,10 @@ def buvid(): logger.error(f"{send['code']}: {send['message']}") return verify_code_login_app(session, headers) else: - logger.success("验证码发送成功") + logger.success(i18n[i18n_lang]["sms_code_send_ok"]) send_token = send["data"]["captcha_key"] while True: - code = prompt([inquirer.Text("code", message="请输入验证码", validate=lambda _, x: len(x) == 6)])["code"] + code = prompt([inquirer.Text("code", message=i18n[i18n_lang]["input_sms_code"], validate=lambda _, x: len(x) == 6)])["code"] # https://passport.bilibili.com/x/passport-login/login/sms data = {"cid": 86, "tel": int(tel), "captcha_key": send_token, "code": int(code), "login_session_id": session_id} @@ -231,24 +238,26 @@ def buvid(): if login["code"] != 0: logger.error(f"{login['code']}: {login['message']}") else: - logger.success("登录成功") + logger.success(i18n[i18n_lang]["login_success"]) cookies = requests.utils.dict_from_cookiejar(session.cookies) return cookie(cookies) def password_login(session, headers): + global i18n_lang + from globals import i18n_lang from Crypto.Cipher import PKCS1_v1_5 from Crypto.PublicKey import RSA - username = prompt([inquirer.Text("username", message="请输入用户名")])["username"] - password = prompt([inquirer.Password("password", message="请输入密码")])["password"] + username = prompt([inquirer.Text("username", message=i18n[i18n_lang]["input_user_name"])])["username"] + password = prompt([inquirer.Password("password", message=i18n[i18n_lang]["input_user_password"])])["password"] captcha = session.get( "https://passport.bilibili.com/x/passport-login/captcha", headers=headers ).json() gt = captcha["data"]["geetest"]["gt"] challenge = captcha["data"]["geetest"]["challenge"] token = captcha["data"]["token"] - logger.info("请稍后,正在执行自动验证...") + logger.info(i18n[i18n_lang]["input_auto_verify"]) cap_data = _verify(gt, challenge, token) while cap_data == False: captcha = session.get( @@ -258,9 +267,9 @@ def password_login(session, headers): gt = captcha["data"]["geetest"]["gt"] challenge = captcha["data"]["geetest"]["challenge"] token = captcha["data"]["token"] - logger.error("验证失败,请重新验证") + logger.error(i18n[i18n_lang]["input_verify_fail"]) cap_data = _verify(gt, challenge, token) - logger.success("验证完成") + logger.success(i18n[i18n_lang]["input_verify_success"]) key = session.get( "https://passport.bilibili.com/x/passport-login/web/key", headers=headers ).json()["data"] @@ -285,11 +294,11 @@ def password_login(session, headers): if login["code"] != 0: logger.error(f"{login['code']}: {login['message']}") if login["code"] == -662: - logger.error("PS: 请求超时,请快一点") + logger.error(i18n[i18n_lang]["request_too_slow"]) return password_login(session, headers) else: if login["data"]["status"] == 2 or login["data"]["status"] == 1: - logger.warning("需要二次验证") + logger.warning(i18n[i18n_lang]["need_2nd_verify"]) # extract tmp_code request_id from login["data"]["url"] tmp_token = login["data"]["url"].split("tmp_token=")[1][:32] try: @@ -307,9 +316,9 @@ def password_login(session, headers): headers=headers, ).json() if info["data"]["account_info"]["bind_tel"]: - logger.info("已绑定手机号") + logger.info(i18n[i18n_lang]["phone_banded"]) tel = info["data"]["account_info"]["hide_tel"] - logger.info("即将给该手机号发送验证码: " + tel) + logger.info(i18n[i18n_lang]["will_send_sms"] + tel) captcha = session.post( "https://passport.bilibili.com/x/safecenter/captcha/pre", headers=headers, @@ -317,10 +326,10 @@ def password_login(session, headers): gt = captcha["data"]["gee_gt"] challenge = captcha["data"]["gee_challenge"] token = captcha["data"]["recaptcha_token"] - logger.info("请稍后,正在执行自动验证...") + logger.info(i18n[i18n_lang]["input_auto_verify"]) cap_data = _verify(gt, challenge, token) while cap_data == False: - logger.error("验证失败,请重新验证") + logger.error(i18n[i18n_lang]["input_verify_fail"]) captcha = session.post( "https://passport.bilibili.com/x/safecenter/captcha/pre", headers=headers, @@ -329,7 +338,7 @@ def password_login(session, headers): challenge = captcha["data"]["gee_challenge"] token = captcha["data"]["recaptcha_token"] cap_data = _verify(gt, challenge, token) - logger.success("验证完成") + logger.success(i18n[i18n_lang]["input_verify_success"]) data = { "recaptcha_token": token, "gee_challenge": cap_data["challenge"], @@ -348,10 +357,10 @@ def password_login(session, headers): logger.error(f"{send['code']}: {send['message']}") return password_login(session, headers) else: - logger.success("验证码发送成功") + logger.success(i18n[i18n_lang]["sms_code_send_ok"]) send_token = send["data"]["captcha_key"] while True: - code = prompt([inquirer.Text("code", message="请输入验证码", validate=lambda _, x: len(x) == 6)])[ + code = prompt([inquirer.Text("code", message=i18n[i18n_lang]["input_sms_code"], validate=lambda _, x: len(x) == 6)])[ "code"] data = { "type": "loginTelCheck", @@ -368,7 +377,7 @@ def password_login(session, headers): if send["code"] != 0: logger.error(f"{send['code']}: {send['message']}") else: - logger.success(i18n["zh"]["login_success"]) + logger.success(i18n[i18n_lang]["login_success"]) code = send["data"]["code"] data = {"source": "risk", "code": code} session.post( @@ -378,23 +387,28 @@ def password_login(session, headers): ).json() cookies = requests.utils.dict_from_cookiejar(session.cookies) return cookie(cookies) - logger.success(i18n["zh"]["login_success"]) + logger.success(i18n[i18n_lang]["login_success"]) cookies = requests.utils.dict_from_cookiejar(session.cookies) return cookie(cookies) def sns_login(session, headers): + global i18n_lang + from globals import i18n_lang method = \ - prompt([inquirer.List("method", message="请选择SNS登录方式", choices=["微信", "QQ", "微博"], default="微信")])[ - "method"] - if method == "微信": + prompt([inquirer.List("method", message=i18n[i18n_lang]["choose_sns_login"],\ + choices=[i18n[i18n_lang]["sns_micromessage"],\ + i18n[i18n_lang]["sns_qq"],\ + i18n[i18n_lang]["sns_microblog"]],\ + default=i18n[i18n_lang]["sns_micromessage"])])["method"] + if method == i18n[i18n_lang]["sns_micromessage"]: sns = "wechat" - elif method == "QQ": + elif method == i18n[i18n_lang]["sns_qq"]: sns = "qq" - elif method == "微博": + elif method == i18n[i18n_lang]["sns_microblog"]: sns = "weibo" else: - logger.error("暂不支持此方式") + logger.error(i18n[i18n_lang]["login_not_supported"]) return sns_login(session, headers) # https://passport.bilibili.com/x/passport-login/web/sns/state/generate state = session.get( @@ -414,9 +428,9 @@ def sns_login(session, headers): data=data, ).json()["data"]["url"] logger.info(url) - logger.info("请在浏览器中打开上面的链接并登录, 然后复制重定向的链接(即提示'校验失败,请重试~'的网址)") + logger.info(i18n[i18n_lang]["open_in_browser"]) # https://passport.bilibili.com/x/passport-login/web/sns/login - redirect = prompt([inquirer.Text("redirect", message="请输入重定向链接")])["redirect"] + redirect = prompt([inquirer.Text("redirect", message=i18n[i18n_lang]["input_redirect"])])["redirect"] # get params from redirect try: redirect = redirect.split("?")[1] @@ -432,7 +446,7 @@ def sns_login(session, headers): "code": params["code"], } except Exception: - logger.error("链接错误,请重新登录") + logger.error(i18n[i18n_lang]["connect_link_error"]) return sns_login(session, headers) login = session.post( "https://passport.bilibili.com/x/passport-login/web/sns/login", @@ -443,14 +457,16 @@ def sns_login(session, headers): logger.error(f"{login['code']}: {login['message']}") else: if not login["data"]["has_bind"]: - logger.error("未绑定SNS账号") + logger.error(i18n[i18n_lang]["connect_no_account"]) return sns_login(session, headers) - logger.success(i18n["zh"]["login_success"]) + logger.success(i18n[i18n_lang]["login_success"]) cookies = requests.utils.dict_from_cookiejar(session.cookies) return cookie(cookies) def interactive_login(sentry_sdk=None): + global i18n_lang + from globals import i18n_lang global sdk sdk = sentry_sdk headers = { @@ -460,34 +476,36 @@ def interactive_login(sentry_sdk=None): session = requests.session() session.get("https://www.bilibili.com/", headers=headers) - try: - method = prompt([inquirer.List("method", message="请选择登录方式", - choices=["cookie", "扫码", "用户名密码", "验证码", "验证码APP版", "SNS"], - default="扫码")]) - if method["method"] == "cookie": - cookie_str = input("请输入cookie: ") + try: # 登录方式 cookie 扫码 用户名密码 web短信 app短信 sns + method = prompt([inquirer.List("method", message=i18n[i18n_lang]["bi_login_method"], + choices=[i18n[i18n_lang]["bi_login_cookie"], i18n[i18n_lang]["bi_login_qrcode"], \ + i18n[i18n_lang]["bi_login_user_pass"], i18n[i18n_lang]["bi_login_web_sms"], \ + i18n[i18n_lang]["bi_login_app_sms"], i18n[i18n_lang]["bi_login_sns"]], + default= i18n[i18n_lang]["bi_login_qrcode"])]) #默认扫码 + if method["method"] == i18n[i18n_lang]["bi_login_cookie"]: + cookie_str = input(i18n[i18n_lang]["bi_input_cookie"]) # verify cookie try: session.get("https://www.bilibili.com/", headers={"User-Agent": "Mozilla/5.0 BiliApp/80000100", "Cookie": cookie_str}) except Exception: - logger.error("cookie不合法") + logger.error(i18n[i18n_lang]["bi_illegal_cookie"]) return interactive_login() - elif method["method"] == "扫码": + elif method["method"] == i18n[i18n_lang]["bi_login_qrcode"]: cookie_str = qr_login(session, headers) - elif method["method"] == "用户名密码": + elif method["method"] == i18n[i18n_lang]["bi_login_user_pass"]: cookie_str = password_login(session, headers) - elif method["method"] == "验证码": + elif method["method"] == i18n[i18n_lang]["bi_login_web_sms"]: cookie_str = verify_code_login(session, headers) - elif method["method"] == "SNS": + elif method["method"] == i18n[i18n_lang]["bi_login_sns"]: cookie_str = sns_login(session, headers) - elif method["method"] == "验证码APP版": + elif method["method"] == i18n[i18n_lang]["bi_login_app_sms"]: cookie_str = verify_code_login_app(session, headers) else: - logger.error(i18n["zh"]["login_not_supported"]) + logger.error(i18n[i18n_lang]["login_not_supported"]) return interactive_login() except Exception as e: - logger.error(i18n["zh"]["login_failed"]) + logger.error(i18n[i18n_lang]["login_failed"]) return interactive_login() logger.debug("=" * 20) diff --git a/main.py b/main.py index dcaa2ef..23e5823 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ # -*- coding: UTF-8 -*- -# Copyright (c) 2023 ZianTT +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder import json import os import threading @@ -26,17 +26,18 @@ def run(hyg): + global i18n_lang if hyg.config["mode"] == 'direct': while True: if hyg.try_create_order(): if "hunter" not in hyg.config: hyg.sdk.capture_message("Pay success!") - logger.success(i18n["zh"]["pay_success"]) + logger.success(i18n[i18n_lang]["pay_success"]) return else: hyg.config['hunter'] += 1 save(hyg.config) - logger.success(i18n["zh"]["hunter_prompt"].format(hyg.config['hunter'])) + logger.success(i18n[i18n_lang]["hunter_prompt"].format(hyg.config['hunter'])) elif hyg.config["mode"] == 'detect': while 1: hyg.risk = False @@ -45,51 +46,51 @@ def run(hyg): status, clickable = hyg.get_ticket_status() if status == 2 or clickable: if status == 1: - logger.warning("未开放购票") + logger.warning(i18n[i18n_lang]["not_begin"]) elif status == 3: - logger.warning("已停售") + logger.warning(i18n[i18n_lang]["has_end_buy"]) elif status == 5: - logger.warning("不可售") + logger.warning(i18n[i18n_lang]["cannot_buy"]) elif status == 102: - logger.warning("已结束") + logger.warning(i18n[i18n_lang]["has_end"]) while True: if hyg.try_create_order(): if "hunter" not in hyg.config: hyg.sdk.capture_message("Pay success!") - logger.success("购票成功!") + logger.success(i18n[i18n_lang]["pay_success"]) return else: hyg.config['hunter'] += 1 save(hyg.config) - logger.success(f"猎手,你的战绩:{hyg.config['hunter']}张") + logger.success(i18n[i18n_lang]["hunter_prompt"].format(hyg.config['hunter'])) break elif status == 1: - logger.warning("未开放购票") + logger.warning(i18n[i18n_lang]["not_begin"]) elif status == 3: - logger.warning("已停售") + logger.warning(i18n[i18n_lang]["has_end_buy"]) elif status == 4: - logger.warning("已售罄") + logger.warning(i18n[i18n_lang]["sold_out"]) elif status == 5: - logger.warning("不可售") + logger.warning(i18n[i18n_lang]["cannot_buy"]) elif status == 6: - logger.error("免费票,程序尚未适配") + logger.error(i18n[i18n_lang]["free_not_supported"]) sentry_sdk.capture_message("Exit by in-app exit") return elif status == 8: - logger.warning("暂时售罄,即将放票") + logger.warning(i18n[i18n_lang]["pro_tem_sold_out"]) elif status == -1: continue else: - logger.error("未知状态:" + str(status)) + logger.error(i18n[i18n_lang]["unk_status"] + str(status)) time.sleep(hyg.config["status_delay"]) elif hyg.config["mode"] == 'time': - logger.info("当前为定时抢票模式") - logger.info("等待到达开票时间...") + logger.info(i18n[i18n_lang]["now_mode_time_on"]) + logger.info(i18n[i18n_lang]["now_waiting_time"]) while hyg.get_time() < hyg.config["time"] - 60: time.sleep(10) - logger.info(f"等待中,距离开票时间还有{hyg.config['time'] - get_time():.2f}秒") - logger.info("唤醒!即将开始抢票!") # Heads up, the wheels are spinning... + logger.info(i18n[i18n_lang]["now_waiting_info"].format(hyg.config['time'] - hyg.get_time())) + logger.info(i18n[i18n_lang]["now_wake_up"]) # Heads up, the wheels are spinning... while True: if hyg.get_time() >= hyg.config["time"]: break @@ -97,18 +98,38 @@ def run(hyg): if hyg.try_create_order(): if "hunter" not in hyg.config: hyg.sdk.capture_message("Pay success!") - logger.success("购票成功!") + logger.success(i18n[i18n_lang]["pay_success"]) return else: hyg.config['hunter'] += 1 save(hyg.config) - logger.success(f"猎手,你的战绩:{hyg.config['hunter']}张") + logger.success(i18n[i18n_lang]["hunter_prompt"].format(hyg.config['hunter'])) def main(): - easter_egg = False - print(i18n["zh"]["start_up"]) - global uid + global i18n_lang +# easter_egg = False +# user_male = False +# user_female = False + if os.path.exists("language"): #加载语言文件 + with open("language", "r", encoding="utf-8") as f: + i18n_lang = f.read() + print("Software language:", i18n_lang) + f.close + else: #加载语言文件不存在时, 创建一个语言文件 + i18n_lang = inquirer.prompt([ + inquirer.List( + name="lang_select", + message="Please select language", + choices=["中文", "English", "中文(猫娘)"], + )] + )["lang_select"] + with open("language", "w", encoding="utf-8") as f: + f.write(i18n_lang) + f.close + print(i18n[i18n_lang]["start_up"]) + global kdl_client + kdl_client = None try: version, sentry_sdk = init() session = requests.session() @@ -116,6 +137,8 @@ def main(): check_update(version) config = load_config() + if config == None: + return headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/618.1.15.10.15 (KHTML, like Gecko) Mobile/21F90 BiliApp/77900100 os/ios model/iPhone 15 mobi_app/iphone build/77900100 osVer/17.5.1 network/2 channel/AppStore c_locale/zh-Hans_CN s_locale/zh-Hans_CH disable_rcmd/0", "Cookie": config["cookie"], @@ -124,34 +147,33 @@ def main(): headers["User-Agent"] = config["user-agent"] session = requests.Session() if "mode" not in config: - mode_str = prompt([inquirer.List("mode", message=i18n["zh"]["choose_mode"], choices=[ - i18n["zh"]["mode_time"], i18n["zh"]["mode_direct"], i18n["zh"]["mode_detect"] - ], default=i18n["zh"]["mode_time"])])["mode"] - if mode_str == i18n["zh"]["mode_direct"]: + mode_str = prompt([inquirer.List("mode", message=i18n[i18n_lang]["choose_mode"], choices=[ + i18n[i18n_lang]["mode_time"], i18n[i18n_lang]["mode_direct"], i18n[i18n_lang]["mode_detect"] + ], default=i18n[i18n_lang]["mode_time"])])["mode"] + if mode_str == i18n[i18n_lang]["mode_direct"]: config["mode"] = 'direct' - logger.info(i18n["zh"]["mode_direct_on"]) - elif mode_str == i18n["zh"]["mode_detect"]: + logger.info(i18n[i18n_lang]["mode_direct_on"]) + elif mode_str == i18n[i18n_lang]["mode_detect"]: config["mode"] = 'detect' - logger.info(i18n["zh"]["mode_detect_on"]) + logger.info(i18n[i18n_lang]["mode_detect_on"]) else: config["mode"] = 'time' - logger.info(i18n["zh"]["mode_time_on"]) + logger.info(i18n[i18n_lang]["mode_time_on"]) if "status_delay" not in config and config["mode"] == 'detect': config["status_delay"] = float(prompt([ inquirer.Text( "status_delay", - message=i18n["zh"]["input_status_delay"], + message=i18n[i18n_lang]["input_status_delay"], default="0.2", validate=lambda _, x: float(x) >= 0 )])["status_delay"]) if "proxy" not in config: - logger.info(i18n["zh"]["no_proxy_by_default"]) + logger.info(i18n[i18n_lang]["no_proxy_by_default"]) config["proxy"] = False if "captcha" not in config: - logger.info(i18n["zh"]["captcha_mode_gt_by_default"]) + logger.info(i18n[i18n_lang]["captcha_mode_gt_by_default"]) config["captcha"] = "local_gt" config["rrocr"] = None - kdl_client = None if config["proxy"] == True: auth = kdl.Auth(config["proxy_auth"][0], config["proxy_auth"][1]) kdl_client = kdl.Client(auth) @@ -164,13 +186,13 @@ def main(): session.keep_alive = False session.get("https://show.bilibili.com") logger.info( - i18n["zh"]["test_proxy"].format(kdl_client.tps_current_ip(sign_type="hmacsha1")) + i18n[i18n_lang]["test_proxy"].format(kdl_client.tps_current_ip(sign_type="hmacsha1")) ) if "again" not in config: - choice = prompt([inquirer.List("again", message=i18n["zh"]["input_is_allow_again"], - choices=[i18n["zh"]["yes"], i18n["zh"]["no"]], default=i18n["zh"]["yes"])])[ + choice = prompt([inquirer.List("again", message=i18n[i18n_lang]["input_is_allow_again"], + choices=[i18n[i18n_lang]["yes"], i18n[i18n_lang]["no"]], default=i18n[i18n_lang]["yes"])])[ "again"] - if choice == i18n["zh"]["no"]: + if choice == i18n[i18n_lang]["no"]: config["again"] = False else: config["again"] = True @@ -182,7 +204,7 @@ def main(): or "id_bind" not in config ): while True: - logger.info(i18n["zh"]["common_project_id"]) + logger.info(i18n[i18n_lang]["common_project_id"]) for i in range(len(common_project_id)): logger.info( common_project_id[i]["name"] @@ -190,9 +212,9 @@ def main(): + str(common_project_id[i]["id"]) ) if len(common_project_id) == 0: - logger.info(i18n["zh"]["empty"]) + logger.info(i18n[i18n_lang]["empty"]) config["project_id"] = prompt([ - inquirer.Text("project_id", message=i18n["zh"]["input_project_id"], + inquirer.Text("project_id", message=i18n[i18n_lang]["input_project_id"], validate=lambda _, x: x.isdigit()) ])["project_id"] url = ( @@ -201,51 +223,51 @@ def main(): ) response = session.get(url, headers=headers) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if config["proxy"]: logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( kdl_client.change_tps_ip(sign_type="hmacsha1") ) ) session.close() response = response.json() if response["errno"] == 3: - logger.error(i18n["zh"]["project_id_not_found"]) + logger.error(i18n[i18n_lang]["project_id_not_found"]) continue if response["data"] == {}: - logger.error(i18n["zh"]["server_no_response"]) + logger.error(i18n[i18n_lang]["server_no_response"]) continue if "screen_list" not in response['data']: - logger.error(i18n["zh"]["no_screen"]) + logger.error(i18n[i18n_lang]["no_screen"]) continue if len(response["data"]["screen_list"]) == 0: - logger.error(i18n["zh"]["no_screen"]) + logger.error(i18n[i18n_lang]["no_screen"]) continue break - logger.info(i18n["zh"]["project_name"].format(response["data"]["name"])) + logger.info(i18n[i18n_lang]["project_name"].format(response["data"]["name"])) config["id_bind"] = response["data"]["id_bind"] config["is_paper_ticket"] = response["data"]["has_paper_ticket"] screens = response["data"]["screen_list"] screen_id = prompt([ - inquirer.List("screen_id", message=i18n["zh"]["select_screen"], + inquirer.List("screen_id", message=i18n[i18n_lang]["select_screen"], choices=[f"{i}. {screens[i]['name']}" for i in range(len(screens))]) ])["screen_id"].split(".")[0] - logger.info(i18n["zh"]["show_screen"].format(screens[int(screen_id)]["name"])) + logger.info(i18n[i18n_lang]["show_screen"].format(screens[int(screen_id)]["name"])) tickets = screens[int(screen_id)]["ticket_list"] # type: ignore sku_id = prompt([ - inquirer.List("sku_id", message=i18n["zh"]["select_sku"], + inquirer.List("sku_id", message=i18n[i18n_lang]["select_sku"], choices=[f"{i}. {tickets[i]['desc']} {tickets[i]['price'] / 100}元" for i in range(len(tickets))]) ])["sku_id"].split(".")[0] - logger.info(i18n["zh"]["show_sku"].format(tickets[int(sku_id)]["desc"])) + logger.info(i18n[i18n_lang]["show_sku"].format(tickets[int(sku_id)]["desc"])) config["screen_id"] = str(screens[int(screen_id)]["id"]) config["sku_id"] = str(tickets[int(sku_id)]["id"]) config["pay_money"] = str(tickets[int(sku_id)]["price"]) config["ticket_desc"] = str(tickets[int(sku_id)]["desc"]) config["time"] = int(tickets[int(sku_id)]["saleStart"]) if tickets[int(sku_id)]["discount_act"] is not None: - logger.info(i18n["zh"]["show_act"].format(tickets[int(sku_id)]["discount_act"]["act_id"])) + logger.info(i18n[i18n_lang]["show_act"].format(tickets[int(sku_id)]["discount_act"]["act_id"])) config["act_id"] = tickets[int(sku_id)]["discount_act"]["act_id"] config["order_type"] = tickets[int(sku_id)]["discount_act"]["act_type"] else: @@ -258,24 +280,26 @@ def main(): url = "https://show.bilibili.com/api/ticket/addr/list" resp_ticket = session.get(url, headers=headers) if resp_ticket.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) if config["proxy"]: logger.info( - i18n["zh"]["manual_change_ip"].format( + i18n[i18n_lang]["manual_change_ip"].format( kdl_client.change_tps_ip(sign_type="hmacsha1") ) ) session.close() addr_list = resp_ticket.json()["data"]["addr_list"] if len(addr_list) == 0: - logger.error("没有收货地址,请先添加收货地址") + logger.error(i18n[i18n_lang]["add_address"]) else: addr = prompt([ - inquirer.List("addr", message="请选择收货地址", choices=[f"{i}. {addr_list[i]['prov'] + addr_list[i]['city'] + addr_list[i]['area'] + addr_list[i]['addr']} {addr_list[i]['name']} {addr_list[i]['phone']}" for i in range(len(addr_list))]) + inquirer.List("addr", message=i18n[i18n_lang]["please_select_address"], \ + choices=[f"{i}. {addr_list[i]['prov'] + addr_list[i]['city'] + addr_list[i]['area'] + \ + addr_list[i]['addr']} {addr_list[i]['name']} {addr_list[i]['phone']}" for i in range(len(addr_list))]) ])["addr"].split(".")[0] addr = addr_list[int(addr)] - logger.info( - f"已选择收货地址:{addr['prov'] + addr['city'] + addr['area'] + addr['addr']} {addr['name']} {addr['phone']}" + logger.info( i18n[i18n_lang]["already_select_address"] + .format(addr['prov'] + addr['city'] + addr['area'] + addr['addr'], addr['name'], addr['phone']) ) config["deliver_info"] = json.dumps( { @@ -302,55 +326,81 @@ def main(): url = "https://show.bilibili.com/api/ticket/buyer/list" response = session.get(url, headers=headers) if response.status_code == 412: - logger.error(i18n["zh"]["not_handled_412"]) + logger.error(i18n[i18n_lang]["not_handled_412"]) buyer_infos = response.json()["data"]["list"] config["buyer_info"] = [] if len(buyer_infos) == 0: - logger.error(i18n["zh"]["buyer_empty"]) + logger.error(i18n[i18n_lang]["buyer_empty"]) return else: multiselect = True if config["id_bind"] == 1: - logger.info(i18n["zh"]["id_bind_single"]) + logger.info(i18n[i18n_lang]["id_bind_single"]) multiselect = False if multiselect: buyerids = prompt([ inquirer.Checkbox( "buyerids", - message=i18n["zh"]["select_buyer"], + message=i18n[i18n_lang]["select_buyer"], +# "*"*(len(buyer_infos[int(select)]["name"])-1)+ buyer_infos[int(select)]["name"][-1], +# buyer_infos[int(select)]["personal_id"][:4]+ "**********"+ buyer_infos[int(select)]["personal_id"][-4:], +# buyer_infos[int(select)]["tel"][:3]+ "****"+ buyer_infos[int(select)]["tel"][-4:], choices=[ - f"{i}. {buyer_infos[i]['name']} {buyer_infos[i]['personal_id']} {buyer_infos[i]['tel']}" for - i in range(len(buyer_infos))], + "{}. {} {} {}".format( + i, + "*"*(len(buyer_infos[i]["name"])-1)+ buyer_infos[i]["name"][-1], + buyer_infos[i]["personal_id"][:4]+ "**********"+ buyer_infos[i]["personal_id"][-4:], + buyer_infos[i]["tel"][:3]+ "****"+ buyer_infos[i]["tel"][-4:], + ) for i in range(len(buyer_infos))], validate=lambda _, x: len(x) > 0 ) ])["buyerids"] buyerids = [int(i.split(".")[0]) for i in buyerids] config["buyer_info"] = [] - female = False - male = False for select in buyerids: config["buyer_info"].append( buyer_infos[int(select)] ) logger.info( - i18n["zh"]["selected_buyer"].format( - buyer_infos[int(select)]["name"], - buyer_infos[int(select)]["personal_id"], - buyer_infos[int(select)]["tel"], + i18n[i18n_lang]["selected_buyer"].format( + "*"*(len(buyer_infos[int(select)]["name"])-1)+ buyer_infos[int(select)]["name"][-1], + buyer_infos[int(select)]["personal_id"][:4]+ "**********"+ buyer_infos[int(select)]["personal_id"][-4:], + buyer_infos[int(select)]["tel"][:3]+ "****"+ buyer_infos[int(select)]["tel"][-4:], ) ) +# if int(buyer_infos[int(select)]["personal_id"][16]) % 2 == 0: +# user_female = True +# else: +# user_male = True +# if easter_egg: +# if len(buyerids) == 1: +# logger.info("单身是这样的🤣 情(xiàn)侣(chōng)们只需要相互做搭子就可以逛的很开心, 可是一个人去逛漫展的人们需要考虑的事情就多了。") +# else: +# if user_male and user_female: +# logger.error("小情侣不得house😡") +# elif user_male and not user_female: +# logger.error("我朝,有南通啊!") +# if len(buyerids) == 4: +# logger.error("我朝,开impart啊!") +# elif user_female and not user_male: +# logger.error("我朝,有女同啊!") else: index = prompt([ - inquirer.List("index", message=i18n["zh"]["select_buyer"], choices=[ - f"{i}. {buyer_infos[i]['name']} {buyer_infos[i]['personal_id']} {buyer_infos[i]['tel']}" for i - in range(len(buyer_infos))]) + inquirer.List("index", message=i18n[i18n_lang]["select_buyer"], choices=[ + "{}. {} {} {}".format( + i, + "*"*(len(buyer_infos[i]["name"])-1)+ buyer_infos[i]["name"][-1], + buyer_infos[i]["personal_id"][:4]+ "**********"+ buyer_infos[i]["personal_id"][-4:], + buyer_infos[i]["tel"][:3]+ "****"+ buyer_infos[i]["tel"][-4:], + ) for i in range(len(buyer_infos)) + ]) ])["index"] config["buyer_info"].append(buyer_infos[int(index.split(".")[0])]) logger.info( - i18n["zh"]["selected_buyer"].format( - buyer_infos[int(index.split(".")[0])]["name"], - buyer_infos[int(index.split(".")[0])]["personal_id"], - buyer_infos[int(index.split(".")[0])]["tel"], + i18n[i18n_lang]["selected_buyer"].format( + "*"*(len(buyer_infos[int(select)]["name"])-1)+ buyer_infos[int(select)]["name"][-1], + buyer_infos[int(select)]["personal_id"][:4]+ "**********"+ buyer_infos[int(select)]["personal_id"][-4:], + buyer_infos[int(select)]["tel"][:3]+ "****"+ buyer_infos[int(select)]["tel"][-4:], ) ) if "count" not in config: @@ -359,14 +409,14 @@ def main(): if config["id_bind"] == 0 and ( "buyer" not in config or "tel" not in config ): - logger.info("请添加联系人信息") - config["buyer"] = input("联系人姓名:") + logger.info(i18n[i18n_lang]["add_contact_info"]) + config["buyer"] = input(i18n[i18n_lang]["add_contact_name"]) config["tel"] = prompt([ - inquirer.Text("tel", message="联系人手机号", validate=lambda _, x: len(x) == 11) + inquirer.Text("tel", message=i18n[i18n_lang]["add_contact_tel"], validate=lambda _, x: len(x) == 11) ])["tel"] if "count" not in config: config["count"] = prompt([ - inquirer.Text("count", message="请输入票数", default="1", + inquirer.Text("count", message=i18n[i18n_lang]["add_buy_tickets"], default="1", validate=lambda _, x: x.isdigit() and int(x) > 0) ])["count"] if config["is_paper_ticket"]: @@ -375,7 +425,8 @@ def main(): config["count"] ) logger.info( - f"共 {config['count']} 张 {config['ticket_desc']} 票,单张价格为 {int(config['pay_money']) / 100},纸质票,邮费免去,总价为{config['all_price'] / 100}" + i18n[i18n_lang]["show_all_price_paper_ticket"].format(config['count'],\ + config['ticket_desc'], int(config['pay_money']) / 100, 0, config['all_price'] / 100) ) else: config["all_price"] = ( @@ -383,14 +434,15 @@ def main(): + config["express_fee"] ) logger.info( - f"共 {config['count']} 张 {config['ticket_desc']} 票,单张价格为 {int(config['pay_money']) / 100},纸质票,邮费为 {config['express_fee'] / 100},总价为{config['all_price'] / 100}" + i18n[i18n_lang]["show_all_price_paper_ticket"].format(config['count'], config['ticket_desc'],\ + int(config['pay_money']) / 100, config['express_fee'] / 100, config['all_price'] / 100) ) else: config["all_price"] = int(config["pay_money"]) * int( config["count"] ) logger.info( - i18n["zh"]["show_all_price_e_ticket"].format( + i18n[i18n_lang]["show_all_price_e_ticket"].format( config["count"], config["ticket_desc"], int(config["pay_money"]) / 100, @@ -403,11 +455,11 @@ def main(): BHYG.waited = True run(BHYG) except KeyboardInterrupt: - logger.info(i18n["zh"]["exit_manual"]) + logger.info(i18n[i18n_lang]["exit_manual"]) return except Exception as e: track = sentry_sdk.capture_exception(e) - logger.error(i18n["zh"]["error_occured"].format(str(e), str(track))) + logger.error(i18n[i18n_lang]["error_occured"].format(str(e), str(track))) return return @@ -416,13 +468,13 @@ def main(): try: main() except KeyboardInterrupt: - logger.info(i18n["zh"]["exit_manual"]) + logger.info(i18n[i18n_lang]["exit_manual"]) from sentry_sdk import Hub client = Hub.current.client if client is not None: client.close(timeout=2.0) - logger.info(i18n["zh"]["exit_sleep_15s"]) + logger.info(i18n[i18n_lang]["exit_sleep_15s"]) try: time.sleep(15) except KeyboardInterrupt: diff --git a/main.spec b/main.spec index 4c154f6..03fc48e 100644 --- a/main.spec +++ b/main.spec @@ -1,37 +1,26 @@ # -*- mode: python ; coding: utf-8 -*- -from PyInstaller.utils.hooks import copy_metadata -import platform +from PyInstaller.utils.hooks import collect_all + +datas = [] +binaries = [] +hiddenimports = ['plyer.platforms.win.notification'] +tmp_ret = collect_all('readchar') +datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] -datas = copy_metadata("readchar") -if platform.system() == "Windows": - name = "BHYG-Windows" -elif platform.system() == "Linux": - name = "BHYG-Linux" -elif platform.system() == "Darwin": - print(platform.machine()) - if "arm" in platform.machine(): - name = "BHYG-macOS-Apple_Silicon" - elif "64" in platform.machine(): - name = "BHYG-macOS-Intel" - else: - name = "BHYG-macOS" -else: - name = "BHYG" a = Analysis( ['main.py'], pathex=[], - binaries=[], + binaries=binaries, datas=datas, - hiddenimports=[], + hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], - excludes=[], + excludes=['PyQt5'], noarchive=False, optimize=0, ) - pyz = PYZ(a.pure) exe = EXE( @@ -40,11 +29,11 @@ exe = EXE( a.binaries, a.datas, [], - name=name, + name='main', debug=False, bootloader_ignore_signals=False, strip=False, - upx=False, + upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, diff --git a/utility.py b/utility.py index 4a9b3e0..03f23f6 100644 --- a/utility.py +++ b/utility.py @@ -1,3 +1,4 @@ +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder import inquirer import requests from loguru import logger @@ -6,17 +7,22 @@ from i18n import i18n - +from globals import * def utility(config): + global i18n_lang + from globals import i18n_lang import base64 def add_buyer(headers): - name = input("请输入购票人姓名:") - id_type = prompt([inquirer.List("id_type", message="请选择证件类型", - choices=["0. 身份证", "1. 护照", "2. 港澳居民往来内地通行证", - "3. 台湾居民往来大陆通行证"], default="身份证"), + name = input(i18n[i18n_lang]["buyer_name"]) + id_type = prompt([inquirer.List("id_type", message=i18n[i18n_lang]["id_type"], + choices=[i18n[i18n_lang]["id_idcard"], + i18n[i18n_lang]["id_passport"], + i18n[i18n_lang]["id_Hong_Kong"], + i18n[i18n_lang]["id_Taiwan"]], + default=i18n[i18n_lang]["id_idcard"]), ]) - personal_id = input("请输入购票人证件号码:") - tel = input("请输入购票人手机号码:") + personal_id = input(i18n[i18n_lang]["in_id_serial_number"]) + tel = input(i18n[i18n_lang]["in_phone_number"]) data = { "name": name, "tel": tel, @@ -28,72 +34,72 @@ def add_buyer(headers): logger.debug(data) response = requests.post("https://show.bilibili.com/api/ticket/buyer/create", headers=headers, data=data) if response.json()["errno"] == 0: - logger.info("添加成功") + logger.info(i18n[i18n_lang]["join_success"]) else: logger.error(f"{response.json()['errno']}: {response.json()['msg']}") return add_buyer(headers) def modify_ua(): - ua = input("请输入您要覆盖的UA:") + ua = input(i18n[i18n_lang]["modify_ua"]) config["ua"] = ua def modify_gaia_vtoken(): - gaia_vtoken = input("请输入您的gaia_vtoken:") + gaia_vtoken = input(i18n[i18n_lang]["modify_gaia_vtoken"]) config["gaia_vtoken"] = gaia_vtoken def hunter_mode(): config["hunter"] = 0 - logger.info("猎手模式已开启(归零)") + logger.info(i18n[i18n_lang]["hunter_mode_on"]) def hunter_mode_off(): if "hunter" in config: config.pop("hunter") - logger.info("猎手模式已关闭") + logger.info(i18n[i18n_lang]["hunter_mode_off"]) def share_mode(config): import json json.dump(config, open("share.json", "w")) import os os.remove("data") - logger.info("分享模式已启动") - logger.info("自动退出中……") + logger.info(i18n[i18n_lang]["share_mode"]) + logger.info(i18n[i18n_lang]["auto_quit"]) import sys sys.exit(0) return def pushplus_config(config): - token = input("请输入您的PushPlus Token(留空关闭):") + token = input(i18n[i18n_lang]["pushplus_token"]) if token == "": if "pushplus" in config: config.pop("pushplus") - logger.info("PushPlus推送已关闭") + logger.info(i18n[i18n_lang]["pushplus_off"]) save(config) return config["pushplus"] = token - logger.info("PushPlus推送已开启") + logger.info(i18n[i18n_lang]["pushplus_on"]) save(config) def save_phone(config): - phone = input("请输入您的手机号码:") + phone = input(i18n[i18n_lang]["input_your_phone"]) config["phone"] = phone - logger.info("手机号码已保存") + logger.info(i18n[i18n_lang]["save_your_phone"]) save(config) def use_proxy(config): - choice = prompt([inquirer.List("proxy", message=i18n["zh"]["input_is_use_proxy"], - choices=[i18n["zh"]["yes"], i18n["zh"]["no"]], default=i18n["zh"]["no"])])[ + choice = prompt([inquirer.List("proxy", message=i18n[i18n_lang]["input_is_use_proxy"], + choices=[i18n[i18n_lang]["yes"], i18n[i18n_lang]["no"]], default=i18n[i18n_lang]["no"])])[ "proxy"] - if choice == i18n["zh"]["yes"]: + if choice == i18n[i18n_lang]["yes"]: while True: try: - config["proxy_auth"] = input(i18n["zh"]["input_proxy"]).split(" ") + config["proxy_auth"] = input(i18n[i18n_lang]["input_proxy"]).split(" ") assert len(config["proxy_auth"]) == 3 break except: - logger.error(i18n["zh"]["wrong_proxy_format"]) + logger.error(i18n[i18n_lang]["wrong_proxy_format"]) continue config["proxy_channel"] = prompt([ - inquirer.Text("proxy_channel", message=i18n["zh"]["input_proxy_channel"],validate=lambda _, x: x.isdigit()) + inquirer.Text("proxy_channel", message=i18n[i18n_lang]["input_proxy_channel"],validate=lambda _, x: x.isdigit()) ])["proxy_channel"] config["proxy"] = True else: @@ -101,20 +107,20 @@ def use_proxy(config): save(config) def captcha_mode(config): - choice = prompt([inquirer.List("captcha", message=i18n["zh"]["input_use_captcha_mode"], choices=[ - i18n["zh"]["local_gt"], - i18n["zh"]["rrocr"], - i18n["zh"]["manual"], - ], default=i18n["zh"]["manual"])])["captcha"] - if choice == i18n["zh"]["local_gt"]: + choice = prompt([inquirer.List("captcha", message=i18n[i18n_lang]["input_use_captcha_mode"], choices=[ + i18n[i18n_lang]["local_gt"], + i18n[i18n_lang]["rrocr"], + i18n[i18n_lang]["manual"], + ], default=i18n[i18n_lang]["manual"])])["captcha"] + if choice == i18n[i18n_lang]["local_gt"]: config["captcha"] = "local_gt" - elif choice == i18n["zh"]["rrocr"]: + elif choice == i18n[i18n_lang]["rrocr"]: config["captcha"] = "rrocr" - config["rrocr"] = input("请输入RROCR KEY:") - elif choice == i18n["zh"]["manual"]: + config["rrocr"] = input(i18n[i18n_lang]["input_rrocr_key"]) + elif choice == i18n[i18n_lang]["manual"]: config["captcha"] = "manual" else: - logger.error(i18n["zh"]["captcha_mode_not_supported"]) + logger.error(i18n[i18n_lang]["captcha_mode_not_supported"]) save(config) headers = { @@ -125,42 +131,51 @@ def captcha_mode(config): select = prompt([ inquirer.List( "select", - message="请选择您要使用的实用工具", - choices=["添加购票人", "覆盖默认UA", "覆盖gaia_vtoken", "开启猎手模式(计数清零)", "关闭猎手模式", - "分享模式", "PushPlus推送", "预填绑定手机号", "代理设置", "选择验证码模式", "返回"], + message= i18n[i18n_lang]["select_tool" ], + choices=[ i18n[i18n_lang]["tool_add_buyer" ], + i18n[i18n_lang]["tool_modify_ua" ], + i18n[i18n_lang]["tool_modify_gaia" ], + i18n[i18n_lang]["tool_hunter_mode" ], + i18n[i18n_lang]["tool_hunter_off" ], + i18n[i18n_lang]["tool_share_mode" ], + i18n[i18n_lang]["tool_pushplus" ], + i18n[i18n_lang]["tool_phone_prefill"], + i18n[i18n_lang]["tool_proxy_setting"], + i18n[i18n_lang]["tool_capacha_mode" ], + i18n[i18n_lang]["back" ]], )]) - if select["select"] == "添加购票人": + if select["select"] == i18n[i18n_lang]["tool_add_buyer" ]: add_buyer(headers) return utility(config) - elif select["select"] == "覆盖默认UA": + elif select["select"] == i18n[i18n_lang]["tool_modify_ua" ]: modify_ua() return utility(config) - elif select["select"] == "覆盖gaia_vtoken": + elif select["select"] == i18n[i18n_lang]["tool_modify_gaia" ]: modify_gaia_vtoken() return utility(config) - elif select["select"] == "开启猎手模式(计数清零)": + elif select["select"] == i18n[i18n_lang]["tool_hunter_mode" ]: hunter_mode() return utility(config) - elif select["select"] == "关闭猎手模式": + elif select["select"] == i18n[i18n_lang]["tool_hunter_off" ]: hunter_mode_off() return utility(config) - elif select["select"] == "分享模式": + elif select["select"] == i18n[i18n_lang]["tool_share_mode" ]: share_mode(config) return utility(config) - elif select["select"] == "PushPlus推送": + elif select["select"] == i18n[i18n_lang]["tool_pushplus" ]: pushplus_config(config) return utility(config) - elif select["select"] == "预填绑定手机号": + elif select["select"] == i18n[i18n_lang]["tool_phone_prefill"]: save_phone(config) return utility(config) - elif select["select"] == "代理设置": + elif select["select"] == i18n[i18n_lang]["tool_proxy_setting"]: use_proxy(config) return utility(config) - elif select["select"] == "选择验证码模式": + elif select["select"] == i18n[i18n_lang]["tool_capacha_mode" ]: captcha_mode(config) return utility(config) - elif select["select"] == "返回": + elif select["select"] == i18n[i18n_lang]["back" ]: return else: - logger.error("暂不支持此功能") + logger.error(i18n[i18n_lang]["tool_not_supported"]) return utility() diff --git a/utils.py b/utils.py index aeefe60..61f9609 100644 --- a/utils.py +++ b/utils.py @@ -1,3 +1,4 @@ +# Copyright (c) 2023-2024 ZianTT, FriendshipEnder def prompt(prompt): import inquirer data = inquirer.prompt(prompt) @@ -23,12 +24,16 @@ def save(data: dict): def load() -> dict: + from i18n import i18n + global i18n_lang + from globals import i18n_lang import base64 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad - from loguru import logger import machineid - import json,os + import json + from loguru import logger + import os key = machineid.id().encode()[:16] try: with open("data", "r", encoding="utf-8") as f: @@ -39,9 +44,9 @@ def load() -> dict: data = unpad(cipher.decrypt(cipher_text), AES.block_size).decode("utf-8") data = json.loads(data) except ValueError: - logger.error("数据错误,运行环境不符") + logger.error(i18n[i18n_lang]["data_error"]) if os.path.exists("share.json"): - logger.info("检测到分享文件,正在迁移") + logger.info(i18n[i18n_lang]["migrate_share"]) with open("share.json", "r", encoding="utf-8") as f: data = json.load(f) save(data) @@ -50,5 +55,5 @@ def load() -> dict: else: data = {} os.remove("data") - logger.info("已销毁原数据") + logger.info(i18n[i18n_lang]["has_destroyed"]) return data