您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动在校园论坛中发帖,智能检测限制,优化用户体验,支持深色模式
// ==UserScript== // @name 自动化大红楼 // @namespace http://tampermonkey.net/ // @version 2.5.5 // @description 自动在校园论坛中发帖,智能检测限制,优化用户体验,支持深色模式 // @author JackZhang144 // @license MIT // @match https://bbs.uestc.edu.cn/thread/2234791* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // ==/UserScript== // 目前默认匹配通过WEBVPN登录河畔后的任意贴地址,后期河畔开放外网之后会修改成匹配原版地址 (function() { 'use strict'; // 默认配置 const DEFAULT_CONFIG = { minDelay: 1000, maxDelay: 2000, contents: ["水水", "水水水", "水水水水", "水水水水水", "水水水水水水", "水水水水水水水"], enabled: false, panelMinimized: false, panelPosition: { x: 20, y: 20 }, // 默认位置 maxConsecutiveErrors: 5 // 默认最大连续错误次数 }; let config = {...DEFAULT_CONFIG}; let isAutoPosting = false; let isWaitingDueToLimit = false; let postingTimeout = null; let waitingTimeout = null; let attemptCount = 0; let errorCount = 0; let isDragging = false; let dragOffset = { x: 0, y: 0 }; // 初始化配置 function initConfig() { try { const savedConfig = GM_getValue('autoPostConfig'); if (savedConfig) { config = {...DEFAULT_CONFIG, ...savedConfig}; } } catch (e) { console.error('配置初始化失败:', e); } } // 保存配置 function saveConfig() { try { GM_setValue('autoPostConfig', config); } catch (e) { console.error('保存配置失败:', e); } } // 创建UI控件 function createControlPanel() { try { // 如果面板已存在,先移除 const existingPanel = document.getElementById('autoPostPanel'); if (existingPanel) { existingPanel.remove(); } // 创建容器 const panel = document.createElement('div'); panel.id = 'autoPostPanel'; panel.style.position = 'fixed'; panel.style.top = config.panelPosition.y + 'px'; panel.style.left = config.panelPosition.x + 'px'; panel.style.zIndex = '999999'; panel.style.padding = config.panelMinimized ? '6px' : '12px'; panel.style.backgroundColor = '#ffffff'; panel.style.borderRadius = '8px'; panel.style.boxShadow = '0 5px 20px rgba(0, 0, 0, 0.15)'; panel.style.fontFamily = '"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'; panel.style.width = config.panelMinimized ? 'auto' : '300px'; panel.style.maxHeight = '85vh'; panel.style.overflowY = 'auto'; panel.style.transition = 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)'; panel.style.backdropFilter = 'blur(5px)'; panel.style.border = '1px solid rgba(0, 0, 0, 0.1)'; panel.style.cursor = 'move'; // 标题栏 const titleBar = document.createElement('div'); titleBar.style.display = 'flex'; titleBar.style.justifyContent = 'space-between'; titleBar.style.alignItems = 'center'; titleBar.style.marginBottom = config.panelMinimized ? '0' : '12px'; titleBar.style.cursor = 'move'; titleBar.style.userSelect = 'none'; titleBar.style.padding = '4px 0'; // 添加拖拽事件监听器 titleBar.addEventListener('mousedown', startDrag); const title = document.createElement('h3'); title.textContent = '自动水帖工具'; title.style.margin = '0'; title.style.fontSize = '16px'; title.style.color = '#2c3e50'; title.style.fontWeight = '600'; title.style.flexGrow = '1'; // 最小化按钮 const minimizeBtn = document.createElement('button'); minimizeBtn.textContent = config.panelMinimized ? '▢' : '−'; minimizeBtn.style.background = 'none'; minimizeBtn.style.border = 'none'; minimizeBtn.style.fontSize = '16px'; minimizeBtn.style.cursor = 'pointer'; minimizeBtn.style.padding = '3px'; minimizeBtn.style.marginLeft = '8px'; minimizeBtn.style.color = '#7f8c8d'; minimizeBtn.style.borderRadius = '3px'; minimizeBtn.style.transition = 'background-color 0.2s'; minimizeBtn.style.width = '24px'; minimizeBtn.style.height = '24px'; minimizeBtn.style.display = 'flex'; minimizeBtn.style.alignItems = 'center'; minimizeBtn.style.justifyContent = 'center'; minimizeBtn.addEventListener('mouseover', function() { this.style.backgroundColor = 'rgba(0, 0, 0, 0.05)'; }); minimizeBtn.addEventListener('mouseout', function() { this.style.backgroundColor = 'transparent'; }); titleBar.appendChild(title); titleBar.appendChild(minimizeBtn); panel.appendChild(titleBar); // 如果面板已最小化,只显示标题栏 if (config.panelMinimized) { // 添加到文档 document.body.appendChild(panel); // 点击标题栏展开面板 titleBar.addEventListener('click', function() { config.panelMinimized = false; saveConfig(); createControlPanel(); }); return; } // 配置区域 const configSection = document.createElement('div'); configSection.style.marginBottom = '12px'; configSection.style.padding = '10px'; configSection.style.backgroundColor = 'rgba(236, 240, 241, 0.3)'; configSection.style.borderRadius = '6px'; // 配置区域标题 const configTitle = document.createElement('h4'); configTitle.textContent = '⚙️ 配置选项'; configTitle.style.margin = '0 0 10px 0'; configTitle.style.fontSize = '14px'; configTitle.style.color = '#34495e'; configTitle.style.fontWeight = '600'; configTitle.style.display = 'flex'; configTitle.style.alignItems = 'center'; configSection.appendChild(configTitle); // 最小间隔时间 const minDelayLabel = document.createElement('label'); minDelayLabel.textContent = '⏱ 最小间隔(毫秒):'; minDelayLabel.style.display = 'block'; minDelayLabel.style.marginBottom = '5px'; minDelayLabel.style.fontWeight = '500'; minDelayLabel.style.fontSize = '12px'; minDelayLabel.style.color = '#2c3e50'; const minDelayInput = document.createElement('input'); minDelayInput.type = 'number'; minDelayInput.value = config.minDelay; minDelayInput.style.width = '100%'; minDelayInput.style.padding = '6px'; minDelayInput.style.border = '1px solid #ddd'; minDelayInput.style.borderRadius = '4px'; minDelayInput.style.marginBottom = '8px'; minDelayInput.min = 1000; minDelayInput.style.boxSizing = 'border-box'; minDelayInput.style.transition = 'border-color 0.3s'; minDelayInput.style.fontSize = '12px'; minDelayInput.addEventListener('focus', function() { this.style.borderColor = '#3498db'; this.style.boxShadow = '0 0 0 2px rgba(52, 152, 219, 0.2)'; }); minDelayInput.addEventListener('blur', function() { this.style.borderColor = '#ddd'; this.style.boxShadow = 'none'; }); // 最大间隔时间 const maxDelayLabel = document.createElement('label'); maxDelayLabel.textContent = '⏱ 最大间隔(毫秒):'; maxDelayLabel.style.display = 'block'; maxDelayLabel.style.marginBottom = '5px'; maxDelayLabel.style.fontWeight = '500'; maxDelayLabel.style.fontSize = '12px'; maxDelayLabel.style.color = '#2c3e50'; const maxDelayInput = document.createElement('input'); maxDelayInput.type = 'number'; maxDelayInput.value = config.maxDelay; maxDelayInput.style.width = '100%'; maxDelayInput.style.padding = '6px'; maxDelayInput.style.border = '1px solid #ddd'; maxDelayInput.style.borderRadius = '4px'; maxDelayInput.style.marginBottom = '8px'; maxDelayInput.min = 2000; maxDelayInput.style.boxSizing = 'border-box'; maxDelayInput.style.transition = 'border-color 0.3s'; maxDelayInput.style.fontSize = '12px'; maxDelayInput.addEventListener('focus', function() { this.style.borderColor = '#3498db'; this.style.boxShadow = '0 0 0 2px rgba(52, 152, 219, 0.2)'; }); maxDelayInput.addEventListener('blur', function() { this.style.borderColor = '#ddd'; this.style.boxShadow = 'none'; }); // 发帖内容 const contentLabel = document.createElement('label'); contentLabel.textContent = '💬 发帖内容(中文逗号分隔):'; contentLabel.style.display = 'block'; contentLabel.style.marginBottom = '5px'; contentLabel.style.fontWeight = '500'; contentLabel.style.fontSize = '12px'; contentLabel.style.color = '#2c3e50'; const contentTextarea = document.createElement('textarea'); contentTextarea.value = config.contents.join(','); contentTextarea.style.width = '100%'; contentTextarea.style.padding = '6px'; contentTextarea.style.border = '1px solid #ddd'; contentTextarea.style.borderRadius = '4px'; contentTextarea.style.marginBottom = '8px'; contentTextarea.style.height = '80px'; contentTextarea.style.resize = 'vertical'; contentTextarea.style.fontSize = '12px'; contentTextarea.style.boxSizing = 'border-box'; contentTextarea.style.transition = 'border-color 0.3s'; contentTextarea.addEventListener('focus', function() { this.style.borderColor = '#3498db'; this.style.boxShadow = '0 0 0 2px rgba(52, 152, 219, 0.2)'; }); contentTextarea.addEventListener('blur', function() { this.style.borderColor = '#ddd'; this.style.boxShadow = 'none'; }); // 最大连续错误次数 const maxErrorsLabel = document.createElement('label'); maxErrorsLabel.textContent = '❌ 最大连续错误次数:'; maxErrorsLabel.style.display = 'block'; maxErrorsLabel.style.marginBottom = '5px'; maxErrorsLabel.style.fontWeight = '500'; maxErrorsLabel.style.fontSize = '12px'; maxErrorsLabel.style.color = '#2c3e50'; const maxErrorsInput = document.createElement('input'); maxErrorsInput.type = 'number'; maxErrorsInput.value = config.maxConsecutiveErrors; maxErrorsInput.style.width = '100%'; maxErrorsInput.style.padding = '6px'; maxErrorsInput.style.border = '1px solid #ddd'; maxErrorsInput.style.borderRadius = '4px'; maxErrorsInput.style.marginBottom = '8px'; maxErrorsInput.min = 1; maxErrorsInput.max = 10; maxErrorsInput.style.boxSizing = 'border-box'; maxErrorsInput.style.transition = 'border-color 0.3s'; maxErrorsInput.style.fontSize = '12px'; maxErrorsInput.addEventListener('focus', function() { this.style.borderColor = '#3498db'; this.style.boxShadow = '0 0 0 2px rgba(52, 152, 219, 0.2)'; }); maxErrorsInput.addEventListener('blur', function() { this.style.borderColor = '#ddd'; this.style.boxShadow = 'none'; }); // 保存配置按钮 const saveConfigBtn = document.createElement('button'); saveConfigBtn.textContent = '💾 保存配置'; saveConfigBtn.style.padding = '8px'; saveConfigBtn.style.backgroundColor = '#3498db'; saveConfigBtn.style.color = 'white'; saveConfigBtn.style.border = 'none'; saveConfigBtn.style.borderRadius = '4px'; saveConfigBtn.style.cursor = 'pointer'; saveConfigBtn.style.fontSize = '13px'; saveConfigBtn.style.marginBottom = '10px'; saveConfigBtn.style.width = '100%'; saveConfigBtn.style.fontWeight = '600'; saveConfigBtn.style.transition = 'all 0.3s'; saveConfigBtn.style.boxShadow = '0 2px 4px rgba(52, 152, 219, 0.3)'; saveConfigBtn.addEventListener('mouseover', function() { this.style.backgroundColor = '#2980b9'; this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 4px 8px rgba(52, 152, 219, 0.4)'; }); saveConfigBtn.addEventListener('mouseout', function() { this.style.backgroundColor = '#3498db'; this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 2px 4px rgba(52, 152, 219, 0.3)'; }); saveConfigBtn.addEventListener('mousedown', function() { this.style.transform = 'translateY(0)'; }); saveConfigBtn.addEventListener('mouseup', function() { this.style.transform = 'translateY(-1px)'; }); // 添加到配置区域 configSection.appendChild(minDelayLabel); configSection.appendChild(minDelayInput); configSection.appendChild(maxDelayLabel); configSection.appendChild(maxDelayInput); configSection.appendChild(contentLabel); configSection.appendChild(contentTextarea); configSection.appendChild(maxErrorsLabel); configSection.appendChild(maxErrorsInput); configSection.appendChild(saveConfigBtn); // 控制区域 const controlSection = document.createElement('div'); controlSection.style.padding = '10px'; controlSection.style.backgroundColor = 'rgba(236, 240, 241, 0.3)'; controlSection.style.borderRadius = '6px'; // 控制区域标题 const controlTitle = document.createElement('h4'); controlTitle.textContent = '🎮 控制面板'; controlTitle.style.margin = '0 0 10px 0'; controlTitle.style.fontSize = '14px'; controlTitle.style.color = '#34495e'; controlTitle.style.fontWeight = '600'; controlTitle.style.display = 'flex'; controlTitle.style.alignItems = 'center'; // 开关按钮 const toggleBtn = document.createElement('button'); toggleBtn.id = 'autoPostToggle'; toggleBtn.textContent = isAutoPosting ? '⏹ 停止自动发帖' : '▶ 开始自动发帖'; toggleBtn.style.padding = '10px'; toggleBtn.style.backgroundColor = isAutoPosting ? '#e74c3c' : '#2ecc71'; toggleBtn.style.color = 'white'; toggleBtn.style.border = 'none'; toggleBtn.style.borderRadius = '4px'; toggleBtn.style.cursor = 'pointer'; toggleBtn.style.fontSize = '13px'; toggleBtn.style.marginBottom = '10px'; toggleBtn.style.width = '100%'; toggleBtn.style.fontWeight = '600'; toggleBtn.style.transition = 'all 0.3s'; toggleBtn.style.boxShadow = isAutoPosting ? '0 2px 4px rgba(231, 76, 60, 0.3)' : '0 2px 4px rgba(46, 204, 113, 0.3)'; toggleBtn.addEventListener('mouseover', function() { if (isAutoPosting) { this.style.backgroundColor = '#c0392b'; this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 4px 8px rgba(231, 76, 60, 0.4)'; } else { this.style.backgroundColor = '#27ae60'; this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 4px 8px rgba(46, 204, 113, 0.4)'; } }); toggleBtn.addEventListener('mouseout', function() { if (isAutoPosting) { this.style.backgroundColor = '#e74c3c'; this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 2px 4px rgba(231, 76, 60, 0.3)'; } else { this.style.backgroundColor = '#2ecc71'; this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 2px 4px rgba(46, 204, 113, 0.3)'; } }); toggleBtn.addEventListener('mousedown', function() { this.style.transform = 'translateY(0)'; }); toggleBtn.addEventListener('mouseup', function() { this.style.transform = 'translateY(-1px)'; }); // 状态显示 const statusContainer = document.createElement('div'); statusContainer.style.display = 'flex'; statusContainer.style.flexDirection = 'column'; statusContainer.style.gap = '6px'; statusContainer.style.marginBottom = '6px'; const status = document.createElement('div'); status.id = 'autoPostStatus'; status.style.display = 'flex'; status.style.alignItems = 'center'; status.style.padding = '6px'; status.style.borderRadius = '4px'; status.style.fontWeight = '500'; status.style.fontSize = '12px'; if (isWaitingDueToLimit) { status.innerHTML = '⏳ 状态: 等待限制解除'; status.style.backgroundColor = 'rgba(230, 126, 34, 0.2)'; status.style.color = '#e67e22'; } else { status.innerHTML = `🔵 状态: ${isAutoPosting ? '运行中' : '已停止'}`; status.style.backgroundColor = isAutoPosting ? 'rgba(46, 204, 113, 0.2)' : 'rgba(127, 140, 141, 0.2)'; status.style.color = isAutoPosting ? '#27ae60' : '#7f8c8d'; } // 计数器显示 const countersContainer = document.createElement('div'); countersContainer.style.display = 'flex'; countersContainer.style.gap = '6px'; const counter = document.createElement('div'); counter.id = 'autoPostCounter'; counter.textContent = `📝 已发帖: ${attemptCount} 次`; counter.style.flex = '1'; counter.style.padding = '6px'; counter.style.borderRadius = '4px'; counter.style.fontSize = '11px'; counter.style.backgroundColor = 'rgba(52, 152, 219, 0.1)'; counter.style.color = '#3498db'; counter.style.fontWeight = '500'; // 错误计数器显示 const errorCounter = document.createElement('div'); errorCounter.id = 'autoPostErrorCounter'; errorCounter.textContent = `❌ 错误: ${errorCount} 次`; errorCounter.style.flex = '1'; errorCounter.style.padding = '6px'; errorCounter.style.borderRadius = '4px'; errorCounter.style.fontSize = '11px'; errorCounter.style.backgroundColor = 'rgba(231, 76, 60, 0.1)'; errorCounter.style.color = '#e74c3c'; errorCounter.style.fontWeight = '500'; countersContainer.appendChild(counter); countersContainer.appendChild(errorCounter); statusContainer.appendChild(status); statusContainer.appendChild(countersContainer); // 添加到控制区域 controlSection.appendChild(controlTitle); controlSection.appendChild(toggleBtn); controlSection.appendChild(statusContainer); // 添加到面板 panel.appendChild(configSection); panel.appendChild(controlSection); // 添加到文档 document.body.appendChild(panel); // 保存配置事件 saveConfigBtn.addEventListener('click', function() { const min = parseInt(minDelayInput.value); const max = parseInt(maxDelayInput.value); const contents = contentTextarea.value.split(',').filter(c => c.trim() !== ''); const maxErrors = parseInt(maxErrorsInput.value); if (isNaN(min) || isNaN(max) || min < 500 || max < 1000 || min >= max) { showNotification('请输入有效的时间间隔(最小≥500,最大≥1000,且最大>最小)', 'error'); return; } if (contents.length === 0) { showNotification('请输入至少一条发帖内容', 'error'); return; } if (isNaN(maxErrors) || maxErrors < 1 || maxErrors > 10) { showNotification('请输入有效的最大连续错误次数(1-10之间)', 'error'); return; } config.minDelay = min; config.maxDelay = max; config.contents = contents; config.maxConsecutiveErrors = maxErrors; saveConfig(); showNotification('✅ 配置已保存!', 'success'); }); // 切换按钮事件 toggleBtn.addEventListener('click', function() { if (isWaitingDueToLimit) { // 如果正在等待限制解除,点击按钮将取消等待并完全停止 clearTimeout(waitingTimeout); isWaitingDueToLimit = false; isAutoPosting = false; config.enabled = false; saveConfig(); updateUI(); showNotification('⏹ 已取消等待并停止自动发帖', 'info'); } else { // 正常切换状态 isAutoPosting = !isAutoPosting; config.enabled = isAutoPosting; saveConfig(); if (isAutoPosting) { startAutoPosting(); } else { stopAutoPosting(); } updateUI(); } }); // 最小化按钮事件 minimizeBtn.addEventListener('click', function(e) { e.stopPropagation(); config.panelMinimized = true; saveConfig(); createControlPanel(); }); // 如果配置为启用,自动开始 if (config.enabled && !isAutoPosting) { isAutoPosting = true; startAutoPosting(); } } catch (e) { console.error('创建控制面板失败:', e); // 如果UI创建失败,显示一个简单的按钮来重新加载 showReloadButton(); } } // 拖拽开始 function startDrag(e) { // 只有在左键点击时才开始拖拽 if (e.button !== 0) return; isDragging = true; const panel = document.getElementById('autoPostPanel'); // 计算鼠标点击位置与面板左上角的偏移 const rect = panel.getBoundingClientRect(); dragOffset.x = e.clientX - rect.left; dragOffset.y = e.clientY - rect.top; // 添加全局事件监听器 document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); // 防止文本选择 e.preventDefault(); } // 拖拽过程 function drag(e) { if (!isDragging) return; const panel = document.getElementById('autoPostPanel'); // 计算新的位置 const x = e.clientX - dragOffset.x; const y = e.clientY - dragOffset.y; // 更新面板位置 panel.style.left = x + 'px'; panel.style.top = y + 'px'; // 保存位置到配置 config.panelPosition = { x, y }; } // 停止拖拽 function stopDrag() { isDragging = false; // 移除全局事件监听器 document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', stopDrag); // 保存位置配置 saveConfig(); } // 显示重新加载按钮 function showReloadButton() { const reloadBtn = document.createElement('button'); reloadBtn.textContent = '🔄 加载自动水帖工具'; reloadBtn.style.position = 'fixed'; reloadBtn.style.top = '20px'; reloadBtn.style.right = '20px'; reloadBtn.style.zIndex = '999999'; reloadBtn.style.padding = '10px 15px'; reloadBtn.style.backgroundColor = '#3498db'; reloadBtn.style.color = 'white'; reloadBtn.style.border = 'none'; reloadBtn.style.borderRadius = '6px'; reloadBtn.style.cursor = 'pointer'; reloadBtn.style.fontSize = '13px'; reloadBtn.style.fontWeight = '600'; reloadBtn.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.15)'; reloadBtn.style.transition = 'all 0.3s'; reloadBtn.addEventListener('mouseover', function() { this.style.backgroundColor = '#2980b9'; this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 6px 12px rgba(0, 0, 0, 0.2)'; }); reloadBtn.addEventListener('mouseout', function() { this.style.backgroundColor = '#3498db'; this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.15)'; }); document.body.appendChild(reloadBtn); } // 显示通知(非阻塞式) function showNotification(message, type = 'info') { // 移除现有通知 const existingNotification = document.getElementById('autoPostNotification'); if (existingNotification) { existingNotification.remove(); } // 创建通知元素 const notification = document.createElement('div'); notification.id = 'autoPostNotification'; notification.textContent = message; notification.style.position = 'fixed'; notification.style.bottom = '20px'; notification.style.right = '20px'; notification.style.padding = '12px 16px'; notification.style.borderRadius = '6px'; notification.style.zIndex = '1000000'; notification.style.fontFamily = '"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'; notification.style.fontSize = '13px'; notification.style.fontWeight = '500'; notification.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)'; notification.style.maxWidth = '300px'; notification.style.opacity = '0'; notification.style.transform = 'translateY(20px)'; notification.style.transition = 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)'; notification.style.display = 'flex'; notification.style.alignItems = 'center'; // 根据类型设置样式 if (type === 'error') { notification.style.backgroundColor = '#ffebee'; notification.style.color = '#c62828'; notification.style.borderLeft = '4px solid #f44336'; } else if (type === 'success') { notification.style.backgroundColor = '#e8f5e9'; notification.style.color = '#2e7d32'; notification.style.borderLeft = '4px solid #4caf50'; } else { notification.style.backgroundColor = '#e3f2fd'; notification.style.color = '#1565c0'; notification.style.borderLeft = '4px solid #2196f3'; } // 添加到文档 document.body.appendChild(notification); // 显示动画 setTimeout(() => { notification.style.opacity = '1'; notification.style.transform = 'translateY(0)'; }, 10); // 5秒后自动消失 setTimeout(() => { if (notification.parentNode) { notification.style.opacity = '0'; notification.style.transform = 'translateY(20px)'; setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); } }, 5000); } // 更新UI状态 function updateUI() { const status = document.getElementById('autoPostStatus'); const toggleBtn = document.getElementById('autoPostToggle'); const counter = document.getElementById('autoPostCounter'); const errorCounter = document.getElementById('autoPostErrorCounter'); if (status) { if (isWaitingDueToLimit) { status.innerHTML = '⏳ 状态: 等待限制解除'; status.style.backgroundColor = 'rgba(230, 126, 34, 0.2)'; status.style.color = '#e67e22'; } else { status.innerHTML = `🔵 状态: ${isAutoPosting ? '运行中' : '已停止'}`; status.style.backgroundColor = isAutoPosting ? 'rgba(46, 204, 113, 0.2)' : 'rgba(127, 140, 141, 0.2)'; status.style.color = isAutoPosting ? '#27ae60' : '#7f8c8d'; } } if (toggleBtn) { if (isWaitingDueToLimit) { toggleBtn.textContent = '⏹ 取消等待并停止'; toggleBtn.style.backgroundColor = '#e74c3c'; toggleBtn.style.boxShadow = '0 2px 4px rgba(231, 76, 60, 0.3)'; } else { toggleBtn.textContent = isAutoPosting ? '⏹ 停止自动发帖' : '▶ 开始自动发帖'; toggleBtn.style.backgroundColor = isAutoPosting ? '#e74c3c' : '#2ecc71'; toggleBtn.style.boxShadow = isAutoPosting ? '0 2px 4px rgba(231, 76, 60, 0.3)' : '0 2px 4px rgba(46, 204, 113, 0.3)'; } } if (counter) { counter.textContent = `📝 已发帖: ${attemptCount} 次`; } if (errorCounter) { errorCounter.textContent = `❌ 错误: ${errorCount} 次`; } } // 检测错误弹窗 function checkErrorPopup() { // 查找包含错误信息的元素 const errorElements = document.querySelectorAll('.MuiAlert-message'); for (const element of errorElements) { if (element.textContent.includes('本帖最多允许连续回复 9 次')) { return true; } } return false; } // 获取随机内容 function getRandomContent() { if (config.contents.length === 0) return "水水"; const index = Math.floor(Math.random() * config.contents.length); return config.contents[index]; } // 获取随机间隔时间 function getRandomDelay() { return Math.floor(Math.random() * (config.maxDelay - config.minDelay + 1)) + config.minDelay; } // 查找文本输入框 - 支持深色和浅色模式 function findTextInput() { // 尝试查找即时渲染模式下的输入框 (vditor-ir) let textInput = document.querySelector('.vditor-ir .vditor-reset[contenteditable="true"]'); // 如果没找到,尝试查找所见即所得模式下的输入框 (vditor-wysiwyg) if (!textInput) { textInput = document.querySelector('.vditor-wysiwyg .vditor-reset[contenteditable="true"]'); } // 如果没找到,尝试查找分屏预览模式下的输入框 (vditor-sv) if (!textInput) { textInput = document.querySelector('.vditor-sv.vditor-reset[contenteditable="true"]'); } return textInput; } // 查找回复按钮 - 支持深色和浅色模式 function findReplyButton() { // 使用更通用的选择器查找回复按钮 return document.querySelector('button.MuiButton-containedPrimary'); } // 输入文本 function inputText(content) { const textInput = findTextInput(); if (textInput) { // 清空现有内容 textInput.innerHTML = ''; // 创建新的段落元素 const p = document.createElement('p'); p.setAttribute('data-block', '0'); p.textContent = content; // 添加到输入框 textInput.appendChild(p); // 触发输入事件 const event = new Event('input', { bubbles: true }); textInput.dispatchEvent(event); // 触发键盘事件 const keydownEvent = new KeyboardEvent('keydown', { bubbles: true }); const keyupEvent = new KeyboardEvent('keyup', { bubbles: true }); textInput.dispatchEvent(keydownEvent); textInput.dispatchEvent(keyupEvent); return true; } return false; } // 点击回复按钮 function clickReplyButton() { const replyButton = findReplyButton(); if (replyButton && !replyButton.disabled) { replyButton.click(); return true; } return false; } // 滚动到页面底部 function scrollToBottom() { window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); } // 检测回复是否成功 function checkPostSuccess() { const textInput = findTextInput(); return textInput && textInput.textContent === ''; } // 处理发帖限制 function handlePostLimit() { errorCount++; updateUI(); console.log('检测到发帖限制,等待一段时间后重试'); showNotification('⚠️ 检测到发帖限制:本帖最多允许连续回复 9 次,休息一会儿再来吧', 'error'); // 检查是否连续错误达到配置的次数 if (errorCount >= config.maxConsecutiveErrors) { stopAutoPosting(); showNotification(`❌ 连续发帖失败${config.maxConsecutiveErrors}次,自动停止发帖机`, 'error'); return; } // 设置等待状态 isWaitingDueToLimit = true; isAutoPosting = false; updateUI(); // 等待配置的最大间隔时间的2倍 // const waitTime = config.maxDelay * 2; // 等待时间设置为固定30秒 const waitTime = 30000 console.log(`等待 ${waitTime} 毫秒后重试`); // 设置等待超时 waitingTimeout = setTimeout(() => { console.log('等待结束,重新开始自动发帖'); isWaitingDueToLimit = false; // 只有当用户没有手动停止时才重新开始 if (config.enabled) { isAutoPosting = true; startAutoPosting(); } updateUI(); }, waitTime); } // 执行一次发帖流程 async function postOnce() { if (attemptCount >= 1000) { stopAutoPosting(); showNotification('ℹ️ 已达到最大尝试次数,自动发帖已停止', 'info'); return false; } attemptCount++; updateUI(); // 获取随机内容 const content = getRandomContent(); console.log(`开始第 ${attemptCount} 次发帖,内容: "${content}"`); // 输入文本 if (!inputText(content)) { console.log('未找到文本输入框'); errorCount++; updateUI(); // 检查是否连续错误达到配置的次数 if (errorCount >= config.maxConsecutiveErrors) { stopAutoPosting(); showNotification(`❌ 连续发帖失败${config.maxConsecutiveErrors}次,自动停止发帖机`, 'error'); } return false; } console.log('文本输入完成'); // 等待一下让文本输入完成 await new Promise(resolve => setTimeout(resolve, 1000)); // 点击回复 if (!clickReplyButton()) { console.log('未找到回复按钮或按钮不可用'); errorCount++; updateUI(); // 检查是否连续错误达到配置的次数 if (errorCount >= config.maxConsecutiveErrors) { stopAutoPosting(); showNotification(`❌ 连续发帖失败${config.maxConsecutiveErrors}次,自动停止发帖机`, 'error'); } return false; } console.log('已点击回复按钮'); // 等待一段时间检查是否有错误弹窗 await new Promise(resolve => setTimeout(resolve, 2000)); // 检查是否有错误弹窗 if (checkErrorPopup()) { handlePostLimit(); return false; } // 等待回复完成 const delay = getRandomDelay(); console.log(`等待 ${delay} 毫秒后进行下一次发帖`); await new Promise(resolve => setTimeout(resolve, delay)); // 检查是否成功 if (checkPostSuccess()) { console.log('回复成功'); // 滚动到页面底部 scrollToBottom(); console.log('已滚动到页面底部'); // 发帖成功,清空错误计数 errorCount = 0; updateUI(); return true; } else { console.log('回复可能失败'); errorCount++; updateUI(); // 检查是否连续错误达到配置的次数 if (errorCount >= config.maxConsecutiveErrors) { stopAutoPosting(); showNotification(`❌ 连续发帖失败${config.maxConsecutiveErrors}次,自动停止发帖机`, 'error'); } return false; } } // 开始自动发帖 function startAutoPosting() { if (postingTimeout) { clearTimeout(postingTimeout); } if (!isAutoPosting) return; const postNext = async () => { if (!isAutoPosting) return; await postOnce(); if (isAutoPosting && !isWaitingDueToLimit) { const nextDelay = getRandomDelay(); postingTimeout = setTimeout(postNext, nextDelay); } }; // 立即开始第一次发帖 postNext(); } // 停止自动发帖 function stopAutoPosting() { if (postingTimeout) { clearTimeout(postingTimeout); postingTimeout = null; } if (waitingTimeout) { clearTimeout(waitingTimeout); waitingTimeout = null; } isAutoPosting = false; isWaitingDueToLimit = false; config.enabled = false; saveConfig(); updateUI(); } // 初始化 function init() { initConfig(); // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { setTimeout(createControlPanel, 2000); }); } else { setTimeout(createControlPanel, 2000); } } // 注册菜单命令(油猴脚本菜单) GM_registerMenuCommand("自动水帖工具配置", function() { // 如果面板已最小化,先恢复 if (config.panelMinimized) { config.panelMinimized = false; saveConfig(); } createControlPanel(); }); // 启动初始化 init(); })();