// ==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);
})
})();