From 025cf2cf8cab9f667de66608955eb69ab5a2e3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E7=AB=AF=E5=AE=A2?= <107686912+Kirito520Asuna@users.noreply.github.com> Date: Fri, 9 Jan 2026 00:29:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=B9=89=E7=88=B6=E7=AB=8B=E6=9C=AC):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0UID=E8=AF=86=E5=88=AB=E5=92=8C=E9=98=B2?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=89=A7=E8=A1=8C=E5=8A=9F=E8=83=BD=20(#2633?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(义父立本): 添加UID识别和防重复执行功能 - 集成OCR UID识别功能,自动获取当前游戏UID - 添加execute.json文件记录执行状态,防止同一天重复执行 - 实现日期检查逻辑,已执行的UID当天不再运行 - 新增utils/uid.js工具模块,包含完整的UID识别和验证功能 - 升级版本号从1.0到1.1 - 添加云端客作为贡献者 - 优化代码格式和错误处理机制 * refactor(date): 调整日期获取逻辑以支持时区偏移 - 将 getCurrentDate 函数重命名为 getAdjustedDate - 实现4小时时区偏移调整功能(立本4点更新) - 使用手动格式化替代 toLocaleDateString 方法 - 更新函数调用以使用新的日期调整逻辑 --- .../assets/RecognitionObject/paimon_menu.png | Bin 0 -> 2372 bytes repo/js/义父立本/execute.json | 7 + repo/js/义父立本/main.js | 61 +++++++- repo/js/义父立本/manifest.json | 6 +- repo/js/义父立本/utils/uid.js | 145 ++++++++++++++++++ 5 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 repo/js/义父立本/assets/RecognitionObject/paimon_menu.png create mode 100644 repo/js/义父立本/execute.json create mode 100644 repo/js/义父立本/utils/uid.js diff --git a/repo/js/义父立本/assets/RecognitionObject/paimon_menu.png b/repo/js/义父立本/assets/RecognitionObject/paimon_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..c424325b17a4956f7802352e7609bfcff2721afa GIT binary patch literal 2372 zcmV-K3A^@*P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2-!(QK~z{rt(SRF zR96@7{OrIrs3OB6ST- zP@DTTKA&cd-_x`4ifbQ;v^!UEWYd=r=ft5o>jaWdZ$#*F1Dp=`Lw#i>UdiRG$$NWQ zlS!qJbxWbLunJ)v#?D#+4Ko*L&U1#Kww~+hpIblw8}X6L@I3V>qTOd=&jwpmmE@tP zN6PlU71k%NS0-b{sVwYPN+w3?*E%@$g9jWc1ShNWOUqVas)}Z=D&g zor^(ddmGo&$7^Bli&I(H`jA5yHPeBFc|eOy)65lLn65z7v3Rt#cd}#T%%3oS_UaWY zP8XsoYg`}%_!_r5rlgw?!gfco5(B+3n1mL!af6==BzpXu8{&uT3qW6mS~El zkk8E=nsSFID@2ckOt;?zeTP8kFWU_L<(r{v?++`_P}J1FJ zrGzD=Wn-M71IC)Hz~luUn6cEC1O>prF&HzQw!v`aH!xiFElga#ftK|K6j!LeQ~fcd zu+pkpT)duyod@HvcufdKo4P~8+8;*FJ6JPb9Y(ejI`+W`*mDLlI_jSS1!4JxCG0UD zjGX)uI0x;=R2zR7yM)5TEgZ8vA~DL$8>v}OxltdFg0S1arJ|~;ihCNEj?QkRP&zVj z41%F+IE+Xf^I2P(TeuM)kAkquN&(Ks|A_jR&0Ndi6qZ$E%2INE=P)evj^tWD4GO}l zs|660<>Kz0L`bDQTlvJ_ zd*fhAC$o!g1)mB9VKP}S5-webP*{r6(tI2{eg@gOg$bAczCL=vv$<53XC zkeQu}q9VFy$(tdpeg^M=ojCvFHMF#d*ud8xObT(KlbXT*a1qj;9&~keb5F|ifiUJp zNhv9icK(5G$#b-d>yYrnMFa##prLW#L-Bw@#s88sxTm*NR@Wfn@W}xeH4txvNhA_- zcq#jW+`Iw^Y1d>OFVNK}fV`&#XU@hWEkpUF{2C@MnmesS7C)bBQI0pl_)qhZ`REaH zbDpBOT!@R8ZXho^3q6ur^t9F?BQ1rEbE?8D^!SPMuy^t0T9ku#bS|6c^{Bu}MgWpb_a$GGT7-OE0cORWTp;=~E8}W|#CW zR${z~Eqp`wL7jee<~rcixt~x{E}+8X!^F>HL`^VR!V1gg=di+jLJq*rB>)Q@{g7Lj zN99xl$_gIiNnRc|O*L9+cNVVl#e~`R*dBe9y@5R87vNS>8Xjb1<5}5<`I$FqjvfA{ zJ{#fD2dJ13WAB0QFoW(@F(=Q`N}ACvu7 z94CBkV1wTuWpOIxN6it998EI_F8V*b>3*K`Kbcg3(3`{ZXAAT4bR7q)3 zi8tS63pzXdYS4k;sTtc~=+|VZ=M}SYR<;_3-Qba3-?kI z@t^~J`z(Eg++aT0lFm2#AOF&x$eXiTPDp7 z;rzyS?wDrzH8d>N!Q5>Jw(UO$fv{#+$=c8<2qR6M$*06z3(!p0ZLKAKm_<8f>KcaW zD?;EHxOd1!^DJDqu(_;R)XLV2!$I8I4&R6q7;oW)^}CL8txPKF!PbNS!W0rV$88s8 zyGKB6kw0#xq;n$%hBwJN2#F7dP7#9<6del#`#`8ISPy>D+c2;{HUVl@8(`$M2PW>3 z7-Q~*9Wn7r`7O`VI~H~U2hk!@J|r+OaV_~#E-csV!lVV>RK9J;^`s2e1q2j4nK*SX zGZ)dZKfxvV0K9gdz}34C(5Yl$!kg!F3Wgp>VC)%%!qQ5vd2ljv^5GwL7@MQMN9={` zxc~SmS}1mjByBi&DgoNon-Ca%l1BE@0_1GH$rL%t!bs"+json, true) + // 写入更新后的设置 + const success = file.writeTextSync(path, json); + if (!success) { + throw new Error("写入设置文件失败"); + } +} + (async function () { //启用自动剧情 //dispatcher.addTimer(new RealtimeTimer("AutoSkip")); @@ -9,6 +39,18 @@ let waitTime = 40; log.warn("游戏窗口非 1920×1080,可能导致图像识别失败,如果执意使用可能造成异常,后果自负"); await sleep(5000); } + + let executeJson = "execute.json" + let execute = JSON.parse(file.readTextSync(executeJson)); + let uid = await uidUtil.ocrUID(); + let currentDate = getAdjustedDate(); + for (const uidElement of execute) { + if (uidElement.uid === uid && uidElement.date === currentDate) { + log.info("UID:{uid} 今天已经立本过了", uid); + return + } + } + let executeList = execute ? execute : new Array() while (attempts < 3) { attempts++; await pathingScript.runFile(`assets/前往立本.json`); @@ -36,6 +78,13 @@ let waitTime = 40; } } + executeList.push({ + uid: uid, + success: success,//冗余 + date: currentDate + }) + await updateFile(executeList, executeJson) + if (!success) { log.error("3次重试均失败"); } @@ -62,8 +111,16 @@ async function findAndClick(target, doClick = true, maxAttempts = 60) { const rg = captureGameRegion(); try { const res = rg.find(target); - if (res.isExist()) { await sleep(checkInterval * 2 + 50); if (doClick) { res.click(); } return true; } - } finally { rg.dispose(); } + if (res.isExist()) { + await sleep(checkInterval * 2 + 50); + if (doClick) { + res.click(); + } + return true; + } + } finally { + rg.dispose(); + } if (i < maxAttempts - 1) await sleep(checkInterval); } return false; diff --git a/repo/js/义父立本/manifest.json b/repo/js/义父立本/manifest.json index b3a09b730..5a0307ce5 100644 --- a/repo/js/义父立本/manifest.json +++ b/repo/js/义父立本/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "义父立本", - "version": "1.0", + "version": "1.1", "bgi_version": "0.44.8", "description": "谢谢老板", "saved_files": [], @@ -9,6 +9,10 @@ { "name": "mno", "links": "https://github.com/Bedrockx" + }, + { + "name": "云端客", + "links": "https://github.com/Kirito520Asuna" } ], "settings_ui": "settings.json", diff --git a/repo/js/义父立本/utils/uid.js b/repo/js/义父立本/utils/uid.js new file mode 100644 index 000000000..87dbc95d4 --- /dev/null +++ b/repo/js/义父立本/utils/uid.js @@ -0,0 +1,145 @@ +const commonPath = 'assets/RecognitionObject/' +const commonMap = new Map([ + ['main_ui', { + path: `${commonPath}`, + name: 'paimon_menu', + type: '.png', + }], +]) +const genshinJson = { + width: 1920,//genshin.width, + height: 1080,//genshin.height, +} + +/** + * 根据键值获取JSON路径 + * @param {string} key - 要查找的键值 + * @returns {any} 返回与键值对应的JSON路径值 + */ +function getJsonPath(key) { + return commonMap.get(key); // 通过commonMap的get方法获取指定键对应的值 +} + +function saveOnlyNumber(str) { + str = str ? str : ''; + // 使用正则表达式匹配字符串中的所有数字 + // \d+ 匹配一个或多个数字 + // .join('') 将匹配到的数字数组连接成一个字符串 + // parseInt 将连接后的字符串转换为整数 + return parseInt(str.match(/\d+/g).join('')); +} + +async function ocrUID() { + let uid_json = { + x: 1683, + y: 1051, + width: 234, + height: 28, + } + let recognitionObjectOcr = RecognitionObject.Ocr(uid_json.x, uid_json.y, uid_json.width, uid_json.height); + let region3 = captureGameRegion() + try { + let res = region3.find(recognitionObjectOcr); + log.debug(`[OCR识别UID]识别结果: ${res.text}, 原始坐标: x=${res.x}, y=${res.y},width:${res.width},height:${res.height}`); + //只保留数字 + let uid + try { + uid = saveOnlyNumber(res.text) + } catch (e) { + log.debug(`UID未识别`) + uid = 0 + } + log.debug(`[OCR识别UID]识别结果: {uid}`, uid); + return uid + }finally { + region3.dispose() + } + +} + +// 判断是否在主界面的函数 +const isInMainUI = () => { + // let name = '主界面' + let main_ui = getJsonPath('main_ui'); + // 定义识别对象 + let paimonMenuRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync(`${main_ui.path}${main_ui.name}${main_ui.type}`), + 0, + 0, + genshinJson.width / 3.0, + genshinJson.width / 5.0 + ); + let captureRegion = captureGameRegion(); + let res = captureRegion.find(paimonMenuRo); + captureRegion.Dispose() + return !res.isEmpty(); +}; + +async function toMainUi() { + let ms = 1000 + let index = 1 + await sleep(ms); + while (!isInMainUI()) { + await sleep(ms); + await genshin.returnMainUi(); // 如果未启用,则返回游戏主界面 + await sleep(ms); + if (index > 3) { + throw new Error(`多次尝试返回主界面失败`); + } + index += 1 + } + +} + +async function compareUid(UID = settings.uid) { + let uid = await ocrUID() + let setUid = 0 + try { + setUid = saveOnlyNumber(UID) + } catch (e) { + // log.warn(`UID未设置`) + } + let compare = uid === setUid + if (compare) { + log.info(`[OCR识别UID]识别结果: {uid} 与设置UID相同`, uid); + } + return compare +} + +async function checkUid() { + let reJson = { + inMainUI: false, + isUid: false + } + if (isInMainUI()) { + reJson.isUid = await compareUid() + } + return reJson +} + +async function check() { + let check = false + if (settings.uid) { + try { + await toMainUi(); + } catch (e) { + log.warn("多次尝试返回主界面失败") + } + let checkJson = await checkUid() + if ((!checkJson.inMainUI) && (!checkJson.isUid)) { + //尝试直接识别 + checkJson.isUid = await compareUid() + } + check = checkJson.isUid + } + return check +} + +this.uidUtil = { + toMainUi, + isInMainUI, + checkUid, + ocrUID, + check, + compareUid, +} \ No newline at end of file