// ==UserScript== // @name 游戏盈亏监控 // @namespace https://greasyfork.org/users/your-id // @version 2.0.2 // @description 监控游戏平台的用户盈亏数据,当盈利/亏损超过设定阈值时弹窗提醒。适用于后台管理系统。大数据量优化的高效监控脚本,支持并行处理 // @author Cisco // @match https://7777m.topcms.org/* // @match https://*.topcms.org/* // @icon https://7777m.topcms.org/favicon.ico // @license MIT // @grant GM_notification // @grant GM_xmlhttpRequest // @run-at document-end // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 配置参数 const config = { checkInterval: 2000, // 检查间隔(毫秒) profitThreshold: null, // 盈利阈值 lossThreshold: null, // 亏损阈值 monitoring: false, currentIndex: 0, columnIndex: 7, // 盈亏数据列下标(从0开始) currentPage: 1, totalPages: 1, totalItems: 0, itemsPerPage: 10, batchSize: 5, // 并行处理的批次大小 maxParallel: 3, // 最大并行请求数 activeRequests: 0, // 当前活跃请求数 processedItems: 0, // 已处理项目数 monitoringDuration: 40, // 默认监控时长(分钟) lastCheckTime: 0, // 上次检查时间戳 startTime: 0 // 监控开始时间 }; // 添加自定义样式 GM_addStyle(` .monitor-panel { position: fixed; top: 20px; right: 20px; z-index: 9999; background: white; padding: 15px; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-family: Arial, sans-serif; width: 320px; max-height: 90vh; overflow-y: auto; } .monitor-header { margin: 0 0 15px 0; color: #333; font-size: 16px; font-weight: bold; border-bottom: 1px solid #eee; padding-bottom: 10px; } .monitor-input-group { margin-bottom: 12px; } .monitor-label { display: block; margin-bottom: 5px; color: #666; font-size: 13px; } .monitor-input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; } .monitor-button { width: 100%; padding: 10px; background: #409EFF; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; transition: background 0.3s; } .monitor-button.stop { background: #F56C6C; } .monitor-stats { margin-top: 15px; font-size: 12px; color: #666; border-top: 1px solid #eee; padding-top: 10px; } .monitor-stat-row { display: flex; justify-content: space-between; margin-bottom: 5px; } .monitor-progress-container { margin: 10px 0; height: 10px; background: #f0f0f0; border-radius: 5px; overflow: hidden; } .monitor-progress-bar { height: 100%; background: linear-gradient(to right, #67C23A, #409EFF); transition: width 0.3s; } .monitor-speed { font-size: 11px; color: #999; text-align: right; } .monitor-alert-count { display: flex; justify-content: space-between; margin-top: 5px; } .monitor-alert-badge { display: inline-block; padding: 2px 6px; border-radius: 10px; font-size: 11px; font-weight: bold; } .profit-badge { background: #f0f9eb; color: #67C23A; } .loss-badge { background: #fef0f0; color: #F56C6C; } `); // 创建控制面板 function createControlPanel() { const panel = document.createElement('div'); panel.className = 'monitor-panel'; panel.innerHTML = `

游戏盈亏监控

状态: 未启动
进度: 0/0
页数: 1/1
速度: 0 条/分钟
预计剩余时间: 计算中...
盈利超标: 0 亏损超标: 0
`; document.body.appendChild(panel); // 恢复上次的设置 const savedProfit = GM_getValue('profitThreshold', null); const savedLoss = GM_getValue('lossThreshold', null); const savedMinutes = GM_getValue('monitoringDuration', 40); const savedParallel = GM_getValue('parallelCount', 3); if (savedProfit) document.getElementById('profitThresholdInput').value = savedProfit; if (savedLoss) document.getElementById('lossThresholdInput').value = savedLoss; document.getElementById('minutesInput').value = savedMinutes; document.getElementById('parallelInput').value = savedParallel; document.getElementById('toggleMonitor').addEventListener('click', toggleMonitoring); } // 切换监控状态 function toggleMonitoring() { const profitVal = parseFloat(document.getElementById('profitThresholdInput').value); const lossVal = parseFloat(document.getElementById('lossThresholdInput').value); const minutes = parseInt(document.getElementById('minutesInput').value) || 40; const parallel = parseInt(document.getElementById('parallelInput').value) || 3; if (isNaN(profitVal) && isNaN(lossVal)) { alert('请至少设置一个阈值'); return; } // 保存设置 GM_setValue('profitThreshold', isNaN(profitVal) ? null : profitVal); GM_setValue('lossThreshold', isNaN(lossVal) ? null : Math.abs(lossVal)); GM_setValue('monitoringDuration', minutes); GM_setValue('parallelCount', parallel); config.profitThreshold = isNaN(profitVal) ? null : profitVal; config.lossThreshold = isNaN(lossVal) ? null : Math.abs(lossVal); config.monitoringDuration = minutes; config.maxParallel = Math.min(Math.max(parallel, 1), 10); config.monitoring = !config.monitoring; const btn = document.getElementById('toggleMonitor'); const status = document.getElementById('statusText'); if (config.monitoring) { btn.textContent = '停止监控'; btn.classList.add('stop'); let statusMsg = '监控中 ('; if (config.profitThreshold) statusMsg += `盈>${config.profitThreshold}`; if (config.lossThreshold) statusMsg += `${config.profitThreshold ? ' ' : ''}亏>${config.lossThreshold}`; status.textContent = statusMsg + ')'; // 重置统计 document.getElementById('profitAlerts').textContent = '0'; document.getElementById('lossAlerts').textContent = '0'; config.startTime = Date.now(); config.processedItems = 0; config.lastCheckTime = Date.now(); startMonitoring(); } else { btn.textContent = '开始监控'; btn.classList.remove('stop'); status.textContent = '已停止'; } } // 主监控流程 function startMonitoring() { if (!config.monitoring) return; // 重置到第一页 const firstPageBtn = document.querySelector('.el-pager .number:first-child'); if (firstPageBtn && !firstPageBtn.classList.contains('active')) { simulateClick(firstPageBtn); setTimeout(() => initMonitoring(), 2000); } else { initMonitoring(); } } function initMonitoring() { updatePaginationInfo(); config.currentPage = 1; config.currentIndex = 0; setTimeRange(); setTimeout(() => clickQueryButton(), 1000); } // 更新分页信息 function updatePaginationInfo() { const pagination = document.querySelector('.el-pagination'); if (!pagination) { config.totalPages = 1; const rows = document.querySelectorAll('.el-table__row:not(.el-table__row--level)'); config.totalItems = rows.length; config.itemsPerPage = rows.length || 10; } else { // 获取总条数 const totalText = pagination.querySelector('.el-pagination__total')?.textContent || ''; config.totalItems = parseInt(totalText.match(/\d+/)?.[0] || 0); // 获取每页条数 const sizeSelect = pagination.querySelector('.el-select .el-input__inner'); config.itemsPerPage = sizeSelect ? parseInt(sizeSelect.value) : 10; // 计算总页数 config.totalPages = Math.ceil(config.totalItems / config.itemsPerPage); } // 更新UI显示 document.getElementById('totalItems').textContent = config.totalItems; document.getElementById('totalPages').textContent = config.totalPages; updateProgressDisplay(); } // 更新进度显示 function updateProgressDisplay() { const currentPos = (config.currentPage - 1) * config.itemsPerPage + config.currentIndex + 1; document.getElementById('currentPosition').textContent = currentPos; document.getElementById('displayPage').textContent = config.currentPage; // 更新进度条 const progressPercent = (currentPos / config.totalItems * 100).toFixed(1); document.getElementById('progressBar').style.width = `${progressPercent}%`; // 计算速度 const now = Date.now(); const elapsedMinutes = (now - config.startTime) / 60000; const speed = elapsedMinutes > 0 ? Math.round(config.processedItems / elapsedMinutes) : 0; document.getElementById('speedText').textContent = `${speed} 条/分钟`; // 计算剩余时间 if (speed > 0) { const remainingItems = config.totalItems - currentPos; const remainingMinutes = Math.ceil(remainingItems / speed); document.getElementById('timeRemaining').textContent = `预计剩余时间: ${remainingMinutes} 分钟`; } } // 设置时间范围 function setTimeRange() { const now = new Date(); const startTime = new Date(now.getTime() - config.monitoringDuration * 60000); // 固定结束时间为当天23:59:59 const endTime = new Date(now); endTime.setHours(23, 59, 59, 0); const formatTime = (date) => { const pad = num => num.toString().padStart(2, '0'); return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; }; const timeInputs = document.querySelectorAll('.el-range-input'); if (timeInputs.length >= 2) { timeInputs[0].value = formatTime(startTime); timeInputs[1].value = formatTime(endTime); timeInputs.forEach(input => input.dispatchEvent(new Event('input', { bubbles: true }))); } } // 点击查询按钮 function clickQueryButton() { const queryBtn = [...document.querySelectorAll('.filter-container button.el-button')] .find(btn => !btn.classList.contains('is-disabled') && btn.textContent.includes('查询')); if (queryBtn) { simulateClick(queryBtn); setTimeout(() => checkUsers(), 3000); } else { console.log('未找到查询按钮'); setTimeout(() => { if (config.monitoring) clickQueryButton(); }, 1000); } } // 检查用户列表 function checkUsers() { const userRows = document.querySelectorAll('.el-table__row:not(.el-table__row--level)'); if (userRows.length === 0) { console.log('未找到用户数据'); setTimeout(() => { if (config.monitoring) startMonitoring(); }, config.checkInterval); return; } config.currentIndex = 0; processBatch(); } // 处理批次数据 (并行处理) function processBatch() { if (!config.monitoring || config.activeRequests >= config.maxParallel) return; const userRows = document.querySelectorAll('.el-table__row:not(.el-table__row--level)'); // 当前页检查完成 if (config.currentIndex >= userRows.length) { if (config.currentPage < config.totalPages) { goToNextPage(); } else { console.log('所有数据检查完成'); setTimeout(() => { if (config.monitoring) startMonitoring(); }, config.checkInterval); } return; } // 计算本批次大小 const batchSize = Math.min(config.batchSize, userRows.length - config.currentIndex, config.maxParallel - config.activeRequests); if (batchSize <= 0) return; // 处理批次中的每个用户 for (let i = 0; i < batchSize; i++) { const currentRow = userRows[config.currentIndex + i]; const userNameElement = currentRow.querySelector('.el-tooltip[style*="color: rgb(24, 144, 255)"]'); if (userNameElement) { config.activeRequests++; processUser(currentRow, userNameElement, () => { config.activeRequests--; config.processedItems++; updateProgressDisplay(); // 继续处理下一批 setTimeout(processBatch, 100); }); } else { config.currentIndex++; config.processedItems++; updateProgressDisplay(); } } config.currentIndex += batchSize; // 继续处理下一批 setTimeout(processBatch, 100); } // 处理单个用户 function processUser(row, userNameElement, callback) { const userName = userNameElement.textContent.trim(); scrollAndClick(userNameElement, () => { setTimeout(() => { checkUserProfit(row, userName, callback); }, 1500); }); } // 检查用户盈亏 function checkUserProfit(row, userName, callback) { const profitCell = document.querySelector(`.el-dialog__body .el-table__row td:nth-child(${config.columnIndex + 1}) .cell`); if (!profitCell) { console.log('未找到盈亏数据'); closeDialog(); if (callback) callback(); return; } const text = profitCell.textContent.trim(); const isProfit = text.includes('+'); const value = parseFloat(text.replace(/[^\d.-]/g, '')); if (!isNaN(value)) { if (isProfit && config.profitThreshold && value >= config.profitThreshold) { notify(`用户 ${userName} 盈利超标: +${value}`, 'profit'); incrementAlertCount('profit'); } else if (!isProfit && config.lossThreshold && Math.abs(value) >= config.lossThreshold) { notify(`用户 ${userName} 亏损超标: ${value}`, 'loss'); incrementAlertCount('loss'); } } closeDialog(); if (callback) callback(); } // 增加警报计数 function incrementAlertCount(type) { const elementId = type === 'profit' ? 'profitAlerts' : 'lossAlerts'; const currentCount = parseInt(document.getElementById(elementId).textContent) || 0; document.getElementById(elementId).textContent = currentCount + 1; } // 翻到下一页 function goToNextPage() { const nextBtn = document.querySelector('.el-pagination .btn-next:not([disabled])'); if (nextBtn) { simulateClick(nextBtn); setTimeout(() => { config.currentPage++; config.currentIndex = 0; updatePaginationInfo(); setTimeout(() => checkUsers(), 2000); }, 2500); } } // 关闭对话框 function closeDialog() { const closeBtn = document.querySelector('.el-dialog__headerbtn'); if (closeBtn) simulateClick(closeBtn); } // 通知提醒 function notify(message, type) { alert(message); if (typeof GM_notification !== 'undefined') { GM_notification({ title: type === 'profit' ? '盈利报警' : '亏损报警', text: message, timeout: 5000 }); } console.warn(message); } // 模拟点击(兼容Element UI) function simulateClick(element) { ['mousedown', 'mouseup', 'click'].forEach(event => { element.dispatchEvent(new MouseEvent(event, { bubbles: true })); }); } // 滚动并点击元素 function scrollAndClick(element, callback) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); setTimeout(() => { simulateClick(element); if (callback) callback(); }, 800); } // 初始化 function init() { // 等待表格加载 const checkTable = setInterval(() => { if (document.querySelector('.el-table')) { clearInterval(checkTable); createControlPanel(); console.log('脚本初始化完成'); } }, 500); } // 启动脚本 if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();