// ==UserScript== // @name [MWI]Auto Sell Assistant // @name:zh-CN [银河奶牛]自动出售助手 // @namespace https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-auto-sell-assistant // @version 1.0.0 // @description [Auto Sell Assistant] One-click auto sell items, optimize operation delay, improve game efficiency // @description:zh-CN 一键自动出售指定物品,智能优化操作延迟,提升游戏效率 // @author shenhuanjie // @license MIT // @match https://www.milkywayidle.com/game* // @icon https://www.milkywayidle.com/favicon.svg // @grant GM_setValue // @grant GM_getValue // @homepage https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-auto-sell-assistant // @supportURL https://cnb.cool/shenhuanjie/skyner-cn/tamper-monkey-script/mwi-auto-sell-assistant // @connect greasyfork.org // @require https://cdn.tailwindcss.com // @run-at document-idle // @noframes // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 全局配置 const CONFIG = { debugMode: true, notificationPosition: 'top-right', notificationDuration: 2000, defaultHighlightColor: 'rgba(255, 0, 0, 0.5)', defaultHighlightDuration: 500, precondition: { selector: '[class*="Item_selected__"]', errorMessage: '请先选择一个物品!' }, hotkey: { key: 'k', altKey: false, ctrlKey: false, shiftKey: false }, marketSelectors: [ '[class*="MarketplacePage_container__"]', '[class*="MarketplacePanel_"]', '[data-testid="marketplace"]' ], localStorageKey: 'autoClickOptimalDelays', minDelay: 200, // 最小延迟时间(毫秒) maxDelay: 2000, // 最大延迟时间(毫秒) delayStep: 50, // 延迟调整步长(毫秒) retryAttempts: 3, // 每步最大重试次数 successThreshold: 5, // 计算最优延迟的成功次数基数 optimizationFactor: 1.3 // 安全余量系数 }; // 从本地存储加载最优延迟配置 let optimalDelays = JSON.parse(GM_getValue(CONFIG.localStorageKey, '{}')); // 初始化执行统计 const executionStats = {}; // 动作链配置 const ACTIONS = [ { description: '前往市场', containerSelector: '[class*="MuiTooltip-tooltip"]', buttonSelector: 'button[class*="Button_button__"][class*="Button_fullWidth__"]', text: '前往市场', checkResult: function() { return checkMarketPage(); }, errorMessage: '无法导航到市场页面!' }, { description: '出售', containerSelector: '[class*="MarketplacePanel_itemContainer__"]', buttonSelector: 'button[class*="Button_sell__"]', text: '出售' }, { description: '全部', containerSelector: '[class*="MarketplacePanel_quantityInputs__"]', buttonSelector: 'button', text: '全部' }, { description: '发布出售订单', containerSelector: '[class*="MarketplacePanel_modalContent__"]', buttonSelector: '[class*="MarketplacePanel_postButtonContainer__"] > button[class*="Button_success__"]', text: '发布出售订单' } ]; // 初始化动作链延迟配置 ACTIONS.forEach(action => { const key = action.description; // 使用本地存储的最优延迟或默认值 action.preDelay = optimalDelays[key] || 800; action.postDelay = optimalDelays[`${key}_post`] || 600; // 初始化执行统计 executionStats[key] = { successes: 0, failures: 0, totalTime: 0, attempts: [] }; }); // 初始化 document.addEventListener('keydown', function(event) { const { key, altKey, ctrlKey, shiftKey } = CONFIG.hotkey; if ( event.key.toLowerCase() === key.toLowerCase() && event.altKey === altKey && event.ctrlKey === ctrlKey && event.shiftKey === shiftKey ) { event.preventDefault(); executeActionChain(); } }); log('脚本已加载', 'info'); log(`使用最优延迟配置: ${JSON.stringify(optimalDelays)}`, 'info'); // 市场页面检查函数 function checkMarketPage() { for (const selector of CONFIG.marketSelectors) { if (document.querySelectorAll(selector).length > 0) { return true; } } return false; } // 执行完整动作链 async function executeActionChain() { // 执行前置条件检查 if (!checkPrecondition()) { showNotification(CONFIG.precondition.errorMessage, 'error'); return; } // showNotification('开始执行动作链...', 'info'); const startTime = performance.now(); for (let i = 0; i < ACTIONS.length; i++) { const action = ACTIONS[i]; let attempt = 0; let success = false; while (attempt < CONFIG.retryAttempts && !success) { attempt++; log(`执行步骤 ${i+1}/${ACTIONS.length}: ${action.description} (尝试 ${attempt}/${CONFIG.retryAttempts})`, 'info'); // showNotification(`执行: ${action.description} (${attempt}/${CONFIG.retryAttempts})`, 'info'); const actionStartTime = performance.now(); try { // 查找元素 const element = findElement(action); if (!element) { throw new Error(`未找到"${action.description}"按钮`); } // 高亮并点击元素 highlightElement(element); // 记录点击前的状态 const beforeClickTime = performance.now(); element.click(); log(`已点击"${action.description}"按钮`, 'success'); // 等待后置延迟并检查结果 await wait(action.postDelay); // 检查结果(如果有检查函数) if (typeof action.checkResult === 'function') { const result = action.checkResult(); if (!result) { throw new Error(action.errorMessage || `执行"${action.description}"后检查失败`); } } // 执行成功 success = true; // 计算实际执行时间 const actualTime = performance.now() - beforeClickTime; // 更新统计信息 updateStats(action.description, true, actualTime); // 显示执行时间 const timeMsg = `步骤 ${i+1}/${ACTIONS.length} (${action.description}) 耗时 ${Math.round(actualTime)}ms`; showNotification(timeMsg, 'success'); // 等待下一个动作的前置延迟 await wait(action.preDelay); } catch (error) { // 执行失败 log(error.message, 'error'); showNotification(`执行失败: ${error.message}`, 'error'); // 更新统计信息 updateStats(action.description, false); // 如果不是最后一次尝试,增加延迟并重试 if (attempt < CONFIG.retryAttempts) { // 增加延迟 action.postDelay = Math.min(action.postDelay + CONFIG.delayStep, CONFIG.maxDelay); log(`增加"${action.description}"的延迟至 ${action.postDelay}ms`, 'warning'); showNotification(`增加延迟至 ${action.postDelay}ms,准备重试`, 'warning'); } else { // 所有重试都失败,记录调试信息并终止 logDebugInfo(action); return; } } } } // 全部步骤执行完毕 const totalDuration = Math.round(performance.now() - startTime); showNotification(`动作链执行完毕,总耗时 ${totalDuration}ms`, 'success'); log(`动作链执行完毕,总耗时 ${totalDuration}ms`, 'success'); // 保存最优延迟配置 saveOptimalDelays(); } // 查找元素函数 function findElement(action) { const containers = document.querySelectorAll(action.containerSelector); for (const container of containers) { const candidates = container.querySelectorAll(action.buttonSelector); for (const candidate of candidates) { if (candidate.textContent.trim() === action.text) { return candidate; } } } // 尝试全局查找 const globalCandidates = document.querySelectorAll(action.buttonSelector); for (const candidate of globalCandidates) { if (candidate.textContent.trim() === action.text) { return candidate; } } return null; } // 前置条件检查 function checkPrecondition() { const elements = document.querySelectorAll(CONFIG.precondition.selector); return elements.length > 0; } // 高亮元素 function highlightElement(element, color = CONFIG.defaultHighlightColor, duration = CONFIG.defaultHighlightDuration) { const originalStyle = element.style.cssText; element.style.cssText = ` ${originalStyle} transition: all 0.3s; box-shadow: 0 0 0 3px ${color}; `; setTimeout(() => { element.style.cssText = originalStyle; }, duration); } // 显示通知(保留更美观的版本) function showNotification(message, type = 'info') { // 移除旧通知 const existingNotifications = document.querySelectorAll('.action-chain-notification'); existingNotifications.forEach(notification => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }); // 创建新通知 const notification = document.createElement('div'); notification.className = 'action-chain-notification'; // 通知样式 notification.style.cssText = ` position: fixed; padding: 10px 16px; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; transition: all 0.3s; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; font-weight: 500; max-width: 300px; transform: translateY(-20px); opacity: 0; `; // 位置配置 const positions = { 'top-right': 'top: 20px; right: 20px;', 'top-left': 'top: 20px; left: 20px;', 'bottom-right': 'bottom: 20px; right: 20px;', 'bottom-left': 'bottom: 20px; left: 20px;' }; notification.style.cssText += positions[CONFIG.notificationPosition] || positions['top-right']; // 类型样式 const types = { info: { bg: 'rgba(30, 144, 255, 0.95)', text: 'white', icon: 'ℹ️' }, success: { bg: 'rgba(76, 175, 80, 0.95)', text: 'white', icon: '✅' }, error: { bg: 'rgba(244, 67, 54, 0.95)', text: 'white', icon: '❌' }, warning: { bg: 'rgba(255, 193, 7, 0.95)', text: '#333', icon: '⚠️' } }; const style = types[type] || types.info; notification.style.backgroundColor = style.bg; notification.style.color = style.text; // 设置内容(添加图标) notification.innerHTML = `
${style.icon} ${message}
`; // 添加到页面 document.body.appendChild(notification); // 显示动画 setTimeout(() => { notification.style.transform = 'translateY(0)'; notification.style.opacity = '1'; }, 10); // 自动消失 setTimeout(() => { notification.style.transform = 'translateY(-20px)'; notification.style.opacity = '0'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, CONFIG.notificationDuration); } // 日志函数 function log(message, level = 'info') { if (!CONFIG.debugMode && level !== 'error') return; const colors = { info: 'color: #333;', success: 'color: #4CAF50;', error: 'color: #F44336; font-weight: bold;', warning: 'color: #FFC107;', debug: 'color: #2196F3;' }; console.log(`%c[自动点击脚本] ${message}`, colors[level] || colors.info); } // 调试信息 function logDebugInfo(action) { if (!CONFIG.debugMode) return; console.groupCollapsed(`🔍 调试信息: ${action.description}`); console.log('▶ 配置参数:', { containerSelector: action.containerSelector, buttonSelector: action.buttonSelector, text: action.text, currentPreDelay: action.preDelay, currentPostDelay: action.postDelay }); const containers = document.querySelectorAll(action.containerSelector); console.log(`▶ 容器查找结果: 找到 ${containers.length} 个容器`); if (containers.length > 0) { console.log('容器列表:', containers); containers.forEach((container, index) => { const buttons = container.querySelectorAll(action.buttonSelector); console.log(` ▶ 容器 ${index + 1}: 找到 ${buttons.length} 个候选按钮`); if (buttons.length > 0) { console.log(' 按钮列表:'); buttons.forEach((btn, btnIndex) => { console.log(` ${btnIndex + 1}. "${btn.textContent.trim()}"`); }); } }); } const globalButtons = document.querySelectorAll(action.buttonSelector); console.log(`▶ 全局按钮查找结果: 找到 ${globalButtons.length} 个候选按钮`); if (globalButtons.length > 0) { console.log('全局按钮列表:'); globalButtons.forEach((btn, index) => { console.log(` ${index + 1}. "${btn.textContent.trim()}"`); }); } console.groupEnd(); } // 等待函数 function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 更新执行统计 function updateStats(actionName, success, executionTime = 0) { const stats = executionStats[actionName]; if (success) { stats.successes++; stats.totalTime += executionTime; stats.attempts.push(executionTime); // 记录最佳执行时间 const bestTime = Math.min(...stats.attempts); // 自动调整延迟 - 如果连续成功,尝试减少延迟 if (stats.successes >= CONFIG.successThreshold) { // 计算平均执行时间并增加安全余量 const avgTime = stats.totalTime / stats.successes; const safeDelay = Math.max( Math.round(bestTime * CONFIG.optimizationFactor), CONFIG.minDelay ); // 如果当前延迟比计算的安全延迟大,减少延迟 if (ACTIONS.find(a => a.description === actionName).postDelay > safeDelay) { ACTIONS.find(a => a.description === actionName).postDelay = safeDelay; log(`优化"${actionName}"的延迟至 ${safeDelay}ms (最佳: ${Math.round(bestTime)}ms)`, 'info'); showNotification(`优化"${actionName}"的延迟至 ${safeDelay}ms`, 'info'); } } } else { stats.failures++; } } // 保存最优延迟配置 function saveOptimalDelays() { const delays = {}; ACTIONS.forEach(action => { delays[action.description] = action.preDelay; delays[`${action.description}_post`] = action.postDelay; }); GM_setValue(CONFIG.localStorageKey, JSON.stringify(delays)); log(`已保存最优延迟配置: ${JSON.stringify(delays)}`, 'info'); // 显示优化结果 const totalSavedTime = Object.values(executionStats) .filter(s => s.successes > 0) .reduce((sum, s) => sum + (s.totalTime / s.successes), 0); if (totalSavedTime > 0) { log(`优化潜力: 平均可节省 ${Math.round(totalSavedTime)}ms`, 'info'); showNotification(`优化完成,平均节省 ${Math.round(totalSavedTime)}ms`, 'success'); } } })();