# cron: 19 12,20 * * * # const $ = new Env("顺丰2026端午活动"); import hashlib import json import os import random import re import time from datetime import datetime from typing import Dict, List, Optional, Any from urllib.parse import unquote from concurrent.futures import ThreadPoolExecutor, as_completed from threading import Lock import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning # 禁用SSL警告 requests.packages.urllib3.disable_warnings(InsecureRequestWarning) inviteId = [] # 运行时从账号列表动态提取 # ==================== 配置常量 ==================== PROXY_TIMEOUT = 15 MAX_PROXY_RETRIES = 5 REQUEST_RETRY_COUNT = 3 CONCURRENT_NUM = int(os.getenv('SFBF', '1')) if CONCURRENT_NUM > 20: CONCURRENT_NUM = 20 elif CONCURRENT_NUM < 1: CONCURRENT_NUM = 1 # 代理开关(默认启用) ENABLE_PROXY = os.getenv('ENABLE_PROXY', 'flase').lower() == 'true' print_lock = Lock() # 端午龙舟活动配置 ACTIVITY_CODE = "DRAGONBOAT_2026" TOKEN = 'wwesldfs29aniversaryvdld29' SYS_CODE = 'MCS-MIMP-CORE' # 需要跳过的任务类型(需要实际操作的) SKIP_TASK_TYPES = [ 'SEND_INTERNATIONAL_PACKAGE', # 寄一单国际件 'LOOK_BIG_PACKAGE_GET_CASH', # 寄大件重货 'SEND_SUCCESS_RECALL', # 去寄快递 'CHARGE_NEW_EXPRESS_CARD', # 充值新速运通全国卡 'CHARGE_COLLECT_ALL', # 充值得8888金币 'OPEN_FAMILY_HOME_MUTUAL', # 开通家庭8折互寄权益 'BUY_PAID_PACKET', # 购买付费券包 'INVITEFRIENDS_PARTAKE_ACTIVITY', # 邀好友首次访问活动 ] # ==================== 日志 ==================== class Logger: def __init__(self): self.messages: List[str] = [] self.lock = Lock() def _log(self, icon: str, msg: str): line = f"{icon} {msg}" with print_lock: print(line) with self.lock: self.messages.append(line) def info(self, msg): self._log('📝', msg) def success(self, msg): self._log('✅', msg) def warning(self, msg): self._log('⚠️', msg) def error(self, msg): self._log('❌', msg) def task(self, msg): self._log('🎯', msg) def medal(self, msg): self._log('🏅', msg) # ==================== 代理管理器 ==================== class ProxyManager: def __init__(self, api_url: str): self.api_url = api_url def get_proxy(self) -> Optional[Dict[str, str]]: # 如果代理开关关闭,直接返回 None if not ENABLE_PROXY: return None try: if not self.api_url: return None response = requests.get(self.api_url, timeout=10) if response.status_code == 200: # 尝试解析 JSON try: data = response.json() # 假设 JSON 结构为 {"data":{"list":[{"ip":"x.x.x.x","port":"xxxx"}]}} if 'data' in data and 'list' in data['data'] and data['data']['list']: proxy_info = data['data']['list'][0] ip = proxy_info.get('ip') port = proxy_info.get('port') if ip and port: proxy = f'http://{ip}:{port}' with print_lock: print(f"✅ 获取代理: {proxy}") return {'http': proxy, 'https': proxy} except (json.JSONDecodeError, KeyError, IndexError): # 不是 JSON,按纯文本处理(兼容旧格式) proxy_text = response.text.strip() if ':' in proxy_text: proxy = proxy_text if proxy_text.startswith('http') else f'http://{proxy_text}' display = proxy if '@' in proxy: parts = proxy.split('@') display = f"http://***:***@{parts[-1]}" with print_lock: print(f"✅ 获取代理: {display}") return {'http': proxy, 'https': proxy} with print_lock: print(f"❌ 获取代理失败: 无法解析代理数据") return None else: with print_lock: print(f"❌ 获取代理失败: HTTP {response.status_code}") return None except Exception as e: with print_lock: print(f"❌ 获取代理异常: {str(e)[:100]}") return None # ==================== HTTP客户端 ==================== class SFHttpClient: def __init__(self, proxy_manager: ProxyManager): self.proxy_manager = proxy_manager self.session = requests.Session() self.session.verify = False # 根据开关决定是否尝试获取代理 if ENABLE_PROXY: proxy = self.proxy_manager.get_proxy() if proxy: self.session.proxies = proxy else: if self.proxy_manager.api_url: print("⚠️ 代理获取失败,将不使用代理") # else: # print("🔒 代理已禁用(ENABLE_PROXY=false)") self.headers = { 'Host': 'mcs-mimp-web.sf-express.com', '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(0xf254173b) XWEB/19027', 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json', 'channel': 'xcxpart', 'platform': 'MINI_PROGRAM', 'accept-language': 'zh-CN,zh;q=0.9', } def _generate_sign(self) -> Dict[str, str]: timestamp = str(int(round(time.time() * 1000))) data = f'token={TOKEN}×tamp={timestamp}&sysCode={SYS_CODE}' signature = hashlib.md5(data.encode()).hexdigest() return { 'syscode': SYS_CODE, 'timestamp': timestamp, 'signature': signature, } def request(self, url: str, data: Optional[Dict] = None, method: str = 'POST') -> Optional[Dict]: retry_count = 0 # 如果代理开关关闭,最多只进行一次尝试(不切换代理) max_proxy_retries = MAX_PROXY_RETRIES if ENABLE_PROXY else 1 proxy_retry_count = 0 while proxy_retry_count < max_proxy_retries: # 每次请求重新生成签名 sign_data = self._generate_sign() headers = {**self.headers, **sign_data} try: if method == 'POST': resp = self.session.post(url, headers=headers, json=data or {}, timeout=PROXY_TIMEOUT) else: resp = self.session.get(url, headers=headers, timeout=PROXY_TIMEOUT) resp.raise_for_status() try: result = resp.json() if result is None: retry_count += 1 if retry_count < REQUEST_RETRY_COUNT: time.sleep(2) continue return None return result except (json.JSONDecodeError, ValueError): retry_count += 1 if retry_count < REQUEST_RETRY_COUNT: time.sleep(2) continue return None except requests.exceptions.RequestException as e: retry_count += 1 error_str = str(e) # 代理相关错误,仅当开关开启时尝试切换代理 if ENABLE_PROXY and ('ProxyError' in error_str or 'SSLError' in error_str or 'ConnectionError' in error_str): proxy_retry_count += 1 if proxy_retry_count < MAX_PROXY_RETRIES: new_proxy = self.proxy_manager.get_proxy() if new_proxy: self.session.proxies = new_proxy retry_count = 0 # 换代理后重置请求重试计数 time.sleep(2) continue # 普通请求错误,重试 if retry_count < REQUEST_RETRY_COUNT: time.sleep(2) continue return None except Exception: return None return None def login(self, url: str) -> tuple: """登录(兼容URL和CK格式)""" try: decoded_input = unquote(url) if decoded_input.startswith('sessionId=') or '_login_mobile_=' in decoded_input: cookie_dict = {} for item in decoded_input.split(';'): item = item.strip() if '=' in item: k, v = item.split('=', 1) cookie_dict[k] = v for k, v in cookie_dict.items(): self.session.cookies.set(k, v, domain='mcs-mimp-web.sf-express.com') user_id = cookie_dict.get('_login_user_id_', '') phone = cookie_dict.get('_login_mobile_', '') return (True, user_id, phone) if phone else (False, '', '') else: self.session.get(unquote(url), headers=self.headers, timeout=PROXY_TIMEOUT) cookies = self.session.cookies.get_dict() user_id = cookies.get('_login_user_id_', '') phone = cookies.get('_login_mobile_', '') return (True, user_id, phone) if phone else (False, '', '') except Exception as e: print(f'登录异常: {str(e)}') return False, '', '' # ==================== 端午龙舟活动任务执行器 ==================== class DragonBoatExecutor: # MODIFIED: 增加 user_id 参数 def __init__(self, http: SFHttpClient, logger: Logger, user_id: str, invite_pool: list = None): self.http = http self.logger = logger self.user_id = user_id self.invite_pool = invite_pool or [] # ---------- API 方法 ---------- def get_activity_index(self) -> Optional[Dict]: """获取端午龙舟活动首页信息""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026IndexService~index' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', {}) return None def get_task_list(self) -> Optional[List[Dict]]: """获取端午龙舟活动任务列表""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~taskList' data = { "activityCode": ACTIVITY_CODE, "channelType": "SFAPP" } resp = self.http.request(url, data=data) if resp and resp.get('success'): return resp.get('obj', []) else: error_msg = resp.get('errorMessage', '未知错误') if resp else '请求失败' self.logger.error(f'获取任务列表失败: {error_msg}') return None def finish_task(self, task_code: str) -> bool: """完成任务(浏览类)""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonRoutePost/memberEs/taskRecord/finishTask' data = {"taskCode": task_code} resp = self.http.request(url, data=data) if resp and resp.get('success'): return True return False # ========== 新增:专用接口-领取寄件会员权益 ========== def receive_vip_benefit(self) -> bool: """领取寄件会员权益(特殊任务)""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberManage~memberEquity~commonEquityReceive' data = {"key": "surprise_benefit"} resp = self.http.request(url, data=data) if resp and resp.get('success'): self.logger.success(f'[领取寄件会员权益] 完成成功') if resp.get('obj'): self.logger.info(f'领取详情: {resp["obj"]}') return True else: error_msg = resp.get('errorMessage', '未知错误') if resp else '请求失败' self.logger.warning(f'[领取寄件会员权益] 完成失败: {error_msg}') return False # ================================================= def get_accrued_task_award(self) -> Optional[Dict]: """获取累计任务奖励进度""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026TaskService~getAccruedTaskAward' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', {}) return None def get_user_rest_integral(self) -> int: """查询用户剩余积分""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~getUserRestIntegral' resp = self.http.request(url, data={}) if resp and resp.get('success'): points = resp.get('obj', 0) self.logger.info(f'当前可用积分: {points}') return points return 0 def exchange_points_for_crush(self) -> bool: """积分兑换砸粽子次数(10积分兑1次)""" points = self.get_user_rest_integral() if points < 10: self.logger.warning(f'积分不足10,无法兑换(当前: {points})') return False url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026TaskService~integralExchange' data = { "exchangeNum": 1, "activityCode": ACTIVITY_CODE } resp = self.http.request(url, data=data) if resp and resp.get('success'): self.logger.success('积分兑换成功,获得1次砸粽子次数') return True else: error_msg = resp.get('errorMessage', '未知错误') if resp else '请求失败' self.logger.warning(f'积分兑换失败: {error_msg}') return False def query_zongzi_status(self) -> Optional[Dict]: """查询粽子状态(金币、粽子数量等)""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026ZongziService~queryStatus' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', {}) return None def crush_zongzi(self) -> Optional[Dict]: """砸粽子""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026ZongziService~crush' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', {}) else: error_msg = resp.get('errorMessage', '未知错误') if resp else '请求失败' self.logger.warning(f'砸粽子失败: {error_msg}') return None def get_daily_gift_status(self, city_code: str = '551') -> Optional[Dict]: """查询每日礼包状态""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026DailyService~getDailyGiftStatus' data = {"cityCode": city_code} resp = self.http.request(url, data=data) if resp and resp.get('success'): return resp.get('obj', {}) return None def query_extra_reward_cards(self) -> Optional[List]: """查询额外奖励卡片(倒计时等)""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026ZongziService~queryExtraRewardCards' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', []) return None def get_family_status(self) -> Optional[Dict]: """查询家庭权益状态""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026FamilyService~familyStatus' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', {}) return None # ---------- 邀请相关 ---------- def do_invite(self): """列表内账号互相邀请""" try: pool = self.invite_pool if self.invite_pool else inviteId available_invites = [inv for inv in pool if inv != self.user_id] if not available_invites: self.logger.warning("没有可用的邀请对象(排除自身后为空),跳过邀请") return random_invite = random.choice(available_invites) url = "https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026IndexService~index" data = {"inviteType": 1, "inviteUserId": random_invite} resp = self.http.request(url, data=data) self.logger.info(f"已向 {random_invite} 发起邀请") except Exception as e: self.logger.error(f"邀请初始化异常: {str(e)}") def get_invite_list(self) -> Optional[List[Dict]]: """查询已邀请好友列表""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2026TaskService~taskInviteList' resp = self.http.request(url, data={}) if resp and resp.get('success'): return resp.get('obj', []) else: error_msg = resp.get('errorMessage', '未知错误') if resp else '请求失败' self.logger.warning(f"查询邀请列表失败: {error_msg}") return None def do_tasks(self, result: Dict) -> None: """执行所有可自动完成的任务""" tasks = self.get_task_list() if tasks is None: return self.logger.info(f'共发现 {len(tasks)} 个任务') for task in tasks: task_name = task.get('taskName', '未知') task_type = task.get('taskType', '') task_code = task.get('taskCode', '') status = task.get('status') process = task.get('process', '') rest_finish = task.get('restFinishTime', 0) virtual_token = task.get('virtualTokenNum', 0) # status=3 表示已完成且已领奖,status=1且restFinishTime=0表示已完成待领奖 if status == 3 or (status == 1 and rest_finish <= 0): can_receive = task.get('canReceiveTokenNum', 0) if can_receive > 0: self.logger.info(f'[{task_name}] 已完成,待领取 {can_receive} 次砸粽子机会') else: self.logger.success(f'[{task_name}] 已完成 ({process})') continue if task_type in SKIP_TASK_TYPES: self.logger.info(f'[{task_name}] 需要实际操作,跳过') continue # 积分兑换特殊处理 if task_type == 'INTEGRAL_EXCHANGE': self.logger.task(f'[{task_name}] 尝试用积分兑换砸粽子次数...') if self.exchange_points_for_crush(): self.logger.success(f'[{task_name}] 积分兑换成功') result['tasks_completed'] += 1 continue # ========== 领取寄件会员权益特殊处理 ========== if task_type == 'RECEIVE_VIP_BENEFIT': self.logger.task(f'[{task_name}] 尝试专用接口领取...') if self.receive_vip_benefit(): result['tasks_completed'] += 1 continue # ================================================= # 有taskCode的任务尝试自动完成(通用接口) if task_code: self.logger.task(f'[{task_name}] 尝试完成任务 (taskCode: {task_code})') if self.finish_task(task_code): self.logger.success(f'[{task_name}] 完成成功,可获得 {virtual_token} 次砸粽子机会') result['tasks_completed'] += 1 else: self.logger.warning(f'[{task_name}] 完成失败') time.sleep(1) else: self.logger.info(f'[{task_name}] 无taskCode,跳过 ({task_type})') def do_fetch_rewards(self, result: Dict) -> None: """查看累计任务奖励进度""" self.logger.info('查看累计任务奖励进度...') time.sleep(1) award = self.get_accrued_task_award() if award: progress = award.get('currentProgress', 0) config = award.get('progressConfig', {}) if config: milestones = ', '.join([f'{k}个任务得{v}次砸粽机会' for k, v in sorted(config.items(), key=lambda x: int(x[0]))]) self.logger.info(f'累计完成任务数: {progress} (里程碑: {milestones})') else: self.logger.info(f'累计完成任务数: {progress}') def do_daily_gift(self, result: Dict) -> None: """查看每日礼包状态""" self.logger.info('查看每日礼包...') time.sleep(1) gift = self.get_daily_gift_status() if gift: if gift.get('received'): products = gift.get('dailyGiftProductList', []) if products: names = ', '.join([p.get('productName', '未知') for p in products]) self.logger.success(f'今日礼包已领取: {names}') else: self.logger.success('今日礼包已领取') elif gift.get('canReceive'): self.logger.info('每日礼包待领取(可能需手动领取)') else: self.logger.info('每日礼包暂不可领取') else: self.logger.warning('获取每日礼包状态失败') def do_extra_rewards(self, result: Dict) -> None: """查询额外奖励卡片(倒计时等)""" cards = self.query_extra_reward_cards() if cards: for card in cards: extra_type = card.get('extraType', '') latest_time = card.get('latestTime', '') countdown = card.get('countdownHours', 0) type_map = {'DAILY': '每日奖励', 'BUSINESS_COUPON': '商业券'} type_name = type_map.get(extra_type, extra_type) self.logger.info(f'额外奖励 [{type_name}]: 上次领取 {latest_time}, 倒计时 {countdown}h') def do_crush_zongzi(self, result: Dict) -> None: """砸粽子直到没有粽子""" # 先查看当前状态 zongzi_status = self.query_zongzi_status() if zongzi_status: accounts = zongzi_status.get('currentAccountList', []) gold_coin_balance = 0 zongzi_balance = 0 for acc in accounts: if acc.get('currency') == 'GOLD_COIN': gold_coin_balance = acc.get('balance', 0) elif acc.get('currency') == 'GOLD_ZONGZI': zongzi_balance = acc.get('balance', 0) total_crush = zongzi_status.get('totalCrushTimes', 0) self.logger.info(f'当前金币: {gold_coin_balance}, 金粽子: {zongzi_balance}, 已砸次数: {total_crush}') if zongzi_balance <= 0: self.logger.info('无金粽子可砸,跳过') return # 开始砸粽子 self.logger.info('开始砸粽子...') crush_count = 0 max_crush = 30 while crush_count < max_crush: time.sleep(1) crush_result = self.crush_zongzi() if crush_result is None: # 检查是否没粽子了 current_status = self.query_zongzi_status() if current_status: for acc in current_status.get('currentAccountList', []): if acc.get('currency') == 'GOLD_ZONGZI': if acc.get('balance', 0) <= 0: self.logger.info('金粽子已用完') break break received = crush_result.get('receivedAccountList', []) current_accounts = crush_result.get('currentAccountList', []) if received: for item in received: currency = item.get('currency', '未知') amount = item.get('amount', 0) self.logger.medal(f'砸粽子获得: {currency} x{amount}') result['crush_detail'].append({'type': currency, 'amount': amount}) result['gold_coin_earned'] += amount crush_count += 1 else: self.logger.info('没有获得奖励') break # 检查剩余粽子 zongzi_balance = 0 gold_coin_balance = 0 for acc in current_accounts: if acc.get('currency') == 'GOLD_ZONGZI': zongzi_balance = acc.get('balance', 0) elif acc.get('currency') == 'GOLD_COIN': gold_coin_balance = acc.get('balance', 0) result['gold_coin_balance'] = gold_coin_balance self.logger.info(f'剩余粽子: {zongzi_balance}, 金币: {gold_coin_balance}') if zongzi_balance <= 0: break self.logger.info(f'砸粽子完成,本次共获得 {result["gold_coin_earned"]} 金币') def run(self) -> Dict[str, Any]: """执行端午龙舟活动全流程""" result = { 'tasks_completed': 0, 'gold_coin_earned': 0, 'crush_detail': [], 'gold_coin_balance': 0, } # MODIFIED: 在任务开始前先发送邀请 self.do_invite() # ===== 查询并显示已邀请好友列表 ===== invite_list = self.get_invite_list() if invite_list: self.logger.success(f"已邀请 {len(invite_list)} 位好友:") for i, friend in enumerate(invite_list, 1): mobile = friend.get('mobile', '未知') invite_date = friend.get('inviteDate', '未知') self.logger.info(f" {i}. {mobile} (邀请时间: {invite_date})") else: self.logger.info("暂无已邀请好友") # 0. 获取活动首页信息 index_info = self.get_activity_index() if index_info: self.logger.info(f'活动时间: {index_info.get("acStartTime", "")} ~ {index_info.get("acEndTime", "")}') self.logger.info(f'历史寄件数: {index_info.get("sendNum", 0)},累计支付: {index_info.get("payAmount", 0)}元') # 1. 每日礼包 self.do_daily_gift(result) # 2. 额外奖励 self.do_extra_rewards(result) # 3. 执行任务 self.do_tasks(result) # 4. 查看累计奖励进度 self.do_fetch_rewards(result) # 5. 砸粽子 self.do_crush_zongzi(result) return result # ==================== 账号执行 ==================== def run_account(account_url: str, index: int, invite_pool: List[str]) -> Dict[str, Any]: """执行单个账号""" logger = Logger() proxy_url = os.getenv('SF_PROXY_API_URL', '') proxy_manager = ProxyManager(proxy_url) # 登录 http = SFHttpClient(proxy_manager) retry_count = 0 login_success = False phone = '' user_id = '' while retry_count < MAX_PROXY_RETRIES and not login_success: try: if retry_count > 0: http = SFHttpClient(proxy_manager) success, user_id, phone = http.login(account_url) if success: login_success = True break except Exception as e: pass retry_count += 1 if retry_count < MAX_PROXY_RETRIES: time.sleep(2) if not login_success: logger.error(f'账号{index + 1} 登录失败') return {'success': False, 'phone': '', 'index': index, 'tasks_completed': 0, 'gold_coin_earned': 0, 'crush_detail': [], 'gold_coin_balance': 0} masked_phone = phone[:3] + "****" + phone[7:] if len(phone) >= 7 else phone logger.success(f'账号{index + 1}: 【{phone}】登录成功') # 随机延迟 time.sleep(random.uniform(1, 3)) executor = DragonBoatExecutor(http, logger, user_id, invite_pool) activity_result = executor.run() return { 'success': True, 'phone': phone, 'index': index, **activity_result, } # ==================== 主程序 ==================== def main(): # 收集所有 sfsyUrl 相关环境变量(兼容青龙面板同名多变量) # 方式1:青龙可能把多条同名变量合并为一个,值用换行分隔 # 方式2:青龙可能重命名为 sfsyUrl, sfsyUrl_1, sfsyUrl_2 ... account_urls = [] invite_pool = [] # 列表内互邀池 for key, value in os.environ.items(): if key == 'sfsyUrl' or key.startswith('sfsyUrl_'): # 青龙同名变量用 & 拼接,例如: url1&url2&url3 # 每个账号格式: sessionId=xxx;_login_mobile_=xxx;_login_user_id_=xxx # 拼接后: ...&sessionId=xxx;... # 按 &sessionId= 拆分,首段已有 sessionId= 前缀,后续段补回 parts = value.split('&sessionId=') if parts[0].strip(): account_urls.append(parts[0].strip()) for p in parts[1:]: u = ('sessionId=' + p).strip() if u and u not in account_urls: account_urls.append(u) if not account_urls: print("未找到任何 sfsyUrl / sfsyUrl_* 环境变量,请检查青龙面板变量配置") return # 从账号 URL 提取所有 user_id 组成邀请池 for url in account_urls: m = re.search(r'_login_user_id_=([A-Fa-f0-9]+)', url) if m: invite_pool.append(m.group(1)) inviteId = invite_pool print(f"邀请池: {len(invite_pool)} 个账号") vars_found = [k for k in os.environ if k == 'sfsyUrl' or k.startswith('sfsyUrl_')] print(f"共获取到 {len(account_urls)} 个账号(来自环境变量: {vars_found})") all_results = [] if CONCURRENT_NUM <= 1: for idx, url in enumerate(account_urls): print(f"\n{'='*60}") print(f"开始处理第 {idx+1}/{len(account_urls)} 个账号") print("="*60) result = run_account(url, idx, invite_pool) all_results.append(result) if idx < len(account_urls) - 1: time.sleep(2) else: with ThreadPoolExecutor(max_workers=CONCURRENT_NUM) as pool: futures = {pool.submit(run_account, url, idx, invite_pool): idx for idx, url in enumerate(account_urls)} for future in as_completed(futures): all_results.append(future.result()) all_results.sort(key=lambda x: x['index']) # ========== 汇总 ========== print("\n" + "=" * 60) print(f"全部执行完毕,共处理 {len(all_results)} 个账号") print("=" * 60) for r in all_results: status = "成功" if r['success'] else "失败" phone = r.get('phone', '未知') masked = phone[:3] + "****" + phone[7:] if len(phone) >= 7 else phone print(f" [{status}] {masked} | 完成任务: {r['tasks_completed']} | 获得金币: {r['gold_coin_earned']}") print("=" * 60) if __name__ == '__main__': main()