From f88af55073a226e41f6b3753123c206ac239437b Mon Sep 17 00:00:00 2001 From: admin <362324317@qq.com> Date: Sat, 16 May 2026 00:45:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=E3=80=8CWX=5FApplet=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WX_Applet/Applet_TXDT.py | 703 +++++++++++++++++++++++++++++++++++++++ WX_Applet/Applet_YYYX.py | 359 ++++++++++++++++++++ 2 files changed, 1062 insertions(+) create mode 100644 WX_Applet/Applet_TXDT.py create mode 100644 WX_Applet/Applet_YYYX.py diff --git a/WX_Applet/Applet_TXDT.py b/WX_Applet/Applet_TXDT.py new file mode 100644 index 0000000..1e994a7 --- /dev/null +++ b/WX_Applet/Applet_TXDT.py @@ -0,0 +1,703 @@ +# cron: 12 10 * * * +# new Env("腾讯地图签到") +import time +import os +import json +import base64 +import requests +import uuid +import hashlib +import random +import threading +import socket +import traceback +from functools import wraps +from requests.adapters import HTTPAdapter + +# ====================== 养鸡场认证Token (自动添加Bearer) ====================== +AUTH_TOKEN = os.getenv('wx_token', '') +if AUTH_TOKEN and not AUTH_TOKEN.startswith('Bearer '): + AUTH_TOKEN = f'Bearer {AUTH_TOKEN}' + +# ====================== 养鸡场服务器配置 ====================== +os.environ['WECHAT_SERVER'] = 'http://192.168.0.250:666' + +# ====================== 腾讯地图小程序配置 ====================== +MAP_APPID = "wx7643d5f831302ab0" # 腾讯地图小程序appid +MAP_ACCESS_KEY = "1" +MAP_SECRET_KEY = "4300eec60bedec22a73408a0d76b03ec" +MAP_HOST = "miniapp.map.qq.com" +MAP_LOGIN_URL = f"https://{MAP_HOST}/minLogin/v2/login" + +# 签到配置 +CHECKIN_ACTIVITY_ID = 1721983577 +CHECKIN_GAME_ID = 1 +CHECKIN_URL = "https://mmapgwh.map.qq.com/activity/v1/checkin" +CHECKIN_TOKEN = "e643d512f085d621bf6c9e80310d0498" +CHECKIN_DEFAULT_SIGN_SECRET = "03a9875e795c3ecff15f617085e72d4cc" +CHECKIN_PATH = "/activity/v1/checkin" + +# ==================== 调试配置 ==================== +DEBUG_MODE = False + +# 抽奖配置 +LOTTERY_GAME_ID = 3 +LOTTERY_DETAIL_URL = "https://mmapgwh.map.qq.com/activity/v1/lottery/detail" +LOTTERY_URL = "https://mmapgwh.map.qq.com/activity/v1/lottery" +LOTTERY_RULE_ID = "tencent_map_lottery" +LOTTERY_DEFAULT_NICK = "微信用户" +LOTTERY_DEFAULT_ICON = "https://4gimg.map.qq.com/map/0fdc3baa5c70ec05e0bb628d266f1ee8.png" + +# 提现配置 +WITHDRAW_GAME_ID = 4 +WITHDRAW_HOME_URL = "https://mmapgwh.map.qq.com/activity/v1/withdraw/home" +WITHDRAW_URL = "https://mmapgwh.map.qq.com/activity/v1/withdraw" +WITHDRAW_RULE_ID = "tencent_map_withdraw" + +# openid本地缓存配置 +OPENID_CACHE_FILE = "腾讯地图签到.json" +OPENID_CACHE_EXPIRE_HOURS = 24000 # 缓存24小时 + +# 快进配置 +MAX_ACCOUNT_TIME = 60 +ACCOUNT_INTERVAL = (2, 4) +REQUEST_TIMEOUT = (5, 8) + +# ====================== openid本地缓存 ====================== +def load_openid_cache(): + """加载本地缓存的openid""" + try: + if os.path.exists(OPENID_CACHE_FILE): + with open(OPENID_CACHE_FILE, 'r', encoding='utf-8') as f: + cache = json.load(f) + return cache + except Exception as e: + print(f"加载openid缓存失败: {e}") + return {} + +def save_openid_cache(wxid, openid, user_id): + """保存openid到本地缓存""" + try: + cache = load_openid_cache() + cache[wxid] = { + "openid": openid, + "user_id": user_id, + "timestamp": time.time() + } + with open(OPENID_CACHE_FILE, 'w', encoding='utf-8') as f: + json.dump(cache, f, ensure_ascii=False, indent=2) + print(f"✅ openid已本地缓存") + except Exception as e: + print(f"保存openid缓存失败: {e}") + +def get_cached_openid(wxid): + """获取缓存的openid,判断是否过期""" + cache = load_openid_cache() + if wxid in cache: + cached = cache[wxid] + age_hours = (time.time() - cached.get("timestamp", 0)) / 3600 + if age_hours < OPENID_CACHE_EXPIRE_HOURS: + print(f"📦 使用缓存openid (缓存时间: {age_hours:.1f}小时前)") + return cached.get("openid"), cached.get("user_id") + else: + print(f"📦 缓存已过期 (缓存时间: {age_hours:.1f}小时前),需要重新获取") + return None, None + +# ====================== 腾讯地图签名算法 ====================== +def map_generate_reqid(): + return hashlib.md5(f"{random.random()} {int(time.time() * 1000)}".encode()).hexdigest() + +def map_generate_seqid(): + return str(uuid.uuid4()) + +def map_generate_sign(reqid, reqtime, business_str, session_id="-1"): + params = { + "appId": MAP_APPID, + "reqId": reqid, + "reqTime": reqtime, + "sessionID": session_id, + "accessKey": MAP_ACCESS_KEY, + "businessStr": business_str + } + sorted_str = "&".join(f"{k}={v}" for k, v in sorted(params.items())) + signed_str = sorted_str + f"&secretKey={MAP_SECRET_KEY}" + return hashlib.sha256(signed_str.encode()).hexdigest() + +# ====================== 腾讯地图签到算法 ====================== +def checkin_generate_sign(reqid, reqtime, path=CHECKIN_PATH): + tmapdefaultstr = f'mapinst=0&mapnonce=0&reqid={reqid}&reqtime={reqtime}{path}{CHECKIN_DEFAULT_SIGN_SECRET}' + tmapdefaultsign = hashlib.md5(tmapdefaultstr.encode()).hexdigest() + timestamp = reqtime[:-3] + signstr = f'request_id={reqid}&from_source={MAP_APPID}×tamp={timestamp}&token={CHECKIN_TOKEN}' + sign = hashlib.sha256(signstr.encode()).hexdigest().upper() + return tmapdefaultsign, sign, timestamp + +# ====================== 工具函数 ====================== +def debug_log(message): + if DEBUG_MODE: + print(message) + +def parse_json_response(response, action_name): + try: + data = response.json() + debug_log(f"🔎 {action_name} 响应JSON: {json.dumps(data, ensure_ascii=False)[:1200]}") + return data + except Exception: + print(f"❌ {action_name} JSON解析失败,响应内容: {response.text[:200] if response.text else '空'}") + return None + +def create_activity_headers(openid, path=CHECKIN_PATH, reqid=None, reqtime=None): + reqid = reqid or str(uuid.uuid4()) + reqtime = reqtime or str(int(time.time() * 1000)) + tmapdefaultsign, sign, timestamp = checkin_generate_sign(reqid, reqtime, path) + headers = { + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541739) XWEB/18955', + 'from_source': MAP_APPID, + 'request_id': reqid, + 'tmap-nonce': '0', + 'tmap-engine': 'web', + 'tmap-reqid': reqid, + 'sign': sign, + 'user_id': openid, + 'tmap-reqtime': reqtime, + 'timestamp': timestamp, + 'tmap-install-id': '0', + 'tmap-default-sign': tmapdefaultsign + } + debug_log(f"🧾 请求头[{path}]: {json.dumps(headers, ensure_ascii=False)}") + return headers + +def create_secure_session(): + session = requests.Session() + session.mount('https://', HTTPAdapter(max_retries=1)) + try: + import urllib3 + urllib3.disable_warnings() + except: + pass + return session + +def fast_retry(func): + @wraps(func) + def wrapper(*args, **kwargs): + for retry in range(1, 2): + try: + result = func(*args, **kwargs) + if result is not None: + return result + except Exception as e: + print(f"🔄 重试1次({str(e)[:30]})") + time.sleep(2) + continue + return None + return wrapper + +# ====================== 获取账号列表 ====================== +def get_wechat_account_list(): + if not AUTH_TOKEN: + print("❌ 无wx_token环境变量") + return [] + url = f"{os.getenv('WECHAT_SERVER')}/prod-api/wechat/wechat/list?pageNum=1&pageSize=1000" + try: + r = requests.get(url, headers={'Authorization': AUTH_TOKEN}, timeout=(3, 6), verify=False) + if r.ok and r.json().get("code") == 200 and isinstance(r.json().get("rows"), list): + return [{"wxid": a["wxId"], "wx_name": a.get("wxName", "未知昵称")} for a in r.json()["rows"] if a.get("wxId")] + print(f"❌ 获取账号列表异常:{r.text[:100]}") + return [] + except Exception as e: + print(f"❌ 获取账号列表失败:{str(e)[:50]}") + return [] + +# ====================== 获取微信code ====================== +@fast_retry +def fetch_code(wxid, wx_name=""): + url = f"{os.getenv('WECHAT_SERVER')}/prod-api/wechat/api/getMiniProgramCode" + name = wx_name if wx_name else wxid[:8] + try: + r = requests.post(url, json={"wxid": wxid, "appid": MAP_APPID}, headers={'Authorization': AUTH_TOKEN}, timeout=(3, 6), verify=False) + if r.ok and r.json().get("code") == 200 and r.json()["data"].get("code"): + code = r.json()["data"]["code"] + print(f"✅ {name} 获取code成功") + return code + print(f"❌ {name} 获取code异常:{r.text[:100]}") + return None + except Exception as e: + print(f"❌ {name} 获取code失败:{str(e)[:50]}") + return None + +# ====================== 腾讯地图小程序登录获取user_id ====================== +def map_login(wxid, wx_name=""): + """腾讯地图小程序登录,获取user_id""" + try: + session = create_secure_session() + + # 获取微信code + code = fetch_code(wxid, wx_name) + if not code: + return None, None, None + + # 生成签名参数 + reqid = map_generate_reqid() + reqtime = str(int(time.time())) + seqid = map_generate_seqid() + + body = { + "seqid": seqid, + "app_id": MAP_APPID, + "auth_code": code, + "devHeader": {} + } + business_str = json.dumps(body, separators=(',', ':')) + sign = map_generate_sign(reqid, reqtime, business_str) + + headers = { + "Content-Type": "application/json", + "mapservice-reqtime": reqtime, + "mapservice-sign": sign, + "mapservice-accesskey": MAP_ACCESS_KEY, + "mapservice-appid": MAP_APPID, + "mapservice-sessionid": "-1", + "mapservice-reqid": reqid, + "mapservice-sign-version": "v2", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541739) XWEB/18955", + "xweb_xhr": "1", + "Referer": f"https://servicewechat.com/{MAP_APPID}/513/page-frame.html", + } + + r = session.post(MAP_LOGIN_URL, data=business_str, headers=headers, timeout=REQUEST_TIMEOUT, verify=False) + + if r.status_code != 200: + print(f"❌ 地图登录失败:{r.status_code}") + return None, None, None + + try: + d = r.json() + except: + print(f"❌ JSON解析失败") + return None, None, None + + if d.get("err_code") != 0: + print(f"❌ 地图登录异常:{d.get('err_msg')}, code={d.get('err_code')}") + return None, None, None + + user_id = d.get("user_id") + openid = d.get("openid") + union_id = d.get("union_id") + session_id = d.get("session_id") + map_session_id = d.get("map_session_id") + + if not user_id: + print(f"❌ 地图登录无user_id") + return None, None, None + + print(f"✅ 地图登录成功 | user_id: {user_id} | openid: {openid[:10] if openid else 'N/A'}...") + + return user_id, openid, {"union_id": union_id, "session_id": session_id, "map_session_id": map_session_id} + + except Exception as e: + print(f"❌ 地图登录异常:{str(e)[:50]}") + return None, None, None + +# ====================== 腾讯地图签到/抽奖/提现 ====================== +def map_checkin(user_id, openid, wx_name=""): + """腾讯地图每日签到""" + try: + session = create_secure_session() + headers = create_activity_headers(openid) + body = { + 'activity_id': CHECKIN_ACTIVITY_ID, + 'game_id': CHECKIN_GAME_ID + } + + print(f"📤 签到请求 - user_id/openid: {openid[:20] if openid else 'None'}...") + debug_log(f"🧾 签到请求体: {json.dumps(body, ensure_ascii=False)}") + r = session.post(CHECKIN_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT, verify=False) + + print(f"📤 签到响应状态: {r.status_code}") + print(f"📤 签到响应内容: {r.text[:500] if r.text else '空'}") + + resp = parse_json_response(r, "签到") + if not resp: + return False, [], False + + code = resp.get('code') + message = resp.get('message', '') + if code == 0: + prizes = resp.get('data', {}).get('prizes', []) + if prizes: + for prize in prizes: + print(f"🎁 每日签到成功:{prize.get('name', '未知奖励')}") + return True, prizes, False + print(f"✅ 每日签到成功(无额外奖励)") + return True, [], False + if code == 11011 or '已经签到' in message: + print(f"✅ 今日已签到:{message}") + return True, [], False + + err_msg = message or '未知错误' + is_invalid_openid = 'openid' in err_msg.lower() or 'trans wx openid' in err_msg.lower() or '511' in str(resp) + print(f"❌ 每日签到失败:{err_msg}") + return False, [], is_invalid_openid + + except Exception as e: + print(f"❌ 签到异常:{str(e)[:50]}") + return False, [], False + +def map_lottery_detail(openid): + """查询抽奖次数""" + try: + session = create_secure_session() + headers = create_activity_headers(openid, path="/activity/v1/lottery/detail") + body = { + "activity_id": CHECKIN_ACTIVITY_ID, + "game_id": LOTTERY_GAME_ID, + "rule_id": LOTTERY_RULE_ID + } + debug_log(f"🧾 抽奖详情请求体: {json.dumps(body, ensure_ascii=False)}") + r = session.post(LOTTERY_DETAIL_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT, verify=False) + resp = parse_json_response(r, "查询抽奖次数") + if not resp: + return 0, False + if resp.get("code") == 0: + count = resp.get("data", {}).get("available_ticket_number", 0) or 0 + print(f"🎯 当前可抽奖次数: {count}") + return int(count), False + err_msg = resp.get('message') or '未知错误' + is_invalid_openid = 'openid' in err_msg.lower() or 'trans wx openid' in err_msg.lower() or '511' in str(resp) + print(f"❌ 查询抽奖次数失败:{err_msg}") + return 0, is_invalid_openid + except Exception as e: + print(f"❌ 查询抽奖次数异常:{str(e)[:50]}") + return 0, False + +def map_lottery(openid): + """执行抽奖""" + lottery_count, invalid_openid = map_lottery_detail(openid) + if invalid_openid: + return [], True + if lottery_count <= 0: + print("ℹ️ 无可用抽奖次数") + return [], False + + session = create_secure_session() + lottery_prizes = [] + for idx in range(1, lottery_count + 1): + try: + headers = create_activity_headers(openid, path="/activity/v1/lottery") + body = { + "activity_id": CHECKIN_ACTIVITY_ID, + "game_id": LOTTERY_GAME_ID, + "rule_id": LOTTERY_RULE_ID, + "nick": LOTTERY_DEFAULT_NICK, + "icon": LOTTERY_DEFAULT_ICON + } + debug_log(f"🧾 抽奖请求体[{idx}]: {json.dumps(body, ensure_ascii=False)}") + r = session.post(LOTTERY_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT, verify=False) + resp = parse_json_response(r, f"抽奖第{idx}次") + if not resp: + continue + if resp.get("code") == 0: + prizes = resp.get("data", {}).get("prizes", []) or [] + lottery_prizes.extend(prizes) + if prizes: + for prize in prizes: + amount = prize.get("amount") + amount_text = f" {amount / 100}金币" if isinstance(amount, (int, float)) and prize.get("type") == "coin" else "" + print(f"🎰 抽奖成功:{prize.get('name', '未知奖励')}{amount_text}") + else: + print(f"🎰 第{idx}次抽奖成功,但未返回奖品") + else: + err_msg = resp.get('message') or '未知错误' + is_invalid_openid = 'openid' in err_msg.lower() or 'trans wx openid' in err_msg.lower() or '511' in str(resp) + print(f"❌ 第{idx}次抽奖失败:{err_msg}") + if is_invalid_openid: + return lottery_prizes, True + break + except Exception as e: + print(f"❌ 第{idx}次抽奖异常:{str(e)[:50]}") + return lottery_prizes, False + +def map_withdraw_home(openid): + """查询提现首页信息,返回(data, invalid_openid)""" + try: + session = create_secure_session() + headers = create_activity_headers(openid, path="/activity/v1/withdraw/home") + body = { + "activity_id": CHECKIN_ACTIVITY_ID, + "game_id": WITHDRAW_GAME_ID, + "rule_id": WITHDRAW_RULE_ID + } + debug_log(f"🧾 提现首页请求体: {json.dumps(body, ensure_ascii=False)}") + r = session.post(WITHDRAW_HOME_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT, verify=False) + resp = parse_json_response(r, "查询提现信息") + if not resp: + return None, False + if resp.get("code") == 0: + data = resp.get("data", {}) + print(f"💰 金币: {data.get('coins', 0)} | 可提现额度: {data.get('withdrawable_amount', 0)} | 门槛: {data.get('current_withdraw_threshold', 0)}") + return data, False + err_msg = resp.get('message') or '未知错误' + is_invalid_openid = 'openid' in err_msg.lower() or 'trans wx openid' in err_msg.lower() or '511' in str(resp) + print(f"❌ 查询提现信息失败:{err_msg}") + return None, is_invalid_openid + except Exception as e: + print(f"❌ 查询提现信息异常:{str(e)[:50]}") + return None, False + +def map_withdraw(openid): + """执行提现,返回 (result_dict_or_None, invalid_openid),result_dict中包含 coins 字段""" + withdraw_info, invalid_openid = map_withdraw_home(openid) + if invalid_openid: + return None, True + if not withdraw_info: + return None, False + + withdrawable_amount = withdraw_info.get("withdrawable_amount", 0) or 0 + threshold = withdraw_info.get("current_withdraw_threshold", 0) or 0 + jackpot = bool(withdraw_info.get("exists_jackpot_withdraw", False)) + + if withdrawable_amount <= 0: + print("ℹ️ 当前无可提现金额") + return {"coins": withdraw_info.get("coins", 0)}, False + if threshold and withdrawable_amount < threshold and not jackpot: + print(f"ℹ️ 提现金额不足门槛,当前可提: {withdrawable_amount},门槛: {threshold}") + return {"coins": withdraw_info.get("coins", 0)}, False + + try: + session = create_secure_session() + headers = create_activity_headers(openid, path="/activity/v1/withdraw") + body = { + "activity_id": CHECKIN_ACTIVITY_ID, + "game_id": WITHDRAW_GAME_ID, + "rule_id": WITHDRAW_RULE_ID, + "jackpot": jackpot + } + debug_log(f"🧾 提现请求体: {json.dumps(body, ensure_ascii=False)}") + r = session.post(WITHDRAW_URL, headers=headers, json=body, timeout=REQUEST_TIMEOUT, verify=False) + resp = parse_json_response(r, "提现") + if not resp: + return None, False + if resp.get("code") == 0: + data = resp.get("data", {}) + batch_id = data.get("batch_id") + print(f"✅ 提现成功,batch_id: {batch_id}") + return {"batch_id": batch_id, "withdrawable_amount": withdrawable_amount, "jackpot": jackpot, "coins": withdraw_info.get("coins", 0)}, False + err_msg = resp.get('message') or '未知错误' + is_invalid_openid = 'openid' in err_msg.lower() or 'trans wx openid' in err_msg.lower() or '511' in str(resp) + print(f"❌ 提现失败:{err_msg}") + return None, is_invalid_openid + except Exception as e: + print(f"❌ 提现异常:{str(e)[:50]}") + return None, False + +# ====================== 单账号处理(增加返回余额) ====================== +def process_account_fast(account, idx, total): + wxid = account["wxid"] + real_name = account["wx_name"].replace("ㅤ", "") if account["wx_name"] else "未知昵称" + print(f"\n------ 账号 {idx}/{total} 🎐 {real_name} -------") + + start_time = time.time() + extra = {} + invalid_openid = False + lottery_prizes = [] + withdraw_result = None + coins = 0 + + def relogin_and_refresh(): + nonlocal user_id, openid, extra + print(f"\n⚠️ openid已失效,重新登录获取...") + user_id, openid, extra = map_login(wxid, real_name) + if not user_id or not openid: + print(f"❌ 重新登录失败,跳过") + return False + save_openid_cache(wxid, openid, user_id) + return True + + # 1. 尝试使用缓存的openid + cached_openid, cached_user_id = get_cached_openid(wxid) + + if cached_openid and cached_user_id: + openid = cached_openid + user_id = cached_user_id + print(f"\n开始每日签到 (使用缓存)...") + checkin_ok, prizes, invalid_openid = map_checkin(user_id, openid, real_name) + else: + # 2. 缓存不存在或已过期,登录获取 + print(f"\n缓存未命中或已过期,开始登录获取...") + user_id, openid, extra = map_login(wxid, real_name) + if not user_id or not openid: + print(f"❌ 登录失败,跳过") + return None + + # 保存到本地缓存 + save_openid_cache(wxid, openid, user_id) + + # 3. 执行签到 + print(f"\n开始每日签到...") + checkin_ok, prizes, invalid_openid = map_checkin(user_id, openid, real_name) + + # 4. 如果openid失效,重新登录 + if invalid_openid: + if not relogin_and_refresh(): + return None + print(f"\n重新签到...") + checkin_ok, prizes, _ = map_checkin(user_id, openid, real_name) + + print(f"\n开始抽奖...") + lottery_prizes, invalid_openid = map_lottery(openid) + if invalid_openid: + if not relogin_and_refresh(): + return None + print(f"\n重新抽奖...") + lottery_prizes, _ = map_lottery(openid) + + print(f"\n开始提现检查...") + withdraw_result, invalid_openid = map_withdraw(openid) + if invalid_openid: + if not relogin_and_refresh(): + return None + print(f"\n重新提现...") + withdraw_result, _ = map_withdraw(openid) + + # 提取余额 + if withdraw_result and isinstance(withdraw_result, dict): + coins = withdraw_result.get("coins", 0) + + elapsed = time.time() - start_time + print(f"⏱️ 账号处理耗时: {elapsed:.2f}秒") + + if elapsed > MAX_ACCOUNT_TIME: + print(f"⚠️ 超过最大时间限制") + + return { + "user_id": user_id, + "open_id": openid, + "nickname": real_name, + "real_name": real_name, + "checkin_ok": checkin_ok, + "prizes": prizes, + "lottery_prizes": lottery_prizes, + "withdraw_result": withdraw_result, + "coins": coins, + **extra + } + +# ====================== 美观表格打印 ====================== +def print_beautiful_table(results): + """打印单账户明细表格""" + # 表头 + header_top = "┌──────┬───────────────────────┬──────────┬──────────┬──────────┬──────────┐" + header_title = "│ 序号 │ 账号名称 │ 签到奖励 │ 抽奖奖励 │ 提现金额 │ 金币余额 │" + header_split = "├──────┼───────────────────────┼──────────┼──────────┼──────────┼──────────┤" + header_bottom = "└──────┴───────────────────────┴──────────┴──────────┴──────────┴──────────┘" + + print("\n" + header_top) + print(header_title) + print(header_split) + + for idx, r in enumerate(results, 1): + name = r['nickname'] + if len(name) > 20: + name = name[:19] + "…" + # 签到奖励汇总 + checkin_prizes = r.get('prizes', []) + if checkin_prizes: + checkin_str = ', '.join([p.get('name', '') for p in checkin_prizes]) + else: + checkin_str = '无' if not r.get('checkin_ok') else '已签到' + # 抽奖奖励汇总 + lottery_prizes = r.get('lottery_prizes', []) + if lottery_prizes: + lottery_str = ', '.join([p.get('name', '') for p in lottery_prizes]) + else: + lottery_str = '无' + # 提现金额 + wd = r.get('withdraw_result') + if wd and isinstance(wd, dict) and wd.get('withdrawable_amount'): + withdraw_str = f"{wd['withdrawable_amount']} 金币" + else: + withdraw_str = '0' + # 金币余额 + coins = r.get('coins', 0) + line = f"│ {idx:^4} │ {name:<21} │ {checkin_str:<8} │ {lottery_str:<8} │ {withdraw_str:<8} │ {coins:^8} │" + print(line) + + print(header_bottom) + +# ====================== 主程序 ====================== +if __name__ == "__main__": + start_total = time.time() + result_lines = [] + fail_count = 0 + checkin_count = 0 + + # 域名检测 + print("🔍 检测腾讯地图域名连通性...") + domains = ["miniapp.map.qq.com", "mmapgwh.map.qq.com"] + for domain in domains: + domain_reachable = False + for _ in range(2): + try: + socket.setdefaulttimeout(10) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((domain, 443)) + s.close() + domain_reachable = True + break + except: + time.sleep(1) + print(f"{'✅' if domain_reachable else '⚠️'} {domain} {'连通性检测通过' if domain_reachable else '检测未通过'}") + + # 获取账号列表 + account_list = get_wechat_account_list() + if not account_list: + print("❌ 无账号,退出") + exit(1) + total = len(account_list) + print(f"\n✅ 共{total}个账号,开始处理(单账号最大{MAX_ACCOUNT_TIME}秒)\n") + + # 批量处理 + for idx, account in enumerate(account_list, 1): + result = process_account_fast(account, idx, total) + if result: + result_lines.append(result) + if result.get("checkin_ok"): + checkin_count += 1 + else: + fail_count += 1 + time.sleep(random.randint(*ACCOUNT_INTERVAL)) + + # 美观表格输出 + print_beautiful_table(result_lines) + + # 汇总统计 + total_coins = sum(r.get('coins', 0) for r in result_lines) + # 累计签到金币(amount 除以100) + total_checkin_gold = sum( + sum(p.get('amount', 0) for p in r.get('prizes', []) if p.get('type') == 'coin') / 100 + for r in result_lines + ) + # 累计抽奖金币 + total_lottery_gold = sum( + sum(p.get('amount', 0) for p in r.get('lottery_prizes', []) if p.get('type') == 'coin') / 100 + for r in result_lines + ) + # 累计提现金额(从 withdrawable_amount 提取) + total_withdraw = 0 + for r in result_lines: + wd = r.get('withdraw_result') + if wd and isinstance(wd, dict) and wd.get('withdrawable_amount'): + total_withdraw += wd['withdrawable_amount'] + + print("\n" + "=" * 60) + print("📊 账号汇总:") + print(f" 总账号: {len(account_list)}") + print(f" 成功: {len(result_lines)} 失败: {fail_count} 签到成功: {checkin_count}") + print(f" 累计签到获得金币: {total_checkin_gold:.2f}") + print(f" 累计抽奖获得金币: {total_lottery_gold:.2f}") + print(f" 累计提现金额: {total_withdraw} 金币") + print(f" 当前总金币余额: {total_coins}") + print("=" * 60) + + total_time = time.time() - start_total + print(f"⏱️ 总耗时: {total_time:.2f}秒") \ No newline at end of file diff --git a/WX_Applet/Applet_YYYX.py b/WX_Applet/Applet_YYYX.py new file mode 100644 index 0000000..05cd654 --- /dev/null +++ b/WX_Applet/Applet_YYYX.py @@ -0,0 +1,359 @@ +# cron: 28 8 * * * +# const $ = new Env("云影优选"); +""" +云影优选 - 完全自动版(1并发 + 独立代理 + 5秒延迟) +配置通过环境变量加载,不再硬编码敏感信息。 +""" + +import os +import requests +import json +import uuid +import time +import random +import threading +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime +from typing import List, Dict, Optional + +# ========== 从环境变量读取配置 ========== +wx_cloud = os.getenv("wx_cloud", "http://127.0.0.1:666") +API_URL = f"{wx_cloud}/prod-api" +APPID = "wx1661b44e984b6fcb" +API_BASE = "https://cid-cps-api.heliang.cc" + +# 敏感凭据:必须在环境变量中设置,脚本内不留痕迹 +wx_token = os.getenv("wx_token", "") +if not wx_token: + print("❌ 错误:环境变量 wx_token 未设置,请先 export wx_token='Bearer ...'") + exit(1) + +HEADERS = { + "Authorization": f"Bearer {wx_token}", + "Content-Type": "application/json" +} + +# ========== 代理配置 ========== +USE_PROXY = os.getenv("USE_PROXY", "true").lower() == "true" +PROXY_YYYX_URL = os.getenv("PROXY_YYYX_URL", "") + +# 并发数 +MAX_WORKERS = int(os.getenv("MAX_WORKERS", "1")) +# 账号间启动延迟(秒) +ACCOUNT_START_DELAY = float(os.getenv("ACCOUNT_START_DELAY", "5")) + +# 线程锁 +print_lock = threading.Lock() + + +def safe_print(msg): + with print_lock: + print(msg) + + +def get_one_proxy() -> Optional[Dict]: + """获取一个代理IP(每个账号独立调用)""" + if not PROXY_YYYX_URL: + safe_print(" ⚠️ 未配置代理提取链接,跳过获取代理") + return None + try: + resp = requests.get(PROXY_YYYX_URL, timeout=10) + proxy_ip = resp.text.strip() + + if proxy_ip and len(proxy_ip) > 5 and not proxy_ip.startswith("{"): + proxy_url = f"http://{proxy_ip}" + return {"http": proxy_url, "https": proxy_url} + return None + except Exception as e: + safe_print(f" ⚠️ 获取代理失败: {e}") + return None + + +# ========== 云端微信接口 ========== +def get_online_accounts() -> List[Dict]: + resp = requests.get(f"{API_URL}/wechat/wechat/list", headers=HEADERS, + params={"pageNum": 1, "pageSize": 999}, verify=False) + result = resp.json() + if result.get("code") == 200: + return [a for a in result.get("rows", []) if a.get("onlineStatus") == "1"] + return [] + + +def get_code(wxid: str) -> Optional[str]: + resp = requests.post(f"{API_URL}/wechat/api/getMiniProgramCode", + headers=HEADERS, + json={"wxid": wxid, "appid": APPID}, + verify=False) + if resp.status_code == 200: + data = resp.json() + return data.get("data", {}).get("code") or data.get("code") or data.get("data") + return None + + +def login_to_get_token(code: str) -> Optional[str]: + headers = { + "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15", + "Content-Type": "application/json", + "appid": APPID, + "mp-platform": "weapp", + "x-web-id": str(uuid.uuid4()), + "Referer": f"https://servicewechat.com/{APPID}/91/page-frame.html" + } + body = {"code": code, "errMsg": "login:ok"} + + try: + resp = requests.post(f"{API_BASE}/user/login", headers=headers, json=body, verify=False) + result = resp.json() + if result.get("error_code") == 0: + return result.get("data", {}).get("token") + except: + pass + return None + + +# ========== 云影优选任务 ========== +class YunyingTask: + def __init__(self, token: str, name: str, proxy: dict = None): + self.token = token + self.name = name + self.base_url = "https://cid-cps-api.heliang.cc" + self.appid = APPID + self.proxy = proxy + + def _generate_uuid(self) -> str: + return str(uuid.uuid4()) + + def _build_headers(self, pageurl="pages%2Fpackage%2Fcashback%2Findex", pageurl_pre="pages%2Fcustom-tabbar%2Findex"): + ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 MicroMessenger/8.0.50 NetType/WIFI Language/zh_CN" + return { + "User-Agent": ua, + "Content-Type": "application/json", + "appid": self.appid, + "mp-platform": "weapp", + "x-token": self.token, + "x-web-id": self._generate_uuid(), + "pageurl": pageurl, + "pageurl-pre": pageurl_pre, + "Referer": f"https://servicewechat.com/{self.appid}/91/page-frame.html" + } + + def _request(self, url: str, data: dict = None, headers: dict = None) -> dict: + try: + resp = requests.post(url, headers=headers or self._build_headers(), + json=data or {}, timeout=30, verify=False, proxies=self.proxy) + return resp.json() + except Exception as e: + safe_print(f" [{self.name}] 请求失败: {e}") + return {} + + def checkin(self) -> bool: + result = self._request(f"{self.base_url}/activity/checkin") + if result.get("error_code") == 0: + coin = result.get("data", {}).get("coin", 0) + safe_print(f" [{self.name}] ✅ 签到成功 +{coin}金币") + return True + else: + msg = result.get("msg", "失败") + if "已签到" in msg: + safe_print(f" [{self.name}] ⚠️ 今日已签到") + else: + safe_print(f" [{self.name}] ❌ 签到失败: {msg}") + return False + + def get_balance(self) -> Dict: + result = self._request(f"{self.base_url}/coin/mine") + if result.get("error_code") == 0: + coin = result.get("data", {}).get("coin", 0) + amount = result.get("data", {}).get("left_amount", 0) + safe_print(f" [{self.name}] 💰 金币: {coin} | 余额: {amount}元") + return {"coin": coin, "amount": amount} + return {"coin": 0, "amount": 0} + + def watch_video(self) -> bool: + result = self._request(f"{self.base_url}/activity/video/reward") + if result.get("error_code") == 0: + coin = result.get("data", {}).get("reward_amount", 0) + safe_print(f" [{self.name}] 📺 看广告 +{coin}金币") + return True + return False + + def coin_to_rmb(self, coin: int) -> bool: + result = self._request(f"{self.base_url}/coin/to-rmb", data={"version": 1}) + if result.get("error_code") == 0: + safe_print(f" [{self.name}] 💱 兑换成功! {coin}金币 → {coin/10}元") + return True + else: + safe_print(f" [{self.name}] ❌ 兑换失败: {result.get('msg')}") + return False + + def get_withdrawal_list(self) -> List[Dict]: + headers = self._build_headers( + pageurl="pages%2Fpackage%2Fcashback%2Fmy-cash%2Findex", + pageurl_pre="pages%2Fpackage%2Fcashback%2Findex" + ) + result = self._request(f"{self.base_url}/coin/withdrawal-list", headers=headers) + + if result.get("error_code") == 0: + items = result.get("data", []) + available = [{"permission_id": i.get("permission_id"), "amount": i.get("amount"), "rmb": i.get("rmb")} + for i in items if i.get("is_available") == True] + available.sort(key=lambda x: x.get("rmb", 0), reverse=True) + return available + return [] + + def withdraw(self, permission_id: str, amount: int, rmb: float) -> bool: + headers = self._build_headers( + pageurl="pages%2Fpackage%2Fcashback%2Fmy-cash%2Findex", + pageurl_pre="pages%2Fpackage%2Fcashback%2Findex" + ) + result = self._request(f"{self.base_url}/coin/withdrawal", + data={"permission_id": permission_id, "amount": amount}, + headers=headers) + if result.get("error_code") == 0: + safe_print(f" [{self.name}] 💸 提现成功! {rmb}元已到账") + return True + else: + safe_print(f" [{self.name}] ❌ 提现失败: {result.get('msg')}") + return False + + def auto_withdraw(self): + balance = self.get_balance() + + if balance.get("coin", 0) > 1: + self.coin_to_rmb(balance.get("coin", 0)) + time.sleep(random.uniform(1, 2)) + balance = self.get_balance() + + withdraw_list = self.get_withdrawal_list() + + if not withdraw_list: + safe_print(f" [{self.name}] ℹ️ 暂无可用提现选项") + return + + for item in withdraw_list: + rmb = item.get("rmb", 0) + if balance.get("amount", 0) >= rmb: + self.withdraw(item.get("permission_id"), item.get("amount"), rmb) + time.sleep(random.uniform(1, 2)) + balance = self.get_balance() + else: + safe_print(f" [{self.name}] ⚠️ 余额不足,无法提现 {rmb}元") + break + + def do_tasks(self): + safe_print(f"\n{'='*50}") + safe_print(f"👤 账号: {self.name}") + if self.proxy: + safe_print(f"🌐 代理: {self.proxy.get('http', '')[:50]}...") + safe_print(f"{'='*50}") + + self.checkin() + time.sleep(random.uniform(1, 2)) + + self.get_balance() + time.sleep(random.uniform(1, 2)) + + safe_print(f" [{self.name}] 🎬 开始看广告...") + success_count = 0 + for i in range(10): + if self.watch_video(): + success_count += 1 + time.sleep(random.uniform(2, 4)) + + safe_print(f" [{self.name}] 📊 完成: 成功看广告 {success_count}/5 次") + + self.auto_withdraw() + + safe_print(f" [{self.name}] 📈 最终状态:") + self.get_balance() + + +def process_one_account(acc: Dict, index: int, total: int) -> Dict: + """处理单个账号 - 每个账号独立获取代理""" + wxid = acc.get("wxId") + name = acc.get("wxName", f"账号{index}") + + safe_print(f"\n🔑 [{index}/{total}] {name} 开始处理...") + + # 每个账号独立获取一个新代理 + proxy = get_one_proxy() if USE_PROXY else None + if proxy: + safe_print(f" [{name}] 🌐 获取独立代理成功") + else: + safe_print(f" [{name}] 🌐 无代理/代理未配置") + + # 获取code + code = get_code(wxid) + if not code: + safe_print(f" [{name}] ❌ 获取code失败") + return {"name": name, "success": False, "error": "获取code失败"} + + # 换取token + token = login_to_get_token(code) + if not token: + safe_print(f" [{name}] ❌ 换取token失败") + return {"name": name, "success": False, "error": "换取token失败"} + + safe_print(f" [{name}] ✅ token: {token[:20]}...") + + # 执行任务 + task = YunyingTask(token, name, proxy) + task.do_tasks() + + return {"name": name, "success": True} + + +def main(): + print("=" * 60) + print("🔄 云影优选 - 全自动版") + print(f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"🚀 并发数: {MAX_WORKERS}") + print(f"⏱️ 启动延迟: 每账号 {ACCOUNT_START_DELAY} 秒") + print(f"🌐 代理模式: {'每个账号独立代理' if USE_PROXY else '关闭'}") + print("=" * 60) + + # 获取云端微信账号 + print("\n📱 获取云端微信账号...") + accounts = get_online_accounts() + + if not accounts: + print("❌ 没有在线账号") + return + + print(f"✅ 在线账号: {len(accounts)}个") + print(f"🚀 开始并发处理,每 {ACCOUNT_START_DELAY} 秒启动一个账号...\n") + + results = [] + futures = [] + + with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: + # 逐个提交任务,每个任务延迟启动 + for i, acc in enumerate(accounts): + future = executor.submit(process_one_account, acc, i+1, len(accounts)) + futures.append(future) + # 每个任务提交后延迟2秒再提交下一个 + if i < len(accounts) - 1: + time.sleep(ACCOUNT_START_DELAY) + + # 等待所有任务完成 + for future in as_completed(futures): + try: + result = future.result() + results.append(result) + except Exception as e: + safe_print(f"❌ 线程执行失败: {e}") + + # 统计结果 + success_count = sum(1 for r in results if r.get("success")) + print("\n" + "=" * 60) + print(f"📊 执行完成!") + print(f" 总账号: {len(accounts)}") + print(f" 成功: {success_count}") + print(f" 失败: {len(accounts) - success_count}") + print("=" * 60) + + +if __name__ == "__main__": + import urllib3 + urllib3.disable_warnings() + main() \ No newline at end of file