From 4f49fbb4d39a4ac594df2cf5be7352c08cc1996c 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: Sat, 4 Apr 2026 14:46:43 +0800 Subject: [PATCH] =?UTF-8?q?refactor(auto-exchange):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=8A=BD=E5=8D=A1=E8=B5=84=E6=BA=90=E5=85=91=E6=8D=A2=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20(#3053)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(auto-exchange): 优化抽卡资源兑换脚本的变量初始化 - 移除不必要的注释内容,保持代码简洁性 - 确保 contentList 变量正确初始化为空数组 - 提升代码可读性和维护性 refactor(auto-exchange): 优化抽卡资源自动兑换的时间记录逻辑 - 将存储结构从 Map 改为数组对象列表,每个对象包含 uid 和时间戳 - 使用 find 方法替代 get 方法查找用户对应的完成时间记录 - 实现了更准确的时间戳记录和更新机制 - 修改文件读写操作以适配新的数据结构 - 修复了时间记录的数据持久化逻辑 - 更新了调试日志的内容显示方式 feat(auto-exchange): 添加兑换抽卡资源任务的通知配置选项 - 实现通知发送条件控制,仅当 config.send_notification 为 true 时发送 - 将固定的通知消息改为变量存储并记录到日志 - 调整文件创建成功的日志级别从 info 降至 debug - 优化代码结构以支持更灵活的通知策略 feat(auto-exchange): 添加通知配置和错误处理优化 - 引入 throwError 工具函数替代原有错误抛出方式 - 新增 send_notification 配置选项控制通知发送 - 优化星尘数量不足时的错误提示信息 - 实现条件通知发送机制,避免重复通知 - 更新设置界面添加通知配置复选框 - 重构错误处理逻辑提高代码健壮性 feat(exchange): 实现每月自动兑换抽卡资源功能 - 添加了OCR识别UID功能用于用户标识 - 实现了重试机制和错误处理逻辑 - 集成了定时刷新任务系统,默认每月1号凌晨4点执行 - 添加了商城抽卡资源自动兑换流程 - 集成了返回主界面的安全导航功能 - 添加了配置文件存储兑换记录和状态管理 - 实现了图像识别定位和点击操作自动化 - 添加了设置界面支持最大重试次数配置 feat(exchange): 实现基于UID的任务状态管理与重试机制 - 添加OCR识别UID功能,支持多用户任务状态区分 - 修改任务刷新逻辑,将单一时间戳改为UID映射的时间存储 - 实现重试机制,支持最大重试次数限制 - 优化资源兑换流程,添加空球检测和安全释放机制 - 改进错误处理,添加详细的日志记录和异常处理 - 升级版本号至1.2并添加新的开发者信息 * refactor(auto-exchange): 优化抽卡资源自动兑换的时间记录逻辑 - 移除重复的时间记录代码,将逻辑集中到兑换成功后执行 - 调整代码结构,先获取当前时间再进行兑换操作 - 简化最后一次执行时间的查找逻辑 - 删除无用的注释代码 - 统一时间戳更新流程,确保数据一致性 fix(auto-exchange): 解决UID识别验证问题 - 添加UID类型检查确保为正整数 - 验证OCR识别结果的有效性 - 在UID识别失败时抛出具体错误信息 - 防止无效UID导致后续兑换流程异常 ``` refactor(exchange): 优化每月兑换抽卡资源任务逻辑 - 将代码块包裹在 try-finally 结构中确保调试日志始终输出 - 移除多余的注释代码以提高代码整洁性 - 保持原有的业务逻辑不变,仅调整代码结构 - 确保 contentList 调试信息在所有情况下都能正确记录 ``` fix(auto-exchange): 修复抽卡资源自动兑换的时间处理逻辑 - 修正了查找用户最后兑换时间的变量名错误 - 更新了时间数据的获取方式,从字符串改为数字格式 - 修复了时间对象创建的逻辑以正确处理时间戳 - 确保当前时间戳能正确更新到用户记录中 * ``` feat(exchange): 添加兑换不足检测功能 - 在用户配置中增加insufficient_exchange状态标识 - 实现兑换不足时的错误抛出机制 - 添加星尘数量验证逻辑 - 当星尘数量少于750时设置兑换不足标志 - 集成通知发送功能用于兑换异常提醒 ``` --- .../assets/paimon_menu.png | Bin 0 -> 2372 bytes repo/js/每月自动兑换抽卡资源/main.js | 303 +++++++++++++----- repo/js/每月自动兑换抽卡资源/manifest.json | 6 +- repo/js/每月自动兑换抽卡资源/settings.json | 18 ++ repo/js/每月自动兑换抽卡资源/utils/tool.js | 82 +++++ repo/js/每月自动兑换抽卡资源/utils/uid.js | 64 ++++ 6 files changed, 387 insertions(+), 86 deletions(-) create mode 100644 repo/js/每月自动兑换抽卡资源/assets/paimon_menu.png create mode 100644 repo/js/每月自动兑换抽卡资源/settings.json create mode 100644 repo/js/每月自动兑换抽卡资源/utils/tool.js create mode 100644 repo/js/每月自动兑换抽卡资源/utils/uid.js diff --git a/repo/js/每月自动兑换抽卡资源/assets/paimon_menu.png b/repo/js/每月自动兑换抽卡资源/assets/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 item.uid === uid) + const lastTimeNumber = last?.time + const lastTime = lastTimeNumber ? new Date(lastTimeNumber) : new Date(0); try { - // 读取文件内容 - let content = await file.readText(filePath); - const lastTime = new Date(content); const nowTime = new Date(); - + current.time = nowTime.getTime(); + 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; @@ -53,7 +92,7 @@ async function isTaskRefreshed(filePath, options = {}) { shouldRefresh = lastTime < yesterdayRefresh; } break; - + case 'weekly': // 每周固定时间刷新 // 获取本周的刷新时间 const thisWeekRefresh = new Date(nowTime); @@ -61,7 +100,7 @@ async function isTaskRefreshed(filePath, options = {}) { const dayDiff = (thisWeekRefresh.getDay() - weeklyDay + 7) % 7; thisWeekRefresh.setDate(thisWeekRefresh.getDate() - dayDiff); thisWeekRefresh.setHours(weeklyHour, 0, 0, 0); - + // 如果当前时间已经过了本周的刷新时间 if (nowTime >= thisWeekRefresh) { shouldRefresh = lastTime < thisWeekRefresh; @@ -72,14 +111,14 @@ async function isTaskRefreshed(filePath, options = {}) { 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; @@ -94,39 +133,60 @@ async function isTaskRefreshed(filePath, options = {}) { case 'custom': // 自定义小时数刷新 shouldRefresh = (nowTime - lastTime) >= customHours * 3600 * 1000; break; - + default: throw new Error(`未知的刷新类型: ${refreshType}`); } - - // 如果文件内容无效或不存在,视为需要刷新 - if (!content || isNaN(lastTime.getTime())) { - await file.writeText(filePath, nowTime.toISOString()); - shouldRefresh = true; + + // // 如果文件内容无效或不存在,视为需要刷新 + // if (!contentList || isNaN(lastTime.getTime())) { + // //todo:写入也要改 contentList.put(uid, nowTime.toISOString()) + // // await file.writeText(filePath, JSON.stringify(contentList)); + // shouldRefresh = true; + // } + try { + if (shouldRefresh) { + const message = `任务已刷新,执行每月兑换抽卡资源`; + log.info(message) + if (config.send_notification) { + notification.send(message); + } + await exchangeGoods(); + + if (contentList.some(item => item.uid === current.uid)) { + contentList.forEach(item => { + if (item.uid === current.uid) { + item.time = current.time; + } + }) + } else { + contentList.push(current); + } + + // 更新最后完成时间 + await file.writeText(filePath, JSON.stringify(contentList)); + return true; + } else { + const message = `任务未刷新,跳过每月兑换抽卡资源`; + log.info(message) + if (config.send_notification) { + notification.send(message); + } + return false; + } + } finally { + log.debug("contentList:", JSON.stringify(contentList)) } - - if (shouldRefresh) { - notification.send(`任务已刷新,执行每月兑换抽卡资源`); - await exchangeGoods(); - // 更新最后完成时间 - await file.writeText(filePath, nowTime.toISOString()); - return true; - } else { - notification.send(`任务未刷新,跳过每月兑换抽卡资源`); - return false; - } - } catch (error) { - // 如果文件不存在,创建新文件并返回true(视为需要刷新) - const createResult = await file.writeText(filePath, ''); + log.error(`刷新任务失败: ${error}`); + const createResult = await file.writeText(filePath, JSON.stringify(contentList)); if (createResult) { - log.info("创建新文件成功"); - await isTaskRefreshed(filePath, options = {}); + log.debug("创建新文件成功"); + await isTaskRefreshed(filePath, options = {}); } } } - //检查是否为正整数 function positiveIntegerJudgment(testNumber) { // 如果输入是字符串,尝试转换为数字 @@ -135,76 +195,149 @@ function positiveIntegerJudgment(testNumber) { const cleaned = testNumber.replace(/[^\d]/g, ''); testNumber = parseInt(cleaned, 10); } - + // 检查是否为有效的数字 if (typeof testNumber !== 'number' || isNaN(testNumber)) { throw new Error(`无效的值: ${testNumber} (必须为数字)`); } - + // 检查是否为整数 if (!Number.isInteger(testNumber)) { throw new Error(`必须为整数: ${testNumber}`); } - + return testNumber; } - async function exchangeGoods() { - - await genshin.returnMainUi();await sleep(1000); - keyPress("ESCAPE"); await sleep(2000);//呼叫派蒙 - click(198,416);await sleep(2000);//点击商城 - click(127,434);await sleep(1000);//尘辉兑换 - click(998,125);await sleep(1000);//星辰兑换 + await toMainUi(); + await sleep(1000); + keyPress("ESCAPE"); + await sleep(2000);//呼叫派蒙 + click(198, 416); + await sleep(2000);//点击商城 + click(127, 434); + await sleep(1000);//尘辉兑换 + click(998, 125); + await sleep(1000);//星辰兑换 + let materialQuantity = ""; //检查星辰的数量 const region = RecognitionObject.ocr(1400, 31, 150, 50); // 星辰数量区域 let capture = captureGameRegion(); - let res = capture.find(region); - capture.dispose(); - let materialQuantity = res.text; - let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity); - if(validatedMaterialQuantity < 750){ - notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`); - throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`); + try { + let res = capture.find(region); + materialQuantity = res.text; + } finally { + if (capture) { + capture.dispose(); + } } - log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`); + const pinkBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pinkBall.png")); let ro1 = captureGameRegion(); - let pinkBall = ro1.find(pinkBallRo); - ro1.dispose(); - if (pinkBall.isExist()) { - pinkBall.click();await sleep(1000); - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1164,782);await sleep(500);//确认兑换 - click(960,754);await sleep(1000);//点击空白处继续 + let pinkBallExist = false + let pinkBall + try { + let pinkBallFind = ro1.find(pinkBallRo); + pinkBallExist = pinkBallFind.isExist(); + pinkBall = { + x: pinkBallFind.x, + y: pinkBallFind.y + } + } finally { + if (ro1) { + ro1.dispose(); + } } + const blueBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/blueBall.png")); let ro2 = captureGameRegion(); - let blueBall = ro2.find(blueBallRo); - ro2.dispose(); - if (blueBall.isExist()) { - blueBall.click();await sleep(1000); - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1290,604);await sleep(500);//增加 - click(1164,782);await sleep(500);//确认兑换 - click(960,754);await sleep(1000);//点击空白处继续 + let blueBallExist = false + let blueBall + try { + let blueBallFind = ro2.find(blueBallRo); + blueBallExist = blueBallFind.isExist(); + blueBall = { + x: blueBallFind.x, + y: blueBallFind.y + } + } finally { + if (ro2) { + ro2.dispose(); + } + } + + if (!pinkBallExist && !blueBallExist) { + log.info(`没有粉球和蓝球,跳过兑换`); + return + } + + let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity); + if (validatedMaterialQuantity < 750) { + config.user.insufficient_exchange=true + throwError(`星尘数量为:${validatedMaterialQuantity},数量不足,无法全部兑换`, config.send_notification) + // notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`); + // throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`); + } + log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`); + + if (pinkBallExist && pinkBall) { + // pinkBall.click(); + click(pinkBall.x, pinkBall.y) + await sleep(1000); + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1164, 782); + await sleep(500);//确认兑换 + click(960, 754); + await sleep(1000);//点击空白处继续 + } + + if (blueBallExist && blueBall) { + // blueBall.click(); + click(blueBall.x, blueBall.y) + await sleep(1000); + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1290, 604); + await sleep(500);//增加 + click(1164, 782); + await sleep(500);//确认兑换 + click(960, 754); + await sleep(1000);//点击空白处继续 + } + const message = `商城抽卡资源兑换完成`; + log.info(message) + if (config.send_notification) { + notification.send(message); } - notification.send(`商城抽卡资源兑换完成`); } +async function main() { + try { + config.tryRe.max = parseInt(settings.try_count_max + "") || config.tryRe.max + config.send_notification = settings.send_notification + } catch (e) { + } + try { + await isTaskRefreshed("assets/monthly.txt", { + refreshType: 'monthly', + monthlyDay: 1, // 每月第1天(默认值,可省略) + monthlyHour: 4 // 凌晨4点(默认值,可省略) + }); + } finally { + await toMainUi() + } +} - -await isTaskRefreshed("assets/monthly.txt", { - refreshType: 'monthly', - monthlyDay: 1, // 每月第1天(默认值,可省略) - monthlyHour: 4 // 凌晨4点(默认值,可省略) -}); - - -})(); +await main(); diff --git a/repo/js/每月自动兑换抽卡资源/manifest.json b/repo/js/每月自动兑换抽卡资源/manifest.json index b8e6e1a01..e87a34f41 100644 --- a/repo/js/每月自动兑换抽卡资源/manifest.json +++ b/repo/js/每月自动兑换抽卡资源/manifest.json @@ -1,12 +1,16 @@ { "manifest_version": 1, "name": "每月自动兑换抽卡资源", - "version": "1.1", + "version": "1.2", "description": "每个月自动兑换蓝球和粉球,兑换资源不够会提醒(需要打开 js 通知),本月兑换过会自动跳过,想要重置 CD可以把monthly.txt中的时间删掉", "authors": [ { "name": "柒叶子", "links": "https://github.com/5117600049" + }, + { + "name": "云端客", + "links": "https://github.com/Kirito520Asuna" } ], "settings_ui": "settings.json", diff --git a/repo/js/每月自动兑换抽卡资源/settings.json b/repo/js/每月自动兑换抽卡资源/settings.json new file mode 100644 index 000000000..5f517cdbb --- /dev/null +++ b/repo/js/每月自动兑换抽卡资源/settings.json @@ -0,0 +1,18 @@ +[ + { + "name": "send_notification", + "type": "checkbox", + "label": "发送通知" + }, + { + "name": "try_count_max", + "type": "select", + "label": "最大重试次数", + "options": [ + "1","2","3", + "4","5","6", + "7","8","9" + ] , + "default": "3" + } +] \ No newline at end of file diff --git a/repo/js/每月自动兑换抽卡资源/utils/tool.js b/repo/js/每月自动兑换抽卡资源/utils/tool.js new file mode 100644 index 000000000..603e8485f --- /dev/null +++ b/repo/js/每月自动兑换抽卡资源/utils/tool.js @@ -0,0 +1,82 @@ +const commonPath = 'assets/' +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方法获取指定键对应的值 +} + +// 判断是否在主界面的函数 +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(); + try { + let res = captureRegion.find(paimonMenuRo); + return !res.isEmpty(); + } finally { + captureRegion.dispose() + } + +}; + +async function toMainUi() { + let ms = 300 + let index = 1 + await sleep(ms); + while (!isInMainUI()) { + await sleep(ms); + await genshin.returnMainUi(); // 如果未启用,则返回游戏主界面 + await sleep(ms); + if (index > 3) { + throwError(`多次尝试返回主界面失败`); + } + index += 1 + } + +} + + +/** + * 抛出错误函数 + * 该函数用于显示错误通知并抛出错误对象 + * @param {string} msg - 错误信息,将用于通知和错误对象 + */ +function throwError(msg, isNotification = false) { + // 使用notification组件显示错误通知 + // notification.error(`${msg}`); + if (isNotification) { + notification.error(`${msg}`); + } + // 抛出一个包含错误信息的Error对象 + throw new Error(`${msg}`); +} + +export { + getJsonPath, + isInMainUI, + toMainUi, + throwError, +} \ No newline at end of file diff --git a/repo/js/每月自动兑换抽卡资源/utils/uid.js b/repo/js/每月自动兑换抽卡资源/utils/uid.js new file mode 100644 index 000000000..f5c18cc90 --- /dev/null +++ b/repo/js/每月自动兑换抽卡资源/utils/uid.js @@ -0,0 +1,64 @@ + +async function saveOnlyNumber(str) { + str = str ? str : ''; + // 使用正则表达式匹配字符串中的所有数字 + // \d匹配一个或多个数字 + // .join('') 将匹配到的数字数组连接成一个字符串 + // parseInt 将连接后的字符串转换为整数 + // return parseInt(str.match(/\d+/g).join('')); + const matches = str.match(/\d+/g); + if (!matches) { + return 0; // 或抛出错误 + } + return parseInt(matches.join(''), 10); +} +/** + * 对指定区域进行OCR文字识别 + * @param {number} x - 区域左上角x坐标,默认为0 + * @param {number} y - 区域左上角y坐标,默认为0 + * @param {number} w - 区域宽度,默认为1920 + * @param {number} h - 区域高度,默认为1080 + * @returns {Promise} 返回识别到的文本内容,如果识别失败则返回null + */ +async function ocrRegion(x = 0, + y = 0, + w = 1920, + h = 1080) { + // 创建OCR识别对象,使用指定的坐标和尺寸 + let recognitionObjectOcr = RecognitionObject.Ocr(x, y, w, h); + // 捕获游戏区域图像 + let region3 = captureGameRegion() + try { + // 在捕获的区域中查找OCR识别对象 + let res = region3.find(recognitionObjectOcr); + // 返回识别到的文本内容,如果不存在则返回undefined + return res?.text + } catch (e) { + // 捕获并记录错误信息 + log.error("识别异常:{1}", e.message) + return null + } finally { + // 确保释放区域资源 + region3.dispose() + } +} +/** + * OCR识别UID的异步函数 + * 该函数用于通过OCR技术识别屏幕上特定位置的UID文本 + * @returns {Promise} - 异步函数,没有明确的返回值 + */ +async function ocrUid() { + // 定义OCR识别的坐标和尺寸参数 + let uid_json = { + x: 1683, // OCR识别区域的左上角x坐标 + y: 1051, // OCR识别区域的左上角y坐标 + width: 234, // OCR识别区域的宽度 + height: 28, // OCR识别区域的高度 + } + let text = await ocrRegion(uid_json.x, uid_json.y, uid_json.width, uid_json.height); + return await saveOnlyNumber(text); +} + +export { + ocrUid, +}