您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
刷新任务到指定类型
// ==UserScript== // @name 猫猫放置任务刷新 // @namespace http://tampermonkey.net/ // @version 0.07 // @description 刷新任务到指定类型 // @author 火龙果 // @license MIT // @match **moyu-idle.com/* // @match **www.moyu-idle.com/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @require https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js // @run-at document-idle // ==/UserScript== (function () { 'use strict'; //等待返回消息时长 设置长时间以防破财 毫秒 const waitForMsg = 30 * 1000; const allTasks = { 采集: { 种植: false, 种植葡萄: false, 种植黑麦: false, 采蘑菇: false, 采浆果: false, 采草药: false, 采集花草: false, 采蜂蜜: false, 砍树: false, 砍竹子: false, 捡贝壳: false, 挖沙: false, 收集云絮: false, 收集彩虹碎片: false }, 钓鱼: { 钓鱼: false, 捞鱼: false, 猫咪捕鱼: false, 深海捕鱼: false, 神秘钓鱼: false }, 照料家禽: { 照料小鸡仔: false, 照料奶牛: false, 照料绵羊: false, 养蚕: false }, 采矿: { 挖矿: false, 矿井采矿: false, 深度开采: false, 开采鱼鳞矿: false, 开采绒毛岩: false, 开采爪痕矿: false, 开采魔晶石: false, 开采猫眼石: false, 开采琥珀瞳石: false }, 烹饪: { 制作野草沙拉: false, 制作野果拼盘: false, 熬制鱼汤: false, 炖蘑菇汤: false, 制作猫薄荷饼干: false, 制作猫咪零食: false, 烤制浆果派: false, 制作豪华猫粮: false, 制作鲜鱼刺身拼盘: false, 制作蛋奶布丁: false, 酿造浆果酒: false, 酿造晨露精酿: false, 酿造铃语精酿: false, 制作浆果奶昔: false, 制作铃语奶昔: false, 制作金枪鱼罐头: false, 制作风味虾仁罐头: false, 制作彩虹鱼干罐头: false, 制作神秘锦鲤罐头: false }, 制造: { 制作斧头: false, 制作铁镐: false, 制作冰镐: false, 制作铁锅: false, 制作铁铲: false, 制作小铁锤: false, 制作钢锅: false, 制作钢铲: false, 制作小钢锤: false, 制作铁钳: false, 制作裁缝剪刀: false, 制作针线包: false, 制作酿造搅拌器: false, 制作秘银工匠锤: false, 制作采矿收纳背篓: false, 烧制炭: false, 木浆: false, 制作木剑: false, 制作铁匕首: false, 制作剧毒匕首: false, 制作木质法杖: false, 制作月光法杖: false, 制作喵影法杖: false, 制作魔晶法杖: false, 制作银项链: false, 制作银手链: false, 制作远古鱼骨项链: false, 制作月光吊坠: false, 制作月光守护者: false, 制作猫薄荷手链: false, 制作余烬庇护: false, 制作分裂核心: false, 制作过载核心: false, 制作伏击吊坠: false, 制作先击吊坠: false, 制作兽牙项链: false, 制作彩虹手链: false, 制作彩虹项链: false, 制造玻璃瓶: false, 制作铁罐头: false, 制作木钓竿: false, 制作竹钓竿: false, 制作竹抄网: false, 制作竹制捕鱼笼: false, 制作铁钓竿: false, 制作铁抄网: false, 制作铁制捕鱼笼: false, 制作自动喂食器: false, 制作猫抓板: false, 制作书桌: false, 木浆造纸: false, 造纸: false, 封装书: false, 制作碳笔: false, }, 锻造: { 熔炼钢: false, 熔炼银: false, 熔炼秘银: false, 熔炼鱼鳞合金: false, 熔炼暗影精铁: false, 制作铁剑: false, 锻造铁甲衣: false, 锻造铁头盔: false, 锻造铁护手: false, 锻造铁护腿: false, 制作钢剑: false, 锻造钢甲衣: false, 锻造钢头盔: false, 锻造钢护手: false, 锻造钢护腿: false, 锻造钢制重锤: false, 锻造银质剑: false, 锻造银质匕首: false, 锻造银护甲: false, 锻造银头盔: false, 锻造银护手: false, 锻造银护腿: false, 锻造哥布林匕首·改: false, 锻造狼皮甲: false, 锻造骷髅盾·强化: false, 锻造巨魔木棒·重型: false, 锻造巨蝎毒矛: false, 锻造守护者核心护符: false, 锻造龙鳞甲: false, 锻造冰霜匕首: false, 锻造影之刃: false, 锻造秘银剑: false, 锻造秘银匕首: false, 锻造秘银头盔: false, 锻造秘银护甲: false, 锻造秘银护手: false, 锻造秘银护腿: false, 锻造鱼鳞合金头盔: false, 锻造鱼鳞合金盔甲: false, 锻造鱼鳞合金护手: false, 锻造鱼鳞合金护腿: false, 锻造暗影精铁剑: false, 锻造暗影精铁头盔: false, 锻造暗影精铁盔甲: false, 锻造暗影精铁臂甲: false, 锻造暗影精铁腿甲: false }, 缝制: { 缝制毛毛衣: false, 缝制毛毛帽: false, 缝制毛毛手套: false, 缝制毛毛裤: false, 缝制羊绒布料: false, 缝制丝绸布料: false, 分离绒毛: false, 缝制绒毛布料: false, 缝制羊毛衣: false, 缝制羊毛帽: false, 缝制羊毛手套: false, 缝制羊毛裤: false, 缝制羊毛罩袍: false, 缝制羊毛法师帽: false, 缝制羊毛法师手套: false, 缝制羊毛法师裤子: false, 缝制羊毛紧身衣: false, 缝制羊毛裹头巾: false, 缝制羊毛绑带手套: false, 缝制羊毛紧身裤: false, 缝制羊毛可爱帽: false, 缝制羊毛可爱手套: false, 缝制羊毛裁缝服: false, 缝制羊毛裁缝手套: false, 缝制羊毛工匠服: false, 缝制羊毛围裙: false, 缝制羊毛隔热手套: false, 缝制羊毛探险背包: false, 缝制园艺手套: false, 缝制采矿工作服: false, 缝制钓鱼帽: false, 缝制钓鱼专注帽: false, 缝制重型矿工手套: false, 缝制灵巧采集靴: false, 缝制厨师帽: false, 缝制毛毛可爱帽: false, 缝制毛毛裁缝服: false, 缝制毛毛裁缝手套: false, 制作采集手环: false, 缝制丝质罩袍: false, 缝制丝质法师帽: false, 缝制丝质法师手套: false, 缝制丝质法师裤子: false, 缝制丝质夜行衣: false, 缝制丝质裹头巾: false, 缝制丝质绑带手套: false, 缝制丝质宽松裤: false, 缝制丝质可爱帽: false, 缝制丝质可爱手套: false, 缝制丝质工匠服: false, 缝制丝质裁缝服: false, 缝制丝质裁缝手套: false, 缝制丝质法师披肩: false, 缝制丝质夜行斗篷: false, 缝制丝质战士披风: false, 缝制丝质活力披风: false, 缝制雪狼皮披风: false, 缝制冰羽靴: false, 缝制虹运飘带: false, 缝制蝠影斗篷: false, 缝制云行靴: false, 缝制云行斗篷: false, 缝制毛绒玩具: false, 制作舒适猫窝: false, 缝制诅咒香囊: false } , 特殊制造: { 制作幸运猫神像: false, 制作猫咪护符: false, 炼制猫薄荷药剂: false, 拼接猫咪文物: false, 融合采集戒指: false } , 炼金: { 猫咪点金术1: false, 猫咪点金术2: false, 猫咪点金术3: false, 基础点金术1: false, 基础点金术2: false, 基础点金术3: false, 制作魔法书: false, 制作星辰魔法书: false, 提炼月光精华: false, 提炼灵质: false, 提炼纯净精华: false, 炼制治疗药水: false, 炼制魔法药水: false, 附灵影之刃: false, 附灵守护者核心护符: false, 净化魂灵之刃: false, 净化诅咒香囊: false } , 探索: { 探索: false, 考古挖掘: false, 寻宝: false } , 提升自我: { 跑步: false, 举重: false, 读书: false, 战斗练习: false, 基础攻击练习: false, 基础防御练习: false, 游泳: false, 搏击训练: false, 抗打击训练: false, 瑜伽练习: false, "复习战斗知识-力量": false, "复习战斗知识-敏捷": false, "复习战斗知识-智力": false } } const settings = GM_getValue('refreshTaskSettings', { goldLimit: 7000, tasks: allTasks }); let isRunning = false; let checkInterval = null; let taskList = []; let startTask = GM_getValue('startTask', false) // 拖动相关变量 let isDragging = false; let offsetX, offsetY; let startX, startY; // 记录拖动开始位置 let isClick = true; // 判断是否是点击事件 // 窗口位置和状态 let windowPosition = { x: GM_getValue('windowX', 20) > window.innerWidth - 40 ? window.innerWidth - 40 : GM_getValue('windowX', 20), // 默认右上角 y: GM_getValue('windowY', 20) > window.innerHeight - 40 ? window.innerHeight - 40 : GM_getValue('windowY', 20), isMinimized: GM_getValue('isMinimized', false) // 默认不最小化 }; let taskListWsStatus = { wait: false, //是否等待 netx: false //是否下一条消息 }; // 全局WebSocket实例引用 let currentSocket = null; let userInfo = null; // ws用户信息 // 添加控制面板样式 GM_addStyle(` #retask-control-panel { position: fixed; top: ${windowPosition.y}px; left: ${windowPosition.x}px; right: 10px; z-index: 9999; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-width: 400px; font-family: Arial, sans-serif; overflow-y: auto; max-height: 90vh; display: ${windowPosition.isMinimized ? 'none' : 'block'}; } /* 检测Element UI的深色模式类 */ body.dark #retask-control-panel, html.dark #retask-control-panel, body.theme-dark #retask-control-panel, html.theme-dark #retask-control-panel { /* 深色模式样式 */ background: #18222c; /* 深色背景 */ color: #ffffff; /* 白色文字 */ } /* 修改后的全局样式,排除特定元素 */ #retask-control-panel *:not(.no-inherit) { color: inherit !important; background-color: transparent !important; } #retask-control-panel .retask-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #a4a4a4ff; cursor: move; /* 显示拖动光标 */ } #retask-control-panel .toolbar { display: flex; gap: 10px; } #retask-control-panel .toolbar-btn { background: none; border: none; color: #333333; font-size: 1.5rem; cursor: pointer; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background 0.2s; } #retask-control-panel .toolbar-btn:hover { background: rgba(255,255,255,0.1); } #retask-control-panel .minimize-btn::before { content: '−'; } .task-category { margin-bottom: 10px; padding: 8px; background: #f5f5f5; border-radius: 4px; } .category-header { display: flex; align-items: center; cursor: pointer; margin-bottom: 5px; } .category-header input { margin-right: 5px; } .category-title { font-weight: bold; } .category-toggle { margin-left: 5px; font-size: 12px; transition: transform 0.2s; } .category-content { padding-left: 15px; display: none; } .task-item { display: flex; align-items: center; margin-bottom: 5px; } .task-item input { margin-right: 5px; } .setting-group { display: flex; align-items: center; gap: 10px; margin-top: 10px; } #gold-limit { width: 80px; padding: 4px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; } #start-button { padding: 5px 12px; background: #409EFF; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; } #start-button:hover { background: #3688E6; } .category-expanded .category-toggle { transform: rotate(90deg); } .category-expanded .category-content { display: block; } .reTaskRestore-btn { position: fixed; background: rgba(154, 160, 183, 0.95); color: white; border: 1px solid #ffffffff; border-radius: 50%; top: ${windowPosition.y}px; left: ${windowPosition.x}px; width: 40px; height: 40px; font-size: 1.5rem; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 5px 15px rgba(0,0,0,0.3); transition: all 0.2s; z-index: 10001; display: ${windowPosition.isMinimized ? 'flex' : 'none'}; cursor: move; /* 显示拖动光标 */ } .reTaskRestore-btn:hover { background: rgba(40, 50, 90, 0.95); transform: scale(1.05); zIndex:999999; } .highlight-card { transition: all 0.3s ease-in-out !important; box-shadow: 0 0 0 3px #4caf50 !important; outline: none !important; /* 防止可能的outline冲突 */ border-radius: 2px !important; /* 可选,增加视觉效果 */ } `); // 创建控制面板 function createControlPanel() { const panel = document.createElement('div'); panel.id = 'retask-control-panel'; // 控制面板标题 const headerHTML = ` <div class="retask-header" id="refreshTask-header"> <div >日常任务刷新</div> <div class="toolbar"> <button class="toolbar-btn minimize-btn" id="retask-minimize-simulator"></button> </div> </div> <div class="setting-group"> <div> <label for="gold-limit" class="text-sm mr-2">金币限制:</label> <input type="number" id="gold-limit" value="7000" min="0"> </div> <div> <label for="gold-limit" class="text-sm mr-2">任务开始:</label> <input type="checkbox" id="startTask-checkbox" class="startTask-checkbox" ${startTask ? 'checked' : ""}> </div> <button id="start-button" class="no-inherit">开始</button> </div> `; // 从设置中获取任务列表并生成HTML let tasksHTML = ''; for (const [category, tasks] of Object.entries(allTasks)) { // 跳过空的分类 if (Object.keys(tasks).length === 0) continue; let taskItemsHTML = ''; for (const [taskName, isChecked] of Object.entries(tasks)) { taskItemsHTML += ` <div class="task-item"> <input type="checkbox" id="task-${taskName}" class="task-checkbox"> <label for="task-${taskName}">${taskName}</label> </div> `; } tasksHTML += ` <div class="task-category" data-category="${category}"> <div class="category-header"> <input type="checkbox" id="category-${category}" class="category-checkbox"> <span class="category-title">${category}</span> <span class="category-toggle">▶</span> </div> <div class="category-content"> ${taskItemsHTML} </div> </div> `; } panel.innerHTML = headerHTML + tasksHTML; document.body.appendChild(panel); // 添加还原按钮 const restoreBtn = document.createElement('button'); restoreBtn.className = 'reTaskRestore-btn'; restoreBtn.id = 'reTaskRestore-simulator'; restoreBtn.innerHTML = '📝'; document.body.appendChild(restoreBtn); //拖动 initDrag(panel) initRestoreBtnDrag(restoreBtn); // 添加最小化事件监听器 const minimizeBtn = document.getElementById('retask-minimize-simulator') minimizeBtn.addEventListener('click', minimizeSimulator); minimizeBtn.addEventListener('touchend', (e) => { e.preventDefault(); e.stopPropagation(); minimizeSimulator(); }); // 修改还原按钮事件,只在点击时恢复,拖动时不恢复 restoreBtn.addEventListener('click', function (e) { // 只有当没有发生拖动时才执行恢复操作 if (isClick) { restoreSimulator(); } // 重置点击状态 isClick = true; }); restoreBtn.addEventListener('touchend', (e) => { e.preventDefault(); e.stopPropagation(); // 只有当没有发生拖动时才执行恢复操作 if (isClick) { restoreSimulator(); } // 重置点击状态 isClick = true; }); // 绑定事件 document.getElementById('gold-limit').addEventListener('change', saveSettings); document.getElementById('start-button').addEventListener('click', startAutomation); document.getElementById('startTask-checkbox').addEventListener('change', () => { startTask = document.getElementById('startTask-checkbox').checked; GM_setValue('startTask', startTask); }); // 为分类添加展开/折叠功能 document.querySelectorAll('.category-header').forEach(header => { header.addEventListener('click', function (e) { if (e.target.type !== 'checkbox') { const categoryEl = this.parentElement; categoryEl.classList.toggle('category-expanded'); } }); }); // 为分类复选框添加全选/取消全选功能 document.querySelectorAll('.category-checkbox').forEach(checkbox => { checkbox.addEventListener('change', function () { const categoryEl = this.closest('.task-category'); const taskCheckboxes = categoryEl.querySelectorAll('.task-checkbox'); taskCheckboxes.forEach(taskCheckbox => { taskCheckbox.checked = this.checked; }); saveSettings(); }); }); // 为任务复选框添加事件监听 document.querySelectorAll('.task-checkbox').forEach(checkbox => { // 任务勾选 const categoryEl = checkbox.closest('.task-category'); const categoryName = categoryEl.dataset.category; checkbox.addEventListener('change', () => { saveSettings() document.getElementById(`category-${categoryName}`).checked = Object.values(settings.tasks[categoryName]).every(isChecked => isChecked); }); }); // // 默认展开第一个分类 // const firstCategory = document.querySelector('.task-category'); // if (firstCategory) { // firstCategory.classList.add('category-expanded'); // } // 加载保存的设置 loadSettings(); } // 最小化模拟器 function minimizeSimulator(e) { const container = document.getElementById('retask-control-panel'); const restoreBtn = document.getElementById('reTaskRestore-simulator'); const minimizeBtn = document.getElementById('retask-minimize-simulator'); // 获取最小化按钮的位置 const minimizeBtnRect = minimizeBtn.getBoundingClientRect(); // 设置还原按钮的位置为最小化按钮的位置 restoreBtn.style.left = `${windowPosition.x}px`; // 居中调整 restoreBtn.style.top = `${windowPosition.y}px`; // 居中调整 container.style.display = 'none'; restoreBtn.style.display = 'flex'; windowPosition.isMinimized = true; saveWindowState(); } // 还原 function restoreSimulator() { const container = document.getElementById('retask-control-panel'); const restoreBtn = document.getElementById('reTaskRestore-simulator'); container.style.display = 'block'; restoreBtn.style.display = 'none'; windowPosition.isMinimized = false; saveWindowState(); } // 保存设置到本地存储 function saveSettings() { settings['goldLimit'] = document.getElementById('gold-limit').value // 收集所有任务的勾选状态 document.querySelectorAll('.task-category').forEach(categoryEl => { const categoryName = categoryEl.dataset.category; settings.tasks[categoryName] = {}; categoryEl.querySelectorAll('.task-item input').forEach(taskInput => { const taskName = taskInput.id.replace('task-', ''); settings.tasks[categoryName][taskName] = taskInput.checked; }); }); GM_setValue('refreshTaskSettings', settings); } // 从本地存储加载设置 function loadSettings() { document.getElementById('gold-limit').value = settings.goldLimit; // 恢复任务勾选状态 document.querySelectorAll('.task-category').forEach(categoryEl => { const categoryName = categoryEl.dataset.category; //类型的勾选 document.getElementById(`category-${categoryName}`).checked = settings.tasks[categoryName] ? Object.values(settings.tasks[categoryName]).every(isChecked => isChecked) : false; categoryEl.querySelectorAll('.task-item input').forEach(taskInput => { const taskName = taskInput.id.replace('task-', ''); if (settings.tasks[categoryName] && settings.tasks[categoryName].hasOwnProperty(taskName)) { taskInput.checked = settings.tasks[categoryName][taskName]; } }); }); console.log('刷新任务设置已加载'); } // 开始自动化操作 function startAutomation() { const startButton = document.getElementById('start-button') if (isRunning) { clearInterval(checkInterval); toStartStatus() } else { // 开始自动化 isRunning = true; startButton.textContent = '停止'; startButton.classList.remove('start'); startButton.classList.add('stop'); startButton.style.backgroundColor = 'red'; sendGetTaskListMsg() taskListWsStatus.wait = true; setTimeout(() => queueMicrotask(startRefishTask), 100); } } async function startRefishTask() { console.log('===== 开始执行自动化检查 ====='); //等待返回消息 const taskListCallback = await waitTaskListCallback() if (!taskListCallback) { toStartStatus() console.error('未找到任务列表!'); return; } // 获取设置值 const goldLimit = settings.goldLimit; //console.log(`当前设置: 金币限制=${goldLimit}`); // 查找游戏卡片 let lastIndex = 0; console.log(`找到 ${taskList.length} 个游戏卡片,开始分析...`); while (isRunning && !isEmptyOrNonNumber(lastIndex) && lastIndex < taskList.length) { if (taskListWsStatus.wait) { //如果是wait 就等待吧 await waitTaskListCallback() await sleep(1000) } console.log(`正在处理第 ${lastIndex + 1} 个游戏卡片...`); try { lastIndex = await handleTaskList(lastIndex); } catch (e) { console.error('处理失败', e) lastIndex++; } } if (isRunning && startTask) { console.log('开始执行任务...'); // 最大次数 const taskMaxTimes = {} const selectedTasks = settings.tasks taskList.forEach((card, index) => { const { category, subCategory, count, unit } = getCardInfo(card); if (count > 0 && selectedTasks[category] && selectedTasks[category][subCategory]) { const key = category + '-' + subCategory; const info = taskMaxTimes[key] if (!info || info[0] < count) { taskMaxTimes[key] = [count, card] } } }); // 点击开始 for (let [key, value] of Object.entries(taskMaxTimes)) { console.log('开始执行任务:', value[1]['title']); if (!isRunning) break; sendStartTaskMessage(value[1]) await sleep(2000) } } else { console.log('任务不执行'); } toStartStatus() console.log('刷新任务结束'); } function toStartStatus() { isRunning = false; const startButton = document.getElementById('start-button') startButton.textContent = '开始'; startButton.classList.remove('stop'); startButton.classList.add('start'); startButton.style.backgroundColor = '#3688E6'; } function getCardInfo(card) { const titleText = card.title // 解析标题 const titleParts = titleText.match(/^(\S+):([^0-9]+?)(\d+)(\D+)$/); if (!titleParts) { console.warn('标题格式不符合预期,无法解析'); return; } const category = titleParts[1].trim().replace(" ", ''); const subCategory = titleParts[2].trim().replace(" ", ''); const count = card.target.count - card.target.current const unit = titleParts[4].trim().replace(" ", ''); return { category, subCategory, count, unit }; } async function handleTaskList(nextIndex) { // 处理每个卡片 const card = taskList[nextIndex] if (!card) { console.log('所有卡片已处理完毕'); return; } const btn = document.querySelector('.el-menu-item:has(.iconify.i-material-symbols\\:format-list-bulleted)') if (btn && btn.classList.contains('is-active')) { // 高亮卡片 const cards = document.querySelectorAll('.el-card.is-always-shadow.mb-4'); if (cards.length > 0) { cards.forEach(card => { card.classList.remove('highlight-card'); }); cards[nextIndex].classList.add('highlight-card'); } } const { category, subCategory, count, unit } = getCardInfo(card) console.log(`处理卡片: ${card.title}`); if (count <= 0) { console.log('任务已完成,跳过处理'); return nextIndex + 1; } const goldLimit = settings.goldLimit const selectedTasks = settings.tasks // 检查是否匹配选中项 if (selectedTasks[category] && selectedTasks[category][subCategory]) { console.log('符合条件,跳过处理'); return nextIndex + 1; } // 获取金币数量 const goldAmount = (card.rerollCount + 1) * 250; //console.log(`当前金币数量: ${goldAmount}, 限制: ${settings.goldLimit}`); // 检查金币是否小于限制并点击重随 if (goldAmount < goldLimit) { console.log(`所需金币(${goldAmount} < ${goldLimit}),执行重随操作`); sendRerollMsg(card.uuid) taskListWsStatus.wait = true console.log('重随按钮已点击'); return nextIndex; } else { console.log(`金币超过限制(${goldAmount} ≥ ${goldLimit}),无需操作`); return nextIndex + 1; } } async function sleep(time) { await new Promise(resolve => setTimeout(resolve, time)); } function isEmptyOrNonNumber(value) { // 或者使用 isNaN(value) 但要注意其对字符串的处理 return value === null || value === undefined || value === '' || Number.isNaN(value); } // 等待任务列表返回 async function waitTaskListCallback() { const now = Date.now(); while (Date.now() - now < waitForMsg && taskListWsStatus.wait) { await sleep(100); } if (taskListWsStatus.wait) { taskListWsStatus.wait = false; taskListWsStatus.netx = false; console.log('任务列表返回超时'); return false; } console.log('任务列表返回成功'); taskListWsStatus.wait = false; taskListWsStatus.netx = false; return true }; function handleTaskListMessage(date) { if (taskListWsStatus.wait) { taskList = date.data || []; taskListWsStatus.wait = false; } } // 修改初始化拖动功能函数,添加触摸事件支持 function initDrag(element) { const header = document.getElementById('refreshTask-header'); // 鼠标事件 header.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); // 触摸事件 header.addEventListener('touchstart', startDrag, { passive: false }); document.addEventListener('touchmove', drag, { passive: false }); document.addEventListener('touchend', stopDrag); function startDrag(e) { // 阻止默认行为,防止页面滚动 e.preventDefault(); isDragging = true; // 获取触摸位置 const touch = e.type.includes('mouse') ? e : e.touches[0]; offsetX = touch.clientX - element.getBoundingClientRect().left; offsetY = touch.clientY - element.getBoundingClientRect().top; // 提高z-index,确保拖动时在最上层 element.style.zIndex = 10001; } function drag(e) { if (!isDragging) return; // 阻止默认行为,防止页面滚动 e.preventDefault(); // 获取触摸位置 const touch = e.type.includes('mouse') ? e : e.touches[0]; // 计算新位置 let newX = touch.clientX - offsetX; let newY = touch.clientY - offsetY; // 限制在视口内 newX = Math.max(0, Math.min(newX, window.innerWidth - 40)); newY = Math.max(0, Math.min(newY, window.innerHeight - 40)); // 更新位置 element.style.left = `${newX}px`; element.style.top = `${newY}px`; // 保存位置 windowPosition.x = newX; windowPosition.y = newY; } function stopDrag() { if (!isDragging) return; isDragging = false; element.style.zIndex = 10000; // 保存窗口位置 saveWindowState(); } } // 同样修改还原按钮的拖动功能,添加触摸支持 function initRestoreBtnDrag(element) { // 鼠标事件 element.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); // 触摸事件 element.addEventListener('touchstart', startDrag, { passive: false }); document.addEventListener('touchmove', drag, { passive: false }); document.addEventListener('touchend', stopDrag); function startDrag(e) { // 阻止默认行为,防止页面滚动 e.preventDefault(); isDragging = true; // 获取触摸位置 const touch = e.type.includes('mouse') ? e : e.touches[0]; offsetX = touch.clientX - element.getBoundingClientRect().left; offsetY = touch.clientY - element.getBoundingClientRect().top; startX = touch.clientX; startY = touch.clientY; // 提高z-index,确保拖动时在最上层 element.style.zIndex = 10002; // 阻止事件冒泡,防止触发点击事件 e.stopPropagation(); } function drag(e) { if (!isDragging) return; // 阻止默认行为,防止页面滚动 e.preventDefault(); // 获取触摸位置 const touch = e.type.includes('mouse') ? e : e.touches[0]; // 计算新位置 let newX = touch.clientX - offsetX; let newY = touch.clientY - offsetY; // 限制在视口内 newX = Math.max(0, Math.min(newX, window.innerWidth - element.offsetWidth)); newY = Math.max(0, Math.min(newY, window.innerHeight - element.offsetHeight)); // 更新位置 element.style.left = `${newX}px`; element.style.top = `${newY}px`; element.style.right = 'auto'; // 清除右侧定位 // 保存还原按钮位置 windowPosition.restoreBtnX = newX; windowPosition.restoreBtnY = newY; // 如果拖动距离超过阈值,则认为不是点击事件 if (Math.abs(touch.clientX - startX) > 5 || Math.abs(touch.clientY - startY) > 5) { isClick = false; } } function stopDrag() { if (!isDragging) return; isDragging = false; element.style.zIndex = 10001; // 保存还原按钮位置 saveWindowState(); } } // 保存窗口状态 function saveWindowState() { GM_setValue('windowX', windowPosition.x); GM_setValue('windowY', windowPosition.y); GM_setValue('isMinimized', windowPosition.isMinimized); } // ================================================================================= // == 核心:WebSocket 拦截器 (原型链拦截) // ================================================================================= //console.log('[WS工具] 准备部署WebSocket拦截器'); // 拦截send方法 const originalSend = WebSocket.prototype.send; WebSocket.prototype.send = function (data) { currentSocket = this; handleSendMessage(data); return originalSend.apply(this, arguments); }; // 拦截onmessage事件 const onmessageDescriptor = Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onmessage'); if (onmessageDescriptor) { Object.defineProperty(WebSocket.prototype, 'onmessage', { ...onmessageDescriptor, set: function (callback) { const wsInstance = this; currentSocket = this; // 包装回调函数以拦截消息 const wrappedCallback = (event) => { handleReceivedMessage(event.data, wsInstance); if (typeof callback === 'function') { callback.call(wsInstance, event); } }; onmessageDescriptor.set.call(this, wrappedCallback); } }); } console.log('[任务刷新工具] WebSocket拦截器部署完成'); // ================================================================================= // —— 消息处理核心函数 —— /** * 处理发送的WebSocket消息 * @param {string|ArrayBuffer} data - 发送的消息数据 */ function handleSendMessage(data) { if (!userInfo) { userInfo = getUserInfo(data); } // 可在此处添加发送消息的自定义处理逻辑 } // —— 解析用户数据 —— function getUserInfo(data) { try { if (typeof data === 'string' && data.length > 2) { const payload = JSON.parse(data.substring(2, data.length)); if (payload[1] && payload[1]['user'] && payload[1]['user']['name']) { return payload[1]['user']; } } } catch (e) { // 解析失败,忽略 } return null; } /** * 处理接收的WebSocket消息 * @param {string|ArrayBuffer} messageData - 接收的消息数据 * @param {WebSocket} ws - WebSocket实例 */ function handleReceivedMessage(messageData, ws) { if (messageData instanceof ArrayBuffer) { try { // 检测压缩格式并解压 const format = detectCompression(messageData); const text = pako.inflate(new Uint8Array(messageData), { to: 'string' }); let parsed = ''; try { parsed = JSON.parse(text); //console.log('%c[WS 已接收]', 'color: #4CAF50; font-weight: bold;', `(已解压 ${format})`, parsed); } catch { parsed = text; //console.log('%c[WS 已接收]', 'color: #4CAF50; font-weight: bold;', `(已解压 ${format}, 非JSON)`, text); } if (taskListWsStatus.netx) { taskListWsStatus.netx = false; setTimeout(() => { handleTaskListMessage(parsed); }, 1); } } catch (err) { //console.error('%c[WS错误]', 'color: #f44336;', '消息解压失败:', err); } } else { // 文本消息直接打印 //console.log('%c[WS 已接收]', 'color: #4CAF50; font-weight: bold;', '(文本消息)', messageData); if (messageData.includes('quest:list') && taskListWsStatus.wait) { taskListWsStatus.netx = true; } else if (messageData.includes('quest:reroll:error')) { taskListWsStatus.wait = false; console.log('任务刷新失败'); } } } /** * 检测数据压缩格式 * @param {ArrayBuffer} buf - 二进制数据 * @returns {string} 压缩格式 ('gzip'|'zlib'|'deflate') */ function detectCompression(buf) { const b = new Uint8Array(buf); if (b.length < 2) return 'deflate'; if (b[0] === 0x1f && b[1] === 0x8b) return 'gzip'; if (b[0] === 0x78 && (((b[0] << 8) | b[1]) % 31) === 0) return 'zlib'; return 'deflate'; } function sendGetTaskListMsg() { let msg = `42["quest:list",{"user":${JSON.stringify(userInfo)},"data":null}]` sendCustomWsMessage(msg) } function sendRerollMsg(questUuid) { let msg = `42["quest:reroll",{"user":${JSON.stringify(userInfo)},"data":{"questUuid":"${questUuid}"}}]` sendCustomWsMessage(msg) } function sendStartTaskMessage(task) { const msg = `42["task:immediatelyStart",{"user":${JSON.stringify(userInfo)},"data":{"actionId":"${task.target.actionId}","repeatCount":${task.target.count},"currentRepeat":${task.target.current},"createTime":${Date.now()}}}]` sendCustomWsMessage(msg) } // —— 自定义发送消息接口 —— /** * 发送自定义WebSocket消息 * @param {string|object} data - 要发送的消息数据(对象会自动转为JSON字符串) * @param {string} [type='custom'] - 消息类型标识 * @returns {boolean} 是否发送成功 */ function sendCustomWsMessage(message) { if (!currentSocket || currentSocket.readyState !== WebSocket.OPEN) { console.error('%c[WS发送失败]', 'color: #f44336;', 'WebSocket未连接或已关闭'); return false; } try { currentSocket.send(message); //console.log('%c[自定义发送]', 'color: #FF9800; font-weight: bold;', message); return true; } catch (error) { console.error('%c[自定义发送失败]', 'color: #f44336;', error); return false; } }; // 等待页面加载完成 window.addEventListener('load', function () { setTimeout(() => { createControlPanel() }, 1000); }) })();