上传文件至「WX_Applet」
This commit is contained in:
703
WX_Applet/Applet_TXDT.py
Normal file
703
WX_Applet/Applet_TXDT.py
Normal file
@@ -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}秒")
|
||||
359
WX_Applet/Applet_YYYX.py
Normal file
359
WX_Applet/Applet_YYYX.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user