您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
实现在种子详情页显示该种在其他站点存在情况
当前为
// ==UserScript== // @name IYUU 全站辅种检测 // @namespace iyuu-crossseed // @version 1.0.2 // @description 实现在种子详情页显示该种在其他站点存在情况 // @author YourName // @match https://*/details.php* // @match http://*/details.php* // @run-at document-idle // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @license GPL-3.0 // @connect 2025.iyuu.cn // @connect * // ==/UserScript== (function () { 'use strict'; /* ========== 0) 可选:默认 Token(留空即可) ========== */ const IYUU_TOKEN_DEFAULT = ''; /* ========== 0.1) 站点图标映射(在此添加/维护图标 URL) ========== * * 用法说明: * 1)优先按 sid 匹配:ICON_MAP.sid[<数字sid>] = 'https://.../logo.png' * 2)其次按名称匹配(nickname 或 site,不区分大小写、会做去空格处理): * ICON_MAP.name['mteam'] = 'https://.../mteam.svg' * ICON_MAP.name['ourbits'] = 'https://.../ourbits.png' * 3)未配置图标的站点将不显示图标,只显示名称(名称字号会更大些)。 */ const ICON_MAP = { sid: { 1: 'https://icon.xiaoge.org/images/pt/FRDS.png', 2: 'https://icon.xiaoge.org/images/pt/PTHOME.png', 3: 'https://icon.xiaoge.org/images/pt/M-Team.png', 4: 'https://icon.xiaoge.org/images/pt/HDsky.png', 8: 'https://icon.xiaoge.org/images/pt/btschool.png', 6: 'https://icon.xiaoge.org/images/pt/Pter.png', 7: 'https://icon.xiaoge.org/images/pt/HDHome.png', 23: 'https://icon.xiaoge.org/images/pt/Nvme.png', 25: 'https://icon.xiaoge.org/images/pt/CHDbits.png', 33: 'https://icon.xiaoge.org/images/pt/OpenCD.png', 68: 'https://icon.xiaoge.org/images/pt/Audiences.png', 72: 'https://icon.xiaoge.org/images/pt/HHCLUB.png', 9: 'https://icon.xiaoge.org/images/pt/OurBits.png', 14: 'https://icon.xiaoge.org/images/pt/TTG.png', 86: 'https://icon.xiaoge.org/images/pt/UBits.png', 93: 'https://icon.xiaoge.org/images/pt/agsv.png', 89: 'https://icon.xiaoge.org/images/pt/carpt.png', 84: 'https://icon.xiaoge.org/images/pt/cyanbug.png', 90: 'https://icon.xiaoge.org/images/pt/dajiao.png', 51: 'https://icon.xiaoge.org/images/pt/dicmusic.png', 40: 'https://icon.xiaoge.org/images/pt/discfan.png', 64: 'https://icon.xiaoge.org/images/pt/gpw.png', 56: 'https://icon.xiaoge.org/images/pt/haidan.png', 29: 'https://icon.xiaoge.org/images/pt/hdarea.png', 105: 'https://icon.xiaoge.org/images/pt/hddolby.png', 57: 'https://icon.xiaoge.org/images/pt/hdfans.png', 97: 'https://icon.xiaoge.org/images/pt/hdkyl.png', 18: 'https://icon.xiaoge.org/images/pt/nicept.png', 88: 'https://icon.xiaoge.org/images/pt/panda.png', 94: 'https://icon.xiaoge.org/images/pt/ptvicomo.png', 95: 'https://icon.xiaoge.org/images/pt/qingwapt.png', 82: 'https://icon.xiaoge.org/images/pt/rousi.png', 24: 'https://icon.xiaoge.org/images/pt/soulvoice.png', 5: 'https://icon.xiaoge.org/images/pt/tjupt.png', 96: 'https://icon.xiaoge.org/images/pt/xingtan.png', 80: 'https://icon.xiaoge.org/images/pt/zhuque.png', 81: 'https://icon.xiaoge.org/images/pt/zmpt.png', }, name: { // 'mteam': 'https://example.com/icons/mteam.svg', // 'hdchina': 'https://example.com/icons/hdchina.png', // 'ourbits': 'https://example.com/icons/ourbits.svg', } }; function lookupIconURL({ sid, nickname, site }) { if (sid != null && ICON_MAP.sid[sid]) return ICON_MAP.sid[sid]; const toKey = (s) => (s || '').toString().trim().toLowerCase(); const n1 = toKey(nickname); const n2 = toKey(site); if (n1 && ICON_MAP.name[n1]) return ICON_MAP.name[n1]; if (n2 && ICON_MAP.name[n2]) return ICON_MAP.name[n2]; return null; // 未配置则不显示图标 } /* ========== 工具:安全样式与插入 ========== */ function addStyle(css) { try { if (typeof GM_addStyle === 'function') return GM_addStyle(css); } catch {} const st = document.createElement('style'); st.textContent = css; (document.head || document.documentElement).appendChild(st); } function safePrepend(parent, child) { try { if (!parent) parent = document.body || document.documentElement; if (parent.firstChild) parent.insertBefore(child, parent.firstChild); else parent.appendChild(child); } catch { (document.body || document.documentElement).appendChild(child); } } function findTopContainer() { const selectors = ['#outer', '#wrapper', '#maincontent', '#content', '.main', 'body']; for (const sel of selectors) { const el = document.querySelector(sel); if (el) return el; } return document.body || document.documentElement; } /* ========== 顶部横幅 UI(样式微调版本) ========== */ addStyle(` .iyuu-topbar{position:sticky;top:0;z-index:999999;background:rgba(9,14,28,.92);color:#fff;border-bottom:1px solid #ffffff1a;backdrop-filter:blur(6px)} .iyuu-topbar-inner{display:flex;align-items:center;gap:12px;padding:10px 14px;flex-wrap:wrap;font:12px/1.45 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial} .iyuu-title{font-weight:700;margin-right:8px;white-space:nowrap} .iyuu-hash{opacity:.75} .iyuu-chips{display:flex;gap:10px;flex-wrap:wrap;align-items:stretch} /* —— 气泡卡 —— */ .iyuu-chip{ display:inline-flex;flex-direction:column;align-items:center;justify-content:center; gap:4px; /* 原 6px → 4px,减少元素间留白 */ padding:6px 8px; /* 原 8x10 → 6x8,收紧内边距 */ border-radius:12px;background:#0f172a;border:1px solid #243045; text-decoration:none;color:#dbeafe; min-width:92px;max-width:140px;min-height:66px; /* 保持原有框架大小不变 */ box-sizing:border-box;text-align:center } .iyuu-chip.ok{border-color:#22c55e;color:#dcfce7} .iyuu-chip:hover{filter:brightness(1.05)} /* 图标放大(原 22 → 28) */ .iyuu-icon{width:28px;height:28px;display:block;object-fit:contain} /* 名称稍大(原 12px 左右 → 13.5px) */ .iyuu-label{display:block;line-height:1.22;font-size:13.5px} /* 计数保持小一点 */ .iyuu-count{opacity:.85;font-size:10px} /* 无图标场景:字号再大一点,更饱满 */ .iyuu-chip.noicon .iyuu-label{font-size:14.5px} .iyuu-badge{font-size:10px;padding:2px 6px;border-radius:999px;background:#f59e0b;color:#221400} .iyuu-badge.ok{background:#22c55e;color:#05290f} .iyuu-badge.no{background:#ef4444;color:#360202} .iyuu-badge.err{background:#f97316;color:#2c1302} .iyuu-divider{height:18px;width:1px;background:#ffffff1a;margin:0 6px} .iyuu-empty{opacity:.8;align-self:center} .iyuu-right{display:flex;align-items:center;gap:8px;margin-left:auto} .iyuu-input{display:flex;align-items:center;gap:6px;background:#0f172a;border:1px solid #243045;border-radius:8px;padding:4px 6px} .iyuu-input input{width:200px;background:transparent;border:none;outline:none;color:#cde3ff} .iyuu-btn{padding:4px 8px;border-radius:6px;border:none;cursor:pointer;background:#1e293b;color:#fff;font-size:12px} .iyuu-btn:hover{filter:brightness(1.05)} .iyuu-token-mask{opacity:.85} .iyuu-eye{cursor:pointer;user-select:none;opacity:.9} `); const bar = document.createElement('div'); bar.className = 'iyuu-topbar'; bar.innerHTML = ` <div class="iyuu-topbar-inner"> <span class="iyuu-title">IYUU 全站检测</span> <span class="iyuu-hash" id="iyuu-hash">hash: ——</span> <span class="iyuu-divider"></span> <span class="iyuu-badge" id="iyuu-badge">待检测</span> <div class="iyuu-chips" id="iyuu-chips"></div> <div class="iyuu-right"> <span>Token:<span class="iyuu-token-mask" id="iyuu-token-mask"></span></span> <div class="iyuu-input"> <input id="iyuu-token-input" type="password" placeholder="在此粘贴 IYUU Token"/> <span class="iyuu-eye" id="iyuu-eye" title="显示/隐藏">👁️</span> </div> <button class="iyuu-btn" id="iyuu-save">保存Token</button> </div> </div> `; safePrepend(findTopContainer(), bar); const chipsEl = bar.querySelector('#iyuu-chips'); const badgeEl = bar.querySelector('#iyuu-badge'); const tokenMaskEl = bar.querySelector('#iyuu-token-mask'); const tokenInput = bar.querySelector('#iyuu-token-input'); const eyeBtn = bar.querySelector('#iyuu-eye'); const saveBtn = bar.querySelector('#iyuu-save'); const hashEl = bar.querySelector('#iyuu-hash'); const setBadge = (cls, text) => { badgeEl.className = `iyuu-badge ${cls||''}`.trim(); badgeEl.textContent = text; }; /** * 新版 addChip:当无 iconURL 时会加上 .noicon 类,从而让名称字号更大。 * 保持卡片 min-width / min-height 不变,仅通过 gap/padding/字号/图标尺寸调整观感。 */ const addChip = ({ label, href, ok=true, count=1, iconURL=null }) => { const a = document.createElement(href ? 'a' : 'div'); a.className = `iyuu-chip ${ok ? 'ok' : ''} ${iconURL ? '' : 'noicon'}`.trim(); if (href) { a.href = href; a.target = '_blank'; a.rel = 'noopener noreferrer'; } // 图标(可选) if (iconURL) { const img = document.createElement('img'); img.className = 'iyuu-icon'; img.src = iconURL; img.alt = ''; a.appendChild(img); } // 名称 const nameEl = document.createElement('span'); nameEl.className = 'iyuu-label'; nameEl.textContent = label; a.appendChild(nameEl); // 数量(仅在 ok 且 count>1 时显示) if (ok && count > 1) { const cnt = document.createElement('span'); cnt.className = 'iyuu-count'; cnt.textContent = `(${count})`; a.appendChild(cnt); } chipsEl.appendChild(a); }; const showEmpty = (msg='未发现可辅种站点') => { const span = document.createElement('span'); span.className='iyuu-empty'; span.textContent=msg; chipsEl.appendChild(span); }; /* ========== 2) Token 存取 ========== */ const TOKEN_KEY = 'iyuu_crossseed_token_v1'; const SID_SHA1_CACHE_KEY = 'iyuu_sid_sha1_cache_v1'; function getStoredToken(){ try { return GM_getValue(TOKEN_KEY, '') || ''; } catch{} return ''; } function setStoredToken(v){ try { GM_setValue(TOKEN_KEY, v || ''); } catch{} } function getToken(){ const t = getStoredToken(); if (t) return t; if (IYUU_TOKEN_DEFAULT) return IYUU_TOKEN_DEFAULT; return ''; } function clearSidSha1Cache(){ try { GM_deleteValue && GM_deleteValue(SID_SHA1_CACHE_KEY); } catch{} try { localStorage.removeItem(SID_SHA1_CACHE_KEY); } catch{} } function maskToken(t){ if(!t) return '(未设置)'; if(t.length<=8) return t; return `${t.slice(0,4)}…${t.slice(-4)}`; } function updateTokenMask(){ tokenMaskEl.textContent = maskToken(getToken()); } updateTokenMask(); eyeBtn.addEventListener('click', () => { tokenInput.type = tokenInput.type === 'password' ? 'text' : 'password'; }); saveBtn.addEventListener('click', () => { const v = (tokenInput.value || '').trim(); if (!v) { tokenInput.focus(); return; } setStoredToken(v); clearSidSha1Cache(); updateTokenMask(); tokenInput.value = ''; runDetection(); }); /* ========== 3) Base32 → Hex(BTIH) ========== */ const B32MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; function base32ToHex(b32){ b32 = (b32 || '').replace(/=+$/,'').toUpperCase(); let bits = '', hex=''; for (const ch of b32){ const v = B32MAP.indexOf(ch); if(v<0) return ''; bits += v.toString(2).padStart(5,'0'); } for (let i=0;i+8<=bits.length;i+=8) hex += parseInt(bits.slice(i,i+8),2).toString(16).padStart(2,'0'); return hex; } /* ========== 4) 提取 infohash(页面/脚本/磁链/属性) ========== */ function extractInfoHashEnhanced() { try { for (const code of Array.from(document.scripts).map(s => s.textContent || '')) { const m = code.match(/['"]([a-fA-F0-9]{40})['"]/); if (m) return m[1].toLowerCase(); } const m2 = (document.body.innerText || '').match(/\b([a-fA-F0-9]{40})\b/); if (m2) return m2[1].toLowerCase(); const usp = new URL(location.href).searchParams; const urlHash = usp.get('infohash') || usp.get('hash'); if (urlHash && /^[a-fA-F0-9]{40}$/.test(urlHash)) return urlHash.toLowerCase(); for (const a of Array.from(document.querySelectorAll('a[href^="magnet:"]'))) { const u = new URL(a.getAttribute('href')); const xt = (u.searchParams.get('xt') || '').split(':').pop(); if (!xt) continue; if (/^[a-fA-F0-9]{40}$/.test(xt)) return xt.toLowerCase(); if (/^[A-Z2-7]{32}$/i.test(xt)) { const hex = base32ToHex(xt); if (hex && hex.length >= 40) return hex.slice(0,40).toLowerCase(); } } const attrHex = document.querySelector('[data-infohash], [data-hash], [title*="infohash"], [title*="Info Hash"]'); if (attrHex){ const cands = [attrHex.getAttribute('data-infohash'), attrHex.getAttribute('data-hash'), attrHex.getAttribute('title')].filter(Boolean).join(' '); const m = cands.match(/\b([a-fA-F0-9]{40})\b/); if (m) return m[1].toLowerCase(); } } catch {} return ''; } /* ========== 5) 从页面定位 .torrent 下载地址(更全面) ========== */ function findTorrentDownloadURL() { const passkeyA = Array.from(document.querySelectorAll('a[href*="download.php?id="]')) .find(a => /passkey=/.test(a.getAttribute('href') || '')); if (passkeyA) return new URL(passkeyA.getAttribute('href'), location.href).href; let a = document.querySelector('a[href*="download.php?id="], a[href*="/download.php?id="]'); if (a) return new URL(a.getAttribute('href'), location.href).href; const byText = Array.from(document.querySelectorAll('a')).find(x => /下载种子|下载地址|\.torrent/i.test(x.textContent||'')); if (byText) return new URL(byText.getAttribute('href'), location.href).href; const onclickA = Array.from(document.querySelectorAll('a[onclick]')).find(x => /download\.php\?id=\d+/.test(x.getAttribute('onclick')||'')); if (onclickA) { const m = (onclickA.getAttribute('onclick')||'').match(/download\.php\?id=\d+/i); if (m) return new URL(m[0], location.href).href; } return ''; } /* ========== 6) 兜底:下载 .torrent 并计算 infohash ========== */ async function fetchInfohashFromTorrent() { const href = findTorrentDownloadURL(); if (!href) return ''; return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: href, responseType: 'arraybuffer', timeout: 30000, anonymous: false, headers: { Referer: location.href }, onload: async (r) => { try { const headers = (r.responseHeaders || '').toLowerCase(); if (headers.includes('content-type: text/html') && !headers.includes('application/x-bittorrent')) return resolve(''); const buf = r.response; if (!buf) return resolve(''); const ih = await computeInfohashFromTorrentBytes(buf); resolve(ih || ''); } catch { resolve(''); } }, onerror: () => resolve(''), ontimeout: () => resolve('') }); }); } /* ========== 7) 可靠 bencode 解析:取 info 原始字节做 SHA-1(BT v1) ========== */ async function computeInfohashFromTorrentBytes(buf) { const b = new Uint8Array(buf); function readLen(pos) { let i = pos, len = 0; if (i >= b.length || b[i] < 0x30 || b[i] > 0x39) throw new Error('len: expect digit'); while (i < b.length && b[i] >= 0x30 && b[i] <= 0x39) { len = len * 10 + (b[i] - 0x30); i++; } if (b[i] !== 0x3A) throw new Error('len: missing colon'); return { len, next: i + 1 }; } function readValueEnd(pos) { const c = b[pos]; if (c === 0x69) { let i = pos + 1; if (b[i] === 0x2D) i++; if (i >= b.length || b[i] < 0x30 || b[i] > 0x39) throw new Error('int: expect digit'); while (i < b.length && b[i] >= 0x30 && b[i] <= 0x39) i++; if (b[i] !== 0x65) throw new Error('int: missing e'); return i + 1; } if (c === 0x6C) { let i = pos + 1; while (b[i] !== 0x65) { i = readValueEnd(i); } return i + 1; } if (c === 0x64) { let i = pos + 1; while (b[i] !== 0x65) { const { len, next } = readLen(i); const keyStart = next, keyEnd = next + len; const key = new TextDecoder().decode(b.slice(keyStart, keyEnd)); i = keyEnd; if (key === 'info') { const valStart = i; const valEnd = readValueEnd(i); const endPos = (typeof valEnd === 'number') ? valEnd : valEnd.end; const infoSlice = b.slice(valStart, endPos); return crypto.subtle.digest('SHA-1', infoSlice).then(d => { const hex = Array.from(new Uint8Array(d)).map(x => x.toString(16).padStart(2,'0')).join(''); return { end: endPos, infohash: hex }; }); } else { i = readValueEnd(i); } } return i + 1; } if (c >= 0x30 && c <= 0x39) { const { len, next } = readLen(pos); return next + len; } throw new Error('value: bad prefix ' + c); } if (b[0] !== 0x64) throw new Error('torrent root not dict'); let i = 1; while (b[i] !== 0x65) { const { len, next } = readLen(i); const keyStart = next, keyEnd = next + len; const key = new TextDecoder().decode(b.slice(keyStart, keyEnd)); i = keyEnd; if (key === 'info') { const valStart = i; const out = await readValueEnd(i); if (typeof out === 'object' && out.infohash) return out.infohash; const infoSlice = b.slice(valStart, out); const d = await crypto.subtle.digest('SHA-1', infoSlice); return Array.from(new Uint8Array(d)).map(x => x.toString(16).padStart(2,'0')).join(''); } else { i = await readValueEnd(i); } } return ''; } /* ========== 8) IYUU API 封装 ========== */ const API_BASE = 'https://2025.iyuu.cn'; const httpGet = (url, headers={}) => new Promise((resolve, reject) => { GM_xmlhttpRequest({ method:'GET', url, headers: Object.assign({'Token': getToken()}, headers), timeout: 20000, onload:r=> (r.status>=200 && r.status<300) ? resolve(r.responseText) : reject(new Error(`HTTP ${r.status}`)), onerror:reject, ontimeout:()=>reject(new Error('timeout')) }); }); const httpPost = (url, data, headers={}) => new Promise((resolve, reject) => { GM_xmlhttpRequest({ method:'POST', url, data, headers: Object.assign({'Token': getToken()}, headers), timeout: 20000, onload:r=> (r.status>=200 && r.status<300) ? resolve(r.responseText) : reject(new Error(`HTTP ${r.status}`)), onerror:reject, ontimeout:()=>reject(new Error('timeout')) }); }); async function sha1Hex(str){ const enc=new TextEncoder().encode(str); const buf=await crypto.subtle.digest('SHA-1', enc); return Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''); } function loadSidSha1(){ try { const o=JSON.parse(localStorage.getItem('iyuu_sid_sha1_cache_v1')||'{}'); if(o.sid_sha1 && o.expire>Date.now()) return o.sid_sha1; } catch{} return null; } function saveSidSha1(v){ try { const seven=7*24*3600*1000; const o={sid_sha1:v, expire:Date.now()+seven}; localStorage.setItem('iyuu_sid_sha1_cache_v1', JSON.stringify(o)); } catch{} } /* ========== 9) 主流程 ========== */ async function runDetection(){ chipsEl.innerHTML = ''; let infohash = extractInfoHashEnhanced(); if (!infohash) { try { infohash = await fetchInfohashFromTorrent(); } catch {} } if (!infohash) { setBadge('err','缺少 hash'); hashEl.textContent='hash: 未识别'; showEmpty('当前页面未能识别到 infohash。已尝试 .torrent 解析仍失败(可能为 v2-only 或下载被替换为 HTML)。'); return; } else { hashEl.textContent = `hash: ${infohash.slice(0,8)}…`; } const token = getToken(); if (!token) { setBadge('err','未设置 Token'); showEmpty('请在右侧输入框粘贴 Token 并点击“保存Token”。'); return; } try { setBadge('', '检测中'); const sitesResp = JSON.parse(await httpGet(`${API_BASE}/reseed/sites/index`)); if (sitesResp.code !== 0) throw new Error(sitesResp.msg || 'sites/index 失败'); const sites = sitesResp.data?.sites || []; const allSid = sites.map(s => s.id); let sid_sha1 = loadSidSha1(); if (!sid_sha1) { const reportResp = JSON.parse(await httpPost(`${API_BASE}/reseed/sites/reportExisting`, JSON.stringify({ sid_list: allSid }), { 'Content-Type':'application/json' })); if (reportResp.code !== 0) throw new Error(reportResp.msg || 'reportExisting 失败'); sid_sha1 = reportResp.data?.sid_sha1; if (!sid_sha1) throw new Error('缺少 sid_sha1'); saveSidSha1(sid_sha1); } const hashes = [infohash].sort(); const jsonStr = JSON.stringify(hashes); const sha1 = await sha1Hex(jsonStr); const timestamp = Math.floor(Date.now()/1000).toString(); const version = '8.2.0'; const form = new URLSearchParams(); form.set('hash', jsonStr); form.set('sha1', sha1); form.set('sid_sha1', sid_sha1); form.set('timestamp', timestamp); form.set('version', version); const reseedResp = JSON.parse(await httpPost(`${API_BASE}/reseed/index/index`, form.toString(), { 'Content-Type': 'application/x-www-form-urlencoded' })); if (reseedResp.code !== 0) throw new Error(reseedResp.msg || 'reseed/index 失败'); const data = reseedResp.data || {}; const firstKey = Object.keys(data)[0]; const items = (firstKey && data[firstKey]?.torrent) ? data[firstKey].torrent : []; if (!items.length) { setBadge('no','未发现'); showEmpty(); return; } setBadge('ok','已获取'); const bySid = new Map(); for (const t of items) { const sid = t.sid; if (!bySid.has(sid)) bySid.set(sid, []); bySid.get(sid).push(t); } for (const [sid, arr] of bySid.entries()) { const s = sites.find(x => x.id === sid); if (!s) continue; const id = arr[0].torrent_id; const scheme = (s.is_https === 0) ? 'http' : 'https'; const details = (s.details_page || 'details.php?id={}').replace('{}', id); const href = `${scheme}://${s.base_url}/${details}`; const iconURL = lookupIconURL({ sid, nickname: s.nickname, site: s.site }); const label = s.nickname || s.site || String(sid); addChip({ label, href, ok: true, count: arr.length, iconURL }); } } catch (e) { setBadge('err','失败'); showEmpty(String(e && e.message || e)); try { console.error('[IYUU-crossseed]', e); } catch {} } } /* ========== 10) 首次进入页面 ========== */ if (getToken()) runDetection(); else { setBadge('err','未设置 Token'); showEmpty('请在右侧输入框粘贴 Token 并点击“保存Token”。'); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址