diff --git a/repo/js/AutoMonday/README.md b/repo/js/AutoMonday/README.md index a6e6259de..5530fa16e 100644 --- a/repo/js/AutoMonday/README.md +++ b/repo/js/AutoMonday/README.md @@ -47,6 +47,11 @@ 2. 感谢茶包大佬,本脚本的框架、逻辑、以及部分代码都是从这位大佬的代码中获取的,甚至创作灵感也是(茶包老师还是太全面了~) --------------------------------------------------------------------------------------------------------------------------------- ## 更新日志 +### 1.1(2025.09.24) +1. 修复了爱可菲在做菜过程中偶现的被电死的bug(做菜做到一半人没了?别担心,后续不会再有这种情况了!!!) +2. 完善了拦截背包过期物品的功能,不用每次打开背包之后再重复点击了! +3. 添加了cd验证功能,无需每次运行再开地图识别,cd依靠Uid存在,相关数据储存留存在本地,不用担心id泄露风险,同时,保留了原来的诱捕完成提示作为二级验证,识别有误也不用担心! +4. 进一步修改了cd一致的逻辑:当诱捕装置刷新但质变仪未刷新时不会干等质变仪刷新,而是会继续执行,等诱捕装置下次刷新之后再三者一起使用。 ### 1.0(2025.09.20) 1. 酱酱!“全自动周一(AutoMonday) V1.0”闪亮登场! 1. 你还在为每周的爱可菲做菜而痛苦吗?你每周的质变仪又忘记用了吗?你每周的晶蝶诱捕装置又忘记收取了吗? diff --git a/repo/js/AutoMonday/assets/全自动爱可菲.json b/repo/js/AutoMonday/assets/全自动爱可菲.json index 763750584..fa1f46be6 100644 --- a/repo/js/AutoMonday/assets/全自动爱可菲.json +++ b/repo/js/AutoMonday/assets/全自动爱可菲.json @@ -21,10 +21,10 @@ } ], "bgi_version": "0.45.0", - "description": "基于电气水晶的全自动爱可菲", + "description": "基于电气水晶的全自动爱可菲做菜", "enable_monster_loot_split": false, "items": [], - "last_modified_time": 1758269208975, + "last_modified_time": 1758611009216, "map_match_method": "", "map_name": "Teyvat", "name": "全自动爱可菲", @@ -36,6 +36,7 @@ "positions": [ { "action": "", + "action_params": "", "id": 1, "move_mode": "walk", "point_ext_params": { @@ -55,7 +56,7 @@ }, { "action": "combat_script", - "action_params": "爱可菲 e(hold)", + "action_params": "爱可菲 e(hold),s(0.75)", "id": 2, "move_mode": "walk", "point_ext_params": { diff --git a/repo/js/AutoMonday/assets/晶蝶诱捕装置.json b/repo/js/AutoMonday/assets/晶蝶诱捕装置.json index 3bec7f78b..1c74489b2 100644 --- a/repo/js/AutoMonday/assets/晶蝶诱捕装置.json +++ b/repo/js/AutoMonday/assets/晶蝶诱捕装置.json @@ -7,15 +7,15 @@ } ], "bgi_version": "0.45.0", - "description": "", + "description": "诱捕位置:枫丹合成台旁边巷子口", "enable_monster_loot_split": false, - "last_modified_time": 1756715619864, - "map_match_method": "SIFT", + "last_modified_time": 1758611261871, + "map_match_method": "", "map_name": "Teyvat", "name": "晶蝶诱捕装置", "tags": [], "type": "collect", - "version": "0.1" + "version": "1.0" }, "positions": [ { diff --git a/repo/js/AutoMonday/main.js b/repo/js/AutoMonday/main.js index 51b19a836..24f979d68 100644 --- a/repo/js/AutoMonday/main.js +++ b/repo/js/AutoMonday/main.js @@ -87,7 +87,7 @@ } // 等待质变仪完成提示出现。 若超时则强制结束流程。 - async function waitTransformer() { + async function waitTransformer(deployed) { var startTime = new Date(); await sleep(500); var NowTime = new Date(); @@ -96,15 +96,17 @@ const intervalTime = 3000; // 3秒的时间间隔,单位为毫秒 // 质变仪判断逻辑 - while ((NowTime - startTime) < actiontime * 1000) { - await textOCR("质变产生了以下物质", 0.7, 1, 0, 539, 251, 800, 425); - if (result.found) { - click(970, 760); - if (!ifAkf) { return true; } - await sleep(150); - break; + if (deployed) { + while ((NowTime - startTime) < actiontime * 1000) { + await textOCR("质变产生了以下物质", 0.7, 1, 0, 539, 251, 800, 425); + if (result.found) { + click(970, 760); + if (!ifAkf) { return true; } + await sleep(150); + break; + } + NowTime = new Date(); } - NowTime = new Date(); } // 厨艺机关判断逻辑 @@ -140,12 +142,7 @@ await keyPress("B"); await sleep(1000); - //连续点击三次防止过期道具卡背包 - await click(970, 760); - await sleep(100); - await click(970, 760); - await sleep(100); - await click(970, 760); + await handleExpiredItems(); //处理过期物品 await sleep(1000); await click(1067, 57);//点开背包,可做图像识别优化 @@ -272,6 +269,114 @@ } } + // 背包过期物品识别,需要在背包界面,并且是1920x1080分辨率下使用 + async function handleExpiredItems() { + const ifGuoqi = await textOCR("物品过期", 5, 0, 0, 870, 280, 170, 40); + if (ifGuoqi.found) { + log.info("检测到过期物品,正在处理..."); + await sleep(500); + await click(980, 750); // 点击确认按钮,关闭提示 + } else { + log.info("未检测到过期物品"); + } + } + + // 获取当前时间七天后的时间戳 + function getSevenDaysLater() { + const now = new Date(); + const sevenDaysLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); // 当前时间 + 7天 + return sevenDaysLater.toISOString(); + } + + // 检查文件是否存在 + async function checkFileExists(filePath) { + try { + // 尝试读取文件,如果成功则文件存在 + await file.readText(filePath); + return true; + } catch (e) { + // 如果读取失败,则文件可能不存在 + return false; + } + } + + // 读取CD记录 + async function readCDRecords() { + let records = {}; + + // 使用基础方法检查文件是否存在 + const fileExists = await checkFileExists(cdRecordPath); + if (fileExists) { + try { + const content = await file.readText(cdRecordPath); + const lines = content.split('\n'); + + for (const line of lines) { + if (line.trim()) { + const [name, timestamp] = line.split('::'); + records[name] = timestamp; + } + } + } catch (e) { + log.error(`读取CD记录失败: ${e}`); + } + } + + return records; + } + + // 写入CD记录 + async function writeCDRecords(records) { + let content = ''; + + for (const name in records) { + content += `${name}::${records[name]}\n`; + } + + try { + // 尝试创建目录(如果环境支持) + try { + if (typeof file.mkdir === 'function') { + file.mkdir('record'); + } + } catch (e) { + // 忽略目录创建错误 + } + + await file.writeText(cdRecordPath, content); + } catch (e) { + log.error(`写入CD记录失败: ${e}`); + } + } + + // 检查路线是否可执行(CD是否已刷新) + function isRouteAvailable(routeName, cdRecords) { + const now = new Date(); + + // 如果记录中没有该路线,说明是第一次执行,可以执行 + if (!cdRecords[routeName]) { + return true; + } + + // 检查CD时间是否已过 + const cdTime = new Date(cdRecords[routeName]); + return now >= cdTime; + } + + // 获取当前账户id + async function getCurrentUsername() { + await genshin.returnMainUi(); + const texts = await textOCR("", 0.3, 0, 2, 1755, 1050, 110, 20); + if (result.found) { + log.debug("当前用户:" + texts.text); + await genshin.returnMainUi(); + return texts.text; + } + log.info("未找到用户名"); + await genshin.returnMainUi(); + + return null; + } //main/====================================================================================== @@ -279,7 +384,7 @@ if (!settings.ifCheck) { log.error("请阅读readme文件并做好相关设置后再运行此脚本!"); return; } //初始化配置 - var actiontime = settings.actiontime != undefined && ~~settings.actiontime > 0 ? ~~settings.actiontime : 180; + var actiontime = 180;//最大等待时间,单位秒 var TEAM; var Material = settings.Material; var BH = `assets/RecognitionObject/${Material}.png`; @@ -300,6 +405,9 @@ // 直接通过映射获取ITEM值(未匹配时默认0) ITEM = materialToItemMap[Material] || 0; + const routeName = "下次刷新时间"; + const username = await getCurrentUsername(); + const cdRecordPath = `record/${username}_cd.txt`;// 修改CD记录文件路径,包含用户名 //检查用户是否配置队伍 if (ifAkf && settings.TEAMname === undefined) { @@ -314,6 +422,16 @@ setGameMetrics(1920, 1080, 1); await genshin.returnMainUi(); + // 读取CD记录 + const cdRecords = await readCDRecords(); + let updatedRecords = { ...cdRecords }; + + // 检查CD + if (!isRouteAvailable(routeName, cdRecords)) { + log.info("CD未刷新,跳过本次执行"); + return; + } + // 打开地图 keyPress("M"); await sleep(1000); @@ -331,13 +449,14 @@ await switchPartyIfNeeded(TEAM); //切换到指定队伍 await AutoPath("全自动质变仪"); - if (!await deployTransformer()) { - throw new Error("质变仪未找到或在cd中"); - };//部署质变仪 + const deployed = await deployTransformer();//部署质变仪 + if (!deployed) { + log.error("质变仪未找到或在cd中"); + } else { + await insertMaterial();//放入材料并开始质变流程 + } - await insertMaterial()//放入材料并开始质变流程 - - await waitTransformer()//等待质变完成 + await waitTransformer(deployed)//等待质变完成 log.info("任务执行完成!!!"); @@ -360,6 +479,10 @@ log.info("已完成 领取晶蝶诱捕装置"); await sleep(1000); + + // 更新CD记录(设置为七天后) + updatedRecords[routeName] = getSevenDaysLater(); + await writeCDRecords(updatedRecords); } catch (error) { log.error(`执行过程中发生错误:${error.message}`); } finally { diff --git a/repo/js/AutoMonday/manifest.json b/repo/js/AutoMonday/manifest.json index 7d6d52e26..ffc89cd3a 100644 --- a/repo/js/AutoMonday/manifest.json +++ b/repo/js/AutoMonday/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "全自动周一", - "version": "1.0", + "version": "1.1", "tags": [ "爱可菲", "质变仪", diff --git a/repo/js/YNF-AutoEat/README.md b/repo/js/YNF-AutoEat/README.md index b84b96e1c..c8b84a6ae 100644 --- a/repo/js/YNF-AutoEat/README.md +++ b/repo/js/YNF-AutoEat/README.md @@ -29,6 +29,9 @@ 2. 感谢氨气老师和秋秋云老师,在审核时提供了宝贵的意见和建议。 --------------------------------------------------------------------------------------------------------------------------------- ## 更新日志 +### 1.4(2025.09.24) +1. 修复了运行次数为某些值时运行结束前往神像之后还会回到秘境的bug +2. 添加了更为完善的过期物品拦截机制,不用再傻乎乎的每次点击同一位置了!(感谢糖雕老师以身殉道提供海量素材~) ### 1.3(2025.09.21) 1. 添加了保证队友状态的逻辑:(队友:我有什么错!我又没有这个天赋! 伊涅芙:那我就有错了嘛!?) 1. 运行脚本时会先去一次神像,确保满状态进副本 diff --git a/repo/js/YNF-AutoEat/main.js b/repo/js/YNF-AutoEat/main.js index 0f9582c3c..a4906394a 100644 --- a/repo/js/YNF-AutoEat/main.js +++ b/repo/js/YNF-AutoEat/main.js @@ -128,6 +128,59 @@ return result; } + /** + * 文字OCR识别封装函数(测试中,未封装完成,后续会优化逻辑) + * @param text 要识别的文字,默认为"空参数" + * @param timeout 超时时间,单位为秒,默认为10秒 + * @param afterBehavior 点击模式,0表示不点击,1表示点击识别到文字的位置,2表示输出模式,默认为0 + * @param debugmodel 调试代码,0表示输入判断模式,1表示输出位置信息,2表示输出判断模式,默认为0 + * @param x OCR识别区域的起始X坐标,默认为0 + * @param y OCR识别区域的起始Y坐标,默认为0 + * @param w OCR识别区域的宽度,默认为1920 + * @param h OCR识别区域的高度,默认为1080 + * @returns 包含识别结果的对象,包括识别的文字、坐标和是否找到的结果 + */ + async function textOCR(text = "空参数", timeout = 10, afterBehavior = 0, debugmodel = 0, x = 0, y = 0, w = 1920, h = 1080) { + const startTime = new Date(); + var Outcheak = 0 + for (var ii = 0; ii < 10; ii++) { + // 获取一张截图 + var captureRegion = captureGameRegion(); + var res1 + var res2 + var conuntcottimecot = 1; + var conuntcottimecomp = 1; + // 对整个区域进行 OCR + var resList = captureRegion.findMulti(RecognitionObject.ocr(x, y, w, h)); + //log.info("OCR 全区域识别结果数量 {len}", resList.count); + if (resList.count !== 0) { + for (let i = 0; i < resList.count; i++) { // 遍历的是 C# 的 List 对象,所以要用 count,而不是 length + let res = resList[i]; + res1 = res.text + conuntcottimecomp++; + if (res.text.includes(text) && debugmodel == 3) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } + if (res.text.includes(text) && debugmodel !== 2) { + conuntcottimecot++; + log.info(`“${res1}”找到`); + if (debugmodel === 1 & x === 0 & y === 0) { log.info("全图代码位置:({x},{y},{h},{w})", res.x - 10, res.y - 10, res.width + 10, res.Height + 10); } else { log.info("文本OCR完成'{text}'", res.text); } + if (afterBehavior === 1) { log.info("点击模式:开"); await sleep(1000); click(res.x, res.y); } else { if (debugmodel === 1 & x === 0 & y === 0) { log.info("点击模式:关") } } + if (afterBehavior === 2) { log.info("F模式:开"); await sleep(100); keyPress("F"); } else { if (debugmodel === 1 & x === 0 & y === 0) { log.info("F模式:关"); } } + if (conuntcottimecot >= conuntcottimecomp / 2) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } else { return result = { found: false }; } + } + if (debugmodel === 2) { + if (res1 === res2) { conuntcottimecot++; res2 = res1; } + //log.info("输出模式:全图代码位置:({x},{y},{h},{w},{string})", res.x-10, res.y-10, res.width+10, res.Height+10, res.text); + if (Outcheak === 1) { if (conuntcottimecot >= conuntcottimecomp / 2) { return result = { text: res.text, x: res.x, y: res.y, found: true }; } else { return result = { found: false }; } } + } + } + } + const NowTime = new Date(); + if ((NowTime - startTime) > timeout * 1000) { if (debugmodel === 2) { if (resList.count === 0) { return result = { found: false }; } else { Outcheak = 1; ii = 2; } } else { Outcheak = 0; if (debugmodel === 1 & x === 0 & y === 0) { log.info(`${timeout}秒超时退出,"${text}"未找到`) }; return result = { found: false }; } } + else { ii = 2; if (debugmodel === 1 & x === 0 & y === 0) { log.info(`"${text}"识别中……`); } } + await sleep(100); + } + } + //判断队内角色 async function includes(characterName) { var avatars = getAvatars(); @@ -193,6 +246,7 @@ await sleep(5000); await keyPress("B"); + await handleExpiredItems();//处理过期物品弹窗 await sleep(1000); await click(860, 50); await sleep(1000); @@ -238,6 +292,19 @@ } + // 背包过期物品识别,需要在背包界面,并且是1920x1080分辨率下使用 + async function handleExpiredItems() { + const ifGuoqi = await textOCR("物品过期", 1, 0, 0, 870, 280, 170, 40); + if (ifGuoqi.found) { + log.info("检测到过期物品,正在处理..."); + await sleep(500); + await click(980, 750); // 点击确认按钮,关闭提示 + } else { + log.info("未检测到过期物品"); + } + } + + // ===== MAIN EXECUTION ===== @@ -285,7 +352,7 @@ for (let i = 0; i < n; i++) { await doit(); dieCount++; - if (dieCount % 8 === 0) { //每8次回一次神像 + if (dieCount % 8 === 0 && dieCount != n) { //每8次回一次神像 log.info("队友们的血量好像有点不太健康欸……先回去补一补!"); await genshin.tpToStatueOfTheSeven(); await sleep(500); diff --git a/repo/js/YNF-AutoEat/manifest.json b/repo/js/YNF-AutoEat/manifest.json index 9722fddf9..8abe62493 100644 --- a/repo/js/YNF-AutoEat/manifest.json +++ b/repo/js/YNF-AutoEat/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "伊涅芙の自助餐", - "version": "1.3", + "version": "1.4", "tags": [ "伊涅芙", "调味品"