This commit is contained in:
XiaoGe-LiBai
2025-06-06 18:58:34 +08:00
parent 51249bac58
commit efe7d248db
4 changed files with 1147 additions and 0 deletions

146
FoYes野练星球.js Normal file
View 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
View 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
View 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
View 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}`);
});