您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
无限刷新直到响应200,含错误页面检测和自动恢复功能
// ==UserScript== // @name Q票终极稳定刷新助手-2 // @namespace http://tampermonkey.net/ // @version 2.0 // @description 无限刷新直到响应200,含错误页面检测和自动恢复功能 // @match https://events.q-tickets.com/qatar/eventdetails/6242778262/ittf-world-table-tennis-championships-finals-doha-2025 // @grant GM_addStyle // @grant GM_notification // @grant GM_log // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function() { 'use strict'; // 配置参数 const CONFIG = { checkInterval: 30000, // 正常检测间隔30秒 pendingTimeout: 6000, // 6秒请求超时 retryIntervals: { serverError: 8000, // 服务器错误重试间隔 networkError: 10000, // 网络错误重试间隔 criticalError: 5000 // 严重错误重试间隔 }, maxServerErrors: 5, // 连续服务器错误最大次数 errorPageKeywords: [ // 错误页面关键词(支持多语言) "该网页无法正常运作", "HTTP ERROR 500", "This page isn't working", "events.q-tickets.com 目前无法处理此请求", "服务器错误", "Service Unavailable", "502 Bad Gateway", "503 Service Temporarily Unavailable", "504 Gateway Time-out" ], statusCheckInterval: 3000 // 脚本存活检查间隔(3秒) }; // 运行时变量(使用GM_setValue持久化) let runtime = { consecutiveServerErrors: GM_getValue('consecutiveServerErrors', 0), lastError: GM_getValue('lastError', null), lastSuccess: GM_getValue('lastSuccess', null), totalChecks: GM_getValue('totalChecks', 0) }; // 保存运行时状态 const saveRuntime = () => { GM_setValue('consecutiveServerErrors', runtime.consecutiveServerErrors); GM_setValue('lastError', runtime.lastError); GM_setValue('lastSuccess', runtime.lastSuccess); GM_setValue('totalChecks', runtime.totalChecks); }; // 创建状态指示器(防丢失版本) const createStatusIndicator = () => { // 如果已存在则先移除 const existing = document.getElementById('refresh-status'); if (existing) existing.remove(); // 添加到body的直接子元素(最高层级) const statusDiv = document.createElement('div'); statusDiv.id = 'refresh-status'; statusDiv.innerHTML = ` <div class="header"> <div class="title"> <span class="icon">🔄</span> <span>票务监控系统 v2.0</span> </div> <span class="status-badge" id="status-badge">INIT</span> </div> <div class="details"> <div>状态: <span id="current-status">系统初始化中...</span></div> <div class="stats"> <span>检测次数: <span id="check-count">0</span></span> <span>错误次数: <span id="error-count">0</span></span> </div> <div class="time-info"> <span>上次成功: <span id="last-success">-</span></span> <span>下次检测: <span id="next-check">-</span></span> </div> </div> <div class="progress-bar"> <div class="progress" id="progress-bar"></div> </div> `; document.documentElement.appendChild(statusDiv); // 添加到html根元素 // 应用样式 GM_addStyle(` #refresh-status { position: fixed !important; top: 12px !important; right: 12px !important; background: rgba(30,30,30,0.95) !important; color: white !important; padding: 14px 18px !important; border-radius: 8px !important; z-index: 2147483647 !important; /* 最大z-index */ font-family: 'Segoe UI', Roboto, sans-serif !important; box-shadow: 0 4px 20px rgba(0,0,0,0.3) !important; border: 1px solid rgba(255,255,255,0.1) !important; min-width: 320px !important; backdrop-filter: blur(8px) !important; } #refresh-status .header { display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 10px !important; padding-bottom: 8px !important; border-bottom: 1px solid rgba(255,255,255,0.1) !important; } #refresh-status .title { font-weight: 600 !important; display: flex !important; align-items: center !important; gap: 10px !important; font-size: 1.05em !important; } #refresh-status .icon { font-size: 1.4em !important; width: 24px !important; text-align: center !important; } #refresh-status .status-badge { font-size: 0.8em !important; padding: 3px 8px !important; border-radius: 12px !important; background: rgba(255,255,255,0.15) !important; } #refresh-status .details { font-size: 0.95em !important; line-height: 1.6 !important; } #refresh-status .stats { display: flex !important; justify-content: space-between !important; margin: 5px 0 !important; font-size: 0.85em !important; opacity: 0.9 !important; } #refresh-status .time-info { margin-top: 8px !important; font-size: 0.88em !important; opacity: 0.9 !important; display: flex !important; justify-content: space-between !important; } #refresh-status.active { background: rgba(46, 125, 50, 0.95) !important; } #refresh-status.warning { background: rgba(237, 108, 2, 0.95) !important; } #refresh-status.error { background: rgba(211, 47, 47, 0.95) !important; } #refresh-status.critical { background: rgba(194, 24, 91, 0.95) !important; } #refresh-status.pending { background: rgba(2, 119, 189, 0.95) !important; } #refresh-status .progress-bar { height: 3px !important; background: rgba(255,255,255,0.2) !important; margin-top: 10px !important; border-radius: 3px !important; overflow: hidden !important; } #refresh-status .progress { height: 100% !important; background: rgba(255,255,255,0.7) !important; width: 0% !important; transition: width 0.1s linear !important; } `); return statusDiv; }; // 初始化状态指示器 let statusDiv = createStatusIndicator(); // 脚本存活检测(防止意外停止) const scriptAliveCheck = () => { if (!document.getElementById('refresh-status')) { GM_log('状态面板丢失,重新创建...'); statusDiv = createStatusIndicator(); } }; // 更新状态显示(防崩溃版本) const updateStatus = (state, message, additionalInfo = '') => { try { const now = new Date(); // 更新DOM元素 const elements = { currentStatus: document.getElementById('current-status'), statusBadge: document.getElementById('status-badge'), checkCount: document.getElementById('check-count'), errorCount: document.getElementById('error-count'), lastSuccess: document.getElementById('last-success'), nextCheck: document.getElementById('next-check') }; if (elements.currentStatus) { elements.currentStatus.innerHTML = `${message} ${additionalInfo ? `<small>(${additionalInfo})</small>` : ''}`; } if (elements.statusBadge) elements.statusBadge.textContent = state; if (elements.checkCount) elements.checkCount.textContent = runtime.totalChecks; if (elements.errorCount) elements.errorCount.textContent = runtime.consecutiveServerErrors; if (elements.lastSuccess) { elements.lastSuccess.textContent = runtime.lastSuccess ? new Date(runtime.lastSuccess).toLocaleTimeString() : '-'; } if (elements.nextCheck) { elements.nextCheck.textContent = new Date(Date.now() + CONFIG.checkInterval).toLocaleTimeString(); } // 更新状态样式 statusDiv.className = ''; const icon = statusDiv.querySelector('.icon'); if (icon) icon.textContent = '🔄'; switch(state) { case 'SUCCESS': statusDiv.classList.add('active'); if (icon) icon.textContent = '✅'; runtime.lastSuccess = Date.now(); break; case 'PENDING': statusDiv.classList.add('pending'); if (icon) icon.textContent = '⏳'; break; case '500': case '504': statusDiv.classList.add('warning'); if (icon) icon.textContent = '⚠️'; break; case 'CRITICAL': statusDiv.classList.add('critical'); if (icon) icon.textContent = '🛑'; break; case 'ERROR': statusDiv.classList.add('error'); if (icon) icon.textContent = '❌'; break; } saveRuntime(); } catch (e) { GM_log('状态更新失败:', e); // 如果状态更新失败,重建整个面板 statusDiv = createStatusIndicator(); } }; // 检测是否为错误页面(增强版) const isErrorPage = () => { try { // 检查1: 页面标题包含错误关键词 const title = document.title || ''; if (CONFIG.errorPageKeywords.some(k => title.includes(k))) { return true; } // 检查2: 可见文本包含错误信息 const bodyText = document.body?.innerText || ''; if (CONFIG.errorPageKeywords.some(k => bodyText.includes(k))) { return true; } // 检查3: Chrome默认错误页面结构 const errorDivs = document.querySelectorAll('div[role="main"], div.error-page'); for (const div of errorDivs) { const text = div.innerText || ''; if (CONFIG.errorPageKeywords.some(k => text.includes(k))) { return true; } } return false; } catch (e) { GM_log('错误页面检测异常:', e); return false; } }; // 带超时和进度显示的fetch请求(防崩溃版) const enhancedFetch = async (url, options) => { refreshController = new AbortController(); options.signal = refreshController.signal; // 进度条更新 let progress = 0; const progressInterval = setInterval(() => { progress = Math.min(progress + 100 / (CONFIG.pendingTimeout / 100), 100); const bar = document.getElementById('progress-bar'); if (bar) bar.style.width = `${progress}%`; }, 100); // 设置超时 const timeoutPromise = new Promise((_, reject) => setTimeout(() => { clearInterval(progressInterval); refreshController.abort(); reject(new Error(`请求超时 (${CONFIG.pendingTimeout}ms)`)); }, CONFIG.pendingTimeout) ); // 实际请求 try { const fetchPromise = fetch(url, options) .then(response => { clearInterval(progressInterval); updateProgressBar(100); return response; }) .catch(err => { clearInterval(progressInterval); updateProgressBar(100); throw err; }); return await Promise.race([fetchPromise, timeoutPromise]); } catch (e) { clearInterval(progressInterval); throw e; } }; // 智能刷新策略(增强版) const smartReload = (errorType) => { let delay = CONFIG.checkInterval; let reason = '计划刷新'; switch(errorType) { case '500': case '504': runtime.consecutiveServerErrors++; delay = CONFIG.retryIntervals.serverError; reason = `服务器${errorType}错误`; break; case 'error-page': runtime.consecutiveServerErrors++; delay = CONFIG.retryIntervals.criticalError; reason = '检测到错误页面'; break; case 'timeout': delay = CONFIG.retryIntervals.networkError; reason = '请求超时'; runtime.consecutiveServerErrors = 0; break; case 'network': delay = CONFIG.retryIntervals.networkError; reason = '网络错误'; runtime.consecutiveServerErrors = 0; break; default: runtime.consecutiveServerErrors = 0; } runtime.lastError = { type: errorType, time: Date.now(), message: reason }; saveRuntime(); // 连续错误处理 if (runtime.consecutiveServerErrors >= CONFIG.maxServerErrors) { GM_notification({ title: '⚠️ 服务器问题警报', text: `连续${runtime.consecutiveServerErrors}次服务器错误\n最后错误: ${reason}`, timeout: 8000, highlight: true }); } GM_log(`[${new Date().toLocaleTimeString()}] ${reason}, ${delay/1000}秒后刷新`); // 使用多种刷新方式组合 setTimeout(() => { try { // 方式1: 普通刷新 window.location.reload(); // 方式2: 备用刷新(如果方式1失败) setTimeout(() => { if (isErrorPage() || document.readyState === 'loading') { window.location.href = window.location.href; } }, 3000); } catch (e) { GM_log('刷新失败:', e); window.location.href = window.location.href; } }, delay); }; // 主检测函数(全保护版本) const checkAvailability = async () => { runtime.totalChecks++; saveRuntime(); try { // 1. 先检查当前是否是错误页面 if (isErrorPage()) { updateStatus('CRITICAL', '检测到错误页面', '自动恢复中...'); smartReload('error-page'); return; } // 2. 更新检测状态 updateStatus('PENDING', '检测服务器状态'); // 3. 执行检测请求 const startTime = Date.now(); const response = await enhancedFetch(window.location.href, { method: 'HEAD', cache: 'no-cache', headers: { 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'X-Requested-With': 'XMLHttpRequest' }, referrerPolicy: 'no-referrer' }); const responseTime = Date.now() - startTime; const statusCode = response.status; GM_log(`[${new Date().toLocaleTimeString()}] 响应: ${statusCode} (${responseTime}ms)`); // 4. 验证响应内容 if (statusCode === 200) { // 额外内容检查 try { const pageCheck = await fetch(window.location.href); const html = await pageCheck.text(); const isPageError = CONFIG.errorPageKeywords.some(k => html.includes(k)); if (isPageError) { updateStatus('CRITICAL', '页面内容异常', '200响应但内容错误'); smartReload('error-page'); } else { // 成功检测到正常页面 clearInterval(intervalId); runtime.consecutiveServerErrors = 0; updateStatus('SUCCESS', '服务已可用', '状态码: 200'); GM_notification({ title: '✅ 可以抢票了!', text: '页面已恢复正常响应\n响应时间: ' + responseTime + 'ms', timeout: 0, highlight: true, onclick: () => window.focus() }); } } catch (contentErr) { updateStatus('ERROR', '内容验证失败', contentErr.message); smartReload('network'); } } else if (statusCode === 500 || statusCode === 504) { updateStatus(statusCode.toString(), '服务器错误', `状态码: ${statusCode}`); smartReload(statusCode.toString()); } else { updateStatus('REFRESH', '刷新页面', `状态码: ${statusCode}`); smartReload('other'); } } catch (error) { const errorType = error.message.includes('timeout') ? 'timeout' : 'network'; updateStatus('ERROR', errorType === 'timeout' ? '请求超时' : '网络错误', error.message); smartReload(errorType); } }; // 启动系统 let intervalId; // 初始化检查 const initialize = () => { // 清除可能存在的旧定时器 if (window.autoRefreshInterval) { clearInterval(window.autoRefreshInterval); } // 创建状态面板 statusDiv = createStatusIndicator(); // 启动主检测循环 intervalId = setInterval(checkAvailability, CONFIG.checkInterval); window.autoRefreshInterval = intervalId; // 启动脚本存活检查 setInterval(scriptAliveCheck, CONFIG.statusCheckInterval); // 立即执行第一次检查 if (isErrorPage()) { updateStatus('CRITICAL', '初始检测到错误页面', '立即刷新'); smartReload('error-page'); } else { checkAvailability(); } // 添加卸载清理 window.addEventListener('beforeunload', () => { clearInterval(intervalId); }); }; // 延迟初始化以确保DOM就绪 if (document.readyState === 'complete') { initialize(); } else { window.addEventListener('load', initialize); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址