From e5d83912d63aa7a964e2eebfa1671c09c23fcf17 Mon Sep 17 00:00:00 2001 From: JJMdzh Date: Wed, 1 Oct 2025 13:50:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=8B=97=E7=B2=AE=E5=88=86?= =?UTF-8?q?=E8=A7=A3=E7=BB=8F=E9=AA=8C=EF=BC=8C=E6=9C=AC=E5=9C=B0=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=EF=BC=8C=E9=80=9A=E7=9F=A5=20(#2046)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add files via upload * Update manifest.json --- repo/js/AutoArtifacts_A_B_Extra/main.js | 511 +++++++++++------- repo/js/AutoArtifacts_A_B_Extra/manifest.json | 8 +- repo/js/AutoArtifacts_A_B_Extra/settings.json | 85 +-- 3 files changed, 360 insertions(+), 244 deletions(-) diff --git a/repo/js/AutoArtifacts_A_B_Extra/main.js b/repo/js/AutoArtifacts_A_B_Extra/main.js index 83b527a4d..0c1bb6b77 100644 --- a/repo/js/AutoArtifacts_A_B_Extra/main.js +++ b/repo/js/AutoArtifacts_A_B_Extra/main.js @@ -1,4 +1,3 @@ - const folderA = 'assets/狗粮A线@Yang-z/'; const folderB = 'assets/狗粮B线@Yang-z/'; const folderE = 'assets/狗粮额外@Yang-z/'; @@ -30,7 +29,7 @@ const pathingA = [ "【收尾】狗粮-稻妻-神无冢-踏鞴砂①-6个/21个-f.json", "【收尾】狗粮-稻妻-神无冢-踏鞴砂②-7个/21个-f.json", "【收尾】狗粮-稻妻-神无冢-踏鞴砂③-8个/21个-f.json" -]; // 98+21个 +]; const pathingB = [ "狗粮-枫丹-枫丹庭区-3个.json", @@ -61,40 +60,38 @@ const pathingB = [ "狗粮-稻妻-鹤观-南-2个-f.json", "(恢复)狗粮-稻妻-清籁岛.json", "【收尾】狗粮-稻妻-清籁岛-清籁丸-20个-f.json" -]; // 97+20个 +]; const pathingE = [ - "【额外】狗粮-纳塔-鸡屁股+8个/9个-f.json", // 12小时刷新 -]; // 7个 + "【额外】狗粮-纳塔-鸡屁股+8个/9个-f.json", +]; const pathingE_A = [ - "【额外】狗粮-须弥-水天丛林+7个-f.json", // 24小时刷新 - "【额外】狗粮-枫丹-研究院区-新枫丹科学院周边+3个-f.json", // 24小时刷新 -]; // 10个 + "【额外】狗粮-须弥-水天丛林+7个-f.json", + "【额外】狗粮-枫丹-研究院区-新枫丹科学院周边+3个-f.json", +]; const pathingE_B = [ - "【额外】狗粮-纳塔-灵谜纹+13个.json" // 24小时刷新 -]; // 13个 + "【额外】狗粮-纳塔-灵谜纹+13个.json" +]; - // 每日拾取点位数及耗时 - // A: (98 + 21) + (8 + 10) = 137 ~ 31 + 10 = 41 minutes - // B: (97 + 20) + (8 + 13) = 138 ~ 32 + 11 = 43 minutes +// 读取用户设置 +let path = settings.path != undefined ? settings.path : ''; +let swapPath = settings.swapPath != undefined && settings.swapPath != '否' ? true : false; +let extra = settings.extra != undefined && settings.extra != '是' ? false : true; +let extraAB = settings.extraAB != undefined && settings.extraAB != '是' ? false : true; +let autoSalvage = settings.autoSalvage != undefined && settings.autoSalvage != '是' ? false : true; +let autoSalvageSpan = settings.autoSalvageSpan != undefined && ~~settings.autoSalvageSpan > 0 ? ~~settings.autoSalvageSpan : 10; +let activeRestore = settings.activeRestore != undefined && settings.activeRestore != '是' ? false : true; +const activeProgress = settings.activeProgress != undefined && settings.activeProgress != '否' ? true : false; +const notify = settings.notify || false; - - // 读取用户设置 - let path = settings.path != undefined ? settings.path : ''; - let swapPath = settings.swapPath != undefined && settings.swapPath != '否' ? true : false; - let extra = settings.extra != undefined && settings.extra != '是' ? false : true; - let extraAB = settings.extraAB != undefined && settings.extraAB != '是' ? false : true; - let autoSalvage = settings.autoSalvage != undefined && settings.autoSalvage != '是' ? false : true; - let autoSalvage4 = settings.autoSalvage4 != undefined && settings.autoSalvage4 != '否' ? true : false; - let autoSalvageSpan = settings.autoSalvageSpan != undefined && ~~settings.autoSalvageSpan > 0 ? ~~settings.autoSalvageSpan : 10; - let activeRestore = settings.activeRestore != undefined && settings.activeRestore != '是' ? false : true; - const activeProgress = settings.activeProgress != undefined && settings.activeProgress != '否' ? true : false; - - - log.debug(`path: ${path}; swapPath: ${swapPath}; extra: ${extra}; extraAB: ${extraAB}; autoSalvage: ${autoSalvage}; autoSalvage4: ${autoSalvage4}; autoSalvageSpan: ${autoSalvageSpan}; activeRestore: ${activeRestore};`); - // await sleep(30000); +// 分解记录变量 +let totalSalvageExp = 0; // 累计EXP(不含首次分解) +const salvageRecords = []; +const salvageLogFile = "狗粮分解日志.txt"; +let pendingPaths = []; // 仅包含已执行的路径 +let count = 0; // 计数已执行的非恢复路径 // 确定路径 function determinePath() { @@ -104,252 +101,314 @@ function determinePath() { const delta = now - benchmark; const days = delta / (1000 * 60 * 60 * 24); path = days % 2 < 1 ? 'A' : 'B'; - if (swapPath) path = path == 'A' ? 'B' : 'A'; } - - // 如果路径切换,清理与当前路径无关的任务记录 if (progress.path !== path) { progress.completedTasks = []; progress.path = path; - saveProgress(); // 保存新的路径和清理后的任务记录 + saveProgress(); } } - // 初始化函数 async function init(shouldRestore = true, shouldResizeMap = false) { - // 关闭强制交互 dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); - - // 恢复和对齐 if (shouldRestore) { - await genshin.tp("1468.0732421875", "1998.04443359375"); + await genshin.tp("-257.8800964", "627.0402832"); await sleep(3000); } - - // 调整地图缩放 (bgi[v0.41.0]后不需要) if (shouldResizeMap) { await resizeMap(); } } - // 调整地图 - async function resizeMap(level = 1) { - await genshin.returnMainUi(); - - keyPress("M"); await sleep(1000); - for (let i = 5; i > 0; --i) { - click(46, 436); await sleep(500); // zoom in - } - for (let i = 0; i < level; ++i) { - click(46, 630); await sleep(500); // zoom out - } - // keyPress("M"); await sleep(1000); - } - - // 拖拽地图 - async function dragMap(byX, byY) { - await genshin.returnMainUi(); - - let byL = Math.sqrt(byX * byX + byY * byY); - let d = 5; - - let dx = Math.round(d * byX / byL); - let dy = Math.round(d * byY / byL); - - let times = Math.round(byX / dx * genshin.screenDpiScale); - - log.debug(`byL: ${byL}; dx: ${dx}; dy: ${dy}; times: ${times}; genshin.screenDpiScale: ${genshin.screenDpiScale};`); - - keyPress("M"); await sleep(1000); - moveMouseBy(-byX, -byY); await sleep(300); - - leftButtonDown(); await sleep(300); - for (let i = 0; i < times; ++i) { - moveMouseBy(dx, dy); await sleep(30); - } - leftButtonUp(); await sleep(300); - } - - // 就近传送(传送脚本文件中的第一个点) - async function tpNearby(filePath) { - const raw = file.ReadTextSync(filePath); - const data = JSON.parse(raw); - await genshin.tp(data['positions'][0]['x'], data['positions'][0]['y']); - } - -// 分解圣遗物 -async function salvage() { - if (!autoSalvage) return; +// 调整地图 +async function resizeMap(level = 1) { await genshin.returnMainUi(); - keyPress("B"); - await sleep(2000); - click(670, 40); - await sleep(1000); // 圣遗物 - click(660, 1010); - await sleep(1000); // 分解 - click(300, 1020); - await sleep(1000); // 快速选择 - - // 点击4星圣遗物 - if (!autoSalvage4) { - click(200, 380); - await sleep(500); + keyPress("M"); await sleep(1000); + for (let i = 5; i > 0; --i) { + click(46, 436); await sleep(500); + } + for (let i = 0; i < level; ++i) { + click(46, 630); await sleep(500); } - - click(340, 1000); - await sleep(1000); // 确认选择 - click(1720, 1015); - await sleep(1500); // 分解 - click(1320, 750); - await sleep(1000); // 进行分解 - - await genshin.returnMainUi(); } -// 进度文件路径 -const progressFile = "progress.json"; +// 拖拽地图 +async function dragMap(byX, byY) { + await genshin.returnMainUi(); + let byL = Math.sqrt(byX * byX + byY * byY); + let d = 5; + let dx = Math.round(d * byX / byL); + let dy = Math.round(d * byY / byL); + let times = Math.round(byX / dx * genshin.screenDpiScale); + log.debug(`byL: ${byL}; dx: ${dx}; dy: ${dy}; times: ${times};`); + keyPress("M"); await sleep(1000); + moveMouseBy(-byX, -byY); await sleep(300); + leftButtonDown(); await sleep(300); + for (let i = 0; i < times; ++i) { + moveMouseBy(dx, dy); await sleep(30); + } + leftButtonUp(); await sleep(300); +} -// 初始化进度 -let progress = { - path: null, - completedTasks: [], - lastRunDate: null // 记录上次运行的日期 +// 就近传送 +async function tpNearby(filePath) { + const raw = file.ReadTextSync(filePath); + const data = JSON.parse(raw); + await genshin.tp(data['positions'][0]['x'], data['positions'][0]['y']); +} + +// OCR圣遗物分解配置 +const AUTO_SALVAGE_CONFIG = { + autoSalvage3: settings.autoSalvage3 || "否", + autoSalvage4: settings.autoSalvage4 || "否" +}; +const OCR_REGIONS = { + expStorage: { x: 1472, y: 883, width: 170, height: 34 }, + expCount: { x: 1472, y: 895, width: 170, height: 34 } }; -// 获取本地日期(YYYY-MM-DD 格式) +const numberReplaceMap = { + "O": "0", "o": "0", "Q": "0", "0": "0", + "I": "1", "l": "1", "i": "1", "1": "1", "一": "1", + "Z": "2", "z": "2", "2": "2", "二": "2", + "E": "3", "e": "3", "3": "3", "三": "3", + "A": "4", "a": "4", "4": "4", + "S": "5", "s": "5", "5": "5", + "G": "6", "b": "6", "6": "6", + "T": "7", "t": "7", "7": "7", + "B": "8", "θ": "8", "8": "8", + "g": "9", "q": "9", "9": "9", +}; + +function processExpText(text) { + let correctedText = text || ""; + let removedSymbols = []; + for (const [wrong, correct] of Object.entries(numberReplaceMap)) { + correctedText = correctedText.replace(new RegExp(wrong, 'g'), correct); + } + let finalText = ''; + for (const char of correctedText) { + if (/[0-9]/.test(char)) finalText += char; + else if (char.trim() !== '') removedSymbols.push(char); + } + return { processedText: finalText, removedSymbols: [...new Set(removedSymbols)] }; +} + +async function recognizeExpRegion(regionName, ra = null, timeout = 2000) { + const ocrRegion = OCR_REGIONS[regionName]; + if (!ocrRegion) { + log.error(`[狗粮OCR] 无效区域:${regionName}`); + return { success: false, expCount: 0 }; + } + log.info(`[狗粮OCR] 识别${regionName}(x=${ocrRegion.x}, y=${ocrRegion.y})`); + const startTime = Date.now(); + while (Date.now() - startTime < timeout) { + try { + const ocrResult = ra.find(RecognitionObject.ocr( + ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height + )); + log.info(`[狗粮OCR] 原始文本:${ocrResult.text}`); + if (ocrResult?.text) { + const { processedText } = processExpText(ocrResult.text); + const expCount = processedText ? parseInt(processedText, 10) : 0; + log.info(`[狗粮OCR] ${regionName}结果:${expCount}`); + return { success: true, expCount }; + } + } catch (error) { + log.warn(`[狗粮OCR] 识别失败:${error.message}`); + } + await sleep(500); + } + log.error(`[狗粮OCR] ${regionName}超时未识别`); + return { success: false, expCount: 0 }; +} + +// 分解函数 +async function executeSalvageWithOCR(pathGroup, isPreRun = false) { + if (!autoSalvage) { + log.info("[狗粮分解] 未开启自动分解,跳过"); + return { success: false, totalExp: 0 }; + } + + const runType = isPreRun ? "首次(路径前)" : "常规"; + const actualPathGroup = isPreRun ? [] : pathGroup; + log.info(`[狗粮分解] 开始${runType}分解(关联路径组:共${actualPathGroup.length}个,均为已执行路径)`); + + let storageExp = 0; + let countExp = 0; + let cachedFrame = null; + + try { + await genshin.returnMainUi(); + keyPress("B"); await sleep(1000); + const coords = [ + [670, 40], // 打开背包 + [660, 1010], // 打开分解 + [300, 1020], // 分解选项页面 + [200, 300, 500, AUTO_SALVAGE_CONFIG.autoSalvage3 !== '是'], // 3星配置 + [200, 380, 500, AUTO_SALVAGE_CONFIG.autoSalvage4 !== '是'], // 4星配置 + [340, 1000], // 确认选择 + [1720, 1015], // 分解按钮 + [1320, 756], // 确认分解 + [1840, 45, 1500], // 退出 + [1840, 45], // 退出 + [1840, 45], // 退出 + ]; + + for (const coord of coords) { + const [x, y, delay = 1000, condition = true] = coord; + if (condition) { + click(x, y); + await sleep(delay); + if (x === 660 && y === 1010) { + cachedFrame?.dispose(); + cachedFrame = captureGameRegion(); + const { expCount } = await recognizeExpRegion("expStorage", cachedFrame, 1000); + storageExp = expCount; + } + if (x === 340 && y === 1000) { + cachedFrame?.dispose(); + cachedFrame = captureGameRegion(); + const { expCount } = await recognizeExpRegion("expCount", cachedFrame, 1000); + countExp = expCount; + } + } + } + + const totalExp = countExp - storageExp; + const actualExp = Math.max(totalExp, 0); + log.info(`[狗粮分解] ${runType}分解完成,获得EXP:${actualExp}`); + + // 记录逻辑 + const recordTime = new Date().toLocaleString(); + const pathGroupStr = actualPathGroup.map((p, i) => `${i+1}. ${p}`).join('\n '); + const record = { time: recordTime, pathGroup: actualPathGroup, exp: actualExp, type: runType }; + salvageRecords.push(record); + + if (!isPreRun) { + totalSalvageExp += actualExp; + } + + // 日志与通知 + const logContent = `[${recordTime}] ${runType}分解 - 关联路径组(共${actualPathGroup.length}个):\n ${pathGroupStr}\n本次分解EXP:${actualExp}${!isPreRun ? `\n累计分解EXP:${totalSalvageExp}` : ''}`; + await writeFile(salvageLogFile, logContent, true); + + if (notify) { + notification.send(`${runType}分解完成!\n关联已执行路径:${actualPathGroup.length}个\n本次EXP:${actualExp}${!isPreRun ? `\n累计EXP:${totalSalvageExp}` : ''}`); + } + + return { success: true, totalExp: actualExp }; + } catch (error) { + log.error(`[狗粮分解] ${runType}分解失败:${error.message}`); + return { success: false, totalExp: 0 }; + } finally { + cachedFrame?.dispose(); + await genshin.returnMainUi(); + } +} + +// 进度管理 +const progressFile = "progress.json"; +let progress = { path: null, completedTasks: [], lastRunDate: null }; + function getLocalDate() { const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要加 1 - const date = String(now.getDate()).padStart(2, '0'); - return `${year}-${month}-${date}`; + return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`; } -// 读取进度 + async function loadProgress() { try { const content = await file.readText(progressFile); const loadedProgress = JSON.parse(content); - - // 获取本地日期 const today = getLocalDate(); - - // 获取保存的日期 - const lastRunDate = loadedProgress.lastRunDate; - - if (lastRunDate && lastRunDate === today) { - // 如果日期一致,加载进度 + if (loadedProgress.lastRunDate === today) { progress.path = loadedProgress.path; progress.completedTasks = Array.isArray(loadedProgress.completedTasks) ? loadedProgress.completedTasks : []; } else { - // 如果日期不一致,重置进度 progress.path = null; progress.completedTasks = []; } - - // 更新当前日期 progress.lastRunDate = today; - // 日志输出 log.info(`加载进度成功: ${JSON.stringify(progress)}`); } catch (error) { log.error("加载进度失败:", error); } } -// 保存进度 + async function saveProgress() { try { - // 获取本地日期 - const today = getLocalDate(); - - // 更新进度并保存 - progress.lastRunDate = today; + progress.lastRunDate = getLocalDate(); await file.writeText(progressFile, JSON.stringify(progress)); - - log.info(`进度已保存,当前日期: ${today}`); + log.info(`进度已保存,当前日期: ${progress.lastRunDate}`); } catch (error) { log.error("保存进度失败:", error); } } - -let count = 0; // 用于记录分解圣遗物的次数 - +// 核心执行函数(关键修正:先执行路径,再加入分解组并判断) async function runFile(filePath, times = 2) { try { - // 检查任务是否已经完成 if (progress.completedTasks.includes(filePath)) { log.info(`任务已跳过: ${filePath}`); return; } - // 记录任务开始时间 const startTime = Date.now(); log.info(`开始执行任务: ${filePath}`); - // 检查是否是恢复任务 let isToRestore = filePath.search("(恢复)") != -1; if (isToRestore && !activeRestore) { log.info(`跳过恢复任务: ${filePath}`); return; } - // 分解圣遗物 - if (!isToRestore && count++ % autoSalvageSpan == 0) { - await salvage(); + // 步骤1:先执行路径(确保路径已完成) + let forceInteraction = filePath.search("-f") != -1; + if (!isToRestore) dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); + + await pathingScript.runFile(filePath); // 执行路径 + await sleep(1000); + dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); + + // 步骤2:路径执行完成后,再加入待分解组 + if (!isToRestore) { + pendingPaths.push(filePath); + log.debug(`路径已执行完成,加入待分解组:${filePath},当前组含${pendingPaths.length}个已执行路径`); } - // 调整地图缩放 (bgi[v0.41.0]后不需要) - // let shouldResizeMap = filePath.search("-m") != -1; - // if (shouldResizeMap) await resizeMap(); + // 步骤3:判断是否触发分解(仅针对已执行的路径) + if (!isToRestore && ++count % autoSalvageSpan == 0) { + log.debug(`已执行${count}个路径,达到分解间隔(${autoSalvageSpan}个),触发常规分解`); + await executeSalvageWithOCR([...pendingPaths], false); + pendingPaths = []; // 清空组,准备收集下一批已执行路径 + } - // 配置自动拾取,根据文件名指定信息,确定是否强制交互(快速拾取) - let forceInteraction = filePath.search("-f") != -1; - if (!isToRestore) dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": forceInteraction })); - - // 执行任务 - await pathingScript.runFile(filePath); - await sleep(1000); - // 配置强制拾取为关闭状态 - dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); - // 记录任务结束时间 + // 保存进度 const endTime = Date.now(); - const duration = endTime - startTime; // 任务运行时长(毫秒) - - // 判断任务是否成功完成 - if (duration < 5000) { // 假设任务运行时长小于5秒则认为任务被取消或异常终止 - log.info(`任务运行时长过短(${duration}ms),可能被取消或异常终止,不保存进度: ${filePath}`); + if (endTime - startTime < 5000) { + log.info(`任务时长过短,不保存进度: ${filePath}`); return; } - // 任务成功完成,更新进度 progress.completedTasks.push(filePath); await saveProgress(); - log.info(`任务成功完成并保存进度: ${filePath}`); + log.info(`任务完成并保存进度: ${filePath}`); - // 地图缩放按键同某些地图标识重叠,导致识别失败(bgi[v0.43.0]后引入) - // // 完成路径后,放大地图,脚本中调用就近传送。仍可能被缩小回去。不可行 - // let shouldResizeMap_after = filePath.search("~m") != -1; - // if (shouldResizeMap_after) { - // await resizeMap(0); - // await tpNearby(filePath); - // } - // 完成路径后,拖拽地图,脚本中调用就近传送。可行 - let shouldDragMap_after = filePath.search("~m") != -1; - if (shouldDragMap_after) { + if (filePath.search("~m") != -1) { await dragMap(-50, 50); await tpNearby(filePath); } } catch (error) { - // 任务失败,记录错误但不保存进度 log.error(`任务执行失败: ${filePath}`, error); await sleep(3000); if (times > 0) { - log.info(`任务失败,尝试重新执行: ${filePath}`); + log.info(`重试任务: ${filePath}`); await runFile(filePath, times - 1); } else { - log.info(`任务失败,不再重试: ${filePath}`); + log.info(`任务不再重试: ${filePath}`); } } } @@ -357,55 +416,95 @@ async function runFile(filePath, times = 2) { // 批量执行 async function batch(folder, files) { for (let file of files) { - const filePath = folder + file; - await runFile(filePath); + await runFile(folder + file); + } +} + +// 文件写入函数 +async function writeFile(filePath, content, isAppend = false, maxRecords = 36500) { + try { + if (isAppend) { + let existingContent = ""; + try { existingContent = await file.readTextSync(filePath); } catch (err) {} + const records = existingContent.split("\n\n").filter(Boolean); + const allRecords = [content, ...records]; + const finalContent = allRecords.slice(0, maxRecords).join("\n\n"); + return file.WriteTextSync(filePath, finalContent, false); + } else { + return file.WriteTextSync(filePath, content, false); + } + } catch (error) { + const result = file.WriteTextSync(filePath, content, false); + log.info(result ? `[日志] 处理成功: ${filePath}` : `[日志] 处理失败: ${filePath}`); + return result; } } // 主函数 (async function () { + totalSalvageExp = 0; + salvageRecords.length = 0; + pendingPaths = []; + count = 0; - // 如果 activeProgress 为否,则直接写入一个空对象到进度文件 if (!activeProgress) { - // 写入空对象到进度文件 const result = await file.writeText(progressFile, JSON.stringify({})); - if (result) { - log.info("进度文件已重置,重新开始任务。"); - } else { - log.error("进度文件重置失败。"); - } + log.info(result ? "进度文件已重置" : "进度文件重置失败"); } else { - // 如果 activeProgress 为是,则正常加载进度 await loadProgress(); } - // 确定路径 determinePath(); - - // 初始化 await init(); - // 执行主线任务 - log.info(`开始执行${progress.path}线路。`); + // 路径前首次分解(无关联路径) + if (autoSalvage) { + log.info("路径前执行首次分解(不计入累计)"); + await executeSalvageWithOCR([], true); + } + + // 执行主路径(按顺序执行,每个路径完成后加入分解组) + log.info(`开始执行${progress.path}线路`); if (progress.path == 'A') { await batch(folderA, pathingA); } else { await batch(folderB, pathingB); } - // 执行额外任务 + // 执行额外路径 if (extra) { await init(); - log.info("开始执行额外线路。"); + log.info("开始执行额外线路"); await batch(folderE, pathingE); + if (path == 'A' || !extraAB) await batch(folderE, pathingE_A); + if (path == 'B' || !extraAB) await batch(folderE, pathingE_B); + } - // 24小时刷新的额外点位隔天拾取,避免空跑 - if (path == 'A' || extraAB == false) await batch(folderE, pathingE_A); - if (path == 'B' || extraAB == false) await batch(folderE, pathingE_B); + // 最终分解剩余已执行路径 + if (pendingPaths.length > 0 && autoSalvage) { + log.info(`任务结束,分解剩余${pendingPaths.length}个已执行路径`); + await executeSalvageWithOCR([...pendingPaths], false); + pendingPaths = []; } await init(); - log.info(`今日狗粮拾取任务完成。拾取路线:${progress.path}${extra ? '+E' : ''}`); + log.info(`今日狗粮任务完成,路线:${progress.path}${extra ? '+E' : ''}`); await sleep(1000); -})(); + // 最终汇总 + const summaryTime = new Date().toLocaleString(); + const summaryContent = `[${summaryTime}] 今日分解总览: +总次数:${salvageRecords.length}次 +有效累计EXP(不含首次分解):${totalSalvageExp} +分解明细: +${salvageRecords.map((r, i) => `${i+1}. [${r.time}] ${r.type}分解:${r.exp} EXP`).join('\n')}`; + + await writeFile(salvageLogFile, `\n===== 今日汇总 =====\n${summaryContent}\n====================\n`, true); + log.info("\n===== 今日分解汇总 ====="); + log.info(summaryContent); + log.info("======================="); + if (notify) { + notification.send(`今日任务完成!\n总分解次数:${salvageRecords.length}次\n累计EXP(不含首次):${totalSalvageExp}\n路线:${progress.path}${extra ? '+额外' : ''}`); + } + +})(); diff --git a/repo/js/AutoArtifacts_A_B_Extra/manifest.json b/repo/js/AutoArtifacts_A_B_Extra/manifest.json index 794afe5ea..94a05c9e9 100644 --- a/repo/js/AutoArtifacts_A_B_Extra/manifest.json +++ b/repo/js/AutoArtifacts_A_B_Extra/manifest.json @@ -1,9 +1,13 @@ { "manifest_version": 1, "name": "狗粮ABE路线,自动拾取分解", - "version": "2.5.4", + "version": "2.5.5", "bgi_version": "0.45.1", "description": "圣遗物狗粮AB及额外路线,自动轮换,自动分解,就近恢复。", + "saved_files": [ + "狗粮分解日志.txt", + "progress.json" + ], "authors": [ { "name": "Yang-z", @@ -12,4 +16,4 @@ ], "settings_ui": "settings.json", "main": "main.js" -} \ No newline at end of file +} diff --git a/repo/js/AutoArtifacts_A_B_Extra/settings.json b/repo/js/AutoArtifacts_A_B_Extra/settings.json index 9580c731a..30769e88a 100644 --- a/repo/js/AutoArtifacts_A_B_Extra/settings.json +++ b/repo/js/AutoArtifacts_A_B_Extra/settings.json @@ -9,25 +9,63 @@ "自动" ] }, - { - "name": "swapPath", - "type": "select", - "label": "自动选择路线时,交换AB路线(默认:否)", - "options": [ - "是", - "否" - ] - }, + { "name": "activeProgress", "type": "select", - "label": "启用断点续跑。多账号选否!(默认:否)", + "label": "----------------------------------\n启用断点续跑。(默认:否)\n↓↓↓↓↓ 【多账号】请选:否 ↓↓↓↓↓", "options": [ "是", "否" ] }, + { + "name": "autoSalvage", + "type": "select", + "label": "----------------------------------\n自动分解(默认:是)", + "options": [ + "是", + "否" + ] + }, + { + "name": "notify", + "type": "checkbox", + "label": "储存有本地记录\n----------------------------------\n分解经验后是否发送通知(默认:否)" + }, + { + "name": "autoSalvage3", + "type": "select", + "label": "需在BGI开启JS通知,并设置通知地址\n----------------------------------\n自动分解包括3星圣遗物(默认:否)", + "options": [ + "是", + "否" + ] + }, + { + "name": "autoSalvage4", + "type": "select", + "label": "----------------------------------\n自动分解包括4星圣遗物(默认:否)", + "options": [ + "是", + "否" + ] + }, + { + "name": "autoSalvageSpan", + "type": "input-text", + "label": "----------------------------------\n自动分解在多少路径后执行一次(默认:10)\n填入1则每路径都分解" + }, + { + "name": "swapPath", + "type": "select", + "label": "----------------------------------\n自动选择路线时,交换AB路线(默认:否)", + "options": [ + "是", + "否" + ] + }, { "name": "extra", "type": "select", @@ -40,37 +78,12 @@ { "name": "extraAB", "type": "select", - "label": "满24小时才刷新的额外点位也分AB路线拾取(默认:是)", + "label": "满24小时才刷新的额外点位\n也分AB路线拾取(默认:是)", "options": [ "是", "否" ] }, - - { - "name": "autoSalvage", - "type": "select", - "label": "自动分解(默认:是)", - "options": [ - "是", - "否" - ] - }, - { - "name": "autoSalvage4", - "type": "select", - "label": "自动分解包括4星圣遗物(默认:否)", - "options": [ - "是", - "否" - ] - }, - { - "name": "autoSalvageSpan", - "type": "input-text", - "label": "自动分解在多少路径后执行一次(默认:10)" - }, - { "name": "activeRestore", "type": "select",