mirror of
https://github.com/XiaoGe-LiBai/yangmao.git
synced 2025-12-16 19:59:30 +08:00
新增
This commit is contained in:
146
FoYes野练星球.js
Normal file
146
FoYes野练星球.js
Normal file
@@ -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);
|
||||
});
|
||||
232
mudaiba.py
Normal file
232
mudaiba.py
Normal file
@@ -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()
|
||||
545
中国联通.py
Normal file
545
中国联通.py
Normal file
@@ -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)
|
||||
224
浓五酒馆.js
Normal file
224
浓五酒馆.js
Normal file
@@ -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}`);
|
||||
});
|
||||
Reference in New Issue
Block a user