diff --git a/apps/manage/index.js b/apps/manage/index.js index 46e47d5..0f01f7b 100644 --- a/apps/manage/index.js +++ b/apps/manage/index.js @@ -12,6 +12,8 @@ import * as version from './version.js'; import * as device from './device.js'; +import * as remind from './remind.js'; + export default { assets, guides, @@ -20,4 +22,5 @@ export default { panel, version, device, + remind, }; diff --git a/apps/manage/remind.js b/apps/manage/remind.js new file mode 100644 index 0000000..00a0407 --- /dev/null +++ b/apps/manage/remind.js @@ -0,0 +1,29 @@ +import settings from '../../lib/settings.js'; +import { rulePrefix } from '../../lib/common.js'; +import ZZZPlugin from '../../lib/plugin.js'; + +export class RemindManage extends ZZZPlugin { + constructor() { + super({ + name: '[ZZZ-Plugin]RemindManage', + dsc: '提醒功能管理', + event: 'message', + priority: 40, // 管理插件优先级较高 + rule: [ + { + reg: `${rulePrefix}设置提醒时间\\s*(.+)`, + fnc: 'setCron', + permission: 'master', + }, + ], + }); + } + + async setCron() { + const match = this.e.msg.match(/设置提醒时间\s*(.+)/); + if (!match) return; + const cron = match.trim(); + settings.setSingleConfig('remind', 'cron', cron); + await this.reply(`式舆防卫战/危局强袭战提醒的定时任务已更新为: ${cron}`); + } +} \ No newline at end of file diff --git a/apps/remind.js b/apps/remind.js new file mode 100644 index 0000000..473c7ed --- /dev/null +++ b/apps/remind.js @@ -0,0 +1,227 @@ +import { ZZZPlugin } from '../lib/plugin.js'; +import settings from '../lib/settings.js'; +import _ from 'lodash'; +import { rulePrefix } from '../lib/common.js'; +import { ZZZChallenge } from '../model/abyss.js'; +import { Deadly } from '../model/deadly.js'; + +const USER_CONFIGS_KEY = 'ZZZ:REMIND:USER_CONFIGS'; + +export class Remind extends ZZZPlugin { + constructor() { + super({ + name: '[ZZZ-Plugin]Remind', + dsc: '式舆防卫战/危局强袭战未完成提醒', + event: 'message', + priority: _.get(settings.getConfig('priority'), 'remind', 80), + rule: [ + { + reg: `${rulePrefix}开启挑战提醒$`, + fnc: 'subscribe', + }, + { + reg: `${rulePrefix}关闭挑战提醒$`, + fnc: 'unsubscribe', + }, + { + reg: `${rulePrefix}设置式舆阈值\\s*(\\d+)`, + fnc: 'setMyAbyssThreshold', + }, + { + reg: `${rulePrefix}设置危局阈值\\s*(\\d+)`, + fnc: 'setMyDeadlyThreshold', + }, + { + reg: `${rulePrefix}查询挑战状态$`, + fnc: 'checkNow', + }, + ], + }); + + this.task = { + name: 'ZZZ-Plugin式舆防卫战/危局强袭战提醒任务', + cron: this.getCron(), + fnc: () => this.runTask(), + }; + } + + getCron() { + const remindConfig = settings.getConfig('remind'); + return _.get(remindConfig, 'cron', '0 30 20 * * ? *'); + } + + async getUserConfig(userId) { + const userConfigJson = await redis.hGet(USER_CONFIGS_KEY, String(userId)); + return userConfigJson ? JSON.parse(userConfigJson) : null; + } + + async setUserConfig(userId, config) { + await redis.hSet(USER_CONFIGS_KEY, String(userId), JSON.stringify(config)); + } + + async subscribe() { + const uid = await this.getUID(); + if (!uid) { + await this.reply('未绑定UID,请先绑定'); + return false; + } + + let userConfig = await this.getUserConfig(this.e.user_id); + + if (userConfig && userConfig.enable) { + await this.reply('提醒已开启,请勿重复操作'); + return false; + } + + if (userConfig) { + userConfig.enable = true; + } else { + const defaultConfig = settings.getConfig('remind'); + userConfig = { + enable: true, + abyssCheckLevel: defaultConfig.abyssCheckLevel, + deadlyStars: defaultConfig.deadlyStars, + }; + } + + await this.setUserConfig(this.e.user_id, userConfig); + await this.reply('提醒功能已开启'); + } + + async unsubscribe() { + let userConfig = await this.getUserConfig(this.e.user_id); + if (!userConfig || !userConfig.enable) { + await this.reply('提醒功能尚未开启'); + return false; + } + + userConfig.enable = false; + await this.setUserConfig(this.e.user_id, userConfig); + await this.reply('提醒功能已关闭'); + } + + async setMyAbyssThreshold() { + const match = this.e.msg.match(/设置式舆阈值\s*(\d+)/); + if (!match) return; + const threshold = Number(match); + + if (threshold < 1 || threshold > 7) { + await this.reply('阈值必须在1到7之间'); + return false; + } + + let userConfig = await this.getUserConfig(this.e.user_id); + if (!userConfig) { + const defaultConfig = settings.getConfig('remind'); + userConfig = { + enable: false, + abyssCheckLevel: defaultConfig.abyssCheckLevel, + deadlyStars: defaultConfig.deadlyStars, + }; + } + + userConfig.abyssCheckLevel = threshold; + await this.setUserConfig(this.e.user_id, userConfig); + await this.reply(`式舆防卫战提醒阈值已设为: 检查前 ${threshold} 层`); + } + + async setMyDeadlyThreshold() { + const match = this.e.msg.match(/设置危局阈值\s*(\d+)/); + if (!match) return; + const threshold = Number(match); + + let userConfig = await this.getUserConfig(this.e.user_id); + if (!userConfig) { + const defaultConfig = settings.getConfig('remind'); + userConfig = { + enable: false, + abyssCheckLevel: defaultConfig.abyssCheckLevel, + deadlyStars: defaultConfig.deadlyStars, + }; + } + + userConfig.deadlyStars = threshold; + await this.setUserConfig(this.e.user_id, userConfig); + await this.reply(`危局强袭战星星阈值已设为: ${threshold}`); + } + + async checkNow() { + const userConfig = await this.getUserConfig(this.e.user_id); + if (!userConfig) { + await this.reply('尚未设置任何提醒,请先设置阈值'); + return false; + } + await this.reply('正在查询,请稍候...'); + const messages = await this.checkUser(this.e.user_id, userConfig, true); // 主动查询,显示所有状态 + if (messages.length > 0) { + await this.reply(messages.join('\n')); + } else { + await this.reply('查询失败,请稍后再试'); + } + } + + async checkUser(userId, userConfig, showAll = false) { + let messages = []; + try { + const user = this.e.bot.pickUser(userId); + const tempE = { ...this.e, user_id: userId, reply: (msg) => user.sendMsg(msg) }; + + const { api, deviceFp } = await this.getAPI(tempE); + await this.getPlayerInfo(tempE); + + // 检查式舆防卫战 + const abyssRawData = await api + .getFinalData('zzzChallenge', { deviceFp }) + .catch(() => ({})); + if (!abyssRawData.has_data) { + messages.push(`式舆防卫战S评级: 0/7`); + } else { + const abyssData = new ZZZChallenge(abyssRawData); + const userThreshold = userConfig.abyssCheckLevel || 7; + if (showAll || !abyssData.areAllSUpTo(userThreshold)) { + const sCount = abyssData.getSRankCountUpTo(7); + const status = abyssData.areAllSUpTo(userThreshold) ? ' ✓' : ''; + messages.push(`式舆防卫战S评级: ${sCount}/7${status}`); + } + } + + // 检查危局强袭战 + const deadlyRawData = await api.getFinalData('zzzDeadly', { deviceFp }).catch(() => ({})); + if (!deadlyRawData.has_data) { + messages.push(`危局强袭战星星: 0/9`); + } else { + const deadlyData = new Deadly(deadlyRawData); + if (showAll || deadlyData.total_star < userConfig.deadlyStars) { + const status = deadlyData.total_star >= userConfig.deadlyStars ? ' ✓' : ''; + messages.push(`危局强袭战星星: ${deadlyData.total_star}/9${status}`); + } + } + } catch (error) { + logger.error(`[ZZZ-Plugin] 为用户 ${userId} 执行检查失败: ${error}`); + messages.push('查询失败,请稍后再试'); + } + return messages; + } + + async runTask() { + const globalRemindConfig = settings.getConfig('remind'); + if (!globalRemindConfig.enable) { + return; + } + logger.info('[ZZZ-Plugin] 开始执行式舆防卫战/危局强袭战提醒任务'); + + const allUserConfigs = await redis.hGetAll(USER_CONFIGS_KEY); + + for (const userId in allUserConfigs) { + const userConfig = JSON.parse(allUserConfigs[userId]); + if (!userConfig.enable) continue; + + const messages = await this.checkUser(userId, userConfig); + if (messages.length > 0) { + const user = this.e.bot.pickUser(userId); + await user.sendMsg(messages.join('\n')); + } + } + logger.info('[ZZZ-Plugin] 式舆防卫战/危局强袭战提醒任务执行完毕'); + } +} \ No newline at end of file diff --git a/defSet/remind.yaml b/defSet/remind.yaml new file mode 100644 index 0000000..3a7b482 --- /dev/null +++ b/defSet/remind.yaml @@ -0,0 +1,4 @@ +enable: true # 功能总开关 +cron: 0 30 20 * * ? * # 定时任务cron表达式,默认每天晚上8:30 +abyssCheckLevel: 7 # 式舆防卫战提醒检查的最高关卡(用户可自行设置,最高为7) +deadlyStars: 6 # 危局强袭战星星阈值 \ No newline at end of file diff --git a/guoba.support.js b/guoba.support.js index 330e2d8..981dfce 100644 --- a/guoba.support.js +++ b/guoba.support.js @@ -194,6 +194,45 @@ export function supportGuoba() { addonAfter: "s", }, }, + { + component: 'SOFT_GROUP_BEGIN', + label: '提醒功能设置', + }, + { + field: 'remind.enable', + label: '开启提醒功能', + bottomHelpMessage: '是否启用式舆防卫战/危局强袭战的定时提醒功能', + component: 'Switch', + }, + { + field: 'remind.cron', + label: '提醒时间', + bottomHelpMessage: '设置新用户订阅时的默认Cron表达式', + component: 'EasyCron', + componentProps: { + placeholder: '请输入或选择cron表达式', + }, + }, + { + field: 'remind.abyssSCount', + label: '默认式舆S评级阈值', + bottomHelpMessage: '新用户订阅时,S评级数量低于此值会收到提醒', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入数字', + }, + }, + { + field: 'remind.deadlyStars', + label: '默认危局星星阈值', + bottomHelpMessage: '新用户订阅时,星星总数低于此值会收到提醒', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入数字', + }, + }, { component: 'SOFT_GROUP_BEGIN', label: '攻略设置', diff --git a/model/abyss.js b/model/abyss.js index 9554aea..4bb4cdc 100644 --- a/model/abyss.js +++ b/model/abyss.js @@ -369,4 +369,42 @@ export class ZZZChallenge { '0' )}`; } + getSRankCountUpTo(maxLevel) { + let s_rank_count = 0; + const min_level = Math.min( + ...this.all_floor_detail.map(f => f.layer_index) + ); + for (let level = 1; level <= maxLevel; level++) { + const floor = this.all_floor_detail.find(f => f.layer_index === level); + if (floor) { + if (floor.rating === 'S') { + s_rank_count++; + } + } else { + if (level < min_level) { + s_rank_count++; + } + } + } + return s_rank_count; + } + + areAllSUpTo(maxLevel) { + const min_level = Math.min( + ...this.all_floor_detail.map(f => f.layer_index) + ); + for (let level = 1; level <= maxLevel; level++) { + const floor = this.all_floor_detail.find(f => f.layer_index === level); + if (floor) { + if (floor.rating !== 'S') { + return false; + } + } else { + if (level >= min_level) { + return false; + } + } + } + return true; + } }