diff --git a/repo/js/ActivitySwitchNotice/README.md b/repo/js/ActivitySwitchNotice/README.md index 305d6431e..90e5d6ba0 100644 --- a/repo/js/ActivitySwitchNotice/README.md +++ b/repo/js/ActivitySwitchNotice/README.md @@ -1,8 +1,8 @@ -# 活动期限通知器 +# 活动期限/周本通知器 ## 项目概述 -这是一个用于《原神》游戏的自动化脚本工具,主要功能是自动检测游戏内活动的剩余时间,并在活动即将结束时发送通知提醒玩家。 +这是一个用于《原神》游戏的自动化脚本工具,主要功能是自动检测游戏内活动的剩余时间,并在活动即将结束时/每周指定日期自动提醒征讨领域减半剩余次数发送通知提醒玩家。 ## 功能特性 @@ -12,8 +12,10 @@ - ✅ 智能解析剩余时间(支持"22天14小时"等格式) - ✅ 可配置的通知阈值(默认8760小时内结束的活动) - ✅ 支持指定特定活动进行监控 +- ✅ 支持活动黑名单过滤功能 - ✅ 防重复检测机制 - ✅ 异常处理和错误恢复 +- ✅ 自动提醒征讨领域减半剩余次数(默认`周日`提醒可配置) --- @@ -22,23 +24,31 @@ ### 快速开始 #### 1. 安装与配置 + - 确保游戏分辨率为 **1920×1080**(推荐分辨率) - 将脚本导入到 BetterGI 脚本管理器中 - 在脚本设置界面进行个性化配置 #### 2. 基础设置 -在 [settings.json]() 中可以配置以下参数: -| 设置项 | 说明 | 默认值 | -|--------|------|--------| -| `toMainUi` | 执行前是否自动返回游戏主界面 | true | -| `activityNameList` | 监控的特定活动名称(用\|分隔) | 空(监控所有活动) | -| `notifyHoursThreshold` | 通知时间阈值(小时) | 8760(365天) | -| `activityKey` | 打开活动页面的快捷键 | F5 | +在 `settings.json` 中可以配置以下参数: + +| 设置项 | 说明 | 默认值 | 开放 | +|:---------------------------|:-----------------------------------------------|:------------|:--:| +| `toMainUi` | 执行前是否自动返回游戏主界面 | true | v | +| `activityNameList` | 监控的特定活动名称(用\|分隔) | 空(监控所有活动) | v | +| `blackActivityNameList` | 黑名单活动名称(用\|分隔) | 空(无黑名单活动) | v | +| `notifyHoursThreshold` | 通知时间阈值(小时) | 8760(365天) | v | +| `activityKey` | 打开活动页面的快捷键 | F5 | v | +| `toTopCount` | 滑动到顶最大尝试次数 | 10 | x | +| `scrollPageCount` | 滑动次数/页 | 4 | x | +| `campaignAreaKey` | 打开征讨领域页面的快捷键 | F1 | v | +| `campaignAreaReminderDay` | 周本提醒日(0-6,0=周日,1=周一,2=周二,3=周三,4=周四,5=周五,6=周六) | 0 | v | ### 使用流程 #### 自动模式(推荐) + 1. 启动脚本后,程序会自动: - 检测当前是否在游戏主界面 - 如未在主界面,自动返回主界面 @@ -46,6 +56,7 @@ - 开始扫描所有活动 #### 手动模式 + 1. 关闭 `toMainUi` 选项 2. 确保游戏处于主界面状态 3. 启动脚本开始扫描 @@ -53,15 +64,19 @@ ### 功能详解 #### 活动筛选 + - **全部活动监控**:`activityNameList` 保持空值,监控所有有剩余时间的活动 - **指定活动监控**:填写活动关键词,如 `海灯节\|盛典`,只监控包含这些关键词的活动 +- **黑名单过滤**:`blackActivityNameList` 可以设置不想接收提醒的活动名称,多个活动用`|`分隔 #### 时间通知机制 + - 默认监控所有活动(`notifyHoursThreshold`=8760小时) - 可设置阈值,如设置为24,则只通知剩余时间≤24小时的活动 -- 即将结束的活动会在通知中标记 `<即将结束>` +- 即将结束(24小时内)的活动会在通知中标记 `<即将结束>` #### 智能防重复 + - 自动识别已扫描过的活动页面 - 防止因页面滚动不准确造成的重复识别 - 自动判断是否已滚动到页面底部 @@ -69,11 +84,13 @@ ### 注意事项 #### 使用环境要求 + - ✅ 游戏分辨率为 1920×1080(最佳兼容性) - ✅ 游戏处于前台运行状态 - ✅ 活动页面可通过设置的快捷键正常打开 #### 运行期间注意事项 + - 🚫 运行期间请勿手动操作鼠标 - 🚫 避免切换窗口或最小化游戏 - ⚠️ 如遇异常可重新启动脚本 @@ -81,10 +98,14 @@ ### 高级配置 #### 自定义快捷键 + 如活动页面不是F5打开,可在 `activityKey` 中修改为对应按键。 +如冒险之书页面不是F1打开,可在 `campaignAreaKey` 中修改为对应按键。 #### 时间阈值设置 + 根据个人需求设置 `notifyHoursThreshold`: + - 24:只关注24小时内结束的活动 - 168:关注一周内结束的活动 - 720:关注一个月内结束的活动 @@ -92,22 +113,25 @@ ### 输出示例 通知消息格式如下: + ``` 原神活动剩余时间提醒: > 海灯节庆典 剩余时间:3天14小时<还剩 86 小时> > 风花节活动 剩余时间:1天5小时<还剩 29 小时><即将结束> ``` + + **`以上为用户使用指南全部内容`** --- - ## 文件结构 ``` ActivitySwitchNotice/ ├── utils/ │ ├── activity.js # 核心活动处理逻辑 +│ ├── campaignArea.js # 征讨领域提醒功能 │ └── notice.js # 通知发送功能 ├── main.js # 主入口文件 ├── manifest.json # 插件配置文件 @@ -116,9 +140,10 @@ ActivitySwitchNotice/ ``` + ## 核心模块 -### [activity.js]() - 活动处理核心 +### `activity.js` - 活动处理核心 主要包含以下功能函数: @@ -132,33 +157,40 @@ ActivitySwitchNotice/ ### `notice.js` - 通知模块 - `sendNotice()` - 发送活动提醒通知,按剩余时间排序 +- `send()` - 发送普通通知 -### [main.js]() - 主程序入口 +### `campaignArea.js` - 征讨领域模块 -- 检测是否在主界面 -- 返回主界面功能 -- 执行主流程 +- `ocrWeeklyCount()` - OCR识别征讨领域周次数 +- `getDayOfWeek()` - 获取当前星期信息 +- `campaignAreaMain()` - 征讨领域提醒主函数 ## 配置选项 -在 [settings.json]() 中可配置以下参数: +在 `settings.json` 中可配置以下参数: -| 配置项 | 类型 | 说明 | -|--------|------|------| -| `toMainUi` | Boolean | 是否先返回主界面再执行 | -| `activityNameList` | String | 指定活动名称(用\|分隔) | -| `notifyHoursThreshold` | Number | 通知阈值(小时) | -| `activityKey` | String | 打开活动页面的快捷键 | +| 配置项 | 类型 | 说明 | +|:---------------------------|:-------:|:--| +| `toMainUi` | Boolean | 是否先返回主界面再执行 | +| `activityNameList` | String | 指定活动名称(用\|分隔) | +| `blackActivityNameList` | String | 黑名单活动名称(用\|分隔) | +| `notifyHoursThreshold` | Number | 通知阈值(小时) | +| `activityKey` | String | 打开活动页面的快捷键 | +| `toTopCount` | Number | 滑动到顶最大尝试次数 | +| `scrollPageCount` | Number | 滑动次数/页 | +| `campaignAreaKey` | String | 打开冒险之书页面的快捷键 | +| `campaignAreaReminderDay` | Number | 周本提醒日(0-6,0=周日,1=周一,2=周二,3=周三,4=周四,5=周五,6=周六) | ## 工作原理 1. 自动返回游戏主界面 -2. 按配置快捷键打开活动页面 -3. 滚动到活动列表顶部 -4. 逐页扫描所有活动 -5. OCR识别每个活动的剩余时间 -6. 解析时间为小时数并过滤 -7. 发送符合条件的活动提醒 +2. 检查是否为设置的提醒日(默认周日),如果是则执行征讨领域提醒功能 +3. 按配置快捷键打开活动页面 +4. 滚动到活动列表顶部 +5. 逐页扫描所有活动 +6. OCR识别每个活动的剩余时间 +7. 解析时间为小时数并过滤(包括黑名单过滤) +8. 发送符合条件的活动提醒 ## 注意事项 @@ -169,6 +201,35 @@ ActivitySwitchNotice/ --- +## 版本历史 + +### 0.0.2 (2025-12-22) + +- 新增 征讨领域周次数提醒功能 +- 新增 `campaignArea.js` 模块,包含征讨领域相关功能 +- 新增 `campaignAreaKey` 配置选项,用于自定义冒险之书页面快捷键 +- 新增 `campaignAreaReminderDay` 配置选项,用于配置提醒日 +- 改进 增强滚动到顶部功能的稳定性 +- 新增 活动黑名单过滤功能,支持通过 `blackActivityNameList` 配置项排除不关心的活动 +- 新增 特殊活动时间格式支持,针对"砺行修远"等活动提供周数显示 +- 新增 额外OCR识别支持,可识别特定活动的附加信息(如"本周进度") +- 改进 活动过滤逻辑,增强黑名单匹配准确性 +- 改进 通知显示格式,增加活动描述信息展示 +- 修复 若干已知问题,提升脚本稳定性 + +### 0.0.1 (2025-12-21) + +- 新增 活动期限检测与通知功能 +- 新增 OCR识别活动列表和剩余时间 +- 新增 自动滚动浏览所有活动页面 +- 新增 智能解析剩余时间(支持"22天14小时"等格式) +- 新增 可配置的通知阈值功能 +- 新增 指定特定活动监控功能 +- 新增 防重复检测机制 +- 新增 异常处理和错误恢复机制 + +--- + ## 其它 作者:云端客 diff --git a/repo/js/ActivitySwitchNotice/main.js b/repo/js/ActivitySwitchNotice/main.js index cf88b1920..5a647dd23 100644 --- a/repo/js/ActivitySwitchNotice/main.js +++ b/repo/js/ActivitySwitchNotice/main.js @@ -1,5 +1,6 @@ eval(file.readTextSync(`utils/activity.js`)); eval(file.readTextSync(`utils/notice.js`)); +eval(file.readTextSync(`utils/campaignArea.js`)); // 判断是否在主界面的函数 const isInMainUI = () => { @@ -42,5 +43,7 @@ async function toMainUi() { * @returns {Promise} */ async function main() { + await campaignAreaUtil.campaignAreaMain() + 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 67e54cb94..36a15f237 100644 --- a/repo/js/ActivitySwitchNotice/manifest.json +++ b/repo/js/ActivitySwitchNotice/manifest.json @@ -1,6 +1,6 @@ { - "name": "活动期限通知器", - "version": "0.0.1", + "name": "活动期限/周本通知器", + "version": "0.0.2", "description": "", "settings_ui": "settings.json", "main": "main.js", diff --git a/repo/js/ActivitySwitchNotice/settings.json b/repo/js/ActivitySwitchNotice/settings.json index e2d093dec..289b46f59 100644 --- a/repo/js/ActivitySwitchNotice/settings.json +++ b/repo/js/ActivitySwitchNotice/settings.json @@ -10,6 +10,11 @@ "type": "input-text", "label": "活动名称(使用|分割)<可不填 默认推送所有有剩余时间的活动>" }, + { + "name": "blackActivityNameList", + "type": "input-text", + "label": "黑名单活动名称(使用|分割)<可不填,默认没有不推送的活动>" + }, { "name": "notifyHoursThreshold", "type": "input-text", @@ -21,5 +26,21 @@ "type": "input-text", "label": "打开活动页面按键(不填,默认:F5)", "default": "F5" - } + }, + { + "name": "campaignAreaReminderDay", + "type": "select", + "label": "周本提醒日(0-6,0=周日,1=周一,2=周二,3=周三,4=周四,5=周五,6=周六)", + "options": [ + "0","1","2","3","4","5","6" + ], + "default": "0" + }, + { + "name": "campaignAreaKey", + "type": "input-text", + "label": "打开冒险之证按键(不填,默认:F1)", + "default": "F1" + }, + ] diff --git a/repo/js/ActivitySwitchNotice/utils/activity.js b/repo/js/ActivitySwitchNotice/utils/activity.js index 1698768f9..1d56832a4 100644 --- a/repo/js/ActivitySwitchNotice/utils/activity.js +++ b/repo/js/ActivitySwitchNotice/utils/activity.js @@ -10,9 +10,10 @@ function settingsParseInt(str, defaultValue) { const config = { activityNameList: (settings.activityNameList ? settings.activityNameList.split('|') : []), activityKey: (settings.activityKey ? settings.activityKey : 'F5'), - toTopCount: settingsParseInt(settings.toTopCount,10),//滑动到顶最大尝试次数 - scrollPageCount: settingsParseInt(settings.scrollPageCount,4),//滑动次数/页 + toTopCount: settingsParseInt(settings.toTopCount, 10),//滑动到顶最大尝试次数 + scrollPageCount: settingsParseInt(settings.scrollPageCount, 4),//滑动次数/页 notifyHoursThreshold: settingsParseInt(settings.notifyHoursThreshold, 8760),//剩余时间阈值(默认 8760小时=365天) + blackActivityNameList: (settings.blackActivityNameList ? settings.blackActivityNameList.split('|').filter(s => s.trim()) : []),//黑名单活动名称 } const ocrRegionConfig = { activity: {x: 267, y: 197, width: 226, height: 616},//活动识别区域坐标和尺寸 @@ -22,12 +23,37 @@ const xyConfig = { top: {x: 344, y: 273}, bottom: {x: 342, y: 791}, } - +const DATE_ENUM = Object.freeze({ + YEAR: '年', + MON: '月', + WEEK: '周', + DAY: '天', + HOUR: '小时', + // 添加反向映射(可选) + fromValue(value) { + return Object.keys(this).find(key => this[key] === value); + } +}); +const activityTermConversionMap = new Map([ + ["砺行修远", {dateEnum: DATE_ENUM.WEEK}], +]); +const needOcrOtherMap = new Map([ + ["砺行修远", ["本周进度", "完成进度"]], +]); const genshinJson = { width: 1920,//genshin.width, height: 1080,//genshin.height, } + +function getDATE_ENUM(activityName) { + for (let key of activityTermConversionMap.keys()) { + if (activityName.includes(key)) + return activityTermConversionMap.get(key) + } + return {dateEnum: DATE_ENUM.HOUR} +} + /** * 滚动页面的异步函数 * @param {number} totalDistance - 总滚动距离 @@ -263,6 +289,32 @@ function formatRemainingTime(timeText) { return `${totalHours}小时(${days > 0 ? days + '天' : ''}${hours > 0 ? hours + '小时' : ''})`; } +/** + * 将总小时数转换为周、天和小时的组合表示 + * @param {number} totalHours - 需要转换的总小时数 + * @returns {string} 返回格式为"X周 Y天 Z小时"的字符串 + */ +function convertHoursToWeeksDaysHours(totalHours) { + // 1周 = 168小时 (7 * 24) + const hoursPerWeek = 168; + const hoursPerDay = 24; + + // 计算整周数 - 使用Math.floor获取完整的周数 + const weeks = Math.floor(totalHours / hoursPerWeek); + + // 剩余小时 - 总小时数减去完整周数对应的小时数 + let remainingHours = totalHours % hoursPerWeek; + + // 从剩余小时中计算天数 - 使用Math.floor获取完整的天数 + const days = Math.floor(remainingHours / hoursPerDay); + + // 剩余的小时 - 剩余小时数减去完整天数对应的小时数 + const hours = remainingHours % hoursPerDay; + + // 返回格式化后的字符串,包含周、天和小时 + return `${weeks}周 ${days}天 ${hours}小时`; +} + /** * OCR识别活动剩余时间的函数 * @param {Object} ocrRegion - OCR识别的区域坐标和尺寸 @@ -378,6 +430,13 @@ async function activityMain() { } } + if (config.blackActivityNameList.length > 0) { + const matched = config.blackActivityNameList.some(keyword => activityName.includes(keyword)); + if (matched) { + continue; // 不关心的活动,跳过不点击 + } + } + // 避免重复点击同一个活动(防止 OCR 误识别或页面抖动) if (activityMap.has(activityName)) { log.info(`活动已记录,跳过重复点击: ${activityName}`); @@ -391,9 +450,32 @@ async function activityMain() { if (totalHours <= 24 && totalHours > 0) { remainingTimeText += '<即将结束>' } + let desc = "" + + let dateEnum = getDATE_ENUM(activityName); + log.debug(`activityName:{activityName},dateEnum:{dateenum.dateEnum}`, activityName, dateEnum.dateEnum) + switch (dateEnum.dateEnum) { + case DATE_ENUM.WEEK: + desc += "|==>" + convertHoursToWeeksDaysHours(totalHours) + "<==|"; + break; + case DATE_ENUM.HOUR: + break; + default: + break; + } + if (needOcrOtherMap.has(activityName)) { + const keys = needOcrOtherMap.get(activityName); + for (const key of keys) { + let text = await OcrRemainingTime(activityName, key); + if (text) { + remainingTimeText += " [" + text + "] " + } + } + } activityMap.set(activityName, { text: remainingTimeText, - hours: totalHours + hours: totalHours, + desc: desc }); log.info(`成功记录 → {activityName} {remainingTime} 共计: {hours} 小时`, activityName, remainingTimeText, totalHours); newActivityCountThisPage++; @@ -427,13 +509,13 @@ async function activityMain() { await sleep(ms); } let activityMapFilter = new Map(); - Array.from(activityMap.entries()) + Array.from(activityMap.entries()) .filter(([name, info]) => info.hours <= config.notifyHoursThreshold) .forEach(([name, info]) => activityMapFilter.set(name, info)); // 7. 全部扫描完毕,统一发送通知(只发一次!) if (activityMapFilter.size > 0) { log.info(`扫描完成,共记录 {activityMap.size} 个活动,即将发送通知`, activityMapFilter.size); - await noticeUtil.sendNotice(activityMapFilter, `原神活动剩余时间提醒(仅显示剩余 ≤ ${config.notifyHoursThreshold} 小时的活动):`); + await noticeUtil.sendNotice(activityMapFilter, `原神活动剩余时间提醒(仅显示剩余 ≤ ${config.notifyHoursThreshold} 小时的活动)${config.blackActivityNameList.length <= 0 ? "" : "|==>已开启黑名单:" + config.blackActivityNameList.join(",") + "<==|"}`); } else { log.warn("未识别到任何活动,未发送通知"); } diff --git a/repo/js/ActivitySwitchNotice/utils/campaignArea.js b/repo/js/ActivitySwitchNotice/utils/campaignArea.js new file mode 100644 index 000000000..f2216f7c6 --- /dev/null +++ b/repo/js/ActivitySwitchNotice/utils/campaignArea.js @@ -0,0 +1,121 @@ +function settingsParseInt(str, defaultValue) { + try { + return str ? parseInt('' + str) : defaultValue; + } catch (e) { + log.warn(`settingsParseInt error:${e}`) + return defaultValue; + } +} + +function settingsParseStr(str, defaultValue) { + return '' + (str ? str : defaultValue); +} + +const config = { + campaignAreaKey: settingsParseStr(settings.campaignAreaKey, 'F1'), + campaignAreaReminderDay: settingsParseInt(settings.campaignAreaReminderDay, 0),//征讨领域提醒日 +} +const ocrRegionConfig = { + weeklyCount: {x: 809, y: 258, width: 277, height: 37},//征讨领域减半次数识别区域坐标和尺寸 +} +const xyConfig = { + campaignArea: {x: 493, y: 537},//征讨领域坐标 + secretRealm: {x: 304, y: 448},//秘境坐标 +} + +/** + * OCR识别周计数函数 + * @param {Object} ocrRegion - OCR识别区域配置,默认为ocrRegionConfig.weeklyCount + * @returns {Object} 返回包含周计数信息的JSON对象,包含text、total和count属性 + * @throws {Error} 当OCR识别失败时抛出错误 + */ +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('') || ''; // 使用正则表达式提取数字和斜杠 + + 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对象 +} + +/** + * 获取当前日期的星期信息 + * @returns {Object} 返回包含星期数字和星期名称的对象 + */ +async function getDayOfWeek() { + // 获取当前日期对象 + const today = new Date(); + // 获取当前日期是星期几(0代表星期日,1代表星期一,以此类推) + const day = today.getDay(); + // 创建包含星期名称的数组 + const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; + let weekDay = `${weekDays[day]}`; + + log.debug(`今天是[{day}]`, day) + log.debug(`今天是[{weekDays}]`, weekDay) + // 返回包含星期数字和对应星期名称的对象 + return { + day: day, + dayOfWeek: weekDay + } +} + +/** + * 执行秘境征讨剩余次数提醒的主函数 + * 该函数会在每周日执行,检查秘境征讨的剩余次数并发送提醒 + */ +async function campaignAreaMain() { + // 获取当前星期信息 + let dayOfWeek = await getDayOfWeek(); + // 如果不是周日(0代表周日),则直接返回 + if (dayOfWeek.day != config.campaignAreaReminderDay) { + log.info(`[{dayOfWeek.dayOfWeek}],跳过执行秘境征讨剩余次数提醒`, dayOfWeek.dayOfWeek) + return + } + // 记录开始执行秘境征讨提醒的日志 + log.info(`[{dayOfWeek.dayOfWeek}],开始执行秘境征讨剩余次数提醒`, dayOfWeek.dayOfWeek) + // 设置操作间隔时间(毫秒) + let ms = 600 + // 等待一段时间 + await sleep(ms) + // 按下配置的热键 + await keyPress(config.campaignAreaKey) + await sleep(ms * 2) + // 点击秘境入口坐标 + await click(xyConfig.secretRealm.x, xyConfig.secretRealm.y) + await sleep(ms * 2) + // 点击秘境征讨坐标 + await click(xyConfig.campaignArea.x, xyConfig.campaignArea.y) + await sleep(ms * 2) + // 使用OCR识别本周秘境征讨剩余次数 + let weekJson = await ocrWeeklyCount(); + + // 如果有剩余次数,则记录日志并发送通知 + if (weekJson.count > 0) { + log.info(`本周剩余消耗减半次数:${weekJson.count}`) + await noticeUtil.send(`>|本周剩余消耗减半次数:${weekJson.count}`, '秘境征讨') + } + +} + +this.campaignAreaUtil = { + campaignAreaMain, +} \ No newline at end of file diff --git a/repo/js/ActivitySwitchNotice/utils/notice.js b/repo/js/ActivitySwitchNotice/utils/notice.js index 2270fca24..2637aa166 100644 --- a/repo/js/ActivitySwitchNotice/utils/notice.js +++ b/repo/js/ActivitySwitchNotice/utils/notice.js @@ -14,14 +14,36 @@ async function sendNotice(map, title, noNotice) { const sortedEntries = Array.from(map.entries()) .sort((a, b) => a[1].hours - b[1].hours); - let noticeText = title ? title + "\n" : "\n" + let noticeText = title ? title + "\n======\n" : "\n" for (const [name, info] of sortedEntries) { - noticeText += `> ${name} ${info.text}<还剩 ${info.hours} 小时>\n`; + noticeText += `> ${name} ${info.text} (还剩 ${info.hours} 小时) ${info.desc}\n----\n`; } // 发送通知 notification.send(noticeText) } +/** + * 异步发送通知的函数 + * @param {string} noticeText - 通知内容文本 + * @param {string} title - 通知标题 + * @param {boolean} noNotice - 是否不发送通知的标志 + */ +async function send(noticeText, title, noNotice) { + // 检查是否有通知内容且设置了不发送通知的标志 + if (noticeText&&noNotice) { + log.info(`无通知内容`) // 记录日志信息 + return // 直接返回,不执行后续操作 + } + // 构建通知文本,如果有标题则先添加标题 + let text = title ? title + "\n======\n" : "\n" + // 添加通知内容 + text += noticeText + // 发送通知 + notification.send(text) + +} + this.noticeUtil = { sendNotice, + send, } \ No newline at end of file