Some checks failed
Deploy static content to Pages / deploy (push) Has been cancelled
662 lines
28 KiB
Python
662 lines
28 KiB
Python
import json
|
|
import threading
|
|
import time
|
|
import tkinter as tk
|
|
from pathlib import Path
|
|
from tkinter import filedialog, messagebox, ttk
|
|
|
|
from scripts.mock_dependency_manager import (
|
|
build_mock_steps,
|
|
build_report,
|
|
default_dependencies,
|
|
)
|
|
|
|
|
|
class TicketHelperGUI:
|
|
def __init__(self):
|
|
self.window = tk.Tk()
|
|
self.window.title("抢票助手 V5.0")
|
|
self.window.geometry("1280x900")
|
|
self.window.config(bg="#e3f2fd")
|
|
self.window.resizable(False, False)
|
|
|
|
self.style = ttk.Style()
|
|
self.style.configure(
|
|
"TButton",
|
|
font=("Arial", 12),
|
|
padding=8,
|
|
relief="flat",
|
|
background="#3498db",
|
|
foreground="white",
|
|
)
|
|
self.style.map("TButton", background=[("active", "#2980b9")])
|
|
self.style.configure("TCheckbutton", font=("Arial", 11), foreground="#333")
|
|
self.style.configure("TLabel", font=("Arial", 11), foreground="#333")
|
|
self.style.configure("TLabelframe.Label", font=("Arial", 12, "bold"))
|
|
|
|
self.create_menus()
|
|
self.create_widgets()
|
|
|
|
def create_menus(self):
|
|
menu_bar = tk.Menu(self.window)
|
|
file_menu = tk.Menu(menu_bar, tearoff=0)
|
|
file_menu.add_command(label="保存配置", command=self.save_config)
|
|
file_menu.add_command(label="加载配置", command=self.load_config)
|
|
file_menu.add_command(label="加载示例配置", command=self.load_demo_config)
|
|
file_menu.add_separator()
|
|
file_menu.add_command(label="退出", command=self.window.quit)
|
|
menu_bar.add_cascade(label="文件", menu=file_menu)
|
|
|
|
help_menu = tk.Menu(menu_bar, tearoff=0)
|
|
help_menu.add_command(label="关于", command=self.show_about)
|
|
menu_bar.add_cascade(label="帮助", menu=help_menu)
|
|
|
|
self.window.config(menu=menu_bar)
|
|
|
|
def create_widgets(self):
|
|
main_frame = tk.Frame(self.window, bg="#e3f2fd")
|
|
main_frame.place(relwidth=1, relheight=1)
|
|
|
|
title = ttk.Label(
|
|
main_frame,
|
|
text="抢票助手配置中心",
|
|
font=("Arial", 26, "bold"),
|
|
foreground="#2c3e50",
|
|
background="#e3f2fd",
|
|
)
|
|
title.pack(pady=20)
|
|
|
|
self.notebook = ttk.Notebook(main_frame)
|
|
self.notebook.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)
|
|
|
|
self.create_global_tab()
|
|
self.create_account_tab()
|
|
self.create_strategy_tab()
|
|
self.create_monitor_tab()
|
|
self.create_notification_tab()
|
|
self.create_plugin_tab()
|
|
self.create_dependency_tab()
|
|
|
|
control_frame = tk.LabelFrame(
|
|
main_frame,
|
|
text="任务控制",
|
|
font=("Arial", 12),
|
|
bg="#ffffff",
|
|
fg="#333",
|
|
padx=10,
|
|
pady=10,
|
|
)
|
|
control_frame.pack(fill=tk.X, padx=20, pady=5)
|
|
|
|
self.auto_buy_check_var = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(
|
|
control_frame,
|
|
text="启用自动抢票",
|
|
variable=self.auto_buy_check_var,
|
|
).grid(row=0, column=0, padx=10, pady=5, sticky="w")
|
|
|
|
self.start_button = ttk.Button(control_frame, text="开始抢票", command=self.start_ticket_task)
|
|
self.start_button.grid(row=0, column=1, padx=10)
|
|
self.stop_button = ttk.Button(
|
|
control_frame,
|
|
text="停止任务",
|
|
command=self.stop_ticket_task,
|
|
state=tk.DISABLED,
|
|
)
|
|
self.stop_button.grid(row=0, column=2, padx=10)
|
|
self.restart_button = ttk.Button(
|
|
control_frame,
|
|
text="重试任务",
|
|
command=self.retry_ticket_task,
|
|
state=tk.DISABLED,
|
|
)
|
|
self.restart_button.grid(row=0, column=3, padx=10)
|
|
|
|
status_frame = tk.Frame(main_frame, bg="#e3f2fd")
|
|
status_frame.pack(fill=tk.X, padx=20, pady=5)
|
|
|
|
self.status_label = ttk.Label(
|
|
status_frame,
|
|
text="当前状态: 未开始",
|
|
font=("Arial", 11),
|
|
background="#e3f2fd",
|
|
foreground="#2c3e50",
|
|
)
|
|
self.status_label.grid(row=0, column=0, padx=10)
|
|
|
|
self.progress_bar = ttk.Progressbar(status_frame, length=240, mode="determinate", maximum=100)
|
|
self.progress_bar.grid(row=0, column=1, padx=10)
|
|
|
|
self.progress_label = ttk.Label(
|
|
status_frame,
|
|
text="进度: 0%",
|
|
font=("Arial", 11),
|
|
background="#e3f2fd",
|
|
foreground="#2c3e50",
|
|
)
|
|
self.progress_label.grid(row=0, column=2, padx=10)
|
|
|
|
log_frame = tk.LabelFrame(
|
|
main_frame,
|
|
text="日志输出",
|
|
font=("Arial", 12),
|
|
bg="#ffffff",
|
|
fg="#333",
|
|
padx=10,
|
|
pady=10,
|
|
)
|
|
log_frame.pack(fill=tk.BOTH, padx=20, pady=10)
|
|
|
|
self.log_text = tk.Text(
|
|
log_frame,
|
|
height=8,
|
|
width=120,
|
|
font=("Arial", 11),
|
|
wrap=tk.WORD,
|
|
bg="#f8f9fa",
|
|
state=tk.DISABLED,
|
|
)
|
|
self.log_text.grid(row=0, column=0, padx=10, pady=5)
|
|
|
|
def create_global_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="全局设置")
|
|
|
|
self.global_log_level = self._add_labeled_combo(
|
|
tab,
|
|
0,
|
|
"日志等级",
|
|
["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
default="DEBUG",
|
|
)
|
|
self.global_timezone = self._add_labeled_entry(tab, 1, "时区", "Asia/Shanghai")
|
|
self.global_ntp_servers = self._add_labeled_entry(tab, 2, "NTP服务器(逗号分隔)", "time.google.com,ntp.aliyun.com")
|
|
|
|
dashboard_frame = tk.LabelFrame(tab, text="Dashboard", bg="#ffffff", padx=10, pady=10)
|
|
dashboard_frame.grid(row=3, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
dashboard_frame.columnconfigure(1, weight=1)
|
|
|
|
self.dashboard_enable = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(dashboard_frame, text="启用可视化面板", variable=self.dashboard_enable).grid(
|
|
row=0, column=0, sticky="w", padx=5, pady=5
|
|
)
|
|
self.dashboard_host = self._add_labeled_entry(dashboard_frame, 1, "面板地址", "0.0.0.0")
|
|
self.dashboard_port = self._add_labeled_entry(dashboard_frame, 2, "面板端口", "8765")
|
|
|
|
def create_account_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="账户配置")
|
|
|
|
self.account_platform = self._add_labeled_combo(
|
|
tab,
|
|
0,
|
|
"平台",
|
|
["taopiaopiao", "maoyan", "showstart", "piaoxingqiu", "others"],
|
|
default="taopiaopiao",
|
|
)
|
|
self.account_mobile = self._add_labeled_entry(tab, 1, "手机号", "")
|
|
self.account_password = self._add_labeled_entry(tab, 2, "密码", "")
|
|
self.account_otp = self._add_labeled_entry(tab, 3, "OTP Secret(可选)", "")
|
|
|
|
target_frame = tk.LabelFrame(tab, text="目标信息", bg="#ffffff", padx=10, pady=10)
|
|
target_frame.grid(row=4, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
target_frame.columnconfigure(1, weight=1)
|
|
|
|
self.target_event_url = self._add_labeled_entry(target_frame, 0, "演出链接", "")
|
|
self.target_date_priorities = self._add_labeled_entry(target_frame, 1, "日期优先级(逗号)", "1,2")
|
|
self.target_session_priorities = self._add_labeled_entry(target_frame, 2, "场次优先级(逗号)", "1")
|
|
self.target_price_range = self._add_labeled_combo(
|
|
target_frame,
|
|
3,
|
|
"票价策略",
|
|
["lowest_to_highest", "highest_to_lowest", "custom"],
|
|
default="lowest_to_highest",
|
|
)
|
|
self.target_tickets = self._add_labeled_entry(target_frame, 4, "购票数量", "2")
|
|
self.target_viewers = self._add_labeled_entry(target_frame, 5, "观影人索引(逗号)", "0,1")
|
|
|
|
proxy_frame = tk.LabelFrame(tab, text="代理设置", bg="#ffffff", padx=10, pady=10)
|
|
proxy_frame.grid(row=5, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
proxy_frame.columnconfigure(1, weight=1)
|
|
|
|
self.proxy_type = self._add_labeled_combo(
|
|
proxy_frame,
|
|
0,
|
|
"代理类型",
|
|
["socks5", "http", "https"],
|
|
default="socks5",
|
|
)
|
|
self.proxy_addr = self._add_labeled_entry(proxy_frame, 1, "代理地址(user:pass@host:port)", "")
|
|
self.proxy_rotate = self._add_labeled_entry(proxy_frame, 2, "轮换间隔(秒)", "300")
|
|
|
|
def create_strategy_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="策略设置")
|
|
|
|
self.strategy_auto = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(tab, text="启用自动出手", variable=self.strategy_auto, background="#ffffff").grid(
|
|
row=0, column=0, sticky="w", padx=10, pady=5
|
|
)
|
|
self.strategy_time = self._add_labeled_entry(tab, 1, "开抢时间(ISO)", "2026-01-25T12:00:00")
|
|
self.strategy_preheat = self._add_labeled_entry(tab, 2, "预热阶段(秒,逗号)", "5.0,2.0,0.5")
|
|
|
|
self.strategy_ai = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(tab, text="启用AI决策", variable=self.strategy_ai, background="#ffffff").grid(
|
|
row=3, column=0, sticky="w", padx=10, pady=5
|
|
)
|
|
self.strategy_ai_model = self._add_labeled_entry(tab, 4, "AI模型路径", "models/lstm_onnx.onnx")
|
|
self.strategy_max_retries = self._add_labeled_entry(tab, 5, "最大重试次数", "180")
|
|
self.strategy_retry_backoff = self._add_labeled_combo(
|
|
tab,
|
|
6,
|
|
"重试策略",
|
|
["exponential", "fixed"],
|
|
default="exponential",
|
|
)
|
|
|
|
def create_monitor_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="监控设置")
|
|
|
|
self.monitor_enable = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(tab, text="启用库存监控", variable=self.monitor_enable, background="#ffffff").grid(
|
|
row=0, column=0, sticky="w", padx=10, pady=5
|
|
)
|
|
self.monitor_poll_interval = self._add_labeled_entry(tab, 1, "轮询间隔(秒)", "1.5")
|
|
|
|
trigger_frame = tk.LabelFrame(tab, text="触发条件(每行一条)", bg="#ffffff", padx=10, pady=10)
|
|
trigger_frame.grid(row=2, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
self.monitor_triggers = tk.Text(trigger_frame, height=5, width=60, font=("Arial", 10))
|
|
self.monitor_triggers.insert(tk.END, "price_drop > 10%\ntickets_added > 0\nstatus_change: soldout -> available")
|
|
self.monitor_triggers.pack(fill=tk.X)
|
|
|
|
def create_notification_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="通知设置")
|
|
|
|
telegram_frame = tk.LabelFrame(tab, text="Telegram", bg="#ffffff", padx=10, pady=10)
|
|
telegram_frame.grid(row=0, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
telegram_frame.columnconfigure(1, weight=1)
|
|
|
|
self.telegram_token = self._add_labeled_entry(telegram_frame, 0, "Bot Token", "")
|
|
self.telegram_chat = self._add_labeled_entry(telegram_frame, 1, "Chat ID", "")
|
|
|
|
email_frame = tk.LabelFrame(tab, text="Email", bg="#ffffff", padx=10, pady=10)
|
|
email_frame.grid(row=1, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
email_frame.columnconfigure(1, weight=1)
|
|
|
|
self.email_smtp = self._add_labeled_entry(email_frame, 0, "SMTP地址", "smtp.example.com:465")
|
|
self.email_user = self._add_labeled_entry(email_frame, 1, "邮箱账号", "")
|
|
self.email_pass = self._add_labeled_entry(email_frame, 2, "邮箱密码", "")
|
|
self.email_recipients = self._add_labeled_entry(email_frame, 3, "收件人(逗号)", "user@domain.com")
|
|
|
|
def create_plugin_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="插件扩展")
|
|
|
|
ttk.Label(tab, text="自定义插件(逗号分隔)", background="#ffffff").grid(
|
|
row=0, column=0, padx=10, pady=10, sticky="w"
|
|
)
|
|
self.plugins_custom = ttk.Entry(tab, width=60, font=("Arial", 11))
|
|
self.plugins_custom.insert(0, "my_adapter.py,extra_notifier.py")
|
|
self.plugins_custom.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
|
|
|
|
def create_dependency_tab(self):
|
|
tab = tk.Frame(self.notebook, bg="#ffffff")
|
|
self.notebook.add(tab, text="依赖管理")
|
|
|
|
description = (
|
|
"本项目用于模拟与学习。可在此配置“伪安装”依赖清单,\n"
|
|
"点击按钮将以日志方式模拟安装流程,不会真实下载或执行安装。"
|
|
)
|
|
ttk.Label(tab, text=description, background="#ffffff", foreground="#555").grid(
|
|
row=0, column=0, columnspan=2, padx=10, pady=10, sticky="w"
|
|
)
|
|
|
|
deps_frame = tk.LabelFrame(tab, text="依赖清单(每行一条)", bg="#ffffff", padx=10, pady=10)
|
|
deps_frame.grid(row=1, column=0, columnspan=2, sticky="ew", padx=10, pady=10)
|
|
self.dependency_list = tk.Text(deps_frame, height=8, width=70, font=("Arial", 10))
|
|
self.dependency_list.insert(tk.END, "\n".join(default_dependencies()) + "\n")
|
|
self.dependency_list.pack(fill=tk.X)
|
|
|
|
self.dep_auto_install = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(
|
|
tab,
|
|
text="启动时自动模拟安装",
|
|
variable=self.dep_auto_install,
|
|
background="#ffffff",
|
|
).grid(row=2, column=0, padx=10, pady=5, sticky="w")
|
|
|
|
install_button = ttk.Button(tab, text="模拟安装依赖", command=self.simulate_dependency_install)
|
|
install_button.grid(row=2, column=1, padx=10, pady=5, sticky="e")
|
|
|
|
export_button = ttk.Button(tab, text="导出模拟安装报告", command=self.export_dependency_report)
|
|
export_button.grid(row=3, column=1, padx=10, pady=5, sticky="e")
|
|
|
|
def _add_labeled_entry(self, parent, row, label, default):
|
|
ttk.Label(parent, text=label, background=parent.cget("bg")).grid(
|
|
row=row, column=0, padx=10, pady=6, sticky="w"
|
|
)
|
|
entry = ttk.Entry(parent, width=48, font=("Arial", 11))
|
|
entry.grid(row=row, column=1, padx=10, pady=6, sticky="ew")
|
|
if default:
|
|
entry.insert(0, default)
|
|
return entry
|
|
|
|
def _add_labeled_combo(self, parent, row, label, values, default=None):
|
|
ttk.Label(parent, text=label, background=parent.cget("bg")).grid(
|
|
row=row, column=0, padx=10, pady=6, sticky="w"
|
|
)
|
|
combo = ttk.Combobox(parent, values=values, state="readonly", width=45, font=("Arial", 11))
|
|
combo.grid(row=row, column=1, padx=10, pady=6, sticky="ew")
|
|
if default:
|
|
combo.set(default)
|
|
elif values:
|
|
combo.set(values[0])
|
|
return combo
|
|
|
|
def _parse_list(self, raw_text):
|
|
return [item.strip() for item in raw_text.split(",") if item.strip()]
|
|
|
|
def _parse_int_list(self, raw_text):
|
|
return [int(item.strip()) for item in raw_text.split(",") if item.strip().isdigit()]
|
|
|
|
def _get_text_lines(self, text_widget):
|
|
content = text_widget.get("1.0", tk.END).strip()
|
|
return [line.strip() for line in content.splitlines() if line.strip()]
|
|
|
|
def start_ticket_task(self):
|
|
if self.dep_auto_install.get():
|
|
self.simulate_dependency_install()
|
|
self.log("任务开始!")
|
|
self.status_label.config(text="当前状态: 抢票进行中")
|
|
self.start_button.config(state=tk.DISABLED)
|
|
self.stop_button.config(state=tk.NORMAL)
|
|
self.restart_button.config(state=tk.NORMAL)
|
|
|
|
self.task_thread = threading.Thread(target=self.simulate_ticket_task, daemon=True)
|
|
self.task_thread.start()
|
|
|
|
def stop_ticket_task(self):
|
|
self.log("任务已停止!")
|
|
self.status_label.config(text="当前状态: 任务已停止")
|
|
self.start_button.config(state=tk.NORMAL)
|
|
self.stop_button.config(state=tk.DISABLED)
|
|
self.restart_button.config(state=tk.DISABLED)
|
|
|
|
def retry_ticket_task(self):
|
|
self.log("正在重试任务...")
|
|
self.start_ticket_task()
|
|
|
|
def simulate_ticket_task(self):
|
|
for i in range(1, 101):
|
|
time.sleep(0.08)
|
|
self.progress_bar["value"] = i
|
|
self.progress_label.config(text=f"进度: {i}%")
|
|
self.window.update_idletasks()
|
|
|
|
self.log("任务完成!")
|
|
self.status_label.config(text="当前状态: 任务完成")
|
|
self.start_button.config(state=tk.NORMAL)
|
|
self.stop_button.config(state=tk.DISABLED)
|
|
self.restart_button.config(state=tk.NORMAL)
|
|
|
|
def simulate_dependency_install(self):
|
|
dependencies = self._get_text_lines(self.dependency_list)
|
|
if not dependencies:
|
|
self.log("依赖清单为空,已跳过。")
|
|
return
|
|
self.log("开始模拟安装依赖...")
|
|
for step in build_mock_steps(dependencies):
|
|
time.sleep(0.03)
|
|
self.log(f"[{step.dependency}] {step.detail}")
|
|
self.log("依赖模拟安装完成。")
|
|
|
|
def log(self, message):
|
|
self.log_text.config(state=tk.NORMAL)
|
|
self.log_text.insert(tk.END, f"{message}\n")
|
|
self.log_text.config(state=tk.DISABLED)
|
|
self.log_text.yview(tk.END)
|
|
|
|
def save_config(self):
|
|
config_data = {
|
|
"version": "4.5",
|
|
"global": {
|
|
"log_level": self.global_log_level.get(),
|
|
"timezone": self.global_timezone.get(),
|
|
"ntp_servers": self._parse_list(self.global_ntp_servers.get()),
|
|
"dashboard": {
|
|
"enable": self.dashboard_enable.get(),
|
|
"host": self.dashboard_host.get(),
|
|
"port": int(self.dashboard_port.get() or 0),
|
|
},
|
|
},
|
|
"accounts": {
|
|
"acc_primary": {
|
|
"platform": self.account_platform.get(),
|
|
"credentials": {
|
|
"mobile": self.account_mobile.get(),
|
|
"password": self.account_password.get(),
|
|
"otp_secret": self.account_otp.get(),
|
|
},
|
|
"target": {
|
|
"event_url": self.target_event_url.get(),
|
|
"priorities": {
|
|
"date": self._parse_int_list(self.target_date_priorities.get()),
|
|
"session": self._parse_int_list(self.target_session_priorities.get()),
|
|
"price_range": self.target_price_range.get(),
|
|
},
|
|
"tickets": int(self.target_tickets.get() or 0),
|
|
"viewers": self._parse_int_list(self.target_viewers.get()),
|
|
},
|
|
"proxy": {
|
|
"type": self.proxy_type.get(),
|
|
"addr": self.proxy_addr.get(),
|
|
"rotate_interval": f"{self.proxy_rotate.get()}s",
|
|
},
|
|
}
|
|
},
|
|
"strategy": {
|
|
"auto_strike": self.strategy_auto.get(),
|
|
"strike_time": self.strategy_time.get(),
|
|
"preheat_stages": [
|
|
float(item) for item in self._parse_list(self.strategy_preheat.get())
|
|
],
|
|
"ai_enabled": self.strategy_ai.get(),
|
|
"ai_model_path": self.strategy_ai_model.get(),
|
|
"max_retries": int(self.strategy_max_retries.get() or 0),
|
|
"retry_backoff": self.strategy_retry_backoff.get(),
|
|
},
|
|
"monitor": {
|
|
"enable": self.monitor_enable.get(),
|
|
"poll_interval": f"{self.monitor_poll_interval.get()}s",
|
|
"triggers": self._get_text_lines(self.monitor_triggers),
|
|
},
|
|
"notification": {
|
|
"channels": [
|
|
{
|
|
"telegram": {
|
|
"bot_token": self.telegram_token.get(),
|
|
"chat_id": self.telegram_chat.get(),
|
|
}
|
|
},
|
|
{
|
|
"email": {
|
|
"smtp": self.email_smtp.get(),
|
|
"user": self.email_user.get(),
|
|
"pass": self.email_pass.get(),
|
|
"recipients": self._parse_list(self.email_recipients.get()),
|
|
}
|
|
},
|
|
]
|
|
},
|
|
"plugins": {"custom": self._parse_list(self.plugins_custom.get())},
|
|
"dependencies": {
|
|
"auto_install": self.dep_auto_install.get(),
|
|
"packages": self._get_text_lines(self.dependency_list),
|
|
},
|
|
}
|
|
|
|
file_path = filedialog.asksaveasfilename(
|
|
defaultextension=".json",
|
|
filetypes=[("JSON文件", "*.json")],
|
|
initialfile="config.json",
|
|
)
|
|
if not file_path:
|
|
return
|
|
|
|
with open(file_path, "w", encoding="utf-8") as file:
|
|
json.dump(config_data, file, ensure_ascii=False, indent=2)
|
|
self.log("配置已保存!")
|
|
|
|
def load_config(self):
|
|
file_path = filedialog.askopenfilename(filetypes=[("JSON文件", "*.json")])
|
|
if not file_path:
|
|
return
|
|
|
|
try:
|
|
with open(file_path, "r", encoding="utf-8") as file:
|
|
config_data = json.load(file)
|
|
except (json.JSONDecodeError, OSError) as error:
|
|
messagebox.showerror("错误", f"无法读取配置文件: {error}")
|
|
return
|
|
self.apply_config(config_data)
|
|
|
|
def load_demo_config(self):
|
|
demo_path = Path(__file__).resolve().parent / "config" / "demo_config.json"
|
|
if not demo_path.exists():
|
|
messagebox.showerror("错误", "未找到示例配置文件!")
|
|
return
|
|
try:
|
|
with open(demo_path, "r", encoding="utf-8") as file:
|
|
config_data = json.load(file)
|
|
except (json.JSONDecodeError, OSError) as error:
|
|
messagebox.showerror("错误", f"无法读取示例配置文件: {error}")
|
|
return
|
|
self.apply_config(config_data)
|
|
|
|
def apply_config(self, config_data):
|
|
global_cfg = config_data.get("global", {})
|
|
self.global_log_level.set(global_cfg.get("log_level", "DEBUG"))
|
|
self.global_timezone.delete(0, tk.END)
|
|
self.global_timezone.insert(0, global_cfg.get("timezone", ""))
|
|
self.global_ntp_servers.delete(0, tk.END)
|
|
self.global_ntp_servers.insert(0, ",".join(global_cfg.get("ntp_servers", [])))
|
|
dashboard_cfg = global_cfg.get("dashboard", {})
|
|
self.dashboard_enable.set(dashboard_cfg.get("enable", True))
|
|
self.dashboard_host.delete(0, tk.END)
|
|
self.dashboard_host.insert(0, dashboard_cfg.get("host", ""))
|
|
self.dashboard_port.delete(0, tk.END)
|
|
self.dashboard_port.insert(0, str(dashboard_cfg.get("port", "")))
|
|
|
|
accounts = config_data.get("accounts", {})
|
|
primary = accounts.get("acc_primary", {})
|
|
self.account_platform.set(primary.get("platform", "taopiaopiao"))
|
|
creds = primary.get("credentials", {})
|
|
self.account_mobile.delete(0, tk.END)
|
|
self.account_mobile.insert(0, creds.get("mobile", ""))
|
|
self.account_password.delete(0, tk.END)
|
|
self.account_password.insert(0, creds.get("password", ""))
|
|
self.account_otp.delete(0, tk.END)
|
|
self.account_otp.insert(0, creds.get("otp_secret", ""))
|
|
|
|
target = primary.get("target", {})
|
|
self.target_event_url.delete(0, tk.END)
|
|
self.target_event_url.insert(0, target.get("event_url", ""))
|
|
priorities = target.get("priorities", {})
|
|
self.target_date_priorities.delete(0, tk.END)
|
|
self.target_date_priorities.insert(0, ",".join(map(str, priorities.get("date", []))))
|
|
self.target_session_priorities.delete(0, tk.END)
|
|
self.target_session_priorities.insert(0, ",".join(map(str, priorities.get("session", []))))
|
|
self.target_price_range.set(priorities.get("price_range", "lowest_to_highest"))
|
|
self.target_tickets.delete(0, tk.END)
|
|
self.target_tickets.insert(0, str(target.get("tickets", "")))
|
|
self.target_viewers.delete(0, tk.END)
|
|
self.target_viewers.insert(0, ",".join(map(str, target.get("viewers", []))))
|
|
|
|
proxy = primary.get("proxy", {})
|
|
self.proxy_type.set(proxy.get("type", "socks5"))
|
|
self.proxy_addr.delete(0, tk.END)
|
|
self.proxy_addr.insert(0, proxy.get("addr", ""))
|
|
self.proxy_rotate.delete(0, tk.END)
|
|
self.proxy_rotate.insert(0, str(proxy.get("rotate_interval", "")).replace("s", ""))
|
|
|
|
strategy = config_data.get("strategy", {})
|
|
self.strategy_auto.set(strategy.get("auto_strike", True))
|
|
self.strategy_time.delete(0, tk.END)
|
|
self.strategy_time.insert(0, strategy.get("strike_time", ""))
|
|
self.strategy_preheat.delete(0, tk.END)
|
|
self.strategy_preheat.insert(0, ",".join(map(str, strategy.get("preheat_stages", []))))
|
|
self.strategy_ai.set(strategy.get("ai_enabled", True))
|
|
self.strategy_ai_model.delete(0, tk.END)
|
|
self.strategy_ai_model.insert(0, strategy.get("ai_model_path", ""))
|
|
self.strategy_max_retries.delete(0, tk.END)
|
|
self.strategy_max_retries.insert(0, str(strategy.get("max_retries", "")))
|
|
self.strategy_retry_backoff.set(strategy.get("retry_backoff", "exponential"))
|
|
|
|
monitor = config_data.get("monitor", {})
|
|
self.monitor_enable.set(monitor.get("enable", True))
|
|
self.monitor_poll_interval.delete(0, tk.END)
|
|
self.monitor_poll_interval.insert(0, str(monitor.get("poll_interval", "")).replace("s", ""))
|
|
self.monitor_triggers.delete("1.0", tk.END)
|
|
self.monitor_triggers.insert(tk.END, "\n".join(monitor.get("triggers", [])))
|
|
|
|
notification = config_data.get("notification", {})
|
|
channels = notification.get("channels", [])
|
|
telegram = next((c.get("telegram") for c in channels if "telegram" in c), {})
|
|
email = next((c.get("email") for c in channels if "email" in c), {})
|
|
self.telegram_token.delete(0, tk.END)
|
|
self.telegram_token.insert(0, telegram.get("bot_token", ""))
|
|
self.telegram_chat.delete(0, tk.END)
|
|
self.telegram_chat.insert(0, telegram.get("chat_id", ""))
|
|
self.email_smtp.delete(0, tk.END)
|
|
self.email_smtp.insert(0, email.get("smtp", ""))
|
|
self.email_user.delete(0, tk.END)
|
|
self.email_user.insert(0, email.get("user", ""))
|
|
self.email_pass.delete(0, tk.END)
|
|
self.email_pass.insert(0, email.get("pass", ""))
|
|
self.email_recipients.delete(0, tk.END)
|
|
self.email_recipients.insert(0, ",".join(email.get("recipients", [])))
|
|
|
|
plugins = config_data.get("plugins", {})
|
|
self.plugins_custom.delete(0, tk.END)
|
|
self.plugins_custom.insert(0, ",".join(plugins.get("custom", [])))
|
|
|
|
dependencies = config_data.get("dependencies", {})
|
|
self.dep_auto_install.set(dependencies.get("auto_install", True))
|
|
self.dependency_list.delete("1.0", tk.END)
|
|
self.dependency_list.insert(tk.END, "\n".join(dependencies.get("packages", [])))
|
|
self.log("配置已加载!")
|
|
|
|
def export_dependency_report(self):
|
|
dependencies = self._get_text_lines(self.dependency_list)
|
|
if not dependencies:
|
|
messagebox.showwarning("提示", "依赖清单为空,无法导出报告。")
|
|
return
|
|
file_path = filedialog.asksaveasfilename(
|
|
defaultextension=".txt",
|
|
filetypes=[("文本文件", "*.txt")],
|
|
initialfile="mock_install_report.txt",
|
|
)
|
|
if not file_path:
|
|
return
|
|
report = build_report(dependencies)
|
|
try:
|
|
with open(file_path, "w", encoding="utf-8") as file:
|
|
file.write(report)
|
|
except OSError as error:
|
|
messagebox.showerror("错误", f"无法写入报告: {error}")
|
|
return
|
|
self.log(f"模拟安装报告已导出: {file_path}")
|
|
|
|
def show_about(self):
|
|
messagebox.showinfo(
|
|
"关于",
|
|
"抢票助手 V5.0\n\n交互界面支持 README 中的配置项预设。\n\n说明: 仅研究用途。",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = TicketHelperGUI()
|
|
app.window.mainloop()
|