打开/关闭菜单
933
1525
112
1.4万
植物大战僵尸杂交版Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

用户:EditBot/console

来自植物大战僵尸杂交版Wiki
EditBot留言 | 贡献2026年7月20日 (一) 02:06的版本 (创建页面,内容为“<div id="bot-console" style="position:fixed;bottom:20px;right:20px;width:400px;max-height:500px;background:rgba(0,0,0,0.9);border-radius:12px;padding:16px;color:#00ff00;font-family:monospace;font-size:12px;z-index:999999;box-shadow:0 8px 30px rgba(0,0,0,0.8);overflow:hidden;border:2px solid #00ff00;resize:both;min-width:300px;"> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;cursor:move;"> <span style="fo…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
       🤖 EditBot 控制台
           ● 运行中
           <button id="bot-toggle" style="background:#ff4444;border:none;color:#fff;padding:2px 10px;border-radius:4px;cursor:pointer;font-size:11px;">停止</button>
           <button id="bot-close" style="background:#555;border:none;color:#fff;padding:2px 8px;border-radius:4px;cursor:pointer;font-size:11px;">✕</button>
           用户: 加载中...
           检测: 0
           写入: 0
⏳ 等待启动...
           <input id="bot-interval" type="number" value="3" style="width:50px;background:#222;border:1px solid #444;color:#fff;border-radius:4px;padding:2px 4px;font-size:11px;">
           
           <button id="bot-scan" style="background:#0088ff;border:none;color:#fff;padding:2px 12px;border-radius:4px;cursor:pointer;font-size:11px;">立即扫描</button>
           <button id="bot-clear" style="background:#555;border:none;color:#fff;padding:2px 12px;border-radius:4px;cursor:pointer;font-size:11px;">清空日志</button>

<style>

  1. bot-log::-webkit-scrollbar { width: 4px; }
  2. bot-log::-webkit-scrollbar-track { background: #111; }
  3. bot-log::-webkit-scrollbar-thumb { background: #444; border-radius: 2px; }
  4. bot-console .log-success { color: #00ff00; }
  5. bot-console .log-warning { color: #ffaa00; }
  6. bot-console .log-error { color: #ff4444; }
  7. bot-console .log-info { color: #66ccff; }
  8. bot-console .log-editor { color: #ff66aa; }

</style>

<script> (function() {

   'use strict';
   // ============ 配置 ============
   var BOT_NAME = mw.config.get('wgUserName') || 'EditBot';
   var CHECK_INTERVAL = 3000;
   var running = false;
   var timer = null;
   var writeCount = 0;
   var scanCount = 0;
   var detectedEditors = {};
   var logLines = [];
   // ============ DOM 引用 ============
   var logEl = document.getElementById('bot-log');
   var statusEl = document.getElementById('bot-status');
   var toggleBtn = document.getElementById('bot-toggle');
   var countEl = document.getElementById('bot-count');
   var writesEl = document.getElementById('bot-writes');
   var userEl = document.getElementById('bot-user');
   var intervalInput = document.getElementById('bot-interval');
   userEl.textContent = BOT_NAME;
   // ============ 日志系统 ============
   function log(msg, type) {
       type = type || 'info';
       var time = new Date().toLocaleTimeString();
       var colors = {
           success: '#00ff00',
           warning: '#ffaa00',
           error: '#ff4444',
           info: '#66ccff',
           editor: '#ff66aa',
           mute: '#555'
       };
       var color = colors[type] || '#ffffff';

var line = '

[' + time + '] ' + msg + '

';

       logEl.innerHTML += line;
       logEl.scrollTop = logEl.scrollHeight;
       if (logEl.children.length > 500) {
           logEl.removeChild(logEl.firstChild);
       }
   }
   // ============ 核心检测 ============
   function scanAllEditors() {
       if (!running) return;
       var api = new mw.Api();
       scanCount++;
       api.get({
           action: 'query',
           list: 'recentchanges',
           rclimit: 200,
           rcprop: 'user|title|timestamp',
           rcshow: '!bot',
           rcnamespace: 0,
           format: 'json'
       }).then(function(data) {
           var pageMap = {};
           var activeUsers = new Set();
           if (data.query && data.query.recentchanges) {
               data.query.recentchanges.forEach(function(rc) {
                   var title = rc.title;
                   var user = rc.user;
                   var timeDiff = Date.now() - new Date(rc.timestamp).getTime();
                   if (timeDiff < 60000 && user !== BOT_NAME) {
                       if (!pageMap[title]) pageMap[title] = new Set();
                       pageMap[title].add(user);
                       activeUsers.add(user);
                   }
               });
           }
           var totalEditors = 0;
           var writePromises = [];
           for (var page in pageMap) {
               var editors = Array.from(pageMap[page]);
               totalEditors += editors.length;
               editors.forEach(function(editor) {
                   if (editor === BOT_NAME) return;
                   var otherEditors = editors.filter(function(e) {
                       return e !== editor && e !== BOT_NAME;
                   });
                   writePromises.push(writeUserStatus(editor, page, otherEditors));
               });
           }
           // 清理不在线的用户状态
           var cleanupPromise = cleanupOldStatus(activeUsers);
           Promise.all(writePromises).then(function() {
               countEl.textContent = totalEditors;
               if (totalEditors > 0) {
                   var editorList = Array.from(activeUsers).slice(0, 5).join('、');
                   log('📊 检测到 ' + totalEditors + ' 位活跃用户:' + editorList + (activeUsers.size > 5 ? ' ...' : ), 'editor');
               } else {
                   log('📊 暂无活跃用户', 'mute');
               }
           }).catch(function(err) {
               log('⚠️ 写入失败: ' + err.message, 'warning');
           });
       }).fail(function(err) {
           log('❌ 扫描失败: ' + (err.error?.info || err.message || '未知错误'), 'error');
       });
   }
   // ============ 写入用户状态 ============
   function writeUserStatus(username, page, editors) {
       return new Promise(function(resolve, reject) {
           var statusPage = 'User:' + username + '/edit-status';
           var api = new mw.Api();
           var statusData = {
               page: page,
               editors: editors.map(function(e) {
                   return { user: e, timestamp: Date.now() };
               }),
               timestamp: Date.now(),
               checkedBy: BOT_NAME,
               status: editors.length > 0 ? 'editing' : 'idle'
           };
           api.postWithEditToken({
               action: 'edit',
               title: statusPage,
               text: JSON.stringify(statusData, null, 2),
               summary: '🤖 ' + BOT_NAME + ' 检测编辑状态',
               minor: true,
               bot: true
           }).then(function() {
               writeCount++;
               writesEl.textContent = writeCount;
               resolve();
           }).fail(function(err) {
               reject(err);
           });
       });
   }
   // ============ 清理过期状态 ============
   function cleanupOldStatus(activeUsers) {
       return new Promise(function(resolve) {
           var api = new mw.Api();
           api.get({
               action: 'query',
               list: 'allpages',
               apnamespace: 2,
               apprefix: 'edit-status',
               aplimit: 100,
               format: 'json'
           }).then(function(data) {
               if (!data.query || !data.query.allpages) {
                   resolve();
                   return;
               }
               var promises = [];
               data.query.allpages.forEach(function(page) {
                   var username = page.title.replace('User:', ).replace('/edit-status', );
                   if (username === BOT_NAME || activeUsers.has(username)) return;
                   var p = new mw.Api().get({
                       action: 'query',
                       titles: page.title,
                       prop: 'revisions',
                       rvprop: 'content',
                       rvlimit: 1,
                       format: 'json'
                   }).then(function(contentData) {
                       var pages = contentData.query.pages;
                       for (var id in pages) {
                           if (pages[id].revisions && pages[id].revisions[0]) {
                               try {
                                   var status = JSON.parse(pages[id].revisions[0]['*']);
                                   if (status.status === 'editing' && (Date.now() - status.timestamp) > 60000) {
                                       var api2 = new mw.Api();
                                       var statusData = {
                                           page: status.page || ,
                                           editors: [],
                                           timestamp: Date.now(),
                                           checkedBy: BOT_NAME,
                                           status: 'idle'
                                       };
                                       return api2.postWithEditToken({
                                           action: 'edit',
                                           title: page.title,
                                           text: JSON.stringify(statusData, null, 2),
                                           summary: '🤖 ' + BOT_NAME + ' 清理过期状态',
                                           minor: true,
                                           bot: true
                                       });
                                   }
                               } catch(e) {}
                           }
                       }
                       return Promise.resolve();
                   });
                   promises.push(p);
               });
               Promise.all(promises).then(resolve);
           }).fail(function() { resolve(); });
       });
   }
   // ============ 执行一轮 ============
   function doRound() {
       if (!running) return;
       log('🔄 扫描中...', 'info');
       scanAllEditors();
   }
   // ============ 控制功能 ============
   function startBot() {
       if (running) return;
       running = true;
       var interval = parseInt(intervalInput.value) || 3;
       CHECK_INTERVAL = interval * 1000;
       statusEl.textContent = '● 运行中';
       statusEl.style.color = '#00ff00';
       toggleBtn.textContent = '停止';
       toggleBtn.style.background = '#ff4444';
       log('✅ 机器人已启动,间隔 ' + interval + ' 秒', 'success');
       doRound();
       timer = setInterval(doRound, CHECK_INTERVAL);
   }
   function stopBot() {
       if (!running) return;
       running = false;
       if (timer) {
           clearInterval(timer);
           timer = null;
       }
       statusEl.textContent = '● 已停止';
       statusEl.style.color = '#ff4444';
       toggleBtn.textContent = '启动';
       toggleBtn.style.background = '#44aa44';
       log('⏹️ 机器人已停止', 'warning');
   }
   function toggleBot() {
       if (running) {
           stopBot();
       } else {
           startBot();
       }
   }
   // ============ 拖拽支持 ============
   var consoleEl = document.getElementById('bot-console');
   var isDragging = false;
   var startX, startY, origX, origY;
   consoleEl.querySelector('div:first-child').addEventListener('mousedown', function(e) {
       if (e.target.tagName === 'BUTTON') return;
       isDragging = true;
       startX = e.clientX;
       startY = e.clientY;
       origX = consoleEl.offsetLeft;
       origY = consoleEl.offsetTop;
       consoleEl.style.cursor = 'grabbing';
       e.preventDefault();
   });
   document.addEventListener('mousemove', function(e) {
       if (!isDragging) return;
       var dx = e.clientX - startX;
       var dy = e.clientY - startY;
       consoleEl.style.left = (origX + dx) + 'px';
       consoleEl.style.top = (origY + dy) + 'px';
       consoleEl.style.right = 'auto';
       consoleEl.style.bottom = 'auto';
   });
   document.addEventListener('mouseup', function() {
       isDragging = false;
       consoleEl.style.cursor = 'default';
   });
   // ============ 按钮绑定 ============
   document.getElementById('bot-toggle').addEventListener('click', toggleBot);
   document.getElementById('bot-close').addEventListener('click', function() {
       if (running) stopBot();
       consoleEl.style.display = 'none';
       log('📦 控制台已隐藏,重新打开请运行 showBot()', 'info');
   });
   document.getElementById('bot-scan').addEventListener('click', function() {
       if (running) {
           log('🔄 手动扫描...', 'info');
           doRound();
       } else {
           log('⚠️ 机器人已停止,请先启动', 'warning');
       }
   });
   document.getElementById('bot-clear').addEventListener('click', function() {
       logEl.innerHTML = ;
       log('🗑️ 日志已清空', 'info');
   });
   intervalInput.addEventListener('change', function() {
       var val = parseInt(this.value) || 3;
       if (val < 1) this.value = 1;
       if (val > 30) this.value = 30;
       if (running) {
           log('⏱️ 间隔已改为 ' + this.value + ' 秒,重启生效', 'info');
           stopBot();
           startBot();
       }
   });
   // ============ 暴露全局 ============
   window.showBot = function() {
       consoleEl.style.display = 'block';
       log('📦 控制台已显示', 'info');
   };
   window.hideBot = function() {
       consoleEl.style.display = 'none';
   };
   // ============ 自动启动 ============
   log('🤖 ' + BOT_NAME + ' 控制台已加载', 'success');
   log('💡 点击 "启动" 开始检测,或拖拽移动控制台', 'info');
   // 自动启动
   startBot();

})(); </script>