From 4055232a2aa4ba1687dc3a0819f1556d7a3a03f2 Mon Sep 17 00:00:00 2001 From: 5117600049 <511760049@qq.com> Date: Mon, 6 Oct 2025 00:19:43 +0800 Subject: [PATCH] =?UTF-8?q?JS=E8=84=9A=E6=9C=AC=E8=B4=AD=E4=B9=B0=E7=8B=97?= =?UTF-8?q?=E7=B2=AE=E6=9B=B4=E6=96=B0=20(#2084)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update settings.json * Update manifest.json * Update main.js * Update 挪德卡莱购买狗粮.json * Update 璃月购买狗粮1.json * Update 璃月购买狗粮2.json * Add files via upload --- repo/js/PurchaseArtifacts/README.md | 8 + .../assets/挪德卡莱购买狗粮.json | 10 +- .../assets/璃月购买狗粮1.json | 13 +- .../assets/璃月购买狗粮2.json | 27 +- repo/js/PurchaseArtifacts/main.js | 250 +++++++++++++++++- repo/js/PurchaseArtifacts/manifest.json | 6 +- repo/js/PurchaseArtifacts/settings.json | 58 +++- 7 files changed, 331 insertions(+), 41 deletions(-) create mode 100644 repo/js/PurchaseArtifacts/README.md diff --git a/repo/js/PurchaseArtifacts/README.md b/repo/js/PurchaseArtifacts/README.md new file mode 100644 index 000000000..111f768d0 --- /dev/null +++ b/repo/js/PurchaseArtifacts/README.md @@ -0,0 +1,8 @@ +商店狗粮周四凌晨4点刷新,可以自行选择想买的商人(默认全选) + +根据体力代换计算,买完8个商人相当于用价值26体力的摩拉换取了43体力的狗粮, + +所以真的不建议将狗粮分解成摩拉,如果你真的很缺。 + +此外脚本完整运行后会记录本次的时间,没有刷新时运行脚本会自动跳过。 + diff --git a/repo/js/PurchaseArtifacts/assets/挪德卡莱购买狗粮.json b/repo/js/PurchaseArtifacts/assets/挪德卡莱购买狗粮.json index c5cb9d61a..da9a6603c 100644 --- a/repo/js/PurchaseArtifacts/assets/挪德卡莱购买狗粮.json +++ b/repo/js/PurchaseArtifacts/assets/挪德卡莱购买狗粮.json @@ -13,7 +13,7 @@ "x": 9367.1484375, "y": 1435.6484375, "type": "teleport", - "move_mode": "run", + "move_mode": "dash", "action": "" }, { @@ -21,7 +21,7 @@ "x": 9396.775390625, "y": 1486.4755859375, "type": "path", - "move_mode": "run", + "move_mode": "dash", "action": "" }, { @@ -29,7 +29,7 @@ "x": 9381.6875, "y": 1561.802734375, "type": "path", - "move_mode": "run", + "move_mode": "dash", "action": "" }, { @@ -37,8 +37,8 @@ "x": 9389.0546, "y": 1566.3916, "type": "target", - "move_mode": "walk", + "move_mode": "dash", "action": "" } ] -} \ No newline at end of file +} diff --git a/repo/js/PurchaseArtifacts/assets/璃月购买狗粮1.json b/repo/js/PurchaseArtifacts/assets/璃月购买狗粮1.json index 6ee2b5f52..e27926bab 100644 --- a/repo/js/PurchaseArtifacts/assets/璃月购买狗粮1.json +++ b/repo/js/PurchaseArtifacts/assets/璃月购买狗粮1.json @@ -18,16 +18,7 @@ "action_params": "" }, { - "action": "set_time", - "action_params": "19:00", "id": 2, - "move_mode": "walk", - "type": "path", - "x": 366.74609375, - "y": -502.52587890625 - }, - { - "id": 3, "x": 360.026171875, "y": -491.16474609375, "type": "path", @@ -36,7 +27,7 @@ "action_params": "" }, { - "id": 4, + "id": 3, "x": 340.1712890625, "y": -471.7400390625, "type": "target", @@ -45,4 +36,4 @@ "action_params": "" }, ] -} \ No newline at end of file +} diff --git a/repo/js/PurchaseArtifacts/assets/璃月购买狗粮2.json b/repo/js/PurchaseArtifacts/assets/璃月购买狗粮2.json index cf760f06e..be709180a 100644 --- a/repo/js/PurchaseArtifacts/assets/璃月购买狗粮2.json +++ b/repo/js/PurchaseArtifacts/assets/璃月购买狗粮2.json @@ -19,24 +19,33 @@ }, { "id": 2, - "x": 378.5673828125, - "y": -532.35009765625, - "type": "path", + "action": "set_time", + "action_params": "19:00", "move_mode": "walk", - "action": "", - "action_params": "" + "type": "path", + "x": 366.748046875, + "y": -502.51904296875, }, { "id": 3, - "x": 330.466796875, - "y": -585.69921875, + "x": 378.5673828125, + "y": -532.35009765625, "type": "path", - "move_mode": "run", + "move_mode": "dash", "action": "", "action_params": "" }, { "id": 4, + "x": 330.466796875, + "y": -585.69921875, + "type": "path", + "move_mode": "dash", + "action": "", + "action_params": "" + }, + { + "id": 5, "x": 340.091796875, "y": -593.6611328125, "type": "path", @@ -45,4 +54,4 @@ "action_params": "" } ] -} \ No newline at end of file +} diff --git a/repo/js/PurchaseArtifacts/main.js b/repo/js/PurchaseArtifacts/main.js index 451cc307c..abdc74f45 100644 --- a/repo/js/PurchaseArtifacts/main.js +++ b/repo/js/PurchaseArtifacts/main.js @@ -5,8 +5,221 @@ await pathingScript.runFile(filePath); } -//读取配置 -let holdingState = settings.holdingState ?? 0; +/** + * 在指定区域内查找并点击指定文字 + * @param {string} targetText - 要点击的目标文字 + * @param {number} x - 识别区域的左上角X坐标 + * @param {number} y - 识别区域的左上角Y坐标 + * @param {number} width - 识别区域的宽度 + * @param {number} height - 识别区域的高度 + * @param {object} options - 可选参数 + * @param {boolean} options.trimText - 是否对OCR结果进行trim处理,默认true + * @param {boolean} options.clickCenter - 是否点击文字区域中心,默认true + * @param {number} options.retryCount - 重试次数,默认1(不重试) + * @param {number} options.retryInterval - 重试间隔(毫秒),默认500 + * @returns {Promise} 是否找到并点击了文字 + */ +async function clickTextInRegion(targetText, x, y, width, height, options = {}) { + const { + trimText = true, + clickCenter = true, + retryCount = 1, + retryInterval = 500 + } = options; + + for (let attempt = 0; attempt <= retryCount; attempt++) { + try { + // 获取游戏区域截图 + const captureRegion = captureGameRegion(); + + // 创建OCR识别对象,限定识别区域 + const ocrRo = RecognitionObject.ocr(x, y, width, height); + + // 在限定区域内进行OCR识别 + const results = captureRegion.findMulti(ocrRo); + + // 遍历OCR结果 + for (let i = 0; i < results.count; i++) { + const res = results[i]; + let detectedText = res.text; + + // 可选:去除前后空白字符 + if (trimText) { + detectedText = detectedText.trim(); + } + + // 检查是否匹配目标文字 + if (detectedText === targetText) { + log.info(`找到目标文字: "${targetText}",位置: (${res.x}, ${res.y}, ${res.width}, ${res.height})`); + + if (clickCenter) { + // 点击文字区域中心 + await sleep(200); + keyDown("VK_LMENU"); + await sleep(500); + res.click(); + await sleep(100); + keyUp("VK_LMENU"); + log.info(`已点击文字中心: "${targetText}"`); + + } else { + // 点击文字区域的左上角 + res.clickTo(0, 0); + log.info(`已点击文字偏移位置: "${targetText}"`); + } + + + return true; + } + } + + // 如果当前尝试未找到,且还有重试机会,则等待后重试 + if (attempt < retryCount) { + log.info(`第${attempt + 1}次尝试未找到文字"${targetText}",${retryInterval}ms后重试...`); + await sleep(retryInterval); + } + } catch (error) { + log.error(`点击文字"${targetText}"时发生错误: ${error.message}`); + if (attempt < retryCount) { + await sleep(retryInterval); + } + } + } + + log.info(`未找到文字: "${targetText}",已尝试${retryCount + 1}次`); + return false; +} + + + + +/** + * 判断任务是否已刷新 + * @param {string} filePath - 存储最后完成时间的文件路径 + * @param {object} options - 配置选项 + * @param {string} [options.refreshType] - 刷新类型: 'hourly'|'daily'|'weekly'|'monthly'|'custom' + * @param {number} [options.customHours] - 自定义小时数(用于'custom'类型) + * @param {number} [options.dailyHour=4] - 每日刷新的小时(0-23) + * @param {number} [options.weeklyDay=1] - 每周刷新的星期(0-6, 0是周日) + * @param {number} [options.weeklyHour=4] - 每周刷新的小时(0-23) + * @param {number} [options.monthlyDay=1] - 每月刷新的日期(1-31) + * @param {number} [options.monthlyHour=4] - 每月刷新的小时(0-23) + * @returns {Promise} - 是否已刷新 + */ +async function isTaskRefreshed(filePath, options = {}) { + const { + refreshType = 'hourly', // 默认每小时刷新 + customHours = 24, // 自定义刷新小时数默认24 + dailyHour = 4, // 每日刷新默认凌晨4点 + weeklyDay = 1, // 每周刷新默认周一(0是周日) + weeklyHour = 4, // 每周刷新默认凌晨4点 + monthlyDay = 1, // 每月刷新默认第1天 + monthlyHour = 4 // 每月刷新默认凌晨4点 + } = options; + + try { + // 读取文件内容 + let content = await file.readText(filePath); + const lastTime = new Date(content); + const nowTime = new Date(); + + + let shouldRefresh = false; + + + switch (refreshType) { + case 'hourly': // 每小时刷新 + shouldRefresh = (nowTime - lastTime) >= 3600 * 1000; + break; + + case 'daily': // 每天固定时间刷新 + // 检查是否已经过了当天的刷新时间 + const todayRefresh = new Date(nowTime); + todayRefresh.setHours(dailyHour, 0, 0, 0); + + // 如果当前时间已经过了今天的刷新时间,检查上次完成时间是否在今天刷新之前 + if (nowTime >= todayRefresh) { + shouldRefresh = lastTime < todayRefresh; + } else { + // 否则检查上次完成时间是否在昨天刷新之前 + const yesterdayRefresh = new Date(todayRefresh); + yesterdayRefresh.setDate(yesterdayRefresh.getDate() - 1); + shouldRefresh = lastTime < yesterdayRefresh; + } + break; + + case 'weekly': // 每周固定时间刷新 + // 获取本周的刷新时间 + const thisWeekRefresh = new Date(nowTime); + // 计算与本周指定星期几的差值 + const dayDiff = (thisWeekRefresh.getDay() - weeklyDay + 7) % 7; + thisWeekRefresh.setDate(thisWeekRefresh.getDate() - dayDiff); + thisWeekRefresh.setHours(weeklyHour, 0, 0, 0); + + // 如果当前时间已经过了本周的刷新时间 + if (nowTime >= thisWeekRefresh) { + shouldRefresh = lastTime < thisWeekRefresh; + } else { + // 否则检查上次完成时间是否在上周刷新之前 + const lastWeekRefresh = new Date(thisWeekRefresh); + lastWeekRefresh.setDate(lastWeekRefresh.getDate() - 7); + shouldRefresh = lastTime < lastWeekRefresh; + } + break; + + case 'monthly': // 每月固定时间刷新 + // 获取本月的刷新时间 + const thisMonthRefresh = new Date(nowTime); + // 设置为本月指定日期的凌晨 + thisMonthRefresh.setDate(monthlyDay); + thisMonthRefresh.setHours(monthlyHour, 0, 0, 0); + + // 如果当前时间已经过了本月的刷新时间 + if (nowTime >= thisMonthRefresh) { + shouldRefresh = lastTime < thisMonthRefresh; + } else { + // 否则检查上次完成时间是否在上月刷新之前 + const lastMonthRefresh = new Date(thisMonthRefresh); + lastMonthRefresh.setMonth(lastMonthRefresh.getMonth() - 1); + shouldRefresh = lastTime < lastMonthRefresh; + } + break; + + case 'custom': // 自定义小时数刷新 + shouldRefresh = (nowTime - lastTime) >= customHours * 3600 * 1000; + break; + + default: + throw new Error(`未知的刷新类型: ${refreshType}`); + } + + // 如果文件内容无效或不存在,视为需要刷新 + if (!content || isNaN(lastTime.getTime())) { + await file.writeText(filePath, ''); + shouldRefresh = true; + } + + if (shouldRefresh) { + notification.send(`购买狗粮已经刷新,执行脚本`); + + + return true; + } else { + log.info(`购买狗粮未刷新`); + return false; + } + + } catch (error) { + // 如果文件不存在,创建新文件并返回true(视为需要刷新) + const createResult = await file.writeText(filePath, ''); + if (createResult) { + log.info("创建新时间记录文件成功,执行脚本"); + return true; + } + else throw new Error(`创建新文件失败`); + } +} + //基本购买流程 async function Shopping() { @@ -22,12 +235,18 @@ let holdingState = settings.holdingState ?? 0; keyPress("ESCAPE"); await sleep(2000); } + async function main() { + if(settings.select1){ await Purchase('蒙德购买狗粮'); await Shopping(); + } + if(settings.select2){ await Purchase('璃月购买狗粮1'); await Shopping(); + } + if(settings.select3){ await Purchase('璃月购买狗粮2'); await sleep(1000); keyDown("w"); @@ -36,7 +255,7 @@ let holdingState = settings.holdingState ?? 0; await sleep(1000); keyPress("F"); await sleep(1200); keyPress("F"); await sleep(1800); - if (holdingState) keyPress("s"); + await clickTextInRegion("我想买些古董。", 1264, 532, 370, 160); await sleep(500); keyPress("F"); await sleep(1200); keyPress("F"); await sleep(1800); @@ -46,7 +265,9 @@ let holdingState = settings.holdingState ?? 0; click(1690, 1020); await sleep(200); // 点击空白处 } keyPress("ESCAPE"); await sleep(2000); + } + if(settings.select4){ await Purchase('稻妻购买狗粮'); await sleep(1500); for (let j = 0; j < 5; j++) { @@ -59,20 +280,41 @@ let holdingState = settings.holdingState ?? 0; click(1690, 1020); await sleep(200); // 点击空白处 } keyPress("ESCAPE"); await sleep(2000); - + } + if(settings.select5){ await Purchase('须弥购买狗粮'); await Shopping(); + } + if(settings.select6){ await Purchase('枫丹购买狗粮'); await Shopping(); + } + if(settings.select7){ await Purchase('纳塔购买狗粮'); await sleep(1000); keyDown("a"); await sleep(500); keyUp("a"); await Shopping(); + } + if(settings.select8){ await Purchase('挪德卡莱购买狗粮'); await Shopping(); + } + + await file.writeText("assets/weekly.txt", new Date().toISOString()); +} + +//每周四4点刷新 +if( await isTaskRefreshed("assets/weekly.txt", { + refreshType: 'weekly', + weeklyDay: 4, // 周一 + weeklyHour: 4 // 凌晨4点 +})){ +await main(); +} + })(); diff --git a/repo/js/PurchaseArtifacts/manifest.json b/repo/js/PurchaseArtifacts/manifest.json index d826e211f..04740e0d2 100644 --- a/repo/js/PurchaseArtifacts/manifest.json +++ b/repo/js/PurchaseArtifacts/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 1, "name": "自动购买狗粮(8点位)", - "version": "1.1.3", + "version": "2.0", "bgi_version": "0.50", - "description": "狗粮周3刷新,价值23体力的摩拉换38体力的狗粮,血赚。龙骨花凝珠没出售,需额外设置。强烈建议使用固定队伍(双风成女行走)", + "description": "自动购买狗粮", "authors": [ { "name": "柒叶子", @@ -16,4 +16,4 @@ ], "settings_ui": "settings.json", "main": "main.js" -} \ No newline at end of file +} diff --git a/repo/js/PurchaseArtifacts/settings.json b/repo/js/PurchaseArtifacts/settings.json index 5ab4f7660..d614be8af 100644 --- a/repo/js/PurchaseArtifacts/settings.json +++ b/repo/js/PurchaseArtifacts/settings.json @@ -1,11 +1,51 @@ [ { - "name": "holdingState", - "type": "select", - "label": "(有无龙骨花凝珠?默认NO)", - "options": [ - "YES", - "NO" - ] - } -] \ No newline at end of file + "name": "select1", + "type": "checkbox", + "label": "蒙德商人", + "default": true + }, + { + "name": "select2", + "type": "checkbox", + "label": "璃月商人1", + "default": true + }, + { + "name": "select3", + "type": "checkbox", + "label": "璃月商人2", + "default": true + }, + { + "name": "select4", + "type": "checkbox", + "label": "稻妻商人", + "default": true + }, + { + "name": "select5", + "type": "checkbox", + "label": "须弥商人", + "default": true + }, + { + "name": "select6", + "type": "checkbox", + "label": "枫丹商人", + "default": true + }, + { + "name": "select7", + "type": "checkbox", + "label": "纳塔商人", + "default": true + }, + { + "name": "select8", + "type": "checkbox", + "label": "挪德卡莱商人", + "default": true + }, + +]