您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
点按抽奖 + 接口极速;右侧奖项统计与明细;面板可拖拽;刷新后统计清空(只保存设置)
// ==UserScript== // @license MIT // @name HDKylin自动抽奖 v1.1 // @namespace http://tampermonkey.net/ // @version 1.1.0 // @description 点按抽奖 + 接口极速;右侧奖项统计与明细;面板可拖拽;刷新后统计清空(只保存设置) // @match *://www.hdkyl.in/wof*.php* // @match *://www.hdkyl.in/dowof*.php* // @match *://hdkyl.in/wof*.php* // @match *://hdkyl.in/dowof*.php* // @match *://dev.hdkylin.top/wof*.php* // @match *://dev.hdkylin.top/dowof*.php* // @grant none // @run-at document-end // ==/UserScript== (function () { 'use strict'; const log = (...a) => console.log('[HDKylin抽奖]', ...a); const err = (...a) => console.error('[HDKylin抽奖][ERR]', ...a); // ——— 工具 ——— function waitForElement(selector, timeout = 8000) { return new Promise((resolve, reject) => { const t0 = Date.now(); (function chk() { const el = document.querySelector(selector); if (el) return resolve(el); if (Date.now() - t0 > timeout) return reject(new Error('元素未找到: ' + selector)); setTimeout(chk, 120); })(); }); } // ——— 主类(1.1核心 + 新UI)——— class AutoLottery { constructor() { // 运行态(不从 localStorage 恢复,刷新=清空统计) this.isRunning = false; this.fastMode = false; // 接口模式 this.drawCount = 0; // 已完成次数(内存) this.results = []; // 明细(内存) // 设置(允许保存在 localStorage) const saved = JSON.parse(localStorage.getItem('hdkylinAutoLotterySettings') || '{}'); this.totalDraws = saved.totalDraws || 10; // 目标次数(请求数/点击数) this.delay = (saved.delay ?? 3000); // 仅对点按模式生效 this.minMagicValue = saved.minMagicValue || 10000; // 页面识别 this.costPerDraw = 1000000; // 单次消耗魔力 this.drawUnit = 1; // 一次接口=几抽(1/10/100) this.detectPageType(); // 其它 this.currentMagicValue = 0; // 8个奖项固定统计(刷新即清空) this.stats = { '一等奖': 0, '二等奖': 0, '三等奖': 0, '四等奖': 0, '五等奖': 0, '六等奖': 0, '鼓励奖': 0, '谢谢参与': 0 }; // 备份原生对话框(保持1.1逻辑) this.originalConfirm = window.confirm; this.originalAlert = window.alert; this.originalPrompt = window.prompt; // 初始化 this.init().catch(err); } // —— 识别页面、接口路径 —— detectPageType() { const href = location.href; if (href.includes('wof100.php')) { this.costPerDraw = 1000000; this.drawUnit = 100; } else if (href.includes('wof10.php')) { this.costPerDraw = 100000; this.drawUnit = 10; } else { this.costPerDraw = 10000; this.drawUnit = 1; } log('页面识别:costPerDraw=', this.costPerDraw, ' drawUnit=', this.drawUnit); } getApiUrl() { const base = location.origin; if (location.pathname.includes('wof100.php')) return `${base}/wof/ajax_chs100.php?app=lottery_json`; if (location.pathname.includes('wof10.php')) return `${base}/wof/ajax_chs10.php?app=lottery_json`; return `${base}/wof/ajax_chs.php?app=lottery_json`; } // —— 初始化 —— async init() { this.getCurrentMagicValue(); this.createPanel(); this.makeDraggable(document.getElementById('autoLotteryPanel')); this.overrideDialogs(); this.updateDisplay(); // 结果页处理(保留1.1的“dowof页抓结果 → 自动跳回”) this.handlePageNavigation(); // 抽奖页存在#inner就等一下(点按模式要用) if (!location.href.includes('dowof')) { await waitForElement('#inner', 2500).catch(() => {}); } log('初始化完成'); } // —— 魔力值 —— getCurrentMagicValue() { try { const el = document.querySelector('.Detail b'); if (el && el.textContent.includes('当前拥有魔力值:')) { const m = el.textContent.match(/当前拥有魔力值:([\d,\.]+)/); if (m) this.currentMagicValue = parseFloat(m[1].replace(/,/g, '')) || 0; } } catch {} return this.currentMagicValue; } canContinue() { const v = this.getCurrentMagicValue(); if (v < Math.max(this.minMagicValue, this.costPerDraw)) return false; if (this.totalDraws > 0 && this.drawCount >= this.totalDraws) return false; return true; } // —— 结果归类(和1.1一致)—— normalizePrize(text) { const t = (text || '').trim(); if (!t) return '谢谢参与'; if (/一等奖|站免/.test(t)) return '一等奖'; if (/二等奖|魔力值?100?万/.test(t)) return '二等奖'; if (/三等奖|VIP|14天|一个月?VIP/.test(t)) return '三等奖'; if (/四等奖|彩虹ID/.test(t)) return '四等奖'; if (/五等奖|上传量|20\.?0?G/i.test(t)) return '五等奖'; if (/六等奖|补签卡/.test(t)) return '六等奖'; if (/鼓励奖|随机魔力|谢谢参与/.test(t)) return /谢谢参与/.test(t) ? '谢谢参与' : '鼓励奖'; return '谢谢参与'; } splitRname(rname) { if (!rname) return []; return rname.split(/\n|,|,|;|;|、/).map(s => s.trim()).filter(Boolean); } addStats(items) { items.forEach(txt => { const k = this.normalizePrize(txt); this.stats[k] = (this.stats[k] || 0) + 1; }); this.renderAwardStats(); } // —— 接口极速模式(照1.1:点击即连发,串行无间隔)—— async fastDrawOnce() { const url = this.getApiUrl(); const res = await fetch(url, { credentials: 'same-origin', headers: {'X-Requested-With':'XMLHttpRequest'} }); const data = await res.json(); // { rid, rname, num } const rname = (data && data.rname) ? String(data.rname) : ''; const items = this.splitRname(rname); // 计数:按返回条目数累加;若接口只给一条,就按 drawUnit this.drawCount += (items.length || this.drawUnit); this.results.push(rname || '未知结果'); this.addStats(items.length ? items : [rname]); this.updateDisplay(); } async startFastAuto() { if (this.isRunning) return; // 保存设置(仅设置) this.totalDraws = parseInt(document.getElementById('drawCountInput').value) || 0; this.minMagicValue = this.costPerDraw; localStorage.setItem('hdkylinAutoLotterySettings', JSON.stringify({ delay: this.delay, totalDraws: this.totalDraws, minMagicValue: this.minMagicValue })); this.isRunning = true; this.fastMode = true; this.updateDisplay(); try { // 1.1的节奏:无等待串行请求,直到达标或不足 while (this.isRunning && this.canContinue()) { await this.fastDrawOnce(); if (this.totalDraws > 0 && this.drawCount >= this.totalDraws) break; } } catch (e) { err('接口抽奖失败', e); } finally { // 停止但保留统计(刷新自然清空) this.stop(false); } } // —— 点按模式(照1.1)—— performSingleDraw() { if (!this.isRunning || this.fastMode) return; if (!this.canContinue()) { this.stop(false); return; } const inner = document.getElementById('inner'); if (inner) (window.$ ? window.$(inner).trigger('click') : inner.click()); } startAuto() { if (this.isRunning) return; this.totalDraws = parseInt(document.getElementById('drawCountInput').value) || 0; this.minMagicValue = this.costPerDraw; localStorage.setItem('hdkylinAutoLotterySettings', JSON.stringify({ delay: this.delay, totalDraws: this.totalDraws, minMagicValue: this.minMagicValue })); this.isRunning = true; this.fastMode = false; this.updateDisplay(); this.performSingleDraw(); // 立即点一次 } // —— 停止(刷新自然清空;这里不清空)—— stop(clearNow = false) { this.isRunning = false; this.fastMode = false; this.updateDisplay(); if (clearNow) { this.drawCount = 0; this.results = []; Object.keys(this.stats).forEach(k => this.stats[k] = 0); this.renderAwardStats(); this.updateDisplay(); } } // —— 结果页处理(照1.1)—— extractFromResultPage() { let r = null; if (window.rname && window.rname.trim()) r = window.rname.trim(); if (!r) { const params = new URLSearchParams(location.search); const pid = params.get('pid'); const map = { '1':'全站站免1天','2':'获得魔力100万','3':'获得VIP一个月','4':'获得彩虹ID一个月', '5':'获得上传量20.0GB','6':'获得补签卡1张','7':'一周年纪念勋章' }; if (pid) r = map[pid] || ('未知奖品ID:' + pid); } if (r) { this.drawCount += 1; this.results.push(r); this.addStats([r]); this.updateDisplay(); } return r; } handlePageNavigation() { // 结果页:抓取结果后,按设置的 delay 跳回抽奖页 if (location.href.includes('dowof')) { setTimeout(() => this.extractFromResultPage(), 400); setTimeout(() => this.gotoWof(), Math.max(this.delay, 800)); } else if (location.href.includes('wof')) { // 点按模式:循环点 if (this.isRunning && !this.fastMode) { if (this.canContinue()) { setTimeout(() => this.performSingleDraw(), 1000); } else { this.stop(false); } } } } gotoWof() { let url = location.href.replace('dowof', 'wof'); if (!url.includes('wof')) url = 'https://www.hdkyl.in/wof.php'; location.href = url; } // —— 对话框覆盖(照1.1)—— overrideDialogs() { const self = this; window.confirm = function (m) { return self.isRunning ? true : self.originalConfirm.call(window, m); }; window.alert = function (m) { if (self.isRunning) { if ('Notification' in window && Notification.permission === 'granted') { new Notification('HDKylin抽奖结果', { body: String(m), icon: '/favicon.ico' }); } return; } return self.originalAlert.call(window, m); }; window.prompt = function (m, d) { return self.isRunning ? (d || '') : self.originalPrompt.call(window, m, d); }; } // —— UI(双列 + 三行按钮)—— createPanel() { if (document.getElementById('autoLotteryPanel')) return; const panel = document.createElement('div'); panel.id = 'autoLotteryPanel'; panel.style.cssText = ` position:fixed;top:20px;right:20px;width:760px;background:#fff; border:2px solid #4595d5;border-radius:12px;box-shadow:0 6px 20px rgba(0,0,0,.25); z-index:99999;font-family:'Microsoft YaHei',Arial,sans-serif;font-size:14px;`; const btnBase = ` display:flex;flex-direction:column;align-items:center;justify-content:center; gap:4px;text-align:center;line-height:1.1;padding:14px 8px;border:none; border-radius:10px;cursor:pointer;font-weight:700;min-height:86px;`; panel.innerHTML = ` <div class="hdk-header" style="background:linear-gradient(135deg,#5887e0,#3b62c6);color:#fff;padding:14px 16px;border-radius:10px 10px 0 0;display:flex;justify-content:space-between;align-items:center;"> <strong>🧩 HDKylin自动抽奖</strong> <button id="closePanel" style="background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer">×</button> </div> <div class="hdk-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:16px;"> <!-- 左列:概览 + 参数 + 按钮 --> <div style="display:flex;flex-direction:column;gap:14px;"> <div style="padding:12px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);border-radius:8px;border-left:4px solid #5887e0;"> <div style="margin-bottom:6px;">💰 当前魔力值: <b id="currentMagic" style="color:#2d6cdf">${this.currentMagicValue.toLocaleString()}</b></div> <div style="margin-bottom:6px;">🎯 可抽奖次数: <b id="maxDraws" style="color:#28a745">${Math.floor(this.currentMagicValue/this.costPerDraw)}</b></div> <div>📊 已完成: <b id="completedDraws" style="color:#17a2b8">${this.drawCount}</b> / <b id="totalPlanned">${this.totalDraws||0}</b></div> </div> <div style="padding:12px;background:#f8f9fa;border-radius:8px;"> <label style="display:block;margin-bottom:8px;font-weight:700;">🎲 抽奖次数(总目标)</label> <input type="number" id="drawCountInput" value="${this.totalDraws}" min="1" max="999" style="width:100%;padding:10px;border:2px solid #ddd;border-radius:8px;"> <div style="height:10px"></div> <label style="display:block;margin-bottom:8px;font-weight:700;">⏱️ 抽奖间隔</label> <div style="display:flex;align-items:center;gap:10px;"> <input type="number" id="delayInput" value="${Math.round(this.delay/1000)}" min="0" max="60" style="width:90px;padding:8px;border:2px solid #ddd;border-radius:8px;"> <span style="color:#666;">秒(接口模式忽略)</span> </div> <div style="height:12px"></div> <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;"> <button id="startAutoBtn" style="${btnBase}background:linear-gradient(135deg,#28a745,#20c997);color:#fff;"> <span>🚀</span><span>开始抽奖</span><span style="font-weight:500;opacity:.85">(点按)</span> </button> <button id="startFastBtn" style="${btnBase}background:linear-gradient(135deg,#ff7a18,#ff3d00);color:#fff;"> <span>⚡</span><span>快速抽奖</span><span style="font-weight:500;opacity:.85">(接口)</span> </button> <button id="stopBtn" style="${btnBase}background:linear-gradient(135deg,#dc3545,#c82333);color:#fff;"> <span>🟥</span><span>停止</span><span style="font-weight:500;opacity:.85"> </span> </button> </div> <div style="margin-top:10px;font-size:12px;color:#666;"> 当前页面一次接口请求 = <b>${this.drawUnit}</b> 抽;接口:<code>${this.getApiUrl().replace(location.origin,'')}</code> </div> </div> <div style="text-align:center;padding:10px;background:#e9ecef;border-radius:8px;"> <b style="color:#495057;">状态:</b> <span id="statusText" style="font-weight:700;color:#28a745">🟢 就绪</span> </div> </div> <!-- 右列:统计 + 明细 --> <div style="display:flex;flex-direction:column;gap:14px;"> <div> <div style="font-weight:700;margin-bottom:8px;color:#495057;">📑 奖项统计:</div> <div id="awardStats" style="display:grid;grid-template-columns:repeat(2,1fr);gap:6px;background:#f8f9fa;padding:10px;border-radius:8px;border:1px solid #dee2e6;"></div> </div> <div style="flex:1;display:flex;flex-direction:column;"> <div style="font-weight:700;margin-bottom:8px;color:#495057;">🧾 抽奖明细:</div> <div id="resultStats" style="flex:1;max-height:360px;overflow-y:auto;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #dee2e6;"> <div style="text-align:center;color:#6c757d;padding:18px;">暂无抽奖结果</div> </div> </div> </div> </div> `; document.body.appendChild(panel); // 事件绑定(1.1节奏) document.getElementById('startAutoBtn')?.addEventListener('click', () => this.startAuto()); document.getElementById('startFastBtn')?.addEventListener('click', () => this.startFastAuto()); document.getElementById('stopBtn')?.addEventListener('click', () => this.stop(false)); document.getElementById('closePanel')?.addEventListener('click', () => panel.remove()); document.getElementById('delayInput')?.addEventListener('change', () => { const v = Math.max(0, parseInt(document.getElementById('delayInput').value) || 0) * 1000; this.delay = v; localStorage.setItem('hdkylinAutoLotterySettings', JSON.stringify({ delay: this.delay, totalDraws: this.totalDraws, minMagicValue: this.minMagicValue })); }); this.injectStyle(); this.renderAwardStats(); } // 奖项统计渲染(8项固定) renderAwardStats() { const box = document.getElementById('awardStats'); if (!box) return; const order = ['一等奖','二等奖','三等奖','四等奖','五等奖','六等奖','鼓励奖','谢谢参与']; box.innerHTML = order.map(k => ` <div style="background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:8px 10px;display:flex;justify-content:space-between;gap:8px;"> <span>${k}</span><b>${this.stats[k]||0}</b> </div>`).join(''); } updateDisplay() { const startBtn = document.getElementById('startAutoBtn'); const fastBtn = document.getElementById('startFastBtn'); const stopBtn = document.getElementById('stopBtn'); if (startBtn) startBtn.disabled = this.isRunning; if (fastBtn) fastBtn.disabled = this.isRunning; if (stopBtn) stopBtn.disabled = !this.isRunning; const status = document.getElementById('statusText'); if (status) { status.textContent = this.isRunning ? (this.fastMode ? '🔴 运行中(快速)' : '🔴 运行中') : '🟢 就绪'; status.style.color = this.isRunning ? '#dc3545' : '#28a745'; } const completed = document.getElementById('completedDraws'); const planned = document.getElementById('totalPlanned'); if (completed) completed.textContent = this.drawCount; if (planned) planned.textContent = this.totalDraws || 0; const magic = document.getElementById('currentMagic'); const max = document.getElementById('maxDraws'); if (magic && max) { const v = this.getCurrentMagicValue(); magic.textContent = v.toLocaleString(); max.textContent = Math.floor(v / this.costPerDraw); } const detail = document.getElementById('resultStats'); if (detail) { detail.innerHTML = this.results.length ? this.results.map((r,i)=>`<div>${i+1}. ${r}</div>`).join('') : '<div style="text-align:center;color:#6c757d;padding:18px;">暂无抽奖结果</div>'; } } // 可拖拽(标题栏拖动) makeDraggable(panel) { if (!panel) return; const handle = panel.querySelector('.hdk-header') || panel; let dragging = false, sx=0, sy=0, cx=0, cy=0; handle.style.cursor = 'move'; handle.addEventListener('mousedown', (e)=>{ if (e.target.id === 'closePanel') return; dragging = true; sx = e.clientX - cx; sy = e.clientY - cy; document.body.style.userSelect='none'; }); document.addEventListener('mousemove', (e)=>{ if (!dragging) return; cx = e.clientX - sx; cy = e.clientY - sy; panel.style.transform = `translate(${cx}px, ${cy}px)`; }); document.addEventListener('mouseup', ()=>{ dragging=false; document.body.style.userSelect=''; }); } injectStyle() { const style = document.createElement('style'); style.textContent = ` #autoLotteryPanel button:hover{ transform:translateY(-1px); box-shadow:0 4px 8px rgba(0,0,0,.18); } #autoLotteryPanel button:disabled{ opacity:.6; cursor:not-allowed; transform:none!important; } #autoLotteryPanel input:focus{ outline:none;border-color:#5887e0;box-shadow:0 0 0 3px rgba(88,135,224,.12); } @media (max-width: 880px){ #autoLotteryPanel{ width:96vw; right:2vw; } #autoLotteryPanel .hdk-grid{ grid-template-columns:1fr; } } #autoLotteryPanel::-webkit-scrollbar{ width:6px; } #autoLotteryPanel::-webkit-scrollbar-thumb{ background:#c1c1c1;border-radius:3px; } `; document.head.appendChild(style); } } if ('Notification' in window && Notification.permission === 'default') { Notification.requestPermission().catch(()=>{}); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new AutoLottery()); } else { new AutoLottery(); } })();