feat: 挑战定时提醒

This commit is contained in:
Qian23333 2025-08-15 08:35:04 +08:00
parent 607b85c922
commit 534ef0765e
6 changed files with 340 additions and 0 deletions

View file

@ -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,
};

29
apps/manage/remind.js Normal file
View file

@ -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}`);
}
}

227
apps/remind.js Normal file
View file

@ -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] 式舆防卫战/危局强袭战提醒任务执行完毕');
}
}

4
defSet/remind.yaml Normal file
View file

@ -0,0 +1,4 @@
enable: true # 功能总开关
cron: 0 30 20 * * ? * # 定时任务cron表达式默认每天晚上8:30
abyssCheckLevel: 7 # 式舆防卫战提醒检查的最高关卡用户可自行设置最高为7
deadlyStars: 6 # 危局强袭战星星阈值

View file

@ -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: '攻略设置',

View file

@ -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;
}
}