From efe7d248db8bdaf7632ce07fb0d7305f11498ffd Mon Sep 17 00:00:00 2001 From: XiaoGe-LiBai <60430782+XiaoGe-LiBai@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:58:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FoYes野练星球.js | 146 +++++++++++++ mudaiba.py | 232 ++++++++++++++++++++ 中国联通.py | 545 +++++++++++++++++++++++++++++++++++++++++++++++ 浓五酒馆.js | 224 +++++++++++++++++++ 4 files changed, 1147 insertions(+) create mode 100644 FoYes野练星球.js create mode 100644 mudaiba.py create mode 100644 中国联通.py create mode 100644 浓五酒馆.js diff --git a/FoYes野练星球.js b/FoYes野练星球.js new file mode 100644 index 0000000..968bc67 --- /dev/null +++ b/FoYes野练星球.js @@ -0,0 +1,146 @@ +/* +name: FoYes野练星球 +cron: 0 8 * * * +#小程序://FoYes野练星球/C3v3nb5YrlLOU4I +export ylxq="备注#Extra-Data" +*/ +const axios = require('axios'); + +// 配置 axios 实例 +const instance = axios.create({ + timeout: 10000, +}); + +// 从环境变量获取账号信息(格式:备注#Extra-Data),用换行符分隔 +const accounts = process.env.ylxq ? process.env.ylxq.split('\n') : []; + +// 解析账号和备注 +function parseAccount(accountWithRemark) { + const parts = accountWithRemark.split('#'); + if (parts.length > 1) { + return { + remark: parts[0].trim(), + extraData: parts[1].trim() + }; + } + return { + remark: accountWithRemark.trim(), + extraData: accountWithRemark.trim() + }; +} + +// 签到接口 +async function checkIn(extraData, remark) { + const url = 'https://h5.youzan.com/wscump/checkin/checkinV2.json'; + const params = { + checkinId: 26259, + app_id: 'wxddc38f2f387306b2', + kdt_id: 46308965, + access_token: '1f9c877769f4e746922a82761c6750' + }; + try { + const response = await instance.get(url, { + params, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c33)XWEB/11581', + 'Extra-Data': extraData + } + }); + return response.data; + } catch (error) { + console.error(`【${remark}】 签到失败:`, error); + throw error; + } +} + +// 查询会员信息接口 +async function getMemberInfo(extraData, remark) { + const url = 'https://h5.youzan.com/wscuser/membercenter/init-data.json'; + const params = { + kdt_id: 46308965, + app_id: 'wxddc38f2f387306b2', + access_token: '1f9c877769f4e746922a82761c6750', + version: '2.201.10.101', + kdtId: 46308965, + onlineKdtId: 46308965, + currentKdtId: 46308965, + needConsumptionAboveCoupon: 1 + }; + try { + const response = await instance.get(url, { + params, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c33)XWEB/11581', + 'Extra-Data': extraData + } + }); + return response.data; + } catch (error) { + console.error(`【${remark}】 查询会员信息失败:`, error); + throw error; + } +} + +// 延时函数 +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +// 处理单个账号的流程 +async function handleAccount(account) { + try { + // 步骤1:签到 + const checkInRes = await checkIn(account.extraData, account.remark); + if (checkInRes.code === 0) { + const title = checkInRes.data.list[0].infos.title; + console.log(`【${account.remark}】 签到成功,获得${title}`); + } else { + console.log(`【${account.remark}】 签到结果:${checkInRes.msg}`); + } + await delay(2000); + + // 步骤2:查询会员信息 + const memberInfoRes = await getMemberInfo(account.extraData, account.remark); + if (memberInfoRes.code === 0) { + const points = memberInfoRes.data.member.stats.points; + console.log(`【${account.remark}】 能量值余额:${points}`); + } else { + console.log(`【${account.remark}】 查询会员信息失败:${memberInfoRes.msg}`); + } + + } catch (err) { + console.error(`【${account.remark}】 执行错误:`, err.message); + } +} + +// 主函数 +async function main() { + try { + const fileUrl = 'http://lihailong.top:38000/file.txt'; + await axios.get(fileUrl) + .then(response => { + console.log(response.data); + }) + .catch(error => { + console.error('获取文件内容失败:', error); + }); + + if (accounts.length === 0) throw new Error("环境变量 ylxq 未设置或格式不正确"); + + // 解析账号和备注 + const accountList = accounts.map(item => parseAccount(item)); + + // 对每个账号进行循环处理 + for (let i = 0; i < accountList.length; i++) { + const account = accountList[i]; + await handleAccount(account); + await delay(3000); + } + } catch (err) { + console.error("主流程错误:", err.message); + } +} + +main().catch(err => { + console.error('主程序异常:', err); +}); diff --git a/mudaiba.py b/mudaiba.py new file mode 100644 index 0000000..7d4ab25 --- /dev/null +++ b/mudaiba.py @@ -0,0 +1,232 @@ +import os +import requests +import re +from bs4 import BeautifulSoup + +# --- 配置信息 (通过环境变量获取) --- +# MUDAIIBA_ACCOUNTS: 字符串,包含多个用户账户信息,格式如下: +# "邮箱1&MD5密码1&bbs_token1 +# 邮箱2&MD5密码2&bbs_token2" +ACCOUNTS_STR = os.getenv("MUDAIIBA_ACCOUNTS") + +# --- 基础URL及路径 --- +BASE_URL = "https://www.mudaiba.com" +SIGN_PATH = "/my-score_sign.htm" +SCORE_PATH = "/my-score.htm" + +def log(message): + """美化后的日志输出函数,带🎁符号""" + print(f"🎁 {message}") + +def log_section(title): + """用于分割不同账户任务的日志标题""" + print(f"\n{'='*50}\n🎁 {title}\n{'='*50}\n") + +def send_notification(title, content): + """发送通知的函数""" + try: + from notify import send as notify_send + notify_send(title, content) + log("通知发送成功") + except ImportError: + log("未找到通知模块,跳过通知发送") + except Exception as e: + log(f"发送通知时出错: {e}") + +def get_common_headers(referer_path="/"): + """获取通用的请求头""" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36", + "Accept": "text/plain, */*; q=0.01", + "DNT": "1", + "Origin": BASE_URL, + "Sec-Fetch-Site": "same-origin", + "Referer": BASE_URL + referer_path, + "Accept-Encoding": "gzip, deflate, br, zstd", + "Accept-Language": "zh-CN,zh;q=0.9", + "Priority": "u=1, i" + } + if "my-score" in referer_path or "my.htm" in referer_path: + headers["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.7" + headers["Sec-Fetch-Dest"] = "document" + headers["Upgrade-Insecure-Requests"] = "1" + headers["Sec-Fetch-Mode"] = "navigate" + headers["Sec-Fetch-User"] = "?1" + headers.pop("X-Requested-With", None) + else: + headers["X-Requested-With"] = "XMLHttpRequest" + headers["Sec-Fetch-Mode"] = "cors" + headers["Sec-Fetch-Dest"] = "empty" + return headers + +def sign_in(bbs_token): + """尝试使用bbs_token进行签到""" + log("尝试直接签到...") + + cookies = {"bbs_token": bbs_token} + headers = get_common_headers(referer_path="/") + + try: + response = requests.post( + url=BASE_URL + SIGN_PATH, + headers=headers, + cookies=cookies, + data={} + ) + response.raise_for_status() + + resp_json = response.json() + if resp_json.get("code") == "0": + message = f"签到成功!消息: {resp_json.get('message', '未知')}" + log(message) + return True, message + else: + message = f"签到失败!代码: {resp_json.get('code')}, 消息: {resp_json.get('message', '未知')}" + log(message) + if "登录" in resp_json.get("message", ""): + log("可能原因:bbs_token已过期或无效。") + return False, message + except requests.exceptions.RequestException as e: + message = f"签到请求发生网络错误: {e}" + log(message) + return False, message + except ValueError: + message = f"签到响应解析失败: {response.text}" + log(message) + return False, message + +def get_user_score(bbs_token): + """使用bbs_token获取用户个人积分信息""" + log("尝试获取个人积分信息...") + cookies = {"bbs_token": bbs_token} + headers = get_common_headers(referer_path="/my.htm") + + try: + response = requests.get( + url=BASE_URL + SCORE_PATH, + headers=headers, + cookies=cookies + ) + response.raise_for_status() + + html_content = response.text + soup = BeautifulSoup(html_content, 'html.parser') + score_table = soup.find('table', class_='table mb-0') + + if not score_table: + message = "未找到积分信息表格,HTML结构可能已改变或bbs_token无效。" + log(message) + if "登录" in html_content or "请登录" in html_content: + log("可能原因:bbs_token已过期或无效,请重新登录获取。") + return None, message + + score_row = score_table.find('tbody').find('tr') + if not score_row: + message = "未找到积分信息行,HTML结构可能已改变。" + log(message) + return None, message + + tds = score_row.find_all('td') + if len(tds) < 3: + message = "积分信息列数不足,HTML结构可能已改变。" + log(message) + return None, message + + experience_text = tds[0].get_text(strip=True) + gold_text = tds[1].get_text(strip=True) + ingot_text = tds[2].get_text(strip=True) + + experience = re.search(r'经验:(\d+)点', experience_text) + gold = re.search(r'金币:(\d+)枚', gold_text.split('\n')[0]) + ingot = re.search(r'元宝:(\d+)个', ingot_text) + + scores = { + '经验': int(experience.group(1)) if experience else 'N/A', + '金币': int(gold.group(1)) if gold else 'N/A', + '元宝': int(ingot.group(1)) if ingot else 'N/A' + } + message = f"当前积分信息:经验 {scores['经验']}点, 金币 {scores['金币']}枚, 元宝 {scores['元宝']}个" + log(message) + return scores, message + + except requests.exceptions.RequestException as e: + message = f"获取积分请求发生网络错误: {e}" + log(message) + return None, message + except Exception as e: + message = f"解析积分信息时发生未知错误: {e}" + log(message) + log("请检查母带吧网站HTML结构是否发生变化,或bbs_token是否有效。") + return None, message + +def main(): + if not ACCOUNTS_STR: + error_msg = "错误:未设置 MUDAIIBA_ACCOUNTS 环境变量!请按照说明设置多用户账户信息。" + log(error_msg) + send_notification("母带吧签到失败", error_msg) + return + + # 解析多用户字符串(使用换行符分隔) + account_lines = [line.strip() for line in ACCOUNTS_STR.splitlines() if line.strip()] + + if not account_lines: + error_msg = "错误:MUDAIIBA_ACCOUNTS 环境变量为空或格式不正确,未能解析到账户信息。" + log(error_msg) + send_notification("母带吧签到失败", error_msg) + return + + log_section("开始执行母带吧所有账户任务...") + notification_title = "母带吧签到结果" + notification_content = [] + success_count = 0 + total_accounts = len(account_lines) + + for i, line in enumerate(account_lines): + parts = [p.strip() for p in line.split("&")] + + email = parts[0] if len(parts) > 0 else "" + password_md5 = parts[1] if len(parts) > 1 else "" + bbs_token = parts[2] if len(parts) > 2 else "" + + log_section(f"开始处理第 {i+1} 个账户: {email if email else '未知邮箱'}") + account_notification = f"账户: {email if email else '未知邮箱'}\n" + + if not email or not password_md5 or not bbs_token: + error_msg = "警告:当前账户配置不完整 (缺少邮箱、MD5密码 或 bbs_token),跳过此账户。" + log(error_msg) + log("请检查 MUDAIIBA_ACCOUNTS 环境变量中此账户的格式是否为 '邮箱&MD5密码&bbs_token'") + account_notification += error_msg + notification_content.append(account_notification) + continue + + # 1. 尝试使用已有的bbs_token进行签到 + sign_success, sign_message = sign_in(bbs_token) + account_notification += f"签到: {'成功 ✅' if sign_success else '失败 ❌'}\n" + if sign_success: + success_count += 1 + + # 2. 获取积分信息 + log("---") + score_info, score_message = get_user_score(bbs_token) + account_notification += f"积分: {score_message}\n" + log("---") + + if not sign_success: + error_msg = "!!! 签到失败。可能原因:bbs_token已过期或无效\n!!! 请手动访问母带吧网站更新token" + log(error_msg) + account_notification += error_msg + "\n" + + notification_content.append(account_notification) + log(f"第 {i+1} 个账户 ({email if email else '未知邮箱'}) 任务执行完毕。") + + # 构建最终通知 + final_title = f"母带吧签到结果 ({success_count}/{total_accounts} 成功)" + final_content = "\n\n".join(notification_content) + + log_section("所有账户任务执行完毕。") + log(f"汇总信息:\n{final_content}") + send_notification(final_title, final_content) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/中国联通.py b/中国联通.py new file mode 100644 index 0000000..4e06c4d --- /dev/null +++ b/中国联通.py @@ -0,0 +1,545 @@ +# encoding: utf-8 +""" +联通APP自动登录并签到脚本 (含PushPlus通知) + +功能: +1. 使用用户提供的加密手机号和密码进行登录 +2. 成功登录后,提取并导出关键Cookies和Tokens供后续脚本使用 +3. 登录成功后,立即执行每日签到功能 +4. 脚本运行结束后,发送PushPlus通知报告结果 + +青龙面板配置: +1. 将脚本文件上传到青龙面板的 scripts 目录下。 +2. 创建定时任务,例如每天运行一次。 +3. 配置所需的环境变量(见下方)。 +4. 在任务的“定时规则”中设置运行时间,例如 `0 8 * * *` 表示每天早上8点运行。 + +环境变量说明: +请在青龙面板中设置以下必要的环境变量。加密的手机号和密码必须通过您自己的抓包获取。 + +- UNICOM_MOBILE_ENCRYPTED: 必需,您的联通手机号的加密值 (从 login.htm 请求体中的 `mobile` 字段获取)。 +- UNICOM_PASSWORD_ENCRYPTED: 必需,您的联通登录密码的加密值 (从 login.htm 请求体中的 `password` 字段获取)。 +- UNICOM_DEVICE_ID: 必需,设备的唯一标识 (从 login.htm 请求体中的 `deviceId`, `deviceCode`, 或 Cookie 中的 `devicedId` 获取,尽量保持一致)。 +- UNICOM_UNIQUE_IDENTIFIER: 必需,设备的唯一标识 (从 login.htm 请求体中的 `uniqueIdentifier` 获取)。 +- PUSH_PLUS_TOKEN: 可选,您的PushPlus Token,用于发送通知。留空则不发送通知。 + +- UNICOM_DEVICE_BRAND: 可选,设备品牌 (默认: iPhone)。 +- UNICOM_DEVICE_MODEL: 可选,设备型号 (默认: iPhone8,2)。 +- UNICOM_DEVICE_OS: 可选,设备OS版本 (默认: 15.8.3)。 +- UNICOM_APP_VERSION: 可选,App版本 (默认: iphone_c@12.0200)。 +- UNICOM_CHANNEL: 可选,渠道 (默认: GGPD)。 +- UNICOM_CITY: 可选,城市代码 (默认: 074|742)。 注意格式可能因抓包而异,使用您抓包中的格式。 +- UNICOM_SIM_OPERATOR: 可选,SIM卡运营商信息 (默认: --,%E4%B8%AD%E5%9B%BD%E7%A7%BB%E5%8A%A8,--,--,--)。 + +成功后,脚本将输出以下环境变量,供**其他独立脚本**使用: +- UNICOM_COOKIE: 登录成功后服务器返回的所有重要Cookies的组合字符串。 +- UNICOM_TOKEN_ONLINE: 登录成功响应体中的 `token_online` 值。 +- UNICOM_PRIVATE_TOKEN: 登录成功响应体中的 `private_token` 值。 + +如何获取加密手机号和密码: +1. 在您的手机上使用抓包工具 (如 Stream, Charles, Fiddler等)。 +2. 打开联通App,进行一次手机号+密码的登录操作。 +3. 在抓包工具中找到 POST 请求到 `https://m.client.10010.com/mobileService/login.htm` 的记录。 +4. 查看该请求的“请求体” (Request Body) 部分。 +5. 复制 `mobile` 字段的值,这就是 `UNICOM_MOBILE_ENCRYPTED`。 +6. 复制 `password` 字段的值,这就是 `UNICOM_PASSWORD_ENCRYPTED`。 +7. 同时复制 `deviceId`, `deviceCode`, `uniqueIdentifier` 等字段的值,用于设置 `UNICOM_DEVICE_ID`, `UNICOM_UNIQUE_IDENTIFIER` 等环境变量。 +8. 从请求头 (`Request Headers`) 或 Cookies 部分获取 `c_version`, `channel`, `city` 等信息,用于设置对应的环境变量。 + +免责声明: +本脚本仅供学习和交流使用,请勿用于非法用途。使用本脚本造成的任何后果由使用者自行承担。 +请尊重联通App的使用协议。 +""" + +import os +import requests +import time +import sys +import json # Added for PushPlus body +from datetime import datetime + +# ANSI颜色码 +COLOR_RESET = "\033[0m" +COLOR_RED = "\033[91m" +COLOR_GREEN = "\033[92m" +COLOR_YELLOW = "\033[93m" +COLOR_BLUE = "\033[94m" +COLOR_PURPLE = "\033[95m" +COLOR_CYAN = "\033[96m" +COLOR_WHITE = "\033[97m" +COLOR_BOLD = "\033[1m" +COLOR_UNDERLINE = "\033[4m" + +# 定义环境变量名称 (输入) +ENV_MOBILE_ENCRYPTED = 'UNICOM_MOBILE_ENCRYPTED' +ENV_PASSWORD_ENCRYPTED = 'UNICOM_PASSWORD_ENCRYPTED' +ENV_DEVICE_ID = 'UNICOM_DEVICE_ID' # Used for deviceId/Code in login body and imei in signin +ENV_UNIQUE_IDENTIFIER = 'UNICOM_UNIQUE_IDENTIFIER' +ENV_PUSH_PLUS_TOKEN = 'PUSH_PLUS_TOKEN' # PushPlus Token + +# 可选环境变量 (输入) +ENV_DEVICE_BRAND = 'UNICOM_DEVICE_BRAND' +ENV_DEVICE_MODEL = 'UNICOM_DEVICE_MODEL' +ENV_DEVICE_OS = 'UNICOM_DEVICE_OS' +ENV_APP_VERSION = 'UNICOM_APP_VERSION' +ENV_CHANNEL = 'UNICOM_CHANNEL' +ENV_CITY = 'UNICOM_CITY' +ENV_SIM_OPERATOR = 'UNICOM_SIM_OPERATOR' +ENV_IP_ADDRESS = 'UNICOM_IP_ADDRESS' + +# 输出环境变量名称 (供其他脚本使用) +OUTPUT_COOKIE = 'UNICOM_COOKIE' +OUTPUT_TOKEN_ONLINE = 'UNICOM_TOKEN_ONLINE' +OUTPUT_PRIVATE_TOKEN = 'UNICOM_PRIVATE_TOKEN' + +# --- 接口信息 --- +LOGIN_URL = "https://m.client.10010.com/mobileService/login.htm" +SIGNIN_PAGE_URL = 'https://img.client.10010.com/SigininApp/index.html' +CONTINUOUS_SIGN_URL = 'https://activity.10010.com/sixPalaceGridTurntableLottery/signin/getContinuous' +DAY_SIGN_URL = 'https://activity.10010.com/sixPalaceGridTurntableLottery/signin/daySign' +PUSHPLUS_URL = 'http://www.pushplus.plus/send' # PushPlus API URL + +# 固定参数 (从抓包数据中获取,可能在不同App版本中变化,如果登录失败请检查这些值) +LOGIN_APP_ID = "2c3ac32a43c90c71330643da26f4251e22fdf2d262b4eea655a6bfa6a592f8dd5cdab4b89575094ca4e4f3c1c3937213c0935cd442bf78b59e6ff2960e069d6021763342e6f354ab3aef410c4eae75c72b3c146a9a679cb8f7cded239c9ba943" +LOGIN_KEY_VERSION = "2" +LOGIN_VOIP_TOKEN = "citc-default-token-do-not-push" +LOGIN_IS_FIRST_INSTALL = "1" +LOGIN_IS_REMEMBER_PWD = "false" +LOGIN_SIM_COUNT = "1" +LOGIN_NET_WAY = "wifi" # 或者 '4g', '5g' + +SIGNIN_PAGE_PARAMS = { + 'cdncachetime': '2909378', # 这个值看起来像缓存时间戳,可能需要根据实际抓包更新 + 'channel': 'wode', + 'webViewNavIsHidden': 'webViewNavIsHidden' +} +DAY_SIGN_PARAMS = {} # 根据提供的JS源码,签到POST参数是空的 + +# 辅助函数:打印带颜色的消息 +def print_color(message, color=COLOR_RESET, bold=False): + bold_code = COLOR_BOLD if bold else "" + print(f"{color}{bold_code}{message}{COLOR_RESET}") + sys.stdout.flush() # 刷新缓冲区,确保立即输出 + +# 获取环境变量 +def get_env(name, required=True, default=None): + value = os.getenv(name, default) + if required and value is None: + print_color(f"❌ {COLOR_RED}错误:请设置环境变量 {COLOR_BOLD}{name}{COLOR_RED}", COLOR_RED) + exit(1) + return value + +# 辅助函数:带重试机制的请求 +def retry_request(request_func, attempts=3, delay=5): + for i in range(attempts): + try: + response = request_func() + response.raise_for_status() # 检查HTTP状态码 + return response + except requests.exceptions.RequestException as e: + if i < attempts - 1: + print_color(f"⚠️ 请求失败,第 {i + 1}/{attempts} 次重试... 错误: {e}", COLOR_YELLOW) + time.sleep(delay) + else: + raise # 最后一次失败则抛出异常 + +# --- 发送PushPlus通知函数 --- +def send_notification(title, content): + push_token = get_env(ENV_PUSH_PLUS_TOKEN, required=False) + if not push_token: + print_color("ℹ️ 未设置 PushPlus Token,跳过发送通知。", COLOR_BLUE) + return + + print_color("\n📤 正在发送 PushPlus 通知...", COLOR_BLUE) + + headers = { + 'Content-Type': 'application/json' + } + payload = { + "token": push_token, + "title": title, + "content": content, + "channel": "wechat", # 默认微信通道 + "template": "txt" # 文本模板 + } + + try: + response = requests.post(PUSHPLUS_URL, headers=headers, data=json.dumps(payload), timeout=10) + response.raise_for_status() + result = response.json() + if result.get('code') == 200: + print_color("✅ PushPlus 通知发送成功。", COLOR_GREEN) + else: + print_color(f"❌ PushPlus 通知发送失败:{result.get('msg', '未知错误')}", COLOR_RED) + except requests.exceptions.RequestException as e: + print_color(f"❌ 发送 PushPlus 通知时发生网络错误:{e}", COLOR_RED) + except Exception as e: + print_color(f"❌ 发送 PushPlus 通知时发生未知错误:{e}", COLOR_RED) + + +# --- 执行登录请求函数 --- +def perform_login(): + print_color(f"\n=== 中国联通自动登录 ===", color=COLOR_BOLD) + print_color("ℹ️ 正在读取登录所需环境变量...", COLOR_BLUE) + + # 获取必填环境变量 + mobile_encrypted = get_env(ENV_MOBILE_ENCRYPTED) + password_encrypted = get_env(ENV_PASSWORD_ENCRYPTED) + device_id = get_env(ENV_DEVICE_ID) + unique_identifier = get_env(ENV_UNIQUE_IDENTIFIER) + + # 获取可选环境变量或使用默认值 (不打印读取日志,减少输出) + device_brand = get_env(ENV_DEVICE_BRAND, required=False, default="iPhone") + device_model = get_env(ENV_DEVICE_MODEL, required=False, default="iPhone8,2") + device_os = get_env(ENV_DEVICE_OS, required=False, default="15.8.3") + app_version = get_env(ENV_APP_VERSION, required=False, default="iphone_c@12.0200") + channel = get_env(ENV_CHANNEL, required=False, default="GGPD") + city = get_env(ENV_CITY, required=False, default="074|742") + sim_operator = get_env(ENV_SIM_OPERATOR, required=False, default="--,%E4%B8%AD%E5%9B%BD%E7%A7%BB%E5%8A%A8,--,--,--") + ip_address = get_env(ENV_IP_ADDRESS, required=False, default="192.168.5.14") + + print_color("\n🚀 尝试使用加密手机号进行登录...", COLOR_YELLOW) + + # 动态生成请求时间 + req_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 构建请求体 + payload = { + "voipToken": LOGIN_VOIP_TOKEN, + "deviceBrand": device_brand, + "simOperator": sim_operator, + "deviceId": device_id, + "netWay": LOGIN_NET_WAY, + "deviceCode": device_id, + "deviceOS": device_os, + "uniqueIdentifier": unique_identifier, + "latitude": "", + "version": app_version, + "pip": ip_address, + "isFirstInstall": LOGIN_IS_FIRST_INSTALL, + "remark4": "", + "keyVersion": LOGIN_KEY_VERSION, + "longitude": "", + "simCount": LOGIN_SIM_COUNT, + "mobile": mobile_encrypted, + "isRemberPwd": LOGIN_IS_REMEMBER_PWD, + "appId": LOGIN_APP_ID, + "reqtime": req_time, + "deviceModel": device_model, + "password": password_encrypted + } + + # 构建请求头 + headers = { + "Host": "m.client.10010.com", + "Accept-Encoding": "gzip, deflate, br", + "Content-Type": "application/x-www-form-urlencoded", + "Connection": "keep-alive", + "Accept": "*/*", + "User-Agent": f"ChinaUnicom4.x/12.2 (com.chinaunicom.mobilebusiness; build:44; iOS {device_os}) Alamofire/4.7.3 unicom{{version:{app_version}}}", + "Accept-Language": "zh-CN,zh-Hans;q=0.9", + } + + # 发送登录请求 + try: + print_color(f"🌐 正在发送登录请求到 {LOGIN_URL}...", COLOR_BLUE) + response = retry_request(lambda: requests.post(LOGIN_URL, data=payload, headers=headers, timeout=10)) + + # 尝试解析JSON响应 + try: + data = response.json() + print_color(f"✅ 接收到响应:HTTP状态码 {response.status_code}, 业务码: {COLOR_BOLD}{data.get('code')}{COLOR_RESET}{COLOR_GREEN}, 描述: {data.get('desc')}", COLOR_GREEN) + + if data.get("code") == "0" or data.get("code") == "0000": + print_color("\n✨ 登录成功!正在提取并导出凭证...", COLOR_GREEN) + + # 提取Cookies + all_cookies = response.cookies + cookie_string = "" + account_phone = "" # 用于通知中的手机号 + for cookie in all_cookies: + cookie_string += f"{cookie.name}={cookie.value}; " + if cookie.name == 'u_account': + account_phone = cookie.value + cookie_string = cookie_string.strip().rstrip(';') + + # 提取JSON体中的Tokens + token_online = data.get("token_online", "") + private_token = data.get("private_token", "") + + print_color(f"\n--- 导出环境变量 (供其他独立任务使用) ---", color=COLOR_PURPLE, bold=True) + print_color(f"ℹ️ 青龙面板会自动识别并设置这些变量。", COLOR_BLUE) + + # 导出Cookies (必须是裸露的print语句) + print(f'export {OUTPUT_COOKIE}="{cookie_string}"') + # 导出JSON Tokens (必须是裸露的print语句) + print(f'export {OUTPUT_TOKEN_ONLINE}="{token_online}"') + print(f'export {OUTPUT_PRIVATE_TOKEN}="{private_token}"') + + print_color(f"--- 环境变量导出完成 ---", color=COLOR_PURPLE, bold=True) + + login_message = f"✅ 登录成功!账号: {account_phone if account_phone else '未知'}" + # 返回提取到的凭证和登录消息 + return True, cookie_string, device_id, token_online, private_token, login_message + + else: + login_message = f"❌ 登录失败!业务处理未成功。" + error_details = f"业务码: {data.get('code')}, 描述: {data.get('desc')}" + print_color(f"\n{login_message} {error_details}", COLOR_RED) + print_color(f"请检查输入的加密手机号、密码和设备信息是否正确。", COLOR_YELLOW) + return False, None, None, None, None, f"{login_message}\n{error_details}" + + except requests.exceptions.JSONDecodeError: + login_message = f"❌ 登录响应不是有效的JSON格式!" + error_details = f"HTTP状态码: {response.status_code}" + print_color(f"\n{login_message} {error_details}", COLOR_RED) + print_color("请检查抓包中的响应内容,确认接口是否返回了预期的数据。", COLOR_YELLOW) + return False, None, None, None, None, f"{login_message}\n{error_details}" + + except requests.exceptions.RequestException as e: + login_message = f"❌ 登录请求发生网络错误:{e}" + print_color(f"\n{login_message}", COLOR_RED) + print_color("请检查网络连接或目标服务器是否可达。", COLOR_YELLOW) + return False, None, None, None, None, login_message + except Exception as e: + login_message = f"❌ 登录时发生未知错误:{e}" + print_color(f"\n{login_message}", COLOR_RED) + print_color("请检查脚本代码或运行环境。", COLOR_YELLOW) + return False, None, None, None, None, login_message + +# --- 执行签到页面请求函数 --- +def perform_signin_page_request(cookie_string): + print_color(f"\n--- 【访问签到页面】开始 ---", COLOR_CYAN) + headers = { + 'Host': 'img.client.10010.com', + 'Accept': '*/*', + 'Accept-Language': 'zh-CN,zh-Hans;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Cookie': cookie_string, + 'User-Agent': os.getenv(ENV_APP_VERSION, "Mozilla/5.0 (iPhone; CPU iPhone OS 15_8_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@12.0200}"), + 'Referer': 'https://img.client.10010.com/' + } + try: + response = retry_request(lambda: requests.get(SIGNIN_PAGE_URL, params=SIGNIN_PAGE_PARAMS, headers=headers, timeout=10)) + + # 精简成功日志 + if response.status_code == 200: + print_color(f"🌐 访问签到页面 | 响应状态码: {response.status_code} ✅", COLOR_GREEN) + return True, "✅ 访问签到页面成功" + else: + print_color(f"❌ 访问签到页面 | 响应状态码: {response.status_code} ❌", COLOR_RED) + return False, f"❌ 访问签到页面失败,状态码: {response.status_code}" + + except requests.exceptions.RequestException as e: + print_color(f"❌ 访问签到页面 | 发生异常!错误信息: {e}", COLOR_RED) + return False, f"❌ 访问签到页面异常: {e}" + except Exception as e: + print_color(f"❌ 访问签到页面 | 发生未知错误:{e}", COLOR_RED) + return False, f"❌ 访问签到页面未知错误: {e}" + finally: + print_color("--- 【访问签到页面】结束 ---", COLOR_CYAN) + +# --- 执行连续签到信息请求函数 --- +def perform_continuous_sign_request(cookie_string, device_id): + print_color(f"\n--- 【获取连续签到信息】开始 ---", COLOR_CYAN) + headers = { + 'Host': 'activity.10010.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh-Hans;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Cookie': cookie_string, + 'User-Agent': os.getenv(ENV_APP_VERSION, "Mozilla/5.0 (iPhone; CPU iPhone OS 15_8_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@12.0200}"), + 'Origin': 'https://img.client.10010.com', + 'Referer': 'https://img.client.10010.com/' + } + params = { + 'taskId': '', + 'channel': 'wode', + 'imei': device_id + } + try: + response = retry_request(lambda: requests.get(CONTINUOUS_SIGN_URL, params=params, headers=headers, timeout=10)) + + print_color(f"🌐 获取连续签到信息 | 响应状态码: {response.status_code}", COLOR_GREEN if response.status_code == 200 else COLOR_RED) + + data = response.json() + print_color(f"📊 业务响应码: {COLOR_BOLD}{data.get('code')}{COLOR_RESET}{COLOR_GREEN}, 描述: {data.get('desc')}", COLOR_GREEN) + + if data and data.get('code') == '0000': + info_data = data.get('data', {}) + print_color(f"✅ 获取连续签到信息 | 成功!", COLOR_GREEN) + continue_count = info_data.get('continueCount', '未知') + today_signed = info_data.get('todayIsSignIn', 'n') == 'y' + keep_desc = info_data.get('keepDesc') + print_color(f"📊 连续签到天数: {continue_count}天, 今日是否已签到: {'是' if today_signed else '否'}", COLOR_GREEN) + if keep_desc: + print_color(f"🎁 连续签到奖励: {keep_desc}", COLOR_GREEN) + check_message = f"✅ 获取签到信息成功 | 连续签到{continue_count}天, 今日{'已' if today_signed else '未'}签到" + if keep_desc: check_message += f", 奖励: {keep_desc}" + return True, today_signed, check_message + elif data and data.get('code') == '0001': + check_message = f"❌ 获取签到信息失败 | 错误原因:{data.get('desc', '未知')}" + print_color(check_message, COLOR_RED) + print_color("❌ 疑似用户未登录或 Cookie 已失效,请重新登录 App 并更新 UNICOM_COOKIE。", COLOR_RED) + return False, False, check_message + else: + check_message = f"❌ 获取签到信息失败 | 响应代码: {data.get('code', '未知')}, 描述: {data.get('desc', '未知')}" + print_color(check_message, COLOR_RED) + return False, False, check_message + + except requests.exceptions.RequestException as e: + check_message = f"❌ 获取签到信息异常: {e}" + print_color(check_message, COLOR_RED) + return False, False, check_message + except requests.exceptions.JSONDecodeError: + check_message = f"❌ 获取签到信息响应不是有效的JSON格式!" + print_color(check_message, COLOR_RED) + return False, False, check_message + except Exception as e: + check_message = f"❌ 获取签到信息未知错误: {e}" + print_color(check_message, COLOR_RED) + return False, False, check_message + finally: + print_color("--- 【获取连续签到信息】结束 ---", COLOR_CYAN) + +# --- 执行签到请求函数 --- +def perform_day_sign_request(cookie_string): + print_color(f"\n--- 【执行每日签到】开始 ---", COLOR_CYAN) + headers = { + 'Host': 'activity.10010.com', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh-Hans;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Cookie': cookie_string, + 'User-Agent': os.getenv(ENV_APP_VERSION, "Mozilla/5.0 (iPhone; CPU iPhone OS 15_8_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 unicom{version:iphone_c@12.0200}"), + 'Origin': 'https://img.client.10010.com', + 'Referer': 'https://img.client.10010.com/', + 'Content-Type': 'application/x-www-form-urlencoded' + } + params = DAY_SIGN_PARAMS + try: + response = retry_request(lambda: requests.post(DAY_SIGN_URL, data=params, headers=headers, timeout=10)) + + print_color(f"🌐 执行每日签到 | 响应状态码: {response.status_code}", COLOR_GREEN if response.status_code == 200 else COLOR_RED) + + data = response.json() + print_color(f"📊 业务响应码: {COLOR_BOLD}{data.get('code')}{COLOR_RESET}{COLOR_GREEN}, 描述: {data.get('desc')}", COLOR_GREEN) + + if data and data.get('code') == '0000': + sign_data = data.get('data', {}) + sign_message = f"✅ 每日签到成功!" + status_desc = sign_data.get('statusDesc', '无描述') + sign_message += f" {status_desc}" + print_color(sign_message, COLOR_GREEN) + + rewards = [] + red_reward = sign_data.get('redSignMessage') + if red_reward: + rewards.append(f"获得奖励: {red_reward}") + black_reward = sign_data.get('blackSignMessage') + if black_reward: + rewards.append(f"额外奖励: {black_reward}") + flower_count = sign_data.get('flowerCount') + if flower_count is not None: + rewards.append(f"花朵数量: {flower_count}") + growth_v = sign_data.get('growthV') + if growth_v is not None: + rewards.append(f"成长值: {growth_v}") + + if rewards: + rewards_str = ", ".join(rewards) + print_color(f"🎁 {rewards_str}", COLOR_GREEN) + sign_message += f"\n🎁 {rewards_str}" + + return True, sign_message + + elif data and data.get('code') == '0002' and isinstance(data.get('desc'), str) and '已经签到' in data.get('desc', ''): + sign_message = f"✅ 每日签到 | 今日已完成签到!" + print_color(sign_message, COLOR_GREEN) + return True, sign_message + elif data and data.get('code') == '0001': + sign_message = f"❌ 每日签到失败!错误原因:{data.get('desc', '未知')}" + print_color(sign_message, COLOR_RED) + print_color("❌ 疑似用户未登录或 Cookie 已失效。", COLOR_RED) + return False, sign_message + else: + sign_message = f"❌ 每日签到失败!响应代码: {data.get('code', '未知')}, 描述: {data.get('desc', '未知')}" + print_color(sign_message, COLOR_RED) + return False, sign_message + + except requests.exceptions.RequestException as e: + sign_message = f"❌ 每日签到异常: {e}" + print_color(sign_message, COLOR_RED) + return False, sign_message + except requests.exceptions.JSONDecodeError: + sign_message = f"❌ 每日签到响应不是有效的JSON格式!" + print_color(sign_message, COLOR_RED) + return False, sign_message + except Exception as e: + sign_message = f"❌ 每日签到未知错误: {e}" + print_color(sign_message, COLOR_RED) + return False, sign_message + finally: + print_color("--- 【执行每日签到】结束 ---", COLOR_CYAN) + + +# --- 主程序入口 --- +if __name__ == "__main__": + # 存储所有通知消息的列表 + notification_messages = [] + script_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + notification_messages.append(f"脚本开始运行: {script_start_time}") + + # 步骤1: 执行登录 + login_success, cookie, device_id_val, token_online, private_token, login_msg = perform_login() + notification_messages.append(login_msg) + + if login_success: + print_color("\n" + "="*40, color=COLOR_BOLD) # 分隔线 + print_color(f"\n=== 联通签到流程开始 ===", color=COLOR_BOLD) + notification_messages.append("\n--- 签到流程 ---") + + # 步骤2: 访问签到页面 (可选) + # 即使访问失败,也尝试继续后续步骤 + _, signin_page_msg = perform_signin_page_request(cookie) + # notification_messages.append(signin_page_msg) # 访问页面成功/失败不太重要,不加入通知内容 + + time.sleep(1) # 间隔1秒 + + # 步骤3: 获取连续签到信息,并检查今日是否已签到 + success_check, already_signed, check_msg = perform_continuous_sign_request(cookie, device_id_val) + notification_messages.append(check_msg) + + + if success_check: + if not already_signed: + print_color("\n➡️ 检测到今日未签到,准备执行每日签到...", COLOR_YELLOW) + time.sleep(1) # 间隔1秒 + # 步骤4: 执行每日签到 + sign_success, sign_msg = perform_day_sign_request(cookie) + notification_messages.append(sign_msg) + else: + print_color("\nℹ️ 今日已签到,无需重复签到。", COLOR_BLUE) + # 签到信息检查函数已经添加了已签到的消息,无需额外添加 + else: + print_color("\n❌ 无法获取签到状态,跳过签到操作。", COLOR_RED) + # 签到信息检查函数已经添加了失败的消息,无需额外添加 + + print_color(f"\n=== 联通签到流程结束 ===", color=COLOR_BOLD) + print_color("\n" + "="*40, color=COLOR_BOLD) # 分隔线 + + else: + print_color("\n❌ 登录失败,无法执行后续操作。", COLOR_RED) + # 登录函数已经添加了失败的消息,无需额外添加 + + print_color(f"\n=== 脚本运行结束 ===", color=COLOR_BOLD) + script_end_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + notification_messages.append(f"\n脚本运行结束: {script_end_time}") + + # 整合所有消息发送通知 + full_notification_content = "\n".join(notification_messages) + notification_title = "联通登录签到脚本运行结果" + send_notification(notification_title, full_notification_content) \ No newline at end of file diff --git a/浓五酒馆.js b/浓五酒馆.js new file mode 100644 index 0000000..ca7ae45 --- /dev/null +++ b/浓五酒馆.js @@ -0,0 +1,224 @@ +// ==UserScript== +// @name 浓五酒馆自动登录签到 +// @namespace http://tampermonkey.net/ +// @version 1.1 +// @description 青龙面板脚本,浓五酒馆自动签到,支持 JWT Token 缓存 +// @match http://*/* +// @grant none +// ==/UserScript== + +/* +环境变量说明: +- NWJG_CK: 登录请求体,JSON 格式,例: {"code":"xxx","appId":"wxed3cf95a14b58a26"} +- NWJG_TOKEN: 缓存的 JWT Token,例: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9... +- 多账号: NWJG_CK 和 NWJG_TOKEN 用 & 分隔,顺序对应 +- 获取 NWJG_CK: 抓包 POST https://stdcrm.dtmiller.com/std-weixin-mp-service/miniApp/custom/login 的 Request Body +*/ + +const axios = require('axios'); +const fs = require('fs'); +const path = require('path'); + +// 环境变量名称 +const CK_ENV_NAME = 'NWJG_CK'; +const TOKEN_ENV_NAME = 'NWJG_TOKEN'; +const TOKEN_FILE = path.join(__dirname, 'nwjg_token.json'); // 持久化存储 token + +// 日志前缀 +const LOG_PREFIX = '[浓五酒馆自动登录签到]'; + +// API 配置 +const BASE_URL = 'https://stdcrm.dtmiller.com'; +const LOGIN_API = '/std-weixin-mp-service/miniApp/custom/login'; +const USER_INFO_API = '/scrm-promotion-service/mini/wly/user/info'; +const SIGN_INFO_API = '/scrm-promotion-service/promotion/sign/userinfo?promotionId=PI6811d6f99b099a000a0c4613'; +const SIGN_TODAY_API = '/scrm-promotion-service/promotion/sign/today?promotionId=PI6811d6f99b099a000a0c4613'; + +// 请求头模板 +const HEADERS = { + '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) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c33)XWEB/13487', + 'Content-Type': 'application/json', + 'Accept': '*/*', + 'sec-fetch-site': 'cross-site', + 'sec-fetch-mode': 'cors', + 'sec-fetch-dest': 'empty', + 'Referer': 'https://servicewechat.com/wxed3cf95a14b58a26/223/page-frame.html', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-CN,zh;q=0.9', + 'Priority': 'u=1, i' +}; + +// 获取环境变量 +function getEnv(key) { + return process.env[key] || ''; +} + +// 读取缓存的 token +function readTokens() { + try { + if (fs.existsSync(TOKEN_FILE)) { + return JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8')); + } + } catch (error) { + console.log(`${LOG_PREFIX} 读取 token 失败: ${error.message}`); + } + return {}; +} + +// 保存 token 到文件 +function saveTokens(tokens) { + try { + fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2)); + } catch (error) { + console.log(`${LOG_PREFIX} 保存 token 失败: ${error.message}`); + } +} + +// 发送通知 +function sendNotify(title, content) { + console.log(`${title}\n${content}`); +} + +// 登录获取 Token +async function login(requestBody) { + try { + const response = await axios.post(`${BASE_URL}${LOGIN_API}`, requestBody, { headers: HEADERS }); + if (response.data.code === 0 && response.data.data) { + return response.data.data; // 返回 JWT Token + } else { + throw new Error(response.data.msg || '登录失败'); + } + } catch (error) { + throw new Error(`登录请求失败: ${error.message}`); + } +} + +// 获取用户信息 +async function getUserInfo(token) { + try { + const headers = { ...HEADERS, 'Authorization': `Bearer ${token}` }; + const response = await axios.get(`${BASE_URL}${USER_INFO_API}`, { headers }); + if (response.data.code === 0 && response.data.data) { + return response.data.data; + } else { + throw new Error(response.data.msg || '获取用户信息失败'); + } + } catch (error) { + throw new Error(`获取用户信息失败: ${error.message}`); + } +} + +// 获取签到信息 +async function getSignInfo(token) { + try { + const headers = { ...HEADERS, 'Authorization': `Bearer ${token}` }; + const response = await axios.get(`${BASE_URL}${SIGN_INFO_API}`, { headers }); + if (response.data.code === 0 && response.data.data) { + return response.data.data; + } else { + throw new Error(response.data.msg || '获取签到信息失败'); + } + } catch (error) { + throw new Error(`获取签到信息失败: ${error.message}`); + } +} + +// 执行今日签到 +async function doSignToday(token) { + try { + const headers = { ...HEADERS, 'Authorization': `Bearer ${token}` }; + const response = await axios.get(`${BASE_URL}${SIGN_TODAY_API}`, { headers }); + if (response.data.code === 0 && response.data.data) { + return response.data.data; + } else { + throw new Error(response.data.msg || '签到失败'); + } + } catch (error) { + throw new Error(`签到请求失败: ${error.message}`); + } +} + +// 主函数 +async function main() { + const accounts = getEnv(CK_ENV_NAME).split('&').filter(Boolean); + const tokenEnv = getEnv(TOKEN_ENV_NAME).split('&').filter(Boolean); + let tokens = readTokens(); + + if (!accounts.length) { + sendNotify(LOG_PREFIX, '❌ 未配置环境变量 NWJG_CK'); + return; + } + + for (let i = 0; i < accounts.length; i++) { + const account = accounts[i]; + const accountKey = `account_${i}`; // 每个账号的唯一键 + console.log(`\n${LOG_PREFIX} 处理第 ${i + 1} 个账号`); + + try { + // 解析请求体 + let requestBody; + try { + requestBody = JSON.parse(account); + } catch (error) { + sendNotify(LOG_PREFIX, `❌ 账号 ${i + 1} 环境变量格式错误: ${error.message}`); + continue; + } + + // 尝试使用缓存的 token + let token = tokens[accountKey] || tokenEnv[i]; + let tokenValid = false; + + if (token) { + console.log(`${LOG_PREFIX} 🚀 尝试使用缓存的 token...`); + try { + await getUserInfo(token); // 测试 token 是否有效 + tokenValid = true; + console.log(`${LOG_PREFIX} ✅ 缓存 token 有效`); + } catch (error) { + console.log(`${LOG_PREFIX} ⚠️ 缓存 token 无效: ${error.message}`); + } + } + + // 如果 token 无效,重新登录 + if (!tokenValid) { + console.log(`${LOG_PREFIX} 🚀 尝试登录...`); + token = await login(requestBody); + tokens[accountKey] = token; + saveTokens(tokens); + console.log(`${LOG_PREFIX} ✅ 登录成功,token 已缓存`); + } + + // 获取用户信息 + const userInfo = await getUserInfo(token); + const member = userInfo.member; + const grade = userInfo.grade; + console.log(`${LOG_PREFIX} 👤 用户信息: ${member.nick_name} (${member.mobile})`); + console.log(`${LOG_PREFIX} 🎖️ 会员等级: ${grade.level_name} (到期: ${grade.expire_time})`); + console.log(`${LOG_PREFIX} 💰 当前积分: ${member.points}`); + + // 获取签到信息 + const signInfo = await getSignInfo(token); + console.log(`${LOG_PREFIX} 📅 签到活动: ${signInfo.promotionName}`); + console.log(`${LOG_PREFIX} 📈 累计签到: ${signInfo.signDays} 天`); + console.log(`${LOG_PREFIX} 🎁 今日奖励: ${signInfo.signDayPrizeName}`); + console.log(`${LOG_PREFIX} ✅ 今日已签: ${signInfo.today ? '是' : '否'}`); + + // 如果今日未签到,执行签到 + if (!signInfo.today) { + console.log(`${LOG_PREFIX} 🚀 尝试签到...`); + const signResult = await doSignToday(token); + console.log(`${LOG_PREFIX} ✅ 签到成功,获得: ${signResult.prize.prizeName}`); + } else { + console.log(`${LOG_PREFIX} ℹ️ 今日已签到,无需重复操作`); + } + + } catch (error) { + sendNotify(LOG_PREFIX, `❌ 账号 ${i + 1} 处理失败: ${error.message}`); + } + } +} + +// 执行主函数 +main().catch(error => { + sendNotify(LOG_PREFIX, `❌ 脚本执行异常: ${error.message}`); +}); \ No newline at end of file