diff --git a/repo/js/ActivitySwitchNotice/README.md b/repo/js/ActivitySwitchNotice/README.md index cd66b99ec..73c149c16 100644 --- a/repo/js/ActivitySwitchNotice/README.md +++ b/repo/js/ActivitySwitchNotice/README.md @@ -20,7 +20,136 @@ - ✅ 自动提醒征讨领域减半剩余次数(默认`周日`提醒可配置) - ✅ 支持独立通知功能(`0.0.4`版本新增 因BGI不支持WebSocket,需搭配bettergi-scripts-tools+开启JS HTTP 权限使用)[前往bettergi-scripts-tools部署](https://github.com/Kirito520Asuna/bettergi-scripts-tools) -## 逻辑流程 +## 核心思维导图 +### 整体架构流程图 +```mermaid +graph TD + A[主程序入口 main.js] --> B[初始化 init] + B --> C[加载工具模块] + C --> D[uid.js, ws.js, notice.js, campaignArea.js, activity.js] + D --> E[读取 manifest.json] + E --> F[检查是否返回主界面] + F --> G[toMainUi] + G --> H[执行主功能 main] + H --> I[每日委托检查] + I --> J[征讨领域检查] + J --> K[活动页面扫描] + K --> L[返回主界面] + L --> M[程序结束] + +``` +### 活动扫描核心流程 +```mermaid +graph TD + A[activityMain函数] --> B[初始化配置] + B --> C[打开活动页面 F5] + C --> D[滚动到页面顶部] + D --> E[开始逐页扫描] + E --> F[OCR识别活动列表] + F --> G{是否有活动?} + G -->|否| H[结束扫描] + G -->|是| I[遍历当前页活动] + I --> J[白名单过滤] + J --> K{是否在白名单?} + K -->|是| L[继续处理] + K -->|否| M[根据关系模式判断] + M --> N{relationship=true?} + N -->|是| O[跳过] + N -->|否| L[继续处理] + L --> P[黑名单过滤] + P --> Q{是否在黑名单?} + Q -->|是| R[条件检测] + Q -->|否| S[点击活动] + R --> T{条件满足?} + T -->|是| U[跳过] + T -->|否| S[点击活动] + S --> V[OCR识别剩余时间] + V --> W[解析时间转小时] + W --> X[应用阈值过滤] + X --> Y[存储活动信息] + Y --> Z[向下滚动一页] + Z --> E + H --> AA[过滤符合条件活动] + AA --> BB[按剩余时间排序] + BB --> CC[发送通知] + CC --> DD[流程结束] +``` +### 征讨领域提醒流程 +```mermaid +graph TD + A[campaignAreaMain函数] --> B[获取当前星期] + B --> C{是否为提醒日?} + C -->|否| D[结束函数] + C -->|是| E[打开冒险之书 F1] + E --> F[点击秘境坐标] + F --> G[点击征讨领域坐标] + G --> H[OCR识别周次数] + H --> I{剩余次数>0?} + I -->|否| J[结束函数] + I -->|是| K[发送通知] + K --> L[流程结束] + +``` +### 通知发送机制 +```mermaid +graph TD + A[sendNotice函数] --> B[检查通知类型配置] + B --> C[获取通知模式] + C --> D{BGI通知?} + D -->|是| E[notification.send] + C --> F{独立通知?} + F -->|是| G[wsUtil.sendText] + C --> H{两者都发送?} + H -->|是| E + H -->|是| G + E --> I[发送完成] + G --> I + I --> J[流程结束] +``` +### 配置解析流程 +```mermaid +graph TD + A[配置初始化] --> B[parseWhiteActivity] + A --> C[parseBlackActivity] + B --> D[白名单列表解析] + C --> E[黑名单条件解析] + D --> F[建立whiteActivityNameList] + E --> G[建立blackActivityMap] + F --> H[设置relationship逻辑] + G --> H + H --> I[配置完成] +``` +### 核心组件依赖关系 +```mermaid +graph LR + subgraph "工具模块" + A[activity.js - 活动处理] + B[notice.js - 通知发送] + C[campaignArea.js - 征讨领域] + D[ws.js - WebSocket通信] + E[uid.js - UID识别] + end + + subgraph "主控模块" + F[main.js - 主入口] + G[settings.json - 配置] + end + + F --> A + F --> B + F --> C + A --> B + A --> D + A --> E + C --> B + C --> E + G -.-> A + G -.-> B + G -.-> C + G -.-> D + +``` +### 逻辑流程 ```mermaid sequenceDiagram autonumber @@ -382,6 +511,8 @@ ActivitySwitchNotice/ ## 版本历史 +### 0.0.7 (2026-01-19) +- 新增每日委托提醒 ### 0.0.6 (2026-01-06) - **功能优化**:新增识别uid通知提醒 实例: diff --git a/repo/js/ActivitySwitchNotice/main.js b/repo/js/ActivitySwitchNotice/main.js index 2a5292d66..b4595058f 100644 --- a/repo/js/ActivitySwitchNotice/main.js +++ b/repo/js/ActivitySwitchNotice/main.js @@ -56,7 +56,11 @@ async function toMainUi() { * @returns {Promise} */ async function main() { - await campaignAreaUtil.campaignAreaMain() + let ms = 600 + await campaignAreaUtil.dailyCommissionMain() + await sleep(ms*2); + await campaignAreaUtil.campaignAreaMain(false) + await sleep(ms*2); await toMainUi() await activityUtil.activityMain() } \ No newline at end of file diff --git a/repo/js/ActivitySwitchNotice/manifest.json b/repo/js/ActivitySwitchNotice/manifest.json index e066fd741..07a2110b6 100644 --- a/repo/js/ActivitySwitchNotice/manifest.json +++ b/repo/js/ActivitySwitchNotice/manifest.json @@ -1,6 +1,6 @@ { "name": "活动期限/周本通知器", - "version": "0.0.6", + "version": "0.0.7", "description": "", "settings_ui": "settings.json", "main": "main.js", diff --git a/repo/js/ActivitySwitchNotice/utils/campaignArea.js b/repo/js/ActivitySwitchNotice/utils/campaignArea.js index dc24664f4..606c1c2d9 100644 --- a/repo/js/ActivitySwitchNotice/utils/campaignArea.js +++ b/repo/js/ActivitySwitchNotice/utils/campaignArea.js @@ -17,10 +17,50 @@ const config = { } const ocrRegionConfig = { weeklyCount: {x: 809, y: 258, width: 277, height: 37},//征讨领域减半次数识别区域坐标和尺寸 + dailyCommission: {x: 630, y: 312, width: 105, height: 118},//每日委托识别区域坐标和尺寸 } const xyConfig = { campaignArea: {x: 493, y: 537},//征讨领域坐标 secretRealm: {x: 304, y: 448},//秘境坐标 + dailyCommission: {x: 266, y: 318},//委托坐标 x=266, y=318, width=69, height=44 +} + +/** + * 每日委托OCR识别函数 + * @param {Object} ocrRegion - OCR识别区域配置,默认为ocrRegionConfig.dailyCommission + * @returns {Object} 返回包含每日委托和体力使用情况的对象 + */ +async function ocrDailyCommission(ocrRegion = ocrRegionConfig.dailyCommission) { + let captureRegion = captureGameRegion(); // 获取游戏区域截图 + try { + const ocrObject = RecognitionObject.Ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height); // 创建OCR识别对象 + // ocrObject.threshold = 1.0; + let resList = captureRegion.findMulti(ocrObject); // 在指定区域进行OCR识别 + let dailyCommission = resList[0].text?.trim(); + const lastName = dailyCommission.at(dailyCommission.length - 1); + + if (dailyCommission.lastIndexOf('1') === 1) { + // 0/4 被识别成==>014 + dailyCommission = dailyCommission.replace('1' + lastName, '/' + lastName) + } + const dailyCommissionSplit = dailyCommission.split('/'); + + // physical + let physical = resList[1].text?.trim(); + const physicalSplit = physical.split('/'); + return { + daily: { + total: parseInt(dailyCommissionSplit[1]), + use: parseInt(dailyCommissionSplit[0]), + }, + physical: { + total: parseInt(physicalSplit[1]), + use: parseInt(physicalSplit[0]), + }, + } + } finally { + captureRegion.dispose(); // 释放截图资源 + } } /** @@ -31,29 +71,32 @@ const xyConfig = { */ async function ocrWeeklyCount(ocrRegion = ocrRegionConfig.weeklyCount) { let captureRegion = captureGameRegion(); // 获取游戏区域截图 - const ocrObject = RecognitionObject.Ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height); // 创建OCR识别对象 - // ocrObject.threshold = 1.0; - let res = captureRegion.find(ocrObject); // 在指定区域进行OCR识别 - captureRegion.dispose(); // 释放截图资源 - if (!res.isExist()) { - log.error(`ocrWeeklyCount not found`) // 记录错误日志 - throw new Error(`ocrWeeklyCount not found`) // 抛出错误异常 - } - let weekJson = { // 初始化周计数JSON对象 - text: res.text, - total: 3, - count: 3, - } - let weekCountText = res.text // 获取OCR识别的文本结果 - let result = weekCountText.match(/[0-9/]+/g)?.join('') || ''; // 使用正则表达式提取数字和斜杠 + try { + const ocrObject = RecognitionObject.Ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height); // 创建OCR识别对象 + // ocrObject.threshold = 1.0; + let res = captureRegion.find(ocrObject); // 在指定区域进行OCR识别 + if (!res.isExist()) { + log.error(`ocrWeeklyCount not found`) // 记录错误日志 + throw new Error(`ocrWeeklyCount not found`) // 抛出错误异常 + } + let weekJson = { // 初始化周计数JSON对象 + text: res.text, + total: 3, + count: 3, + } + let weekCountText = res.text // 获取OCR识别的文本结果 + let result = weekCountText.match(/[0-9/]+/g)?.join('') || ''; // 使用正则表达式提取数字和斜杠 - log.debug(`识别结果:{weekCountText}`, weekCountText) // 记录原始识别结果 - log.debug(`处理结果:{result}`, result) // 记录处理后的结果 - const numbers = result.split('/').map((item) => parseInt(item)); // 分割字符串并转换为数字数组 - weekJson.total = numbers[1] // 设置总数 - weekJson.count = numbers[0] // 设置当前计数 - log.debug(`Json:{weekJson}`, weekJson) // 记录最终JSON结果 - return weekJson // 返回处理后的周计数JSON对象 + log.debug(`识别结果:{weekCountText}`, weekCountText) // 记录原始识别结果 + log.debug(`处理结果:{result}`, result) // 记录处理后的结果 + const numbers = result.split('/').map((item) => parseInt(item)); // 分割字符串并转换为数字数组 + weekJson.total = numbers[1] // 设置总数 + weekJson.count = numbers[0] // 设置当前计数 + log.debug(`Json:{weekJson}`, weekJson) // 记录最终JSON结果 + return weekJson // 返回处理后的周计数JSON对象 + } finally { + captureRegion.dispose(); // 释放截图资源 + } } /** @@ -82,7 +125,7 @@ async function getDayOfWeek() { * 执行秘境征讨剩余次数提醒的主函数 * 该函数会在每周日执行,检查秘境征讨的剩余次数并发送提醒 */ -async function campaignAreaMain() { +async function campaignAreaMain(openKey = true) { // 获取当前星期信息 let dayOfWeek = await getDayOfWeek(); // 如果不是周日(0代表周日),则直接返回 @@ -94,10 +137,12 @@ async function campaignAreaMain() { } // 设置操作间隔时间(毫秒) let ms = 600 - // 等待一段时间 - await sleep(ms) - // 按下配置的热键 - await keyPress(config.campaignAreaKey) + if (openKey) { + // 等待一段时间 + await sleep(ms) + // 按下配置的热键 + await keyPress(config.campaignAreaKey) + } await sleep(ms * 2) // 点击秘境入口坐标 await click(xyConfig.secretRealm.x, xyConfig.secretRealm.y) @@ -117,6 +162,41 @@ async function campaignAreaMain() { } +/** + * 每日委托主函数 + * @param {boolean} openKey - 是否开启热键功能,默认为true + * @returns {Promise} + */ +async function dailyCommissionMain(openKey = true) { + // 获取当前星期信息 + let dayOfWeek = await getDayOfWeek(); + // 如果不是周日(0代表周日), + const bool = dayOfWeek.day != config.campaignAreaReminderDay; + + // 设置操作间隔时间(毫秒) + let ms = 600 + // 等待一段时间 + await sleep(ms) + if (openKey || bool) { + // 按下配置的热键 + await keyPress(config.campaignAreaKey) + } + await sleep(ms * 2) + // 点击秘境入口坐标 + await click(xyConfig.dailyCommission.x, xyConfig.dailyCommission.y) + await sleep(ms * 2) + const re = await ocrDailyCommission(); + log.debug(`dailyCommission:{re}`, re) + // 如果有每日未完成/领取,则记录日志并发送通知 + if (re.daily.total > re.daily.use + || re.physical.total > re.physical.use + ) { + let uid = await uidUtil.ocrUID() + await noticeUtil.sendText(`>|每日委托奖励:${re.daily.use}/${re.daily.total}\n>|原粹树脂消耗:${re.physical.use}/${re.physical.total}`, `UID:${uid}\n每日委托`) + } +} + this.campaignAreaUtil = { campaignAreaMain, + dailyCommissionMain, } \ No newline at end of file