// ==UserScript== // @name 游戏盈亏监控 // @namespace https://greasyfork.org/users/your-id // @version 2.2.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 // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @run-at document-end // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 配置参数 const config = { checkInterval: 2000, profitThreshold: null, lossThreshold: null, monitoring: false, currentIndex: 0, columnIndex: 7, currentPage: 1, totalPages: 1, totalItems: 0, itemsPerPage: 10, batchSize: 5, maxParallel: 3, activeRequests: 0, processedItems: 0, monitoringDuration: 40, lastCheckTime: 0, startTime: 0, panelCollapsed: false, profitAlerts: 0, lossAlerts: 0 }; // 兼容性存储对象 const storage = { get: function(key, defaultValue) { try { if (typeof GM_getValue !== 'undefined') { return GM_getValue(key, defaultValue); } const value = localStorage.getItem(`monitor_${key}`); return value !== null ? JSON.parse(value) : defaultValue; } catch (e) { console.error('Storage get error:', e); return defaultValue; } }, set: function(key, value) { try { if (typeof GM_setValue !== 'undefined') { GM_setValue(key, value); } else { localStorage.setItem(`monitor_${key}`, JSON.stringify(value)); } } catch (e) { console.error('Storage set error:', e); } } }; // 添加自定义样式 function addStyles() { const css = ` .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; transition: all 0.3s ease; } .monitor-panel.collapsed { width: 40px; height: 40px; overflow: hidden; padding: 5px; } .toggle-panel { position: absolute; top: 5px; right: 5px; width: 30px; height: 30px; border: none; background: #f0f0f0; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; z-index: 10000; } .toggle-panel:hover { background: #e0e0e0; } .collapsed .panel-content { display: none; } .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; } `; const style = document.createElement('style'); style.type = 'text/css'; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); } // 创建控制面板 function createControlPanel() { addStyles(); const panel = document.createElement('div'); panel.className = 'monitor-panel'; panel.id = 'monitorPanel'; // 添加收起/展开按钮 const toggleBtn = document.createElement('button'); toggleBtn.className = 'toggle-panel'; toggleBtn.innerHTML = '×'; toggleBtn.title = '收起/展开控制面板'; toggleBtn.addEventListener('click', togglePanel); // 面板内容 const panelContent = document.createElement('div'); panelContent.className = 'panel-content'; panelContent.innerHTML = `

游戏盈亏监控

状态: 未启动
进度: 0/0
页数: 1/1
速度: 0 条/分钟
预计剩余时间: 计算中...
盈利超标: 0 亏损超标: 0
`; panel.appendChild(toggleBtn); panel.appendChild(panelContent); document.body.appendChild(panel); // 恢复面板状态和设置 config.panelCollapsed = storage.get('panelCollapsed', false); if (config.panelCollapsed) { panel.classList.add('collapsed'); toggleBtn.innerHTML = '≡'; } const savedProfit = storage.get('profitThreshold', null); const savedLoss = storage.get('lossThreshold', null); const savedMinutes = storage.get('monitoringDuration', 40); const savedParallel = storage.get('parallelCount', 3); config.profitAlerts = storage.get('profitAlerts', 0); config.lossAlerts = storage.get('lossAlerts', 0); 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('profitAlerts').textContent = config.profitAlerts; document.getElementById('lossAlerts').textContent = config.lossAlerts; document.getElementById('toggleMonitor').addEventListener('click', toggleMonitoring); } // 收起/展开面板 function togglePanel() { const panel = document.getElementById('monitorPanel'); config.panelCollapsed = !panel.classList.contains('collapsed'); if (config.panelCollapsed) { panel.classList.add('collapsed'); this.innerHTML = '≡'; } else { panel.classList.remove('collapsed'); this.innerHTML = '×'; } storage.set('panelCollapsed', config.panelCollapsed); } // 切换监控状态 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; } storage.set('profitThreshold', isNaN(profitVal) ? null : profitVal); storage.set('lossThreshold', isNaN(lossVal) ? null : Math.abs(lossVal)); storage.set('monitoringDuration', minutes); storage.set('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 + ')'; 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 setPageSize(size, callback) { const pageSizeSelect = document.querySelector('.el-pagination__sizes .el-select'); if (!pageSizeSelect) { console.log('未找到每页条数选择框'); if (callback) callback(); return; } // 先检查当前是否已经是目标条数 const currentSize = pageSizeSelect.querySelector('.el-input__inner').value; if (currentSize === `${size}条/页`) { if (callback) callback(); return; } // 点击打开下拉框 simulateClick(pageSizeSelect); setTimeout(() => { // 在下拉框中选择指定条数 const dropdown = document.querySelector('.el-pagination__sizes .el-select-dropdown:not([style*="display: none"])'); if (dropdown) { const items = dropdown.querySelectorAll('.el-select-dropdown__item'); let found = false; for (const item of items) { if (item.textContent.trim() === `${size}条/页`) { // 先滚动到该选项 item.scrollIntoViewIfNeeded(); // 添加一点延迟确保滚动完成 setTimeout(() => { simulateClick(item); found = true; // 等待状态更新 setTimeout(() => { if (callback) callback(); }, 800); }, 200); break; } } if (!found) { console.log('未找到条数选项:', `${size}条/页`); if (callback) callback(); } } else { console.log('未找到可见的条数下拉框'); if (callback) callback(); } }, 800); } // 修改updatePaginationInfo函数以正确处理200条/页的情况 function updatePaginationInfo() { const pagination = document.querySelector('.el-pagination'); if (!pagination) { const rows = document.querySelectorAll('.el-table__row:not(.el-table__row--level)'); config.totalItems = rows.length; config.itemsPerPage = rows.length || 200; // 默认改为200 config.totalPages = 1; } else { const totalText = pagination.querySelector('.el-pagination__total')?.textContent || ''; config.totalItems = parseInt(totalText.match(/\d+/)?.[0] || 0); const sizeText = pagination.querySelector('.el-select .el-input__inner')?.value || '200'; config.itemsPerPage = parseInt(sizeText) || 200; // 默认改为200 config.totalPages = Math.ceil(config.totalItems / config.itemsPerPage); } document.getElementById('totalItems').textContent = config.totalItems; document.getElementById('totalPages').textContent = config.totalPages; document.getElementById('currentPosition').textContent = (config.currentPage - 1) * config.itemsPerPage + config.currentIndex + 1; } // 更新进度显示 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 selectOrderStatus(status, callback) { const statusSelect = document.querySelector('.el-select.filter-item'); if (!statusSelect) { console.log('未找到订单状态选择框'); if (callback) callback(); return; } // 先检查当前是否已经是目标状态 const currentStatus = statusSelect.querySelector('.el-input__inner').value; if (currentStatus === status) { if (callback) callback(); return; } // 点击打开下拉框 simulateClick(statusSelect); setTimeout(() => { // 查找可见的下拉框 const dropdown = Array.from(document.querySelectorAll('.el-select-dropdown')) .find(d => !d.style.display || d.style.display !== 'none'); if (dropdown) { const items = dropdown.querySelectorAll('.el-select-dropdown__item'); let found = false; for (const item of items) { if (item.textContent.trim() === status) { // 滚动到该选项 scrollIntoViewIfNeeded(item); // 添加延迟确保滚动完成 setTimeout(() => { simulateClick(item); found = true; // 等待状态更新 setTimeout(() => { if (callback) callback(); }, 800); }, 200); break; } } if (!found) { console.log('未找到状态选项:', status); if (callback) callback(); } } else { console.log('未找到可见的下拉框'); if (callback) callback(); } }, 800); } // 修改setTimeRange函数,确保在选择时间后不立即查询 function setTimeRange(callback) { const now = new Date(); const startTime = new Date(now.getTime() - config.monitoringDuration * 60000); 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 }))); // 添加延迟确保时间设置生效 setTimeout(() => { if (callback) callback(); }, 500); } else if (callback) { callback(); } } // 点击查询按钮 function clickQueryButton() { // 先选择订单状态为"已支付" selectOrderStatus('已支付', () => { // 状态选择完成后再点击查询按钮 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); } }); } // 在initMonitoring函数中添加设置每页条数的逻辑 function initMonitoring() { updatePaginationInfo(); config.currentPage = 1; config.currentIndex = 0; // 设置每页显示200条 setPageSize(200, () => { // 然后设置时间范围 setTimeRange(() => { // 然后选择订单状态 selectOrderStatus('已支付', () => { // 最后执行查询 setTimeout(() => 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; const newCount = currentCount + 1; document.getElementById(elementId).textContent = newCount; if (type === 'profit') { config.profitAlerts = newCount; storage.set('profitAlerts', newCount); } else { config.lossAlerts = newCount; storage.set('lossAlerts', newCount); } } // 翻到下一页 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) { if (!element) { console.log('无法模拟点击,元素不存在'); return; } try { // 创建更兼容的鼠标事件 const createEvent = (type) => { try { // 尝试使用更兼容的方式创建事件 return new MouseEvent(type, { bubbles: true, cancelable: true, view: window, buttons: 1 }); } catch (e) { // 如果失败,使用更简单的初始化方式 console.log('使用备用事件创建方式'); const event = document.createEvent('MouseEvents'); event.initMouseEvent( type, // event type true, // bubbles true, // cancelable window, // view 1, // detail 0, 0, 0, 0, // screen coordinates false, // ctrlKey false, // altKey false, // shiftKey false, // metaKey 0, // button null // relatedTarget ); return event; } }; // 触发完整点击序列 ['mousedown', 'mouseup', 'click'].forEach(eventType => { const event = createEvent(eventType); element.dispatchEvent(event); }); // 对于某些元素可能需要触发focus事件 if (element.tagName === 'INPUT' || element.tagName === 'SELECT') { element.focus(); } } catch (e) { console.error('模拟点击失败:', e); // 作为最后手段,尝试直接调用click方法 if (typeof element.click === 'function') { element.click(); } } } // 改进的滚动到元素函数 function scrollIntoViewIfNeeded(element) { if (!element) return; try { if (typeof element.scrollIntoViewIfNeeded === 'function') { element.scrollIntoViewIfNeeded(); } else { element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' }); } } catch (e) { console.log('滚动失败:', e); } } // 滚动并点击元素 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); } })();