339 lines
12 KiB
Python
339 lines
12 KiB
Python
# cron: 0 8 * * *
|
||
# new Env("中通快递")
|
||
"""
|
||
脚本: 中通小程序自动签到
|
||
作者: 3iXi
|
||
创建日期: 2025-06-24
|
||
修改日期: 2025-06-27
|
||
更新内容:更换为账号密码登录,Token永不过期
|
||
----------------------
|
||
描述: 使用账号密码登录中通快递,自动签到获取积分;访问网页zto.com登录后在“个人中心”-“账户绑定”里设置密码。
|
||
环境变量:
|
||
变量名:ztozm
|
||
变量值:手机号&密码,多个账号用#分隔
|
||
签到奖励:积分,可兑东西
|
||
"""
|
||
|
||
import os
|
||
import time
|
||
import requests
|
||
from datetime import datetime, timedelta
|
||
|
||
def parse_account_credentials(credentials):
|
||
"""解析账号密码信息"""
|
||
try:
|
||
if '&' not in credentials:
|
||
print("账号密码格式错误,应为:手机号&密码")
|
||
return None
|
||
|
||
username, password = credentials.split('&', 1)
|
||
if not username or not password:
|
||
print("账号或密码不能为空")
|
||
return None
|
||
|
||
return {
|
||
"username": username.strip(),
|
||
"password": password.strip()
|
||
}
|
||
except Exception as e:
|
||
print(f"解析账号密码失败: {e}")
|
||
return None
|
||
|
||
def get_timestamp():
|
||
"""获取当前13位时间戳"""
|
||
return str(int(time.time() * 1000))
|
||
|
||
def get_date_range():
|
||
"""获取查询签到记录的日期范围"""
|
||
today = datetime.now()
|
||
start_date = (today - timedelta(days=3)).strftime("%Y-%m-%d 00:00:00")
|
||
end_date = (today + timedelta(days=3)).strftime("%Y-%m-%d 23:59:59")
|
||
return start_date, end_date
|
||
|
||
def get_today_date():
|
||
"""获取今天的日期"""
|
||
return datetime.now().strftime("%Y-%m-%d")
|
||
|
||
def login_with_password(username, password):
|
||
"""使用账号密码登录获取Token"""
|
||
url = "https://hdgateway.zto.com/auth_account_loginByPassword"
|
||
|
||
headers = {
|
||
"Host": "hdgateway.zto.com",
|
||
"Connection": "keep-alive",
|
||
"Content-Type": "application/json;charset=UTF-8",
|
||
"x-clientCode": "pc",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
|
||
"Accept": "application/json, text/plain, */*",
|
||
"Origin": "https://auth.zto.com",
|
||
"Referer": "https://auth.zto.com/",
|
||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||
"Accept-Language": "zh-CN,zh;q=0.9"
|
||
}
|
||
|
||
payload = {
|
||
"userName": username,
|
||
"password": password,
|
||
"isAgainBind": False
|
||
}
|
||
|
||
try:
|
||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||
data = response.json()
|
||
|
||
if data.get("status") == True:
|
||
token = data["result"]["token"]
|
||
print(f"账号 {username} 登录成功")
|
||
return token
|
||
else:
|
||
print(f"账号 {username} 登录失败: {data.get('message', '未知错误')}")
|
||
return None
|
||
except Exception as e:
|
||
print(f"账号 {username} 登录请求失败: {e}")
|
||
return None
|
||
|
||
def check_today_sign_status(token):
|
||
"""检查今日是否签到"""
|
||
url = "https://membergateway.zto.com/member/activity/queryRecentSign"
|
||
|
||
start_date, end_date = get_date_range()
|
||
|
||
headers = {
|
||
"Host": "membergateway.zto.com",
|
||
"Connection": "keep-alive",
|
||
"x-version": "V8.93.2",
|
||
"x-token": token,
|
||
"x-clientCode": "wechatMiniZtoHelper",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) XWEB/13871",
|
||
"Content-Type": "application/json",
|
||
"x-sv-v": "0.22.0",
|
||
"Accept": "*/*",
|
||
"Referer": "https://servicewechat.com/wx7ddec43d9d27276a/553/page-frame.html",
|
||
"Accept-Encoding": "gzip, deflate, br",
|
||
"Accept-Language": "zh-CN,zh;q=0.9"
|
||
}
|
||
|
||
payload = {
|
||
"startDate": start_date,
|
||
"endDate": end_date
|
||
}
|
||
|
||
try:
|
||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||
data = response.json()
|
||
|
||
if data.get("status"):
|
||
daily_list = data["result"]["dailyList"]
|
||
today_date = get_today_date()
|
||
|
||
for day_info in daily_list:
|
||
if day_info["date"] == today_date:
|
||
return {
|
||
"is_signed": day_info["isSigned"],
|
||
"total_points": data["result"]["totalPoints"]
|
||
}
|
||
return None
|
||
else:
|
||
print(f"查询签到状态失败: {data.get('message', '未知错误')}")
|
||
return None
|
||
except Exception as e:
|
||
print(f"查询签到状态请求失败: {e}")
|
||
return None
|
||
|
||
def sign_in(token):
|
||
"""开始签到"""
|
||
url = "https://membergateway.zto.com/member/activity/signIn"
|
||
|
||
headers = {
|
||
"Host": "membergateway.zto.com",
|
||
"Connection": "keep-alive",
|
||
"x-version": "V8.93.2",
|
||
"x-token": token,
|
||
"x-clientCode": "wechatMiniZtoHelper",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) XWEB/13871",
|
||
"Content-Type": "application/json",
|
||
"x-sv-v": "0.22.0",
|
||
"Accept": "*/*",
|
||
"Referer": "https://servicewechat.com/wx7ddec43d9d27276a/553/page-frame.html",
|
||
"Accept-Encoding": "gzip, deflate, br",
|
||
"Accept-Language": "zh-CN,zh;q=0.9"
|
||
}
|
||
|
||
payload = {
|
||
"signType": "TODAY_SIGN",
|
||
"signDate": f"{get_today_date()} 00:00:00",
|
||
"supplementaryScene": None
|
||
}
|
||
|
||
try:
|
||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||
data = response.json()
|
||
|
||
if data.get("status"):
|
||
points_earned = data["result"]["pointsEarned"]
|
||
print(f"签到成功,得到{points_earned}积分")
|
||
return True
|
||
else:
|
||
print(f"签到失败: {data.get('message', '未知错误')}")
|
||
return False
|
||
except Exception as e:
|
||
print(f"签到请求失败: {e}")
|
||
return False
|
||
|
||
def check_and_claim_resign_card(token):
|
||
"""检查并领取补签卡(每月1次)"""
|
||
url = "https://membergateway.zto.com/member/activity/getMyActivityProps"
|
||
|
||
headers = {
|
||
"Host": "membergateway.zto.com",
|
||
"Connection": "keep-alive",
|
||
"x-version": "V8.93.2",
|
||
"x-token": token,
|
||
"x-clientCode": "wechatMiniZtoHelper",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) XWEB/13871",
|
||
"Content-Type": "application/json",
|
||
"x-sv-v": "0.22.0",
|
||
"Accept": "*/*",
|
||
"Referer": "https://servicewechat.com/wx7ddec43d9d27276a/553/page-frame.html",
|
||
"Accept-Encoding": "gzip, deflate, br",
|
||
"Accept-Language": "zh-CN,zh;q=0.9"
|
||
}
|
||
|
||
payload = {"propsType": "RESIGN_CARD"}
|
||
|
||
try:
|
||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||
data = response.json()
|
||
|
||
if data.get("status"):
|
||
monthly_collection_status = data["result"]["monthlyCollectionStatus"]
|
||
if monthly_collection_status == 1:
|
||
# 领取补签卡,如果这个月领了的话就什么都不做
|
||
claim_url = "https://membergateway.zto.com/member/activity/issueProps"
|
||
claim_payload = {
|
||
"propsType": "RESIGN_CARD",
|
||
"taskType": "MONTHLY_COLLECTION"
|
||
}
|
||
|
||
claim_response = requests.post(claim_url, headers=headers, json=claim_payload, timeout=30)
|
||
claim_data = claim_response.json()
|
||
|
||
if claim_data.get("status"):
|
||
print("每月免费领取补签卡*1成功")
|
||
|
||
except Exception as e:
|
||
print(f"检查/领取补签卡失败: {e}")
|
||
|
||
def get_member_points(token):
|
||
"""获取积分"""
|
||
url = "https://membergateway.zto.com/member/getMemberPoints"
|
||
|
||
headers = {
|
||
"Host": "membergateway.zto.com",
|
||
"Connection": "keep-alive",
|
||
"x-version": "V8.93.2",
|
||
"x-token": token,
|
||
"x-clientCode": "wechatMiniZtoHelper",
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) XWEB/13871",
|
||
"Content-Type": "application/json",
|
||
"x-sv-v": "0.22.0",
|
||
"Accept": "*/*",
|
||
"Referer": "https://servicewechat.com/wx7ddec43d9d27276a/553/page-frame.html",
|
||
"Accept-Encoding": "gzip, deflate, br",
|
||
"Accept-Language": "zh-CN,zh;q=0.9"
|
||
}
|
||
|
||
payload = {}
|
||
|
||
try:
|
||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||
data = response.json()
|
||
|
||
if data.get("success"):
|
||
total_point = data["data"]["totalPoint"]
|
||
over_due_point = data["data"]["overDuePoint"]
|
||
over_due_message = data["data"]["overDueMessage"]
|
||
|
||
if over_due_point:
|
||
print(f"当前积分{total_point},{over_due_message}")
|
||
else:
|
||
print(f"当前积分{total_point}")
|
||
|
||
except Exception as e:
|
||
print(f"获取积分信息失败: {e}")
|
||
|
||
def process_account(credentials):
|
||
"""处理单个账户的签到流程"""
|
||
# 解析账号密码
|
||
account_info = parse_account_credentials(credentials)
|
||
if not account_info:
|
||
print("解析账号密码失败,跳过此账户")
|
||
return
|
||
|
||
username = account_info["username"]
|
||
password = account_info["password"]
|
||
|
||
print(f"\n开始处理账户: {username}")
|
||
|
||
# 使用账号密码登录获取Token
|
||
token = login_with_password(username, password)
|
||
if not token:
|
||
print("登录失败,跳过此账户")
|
||
return
|
||
|
||
# 检查今天是否签到
|
||
sign_status = check_today_sign_status(token)
|
||
if not sign_status:
|
||
print("无法获取签到状态,跳过此账户")
|
||
return
|
||
|
||
if sign_status["is_signed"]:
|
||
print(f"今天已签到,当前积分{sign_status['total_points']}")
|
||
else:
|
||
print("今天未签到,开始签到...")
|
||
sign_in(token)
|
||
|
||
# 检查并领取补签卡
|
||
check_and_claim_resign_card(token)
|
||
|
||
# 获取积分信息
|
||
get_member_points(token)
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("中通快递自动签到脚本启动...")
|
||
|
||
# 获取环境变量
|
||
zto_credentials = os.getenv("ztozm")
|
||
if not zto_credentials:
|
||
print("未找到环境变量'ztozm',请先设置")
|
||
print("格式:手机号&密码,多个账号用#分隔")
|
||
return
|
||
|
||
# 分割多个账号密码
|
||
credentials_list = zto_credentials.split("#")
|
||
print(f"共找到{len(credentials_list)}个账号")
|
||
|
||
# 处理每个账户
|
||
for i, credentials in enumerate(credentials_list, 1):
|
||
credentials = credentials.strip()
|
||
if not credentials:
|
||
continue
|
||
|
||
print(f"\n{'='*30}")
|
||
print(f"处理第{i}个账户")
|
||
print(f"{'='*30}")
|
||
|
||
process_account(credentials)
|
||
|
||
# 为避免请求过于频繁,添加延时
|
||
if i < len(credentials_list):
|
||
time.sleep(2)
|
||
|
||
print(f"\n{'='*30}")
|
||
print("所有账户处理完成!")
|
||
print(f"{'='*30}")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|