diff --git a/Points_Based/zto.py b/Points_Based/zto.py new file mode 100644 index 0000000..946c798 --- /dev/null +++ b/Points_Based/zto.py @@ -0,0 +1,338 @@ +# 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()