From 02ff2fba7892a930f3138aba8c727d70090e8071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BA=81=E5=8A=A8=E7=9A=84=E6=B0=A8=E6=B0=94?= <131591012+zaodonganqi@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:10:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=87=AA=E5=8A=A8=E5=85=91?= =?UTF-8?q?=E6=8D=A2=E7=A0=81=20(#2024)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 优化自动兑换码 * 优化自动兑换码 * 修改readme --- repo/js/AutoCode/README.md | 21 ++++- repo/js/AutoCode/codes.txt | 1 + repo/js/AutoCode/main.js | 151 +++++++++++++++++++++------------ repo/js/AutoCode/manifest.json | 10 ++- repo/js/AutoCode/settings.json | 8 ++ 5 files changed, 132 insertions(+), 59 deletions(-) create mode 100644 repo/js/AutoCode/settings.json diff --git a/repo/js/AutoCode/README.md b/repo/js/AutoCode/README.md index 976277968..ea0cfc703 100644 --- a/repo/js/AutoCode/README.md +++ b/repo/js/AutoCode/README.md @@ -1,3 +1,18 @@ -兑换码+截止时间存储地址codes.txt -格式 兑换码,xxxx.xx.xx xx:xx:xx -仅支持国服 \ No newline at end of file +# 原神兑换码自动兑换工具使用说明 + +## 准备工作 +若你兑换的是自己通过某些渠道获得的专属兑换码(如联动套餐), +请在`BetterGI\User\JsScript\AutoCode\codes.txt`中按照`兑换码,截止时间`的格式填入兑换码(格式务必参考已有内容,且使用英文逗号) +若你兑换的是公共渠道的兑换码(如前瞻直播、周年庆福利兑换码等) ,那恭喜你,不需要任何准备工作!直接点击即用! + +## 用户名设置(多用户使用) +添加脚本进调度器后,右键调度器内js脚本,点击 `修改js脚本自定义设置` ,在其中设置用户名 + +### 用户名规则: + +```json +支持中文、英文、数字 + +长度 1-20 个字符 + +如未设置或格式错误,将使用默认用户名 "default" \ No newline at end of file diff --git a/repo/js/AutoCode/codes.txt b/repo/js/AutoCode/codes.txt index 66ddf6846..8683dcc10 100644 --- a/repo/js/AutoCode/codes.txt +++ b/repo/js/AutoCode/codes.txt @@ -4,3 +4,4 @@ VYKGJWZHY2AN,2025.10.20 00:00:00 CY2H2XHYZKCS,2025.10.20 00:00:00 NG3ZJXHYG3CW,2025.10.20 00:00:00 NG2Z3EHYYKVJ,2025.10.20 00:00:00 +原神5周年快乐,2025.11.1 00:00:00 diff --git a/repo/js/AutoCode/main.js b/repo/js/AutoCode/main.js index 43a32dd97..96dfafa97 100644 --- a/repo/js/AutoCode/main.js +++ b/repo/js/AutoCode/main.js @@ -1,117 +1,158 @@ +let username = settings.useename || "default"; + (async function () { - setGameMetrics(1920, 1080, 1); - // 1. 返回主界面,等待1秒 + setGameMetrics(1920, 1080, 1); + + // 1. 返回主界面 await genshin.returnMainUi(); await sleep(1000); - // 2. 通过keyPress点按esc键(VK_ESCAPE),等待2秒。ocr识别设置图片并点击,等待2秒。识别账户图片并点击,等待0.5秒,识别前往兑换图片并点击,等待0.5秒 + // 2. 检验设置用户名 + function getUsername() { + username = username.trim(); + // 只允许 中文 / 英文 / 数字,长度 1~20 + if (!username || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(username)) { + log.error(`用户名${username}违规,暂时使用默认用户名,请查看readme后修改`) + username = "default"; + } + return username; + } + + username = getUsername(); + const recordPath = `record/record_${username}.txt`; + + // 3. 读取已兑换记录 + let redeemedCodes = new Set(); + try { + const recordContent = file.readTextSync(recordPath); + if (recordContent) { + redeemedCodes = new Set(recordContent.split("\n").map(l => l.trim()).filter(Boolean)); + } + } catch (e) { + log.warn(`未找到 ${recordPath},稍后会自动创建`); + } + + // 4. 打开兑换界面 keyPress("ESCAPE"); await sleep(2000); const settingsRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/settings.png")); const settingsRes = captureGameRegion().find(settingsRo); - if (settingsRes.isExist()) { - settingsRes.click(); - } + if (settingsRes.isExist()) settingsRes.click(); await sleep(2000); const accountRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/account.png")); const accountRes = captureGameRegion().find(accountRo); - if (accountRes.isExist()) { - accountRes.click(); - } + if (accountRes.isExist()) accountRes.click(); await sleep(500); const goToRedeemRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/go_to_redeem.png")); const goToRedeemRes = captureGameRegion().find(goToRedeemRo); - if (goToRedeemRes.isExist()) { - goToRedeemRes.click(); - } + if (goToRedeemRes.isExist()) goToRedeemRes.click(); await sleep(500); - // 3. 新建一个txt用于存储兑换码及截止时间,之间换行区分,格式为【兑换码,截止时间】 + // 5. 读取 codes.txt 进行兑换 try { const content = file.readTextSync("codes.txt"); - const codes = content.split('\n'); + const codes = content.split("\n"); for (let i = 0; i < codes.length; i++) { - const codeInfo = codes[i].split(','); - const code = codeInfo[0]; - const deadline = codeInfo[1]; + const line = codes[i].trim(); + if (!line) continue; - // a. 获取当前时间【xxxx.xx.xx xx:xx:xx】(年月日时分秒),与截止时间进行对比 + // 找到最后一个英文逗号 + const lastCommaIndex = line.lastIndexOf(','); + let code, deadline; + if (lastCommaIndex === -1) { + // 没有逗号,则整个当作code + code = line; + deadline = ''; + } else { + code = line.slice(0, lastCommaIndex).trim(); + deadline = line.slice(lastCommaIndex + 1).trim(); + } + + if (!code) continue; + + // 跳过已兑换的 + if (redeemedCodes.has(code)) { + log.info(`检测到${redeemedCodes.size}个兑换码已兑换过,跳过`); + continue; + } + + // 时间检查 const now = new Date(); const currentTime = now.getFullYear() + '.' + String(now.getMonth() + 1).padStart(2, '0') + '.' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0'); - if (currentTime > deadline) { log.info(`兑换码【${code}】已超过截止时间,跳过`); continue; } - // b. 识别输入兑换码图片并点击 + // 输入兑换码 const inputCodeRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/input_code.png")); const inputCodeRes = captureGameRegion().find(inputCodeRo); - if (inputCodeRes.isExist()) { - inputCodeRes.click(); - } + if (inputCodeRes.isExist()) inputCodeRes.click(); await sleep(300); - // c. 通过虚拟键代码依次keyPress键入兑换码的每一个字符 await inputText(code); - await sleep(500); + await sleep(500); - // d. 输入完毕后,识别兑换图片并点击,等待1.5秒 + // 点击兑换按钮 const redeemRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/redeem.png")); const redeemRes = captureGameRegion().find(redeemRo); - if (redeemRes.isExist()) { - redeemRes.click(); - } + if (redeemRes.isExist()) redeemRes.click(); await sleep(1500); - // e. 识别无效图片、已使用图片、过期图片、确认图片、未开启图片 - const invalidRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/invalid.png")); - const invalidRes = captureGameRegion().find(invalidRo); - if (invalidRes.isExist()) { - log.info(`兑换码【${code}】无效`); - } + // 检测各种状态 + const invalidRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/invalid.png"))); + if (invalidRes.isExist()) log.info(`兑换码【${code}】无效`); - const usedRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/used.png")); - const usedRes = captureGameRegion().find(usedRo); + const usedRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/used.png"))); if (usedRes.isExist()) { - log.info(`兑换码【${code}】已使用`); + // 写入记录 + const writeOk = file.writeTextSync(recordPath, code + "\n", true); + if (writeOk) { + log.info(`兑换码【${code}】已使用`); + redeemedCodes.add(code); + } } - const expiredRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/expired.png")); - const expiredRes = captureGameRegion().find(expiredRo); + const expiredRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/expired.png"))); if (expiredRes.isExist()) { - log.info(`兑换码【${code}】已过期`); + // 写入记录 + const writeOk = file.writeTextSync(recordPath, code + "\n", true); + if (writeOk) { + log.info(`兑换码【${code}】已过期`); + redeemedCodes.add(code); + } } - const notopenRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/not_open.png")); - const notopenRes = captureGameRegion().find(notopenRo); - if (notopenRes.isExist()) { - log.info(`兑换码【${code}】未开启`); - } + const notopenRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/not_open.png"))); + if (notopenRes.isExist()) log.info(`兑换码【${code}】未开启`); - const confirmRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/confirm.png")); - const confirmRes = captureGameRegion().find(confirmRo); + const confirmRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/confirm.png"))); if (confirmRes.isExist()) { log.info(`兑换码【${code}】成功兑换`); confirmRes.click(); + + // 写入记录 + const writeOk = file.writeTextSync(recordPath, code + "\n", true); + if (writeOk) { + log.info(`已记录兑换码【${code}】到 ${recordPath}`); + redeemedCodes.add(code); + } } - // f. 识别清除图片并点击,若未识别到则不做处理 - const clearRo = RecognitionObject.TemplateMatch(file.readImageMatSync("assets/clear.png")); - const clearRes = captureGameRegion().find(clearRo); - if (clearRes.isExist()) { - clearRes.click(); - } + // 清除输入 + const clearRes = captureGameRegion().find(RecognitionObject.TemplateMatch(file.readImageMatSync("assets/clear.png"))); + if (clearRes.isExist()) clearRes.click(); + await sleep(4000); } } catch (error) { log.error(`读取兑换码文件失败: ${error}`); } - // 4. 所有兑换码兑换完成后返回主界面 + // 6. 返回主界面 await genshin.returnMainUi(); })(); \ No newline at end of file diff --git a/repo/js/AutoCode/manifest.json b/repo/js/AutoCode/manifest.json index 6488b31a6..824cdae1e 100644 --- a/repo/js/AutoCode/manifest.json +++ b/repo/js/AutoCode/manifest.json @@ -8,8 +8,16 @@ { "name": "Tool_tingsu", "links": "https://github.com/Tooltingsu" + }, + { + "name": "躁动的氨气", + "links": "https://github.com/zaodonganqi" } ], - "main": "main.js" + "settings_ui": "settings.json", + "main": "main.js", + "saved_files": [ + "record/*.txt" + ] } diff --git a/repo/js/AutoCode/settings.json b/repo/js/AutoCode/settings.json new file mode 100644 index 000000000..3c8922ab7 --- /dev/null +++ b/repo/js/AutoCode/settings.json @@ -0,0 +1,8 @@ +[ + { + "name": "username", + "type": "input-text", + "label": "账户名称\n用于多账户运行时区分不同账户", + "default": "默认账户" + } +] \ No newline at end of file