import tkinter as tk from tkinter import scrolledtext, messagebox import requests import threading import time import json class PurchaseApp: def __init__(self, root): self.root = root self.root.title("米画师商品购买工具") self.root.geometry("600x500") self.running = False self.stop_flag = False self.setup_ui() def setup_ui(self): # 密码输入框 tk.Label(self.root, text="支付密码:").pack(pady=5) self.password_entry = tk.Entry(self.root, show="*", width=50) self.password_entry.pack(pady=5) # 商品ID输入框 tk.Label(self.root, text="商品ID:").pack(pady=5) self.product_id_entry = tk.Entry(self.root, width=50) self.product_id_entry.pack(pady=5) # 按钮框架 button_frame = tk.Frame(self.root) button_frame.pack(pady=20) self.start_button = tk.Button(button_frame, text="启动", command=self.start_request, bg="green", fg="white", width=10) self.start_button.pack(side=tk.LEFT, padx=10) self.stop_button = tk.Button(button_frame, text="停止", command=self.stop_request, bg="red", fg="white", width=10, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=10) # 日志框架 tk.Label(self.root, text="请求日志:").pack(pady=5) self.log_text = scrolledtext.ScrolledText(self.root, width=70, height=20, wrap=tk.WORD) self.log_text.pack(pady=5, padx=10, fill=tk.BOTH, expand=True) def log_message(self, message, is_error=False): """在日志框中添加消息""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") tag = "ERROR" if is_error else "INFO" log_entry = f"[{timestamp}] [{tag}] {message}\n" self.log_text.insert(tk.END, log_entry) self.log_text.see(tk.END) # 如果是错误消息,用红色显示 if is_error: start_idx = self.log_text.index(f"end-{len(log_entry)}c") end_idx = self.log_text.index("end-1c") self.log_text.tag_add("error", start_idx, end_idx) self.log_text.tag_config("error", foreground="red") def make_request(self, product_id, pay_password): """执行单次请求""" url = f"https://www.mihuashi.com/api/v1/manufactures/{product_id}/purchase" # 构建请求数据 payload = f"pay_password={pay_password}&remember_token=xxrohSdEFqQSt0fNY1mh4k&specification_ids%5B%5D=3931" headers = { "content-type": "application/x-www-form-urlencoded", "baggage": "sentry-environment=production,sentry-public_key=afa52c0802b4441ba513fb25cb6bc20a,sentry-release=com.mihuashi.iOS%408.7.1%2B231,sentry-trace_id=a0ac9f45d3f74b51b387a345b0d95389", "x-track-trans-data": "CiQ4N2Q4NTRmOS02ZTQ4LTQ3YmMtOGIwYy1mMzRhY2U2Yzg4YTUSFQoMaG90XzE2NzY0X3YyFQDAr0QYHRISCggxNjc3OF92MhVgU9G+GNcBGhAKCTE1NzU4OmN0ch0A0l09GhAKCTE1NzU4OmN2ch0AyLg5IgAq1wIhC3L415YlBiR6vAJ7ImFwaV9yZXFfdGltZSI6MTc4MTI1MDQ2NCwic3BtXzMiOiLmjqjojZAiLCJzcG1fMiI6Iuapseeqly3mnI3orr4m5rC05Y2wIiwicGFnZV9udW0iOjEsImFiX3ZlcnNpb24iOiIiLCJzcG0iOiLnsbPnlLvluIgkIyMk5qmx56qXLeacjeiuvibmsLTljbAkIyMk5o6o6I2QIiwiY2xvc2VfcGVyc29uYWxpemVkX3JlYyI6ZmFsc2UsImJodl90aW1lIjoiMTc4MTI1MDQ2NCIsImJodl90aW1lX2hvdXIiOiIxNSIsImJodl90aW1lX21vbnRoZGF5IjoiMTIiLCJiaHZfdGltZV93ZWVrZGF5IjoiNSIsImZha2VfY29udGV4dF9pZCI6ImZha2VfY29udGV4dF9pZCJ9sAEOygEGNzY3NDY00AEy", "sensor-id": "96F047C1-773B-4596-BA8F-EB8E3D08D404", "authorization": "Bearer xxrohSdEFqQSt0fNY1mh4k", "x-track-volc-extra": '{"recall_list":"hot_16764_v2,16778_v2","doc_type":"manufacture","status":"1","tags":"","score":"0.00001649724435992539"}', "accept": "*/*", "priority": "u=3, i", "x-track-spm": "%E7%B1%B3%E7%94%BB%E5%B8%88$%23%23$%E6%9C%8D%E8%AE%BE&%E6%B0%B4%E5%8D%B0$%23%23$%E8%AF%A6%E6%83%85%E9%A1%B5$%23%23$12", "accept-language": "zh-Hans-AU;q=1, en-AU;q=0.9", "sentry-trace": "a0ac9f45d3f74b51b387a345b0d95389-2b3cdabdb84741e4-0", "x-network-type": "wifi", "user-agent": "MHSIPhoneApp/8.7.1 (iPhone; iOS 26.5; Scale/3.00; iPhone15,4)", "cookie": "aliyungf_tc=c3e9536bffb022b57059d7436f69e970c3ced15034d620e006be4582617a4e9e", "Accept-Encoding": "gzip, deflate, br", "Connection": "keep-alive" } try: response = requests.request("POST", url, data=payload, headers=headers, timeout=10) # 尝试解析JSON响应 try: response_json = response.json() formatted_response = json.dumps(response_json, ensure_ascii=False, indent=2) except: formatted_response = response.text self.log_message(f"请求URL: {url}") self.log_message(f"响应状态码: {response.status_code}") self.log_message(f"响应内容: {formatted_response[:500]}") # 限制显示长度 if response.status_code == 200: self.log_message("请求成功") else: self.log_message(f"请求失败,状态码: {response.status_code}", is_error=True) except requests.exceptions.Timeout: self.log_message("请求超时", is_error=True) except requests.exceptions.ConnectionError: self.log_message("连接错误,请检查网络", is_error=True) except Exception as e: self.log_message(f"请求异常: {str(e)}", is_error=True) def request_loop(self): """请求循环,每2秒执行一次""" product_id = self.product_id_entry.get().strip() pay_password = self.password_entry.get() # 验证输入 if not product_id: self.log_message("请输入商品ID", is_error=True) self.stop_request() return if not pay_password: self.log_message("请输入支付密码", is_error=True) self.stop_request() return # 验证商品ID是否为数字 try: int(product_id) except ValueError: self.log_message("商品ID必须是数字", is_error=True) self.stop_request() return self.log_message(f"开始请求循环,商品ID: {product_id}") while self.running and not self.stop_flag: self.make_request(product_id, pay_password) # 等待2秒,但每秒检查一次停止标志 for _ in range(2): if not self.running or self.stop_flag: break time.sleep(1) self.log_message("请求循环已停止") def start_request(self): """启动请求线程""" # 验证输入 if not self.password_entry.get(): messagebox.showwarning("警告", "请输入支付密码") return if not self.product_id_entry.get(): messagebox.showwarning("警告", "请输入商品ID") return self.running = True self.stop_flag = False # 更新按钮状态 self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) # 启动请求线程 self.request_thread = threading.Thread(target=self.request_loop, daemon=True) self.request_thread.start() def stop_request(self): """停止请求""" self.running = False self.stop_flag = True # 更新按钮状态 self.start_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) def main(): root = tk.Tk() app = PurchaseApp(root) root.mainloop() if __name__ == "__main__": main()