|
|
@@ -1,50 +1,40 @@
|
|
|
import tkinter as tk
|
|
|
-from tkinter import ttk, scrolledtext, messagebox, filedialog
|
|
|
+from tkinter import ttk, scrolledtext, messagebox
|
|
|
from datetime import datetime
|
|
|
+import threading
|
|
|
+import requests
|
|
|
+
|
|
|
+
|
|
|
+# ========== 固定配置 ==========
|
|
|
+BASE_URL = "http://task.port.run" # 服务器地址(固定)
|
|
|
+CHANNEL = "自动注册" # 频道
|
|
|
+TIMEOUT = "6000" # 任务超时秒数
|
|
|
|
|
|
|
|
|
class NewToolGUI:
|
|
|
def __init__(self, root):
|
|
|
self.root = root
|
|
|
self.root.title("新工具 - 批量购买平台")
|
|
|
- self.root.geometry("900x950")
|
|
|
+ self.root.geometry("900x850")
|
|
|
self.root.resizable(False, False)
|
|
|
|
|
|
- # 设置样式
|
|
|
+ self.running = False
|
|
|
+
|
|
|
+ # 样式
|
|
|
style = ttk.Style()
|
|
|
style.configure("TLabel", font=("微软雅黑", 10))
|
|
|
style.configure("TButton", font=("微软雅黑", 10))
|
|
|
style.configure("TEntry", font=("微软雅黑", 10))
|
|
|
|
|
|
- # 主框架
|
|
|
main_frame = ttk.Frame(root, padding="20 20 20 20")
|
|
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
- # ========== 代理设置 ==========
|
|
|
- proxy_frame = ttk.LabelFrame(main_frame, text="代理设置", padding="10 10 10 10")
|
|
|
- proxy_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
-
|
|
|
- ttk.Label(proxy_frame, text="代理提取链接:").grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
|
- self.proxy_url_var = tk.StringVar(value="")
|
|
|
- proxy_url_entry = ttk.Entry(proxy_frame, textvariable=self.proxy_url_var, width=50)
|
|
|
- proxy_url_entry.grid(row=0, column=1, sticky=tk.W, padx=(10, 0), pady=5)
|
|
|
-
|
|
|
- ttk.Label(proxy_frame, text="代理更换间隔(秒):").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
- self.proxy_interval_var = tk.StringVar(value="60")
|
|
|
- proxy_interval_entry = ttk.Entry(proxy_frame, textvariable=self.proxy_interval_var, width=10)
|
|
|
- proxy_interval_entry.grid(row=1, column=1, sticky=tk.W, padx=(10, 0), pady=5)
|
|
|
-
|
|
|
- # 代理状态显示
|
|
|
- self.proxy_status = ttk.Label(proxy_frame, text="代理状态: 未启用", font=("微软雅黑", 9), foreground="gray")
|
|
|
- self.proxy_status.grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=(5, 0))
|
|
|
-
|
|
|
# ========== 账号列表 ==========
|
|
|
account_frame = ttk.LabelFrame(main_frame, text="账号列表 (每行一个账号,格式:邮箱 | 密码)", padding="10 10 10 10")
|
|
|
account_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
|
|
|
self.account_list_text = tk.Text(account_frame, height=6, font=("Consolas", 9), wrap=tk.WORD)
|
|
|
self.account_list_text.pack(fill=tk.X)
|
|
|
- # 示例占位内容
|
|
|
self.account_list_text.insert(tk.END, "example1@mail.com | password123\n")
|
|
|
self.account_list_text.insert(tk.END, "example2@mail.com | password456\n")
|
|
|
|
|
|
@@ -52,19 +42,16 @@ class NewToolGUI:
|
|
|
purchase_frame = ttk.LabelFrame(main_frame, text="购买参数", padding="10 10 10 10")
|
|
|
purchase_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
|
|
|
- # 游戏购买地址
|
|
|
ttk.Label(purchase_frame, text="游戏购买地址:").grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
|
self.game_url_var = tk.StringVar(value="")
|
|
|
game_url_entry = ttk.Entry(purchase_frame, textvariable=self.game_url_var, width=60)
|
|
|
game_url_entry.grid(row=0, column=1, columnspan=2, sticky=tk.W, padx=(10, 0), pady=5)
|
|
|
|
|
|
- # 购买数量
|
|
|
ttk.Label(purchase_frame, text="购买数量:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
self.quantity_var = tk.StringVar(value="1")
|
|
|
quantity_entry = ttk.Entry(purchase_frame, textvariable=self.quantity_var, width=10)
|
|
|
quantity_entry.grid(row=1, column=1, sticky=tk.W, padx=(10, 0), pady=5)
|
|
|
|
|
|
- # 游戏优惠券代码
|
|
|
ttk.Label(purchase_frame, text="游戏优惠券代码:").grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
|
self.coupon_var = tk.StringVar(value="")
|
|
|
coupon_entry = ttk.Entry(purchase_frame, textvariable=self.coupon_var, width=30)
|
|
|
@@ -83,7 +70,7 @@ class NewToolGUI:
|
|
|
clear_btn = ttk.Button(button_frame, text="清空日志", command=self.clear_log, width=15)
|
|
|
clear_btn.pack(side=tk.LEFT, padx=(10, 0))
|
|
|
|
|
|
- # ========== 进度状态 ==========
|
|
|
+ # ========== 执行状态 ==========
|
|
|
status_frame = ttk.LabelFrame(main_frame, text="执行状态", padding="10 10 10 10")
|
|
|
status_frame.pack(fill=tk.X, pady=(0, 15))
|
|
|
|
|
|
@@ -93,9 +80,9 @@ class NewToolGUI:
|
|
|
self.progress_var = tk.StringVar(value="0%")
|
|
|
progress_frame = ttk.Frame(status_frame)
|
|
|
progress_frame.pack(anchor=tk.W, pady=(5, 0))
|
|
|
- self.progress_label = ttk.Label(progress_frame, text="进度:", font=("微软雅黑", 9))
|
|
|
- self.progress_label.pack(side=tk.LEFT)
|
|
|
- self.progress_bar = ttk.Label(progress_frame, textvariable=self.progress_var, font=("微软雅黑", 9), foreground="blue")
|
|
|
+ ttk.Label(progress_frame, text="进度:", font=("微软雅黑", 9)).pack(side=tk.LEFT)
|
|
|
+ self.progress_bar = ttk.Label(progress_frame, textvariable=self.progress_var,
|
|
|
+ font=("微软雅黑", 9), foreground="blue")
|
|
|
self.progress_bar.pack(side=tk.LEFT, padx=(5, 0))
|
|
|
|
|
|
# ========== 日志输出 ==========
|
|
|
@@ -103,14 +90,10 @@ class NewToolGUI:
|
|
|
result_frame.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
self.result_text = scrolledtext.ScrolledText(
|
|
|
- result_frame,
|
|
|
- wrap=tk.WORD,
|
|
|
- font=("Consolas", 9),
|
|
|
- height=15
|
|
|
+ result_frame, wrap=tk.WORD, font=("Consolas", 9), height=15
|
|
|
)
|
|
|
self.result_text.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
- # 日志颜色标签
|
|
|
self.result_text.tag_config("timestamp", foreground="gray")
|
|
|
self.result_text.tag_config("info", foreground="black")
|
|
|
self.result_text.tag_config("success", foreground="green")
|
|
|
@@ -118,18 +101,123 @@ class NewToolGUI:
|
|
|
self.result_text.tag_config("warning", foreground="orange")
|
|
|
self.result_text.tag_config("debug", foreground="purple")
|
|
|
|
|
|
- # ========== 以下是占位方法,不实现实际功能 ==========
|
|
|
+ # ================= 日志工具 =================
|
|
|
+ def log(self, message, level="info"):
|
|
|
+ def _append():
|
|
|
+ ts = datetime.now().strftime("%H:%M:%S")
|
|
|
+ self.result_text.insert(tk.END, f"[{ts}] ", "timestamp")
|
|
|
+ self.result_text.insert(tk.END, f"{message}\n", level)
|
|
|
+ self.result_text.see(tk.END)
|
|
|
+ self.root.after(0, _append)
|
|
|
+
|
|
|
+ def set_status(self, text, color="black"):
|
|
|
+ self.root.after(0, lambda: self.status_label.config(text=text, foreground=color))
|
|
|
+
|
|
|
+ def set_progress(self, current, total):
|
|
|
+ pct = int(current / total * 100) if total else 0
|
|
|
+ self.root.after(0, lambda: self.progress_var.set(f"{current}/{total} ({pct}%)"))
|
|
|
+
|
|
|
+ # ================= 业务逻辑 =================
|
|
|
+ def parse_accounts(self):
|
|
|
+ raw = self.account_list_text.get("1.0", tk.END).strip()
|
|
|
+ accounts = []
|
|
|
+ for line in raw.splitlines():
|
|
|
+ line = line.strip()
|
|
|
+ if not line:
|
|
|
+ continue
|
|
|
+ if "|" in line:
|
|
|
+ parts = [p.strip() for p in line.split("|", 1)]
|
|
|
+ email, password = parts[0], parts[1]
|
|
|
+ else:
|
|
|
+ email, password = line, ""
|
|
|
+ accounts.append((email, password))
|
|
|
+ return accounts
|
|
|
+
|
|
|
+ def publish_task(self, content):
|
|
|
+ url = f"{BASE_URL.rstrip('/')}/publish/{CHANNEL}/{TIMEOUT}"
|
|
|
+ try:
|
|
|
+ resp = requests.post(url, json=content, timeout=10)
|
|
|
+ data = resp.json()
|
|
|
+ if data.get("success"):
|
|
|
+ return True, data.get("taskId", "")
|
|
|
+ return False, data.get("error", f"HTTP {resp.status_code}")
|
|
|
+ except Exception as e:
|
|
|
+ return False, str(e)
|
|
|
|
|
|
def start_purchase(self):
|
|
|
- """占位:开始批量购买"""
|
|
|
- messagebox.showinfo("提示", "这是界面演示,未实现实际功能。")
|
|
|
+ if self.running:
|
|
|
+ messagebox.showwarning("提示", "任务正在执行中")
|
|
|
+ return
|
|
|
+
|
|
|
+ accounts = self.parse_accounts()
|
|
|
+ if not accounts:
|
|
|
+ messagebox.showwarning("提示", "账号列表为空")
|
|
|
+ return
|
|
|
+
|
|
|
+ game_url = self.game_url_var.get().strip()
|
|
|
+ if not game_url:
|
|
|
+ messagebox.showwarning("提示", "请填写游戏购买地址")
|
|
|
+ return
|
|
|
+
|
|
|
+ quantity = self.quantity_var.get().strip() or "1"
|
|
|
+ coupon = self.coupon_var.get().strip()
|
|
|
+
|
|
|
+ self.running = True
|
|
|
+ self.start_btn.config(state="disabled")
|
|
|
+ self.stop_btn.config(state="normal")
|
|
|
+ self.set_status("正在提交...", "blue")
|
|
|
+ self.set_progress(0, len(accounts))
|
|
|
+
|
|
|
+ threading.Thread(
|
|
|
+ target=self._publish_worker,
|
|
|
+ args=(accounts, game_url, quantity, coupon),
|
|
|
+ daemon=True
|
|
|
+ ).start()
|
|
|
+
|
|
|
+ def _publish_worker(self, accounts, game_url, quantity, coupon):
|
|
|
+ total = len(accounts)
|
|
|
+ success = 0
|
|
|
+ fail = 0
|
|
|
+
|
|
|
+ self.log(f"共 {total} 个账号,开始提交任务...", "info")
|
|
|
+
|
|
|
+ for idx, (email, password) in enumerate(accounts, start=1):
|
|
|
+ if not self.running:
|
|
|
+ self.log("用户已停止任务", "warning")
|
|
|
+ break
|
|
|
+
|
|
|
+ content = {
|
|
|
+ "email": email,
|
|
|
+ "password": password,
|
|
|
+ "game_url": game_url,
|
|
|
+ "quantity": quantity,
|
|
|
+ "coupon": coupon,
|
|
|
+ }
|
|
|
+
|
|
|
+ ok, info = self.publish_task(content)
|
|
|
+ if ok:
|
|
|
+ success += 1
|
|
|
+ self.log(f"[{idx}/{total}] 已提交: {email} -> {info}", "success")
|
|
|
+ else:
|
|
|
+ fail += 1
|
|
|
+ self.log(f"[{idx}/{total}] 提交失败: {email} -> {info}", "error")
|
|
|
+
|
|
|
+ self.set_progress(idx, total)
|
|
|
+
|
|
|
+ self.log(f"全部完成,成功 {success} 条,失败 {fail} 条", "info")
|
|
|
+ self.set_status(f"完成 (成功 {success} / 失败 {fail})", "green")
|
|
|
+ self.running = False
|
|
|
+ self.root.after(0, lambda: self.start_btn.config(state="normal"))
|
|
|
+ self.root.after(0, lambda: self.stop_btn.config(state="disabled"))
|
|
|
|
|
|
def stop_purchase(self):
|
|
|
- """占位:停止购买"""
|
|
|
- messagebox.showinfo("提示", "这是界面演示,未实现实际功能。")
|
|
|
+ if not self.running:
|
|
|
+ return
|
|
|
+ self.running = False
|
|
|
+ self.log("正在停止任务...", "warning")
|
|
|
+ self.set_status("已停止", "orange")
|
|
|
|
|
|
def clear_log(self):
|
|
|
- """清空日志"""
|
|
|
self.result_text.delete(1.0, tk.END)
|
|
|
self.status_label.config(text="已清空日志", foreground="gray")
|
|
|
|