Update sfsy.py

This commit is contained in:
blusunny
2025-09-11 22:45:13 +08:00
committed by GitHub
parent 751fa417cb
commit b14b2230da

395
sfsy.py
View File

@@ -1,6 +1,9 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
cron "9 15 * * *" script-path=xxx.py,tag=匹配cron用
new Env('顺丰速运签到')
"""
"""
顺丰速运自动化脚本
来源:
@@ -11,13 +14,12 @@
变量名: sfsyUrl
格式: 多账号用换行分割
获取方式:
1. 顺丰APP绑定微信后添加机器人发送“顺丰”
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 进行编码
Cron: 11 7 * * *
"""
import hashlib
@@ -33,6 +35,20 @@ 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 = ''
@@ -45,6 +61,38 @@ def Log(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:
@@ -63,7 +111,7 @@ class RUN:
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(0x63090551) XWEB/6945 Flue',
'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',
@@ -73,11 +121,7 @@ class RUN:
'platform': 'MINI_PROGRAM',
}
# 32周年活动相关属性
self.ifPassAllLevel = False
self.surplusPushTime = 0
self.lotteryNum = 0
self.anniversary_black = False
# 会员日活动相关属性
self.member_day_black = False
self.member_day_red_packet_drew_today = False
self.member_day_red_packet_map = {}
@@ -135,7 +179,7 @@ class RUN:
for retry_count in range(max_retries):
try:
if req_type.lower() == 'get':
enkelt = self.s.get(url, headers=self.headers, timeout=30)
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:
@@ -144,7 +188,7 @@ class RUN:
response.raise_for_status()
return response.json()
except (requests.exceptions.RequestException, json.JSONDisposeError) as e:
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)
@@ -168,23 +212,6 @@ class RUN:
else:
Log(f'❌ 签到失败!原因:{response.get("errorMessage", "未知错误")}')
def superWelfare_receiveRedPacket(self):
"""领取超值福利签到奖励"""
Log('🎁 超值福利签到')
json_data = {"channel": "czflqdlhbxcx"}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberActLengthy~redPacketActivityService~superWelfare~receiveRedPacket'
response = self.do_request(url, data=json_data)
if response.get('success'):
gift_list = response.get('obj', {}).get('giftList', [])
if response.get('obj', {}).get('extraGiftList', []):
gift_list.extend(response['obj']['extraGiftList'])
gift_names = ', '.join([gift['giftName'] for gift in gift_list]) or '无奖励'
receive_status = response.get('obj', {}).get('receiveStatus')
status_message = '领取成功' if receive_status == 1 else '已领取过'
Log(f'🎉 超值福利签到[{status_message}]: {gift_names}')
else:
Log(f'❌ 超值福利签到失败: {response.get("errorMessage", "未知错误")}')
def get_SignTaskList(self, end=False):
"""获取签到任务列表"""
Log('🎯 开始获取签到任务列表' if not end else '💰 查询最终积分')
@@ -361,194 +388,6 @@ class RUN:
else:
Log(f'🍯 执行后丰蜜:【{usableHoney}')
def EAR_END_2023_TaskList(self):
"""执行年终集卡任务"""
Log('🎭 开始年终集卡任务')
json_data = {"activityCode": "YEAREND_2024", "channelType": "MINI_PROGRAM"}
self.headers.update({'channel': '24nzdb', 'platform': 'MINI_PROGRAM', 'syscode': 'MCS-MIMP-CORE'})
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~taskList'
response = self.do_request(url, data=json_data)
if response.get('success'):
for item in response["obj"]:
self.title = item["taskName"]
self.taskType = item["taskType"]
status = item["status"]
if status == 3:
Log(f'✨ 【{self.taskType}】-已完成')
continue
if self.taskType == 'INTEGRAL_EXCHANGE':
self.EAR_END_2023_ExchangeCard()
elif self.taskType == 'CLICK_MY_SETTING':
self.taskCode = item["taskCode"]
self.addDeliverPrefer()
if "taskCode" in item:
self.taskCode = item["taskCode"]
self.doTask()
time.sleep(2)
self.EAR_END_2023_receiveTask()
else:
Log(f'⚠️ 暂时不支持【{self.title}】任务')
def EAR_END_2023_ExchangeCard(self):
"""年终集卡兑换"""
Log('>>> 执行年终集卡兑换')
# 占位符,需补充具体兑换逻辑
pass
def EAR_END_2023_receiveTask(self):
"""领取年终集卡任务奖励"""
Log(f'🎁 领取年终集卡【{self.title}】任务奖励')
json_data = {"taskCode": self.taskCode, "activityCode": "YEAREND_2024"}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~fetchTaskReward'
response = self.do_request(url, data=json_data)
Log(f'✨ 【{self.title}】任务奖励-{"领取成功" if response.get("success") else response.get("errorMessage", "失败")}')
def activityTaskService_taskList(self):
"""获取32周年活动任务列表"""
Log('🎭 开始32周年活动任务')
json_data = {"activityCode": "DRAGONBOAT_2025", "channelType": "MINI_PROGRAM"}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~activityTaskService~taskList'
response = self.do_request(url, data=json_data)
if response.get('success'):
skip_task_types = [
'PLAY_ACTIVITY_GAME', 'SEND_SUCCESS_RECALL', 'OPEN_SUPER_CARD',
'CHARGE_NEW_EXPRESS_CARD', 'OPEN_NEW_EXPRESS_CARD', 'OPEN_FAMILY_CARD', 'INTEGRAL_EXCHANGE'
]
task_list = [x for x in response.get('obj', []) if x.get('status') == 2 and x.get('taskType') not in skip_task_types]
if not task_list:
Log('📝 没有可执行的任务')
return
Log(f'📝 获取到未完成任务: {len(task_list)}')
for task in task_list:
Log(f'📝 开始任务: {task.get("taskName")} [{task.get("taskType")}]')
time.sleep(random.uniform(1.5, 3))
self.activityTaskService_finishTask(task)
time.sleep(1.5)
else:
Log(f'❌ 获取活动任务失败: {response.get("errorMessage", "未知错误")}')
def activityTaskService_finishTask(self, task):
"""完成32周年活动任务"""
json_data = {"taskCode": task.get('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'📝 完成任务[{task.get("taskName")}]: {"成功" if response.get("success") else response.get("errorMessage", "失败")}')
def dragonBoatGame2025ServiceWin(self, levelIndex):
"""完成龙舟游戏关卡"""
json_data = {"levelIndex": levelIndex}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoatGame2025Service~win'
response = self.do_request(url, data=json_data)
Log(f'🎮 第{levelIndex}关通关-{"成功" if response.get("success") else response.get("errorMessage", "失败")}')
def dragonBoat2025HastenService(self):
"""查询龙舟加速状态"""
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2025HastenService~getHastenStatus'
response = self.do_request(url, data={})
if response.get('success'):
self.lotteryNum = response.get('obj', {}).get('remainHastenChance', 0)
Log(f'🎲 剩余加速次数: {self.lotteryNum}')
else:
Log(f'❌ 查询加速次数失败: {response.get("errorMessage", "未知错误")}')
def hastenLottery(self):
"""执行龙舟加速抽奖"""
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2025HastenService~hastenLottery'
response = self.do_request(url, data={})
if response.get('success'):
remain = response.get('obj', {}).get('remainHastenChance', 0)
Log(f'🎲 加速成功,剩余加速次数: {remain}')
else:
Log(f'❌ 加速失败: {response.get("errorMessage", "未知错误")}')
def prizeDraw(self, opt):
"""领取龙舟活动奖励"""
json_data = {"currency": opt.get('currency')}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2025LotteryService~prizeDraw'
response = self.do_request(url, data=json_data)
if response.get('success'):
gift_name = response.get('obj', {}).get('giftBagName', '未知奖励')
Log(f'🎁 抽奖获得: {gift_name}')
else:
Log(f'❌ 抽奖失败: {response.get("errorMessage", "未知错误")}')
def getUpgradeStatus(self):
"""查询龙舟活动升级状态"""
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2025UpgradeService~getUpgradeStatus'
response = self.do_request(url, data={})
if response.get('success'):
current_ratio = response.get('obj', {}).get('currentRatio', 0)
level_list = [x for x in response.get('obj', {}).get('levelList', []) if x.get('balance', 0) > 0]
if level_list:
Log(f'🎯 当前进度: {current_ratio}%,已达到兑换条件')
for item in level_list:
self.prizeDraw(item)
time.sleep(1.5)
else:
Log(f'⏳ 当前进度: {current_ratio}%')
else:
Log(f'❌ 查询加速状态失败: {response.get("errorMessage", "未知错误")}')
def activityTaskService_integralExchange(self):
"""执行积分兑换"""
json_data = {"exchangeNum": 1, "activityCode": "DRAGONBOAT_2025"}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoat2025TaskService~integralExchange'
response = self.do_request(url, data=json_data)
Log(f'✅ 积分兑换-{"成功" if response.get("success") else response.get("errorMessage", "失败")}')
def dragonBoatGame2025Service(self):
"""获取龙舟游戏信息"""
json_data = {"channelType": "MINI_PROGRAM"}
url = 'https://mcs-mimp-web.sf-express.com/mcs-mimp/commonPost/~memberNonactivity~dragonBoatGame2025Service~indexInfo'
response = self.do_request(url, data=json_data)
if response.get('success'):
self.surplusPushTime = response.get('obj', {}).get('surplusPushTime', 0)
self.ifPassAllLevel = response.get('obj', {}).get('ifPassAllLevel', False)
Log(f'🎮 剩余游戏次数: {self.surplusPushTime}')
return True
Log(f'❌ 访问失败: {response.get("errorMessage", "未知错误")}')
return False
def addDeliverPrefer(self):
"""新增收件偏好"""
Log(f'>>> 开始【{self.title}】任务')
json_data = {
"country": "中国",
"countryCode": "A000086000",
"province": "北京市",
"provinceCode": "A110000000",
"city": "北京市",
"cityCode": "A111000000",
"county": "东城区",
"countyCode": "A110101000",
"address": "1号楼1单元101",
"latitude": "",
"longitude": "",
"memberId": "",
"locationCode": "010",
"zoneCode": "CN",
"postCode": "",
"takeWay": "7",
"callBeforeDelivery": 'false',
"deliverTag": "2,3,4,1",
"deliverTagContent": "",
"startDeliverTime": "",
"selectCollection": 'false',
"serviceName": "",
"serviceCode": "",
"serviceType": "",
"serviceAddress": "",
"serviceDistance": "",
"serviceTime": "",
"serviceTelephone": "",
"channelCode": "RW11111",
"taskId": self.taskId,
"extJson": "{\"noDeliverDetail\":[]}"
}
url = 'https://ucmp.sf-express.com/cx-wechat-member/member/deliveryPreference/addDeliverPrefer'
response = self.do_request(url, data=json_data)
Log(f'✨ 新增一个收件偏好-{"成功" if response.get("success") else response.get("errorMessage", "失败")}')
def member_day_index(self):
"""执行会员日活动"""
Log('🎭 会员日活动')
@@ -621,7 +460,8 @@ class RUN:
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_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:
@@ -635,26 +475,59 @@ class RUN:
Log('📝 会员日任务风控')
def member_day_finish_task(self, task):
"""完成会员日任务"""
payload = {'taskCode': task['taskCode']}
"""完成会员日任务 - 修复版本"""
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["taskName"]}]: 成功')
Log(f'📝 完成会员日任务[{task_name}]: 成功')
self.member_day_fetch_mix_task_reward(task)
else:
error_message = response.get('errorMessage', '无返回')
Log(f'📝 完成会员日任务[{task["taskName"]}]: {error_message}')
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)
Log(f'🎁 领取会员日任务[{task["taskName"]}]: {"成功" if response.get("success") else response.get("errorMessage", "失败")}')
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):
"""领取会员日红包"""
@@ -722,7 +595,8 @@ class RUN:
# 执行签到任务
self.sign()
self.superWelfare_receiveRedPacket()
# 注释掉超值福利签到(经常失败,影响体验)
# self.superWelfare_receiveRedPacket()
self.get_SignTaskList()
self.get_SignTaskList(True)
@@ -736,30 +610,7 @@ class RUN:
days_left = (activity_end_date - datetime.now()).days
Log(f"⏰ 采蜜活动截止兑换还有{days_left}天,请及时进行兑换!!")
# 执行32周年活动任务
try:
self.activityTaskService_taskList()
self.activityTaskService_integralExchange()
if self.dragonBoatGame2025Service() and not self.ifPassAllLevel:
for index in range(1, 5):
self.dragonBoatGame2025ServiceWin(index)
time.sleep(1.5)
self.dragonBoat2025HastenService()
while self.lotteryNum > 0:
self.hastenLottery()
time.sleep(1)
self.getUpgradeStatus()
self.lotteryNum -= 1
except Exception as e:
Log(f'❌ 32周年活动执行异常: {str(e)}')
# 年终集卡任务
if datetime.now() < datetime(2025, 4, 8, 19, 0):
self.EAR_END_2023_TaskList()
else:
Log('🎭 周年庆活动已结束')
# 会员日任务
# 会员日任务每月26-28日
if 26 <= datetime.now().day <= 28:
self.member_day_index()
else:
@@ -769,8 +620,19 @@ class RUN:
return True
def sendMsg(self, help=False):
"""发送通知(占位符"""
pass
"""发送通知(实现真正的通知功能"""
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():
"""计算当前季度结束日期"""
@@ -784,6 +646,18 @@ 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:
@@ -791,5 +665,28 @@ if __name__ == '__main__':
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')} ====")