// ==UserScript==
// @name 游戏盈亏监控
// @namespace https://greasyfork.org/users/your-id
// @version 2.5.90
// @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, // 请求和轮询间隔(ms)
profitThreshold: null, // 盈利阈值
lossThreshold: null, // 亏损阈值
monitoring: false, // 监控状态
currentIndex: 0, // 当前行索引
currentPage: 1, // 当前页码
totalPages: 1, // 总页数
totalItems: 0, // 总记录数
itemsPerPage: 0, // 每页记录数
batchSize: 5, // 每批处理条数
maxParallel: 3, // 最大并发检查数
activeRequests: 0, // 当前并发数
processedItems: 0, // 已处理记录数
monitoringDuration: 40, // 监控时长(分钟)
startTime: 0, // 监控开始时间戳
panelCollapsed: false, // 面板折叠状态
profitAlerts: 0, // 盈利报警累计数
lossAlerts: 0, // 亏损报警累计数
dialogIdCounter: 0, // 对话框ID计数器
activeDialogs: {} // 当前打开的对话框
};
/**
* 本地存储封装
*/
const storage = {
get(key, defaultValue) {
try {
if (typeof GM_getValue === 'function') {
return GM_getValue(key, defaultValue);
}
const raw = localStorage.getItem(`monitor_${key}`);
return raw != null ? JSON.parse(raw) : defaultValue;
} catch (e) {
console.error('Storage.get 错误:', e);
return defaultValue;
}
},
set(key, value) {
try {
if (typeof GM_setValue === 'function') {
GM_setValue(key, value);
} else {
localStorage.setItem(`monitor_${key}`, JSON.stringify(value));
}
} catch (e) {
console.error('Storage.set 错误:', e);
}
}
};
/**
* 注入自定义样式
*/
function addStyles() {
const css = `
.monitor-panel { position:fixed;top:20px;right:20px;z-index:9999;background:#fff;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 .3s; }
.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; }
.toggle-panel:hover { background:#e0e0e0; }
.collapsed .panel-content { display:none; }
.monitor-header { margin:0 0 15px;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:#fff;border:none;border-radius:4px;font-weight:bold;cursor:pointer;transition:background .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 .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.textContent = css;
document.head.appendChild(style);
}
/**
* 创建控制面板
*/
function createControlPanel() {
addStyles();
const panel = document.createElement('div');
panel.id = 'monitorPanel';
panel.className = 'monitor-panel';
panel.innerHTML = `
`;
document.body.appendChild(panel);
// 恢复折叠状态
config.panelCollapsed = storage.get('panelCollapsed', false);
if (config.panelCollapsed) panel.classList.add('collapsed');
// 恢复历史配置
const savedP = storage.get('profitThreshold', '');
const savedL = storage.get('lossThreshold', '');
if (savedP !== null) document.getElementById('profitThresholdInput').value = savedP;
if (savedL !== null) document.getElementById('lossThresholdInput').value = savedL;
document.getElementById('profitAlerts').textContent = storage.get('profitAlerts', 0);
document.getElementById('lossAlerts').textContent = storage.get('lossAlerts', 0);
// 事件绑定
document.getElementById('togglePanelBtn').addEventListener('click', togglePanel);
document.getElementById('toggleMonitor').addEventListener('click', toggleMonitoring);
}
/** 折叠/展开面板 */
function togglePanel() {
const panel = document.getElementById('monitorPanel');
config.panelCollapsed = !config.panelCollapsed;
panel.classList.toggle('collapsed', config.panelCollapsed);
storage.set('panelCollapsed', config.panelCollapsed);
}
/** 开始/停止监控 切换 */
function toggleMonitoring() {
const p = parseFloat(document.getElementById('profitThresholdInput').value);
const l = parseFloat(document.getElementById('lossThresholdInput').value);
const m = parseInt(document.getElementById('minutesInput').value) || 40;
const par = Math.min(Math.max(parseInt(document.getElementById('parallelInput').value) || 3, 1), 10);
if (isNaN(p) && isNaN(l)) { alert('请至少设置一个阈值'); return; }
config.profitThreshold = isNaN(p) ? null : p;
config.lossThreshold = isNaN(l) ? null : l;
config.monitoringDuration = m;
config.maxParallel = par;
storage.set('profitThreshold', config.profitThreshold);
storage.set('lossThreshold', config.lossThreshold);
config.monitoring = !config.monitoring;
const btn = document.getElementById('toggleMonitor');
const stat = document.getElementById('statusText');
if (config.monitoring) {
btn.textContent = '停止监控'; btn.classList.add('stop'); stat.textContent = '监控中';
config.startTime = Date.now(); config.processedItems = 0;
startMonitoring();
} else {
btn.textContent = '开始监控'; btn.classList.remove('stop'); stat.textContent = '已停止';
}
}
/** 安全点击,尝试多种Event */
function safeClick(el) {
if (!el) return;
['mousedown','mouseup','click'].forEach(e => el.dispatchEvent(new MouseEvent(e, { bubbles: true, cancelable: true })));
}
/** 初始监控流程 */
function startMonitoring() {
if (!config.monitoring) return;
goToFirstPage(() => initMonitoring());
}
/** 跳转到第一页 */
function goToFirstPage(cb) {
const first = document.querySelector('.el-pager .number:first-child');
if (first && !first.classList.contains('active')) {
safeClick(first); setTimeout(cb, 500);
} else cb();
}
/** 初始化筛选与分页 */
function initMonitoring() {
config.currentPage = 1; config.currentIndex = 0;
const steps = [
next => selectDropdownOption('.el-select.filter-item','已支付', () => next()),
next => setTimeRange(() => next()),
next => setPageSize(200, () => next()),
() => { updatePaginationInfo(); setTimeout(checkUsers, 500); }
];
(function run() { if (steps.length) steps.shift()(run); })();
}
/** 选择下拉选项 */
function selectDropdownOption(sel,text,cb) {
const dd = document.querySelector(sel);
if (!dd) return cb(false);
const input = dd.querySelector('.el-input__inner'); if (input.value===text) return cb(true);
safeClick(dd);
setTimeout(() => {
const items = [...document.querySelectorAll('.el-select-dropdown__item')].filter(i=>i.offsetParent);
const opt = items.find(i=>i.textContent.trim()===text);
if (opt) { safeClick(opt); setTimeout(() => cb(true), 300); } else cb(false);
}, 300);
}
/** 设置时间范围 */
function setTimeRange(cb) {
const now = new Date(); const start = new Date(now - config.monitoringDuration*60000);
const inputs = document.querySelectorAll('.el-range-input');
if (inputs.length>=2) {
inputs[0].value = formatDate(start); inputs[1].value = formatDate(now);
inputs.forEach(i=>['input','change'].forEach(e=>i.dispatchEvent(new Event(e,{ bubbles:true }))))
}
setTimeout(cb,300);
}
function formatDate(d) { const z=n=>String(d['get'+d? '' :'']).padStart(2,'0'); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`; }
/** 设置每页数量 */
function setPageSize(size, cb) { selectDropdownOption('.el-pagination__sizes .el-select', `${size}条/页`, success=>{ if (success) { config.itemsPerPage=size; setTimeout(()=>{ updatePaginationInfo(); cb(); },500);} else cb(); }); }
/** 更新分页信息 */
function updatePaginationInfo() {
const pag = document.querySelector('.el-pagination__total')?.textContent || '';
const total = parseInt((pag.match(/(\d+)/)||[0,0])[1]) || 0;
config.totalItems = total; config.totalPages = config.itemsPerPage? Math.ceil(total/config.itemsPerPage):1;
document.getElementById('totalItems').textContent = config.totalItems;
document.getElementById('totalPages').textContent = config.totalPages;
}
/** 查找并检查用户列表 */
function checkUsers() {
if (!config.monitoring) return;
const rows=document.querySelectorAll('.el-table__row:not(.el-table__row--level)');
if (!rows.length) return setTimeout(startMonitoring,config.checkInterval);
config.currentIndex=0; processBatch(rows);
}
/** 批量并发处理 */
function processBatch(rows) {
if (config.activeRequests{ config.activeRequests--; config.processedItems++; updateProgress(); processBatch(rows); });
} else { config.processedItems++; updateProgress(); processBatch(rows); }
} else if (config.currentIndexprocessBatch(rows),100);
else nextPageOrRestart();
}
/** 更新进度 */
function updateProgress() {
const pos=document.getElementById('currentPosition'); pos.textContent=config.processedItems;
const speed = config.processedItems/Math.max((Date.now()-config.startTime)/60000,1);
document.getElementById('speedText').textContent = `${Math.round(speed)} 条/分钟`;
}
/** 跳转下一页或重启 */
function nextPageOrRestart() {
if (config.currentPage{ updatePaginationInfo(); checkUsers(); },config.checkInterval);
} else {
config.processedItems=0; setTimeout(startMonitoring,config.checkInterval);
}
}
/** 打开用户对话框并检查余额 */
function processUser(elem, done) {
const username = elem.textContent.trim();
const id = `dlg${++config.dialogIdCounter}`;
// 关闭其他对话框
document.querySelectorAll('.el-dialog__wrapper:not([style*="none"]) .el-dialog__headerbtn').forEach(btn=>safeClick(btn));
// 打开目标对话框
elem.href = elem.href.split('?')[0] + `?t=${Date.now()}`;
safeClick(elem);
// 等待对话框
const start=Date.now(); const mo=new MutationObserver(()=>{
const dlg=document.querySelector('.el-dialog__wrapper:not([style*="none"])');
if (dlg && dlg.textContent.match(/余额|Balance/)) {
mo.disconnect(); parseDialog(dlg, username);
}
if (Date.now()-start>3000) { mo.disconnect(); done(); }
}); mo.observe(document.body,{ childList:true, subtree:true });
function parseDialog(dlg,user) {
const ths=[...dlg.querySelectorAll('th')]; const idx=ths.findIndex(th=>/余额|Balance/.test(th.textContent));
const tr=[...dlg.querySelectorAll('tr')].find(r=>r.textContent.includes(user.split('_')[1]));
if (tr && idx>-1) {
const cell=tr.querySelector(`td:nth-child(${idx+1})`);
const val=parseFloat(cell.textContent.replace(/[^\d.-]/g,''));
if (!isNaN(val)) {
if (config.profitThreshold!=null && val>=config.profitThreshold) alertUser(user,val,'profit');
if (config.lossThreshold!=null && val<=config.lossThreshold) alertUser(user,val,'loss');
}
}
// 关闭对话框后继续
safeClick(dlg.querySelector('.el-dialog__headerbtn'));
done();
}
}
/** 弹出报警 */
function alertUser(user,val,type) {
const msg = `用户 ${user} 余额 ${type==='profit'?'超标':'不足'}: ${val}`;
console.warn(msg);
if (typeof GM_notification==='function') GM_notification({ title:type==='profit'?'盈利报警':'亏损报警', text:msg, timeout:5000 });
else alert(msg);
const el = document.getElementById(type==='profit'?'profitAlerts':'lossAlerts');
const count = parseInt(el.textContent)||0; el.textContent=count+1;
storage.set(type==='profit'?'profitAlerts':'lossAlerts',count+1);
}
/** 初始化入口 */
function init() {
const timer=setInterval(()=>{
if (document.querySelector('.el-table')) { clearInterval(timer); createControlPanel(); }
},500);
}
if (document.readyState==='complete') init(); else window.addEventListener('load',init);
})();