let userName = settings.userName || "默认账户"; (async function () { // 定义一个函数用于模拟按键操作 async function simulateKeyOperations(key, duration) { keyDown(key); await sleep(duration); keyUp(key); await sleep(500); // 释放按键后等待 500 毫秒 } // 检验账户名 async function getUserName() { userName = userName.trim(); //数字,中英文,长度在20个字符以内 if (!userName || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(userName)) { log.error(`账户名${userName}违规,暂时使用默认账户名,请查看readme后修改`) userName = "默认账户"; } return userName; } /** * 判断任务是否已刷新(固定为每周四4点刷新) * @param {string} filePath - 存储最后完成时间的文件路径 * @returns {Promise} - 是否已刷新 */ async function isTaskRefreshed(filePath) { const WEEKLY_DAY = 4; // 周四(0是周日,1是周一,4是周四) const WEEKLY_HOUR = 4; // 凌晨4点 try { // 读取文件内容 let content = await file.readText(filePath); // 如果文件内容为空或无效,视为需要刷新 if (!content) { await file.writeText(filePath, ''); log.info("创建新时间记录文件成功,执行脚本"); return true; } const lastTime = new Date(content); const nowTime = new Date(); // 检查上次记录时间是否有效 if (isNaN(lastTime.getTime())) { log.info("时间记录文件内容无效,执行脚本"); return true; } // 获取本周的刷新时间 const thisWeekRefresh = new Date(nowTime); // 计算与本周周四的差值 const dayDiff = (thisWeekRefresh.getDay() - WEEKLY_DAY + 7) % 7; thisWeekRefresh.setDate(thisWeekRefresh.getDate() - dayDiff); thisWeekRefresh.setHours(WEEKLY_HOUR, 0, 0, 0); // 如果当前时间已经过了本周的刷新时间 if (nowTime >= thisWeekRefresh) { // 检查上次完成时间是否在本周刷新之前 if (lastTime < thisWeekRefresh) { notification.send("购买狗粮已经刷新,执行脚本"); return true; } } else { // 否则检查上次完成时间是否在上周刷新之前 const lastWeekRefresh = new Date(thisWeekRefresh); lastWeekRefresh.setDate(lastWeekRefresh.getDate() - 7); if (lastTime < lastWeekRefresh) { notification.send("购买狗粮已经刷新,执行脚本"); return true; } } log.info("购买狗粮未刷新"); return false; } catch (error) { // 如果文件不存在或读取失败,创建新文件并返回true log.info(`文件读取失败: ${error.message},创建新文件`); await file.writeText(filePath, ''); return true; } } // 购买圣遗物 - 只执行购买部分(不包含寻路) async function purchaseOnly(locationName, isRetry = false) { log.info(`开始购买流程: ${locationName}${isRetry ? ' (重试)' : ''}`); // 定义模板 let fDialogueRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/Picture/F_Dialogue.png"), 1050, 400, 100, 400); let shopDialogueRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/Picture/Shopping.png"), 1259, 540, 100, 400); let shopDialogueRo2 = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/Picture/Shopping2.png"), 0, 0, 150, 100); let conFirmRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/Picture/Confirm.png"), 1585, 1005, 31, 31); let guDong = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/Picture/古董.png")); // 定义一个函数识别并交互 NPC async function checkFAlignment(fDialogueRo) { let ra = captureGameRegion(); let fRes = ra.find(fDialogueRo); ra.dispose(); if (!fRes.isExist()) { let f_attempts = 0; // 初始化尝试次数 while (f_attempts < 6) { // 最多尝试 5 次 f_attempts++; log.info(`当前尝试次数:${f_attempts}`); if (f_attempts <= 3) { // 第 1-3 次尝试 await simulateKeyOperations("S", 200); // 后退 200 毫秒 await sleep(200); } else if (f_attempts <= 5) { // 第 4-5 次尝试 await simulateKeyOperations("W", 400); // 前进 400 毫秒 await sleep(500); } else { // 第 6 次尝试,尝试次数已达上限 log.warn("无法找到NPC,退出购买流程"); break; } // 检查是否找到 F 图标 ra = captureGameRegion(); fRes = ra.find(fDialogueRo); // 重新查找 F 图标 ra.dispose(); if (fRes.isExist()) { log.info("找到 F 图标"); break; // 找到后退出循环 } log.warn(`尝试 ${f_attempts}:寻找 F 图标`); } // 如果尝试次数用完仍未找到 F 图标,返回 false if (!fRes.isExist()) { log.warn("经过多次尝试后仍未找到 F 图标"); return { success: false, reason: "npc_not_found" }; } } return { success: true, reason: "" }; } let alignmentResult = await checkFAlignment(fDialogueRo); if(!alignmentResult.success){ // 返回一个对象,包含购买数量和失败原因 return { purchasedCount: 0, failureReason: alignmentResult.reason, aligned: false }; } // 进入对话选项 for (let i = 0; i < 5; i++) { // 最多 F 5次 let captureRegion = captureGameRegion(); // 获取一张截图 let res; if (locationName=='璃月购买狗粮2'){ res = captureRegion.Find(guDong); }else{ res = captureRegion.Find(shopDialogueRo); } if (res.isEmpty()) { keyPress("F"); await sleep(1000); } else { res.click(); log.info("已到达对话选项界面,点击商店图标({x},{y},{h},{w})", res.x, res.y, res.width, res.Height); break; } await sleep(500); } // 进入商店界面 for (let i = 0; i < 5; i++) { // 最多 F 5次 let captureRegion = captureGameRegion(); // 获取一张截图 let res = captureRegion.Find(shopDialogueRo2); captureRegion.dispose(); if (res.isEmpty()) { keyPress("F"); await sleep(1000); } else { log.info("已到达商店界面"); break; } await sleep(500); } if (locationName=='稻妻购买狗粮'){ click(200, 400); await sleep(500); // 选择狗粮 } // 购买狗粮 let purchasedCount = 0; // 记录购买次数 for (let i = 0; i < 6; i++) { // 最多购买6次 let captureRegion = captureGameRegion(); // 获取一张截图 let res = captureRegion.Find(conFirmRo); captureRegion.dispose(); if (res.isEmpty()) { log.info('圣遗物已售罄'); break; }else{ // 识别到购买标识,模拟购买操作的后续点击 await click(1600, 1020); await sleep(1000); // 购买 await click(1320, 780); await sleep(1000); // 最终确认 await click(1320, 780); await sleep(1000); // 点击空白 purchasedCount++; // 购买成功,计数加1 } await sleep(500); } // 返回购买结果对象 return { purchasedCount, failureReason: purchasedCount === 0 ? "sold_out" : "", aligned: true }; } // 完整的购买流程(包含寻路) async function purChase(locationName) { // 寻路 log.info(`加载路径文件: ${locationName}`); let filePath = `assets/Pathing/${locationName}.json`; await pathingScript.runFile(filePath); await sleep(1000); // 执行购买 return await purchaseOnly(locationName); } // 检查函数,如果未买完则重新对话购买 async function checkAndPurchase(locationName) { let maxRetries = 2; // 最大重试次数(加上第一次共3次) let retryCount = 0; let totalPurchased = 0; // 第一次执行完整的购买流程(包含寻路) log.info(`开始执行 ${locationName} 的完整购买流程`); let purchaseResult = await purChase(locationName); let purchasedCount = purchaseResult.purchasedCount; let failureReason = purchaseResult.failureReason; let aligned = purchaseResult.aligned; totalPurchased += purchasedCount; // 如果购买数量为0,检查失败原因 if (purchasedCount === 0) { if (failureReason === "npc_not_found" || !aligned) { // NPC对齐失败,重新执行一次完整购买流程 log.info(`${locationName} NPC对齐失败,重新执行完整购买流程`); await genshin.returnMainUi(); await sleep(2000); purchaseResult = await purChase(locationName); purchasedCount = purchaseResult.purchasedCount; failureReason = purchaseResult.failureReason; aligned = purchaseResult.aligned; totalPurchased = purchasedCount; // 重置总购买数 if (purchasedCount === 0 && (failureReason === "npc_not_found" || !aligned)) { log.warn(`${locationName} 第二次完整购买仍然NPC对齐失败,跳过此地点`); return 0; } } else if (failureReason === "sold_out") { // 商店已售罄,说明之前已经买过了 log.info(`${locationName} 商店已售罄,之前已完整购买过`); return 0; } } // 如果第一次没买完(且数量大于0),尝试重新对话购买 while (totalPurchased < 5 && retryCount < maxRetries) { retryCount++; log.info(`第 ${retryCount} 次重试购买 ${locationName},已购买 ${totalPurchased} 个`); // 返回主界面 await genshin.returnMainUi(); await sleep(2000); // 重新执行购买(不包含寻路) log.info(`重新执行 ${locationName} 的购买流程(不包含寻路)`); purchaseResult = await purchaseOnly(locationName, true); purchasedCount = purchaseResult.purchasedCount; failureReason = purchaseResult.failureReason; // 如果重新购买时数量为0,检查失败原因 if (purchasedCount === 0) { if (failureReason === "npc_not_found") { log.info(`${locationName} 重试时NPC对齐失败,停止重试`); break; } else if (failureReason === "sold_out") { log.info(`${locationName} 重试时已无圣遗物可购买,停止重试`); break; } } totalPurchased += purchasedCount; if (totalPurchased >= 5) { log.info(`成功购买 ${locationName} 的所有圣遗物`); break; } } if (totalPurchased < 5 && totalPurchased > 0) { log.warn(`购买 ${locationName} 未完成,只购买了 ${totalPurchased} 个圣遗物`); } else if (totalPurchased === 0) { log.info(`${locationName} 无圣遗物可购买`); } return totalPurchased; } async function main() { await genshin.returnMainUi(); // 使用数组存储要执行的地点 const purchaseTasks = [ { enabled: settings.select1, name: '蒙德购买狗粮' }, { enabled: settings.select2, name: '璃月购买狗粮1' }, { enabled: settings.select3, name: '璃月购买狗粮2', time: { hour: 19, minute: 0 } }, { enabled: settings.select4, name: '稻妻购买狗粮' }, { enabled: settings.select5, name: '须弥购买狗粮' }, { enabled: settings.select6, name: '枫丹购买狗粮' }, { enabled: settings.select7, name: '纳塔购买狗粮' }, { enabled: settings.select8, name: '挪德卡莱购买狗粮' } ]; let totalPurchased = 0; for (const task of purchaseTasks) { if (task.enabled) { // 如果有时间设置,先设置时间 if (task.time) { await genshin.setTime(task.time.hour, task.time.minute) } // 执行检查并购买 let count = await checkAndPurchase(task.name); totalPurchased += count; log.info(`${task.name} 完成,购买了 ${count} 个圣遗物`); // 返回主界面准备下一个任务 await genshin.returnMainUi(); await sleep(1000); } } notification.send(`所有任务完成,总共购买了 ${totalPurchased} 个圣遗物`); await file.writeText(recordPath, new Date().toISOString()); } userName = await getUserName(); const recordPath = `assets/${userName}.txt`; //每周四4点刷新 if( await isTaskRefreshed(recordPath)|| settings.select9){ await main(); } })();