# Source: https://github.com/smallfawn/QLScriptPublic/blob/main/daily/qiehuang.py # Raw: https://raw.githubusercontent.com/smallfawn/QLScriptPublic/main/daily/qiehuang.py # Repo: smallfawn/QLScriptPublic # Path: daily/qiehuang.py # UploadedAt: 2026-05-14T17:47:57+08:00 # SHA256: 678fa3406b5408e8ba04725e090ea9f8db8951fb4406c132dd23950af0270e0a # Category: web版/账密 # Evidence: web/H5关键词 + username/password/login # cron: 25 10 * * * # new Env('统一快乐星球茄皇') import requests import json import os import time import re import random from collections import defaultdict from notify import send users = os.getenv("QH", '').splitlines() # 清洗数据:去除每个用户字符串的首尾空格,过滤空行/全空格的无效数据 users = [user.strip() for user in users if user.strip()] # 解析为 (wid, 手机号) 列表 parsed_users = [] for user_str in users: if "#" in user_str: # 按#拆分(只拆1次,避免手机号含#导致拆分错误) wid, phone = user_str.split("#", 1) parsed_users.append((wid.strip(), phone.strip())) else: # 兼容旧格式:只有wid没有手机号时,手机号为空字符串 parsed_users.append((user_str.strip(), "")) # UA可自行替换为自己的 user_agent = "Mozilla/5.0 (Linux; Android 14; 23046RP50C Build/UKQ1.230804.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.172 Safari/537.36 XWEB/1420045 MMWEBSDK/20250201 MMWEBID/5714 MicroMessenger/8.0.57.2820(0x28003956) WeChat/arm64 Weixin Android Tablet NetType/WIFI Language/zh_CN ABI/arm64 miniProgram/wx532ecb3bdaaf92f9" STEP_ORDER = ["登录", "领取种子", "签到", "浏览任务", "收获作物", "播种", "循环浇水"] STEP_EMOJI = {"登录": "🔑", "领取种子": "🌱", "签到": "📅", "浏览任务": "🔍", "收获作物": "🌾", "播种": "🌱", "循环浇水": "🔄"} def _short(s, n=120): s = s.strip() return s if len(s) <= n else s[:n - 1] + "…" def _pick_status(line: str) -> str: if "✅" in line: return "✅" if "⚠️" in line: return "⚠️" if "❌" in line: return "❌" return "ℹ️" def _step_key(line: str) -> str: for k in STEP_ORDER: if k in line: return k return "信息" def _pull_resource_snapshot(lines): res = {} for line in reversed(lines): if "☀️" in line: try: res["sun"] = int(re.findall(r"☀️(\d+)", line)[0]) except: pass if "💧" in line: try: res["water"] = int(re.findall(r"💧(\d+)", line)[0]) except: pass if "🍅" in line: try: res["fruit"] = int(re.findall(r"🍅(\d+)", line)[0]) except: pass if len(res) >= 2: break return res def render_report(all_lines): blocks, cur = [], [] for ln in all_lines: if ln.strip().startswith("👤 用户"): if cur: blocks.append(cur) cur = [ln.strip()] elif ln is not None: cur.append(ln.rstrip()) if cur: blocks.append(cur) out = [] for b in blocks: if not b: continue header = b[0].strip() out.append("━━━━━━━━━━━━━━━━━━━━━━") out.append(header) bucket = defaultdict(list) for ln in b[1:]: if not ln.strip(): continue bucket[_step_key(ln)].append(ln) snap = _pull_resource_snapshot(b) if snap: res_line = "📊 当前资源:" if "sun" in snap: res_line += f"☀️{snap['sun']} " if "water" in snap: res_line += f"💧{snap['water']} " if "fruit" in snap: res_line += f"🍅{snap['fruit']} " out.append(res_line.strip()) for step in STEP_ORDER + ["信息"]: if step not in bucket: continue lines = bucket[step] cleaned, seen = [], set() for ln in lines: if set(ln.strip()) in (set("="),): continue # 关键修复:循环浇水日志不做去重(保留所有记录) if step == "循环浇水": cleaned.append(ln) else: norm = re.sub(r"账号\d+:", "", re.sub(r"\s+", " ", ln)).strip() if norm not in seen: seen.add(norm) cleaned.append(ln) # 循环浇水保留所有记录 if step == "循环浇水": picked = cleaned else: picked = cleaned[-1:] if cleaned else [] for pln in picked: status = _pick_status(pln) icon = STEP_EMOJI.get(step, "•") body = re.sub(r"^[🔑🌱📅🔍🌾🔄]+\s*" + re.escape(step) + r"[::]?\s*", "", pln) body = re.sub(r"^[✅❌⚠️ℹ️]+\s*", "", body) out.append(f"{icon} {step} {status} {_short(body)}") succ = sum("✅" in ln for ln in b) fail = sum("❌" in ln for ln in b) warn = sum("⚠️" in ln for ln in b) out.append(f"🧾 小结:成功 {succ} · 预警 {warn} · 失败 {fail}") out.append("━━━━━━━━━━━━━━━━━━━━━━") return "\n".join(out) def login(wid, phone, user_logs): step = "登录" # 校验手机号是否存在 if not phone: msg = "未配置手机号,登录失败 🔒" print(msg) user_logs.append(f"🔑 {step}: {msg}") return None try: url = "https://api.zhumanito.cn/api/login" # 新增手机号参数 wm_phone payload = {"wid": wid, "wm_phone": phone} headers = {'User-Agent': user_agent, 'Content-Type': "application/json"} response = requests.post(url, data=json.dumps(payload), headers=headers) response.raise_for_status() dljson = response.json() # 响应格式新增 code 字段校验 if dljson.get("code") == 200 and 'data' in dljson and 'token' in dljson['data'] and 'user' in dljson['data'] and 'land' in dljson['data']: msg = f"登录成功(手机号:{phone})✅" print(msg) user_logs.append(f"🔑 {step}: {msg}") time.sleep(random.uniform(4, 5)) return { "token": dljson['data']['token'], "user_data": dljson['data']['user'], "land_data": dljson['data']['land'] } else: msg = f"登录失败,返回数据: {dljson} ❌" print(msg) user_logs.append(f"🔑 {step}: {msg}") return None except Exception as e: msg = f"登录出错(手机号:{phone}): {str(e)} ❌" print(msg) user_logs.append(f"🔑 {step}: {msg}") return None def get_seeds(authorization, user_logs): step = "领取种子" if not authorization: msg = "未获取到授权,无法领取种子 🔒" print(msg) user_logs.append(f"🌱 {step}: {msg}") return try: url = "https://api.zhumanito.cn/api/guide" headers = {'User-Agent': user_agent, 'Content-Type': "application/json", 'authorization': authorization} for st in (1, 2): payload = {"status": st} response = requests.post(url, data=json.dumps(payload), headers=headers) response.raise_for_status() user_logs.append(f"🌱 {step}: 领取/引导完成 ✅") time.sleep(random.uniform(4, 5)) except Exception as e: msg = f"领取种子出错: {str(e)} ❌" print(msg) user_logs.append(f"🌱 {step}: {msg}") def check_in(authorization, user_logs): step = "签到" if not authorization: msg = "未获取到授权,无法签到 🔒" print(msg) user_logs.append(f"📅 {step}: {msg}") return try: url = "https://api.zhumanito.cn/api/task/complete" headers = {'User-Agent': user_agent, 'Content-Type': "application/x-www-form-urlencoded", 'authorization': authorization} response = requests.post(url, headers=headers) response_data = response.json() if response_data.get("msg") == "成功": msg = "签到成功 ✅" print(f"签到结果: {msg}") user_logs.append(f"📅 {step}: {msg}") elif response_data.get("msg") == "不可重复完成": msg = "今日已签到,无需重复操作 ✅" print(f"签到结果: {msg}") user_logs.append(f"📅 {step}: {msg}") else: msg = f"失败,原因: {response_data.get('msg', '未知错误')} ❌" print(f"签到结果: {msg}") user_logs.append(f"📅 {step}: {msg}") time.sleep(random.uniform(4, 5)) except Exception as e: msg = f"签到出错: {str(e)} ❌" print(msg) user_logs.append(f"📅 {step}: {msg}") def explore(authorization, wid, user_logs): step = "浏览任务" if not authorization: msg = "未获取到授权,无法执行浏览任务 🔒" print(msg) user_logs.append(f"🔍 {step}: {msg}") return max_retry = 3 retry_count = 0 while retry_count < max_retry: try: url = f"https://api.zhumanito.cn/?wid={wid}" headers = { 'Host': 'api.zhumanito.cn', 'User-Agent': user_agent, 'authorization': authorization, 'sec-ch-ua': '"Chromium";v="142", "Android WebView";v="142", "Not_A Brand";v="99"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Android"', 'upgrade-insecure-requests': '1', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/wxpic,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'x-requested-with': 'com.tencent.mm', 'sec-fetch-site': 'same-site', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'referer': 'https://h5.zhumanito.cn/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'priority': 'u=0, i' } response = requests.get(url, headers=headers, allow_redirects=False, timeout=10, verify=True) if response.status_code == 302: msg = "浏览任务完成✅" print(f"浏览任务:{msg}") user_logs.append(f"🔍 {step}: {msg}") time.sleep(random.uniform(4, 5)) break elif response.status_code == 429: retry_after = int(response.headers.get("Retry-After", "1")) retry_count += 1 if retry_count < max_retry: msg = f"浏览请求限速,等待{retry_after}秒后重试(第{retry_count}/{max_retry}次)" print(f"浏览任务:{msg}") time.sleep(retry_after) else: msg = f"浏览请求多次限速,放弃重试 ❌" print(f"浏览任务:{msg}") user_logs.append(f"🔍 {step}: {msg}") else: msg = f"浏览失败,状态码: {response.status_code} ❌" print(f"浏览任务:{msg}") user_logs.append(f"🔍 {step}: {msg}") break except requests.exceptions.RequestException as e: msg = f"浏览任务出错: {str(e)} ❌" print(msg) user_logs.append(f"🔍 {step}: {msg}") break def harvest(authorization, user_logs, account): step = "收获作物" try: url = "https://api.zhumanito.cn/api/harvest" headers = { 'User-Agent': user_agent, 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 'authorization': authorization } before_fruit = int(account["user_data"].get("fruit_num", 0)) response = requests.post(url, headers=headers, data=b"", timeout=15) response.raise_for_status() res_json = response.json() if res_json.get("code") == 200: account["user_data"] = res_json["data"]["user"] account["land_data"] = res_json["data"]["land"] after_fruit = int(account["user_data"].get("fruit_num", 0)) total_after = int(account["user_data"].get("total_fruit_num", after_fruit)) delta = max(0, after_fruit - before_fruit) msg = f"收获成功!🍅+{delta} → 现有 {after_fruit}(累计 {total_after})" print(msg) user_logs.append(f"🌾 {step}: {msg}") snap_line = f"📊 收获后资源:☀️{account['user_data'].get('sun_num',0)} 💧{account['user_data'].get('water_num',0)} 🍅{after_fruit}" print(snap_line) user_logs.append(snap_line) time.sleep(random.uniform(4, 5)) return True else: msg = f"收获失败: {res_json.get('msg', '未知信息')} ⚠️" print(msg) user_logs.append(f"🌾 {step}: {msg}") return False except Exception as e: msg = f"收获请求出错: {str(e)} ❌" print(msg) user_logs.append(f"🌾 {step}: {msg}") return False def plant_seed(authorization, user_logs, account): step = "播种" try: url = "https://api.zhumanito.cn/api/seed" headers = { 'User-Agent': user_agent, 'Content-Type': "application/x-www-form-urlencoded;charset=utf-8", 'authorization': authorization } response = requests.post(url, headers=headers, data=b"", timeout=15) response.raise_for_status() res_json = response.json() if res_json.get("code") == 200: msg = "播种成功!✅" print(msg) user_logs.append(f"🌱 {step}: {msg}") account["user_data"] = res_json["data"]["user"] account["land_data"] = res_json["data"]["land"] time.sleep(random.uniform(4, 5)) return True else: msg = f"播种失败: {res_json.get('msg', '未知信息')} ⚠️" print(msg) user_logs.append(f"🌱 {step}: {msg}") return False except Exception as e: msg = f"播种请求出错: {str(e)} ❌" print(msg) user_logs.append(f"🌱 {step}: {msg}") return False def water_once(headers, account_idx): """单次浇水(带限速重试)""" max_retry = 3 retry_count = 0 while retry_count < max_retry: try: response = requests.post( "https://api.zhumanito.cn/api/water", headers=headers, data=b"", allow_redirects=False, timeout=(25, 30) ) if response.status_code == 200: return response.json() elif response.status_code == 429: retry_after = int(response.headers.get("Retry-After", "1")) retry_count += 1 if retry_count < max_retry: print(f"账号{account_idx}:浇水请求限速,等待{retry_after}秒后重试(第{retry_count}/{max_retry}次)") time.sleep(retry_after) else: raise Exception(f"浇水请求多次限速({max_retry}次),放弃重试") else: raise Exception(f"响应状态码异常: {response.status_code},内容: {response.text}") except json.JSONDecodeError: raise Exception(f"返回非JSON数据: {response.text}") except Exception as e: if retry_count >= max_retry - 1: raise e retry_count += 1 time.sleep(1) return None def loop_watering(headers, account_idx, account, user_logs): step = "循环浇水" user_logs.append(f"🔄 {step}:进入循环浇水(需💧≥20且☀️≥20)") print(f"\n🔄 账号{account_idx}:进入循环浇水(需💧≥20且☀️≥20)") water_headers = headers.copy() water_headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8" while True: water = account["user_data"].get("water_num", 0) sun = account["user_data"].get("sun_num", 0) if water >= 20 and sun >= 20: log_msg = f"🔄 {step}:📌 账号{account_idx}:资源满足(💧{water},☀️{sun}),浇水..." print(log_msg) user_logs.append(log_msg) try: res = water_once(water_headers, account_idx) if res.get("code") == 200: # 浇水成功,更新用户数据 account["user_data"] = res["data"]["user"] land = res["data"].get("land", []) success_msg = f"🔄 {step}:✅ 账号{account_idx}:浇水成功!" status_msg = f"🔄 {step}:📊 剩余:💧{account['user_data']['water_num']},☀️{account['user_data']['sun_num']}" print("="*35) print(success_msg) print(status_msg) # 关键修改:每条浇水成功日志都保留(不合并、不删除) user_logs.append(success_msg) user_logs.append(status_msg) if land: land_msg = f"🔄 {step}:🌱 土地:共{len(land)}块,阶段{land[0]['seed_stage']} 🌱" print(land_msg) user_logs.append(land_msg) print("="*35) time.sleep(random.uniform(4, 5)) else: # 浇水失败(如达上限、其他错误) fail_msg = f"🔄 {step}:❌ 账号{account_idx}:浇水失败:{res.get('msg', '未知错误')}" print(fail_msg) user_logs.append(fail_msg) break except Exception as e: error_msg = f"🔄 {step}:⚠️ 账号{account_idx}:浇水请求异常:{str(e)} ❌" print(error_msg) user_logs.append(error_msg) break else: end_msg = f"🔄 {step}:🔚 账号{account_idx}:资源不足(💧{water},☀️{sun}),停止浇水 ⏹️" print(end_msg) user_logs.append(end_msg) fruit = account['user_data'].get('fruit_num', 0) final_snap = f"📊 最终资源:☀️{sun} 💧{water} 🍅{fruit}" print(final_snap) user_logs.append(final_snap) break def process_user(wid, phone, user_index): user_logs = [f"👤 用户{user_index}: wid={wid} | 手机号={phone}"] print(f"\n===== 开始处理用户 {user_index} (wid: {wid}, 手机号: {phone}) =====") # 登录时传入 wid 和手机号 login_data = login(wid, phone, user_logs) if login_data: auth_token = login_data["token"] headers = {'User-Agent': user_agent, 'authorization': auth_token} account = {"user_data": login_data["user_data"], "land_data": login_data["land_data"]} fruit = account['user_data'].get('fruit_num', 0) print(f"📊 当前番茄数量:{fruit}") user_logs.append(f"📊 当前番茄数量:{fruit}") if account["user_data"].get("new_status", 2) != 2: get_seeds(auth_token, user_logs) check_in(auth_token, user_logs) explore(auth_token, wid, user_logs) current_stage = 0 if account["land_data"] and len(account["land_data"]) > 0: current_stage = account["land_data"][0].get("seed_stage", 0) print(f"\n🧠 账号{user_index}:智能判断... 当前土地状态: {current_stage}") user_logs.append(f"ℹ️ 土地状态: {current_stage}") if current_stage == 5: print("判断:作物已成熟。") user_logs.append("🧠 判断:作物已成熟。") print(f">> 账号{user_index}:执行 [收获]...") harvest_success = harvest(auth_token, user_logs, account) if harvest_success: print(f">> 账号{user_index}:执行 [播种]...") plant_seed(auth_token, user_logs, account) elif current_stage == 0: print("判断:土地为空。") user_logs.append("🧠 判断:土地为空。") print(f">> 账号{user_index}:执行 [播种]...") plant_seed(auth_token, user_logs, account) else: print("判断:作物生长中... 无需收获或播种。") user_logs.append("🧠 判断:作物生长中。") loop_watering(headers, user_index, account, user_logs) else: msg = "获取授权失败,无法执行后续操作 🔒" print(msg) user_logs.append(f"⚠️ {msg}") print(f"===== 完成处理用户 {user_index} =====\n") time.sleep(3) return user_logs if __name__ == "__main__": if not parsed_users or len(parsed_users) == 0: print("未从环境变量TYQH中获取到任何用户信息! 🚫") send("统一茄皇", "未从环境变量TYQH中获取到任何用户信息! 🚫") else: print(f"共检测到 {len(parsed_users)} 个用户,开始依次处理... 👥") all_logs = [] for i, (wid, phone) in enumerate(parsed_users, 1): try: user_logs = process_user(wid, phone, i) all_logs.extend(user_logs) all_logs.append("") except Exception as e: error_msg = f"用户 {i} 处理过程中发生未捕获错误: {str(e)} ❌" print(error_msg) all_logs.append(f"❌ {error_msg}") all_logs.append("") # 推送完整报告(包含所有浇水成功记录) report = render_report(all_logs) print("\n" + "="*50) print("最终推送通知内容:") print(report) print("="*50) send("统一茄皇", report)