/* * @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); });