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 1/8] 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 81e0a10..e745410 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 75059f3..4f2ec12 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 676f7ec..27feb91 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 769b93c..90a000d 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 a637477..45bb02d 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 1cd4055..3691e7c 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 764fcf3..bd6a711 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 7da88ab..486d8a1 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 fd93df0..879ede1 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 3bf7278..aece23d 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 e129f43..5940978 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`, }, }, }, From 22e681e51fca37874e450695ab8ea18b0da9feea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 23:52:13 +0800 Subject: [PATCH 2/8] chore(dev): release 0.12.3 (#161) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 119fbb4..0297ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # 更新日志 +## [0.12.3](https://github.com/KarinJS/Karin/compare/v0.12.2...v0.12.3) (2024-09-12) + + +### Bug Fixes + +* close [#153](https://github.com/KarinJS/Karin/issues/153) [#156](https://github.com/KarinJS/Karin/issues/156) [#152](https://github.com/KarinJS/Karin/issues/152) ([8447b93](https://github.com/KarinJS/Karin/commit/8447b93b0e9d7f992a310c9bb4bbd1fd5f9ae0a6)) +* store ([6f7bda3](https://github.com/KarinJS/Karin/commit/6f7bda3b4592059cede1505da95b88d1f46face0)) + ## [0.12.2](https://github.com/KarinJS/Karin/compare/v0.12.1...v0.12.2) (2024-08-27) diff --git a/package.json b/package.json index 085d0fb..6d7ce4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-karin", - "version": "0.12.2", + "version": "0.12.3", "private": false, "description": "基于 Kritor 进行开发的nodejs机器人框架", "homepage": "https://github.com/KarinJS/Karin", From 7e9ac29d1668d13f7d233ebd4c16f99e520cced2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= Date: Fri, 13 Sep 2024 00:11:43 +0800 Subject: [PATCH 3/8] fix: close #162 --- src/api/git.ts | 17 +++++++++ src/api/npm.ts | 68 +++++++++++++++++++++++++++++++++++ src/api/readJson.ts | 9 +++++ src/cli/index.ts | 41 ++++----------------- src/cli/start.ts | 3 ++ src/utils/common/common.ts | 73 ++++---------------------------------- 6 files changed, 110 insertions(+), 101 deletions(-) create mode 100644 src/api/git.ts create mode 100644 src/api/npm.ts create mode 100644 src/api/readJson.ts diff --git a/src/api/git.ts b/src/api/git.ts new file mode 100644 index 0000000..c002759 --- /dev/null +++ b/src/api/git.ts @@ -0,0 +1,17 @@ +import fs from 'fs' +import { dirName } from 'karin/types' + +/** + * 获取git插件列表 + * @param isPack - 是否屏蔽不带package.json的插件,默认为false + */ +export function getGitPlugins (isPack = false): Array { + const dir = './plugins' + let list = fs.readdirSync(dir, { withFileTypes: true }) + /** 忽略非文件夹、非 karin-plugin-开头的文件夹 */ + list = list.filter(v => v.isDirectory() && v.name.startsWith('karin-plugin-')) + if (isPack) list = list.filter(v => fs.existsSync(`${dir}/${v.name}/package.json`)) + const arr: dirName[] = [] + list.map(v => arr.push(v.name as dirName)) + return arr +} diff --git a/src/api/npm.ts b/src/api/npm.ts new file mode 100644 index 0000000..1e83924 --- /dev/null +++ b/src/api/npm.ts @@ -0,0 +1,68 @@ +import fs from 'fs' +import path from 'path' +import { NpmInfo } from 'karin/utils' +import { readJson } from './readJson' + +/** + * 获取npm插件列表 + * @param showDetails - 是否返回详细信息 + * 默认只返回插件npm包名,为true时返回详细的{dir, name}[] + */ +export async function getNpmPlugins (showDetails: T): Promise { + /** 屏蔽的依赖包列表 */ + const exclude = ['art-template', 'axios', 'chalk', 'chokidar', 'commander', 'express', 'level', 'lodash', 'log4js', 'moment', 'node-karin', 'node-schedule', 'redis', 'ws', 'yaml'] + + const pkg = readJson('./package.json') + const dependencies = Object.keys(pkg.dependencies).filter((name) => !exclude.includes(name)) + + if (!showDetails) { + const list: string[] = [] + const readPackageJson = async (name: string) => { + try { + const pkgPath = path.join(process.cwd(), 'node_modules', name, 'package.json') + const pkg = readJson(pkgPath) + if (pkg?.karin) list.push(name) + } catch (error: any) { + logger.error(`[common] 解析 package.json 时出错:${error.stack || error.message || JSON.stringify(error)}`) + } + } + + await Promise.all(dependencies.map(readPackageJson)) + return list as T extends true ? NpmInfo[] : string[] + } + + const list: NpmInfo[] = [] + /** 获取详细的npm信息 */ + const readPackageJson = async (files: string) => { + try { + const root = path.join(process.cwd(), 'node_modules', files) + const pkgPath = path.join(root, 'package.json') + const pkg = readJson(pkgPath) + if (!pkg?.karin) return + + if (pkg?.main) { + list.push({ plugin: files, path: path.dirname(pkg.main), file: path.basename(pkg.main), isMain: true }) + } + + if (pkg?.karin?.apps?.length) { + pkg.karin.apps.forEach((app: string) => { + if (!fs.existsSync(path.join(root, app))) { + logger.error(`[common] npm插件${files}的app目录${app}不存在 已跳过`) + return + } + + fs.readdirSync(path.join(root, app)).forEach((filename: string) => { + /** 忽略非js文件 npm包不考虑ts */ + if (!filename.endsWith('.js')) return + list.push({ plugin: files, path: app, file: filename, isMain: false }) + }) + }) + } + } catch (error: any) { + logger.error(`[common] 获取npm插件列表时出错:${error.stack || error.message || JSON.stringify(error)}`) + } + } + + await Promise.all(dependencies.map(readPackageJson)) + return list as T extends true ? NpmInfo[] : string[] +} diff --git a/src/api/readJson.ts b/src/api/readJson.ts new file mode 100644 index 0000000..3f7243b --- /dev/null +++ b/src/api/readJson.ts @@ -0,0 +1,9 @@ +import fs from 'fs' + +/** + * - 解析json文件 + * @param file - 文件路径 + */ +export function readJson (file: string): any { + return JSON.parse(fs.readFileSync(file, 'utf8')) +} diff --git a/src/cli/index.ts b/src/cli/index.ts index be5ca29..d78ccb3 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -5,6 +5,8 @@ import axios from 'axios' import { fileURLToPath } from 'url' import { getRegistry } from './pkg' import { exec as execCmd, spawn, ChildProcess, ExecOptions } from 'child_process' +import { getNpmPlugins } from 'karin/api/npm' +import { getGitPlugins } from 'karin/api/git' export const enum Runner { Node = 'node', @@ -153,7 +155,7 @@ export class KarinCli { /** 修正入口文件路径 兼容0.6.28以前的版本 */ if (!fs.existsSync('./src') && filePath === './config/config/pm2.yaml') { - const script = './node_modules/node-karin/lib/index.js' + const script = './node_modules/node-karin/lib/cli/start.js' if (data.apps[0].script !== script) { data.apps[0].script = script fs.writeFileSync(filePath, yaml.stringify(data)) @@ -206,25 +208,8 @@ export class KarinCli { * 更新依赖 */ async update () { - /** 屏蔽的依赖包列表 */ - const pkgdependencies = [ - 'art-template', - 'axios', - 'chalk', - 'chokidar', - 'commander', - 'express', - 'level', - 'lodash', - 'log4js', - 'moment', - 'node-schedule', - 'redis', - 'ws', - 'yaml', - ] - - const list = Object.keys(this.pkg(false).dependencies).filter(key => !pkgdependencies.includes(key)) + const list = await getNpmPlugins(false) + list.push('node-karin') /** 获取包管理器 */ const pkg = getRegistry() @@ -252,7 +237,7 @@ export class KarinCli { console.log('[依赖更新] 所有npm依赖已更新完成~') console.log('[依赖更新] 开始更新git插件...') - const gitList = this.getGitPlugins() + const gitList = getGitPlugins() if (!gitList.length) return console.log('[依赖更新] 没有git插件需要更新~') await Promise.all(gitList.map(async item => { @@ -310,20 +295,6 @@ export class KarinCli { return text.trim() } - /** - * 获取git插件列表 - */ - getGitPlugins (): Array { - const dir = path.resolve(process.cwd(), 'plugins') - let list = fs.readdirSync(dir, { withFileTypes: true }) - /** 忽略非文件夹、非 karin-plugin-开头的文件夹 */ - list = list.filter(v => v.isDirectory() && v.name.startsWith('karin-plugin-')) - list = list.filter(v => fs.existsSync(`${dir}/${v.name}/package.json`)) - const arr: string[] = [] - list.map(v => arr.push(v.name)) - return arr - } - /** * 封装exec * @param cmd - 命令 diff --git a/src/cli/start.ts b/src/cli/start.ts index 30c3751..a087290 100644 --- a/src/cli/start.ts +++ b/src/cli/start.ts @@ -1,6 +1,9 @@ import { program } from 'commander' import { KarinCli, Mode, Runner, Lang } from './index' +/** 兼容node .启动方式 */ +if (process.argv.length === 2) process.argv.push('.') + const karin = new KarinCli() program.version(karin.pkg(true).version, '-v, --version', '显示版本号') program.command('.').description('启动karin').action(() => karin.start(Mode.Prod, Lang.Js, Runner.Node)) diff --git a/src/utils/common/common.ts b/src/utils/common/common.ts index b8bd911..de5e498 100644 --- a/src/utils/common/common.ts +++ b/src/utils/common/common.ts @@ -9,6 +9,9 @@ import { createRequire } from 'module' import { pipeline, Readable } from 'stream' import { logger, segment, YamlEditor } from 'karin/utils' import { ButtonElement, ButtonType, dirName, KarinElement, NodeElement, KeyBoardElement } from 'karin/types' +import { readJson } from 'karin/api/readJson' +import { getNpmPlugins } from 'karin/api/npm' +import { getGitPlugins } from 'karin/api/git' export interface NpmInfo { plugin: string, @@ -160,7 +163,7 @@ export class Common { */ readJson (file: string, isThrow = false): any { try { - return JSON.parse(fs.readFileSync(file, 'utf8')) + return readJson(file) } catch (error) { logger.debug(`[common][error] 读取json文件错误: ${file} ` + error) if (isThrow) throw error @@ -426,14 +429,7 @@ export class Common { * @param isPack - 是否屏蔽不带package.json的插件,默认为false */ getPlugins (isPack = false): Array { - const dir = this.absPath('./plugins', false) - let list = fs.readdirSync(dir, { withFileTypes: true }) - /** 忽略非文件夹、非 karin-plugin-开头的文件夹 */ - list = list.filter(v => v.isDirectory() && v.name.startsWith('karin-plugin-')) - if (isPack) list = list.filter(v => fs.existsSync(`${dir}/${v.name}/package.json`)) - const arr: dirName[] = [] - list.map(v => arr.push(v.name as dirName)) - return arr + return this.getGitPlugins(isPack) } /** @@ -441,7 +437,7 @@ export class Common { * @param isPack - 是否屏蔽不带package.json的插件,默认为false */ getGitPlugins (isPack = false): Array { - return this.getPlugins(isPack) + return getGitPlugins(isPack) } /** @@ -450,62 +446,7 @@ export class Common { * 默认只返回插件npm包名,为true时返回详细的{dir, name}[] */ async getNpmPlugins (showDetails: T): Promise { - /** 屏蔽的依赖包列表 */ - const exclude = ['art-template', 'axios', 'chalk', 'chokidar', 'commander', 'express', 'level', 'lodash', 'log4js', 'moment', 'node-karin', 'node-schedule', 'redis', 'ws', 'yaml'] - - const pkg = this.readJson('./package.json') - const dependencies = Object.keys(pkg.dependencies).filter((name) => !exclude.includes(name)) - - if (!showDetails) { - const list: string[] = [] - const readPackageJson = async (name: string) => { - try { - const pkgPath = path.join(process.cwd(), 'node_modules', name, 'package.json') - const pkg = this.readJson(pkgPath) - if (pkg?.karin) list.push(name) - } catch (error: any) { - logger.error(`[common] 解析 package.json 时出错:${error.stack || error.message || JSON.stringify(error)}`) - } - } - - await Promise.all(dependencies.map(readPackageJson)) - return list as T extends true ? NpmInfo[] : string[] - } - - const list: NpmInfo[] = [] - /** 获取详细的npm信息 */ - const readPackageJson = async (files: string) => { - try { - const root = path.join(process.cwd(), 'node_modules', files) - const pkgPath = path.join(root, 'package.json') - const pkg = this.readJson(pkgPath) - if (!pkg?.karin) return - - if (pkg?.main) { - list.push({ plugin: files, path: path.dirname(pkg.main), file: path.basename(pkg.main), isMain: true }) - } - - if (pkg?.karin?.apps?.length) { - pkg.karin.apps.forEach((app: string) => { - if (!fs.existsSync(path.join(root, app))) { - logger.error(`[common] npm插件${files}的app目录${app}不存在 已跳过`) - return - } - - fs.readdirSync(path.join(root, app)).forEach((filename: string) => { - /** 忽略非js文件 npm包不考虑ts */ - if (!filename.endsWith('.js')) return - list.push({ plugin: files, path: app, file: filename, isMain: false }) - }) - }) - } - } catch (error: any) { - logger.error(`[common] 获取npm插件列表时出错:${error.stack || error.message || JSON.stringify(error)}`) - } - } - - await Promise.all(dependencies.map(readPackageJson)) - return list as T extends true ? NpmInfo[] : string[] + return getNpmPlugins(showDetails) } /** From ee7142610f2f1ee04e540cc51e399adba768a2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= Date: Fri, 13 Sep 2024 00:29:01 +0800 Subject: [PATCH 4/8] fix: close #164 --- src/adapter/onebot/11/event.ts | 32 +++++++++++++++++++--- src/types/onebot11/event.ts | 49 ++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/adapter/onebot/11/event.ts b/src/adapter/onebot/11/event.ts index bbabca4..f052535 100644 --- a/src/adapter/onebot/11/event.ts +++ b/src/adapter/onebot/11/event.ts @@ -98,11 +98,12 @@ export class OB11Event { const self_id = data.self_id + '' let notice = {} as KarinNotice - const user_id = data.user_id + '' + /** 别问为啥any... 我是any糕手~ */ + const user_id = ((data as any).user_id || (data as any).operator_id) + '' const event_id = `notice.${time}` const sender = { - uid: data.user_id + '', - uin: data.user_id + '', + uid: user_id, + uin: user_id, nick: '', role: Role.Unknown, } @@ -370,6 +371,31 @@ export class OB11Event { notice = new KarinNotice(options) break } + // Language表情动态上报 + case 'reaction': { + const group_id = data.group_id + '' + const content = { + group_id, + message_id: data.message_id, + face_id: Number(data.code), + is_set: data.sub_type === 'add', + } + + const options = { + raw_event: data, + time, + self_id, + user_id, + event_id, + sender, + contact, + content, + group_id, + sub_event: NoticeSubType.GroupMessageReaction, + } + notice = new KarinNotice(options) + break + } default: { return this.adapter.logger('error', '未知通知事件:', JSON.stringify(data)) } diff --git a/src/types/onebot11/event.ts b/src/types/onebot11/event.ts index 280300c..0e8b30e 100644 --- a/src/types/onebot11/event.ts +++ b/src/types/onebot11/event.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-vars */ - import { OB11Segment } from './segment' import { GroupSender, PrivateSender, ob11Sender } from './sender' @@ -11,7 +9,7 @@ export const enum OB11PostType { Notice = 'notice', Request = 'request', MetaEvent = 'meta_event', - MessageSent = 'message_sent' + MessageSent = 'message_sent', } /** @@ -19,7 +17,7 @@ export const enum OB11PostType { */ export const enum OB11MessageType { Private = 'private', - Group = 'group' + Group = 'group', } /** @@ -31,7 +29,7 @@ export const enum OB11MessageSubType { Other = 'other', Normal = 'normal', Anonymous = 'anonymous', - Notice = 'notice' + Notice = 'notice', } /** @@ -174,7 +172,8 @@ export const enum OB11NoticeType { GroupRecall = 'group_recall', FriendRecall = 'friend_recall', Notify = 'notify', - GroupMsgEmojiLike = 'group_msg_emoji_like' + GroupMsgEmojiLike = 'group_msg_emoji_like', + GroupMsgEmojiLikeLagrange = 'reaction', } /** @@ -502,12 +501,46 @@ export interface OneBot11GroupMessageReaction extends OneBot11Notice { }> } +/** + * 群表情回应事件 Lagrange + */ +export interface OneBot11GroupMessageReactionLagrange extends OneBot11Notice { + /** + * - 消息类型 + */ + notice_type: OB11NoticeType.GroupMsgEmojiLikeLagrange + /** + * - 提示类型 + */ + sub_type: 'remove' | 'add' + /** + * - 群号 + */ + group_id: string + /** + * - 发送者 QQ 号 + */ + operator_id: string + /** + * - 消息 ID + */ + message_id: string + /** + * - 表情ID + */ + code: string + /** + * - 表情数量 + */ + count: number +} + /** * 请求事件类型 */ export const enum OB11RequestType { Friend = 'friend', - Group = 'group' + Group = 'group', } /** @@ -627,7 +660,7 @@ export type OB11MessageEvent = OB11PrivateMessage | OB11GroupMessage /** * 所有通知事件类型 */ -export type OB11NoticeEvent = OneBot11GroupUpload | OneBot11GroupAdmin | OneBot11GroupDecrease | OneBot11GroupIncrease | OneBot11GroupBan | OneBot11FriendAdd | OneBot11GroupRecall | OneBot11FriendRecall | OneBot11Poke | OneBot11LuckyKing | OneBot11Honor | OneBot11GroupMessageReaction +export type OB11NoticeEvent = OneBot11GroupUpload | OneBot11GroupAdmin | OneBot11GroupDecrease | OneBot11GroupIncrease | OneBot11GroupBan | OneBot11FriendAdd | OneBot11GroupRecall | OneBot11FriendRecall | OneBot11Poke | OneBot11LuckyKing | OneBot11Honor | OneBot11GroupMessageReaction | OneBot11GroupMessageReactionLagrange /** * 所有请求事件类型 From 76aaa476ea30170f8f023655c5a4f36d9e3c5f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= Date: Fri, 13 Sep 2024 00:34:19 +0800 Subject: [PATCH 5/8] fix: close #157 --- src/adapter/onebot/11/convert.ts | 3 +++ src/types/element/element.ts | 12 ++++++++++-- src/utils/core/segment.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/adapter/onebot/11/convert.ts b/src/adapter/onebot/11/convert.ts index 05bb138..c9abe3e 100644 --- a/src/adapter/onebot/11/convert.ts +++ b/src/adapter/onebot/11/convert.ts @@ -158,6 +158,9 @@ export function KarinConvertAdapter (data: Array, bot: AdapterOneB elements.push({ type: 'weather', data: { city: i.city, type: i.type } }) break } + case 'raw': + elements.push(i.data) + break case 'button': case 'markdown': case 'keyboard': diff --git a/src/types/element/element.ts b/src/types/element/element.ts index 2b06cea..35acb2e 100644 --- a/src/types/element/element.ts +++ b/src/types/element/element.ts @@ -1,4 +1,4 @@ -export type ElementType = 'text' | 'at' | 'face' | 'bubble_face' | 'reply' | 'image' | 'video' | 'basketball' | 'dice' | 'rps' | 'poke' | 'music' | 'weather' | 'location' | 'share' | 'gift' | 'market_face' | 'forward' | 'contact' | 'json' | 'xml' | 'file' | 'markdown' | 'markdown_tpl' | 'keyboard' | 'node' | 'rows' | 'record' | 'long_msg' +export type ElementType = 'text' | 'at' | 'face' | 'bubble_face' | 'reply' | 'image' | 'video' | 'basketball' | 'dice' | 'rps' | 'poke' | 'music' | 'weather' | 'location' | 'share' | 'gift' | 'market_face' | 'forward' | 'contact' | 'json' | 'xml' | 'file' | 'markdown' | 'markdown_tpl' | 'keyboard' | 'node' | 'rows' | 'record' | 'long_msg' | 'raw' export interface Element { /** @@ -585,7 +585,15 @@ export interface LongMsgElement extends Element { id: string } -export type KarinElement = TextElement | AtElement | FaceElement | BubbleFaceElement | ReplyElement | ImageElement | VideoElement | BasketballElement | DiceElement | RpsElement | PokeElement | MusicElement | WeatherElement | LocationElement | ShareElement | GiftElement | MarketFaceElement | ForwardElement | ContactElement | JsonElement | XmlElement | FileElement | ButtonElement | KeyBoardElement | RecordElement | LongMsgElement | TplMarkdownElement | RawMarkdownElement +/** + * 原生元素 + */ +export interface RawElement extends Element { + type: 'raw' + data: any +} + +export type KarinElement = TextElement | AtElement | FaceElement | BubbleFaceElement | ReplyElement | ImageElement | VideoElement | BasketballElement | DiceElement | RpsElement | PokeElement | MusicElement | WeatherElement | LocationElement | ShareElement | GiftElement | MarketFaceElement | ForwardElement | ContactElement | JsonElement | XmlElement | FileElement | ButtonElement | KeyBoardElement | RecordElement | LongMsgElement | TplMarkdownElement | RawMarkdownElement | RawElement /** * - 构建自定义转发节点 此元素仅可通过专用接口发送 不支持混合发送 diff --git a/src/utils/core/segment.ts b/src/utils/core/segment.ts index 6e170f9..fde9542 100644 --- a/src/utils/core/segment.ts +++ b/src/utils/core/segment.ts @@ -31,6 +31,7 @@ import { RecordElement, KeyBoardElement, Button, + RawElement, } from 'karin/types' export const segment = new (class Segment { @@ -579,4 +580,15 @@ export const segment = new (class Segment { content, } } + + /** + * 发送原生格式给予适配器处理 + * @param data - 原生数据 + */ + raw (data: any): RawElement { + return { + type: 'raw', + data, + } + } })() From 0b6d497a9d08093ea55bf6ea6b33b7ab1c47b44c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 00:35:19 +0800 Subject: [PATCH 6/8] chore(dev): release 0.12.4 (#165) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0297ade..fd58340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # 更新日志 +## [0.12.4](https://github.com/KarinJS/Karin/compare/v0.12.3...v0.12.4) (2024-09-12) + + +### Bug Fixes + +* close [#157](https://github.com/KarinJS/Karin/issues/157) ([76aaa47](https://github.com/KarinJS/Karin/commit/76aaa476ea30170f8f023655c5a4f36d9e3c5f70)) +* close [#162](https://github.com/KarinJS/Karin/issues/162) ([7e9ac29](https://github.com/KarinJS/Karin/commit/7e9ac29d1668d13f7d233ebd4c16f99e520cced2)) +* close [#164](https://github.com/KarinJS/Karin/issues/164) ([ee71426](https://github.com/KarinJS/Karin/commit/ee7142610f2f1ee04e540cc51e399adba768a2cc)) + ## [0.12.3](https://github.com/KarinJS/Karin/compare/v0.12.2...v0.12.3) (2024-09-12) diff --git a/package.json b/package.json index 6d7ce4c..2d6c4cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-karin", - "version": "0.12.3", + "version": "0.12.4", "private": false, "description": "基于 Kritor 进行开发的nodejs机器人框架", "homepage": "https://github.com/KarinJS/Karin", From 92bb5e302f26873dbaaa3bd279b9573033aab4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E7=91=BE?= Date: Fri, 13 Sep 2024 00:38:25 +0800 Subject: [PATCH 7/8] fix: build error --- src/modules/commander.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/commander.ts b/src/modules/commander.ts index 2732548..506b475 100644 --- a/src/modules/commander.ts +++ b/src/modules/commander.ts @@ -1,2 +1 @@ export * from 'commander' -export { default } from 'commander' From a732e5d8a8c0995f9adb4683388785ee1ad22bc8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 00:39:08 +0800 Subject: [PATCH 8/8] chore(dev): release 0.12.5 (#167) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd58340..9726530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 更新日志 +## [0.12.5](https://github.com/KarinJS/Karin/compare/v0.12.4...v0.12.5) (2024-09-12) + + +### Bug Fixes + +* build error ([92bb5e3](https://github.com/KarinJS/Karin/commit/92bb5e302f26873dbaaa3bd279b9573033aab4f8)) + ## [0.12.4](https://github.com/KarinJS/Karin/compare/v0.12.3...v0.12.4) (2024-09-12) diff --git a/package.json b/package.json index 2d6c4cb..ba27b2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-karin", - "version": "0.12.4", + "version": "0.12.5", "private": false, "description": "基于 Kritor 进行开发的nodejs机器人框架", "homepage": "https://github.com/KarinJS/Karin",