您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动抽奖工具,支持完整统计和动效优化
// ==UserScript== // @name HHCLUB 魔力抽奖助手-统计版 // @namespace http://tampermonkey.net/ // @version 0.4 // @description 自动抽奖工具,支持完整统计和动效优化 // @author Assistant // @match https://hhanclub.top/lucky.php* // @grant GM_setValue // @grant GM_getValue // @run-at document-idle // @license MIT // ==/UserScript== (function() { 'use strict'; // 检查抽奖页面 if (!document.querySelector('#spain1') || !document.querySelector('#lotteryButton')) return; // 全局变量 let isAutoDrawing = false; let sessionResults = []; let sessionDrawCount = 0; // 总尝试次数 let sessionSuccessCount = 0; // 成功次数 const COST_PER_DRAW = 2000; // 本次统计数据 let sessionStats = { magic: 0, upload: 0, signCard: 0, invite: 0, rainbowId: 0, vip: 0 }; // 数据存储相关 const getCumulativeStats = () => ({ totalDraws: GM_getValue('totalDraws', 0), totalSuccess: GM_getValue('totalSuccess', 0), totalCost: GM_getValue('totalCost', 0), totalMagic: GM_getValue('totalMagic', 0), totalUpload: GM_getValue('totalUpload', 0), totalSignCard: GM_getValue('totalSignCard', 0), totalInvite: GM_getValue('totalInvite', 0), totalRainbowId: GM_getValue('totalRainbowId', 0), totalVip: GM_getValue('totalVip', 0), prizeStats: JSON.parse(GM_getValue('prizeStats', '{}')) }); const saveCumulativeStats = (stats) => { Object.entries(stats).forEach(([key, value]) => { GM_setValue(key, typeof value === 'object' ? JSON.stringify(value) : value); }); }; // 解析奖品信息 const parsePrize = (prizeName) => { const patterns = { magic: /魔力\s*(\d+)/, upload: /上传量\s*(\d+)\s*GB/i, signCard: /补签卡\s*(\d+)/, invite: /邀请\s*(\d+)/, rainbowId: /彩虹\s*ID.*?(\d+)\s*Day/i, vip: /VIP.*?(\d+)\s*Day/i }; const result = {}; Object.entries(patterns).forEach(([key, pattern]) => { const match = prizeName.match(pattern); result[key] = match ? parseInt(match[1]) : 0; }); return result; }; // 创建悬浮面板 function createFloatingPanel() { const panel = document.createElement('div'); panel.id = 'lottery-helper-panel'; panel.innerHTML = ` <div style=" position: fixed; top: 20px; left: 20px; width: 280px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; padding: 20px; color: white; box-shadow: 0 20px 40px rgba(0,0,0,0.4); z-index: 50000; font-family: 'Microsoft YaHei', Arial, sans-serif; user-select: none; backdrop-filter: blur(15px); "> <div style="text-align: center; font-weight: bold; margin-bottom: 20px; font-size: 18px;"> 🎰 魔力抽奖助手 </div> <button id="auto-draw" style=" background: linear-gradient(135deg, #FF6B6B, #FF8E53); border: none; color: white; padding: 15px; border-radius: 10px; cursor: pointer; font-size: 16px; font-weight: 600; width: 100%; margin-bottom: 15px; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4); ">🚀 自动抽奖</button> <button id="stop-draw" style=" background: linear-gradient(135deg, #95A5A6, #7F8C8D); border: none; color: white; padding: 12px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; width: 100%; margin-bottom: 15px; display: none; transition: all 0.3s ease; ">⏹️ 停止抽奖</button> <!-- 动态进度条 --> <div id="progress-section" style="margin-bottom: 15px; display: none;"> <div style="background: rgba(255,255,255,0.2); height: 12px; border-radius: 6px; overflow: hidden; position: relative;"> <div id="progress-bar" style=" height: 100%; width: 0%; border-radius: 6px; position: relative; overflow: hidden; background: linear-gradient(90deg, #4ECDC4, #44A08D); transition: width 0.5s ease; "> <!-- 动态光效 --> <div style=" position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent); animation: progress-shine 2s infinite; "></div> </div> </div> <div id="progress-text" style="font-size: 12px; text-align: center; margin-top: 5px; opacity: 0.9;"> 准备中... </div> </div> <!-- 本次统计 --> <div style=" background: rgba(255,255,255,0.1); border-radius: 10px; padding: 15px; margin-bottom: 15px; backdrop-filter: blur(10px); "> <div style="font-size: 14px; font-weight: 600; margin-bottom: 12px; color: #FFE082; text-align: center;"> 📊 本次统计 </div> <div style="font-size: 11px; line-height: 1.8;"> <div style="display: flex; justify-content: space-between;"><span>状态:</span><span id="current-status">待机中</span></div> <div style="display: flex; justify-content: space-between;"><span>抽取:</span><span><span id="session-success">0</span>/<span id="session-total">0</span></span></div> <div style="display: flex; justify-content: space-between;"><span>消耗:</span><span id="session-cost">0</span></div> <div style="display: flex; justify-content: space-between;"><span>魔力:</span><span id="session-magic" style="color: #4ECDC4;">+0</span></div> <div style="display: flex; justify-content: space-between;"><span>上传量:</span><span id="session-upload" style="color: #4ECDC4;">+0 GB</span></div> <div style="display: flex; justify-content: space-between;"><span>补签卡:</span><span id="session-signcard" style="color: #4ECDC4;">+0</span></div> <div style="display: flex; justify-content: space-between;"><span>邀请:</span><span id="session-invite" style="color: #4ECDC4;">+0</span></div> <div style="display: flex; justify-content: space-between;"><span>彩虹ID:</span><span id="session-rainbow" style="color: #4ECDC4;">+0天</span></div> <div style="display: flex; justify-content: space-between;"><span>VIP:</span><span id="session-vip" style="color: #4ECDC4;">+0天</span></div> </div> </div> <button id="show-stats" style=" background: linear-gradient(135deg, #74B9FF, #0984E3); border: none; color: white; padding: 12px 20px; border-radius: 20px; cursor: pointer; font-size: 13px; font-weight: 600; width: 100%; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(116, 185, 255, 0.3); ">📈 查看累积统计</button> <!-- 添加进度条动画样式 --> <style> @keyframes progress-shine { 0% { left: -100%; } 100% { left: 100%; } } @keyframes progress-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } </style> </div> `; document.body.appendChild(panel); addButtonEffects(); } // 添加按钮特效 const addButtonEffects = () => { ['auto-draw', 'stop-draw', 'show-stats'].forEach(id => { const btn = document.getElementById(id); if (!btn) return; btn.addEventListener('mouseenter', function() { if (!this.disabled) { this.style.transform = 'translateY(-3px) scale(1.02)'; this.style.filter = 'brightness(1.1)'; } }); btn.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0) scale(1)'; this.style.filter = 'brightness(1)'; }); }); }; // 更新状态和统计 const updateStatus = (status) => { const element = document.getElementById('current-status'); if (element) { element.textContent = status; element.style.color = status.includes('错误') ? '#FF6B6B' : '#4ECDC4'; } }; // 🔥 修正消耗计算逻辑:只按成功次数计算消耗 const updateSessionStats = () => { const updates = { 'session-success': sessionSuccessCount, 'session-total': sessionDrawCount, 'session-cost': (sessionSuccessCount * COST_PER_DRAW).toLocaleString(), // 👈 修正:按成功次数计算 'session-magic': '+' + sessionStats.magic.toLocaleString(), 'session-upload': '+' + sessionStats.upload + ' GB', 'session-signcard': '+' + sessionStats.signCard, 'session-invite': '+' + sessionStats.invite, 'session-rainbow': '+' + sessionStats.rainbowId + '天', 'session-vip': '+' + sessionStats.vip + '天' }; Object.entries(updates).forEach(([id, value]) => { const element = document.getElementById(id); if (element) { element.textContent = value; if (id.startsWith('session-') && id !== 'session-success' && id !== 'session-total' && id !== 'session-cost') { element.style.color = value === '+0' || value === '+0 GB' || value === '+0天' ? '#FFE082' : '#4ECDC4'; } } }); }; // 增强进度条效果 const updateProgress = (current, isInfinite = false) => { const progressSection = document.getElementById('progress-section'); const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); if (!progressSection || !progressBar || !progressText) return; if (isInfinite) { progressSection.style.display = 'block'; progressBar.style.width = '100%'; progressBar.style.background = 'linear-gradient(90deg, #FF6B6B, #FF8E53)'; progressBar.style.animation = 'progress-pulse 1.5s ease-in-out infinite'; progressText.textContent = `连续抽奖中... 已完成 ${current} 次`; } else { progressSection.style.display = 'none'; progressBar.style.animation = 'none'; } }; // 等待抽奖状态 const waitForNotRunning = (timeout = 10000) => new Promise((resolve) => { const startTime = Date.now(); const checkInterval = setInterval(() => { if (!window.running || Date.now() - startTime > timeout) { clearInterval(checkInterval); if (window.running) window.running = false; resolve(); } }, 100); }); // 执行单次抽奖 const performSingleDraw = async () => { await waitForNotRunning(8000); if (window.running) throw new Error('抽奖状态异常'); window.running = true; console.log(`执行第${sessionDrawCount + 1}次抽奖...`); try { const response = await fetch('/plugin/lucky-draw', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, body: '' }); window.running = false; if (!response.ok) throw new Error(`HTTP ${response.status}`); const responseData = await response.json(); if (responseData.ret !== 0) throw new Error(responseData.msg || '抽奖失败'); const result = { prize: responseData.data['prize_text'], angle: responseData.data.angle, timestamp: new Date().toLocaleString(), rawData: responseData.data }; sessionResults.push(result); sessionDrawCount++; sessionSuccessCount++; // 只有成功时才增加成功计数 const parsed = parsePrize(result.prize); Object.keys(sessionStats).forEach(key => sessionStats[key] += parsed[key] || 0); updateSessionStats(); console.log(`第${sessionDrawCount}次抽奖成功:`, result.prize); return result; } catch (error) { window.running = false; sessionDrawCount++; // 失败也计入总尝试次数 updateSessionStats(); // 🔥 关键修正:失败时也更新统计,但成功次数不变,所以消耗不变 throw error; } }; // 获取称号 const getTitleByNetGain = (netGain, hasVip) => { if (hasVip) return '万里挑一的好运!'; if (netGain >= 100000) return '全服最好运!'; if (netGain >= -5000) return '运气不错!'; if (netGain >= -10000) return '菜鸡!'; if (netGain >= -30000) return '手气真差!'; if (netGain >= -70000) return '大冤种!'; if (netGain >= -100000) return '倒霉鬼!'; return '憨的一逼!'; }; // 自动抽奖主函数 const startAutoDrawing = async () => { if (isAutoDrawing) return alert('抽奖正在进行中...'); isAutoDrawing = true; sessionResults = []; sessionDrawCount = sessionSuccessCount = 0; sessionStats = { magic: 0, upload: 0, signCard: 0, invite: 0, rainbowId: 0, vip: 0 }; updateButtonsState(true); updateStatus('自动抽奖中...'); updateSessionStats(); updateProgress(0, true); let consecutiveFailures = 0; const maxConsecutiveFailures = 5; try { while (isAutoDrawing) { updateStatus(`抽奖中... (已成功 ${sessionSuccessCount} 次)`); updateProgress(sessionSuccessCount, true); try { await performSingleDraw(); consecutiveFailures = 0; await new Promise(resolve => setTimeout(resolve, 1500)); } catch (error) { consecutiveFailures++; console.warn(`抽奖失败 (连续${consecutiveFailures}次):`, error); if (consecutiveFailures >= maxConsecutiveFailures) { console.error('连续失败次数过多,暂停10秒'); updateStatus('连续失败,暂停中...'); await new Promise(resolve => setTimeout(resolve, 10000)); consecutiveFailures = 0; } else { await new Promise(resolve => setTimeout(resolve, 3000 * consecutiveFailures)); } } } } catch (error) { console.error('自动抽奖异常:', error); } finally { isAutoDrawing = false; updateButtonsState(false); updateProgress(sessionSuccessCount, true); updateStatus(`已停止 - 成功${sessionSuccessCount}次`); if (sessionSuccessCount > 0) { updateCumulativeStats(sessionResults); setTimeout(showSessionResults, 1000); } } }; // 更新累积统计 const updateCumulativeStats = (results) => { const stats = getCumulativeStats(); results.forEach(result => { stats.totalDraws++; stats.totalSuccess++; stats.totalCost += COST_PER_DRAW; // 累积统计按成功次数计算消耗 const prizeName = result.prize; stats.prizeStats[prizeName] = (stats.prizeStats[prizeName] || 0) + 1; const parsed = parsePrize(prizeName); stats.totalMagic += parsed.magic; stats.totalUpload += parsed.upload; stats.totalSignCard += parsed.signCard; stats.totalInvite += parsed.invite; stats.totalRainbowId += parsed.rainbowId; stats.totalVip += parsed.vip; }); saveCumulativeStats(stats); }; // 显示本次结果 const showSessionResults = () => { if (!sessionResults.length) return; const sessionPrizeStats = {}; sessionResults.forEach(result => { sessionPrizeStats[result.prize] = (sessionPrizeStats[result.prize] || 0) + 1; }); // 🔥 修正净收益计算:按成功次数计算消耗 const netGain = sessionStats.magic - (sessionSuccessCount * COST_PER_DRAW); let resultHTML = ` <div style="max-height: 70vh; overflow-y: auto;"> <h3 style="margin-top: 0; color: #333; text-align: center;">🎉 本次抽奖结果</h3> <div style="background: linear-gradient(135deg, #74B9FF 0%, #0984E3 100%); color: white; padding: 15px; border-radius: 10px; margin-bottom: 15px; text-align: center;"> <div style="font-size: 16px; font-weight: bold;">本次统计</div> <div style="margin-top: 10px; font-size: 13px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px;"> <div>尝试次数: ${sessionDrawCount} 次</div> <div>成功次数: ${sessionSuccessCount} 次</div> <div>成功率: ${sessionDrawCount > 0 ? ((sessionSuccessCount/sessionDrawCount)*100).toFixed(1) : 0}%</div> <div>消耗憨豆: ${(sessionSuccessCount * COST_PER_DRAW).toLocaleString()}</div> <div>获得魔力: ${sessionStats.magic.toLocaleString()}</div> <div>上传量: +${sessionStats.upload} GB</div> <div>补签卡: +${sessionStats.signCard}</div> <div>邀请: +${sessionStats.invite}</div> <div>彩虹ID: +${sessionStats.rainbowId}天</div> <div>VIP: +${sessionStats.vip}天</div> <div style="grid-column: 1/-1; color: ${netGain >= 0 ? '#4ECDC4' : '#FF6B6B'}; font-weight: bold; font-size: 14px;"> 净收益: ${netGain >= 0 ? '+' : ''}${netGain.toLocaleString()} </div> </div> </div> <div> <h4 style="color: #2c3e50; border-bottom: 2px solid #74B9FF; padding-bottom: 8px;">🏆 奖品详情</h4> `; Object.entries(sessionPrizeStats).sort((a, b) => b[1] - a[1]).forEach(([prize, count]) => { const percentage = ((count / sessionResults.length) * 100).toFixed(1); resultHTML += ` <div style="margin: 6px 0; padding: 10px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px; display: flex; justify-content: space-between; align-items: center; font-size: 13px;"> <span style="font-weight: bold;">${prize}</span> <span style="background: rgba(255,255,255,0.2); padding: 3px 8px; border-radius: 12px; font-size: 12px;">${count} 次 (${percentage}%)</span> </div> `; }); resultHTML += `</div></div>`; createModal('本次抽奖结果', resultHTML); }; // 显示累积统计 - 增大称号文字 const showCumulativeStats = () => { const stats = getCumulativeStats(); if (!stats.totalDraws) return alert('暂无累积统计数据'); const netTotalGain = stats.totalMagic - stats.totalCost; const hasVip = stats.totalVip > 0; const titleText = getTitleByNetGain(netTotalGain, hasVip); let statsHTML = ` <div style="max-height: 75vh; overflow-y: auto;"> <!-- 超大称号显示 --> <div style=" position: absolute; top: -10px; left: -10px; background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%); color: white; padding: 15px 25px; border-radius: 20px 0 20px 0; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); z-index: 10; animation: title-glow 2s ease-in-out infinite alternate; ">${titleText}</div> <style> @keyframes title-glow { from { box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); } to { box-shadow: 0 12px 35px rgba(255, 107, 107, 0.6); } } </style> <h3 style="margin-top: 35px; color: #333; text-align: center;">📊 累积统计数据</h3> <div style="background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%); color: white; padding: 15px; border-radius: 10px; margin-bottom: 15px; text-align: center;"> <div style="font-size: 16px; font-weight: bold; margin-bottom: 10px;">总体数据</div> <div style="font-size: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 6px;"> <div>总抽奖: ${stats.totalDraws.toLocaleString()} 次</div> <div>成功率: ${((stats.totalSuccess / stats.totalDraws) * 100).toFixed(1)}%</div> <div>总消耗: ${stats.totalCost.toLocaleString()}</div> <div>总魔力: ${stats.totalMagic.toLocaleString()}</div> <div>上传量: ${stats.totalUpload} GB</div> <div>补签卡: ${stats.totalSignCard}</div> <div>邀请: ${stats.totalInvite}</div> <div>彩虹ID: ${stats.totalRainbowId}天</div> <div>VIP: ${stats.totalVip}天</div> <div style="grid-column: 1/-1; color: ${netTotalGain >= 0 ? '#4ECDC4' : '#FFE082'}; font-weight: bold; font-size: 15px; margin-top: 5px;"> 累积净收益: ${netTotalGain >= 0 ? '+' : ''}${netTotalGain.toLocaleString()} </div> </div> </div> <div> <h4 style="color: #2c3e50; border-bottom: 2px solid #FF6B6B; padding-bottom: 8px;">🎁 奖品明细</h4> <div style="max-height: 300px; overflow-y: auto;"> `; Object.entries(stats.prizeStats).sort((a, b) => b[1] - a[1]).forEach(([prize, count]) => { const percentage = ((count / stats.totalSuccess) * 100).toFixed(1); statsHTML += ` <div style="margin: 5px 0; padding: 8px; background: linear-gradient(135deg, #74B9FF 0%, #0984E3 100%); color: white; border-radius: 6px; display: flex; justify-content: space-between; align-items: center; font-size: 12px;"> <span style="font-weight: bold;">${prize}</span> <span style="background: rgba(255,255,255,0.2); padding: 2px 6px; border-radius: 10px; font-size: 11px;">${count} 次 (${percentage}%)</span> </div> `; }); statsHTML += ` </div> </div> <div style="text-align: center; margin-top: 15px;"> <button style="background: #e74c3c; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 12px;" onclick="clearCumulativeStats()">清空统计数据</button> </div> </div> `; createModal('累积统计', statsHTML); }; // 创建模态框 const createModal = (title, content) => { document.getElementById('stats-modal')?.remove(); const modal = document.createElement('div'); modal.id = 'stats-modal'; modal.innerHTML = ` <div style=" position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 60000; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(8px); " onclick="event.stopPropagation(); if(event.target === this) this.remove();"> <div style=" background: white; padding: 25px; border-radius: 15px; max-width: 700px; width: 90%; max-height: 85vh; overflow-y: auto; position: relative; box-shadow: 0 25px 50px rgba(0,0,0,0.4); " onclick="event.stopPropagation();"> <button style=" position: absolute; top: 15px; right: 20px; background: none; border: none; font-size: 24px; cursor: pointer; color: #999; font-weight: bold; z-index: 61000; " onclick="document.getElementById('stats-modal').remove()">×</button> ${content} <div style="text-align: center; margin-top: 20px;"> <button style=" background: linear-gradient(135deg, #74B9FF 0%, #0984E3 100%); color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-weight: 600; transition: transform 0.2s ease; " onclick="document.getElementById('stats-modal').remove()" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'">确定</button> </div> </div> </div> `; document.body.appendChild(modal); }; // 清空统计 window.clearCumulativeStats = function() { if (!confirm('确定要清空所有累积统计数据吗?此操作不可恢复!')) return; ['totalDraws', 'totalSuccess', 'totalCost', 'totalMagic', 'totalUpload', 'totalSignCard', 'totalInvite', 'totalRainbowId', 'totalVip'].forEach(key => GM_setValue(key, 0)); GM_setValue('prizeStats', '{}'); alert('统计数据已清空'); document.getElementById('stats-modal')?.remove(); }; // 停止抽奖 const stopDrawing = () => { isAutoDrawing = false; updateStatus('正在停止...'); console.log('用户手动停止抽奖'); }; // 更新按钮状态 const updateButtonsState = (drawing) => { const autoDrawBtn = document.getElementById('auto-draw'); const stopDrawBtn = document.getElementById('stop-draw'); const progressSection = document.getElementById('progress-section'); if (autoDrawBtn) { autoDrawBtn.style.display = drawing ? 'none' : 'block'; autoDrawBtn.disabled = drawing; } if (stopDrawBtn) stopDrawBtn.style.display = drawing ? 'block' : 'none'; if (progressSection) progressSection.style.display = drawing ? 'block' : 'none'; }; // 初始化 const init = () => { setTimeout(() => { console.log('魔力抽奖助手正在初始化...'); createFloatingPanel(); // 绑定事件 document.getElementById('auto-draw')?.addEventListener('click', startAutoDrawing); document.getElementById('stop-draw')?.addEventListener('click', stopDrawing); document.getElementById('show-stats')?.addEventListener('click', showCumulativeStats); // 防止意外刷新 window.addEventListener('beforeunload', (e) => { if (isAutoDrawing) { e.preventDefault(); e.returnValue = '抽奖正在进行中,确定要离开吗?'; return e.returnValue; } }); updateStatus('就绪'); updateSessionStats(); console.log('魔力抽奖助手已启动'); const stats = getCumulativeStats(); if (stats.totalDraws > 0) { console.log(`累积统计: ${stats.totalDraws}次抽奖, 净收益: ${stats.totalMagic - stats.totalCost}`); } }, 1500); }; // 启动 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();