#!/usr/bin/python3 # -*- coding: utf-8 -*- """ cron "9 15 * * *" script-path=xxx.py,tag=匹配cron用 new Env('顺丰速运签到') """ """ 顺丰速运自动化脚本 来源: - https://github.com/faintout/myself-script/blob/main/sfsy.py - https://github.com/Xx1aoy1/scripts/blob/main/sf2.py 功能: 遍历生活特权所有分组的券进行领券,完成任务领取丰蜜积分 变量名: sfsyUrl 格式: 多账号用换行分割 获取方式: 1. 顺丰APP绑定微信后,添加机器人发送"顺丰" 2. 打开小程序或APP-我的-积分,抓包以下URL之一: - https://mcs-mimp-web.sf-express.com/mcs-mimp/share/weChat/shareGiftReceiveRedirect - https://mcs-mimp-web.sf-express.com/mcs-mimp/share/app/shareRedirect 编码: 抓取URL后,使用 https://www.toolhelper.cn/EncodeDecode/Url 进行编码 """ import hashlib import json import os import random import time from datetime import datetime, timedelta import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning from urllib.parse import unquote # 禁用安全请求警告 requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # ---------------- 统一通知模块加载(和其他脚本一样)---------------- hadsend = False send = None try: from notify import send hadsend = True print("✅ 已加载notify.py通知模块") except ImportError: print("⚠️ 未加载通知模块,跳过通知功能") # 随机延迟配置 max_random_delay = int(os.getenv("MAX_RANDOM_DELAY", "3600")) random_signin = os.getenv("RANDOM_SIGNIN", "true").lower() == "true" # 全局日志变量 send_msg = '' one_msg = '' def Log(cont=''): """记录日志""" global send_msg, one_msg print(cont) if cont: one_msg += f'{cont}\n' send_msg += f'{cont}\n' def format_time_remaining(seconds): """格式化时间显示""" if seconds <= 0: return "立即执行" hours = seconds // 3600 minutes = (seconds % 3600) // 60 secs = seconds % 60 if hours > 0: return f"{hours}小时{minutes}分{secs}秒" elif minutes > 0: return f"{minutes}分{secs}秒" else: return f"{secs}秒" def wait_with_countdown(delay_seconds, task_name): """带倒计时的随机延迟等待""" if delay_seconds <= 0: return Log(f"{task_name} 需要等待 {format_time_remaining(delay_seconds)}") remaining = delay_seconds while remaining > 0: if remaining <= 10 or remaining % 10 == 0: Log(f"{task_name} 倒计时: {format_time_remaining(remaining)}") sleep_time = 1 if remaining <= 10 else min(10, remaining) time.sleep(sleep_time) remaining -= sleep_time inviteId = [''] class RUN: def __init__(self, info, index): """初始化账号信息""" global one_msg one_msg = '' split_info = info.split('@') url = split_info[0] self.send_UID = split_info[-1] if len(split_info) > 1 and "UID_" in split_info[-1] else None self.index = index + 1 self.s = requests.session() self.s.verify = False self.headers = { 'Host': 'mcs-mimp-web.sf-express.com', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x6309092b) XWEB/6763 Flue', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'sec-fetch-site': 'none', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'accept-language': 'zh-CN,zh', 'platform': 'MINI_PROGRAM', } # 会员日活动相关属性 self.member_day_black = False self.member_day_red_packet_drew_today = False self.member_day_red_packet_map = {} self.today = datetime.now().strftime('%Y-%m-%d') self.max_level = 8 self.packet_threshold = 1 << (self.max_level - 1) self.login_res = self.login(url) def get_deviceId(self, characters='abcdef0123456789'): """生成随机设备ID""" result = '' for char in 'xxxxxxxx-xxxx-xxxx': if char == 'x': result += random.choice(characters) else: result += char return result def login(self, sfurl): """登录顺丰账号""" try: ress = self.s.get(sfurl, headers=self.headers) self.user_id = self.s.cookies.get_dict().get('_login_user_id_', '') self.phone = self.s.cookies.get_dict().get('_login_mobile_', '') self.mobile = self.phone[:3] + "*" * 4 + self.phone[7:] if self.phone else '' if self.phone: Log(f'👤 账号{self.index}:【{self.mobile}】登陆成功') return True else: Log(f'❌ 账号{self.index}获取用户信息失败') return False except Exception as e: Log(f'❌ 登录异常: {str(e)}') return False def getSign(self): """生成请求签名""" timestamp = str(int(time.time() * 1000)) token = 'wwesldfs29aniversaryvdld29' sysCode = 'MCS-MIMP-CORE' data = f'token={token}×tamp={timestamp}&sysCode={sysCode}' signature = hashlib.md5(data.encode()).hexdigest() data = { 'sysCode': sysCode, 'timestamp': timestamp, 'signature': signature } self.headers.update(data) return data def do_request(self, url, data=None, req_type='post', max_retries=3): """发送HTTP请求""" self.getSign() for retry_count in range(max_retries): try: if req_type.lower() == 'get': response = self.s.get(url, headers=self.headers, timeout=30) elif req_type.lower() == 'post': response = self.s.post(url, headers=self.headers, json=data or {}, timeout=30) else: raise ValueError(f'Invalid req_type: {req_type}') response.raise_for_status() return response.json() except (requests.exceptions.RequestException, json.JSONDecodeError) as e: Log(f'❌ 请求失败 ({retry_count + 1}/{max_retries}): {str(e)}') if retry_count < max_retries - 1: time.sleep(2) continue return {'success': False, 'errorMessage': str(e)} return {'success': False, 'errorMessage': 'All retries failed'} def sign(self): """执行签到任务""" Log('🎯 开始执行签到') json_data = {"comeFrom": "vioin", "channelFrom": "WEIXIN"} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~integralTaskSignPlusService~automaticSignFetchPackage' response = self.do_request(url, data=json_data) if response.get('success'): count_day = response.get('obj', {}).get('countDay', 0) if response.get('obj', {}).get('integralTaskSignPackageVOList'): packet_name = response["obj"]["integralTaskSignPackageVOList"][0]["packetName"] Log(f'✨ 签到成功,获得【{packet_name}】,本周累计签到【{count_day + 1}】天') else: Log(f'📝 今日已签到,本周累计签到【{count_day + 1}】天') else: Log(f'❌ 签到失败!原因:{response.get("errorMessage", "未知错误")}') def get_SignTaskList(self, end=False): """获取签到任务列表""" Log('🎯 开始获取签到任务列表' if not end else '💰 查询最终积分') json_data = {"channelType": "1", "deviceId": self.get_deviceId()} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~integralTaskStrategyService~queryPointTaskAndSignFromES' response = self.do_request(url, data=json_data) if response.get('success') and response.get('obj'): totalPoint = response["obj"]["totalPoint"] Log(f'💰 {"执行前" if not end else "当前"}积分:【{totalPoint}】') if not end: for task in response["obj"]["taskTitleLevels"]: self.taskId = task["taskId"] self.taskCode = task["taskCode"] self.strategyId = task["strategyId"] self.title = task["title"] status = task["status"] skip_title = ['用行业模板寄件下单', '去新增一个收件偏好', '参与积分活动'] if status == 3: Log(f'✨ {self.title}-已完成') continue if self.title in skip_title: Log(f'⏭️ {self.title}-跳过') continue self.doTask() time.sleep(2) self.receiveTask() def doTask(self): """完成签到任务""" Log(f'🎯 开始去完成【{self.title}】任务') json_data = {"taskCode": self.taskCode} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonRoutePost/memberEs/taskRecord/finishTask' response = self.do_request(url, data=json_data) Log(f'✨ 【{self.title}】任务-{"已完成" if response.get("success") else response.get("errorMessage", "失败")}') def receiveTask(self): """领取签到任务奖励""" Log(f'🎁 开始领取【{self.title}】任务奖励') json_data = { "strategyId": self.strategyId, "taskId": self.taskId, "taskCode": self.taskCode, "deviceId": self.get_deviceId() } url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~integralTaskStrategyService~fetchIntegral' response = self.do_request(url, data=json_data) Log(f'✨ 【{self.title}】任务奖励-{"领取成功" if response.get("success") else response.get("errorMessage", "失败")}') def do_honeyTask(self): """完成丰蜜任务""" Log(f'🎯 开始完成【{self.taskType}】任务') json_data = {"taskCode": self.taskCode} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberEs~taskRecord~finishTask' response = self.do_request(url, data=json_data) Log(f'✨ 【{self.taskType}】任务-{"已完成" if response.get("success") else response.get("errorMessage", "失败")}') def receive_honeyTask(self): """领取丰蜜任务奖励""" Log(f'🎁 领取【{self.taskType}】丰蜜任务') self.headers.update({ 'syscode': 'MCS-MIMP-CORE', 'channel': 'wxwdsj', 'accept': 'application/json, text/plain, */*', 'content-type': 'application/json;charset=UTF-8', 'platform': 'MINI_PROGRAM' }) json_data = {"taskType": self.taskType} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~receiveExchangeIndexService~receiveHoney' response = self.do_request(url, data=json_data) Log(f'✨ 收取任务【{self.taskType}】-{"成功" if response.get("success") else response.get("errorMessage", "失败")}') def get_coupom(self, goods): """领取优惠券""" json_data = { "from": "Point_Mall", "orderSource": "POINT_MALL_EXCHANGE", "goodsNo": goods['goodsNo'], "quantity": 1, "taskCode": self.taskCode } url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberGoods~pointMallService~createOrder' response = self.do_request(url, data=json_data) return response.get('success') def get_coupom_list(self): """获取优惠券列表""" json_data = {"memGrade": 2, "categoryCode": "SHTQ", "showCode": "SHTQWNTJ"} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberGoods~mallGoodsLifeService~list' response = self.do_request(url, data=json_data) if response.get('success'): all_goods = [] for obj in response.get("obj", []): all_goods.extend(obj.get("goodsList", [])) for goods in all_goods: if goods.get('exchangeTimesLimit', 0) >= 1: if self.get_coupom(goods): Log('✨ 成功领取券,任务结束!') return Log('📝 所有券尝试完成,没有可用的券或全部领取失败。') else: Log(f'❌ 获取券列表失败!原因:{response.get("errorMessage", "未知错误")}') def get_honeyTaskListStart(self): """获取丰蜜任务列表""" Log('🍯 开始获取采蜜换大礼任务列表') self.headers['channel'] = 'wxwdsj' url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~receiveExchangeIndexService~taskDetail' response = self.do_request(url, data={}) if response.get('success'): for item in response["obj"]["list"]: self.taskType = item["taskType"] status = item["status"] if status == 3: Log(f'✨ 【{self.taskType}】-已完成') continue if "taskCode" in item: self.taskCode = item["taskCode"] if self.taskType == 'DAILY_VIP_TASK_TYPE': self.get_coupom_list() else: self.do_honeyTask() if self.taskType == 'BEES_GAME_TASK_TYPE': self.honey_damaoxian() time.sleep(2) def honey_damaoxian(self): """执行大冒险任务""" Log('>>> 执行大冒险任务') gameNum = 5 for i in range(1, gameNum + 1): json_data = {"gatherHoney": 20} Log(f'>> 开始第{i}次大冒险') url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~receiveExchangeGameService~gameReport' response = self.do_request(url, data=json_data) if response.get('success'): gameNum = response.get('obj')['gameNum'] Log(f'> 大冒险成功!剩余次数【{gameNum}】') time.sleep(2) elif response.get("errorMessage") == '容量不足': Log('> 需要扩容') self.honey_expand() else: Log(f'> 大冒险失败!【{response.get("errorMessage", "未知错误")}】') break def honey_expand(self): """容器扩容""" Log('>>> 容器扩容') url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~receiveExchangeIndexService~expand' response = self.do_request(url, data={}) if response.get('success'): Log(f'> 成功扩容【{response.get("obj", "未知")}】容量') else: Log(f'> 扩容失败!【{response.get("errorMessage", "未知错误")}】') def honey_indexData(self, end=False): """执行采蜜换大礼任务""" Log('🍯 开始执行采蜜换大礼任务' if not end else '🍯 查询最终丰蜜') random_invite = random.choice([invite for invite in inviteId if invite != self.user_id]) self.headers['channel'] = 'wxwdsj' json_data = {"inviteUserId": random_invite} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~receiveExchangeIndexService~indexData' response = self.do_request(url, data=json_data) if response.get('success'): usableHoney = response.get('obj').get('usableHoney') activityEndTime = response.get('obj').get('activityEndTime', '') if not end: Log(f'📅 本期活动结束时间【{activityEndTime}】') Log(f'🍯 执行前丰蜜:【{usableHoney}】') for task in response.get('obj').get('taskDetail', []): self.taskType = task['type'] self.receive_honeyTask() time.sleep(2) else: Log(f'🍯 执行后丰蜜:【{usableHoney}】') def member_day_index(self): """执行会员日活动""" Log('🎭 会员日活动') invite_user_id = random.choice([invite for invite in inviteId if invite != self.user_id]) payload = {'inviteUserId': invite_user_id} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayIndexService~index' response = self.do_request(url, data=payload) if response.get('success'): lottery_num = response.get('obj', {}).get('lotteryNum', 0) can_receive_invite_award = response.get('obj', {}).get('canReceiveInviteAward', False) if can_receive_invite_award: self.member_day_receive_invite_award(invite_user_id) self.member_day_red_packet_status() Log(f'🎁 会员日可以抽奖{lottery_num}次') for _ in range(lottery_num): self.member_day_lottery() if self.member_day_black: return self.member_day_task_list() if self.member_day_black: return self.member_day_red_packet_status() else: error_message = response.get('errorMessage', '无返回') Log(f'📝 查询会员日失败: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_receive_invite_award(self, invite_user_id): """领取会员日邀请奖励""" payload = {'inviteUserId': invite_user_id} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayIndexService~receiveInviteAward' response = self.do_request(url, data=payload) if response.get('success'): product_name = response.get('obj', {}).get('productName', '空气') Log(f'🎁 会员日奖励: {product_name}') else: error_message = response.get('errorMessage', '无返回') Log(f'📝 领取会员日奖励失败: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_lottery(self): """会员日抽奖""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayLotteryService~lottery' response = self.do_request(url, data={}) if response.get('success'): product_name = response.get('obj', {}).get('productName', '空气') Log(f'🎁 会员日抽奖: {product_name}') else: error_message = response.get('errorMessage', '无返回') Log(f'📝 会员日抽奖失败: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_task_list(self): """获取会员日任务列表""" payload = {'activityCode': 'MEMBER_DAY', 'channelType': 'MINI_PROGRAM'} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~taskList' response = self.do_request(url, data=payload) if response.get('success'): task_list = response.get('obj', []) for task in task_list: if task['status'] == 1: if self.member_day_black: return self.member_day_fetch_mix_task_reward(task) elif task['status'] == 2 and task['taskType'] not in [ 'SEND_SUCCESS', 'INVITEFRIENDS_PARTAKE_ACTIVITY', 'OPEN_SVIP', 'OPEN_NEW_EXPRESS_CARD', 'OPEN_FAMILY_CARD', 'CHARGE_NEW_EXPRESS_CARD', 'INTEGRAL_EXCHANGE', 'OPEN_SUPER_CARD' # 添加购买至尊会员到跳过列表 ]: for _ in range(task['restFinishTime']): if self.member_day_black: return self.member_day_finish_task(task) else: error_message = response.get('errorMessage', '无返回') Log(f'📝 查询会员日任务失败: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_finish_task(self, task): """完成会员日任务 - 修复版本""" task_name = task.get("taskName", "未知任务") task_type = task.get("taskType", "") # 检查任务是否应该被跳过 skip_task_types = [ 'SEND_SUCCESS', 'INVITEFRIENDS_PARTAKE_ACTIVITY', 'OPEN_SVIP', 'OPEN_NEW_EXPRESS_CARD', 'OPEN_FAMILY_CARD', 'CHARGE_NEW_EXPRESS_CARD', 'INTEGRAL_EXCHANGE', 'OPEN_SUPER_CARD' ] if task_type in skip_task_types: Log(f'⏭️ 会员日任务[{task_name}]-跳过执行({task_type})') return # 智能获取任务代码 task_code = None if 'taskCode' in task: task_code = task['taskCode'] elif 'taskType' in task: task_code = task['taskType'] # 某些任务使用taskType作为taskCode else: Log(f'📝 任务[{task_name}]缺少必要字段,跳过执行') Log(f'📝 任务详情: {json.dumps(task, ensure_ascii=False, indent=2)}') return # 执行任务 payload = {'taskCode': task_code} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberEs~taskRecord~finishTask' response = self.do_request(url, data=payload) if response.get('success'): Log(f'📝 完成会员日任务[{task_name}]: 成功') self.member_day_fetch_mix_task_reward(task) else: error_message = response.get('errorMessage', '无返回') Log(f'📝 完成会员日任务[{task_name}]: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_fetch_mix_task_reward(self, task): """领取会员日任务奖励""" task_name = task.get("taskName", "未知任务") payload = {'taskType': task['taskType'], 'activityCode': 'MEMBER_DAY', 'channelType': 'MINI_PROGRAM'} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~fetchMixTaskReward' response = self.do_request(url, data=payload) if response.get('success'): Log(f'🎁 领取会员日任务[{task_name}]: 成功') else: error_message = response.get('errorMessage', '失败') Log(f'🎁 领取会员日任务[{task_name}]: {error_message}') def member_day_receive_red_packet(self, hour): """领取会员日红包""" payload = {'receiveHour': hour} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayTaskService~receiveRedPacket' response = self.do_request(url, data=payload) Log(f'🎁 会员日领取{hour}点红包-{"成功" if response.get("success") else response.get("errorMessage", "失败")}') def member_day_red_packet_status(self): """查询会员日红包状态""" url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayPacketService~redPacketStatus' response = self.do_request(url, data={}) if response.get('success'): packet_list = response.get('obj', {}).get('packetList', []) self.member_day_red_packet_map = {packet['level']: packet['count'] for packet in packet_list} for level in range(1, self.max_level): count = self.member_day_red_packet_map.get(level, 0) while count >= 2: self.member_day_red_packet_merge(level) count -= 2 packet_summary = [f"[{level}]X{count}" for level, count in self.member_day_red_packet_map.items() if count > 0] Log(f"📝 会员日合成列表: {', '.join(packet_summary) or '无红包'}") if self.member_day_red_packet_map.get(self.max_level): Log(f"🎁 会员日已拥有[{self.max_level}级]红包X{self.member_day_red_packet_map[self.max_level]}") self.member_day_red_packet_draw(self.max_level) else: remaining_needed = sum(1 << (int(level) - 1) for level, count in self.member_day_red_packet_map.items() if count > 0) remaining = self.packet_threshold - remaining_needed Log(f"📝 会员日距离[{self.max_level}级]红包还差: [1级]红包X{remaining}") else: error_message = response.get('errorMessage', '无返回') Log(f'📝 查询会员日合成失败: {error_message}') if '没有资格参与活动' in error_message: self.member_day_black = True Log('📝 会员日任务风控') def member_day_red_packet_merge(self, level): """合成会员日红包""" payload = {'level': level, 'num': 2} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayPacketService~redPacketMerge' response = self.do_request(url, data=payload) if response.get('success'): Log(f'🎁 会员日合成: [{level}级]红包X2 -> [{level + 1}级]红包') self.member_day_red_packet_map[level] = self.member_day_red_packet_map.get(level, 0) - 2 self.member_day_red_packet_map[level + 1] = self.member_day_red_packet_map.get(level + 1, 0) + 1 else: Log(f'📝 会员日合成[{level}级]红包失败: {response.get("errorMessage", "无返回")}') def member_day_red_packet_draw(self, level): """提取会员日红包""" payload = {'level': str(level)} url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~memberDayPacketService~redPacketDraw' response = self.do_request(url, data=payload) if response.get('success'): coupon_names = [item['couponName'] for item in response.get('obj', [])] or ['空气'] Log(f"🎁 会员日提取[{level}级]红包: {', '.join(coupon_names)}") else: Log(f"📝 会员日提取[{level}级]红包失败: {response.get('errorMessage', '无返回')}") def main(self): """主执行逻辑""" if not self.login_res: return False time.sleep(random.uniform(1, 3)) # 执行签到任务 self.sign() # 注释掉超值福利签到(经常失败,影响体验) # self.superWelfare_receiveRedPacket() self.get_SignTaskList() self.get_SignTaskList(True) # 执行丰蜜任务 self.get_honeyTaskListStart() self.honey_indexData() self.honey_indexData(True) # 检查活动截止时间 activity_end_date = get_quarter_end_date() days_left = (activity_end_date - datetime.now()).days Log(f"⏰ 采蜜活动截止兑换还有{days_left}天,请及时进行兑换!!") # 会员日任务(每月26-28日) if 26 <= datetime.now().day <= 28: self.member_day_index() else: Log('⏰ 未到指定时间不执行会员日任务') self.sendMsg() return True def sendMsg(self, help=False): """发送通知(实现真正的通知功能)""" global one_msg if hadsend and one_msg: try: # 限制消息长度,避免过长 if len(one_msg) > 4000: one_msg = one_msg[-4000:] one_msg = "...(消息过长,已截取后半部分)\n" + one_msg send(f'顺丰速运账号{self.index}', one_msg.strip()) print(f'✅ 账号{self.index}通知发送完成') except Exception as e: print(f'❌ 账号{self.index}通知发送失败: {e}') def get_quarter_end_date(): """计算当前季度结束日期""" current_date = datetime.now() current_month = current_date.month current_year = current_date.year next_quarter_first_day = datetime(current_year, ((current_month - 1) // 3 + 1) * 3 + 1, 1) return next_quarter_first_day - timedelta(days=1) if __name__ == '__main__': """主程序入口""" APP_NAME = '顺丰速运' ENV_NAME = 'sfsyUrl' print(f"==== 顺丰速运签到开始 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ====") # 随机延迟(整体延迟) if random_signin: delay_seconds = random.randint(0, max_random_delay) if delay_seconds > 0: signin_time = datetime.now() + timedelta(seconds=delay_seconds) print(f"🎲 随机模式: 延迟 {format_time_remaining(delay_seconds)} 后开始") print(f"⏰ 预计开始时间: {signin_time.strftime('%H:%M:%S')}") wait_with_countdown(delay_seconds, "顺丰签到") token = os.getenv(ENV_NAME) tokens = token.split('\n') if token else [] if tokens: Log(f"🚚 共获取到{len(tokens)}个账号") for index, infos in enumerate(tokens): Log(f"==================================\n🚚 处理账号{index + 1}") RUN(infos, index).main() # 多账号间随机等待 if index < len(tokens) - 1: # 不是最后一个账号 delay = random.uniform(10, 30) print(f"💤 随机等待 {delay:.1f} 秒后处理下一个账号...") time.sleep(delay) # 最终汇总通知 if hadsend and send_msg: try: # 汇总所有账号的结果 summary_msg = f"""🚚 顺丰速运签到汇总 📊 总计处理: {len(tokens)}个账号 📅 执行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 详细结果请查看各账号单独通知""" send('顺丰速运汇总', summary_msg) print('✅ 汇总通知发送完成') except Exception as e: print(f'❌ 汇总通知发送失败: {e}') else: Log("❌ 未获取到sfsyUrl环境变量") print(f"==== 顺丰速运签到完成 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ====")