From 8447b93b0e9d7f992a310c9bb4bbd1fd5f9ae0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= Date: Thu, 12 Sep 2024 23:49:57 +0800 Subject: [PATCH] fix: close #153 #156 #152 --- config/defSet/group.yaml | 4 +- src/core/init/dir.ts | 9 ++-- src/core/init/init.ts | 2 +- src/core/listener/listener.ts | 31 ++++++++++++++ src/core/plugin/loader.ts | 16 +++++++ src/event/handler/message.ts | 81 ++++++++++++++++++++++++++++------- src/event/handler/notice.ts | 1 + src/event/handler/request.ts | 1 + src/types/plugin/plugin.ts | 44 +++++++++++++++++-- src/utils/config/config.ts | 64 +++++++++++++-------------- src/utils/core/logger.ts | 4 +- 11 files changed, 199 insertions(+), 58 deletions(-) diff --git a/config/defSet/group.yaml b/config/defSet/group.yaml index 81e0a101..e7454107 100644 --- a/config/defSet/group.yaml +++ b/config/defSet/group.yaml @@ -18,7 +18,7 @@ default: disable: [] # 单个Bot默认配置 -Bot.self_id: +Bot:self_id: # 群聊中所有消息冷却时间,单位秒,0则无限制 GroupCD: 0 # 群聊中 每个人的消息冷却时间,单位秒,0则无限制。注意,开启后所有消息都会进CD,无论是否触发插件。 @@ -37,7 +37,7 @@ Bot.self_id: disable: [] # 单个Bot单个群配置 -Bot.self_id.group_id: +Bot:self_id:group_id: # 群聊中所有消息冷却时间,单位秒,0则无限制 GroupCD: 0 # 群聊中 每个人的消息冷却时间,单位秒,0则无限制。注意,开启后所有消息都会进CD,无论是否触发插件。 diff --git a/src/core/init/dir.ts b/src/core/init/dir.ts index 75059f39..4f2ec12e 100644 --- a/src/core/init/dir.ts +++ b/src/core/init/dir.ts @@ -2,7 +2,8 @@ import path from 'path' import { fileURLToPath } from 'url' const filename = fileURLToPath(import.meta.url) -/** - * - 获取当前npm包的根目录 - */ -export const karinDir = path.resolve(filename, '../../../../').replace(/\\/g, '/').replace(/\/$/, '') +/** 获取当前npm包的根目录 */ +export const karinDir = path.resolve(filename, '../../../..').replace(/\/$/, '') + +/** 当前是否处于npm包环境 否则代表处于开发环境 */ +export const isPkg = karinDir.includes('node_modules') diff --git a/src/core/init/init.ts b/src/core/init/init.ts index 676f7ecb..27feb917 100644 --- a/src/core/init/init.ts +++ b/src/core/init/init.ts @@ -1,4 +1,4 @@ -import { logger } from 'karin/utils' +import { logger } from 'karin/utils/core/logger' /** * 启动日志 diff --git a/src/core/listener/listener.ts b/src/core/listener/listener.ts index 769b93c8..90a000d5 100644 --- a/src/core/listener/listener.ts +++ b/src/core/listener/listener.ts @@ -11,6 +11,7 @@ import { KarinNoticeType, KarinRequestType, KarinMessageType, + NodeElement, } from 'karin/types' type AdapterType = KarinAdapter['adapter']['type'] @@ -115,6 +116,36 @@ export class Listeners extends EventEmitter { this.list.push({ index, type: data.type, bot: data.bot }) logger.info(`[机器人][注册][${data.type}] ` + logger.green(`[account:${data.bot.account.uid || data.bot.account.uin}(${data.bot.account.name})]`)) this.#online(data.bot.account.uid || data.bot.account.uin) + + /** 对sendForwardMessage方法进行修改 添加中间键 */ + const sendForwardMessage = data.bot.sendForwardMessage + data.bot.sendForwardMessage = async (contact: Contact, elements: Array) => { + for (const info of pluginLoader.use.forwardMsg) { + try { + let next = false + let exit = false + const nextFn = () => { next = true } + const exitFn = () => { exit = true } + + await info.fn(contact, elements, nextFn, exitFn) + + if (exit) { + const plugin = pluginLoader.plugin.get(info.key)! + logger.debug(`[消息中间件][${plugin.plugin}][${plugin.file}] 主动操作退出`) + return { message_id: '' } + } + + if (!next) break + } catch (e) { + logger.error('[消息中间件] 调用失败,已跳过') + logger.error(e) + } + } + + const result = await sendForwardMessage(contact, elements) + return result + } + logger.debug('注册', this.list) return index } diff --git a/src/core/plugin/loader.ts b/src/core/plugin/loader.ts index a6374776..45bb02df 100644 --- a/src/core/plugin/loader.ts +++ b/src/core/plugin/loader.ts @@ -100,6 +100,8 @@ class PluginLoader { recvMsg: [], replyMsg: [], sendMsg: [], + forwardMsg: [], + notFound: [], } this.ext = process.env.karin_app_lang === 'ts' ? ['.js', '.ts'] : ['.js'] @@ -367,6 +369,8 @@ class PluginLoader { this.use.recvMsg = lodash.orderBy(this.use.recvMsg, ['rank'], ['asc']) this.use.replyMsg = lodash.orderBy(this.use.replyMsg, ['rank'], ['asc']) this.use.sendMsg = lodash.orderBy(this.use.sendMsg, ['rank'], ['asc']) + this.use.forwardMsg = lodash.orderBy(this.use.forwardMsg, ['rank'], ['asc']) + this.use.notFound = lodash.orderBy(this.use.notFound, ['rank'], ['asc']) const handler = Object.keys(this.handler) handler.forEach(key => { @@ -513,6 +517,10 @@ class PluginLoader { /** * 缓存插件 * @param index - 插件索引 + * @param plugin - 插件名称 + * @param file - 插件文件 + * @param info - 插件信息 + * @param App - 插件类 */ async cachePlugin (index: number, plugin: string, file: string, info: AppType, App?: any) { if (!info?.name) { @@ -632,6 +640,9 @@ class PluginLoader { /** * 卸载插件 + * @param plugin - 插件名称 + * @param _path - 插件apps相对路径 + * @param file - 插件文件名称 */ uninstallApp (plugin: string, _path: string, file: string) { this.plugin.forEach((info, key) => { @@ -644,6 +655,8 @@ class PluginLoader { this.use.recvMsg = this.use.recvMsg.filter(val => val.key !== key) this.use.replyMsg = this.use.replyMsg.filter(val => val.key !== key) this.use.sendMsg = this.use.sendMsg.filter(val => val.key !== key) + this.use.forwardMsg = this.use.forwardMsg.filter(val => val.key !== key) + this.use.notFound = this.use.notFound.filter(val => val.key !== key) /** 定时任务需要先停止 */ this.task = this.task.filter(val => { @@ -673,6 +686,9 @@ class PluginLoader { /** * 监听文件夹更新 + * @param plugin - 插件名称 + * @param _path - 插件apps相对路径 + * @returns 是否成功 */ async watchDir (plugin: string, _path: string) { const root = path.join(this.dir, plugin, _path) diff --git a/src/event/handler/message.ts b/src/event/handler/message.ts index 1cd4055e..3691e7c3 100644 --- a/src/event/handler/message.ts +++ b/src/event/handler/message.ts @@ -3,27 +3,39 @@ import { review } from './review' import { EventBaseHandler } from './base' import { logger, config } from 'karin/utils' import { karin, stateArr, pluginLoader } from 'karin/core' -import { KarinMessageType, PluginCommandInfoType } from 'karin/types' +import { KarinMessageType, MessageSubType, PluginCommandInfoType } from 'karin/types' /** * 消息事件 */ export class MessageHandler extends EventBaseHandler { e: KarinMessageType + /** 当前事件是否是上下文事件 */ + isContext: boolean constructor (e: KarinMessageType) { super(e) this.e = e + this.start() + this.isContext = !!this.getContext() + } + + async start () { this.init() + await this.startUse() - if (this.e.group_id) { + if (this.e.sub_event === MessageSubType.GroupMessage) { if (!this.getCd()) return - if (!this.getMode()) return + /** 下文不走响应模式 */ + if (!this.isContext && !this.getMode()) return if (!this.getGroupEnable()) return if (!this.getUserEnable()) return } else { if (!this.private()) return } + /** 上下文 */ + if (await this.context()) return + /** 处理消息 */ this.deal() } @@ -32,7 +44,7 @@ export class MessageHandler extends EventBaseHandler { * 先对消息事件进行初始化 */ init () { - karin.emit('karin:count:recv', 1) + karin.emit('karin:count:recv', this.e) const logs = [] for (const val of this.e.elements) { switch (val.type) { @@ -176,20 +188,37 @@ export class MessageHandler extends EventBaseHandler { } /** - * 响应模式检查 返回false表示未通过 + * 开始中间件 */ - getMode () { - if (review.mode(this.e, this.config)) return true - logger.debug(`[消息拦截][${this.e.group_id}][${this.e.user_id}] 响应模式不匹配`) - return false + async startUse () { + for (const info of pluginLoader.use.recvMsg) { + try { + let next = false + let exit = false + const nextFn = () => { next = true } + const exitFn = () => { exit = true } + + await info.fn(this.e, nextFn, exitFn) + + if (exit) { + const plugin = pluginLoader.plugin.get(info.key)! + logger.debug(`[消息中间件][${plugin.plugin}][${plugin.file}] 主动操作退出`) + return + } + + if (!next) break + } catch (e) { + logger.error('[消息中间件] 调用失败,已跳过') + logger.error(e) + } + } } /** - * 处理消息 + * 结束中间件 */ - async deal () { - /** 先调用中间件 */ - for (const info of pluginLoader.use.recvMsg) { + async endUse () { + for (const info of pluginLoader.use.notFound) { try { let next = false let exit = false @@ -210,10 +239,21 @@ export class MessageHandler extends EventBaseHandler { logger.error(e) } } + } - /** 上下文 */ - if (await this.context()) return + /** + * 响应模式检查 返回false表示未通过 + */ + getMode () { + if (review.mode(this.e, this.config)) return true + logger.debug(`[消息拦截][${this.e.group_id}][${this.e.user_id}] 响应模式不匹配`) + return false + } + /** + * 处理消息 + */ + async deal () { const app = this.e.group_id ? (info: PluginCommandInfoType) => review.PluginEnable(info, this.config) : () => true @@ -262,6 +302,17 @@ export class MessageHandler extends EventBaseHandler { } } } + + logger.debug(`[事件处理][${this.e.self_id}][${this.e.user_id}][${this.e.event_id}] 未匹配到任何插件`) + await this.endUse() + } + + /** + * 获取下文 + */ + getContext () { + const key = this.e.isGroup ? `${this.e.group_id}.${this.e.user_id}` : this.e.user_id + return stateArr[key] } /** diff --git a/src/event/handler/notice.ts b/src/event/handler/notice.ts index 764fcf3d..bd6a7112 100644 --- a/src/event/handler/notice.ts +++ b/src/event/handler/notice.ts @@ -88,6 +88,7 @@ export class NoticeHandler extends EventBaseHandler { break } } + logger.debug(`[事件处理][${this.e.self_id}][${this.e.user_id}][${this.e.event_id}] 未匹配到任何插件`) } /** diff --git a/src/event/handler/request.ts b/src/event/handler/request.ts index 7da88aba..486d8a17 100644 --- a/src/event/handler/request.ts +++ b/src/event/handler/request.ts @@ -84,6 +84,7 @@ export class RequestHandler extends EventBaseHandler { break } } + logger.debug(`[事件处理][${this.e.self_id}][${this.e.user_id}][${this.e.event_id}] 未匹配到任何插件`) } /** diff --git a/src/types/plugin/plugin.ts b/src/types/plugin/plugin.ts index fd93df0b..879ede17 100644 --- a/src/types/plugin/plugin.ts +++ b/src/types/plugin/plugin.ts @@ -2,7 +2,7 @@ import schedule from 'node-schedule' import { Plugin } from 'karin/core' import { Reply, replyCallback, replyForward } from '../event/reply' import { KarinNoticeType, KarinRequestType, AllListenEvent, KarinMessageType, PermissionType, AllMessageSubType, Contact, AllNoticeSubType, AllRequestSubType } from '../event' -import { KarinElement } from '../element/element' +import { KarinElement, NodeElement } from '../element/element' /** * - 插件根目录名称 @@ -27,7 +27,7 @@ export const enum AppsType { /** npm插件 */ Npm = 'npm', /** 单个app插件 */ - Js = 'js' + Js = 'js', } /** @@ -37,7 +37,7 @@ export const enum MethodType { /** 函数 */ Function = 'function', /** 类 */ - Class = 'class' + Class = 'class', } /** @@ -216,6 +216,44 @@ export interface PluginMiddlewareInfoType { /** 优先级 */ rank: number }> + /** 发送合并转发前 */ + forwardMsg: Array<{ + /** 插件基本信息的映射key */ + key: number, + /** 插件包名称 */ + name: string, + /** 插件执行方法 */ + fn: ( + /** 发送的目标信息 */ + contact: Contact, + /** 发送的消息体 */ + elements: Array, + /** 是否继续执行下一个中间件 */ + next: Function, + /** 是否不发送此条消息 */ + exit: Function + ) => Promise, + /** 优先级 */ + rank: number + }> + /** 消息事件没有找到任何匹配的插件触发 */ + notFound: Array<{ + /** 插件基本信息的映射key */ + key: number, + /** 插件包名称 */ + name: string, + /** 插件执行方法 */ + fn: ( + /** 消息事件方法 */ + e: KarinMessageType, + /** 是否继续执行下一个中间件 */ + next: Function, + /** 是否退出此条消息 不再执行匹配插件 */ + exit: Function + ) => Promise, + /** 优先级 */ + rank: number + }> } /** diff --git a/src/utils/config/config.ts b/src/utils/config/config.ts index 3bf72782..aece23da 100644 --- a/src/utils/config/config.ts +++ b/src/utils/config/config.ts @@ -3,7 +3,7 @@ import Yaml from 'yaml' import path from 'path' import { Logger } from 'log4js' import chokidar from 'chokidar' -import { karinDir } from 'karin/core/init/dir' +import { karinDir, isPkg } from 'karin/core/init/dir' import { common } from 'karin/utils/common/common' import { Redis, App, Config, Server, Package, GroupCfg, KarinEventTypes } from 'karin/types' @@ -11,31 +11,24 @@ import { Redis, App, Config, Server, Package, GroupCfg, KarinEventTypes } from ' * 配置文件 */ export const config = new (class Cfg { - /** - * 运行目录绝对路径 - */ + /** karin运行目录绝对路径 */ dir: string - /** - * 运行目录配置文件夹路径 - */ + /** 用户生成配置目录 */ cfgDir: string - /** - * node-karin npm包路径 - */ - pkgDir: string - /** - * node-karin 包配置文件夹路径 - */ + /** 默认配置目录 */ pkgCfgDir: string + /** 缓存 */ change: Map + /** 监听 */ watcher: Map + /** 拦截器状态 */ review: boolean + logger!: Logger constructor () { this.dir = process.cwd() - this.pkgDir = karinDir this.cfgDir = this.dir + '/config' - this.pkgCfgDir = this.pkgDir + '/config/defSet' + this.pkgCfgDir = karinDir + '/config/defSet' /** 缓存 */ this.change = new Map() @@ -58,13 +51,22 @@ export const config = new (class Cfg { ] list.forEach(path => this.mkdir(path)) - if (this.pkgCfgDir !== (this.cfgDir + '/defSet').replace(/\\/g, '/')) { + + /** 拷贝默认配置文件 */ + if (isPkg) { const files = fs.readdirSync(this.pkgCfgDir).filter(file => file.endsWith('.yaml')) files.forEach(file => { const path = `${this.cfgDir}/config/${file}` const pathDef = `${this.pkgCfgDir}/${file}` if (!fs.existsSync(path)) fs.copyFileSync(pathDef, path) }) + } else { + const files = fs.readdirSync(this.cfgDir + '/defSet').filter(file => file.endsWith('.yaml')) + files.forEach(file => { + const path = `${this.cfgDir}/config/${file}` + const pathDef = `${this.cfgDir}/defSet/${file}` + if (!fs.existsSync(path)) fs.copyFileSync(pathDef, path) + }) } /** 为每个插件包创建统一存储的文件夹 */ @@ -181,26 +183,26 @@ export const config = new (class Cfg { /** 取配置 */ const config = this.getYaml('config', 'config', true) const defSet = this.getYaml('defSet', 'config', false) - const data = { ...defSet, ...config } + const data = { ...defSet, ...config } as Config const Config = { ...data, WhiteList: { - users: data.WhiteList.users.map((i: string | number) => String(i)), - groups: data.WhiteList.groups.map((i: string | number) => String(i)), - GroupMsgLog: data.WhiteList.GroupMsgLog.map((i: string | number) => String(i)), + users: data?.WhiteList?.users?.map((i: string | number) => String(i)) || [], + groups: data?.WhiteList?.groups?.map((i: string | number) => String(i)) || [], + GroupMsgLog: data?.WhiteList?.GroupMsgLog?.map((i: string | number) => String(i)) || [], }, BlackList: { - users: data.BlackList.users.map((i: string | number) => String(i)), - groups: data.BlackList.groups.map((i: string | number) => String(i)), - GroupMsgLog: data.BlackList.GroupMsgLog.map((i: string | number) => String(i)), + users: data?.BlackList?.users?.map((i: string | number) => String(i)) || [], + groups: data?.BlackList?.groups?.map((i: string | number) => String(i)) || [], + GroupMsgLog: data?.BlackList?.GroupMsgLog?.map((i: string | number) => String(i)) || [], }, - master: data.master.map((i: string | number) => String(i)), - admin: data.admin.map((i: string | number) => String(i)), + master: data?.master?.map((i: string | number) => String(i)) || [], + admin: data?.admin?.map((i: string | number) => String(i)) || [], private: { - white_list: data.private.white_list.map((i: string | number) => String(i)), - tips: data.private.tips, + white_list: data?.private?.white_list?.map((i: string | number) => String(i)) || [], + tips: data?.private?.tips || '', }, - } + } as Config /** 缓存 */ this.change.set(key, Config) return Config @@ -229,7 +231,7 @@ export const config = new (class Cfg { * 实时获取packageon文件 */ get package (): Package { - const data = fs.readFileSync(this.pkgDir + '/package.json', 'utf8') + const data = fs.readFileSync(karinDir + '/package.json', 'utf8') const pack = JSON.parse(data) as Package return pack } @@ -342,7 +344,7 @@ export const config = new (class Cfg { // 应该改成事件监听 if (this.review) return this.review = true - const { review } = await import('karin/event') + const { review } = await import('karin/event/handler/review') review.main() this.review = false } diff --git a/src/utils/core/logger.ts b/src/utils/core/logger.ts index e129f437..5940978f 100644 --- a/src/utils/core/logger.ts +++ b/src/utils/core/logger.ts @@ -10,7 +10,7 @@ const { log_level, log_days_Keep, log4jsCfg } = config.Config const level = log_level || log4jsCfg.level || 'info' const daysToKeep = log_days_Keep || log4jsCfg.daysToKeep || 7 const { overall, fragments, maxLogSize } = log4jsCfg -const defaultOptions = { appenders: ['console'], level, enableCallStack: process.env.KarinMode === 'dev' } +const defaultOptions = { appenders: ['console'], level, enableCallStack: process.env.karin_app_mode === 'dev' } const options: { appenders: { @@ -61,7 +61,7 @@ const options: { type: 'console', layout: { type: 'pattern', - pattern: `%[[Karin][%d{hh:mm:ss.SSS}][%4.4p]%] ${process.env.KarinMode === 'dev' ? '[%f{3}:%l] ' : ''}%m`, + pattern: `%[[Karin][%d{hh:mm:ss.SSS}][%4.4p]%] ${process.env.karin_app_mode === 'dev' ? '[%f{3}:%l] ' : ''}%m`, }, }, },