Files
Yangmao_Script/WX_Applet/Applet_TXDT.py
2026-05-16 00:45:02 +08:00

703 lines
29 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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}&timestamp={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}")