Files
XiaoGe-LiBai-yangmao/bz公益.js
2025-12-10 15:07:22 +08:00

330 lines
10 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* @File: lucky_sign_lottery.js
* @Description: 适用于 lucky.5202030.xyz 的全自动脚本
* @Version: 2.0.0
* @Features:
* 1. 每日自动签到
* 2. 每日/每周/每月福利自动领取 (New)
* 3. 自动抽奖 (直到次数耗尽)
* 4. Token 过期/无效时自动使用账号密码登录 (New)
* * @Usage:
* 1. 在青龙面板 "依赖管理" -> "NodeJs" 中,确保安装 `axios`。
* 2. 在 "环境变量" 中添加以下变量:
* - LUCKY_XYZ_USERNAME: 你的登录用户名 (必填用于Token过期时自动登录)
* - LUCKY_XYZ_PASSWORD: 你的登录密码 (必填用于Token过期时自动登录)
* - LUCKY_XYZ_TOKEN: (可选) 如果你不想填账号密码,可直接填 Bearer Token。
* * 3. 设置定时任务,建议每天 00:10 执行: "10 0 * * *"
*/
const axios = require('axios');
// ================= 配置区域 =================
// 环境变量名称
const ENV = {
TOKEN: 'LUCKY_XYZ_TOKEN',
USER: 'LUCKY_XYZ_USERNAME',
PASS: 'LUCKY_XYZ_PASSWORD'
};
// API 端点
const API = {
LOGIN: 'https://lucky.5202030.xyz/api/auth/login/password',
SIGN: 'https://lucky.5202030.xyz/api/sign/',
LOTTERY: 'https://lucky.5202030.xyz/api/lottery/',
CLAIM: 'https://lucky.5202030.xyz/api/lucky-code/privileges/claim/all' // 新增福利接口
};
// 配置参数
const CONFIG = {
LOTTERY_DELAY: 500, // 抽奖间隔(ms)
MAX_ATTEMPTS: 200, // 最大抽奖次数保护
USER_AGENT: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
};
// 全局变量,用于存储运行时 Token
let CURRENT_TOKEN = process.env[ENV.TOKEN] || '';
// ================= 工具函数 =================
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 获取最新的请求头
function getHeaders() {
return {
'accept': 'application/json, text/plain, */*',
'authorization': CURRENT_TOKEN.startsWith('Bearer') ? CURRENT_TOKEN : `Bearer ${CURRENT_TOKEN}`,
'content-type': 'application/json',
'origin': 'https://lucky.5202030.xyz',
'referer': 'https://lucky.5202030.xyz/',
'user-agent': CONFIG.USER_AGENT
};
}
// 格式化日志输出
function log(msg, type = 'info') {
const icon = {
info: '',
success: '✅',
warn: '⚠️',
error: '❌',
gift: '🎁',
money: '💰'
}[type] || '';
console.log(`${icon} ${msg}`);
}
// ================= 核心逻辑 =================
/**
* 核心功能:登录获取 Token
*/
async function doLogin() {
const username = process.env[ENV.USER];
const password = process.env[ENV.PASS];
if (!username || !password) {
log('未配置用户名或密码,无法执行自动登录。请检查环境变量。', 'error');
return false;
}
log(`正在尝试使用账号 [${username}] 登录...`, 'info');
try {
const response = await axios.post(API.LOGIN, {
username: username,
password: password
}, {
headers: {
'content-type': 'application/json',
'user-agent': CONFIG.USER_AGENT
}
});
const data = response.data;
if (data.success && data.data && data.data.access_token) {
// 更新全局 Token
CURRENT_TOKEN = `Bearer ${data.data.access_token}`;
log(`登录成功!当前余额: ${data.data.user.balance}`, 'success');
return true;
} else {
log(`登录失败: ${data.message}`, 'error');
return false;
}
} catch (error) {
log(`登录接口请求异常: ${error.message}`, 'error');
return false;
}
}
/**
* 通用请求包装器 (包含 401 自动重试逻辑)
* @param {Function} requestFn 实际的请求函数
* @param {string} taskName 任务名称
*/
async function requestWithRetry(requestFn, taskName) {
try {
// 如果没有 Token先尝试登录
if (!CURRENT_TOKEN) {
log('检测到 Token 为空,尝试自动登录...', 'warn');
const loginSuccess = await doLogin();
if (!loginSuccess) throw new Error('登录失败,无法继续执行任务');
}
return await requestFn();
} catch (error) {
// 捕获 401 Unauthorized 错误
if (error.response && error.response.status === 401) {
log(`Token 已过期或无效,正在尝试重新登录...`, 'warn');
const loginSuccess = await doLogin();
if (loginSuccess) {
log(`登录成功,重新执行任务: ${taskName}`, 'info');
return await requestFn(); // 重试一次
} else {
log(`重新登录失败,放弃任务: ${taskName}`, 'error');
}
} else {
throw error; // 其他错误直接抛出
}
}
}
/**
* 任务1每日签到
*/
async function doSignIn() {
console.log('\n--- 🟢 任务1: 每日签到 ---');
const execute = async () => {
const response = await axios.post(API.SIGN, null, { headers: getHeaders() });
const data = response.data;
if (data.success) {
log(`签到成功: ${data.message}`, 'success');
log(`获得奖励: ${data.reward}`, 'gift');
log(`当前余额: ${data.current_balance}`, 'money');
} else {
log(`签到结果: ${data.message}`, 'info');
}
};
try {
await requestWithRetry(execute, '每日签到');
} catch (error) {
log(`签到执行异常: ${error.message}`, 'error');
}
}
/**
* 任务2领取福利 (每日/每周/每月)
*/
async function doClaimPrivileges() {
console.log('\n--- 🟢 任务2: 领取特权福利 ---');
const execute = async () => {
const response = await axios.post(API.CLAIM, null, { headers: getHeaders() });
const data = response.data;
if (data.success) {
log(`${data.message}`, 'success');
// 解析并打印详细获得的物品
if (data.total_items && data.total_items.length > 0) {
console.log(' [获得物品清单]:');
data.total_items.forEach(item => {
// 简单的字典翻译,可根据实际情况补充
const itemNameMap = {
'time_rewind': '时光倒流(补签卡)',
'lucky_charm': '幸运符',
'revive_ticket': '复活票'
};
const name = itemNameMap[item.item_type] || item.item_type;
console.log(` - ${name} x ${item.quantity}`);
});
} else {
console.log(' - 本次没有获得新的物品 (可能已领取)');
}
} else {
log(`领取福利失败: ${data.message}`, 'warn');
}
};
try {
await requestWithRetry(execute, '领取福利');
} catch (error) {
log(`领取福利异常: ${error.message}`, 'error');
}
}
/**
* 任务3自动抽奖
*/
async function doLottery() {
console.log('\n--- 🟢 任务3: 自动抽奖 ---');
const stats = {
totalNet: 0,
draws: 0,
wins: 0,
losses: 0,
draws_0: 0,
endBalance: 0
};
let remainingAttempts = -1;
// 定义单次抽奖逻辑
const executeOneDraw = async () => {
return axios.post(API.LOTTERY, null, { headers: getHeaders() });
};
// 如果一开始就没 Token先登录一次避免循环里每次都判断
if (!CURRENT_TOKEN) {
if (!await doLogin()) return;
}
for (let i = 1; i <= CONFIG.MAX_ATTEMPTS; i++) {
try {
// 这里不使用 requestWithRetry 避免过于复杂的嵌套,
// 假设任务1和2通过后 Token 应该是有效的。
// 如果中途过期 (极少见)catch 块会捕获。
const response = await executeOneDraw();
const data = response.data;
if (data.success) {
stats.draws++;
stats.totalNet += data.net_change;
stats.endBalance = data.current_balance;
if (data.net_change > 0) stats.wins++;
else if (data.net_change < 0) stats.losses++;
else stats.draws_0++;
console.log(`[第${String(i).padStart(3, '0')}抽] ${data.message} | 变动: ${data.net_change} | 剩余: ${data.remaining_attempts}`);
remainingAttempts = data.remaining_attempts;
if (remainingAttempts === 0) {
log('今日抽奖次数已耗尽', 'info');
break;
}
} else {
log(`抽奖停止: ${data.message}`, 'warn');
break;
}
await delay(CONFIG.LOTTERY_DELAY);
} catch (error) {
// 如果中途 401尝试补救一次
if (error.response && error.response.status === 401) {
log('抽奖途中 Token 失效,尝试重连...', 'warn');
if (await doLogin()) {
i--; // 回退计数器,重试这一次
continue;
} else {
break; // 登录失败,彻底退出
}
}
log(`抽奖请求错误: ${error.message}`, 'error');
break;
}
}
// 统计报告
console.log('\n📊 [抽奖统计]');
if (stats.draws > 0) {
console.log(` - 总次数: ${stats.draws}`);
console.log(` - 盈利局: ${stats.wins}`);
console.log(` - 亏损局: ${stats.losses}`);
console.log(` - 最终余额: ${stats.endBalance}`);
console.log(` - 今日盈亏: ${stats.totalNet.toFixed(2)}`);
} else {
console.log(' - 未进行有效抽奖');
}
}
// ================= 主程序 =================
async function main() {
console.log(`🚀 Lucky.XYZ 自动脚本启动`);
// 1. 签到
await doSignIn();
// 2. 领福利 (中间间隔一下)
await delay(1000);
await doClaimPrivileges();
// 3. 抽奖
await delay(1000);
await doLottery();
console.log('\n🏁 所有任务执行完毕。');
}
main().catch(error => {
console.error('❌ 脚本致命错误:', error);
process.exit(1);
});