node-karin 0.9.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/defSet/group.yaml +40 -2
- package/lib/adapter/onebot/11/index.d.ts +2 -0
- package/lib/adapter/onebot/11/index.js +3 -1
- package/lib/cli/init.js +1 -1
- package/lib/cli/karin.js +1 -1
- package/lib/core/index.d.ts +9 -9
- package/lib/core/index.js +9 -9
- package/lib/core/init/dir.js +7 -0
- package/lib/core/init/init.js +46 -0
- package/lib/core/{karin.d.ts → karin/karin.d.ts} +1 -1
- package/lib/core/karin/karin.js +194 -0
- package/lib/core/{listener.d.ts → listener/listener.d.ts} +1 -1
- package/lib/core/listener/listener.js +213 -0
- package/lib/core/{plugin.app.d.ts → plugin/app.d.ts} +1 -1
- package/lib/core/plugin/app.js +19 -0
- package/lib/core/{plugin.d.ts → plugin/base.d.ts} +1 -1
- package/lib/core/plugin/base.js +140 -0
- package/lib/core/{plugin.loader.d.ts → plugin/loader.d.ts} +3 -3
- package/lib/core/plugin/loader.js +579 -0
- package/lib/core/process/process.js +100 -0
- package/lib/core/server/server.js +283 -0
- package/lib/db/index.d.ts +2 -2
- package/lib/db/index.js +2 -2
- package/lib/db/level/level.js +36 -0
- package/lib/db/redis/redis.js +135 -0
- package/lib/db/redis/redis_level.js +287 -0
- package/lib/event/{event.handler.d.ts → handler/base.d.ts} +2 -2
- package/lib/event/handler/base.js +173 -0
- package/lib/event/{message.handler.d.ts → handler/message.d.ts} +3 -3
- package/lib/event/handler/message.js +270 -0
- package/lib/event/{notice.handler.d.ts → handler/notice.d.ts} +3 -3
- package/lib/event/handler/notice.js +212 -0
- package/lib/event/{request.handler.d.ts → handler/request.d.ts} +3 -3
- package/lib/event/handler/request.js +118 -0
- package/lib/event/{review.handler.d.ts → handler/review.d.ts} +3 -3
- package/lib/event/handler/review.js +391 -0
- package/lib/event/index.d.ts +5 -5
- package/lib/event/index.js +5 -5
- package/lib/render/base.d.ts +1 -1
- package/lib/render/client.d.ts +1 -1
- package/lib/render/client.js +5 -7
- package/lib/render/client_even.d.ts +30 -0
- package/lib/render/client_even.js +153 -0
- package/lib/render/http.d.ts +1 -1
- package/lib/render/http.js +1 -1
- package/lib/render/server.js +1 -1
- package/lib/types/adapter/api.d.ts +5 -1
- package/lib/types/adapter/{adapter.d.ts → base.d.ts} +2 -2
- package/lib/types/config/config.js +1 -0
- package/lib/types/element/element.js +1 -0
- package/lib/types/event/message.d.ts +1 -1
- package/lib/types/event/reply.d.ts +1 -1
- package/lib/types/index.d.ts +6 -6
- package/lib/types/index.js +6 -6
- package/lib/types/logger/logger.js +1 -0
- package/lib/types/{plugin.d.ts → plugin/plugin.d.ts} +3 -3
- package/lib/types/plugin/plugin.js +1 -0
- package/lib/types/render/render.js +1 -0
- package/lib/utils/{common.d.ts → common/common.d.ts} +14 -13
- package/lib/utils/common/common.js +591 -0
- package/lib/utils/{config.d.ts → config/config.d.ts} +37 -19
- package/lib/utils/config/config.js +328 -0
- package/lib/utils/config/updateVersion.js +145 -0
- package/lib/utils/config/yamlEditor.js +292 -0
- package/lib/utils/{handler.d.ts → core/handler.d.ts} +1 -1
- package/lib/utils/core/handler.js +115 -0
- package/lib/utils/core/init.js +213 -0
- package/lib/utils/core/logger.js +105 -0
- package/lib/utils/{segment.d.ts → core/segment.d.ts} +1 -1
- package/lib/utils/core/segment.js +441 -0
- package/lib/utils/index.d.ts +11 -11
- package/lib/utils/index.js +11 -11
- package/lib/utils/{button.d.ts → tools/button.d.ts} +1 -1
- package/lib/utils/tools/button.js +38 -0
- package/lib/utils/tools/exec.js +37 -0
- package/lib/utils/tools/ffmpeg.js +25 -0
- package/lib/utils/tools/update.js +139 -0
- package/package.json +1 -1
- package/lib/core/dir.js +0 -7
- package/lib/core/init.js +0 -42
- package/lib/core/karin.js +0 -194
- package/lib/core/listener.js +0 -217
- package/lib/core/plugin.app.js +0 -19
- package/lib/core/plugin.js +0 -145
- package/lib/core/plugin.loader.js +0 -561
- package/lib/core/process.js +0 -98
- package/lib/core/server.js +0 -269
- package/lib/db/level.js +0 -37
- package/lib/db/redis.js +0 -134
- package/lib/db/redis_level.js +0 -293
- package/lib/event/event.handler.js +0 -167
- package/lib/event/message.handler.js +0 -254
- package/lib/event/notice.handler.js +0 -204
- package/lib/event/request.handler.js +0 -110
- package/lib/event/review.handler.js +0 -386
- package/lib/types/config.js +0 -1
- package/lib/types/element.js +0 -1
- package/lib/types/logger.js +0 -1
- package/lib/types/plugin.js +0 -1
- package/lib/types/render.js +0 -1
- package/lib/utils/button.js +0 -34
- package/lib/utils/common.js +0 -596
- package/lib/utils/config.js +0 -318
- package/lib/utils/exec.js +0 -36
- package/lib/utils/ffmpeg.js +0 -25
- package/lib/utils/handler.js +0 -109
- package/lib/utils/init.js +0 -208
- package/lib/utils/logger.js +0 -104
- package/lib/utils/segment.js +0 -470
- package/lib/utils/update.js +0 -135
- package/lib/utils/updateVersion.js +0 -145
- package/lib/utils/yamlEditor.js +0 -279
- /package/lib/core/{dir.d.ts → init/dir.d.ts} +0 -0
- /package/lib/core/{init.d.ts → init/init.d.ts} +0 -0
- /package/lib/core/{process.d.ts → process/process.d.ts} +0 -0
- /package/lib/core/{server.d.ts → server/server.d.ts} +0 -0
- /package/lib/db/{level.d.ts → level/level.d.ts} +0 -0
- /package/lib/db/{redis.d.ts → redis/redis.d.ts} +0 -0
- /package/lib/db/{redis_level.d.ts → redis/redis_level.d.ts} +0 -0
- /package/lib/types/adapter/{adapter.js → base.js} +0 -0
- /package/lib/types/{config.d.ts → config/config.d.ts} +0 -0
- /package/lib/types/{element.d.ts → element/element.d.ts} +0 -0
- /package/lib/types/{logger.d.ts → logger/logger.d.ts} +0 -0
- /package/lib/types/{render.d.ts → render/render.d.ts} +0 -0
- /package/lib/utils/{updateVersion.d.ts → config/updateVersion.d.ts} +0 -0
- /package/lib/utils/{yamlEditor.d.ts → config/yamlEditor.d.ts} +0 -0
- /package/lib/utils/{init.d.ts → core/init.d.ts} +0 -0
- /package/lib/utils/{logger.d.ts → core/logger.d.ts} +0 -0
- /package/lib/utils/{exec.d.ts → tools/exec.d.ts} +0 -0
- /package/lib/utils/{ffmpeg.d.ts → tools/ffmpeg.d.ts} +0 -0
- /package/lib/utils/{update.d.ts → tools/update.d.ts} +0 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import lodash from 'lodash';
|
|
4
|
+
import chokidar from 'chokidar';
|
|
5
|
+
import schedule from 'node-schedule';
|
|
6
|
+
import { listener } from '../listener/listener.js';
|
|
7
|
+
import PluginApp from './app.js';
|
|
8
|
+
import { render } from '../../render/index.js';
|
|
9
|
+
import { common, handler, logger } from '../../utils/index.js';
|
|
10
|
+
class PluginLoader {
|
|
11
|
+
dir;
|
|
12
|
+
dirPath;
|
|
13
|
+
/**
|
|
14
|
+
* - 插件索引ID
|
|
15
|
+
*/
|
|
16
|
+
index;
|
|
17
|
+
/**
|
|
18
|
+
* - 命令插件索引列表
|
|
19
|
+
*/
|
|
20
|
+
ruleIds;
|
|
21
|
+
/**
|
|
22
|
+
* - 按钮插件索引列表
|
|
23
|
+
*/
|
|
24
|
+
buttonIds;
|
|
25
|
+
/**
|
|
26
|
+
* - accept插件索引列表
|
|
27
|
+
*/
|
|
28
|
+
acceptIds;
|
|
29
|
+
/**
|
|
30
|
+
* - handler
|
|
31
|
+
*/
|
|
32
|
+
handlerIds;
|
|
33
|
+
/**
|
|
34
|
+
* - 插件列表
|
|
35
|
+
*/
|
|
36
|
+
FileList;
|
|
37
|
+
/**
|
|
38
|
+
* - 是否ts环境
|
|
39
|
+
*/
|
|
40
|
+
isTs;
|
|
41
|
+
/**
|
|
42
|
+
* - 是否开发环境
|
|
43
|
+
*/
|
|
44
|
+
isDev;
|
|
45
|
+
/**
|
|
46
|
+
* - 最终缓存的插件列表 通过index索引
|
|
47
|
+
*/
|
|
48
|
+
PluginList;
|
|
49
|
+
/**
|
|
50
|
+
* - 定时任务
|
|
51
|
+
*/
|
|
52
|
+
task;
|
|
53
|
+
/**
|
|
54
|
+
* - 监听器
|
|
55
|
+
*/
|
|
56
|
+
watcher;
|
|
57
|
+
/**
|
|
58
|
+
* - 热更新列表
|
|
59
|
+
*/
|
|
60
|
+
watchList;
|
|
61
|
+
/**
|
|
62
|
+
* - 所有插件包package.json
|
|
63
|
+
*/
|
|
64
|
+
pkgJson;
|
|
65
|
+
/**
|
|
66
|
+
* - 依赖缺失收集
|
|
67
|
+
*/
|
|
68
|
+
dependErr;
|
|
69
|
+
constructor() {
|
|
70
|
+
this.index = 0;
|
|
71
|
+
this.dir = './plugins';
|
|
72
|
+
this.dirPath = common.urlToPath(import.meta.url);
|
|
73
|
+
this.isTs = process.env.karin_app_lang === 'ts';
|
|
74
|
+
this.isDev = process.env.karin_app_mode === 'dev';
|
|
75
|
+
this.watcher = new Map();
|
|
76
|
+
this.watchList = [];
|
|
77
|
+
this.FileList = [];
|
|
78
|
+
this.PluginList = {};
|
|
79
|
+
this.task = [];
|
|
80
|
+
this.ruleIds = [];
|
|
81
|
+
this.acceptIds = [];
|
|
82
|
+
this.buttonIds = [];
|
|
83
|
+
this.handlerIds = {};
|
|
84
|
+
this.pkgJson = {};
|
|
85
|
+
this.dependErr = {};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 插件初始化
|
|
89
|
+
*/
|
|
90
|
+
async load() {
|
|
91
|
+
await this.getPlugins();
|
|
92
|
+
listener.once('plugin.watch', () => {
|
|
93
|
+
for (const v of this.watchList) {
|
|
94
|
+
v.name ? this.watch(v.dir, v.name) : this.watchDir(v.dir);
|
|
95
|
+
logger.debug(`[热更新][${v.dir}]${v.name ? `[${v.name}]` : ''} 监听中...`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
logger.info(logger.green('-----------'));
|
|
99
|
+
logger.info('加载插件中..');
|
|
100
|
+
/** 载入插件 */
|
|
101
|
+
const promises = this.FileList.map(async ({ dir, name }) => await this.createdApp(dir, name, false));
|
|
102
|
+
/** 获取npm插件 */
|
|
103
|
+
const npm = await common.getNpmPlugins(true);
|
|
104
|
+
/** 载入npm插件 */
|
|
105
|
+
promises.push(...npm.map(async ({ dir, name, isMain }) => await this.createdApp(dir, name, false, true, isMain)));
|
|
106
|
+
/** 等待所有插件加载完成 */
|
|
107
|
+
await Promise.all(promises);
|
|
108
|
+
/** 打印依赖缺失 */
|
|
109
|
+
this.printDependErr();
|
|
110
|
+
/** 释放缓存 */
|
|
111
|
+
this.FileList = [];
|
|
112
|
+
/** 优先级排序并打印插件信息 */
|
|
113
|
+
this.orderBy(true);
|
|
114
|
+
listener.emit('plugin.watch');
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 获取所有插件
|
|
119
|
+
*/
|
|
120
|
+
async getPlugins() {
|
|
121
|
+
/** 获取所有插件包 */
|
|
122
|
+
const plugins = common.getPlugins();
|
|
123
|
+
for (const dir of plugins) {
|
|
124
|
+
/**
|
|
125
|
+
* - 插件包路径
|
|
126
|
+
* - 例如: ./plugins/karin-plugin-example
|
|
127
|
+
*/
|
|
128
|
+
const PluginPath = `${this.dir}/${dir}`;
|
|
129
|
+
/**
|
|
130
|
+
* 一共3种模式
|
|
131
|
+
* 1. npm run dev 开发模式 直接加载ts,js文件,对于同时存在编译产物、源代码的情况,优先加载编译产物而不加载源代码
|
|
132
|
+
* 2. node . 生产模式 只加载js文件
|
|
133
|
+
* 3. npm run debug 调试模式 利用tsx自身的热更新机制,重启karin
|
|
134
|
+
*/
|
|
135
|
+
/** 非插件包 加载该文件夹下全部js 视语言环境加载ts */
|
|
136
|
+
if (!common.isPlugin(PluginPath)) {
|
|
137
|
+
this.getApps(dir, this.isTs, true);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
/** package */
|
|
141
|
+
const pkg = common.readJson(`${PluginPath}/package.json`);
|
|
142
|
+
/** 缓存package.json */
|
|
143
|
+
this.pkgJson[dir] = pkg;
|
|
144
|
+
/** 入口文件 */
|
|
145
|
+
if (pkg.main) {
|
|
146
|
+
const { dir: dirName, pop } = common.splitPath(pkg.main);
|
|
147
|
+
const dirPath = `${dir}/${dirName}`;
|
|
148
|
+
if (common.exists(`${this.dir}/${dirPath}/${pop}`)) {
|
|
149
|
+
this.FileList.push({ dir: dirPath, name: pop });
|
|
150
|
+
this.isDev && this.watchList.push({ dir: dirPath, name: pop });
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
logger.debug(`[插件收集][${pkg.main}] 入口文件不存在,已忽略`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
/** 没有配置入口默认为index.js */
|
|
158
|
+
if (common.exists(`${PluginPath}/index.js`)) {
|
|
159
|
+
this.FileList.push({ dir, name: 'index.js' });
|
|
160
|
+
this.isDev && this.watchList.push({ dir, name: 'index.js' });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** 全部apps路径 */
|
|
164
|
+
const apps = ['apps'];
|
|
165
|
+
if (pkg?.karin?.apps && Array.isArray(pkg.karin.apps))
|
|
166
|
+
apps.push(...pkg.karin.apps);
|
|
167
|
+
/** js环境 或 ts环境、js插件 */
|
|
168
|
+
if (!this.isTs || !common.exists(`${PluginPath}/tsconfig.json`)) {
|
|
169
|
+
apps.forEach((apps) => {
|
|
170
|
+
/** 路径存在才加载 */
|
|
171
|
+
if (common.isDir(`${PluginPath}/${apps}`)) {
|
|
172
|
+
this.getApps((`${dir}/${apps}`), false, this.isDev);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
logger.debug(`[插件收集][${dir}/${apps}] 路径不存在,已忽略`);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
/** ts环境 ts插件 */
|
|
181
|
+
const outDir = pkg?.karin?.outDir || 'lib';
|
|
182
|
+
const rootDir = pkg?.karin?.rootDir || 'src';
|
|
183
|
+
/** 编译产物存在 */
|
|
184
|
+
if (common.exists(`${PluginPath}/${outDir}/apps`)) {
|
|
185
|
+
this.getApps((`${dir}/${outDir}/apps`), true, this.isDev);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
/** 入口文件 */
|
|
190
|
+
if (common.exists(`${PluginPath}/${rootDir}/index.ts`)) {
|
|
191
|
+
const dirPath = `${dir}/${rootDir}`;
|
|
192
|
+
this.FileList.push({ dir: dirPath, name: 'index.ts' });
|
|
193
|
+
this.isDev && this.watchList.push({ dir: dirPath, name: 'index.ts' });
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
/** ts源码 */
|
|
197
|
+
if (common.exists(`${PluginPath}/${rootDir}/apps`)) {
|
|
198
|
+
this.getApps((`${dir}/${rootDir}/apps`), true, this.isDev);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return plugins;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 获取指定文件夹下的所有插件
|
|
207
|
+
* @param dir - 插件包名称
|
|
208
|
+
* @param isTs - 是否获取ts插件
|
|
209
|
+
*/
|
|
210
|
+
getApps(dir, isTs, isWatch = false) {
|
|
211
|
+
isWatch && this.watchList.push({ dir });
|
|
212
|
+
const ext = isTs ? ['.js', '.ts'] : ['.js'];
|
|
213
|
+
const list = fs.readdirSync(`${this.dir}/${dir}`, { withFileTypes: true });
|
|
214
|
+
for (const file of list) {
|
|
215
|
+
const extname = path.extname(file.name);
|
|
216
|
+
if (!ext.includes(extname))
|
|
217
|
+
continue;
|
|
218
|
+
this.FileList.push({ dir, name: file.name });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 排序并打印插件信息
|
|
223
|
+
* @param isPrint - 是否打印
|
|
224
|
+
*/
|
|
225
|
+
orderBy(isPrint = false) {
|
|
226
|
+
let taskCount = 0;
|
|
227
|
+
let handlerCount = 0;
|
|
228
|
+
const rule = [];
|
|
229
|
+
const button = [];
|
|
230
|
+
const accept = [];
|
|
231
|
+
Object.keys(this.PluginList).forEach(key => {
|
|
232
|
+
taskCount += this.PluginList[key].task.length;
|
|
233
|
+
if (this.PluginList[key].rule.length)
|
|
234
|
+
rule.push({ key, val: this.PluginList[key].priority });
|
|
235
|
+
if (this.PluginList[key].button.length)
|
|
236
|
+
button.push({ key, val: this.PluginList[key].priority });
|
|
237
|
+
if (this.PluginList[key].accept)
|
|
238
|
+
accept.push({ key, val: this.PluginList[key].priority });
|
|
239
|
+
});
|
|
240
|
+
this.ruleIds = lodash.orderBy(rule, ['val'], ['asc']).map((v) => Number(v.key));
|
|
241
|
+
logger.debug('rule排序完成...');
|
|
242
|
+
this.buttonIds = lodash.orderBy(button, ['val'], ['asc']).map((v) => Number(v.key));
|
|
243
|
+
logger.debug('button排序完成...');
|
|
244
|
+
this.acceptIds = lodash.orderBy(accept, ['val'], ['asc']).map((v) => Number(v.key));
|
|
245
|
+
if (!isPrint)
|
|
246
|
+
return;
|
|
247
|
+
const PluginListKeys = Object.keys(this.PluginList);
|
|
248
|
+
const handlerKeys = Object.keys(this.handlerIds);
|
|
249
|
+
handlerKeys.forEach((key) => {
|
|
250
|
+
handlerCount += this.handlerIds[key].length;
|
|
251
|
+
});
|
|
252
|
+
logger.info(`[插件][${PluginListKeys.length}个] 加载完成`);
|
|
253
|
+
logger.info(`[渲染器][${render.Apps.length}个] 加载完成`);
|
|
254
|
+
logger.info(`[rule][${this.ruleIds.length}个] 加载完成`);
|
|
255
|
+
logger.info(`[button][${this.buttonIds.length}个] 加载完成`);
|
|
256
|
+
logger.info(`[accept][${this.acceptIds.length}个] 加载完成`);
|
|
257
|
+
logger.info(`[定时任务][${taskCount}个] 加载完成`);
|
|
258
|
+
logger.info(`[Handler][Key:${handlerKeys.length}个][fnc:${handlerCount}个] 加载完成`);
|
|
259
|
+
logger.info(logger.green('-----------'));
|
|
260
|
+
logger.info(`Karin启动完成:耗时 ${logger.green(process.uptime().toFixed(2))} 秒...`);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 新增插件
|
|
264
|
+
* @param dir - 插件包路径
|
|
265
|
+
* @param name - 插件名称
|
|
266
|
+
* @param isOrderBy - 是否为动态导入 默认为静态导入
|
|
267
|
+
* @param isNpm - 是否为npm包
|
|
268
|
+
* @param isMain - 是否为主入口文件
|
|
269
|
+
*/
|
|
270
|
+
async createdApp(dir, name, isOrderBy = false, isNpm = false, isMain = false) {
|
|
271
|
+
try {
|
|
272
|
+
const list = [];
|
|
273
|
+
let path = `${this.dirPath}${isNpm ? 'node_modules' : 'plugins'}/${dir}/${name}`;
|
|
274
|
+
if (isOrderBy)
|
|
275
|
+
path = path + `?${Date.now()}`;
|
|
276
|
+
const tmp = await import(path);
|
|
277
|
+
/** npm包的入口文件不作为app载入 只加载 */
|
|
278
|
+
if (isMain)
|
|
279
|
+
return true;
|
|
280
|
+
lodash.forEach(tmp, (App) => {
|
|
281
|
+
const index = this.index;
|
|
282
|
+
this.index++;
|
|
283
|
+
if (typeof App === 'object' && App?.file?.type === 'function') {
|
|
284
|
+
if (!App?.name) {
|
|
285
|
+
logger.error(`[${dir}][${name}] 插件名称错误`);
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
App.file.dir = dir;
|
|
289
|
+
App.file.name = name;
|
|
290
|
+
App.rule.forEach((v, index) => {
|
|
291
|
+
App.rule[index].log = v.log === false ? (id, log) => logger.bot('debug', id, log) : (id, log) => logger.bot('mark', id, log);
|
|
292
|
+
});
|
|
293
|
+
/** handler */
|
|
294
|
+
App.task = this.addTaskFnc(dir, App);
|
|
295
|
+
this.PluginList[index] = App;
|
|
296
|
+
handler.add(index + '', App);
|
|
297
|
+
if (App.accept)
|
|
298
|
+
this.acceptIds.push(index);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (typeof App !== 'function' || !App?.prototype?.constructor)
|
|
302
|
+
return false;
|
|
303
|
+
const Class = new App();
|
|
304
|
+
if (!Class.name) {
|
|
305
|
+
logger.error(`[${dir}][${name}] 插件名称错误`);
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
logger.debug(`载入插件 [${name}][${Class.name}]`);
|
|
309
|
+
this.PluginList[index] = PluginApp({
|
|
310
|
+
file: { dir, name, type: 'class', fnc: App },
|
|
311
|
+
name: Class.name,
|
|
312
|
+
event: Class.event,
|
|
313
|
+
priority: Class.priority,
|
|
314
|
+
accept: false,
|
|
315
|
+
});
|
|
316
|
+
/** 异步收集 加载速度 */
|
|
317
|
+
list.push(this.addRule(dir, name, index, Class));
|
|
318
|
+
list.push(this.addTask(dir, name, index, Class, App));
|
|
319
|
+
list.push(this.addAccept(index, Class));
|
|
320
|
+
list.push(this.addButton(dir, name, index, Class));
|
|
321
|
+
list.push(this.addInit(Class));
|
|
322
|
+
return true;
|
|
323
|
+
});
|
|
324
|
+
await Promise.all(list);
|
|
325
|
+
/** 建立对应的规则索引并排序 */
|
|
326
|
+
if (isOrderBy)
|
|
327
|
+
this.orderBy();
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
if (/Cannot find package '(.+?)'/.exec(error)?.[1]) {
|
|
332
|
+
const key = `${dir}.${name}`;
|
|
333
|
+
if (this.dependErr[key])
|
|
334
|
+
return false;
|
|
335
|
+
this.dependErr[key] = {
|
|
336
|
+
dir,
|
|
337
|
+
name,
|
|
338
|
+
depend: /Cannot find package '(.+?)'/.exec(error)?.[1] || '',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
logger.error(`载入插件错误:${logger.red(`${dir}/${name}`)}`);
|
|
343
|
+
logger.error(error);
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 打印依赖缺失
|
|
350
|
+
*/
|
|
351
|
+
printDependErr() {
|
|
352
|
+
try {
|
|
353
|
+
const keys = Object.keys(this.dependErr);
|
|
354
|
+
if (!keys.length)
|
|
355
|
+
return;
|
|
356
|
+
const msg = ['-----依赖缺失----'];
|
|
357
|
+
keys.forEach(key => {
|
|
358
|
+
const { dir, name, depend } = this.dependErr[key];
|
|
359
|
+
msg.push(`[${dir}][${name}] 缺少依赖:${logger.red(depend)}`);
|
|
360
|
+
});
|
|
361
|
+
msg.push('-------------------');
|
|
362
|
+
logger.error(msg.join('\n'));
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
/** 回收缓存 */
|
|
366
|
+
this.dependErr = {};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 新增rule
|
|
371
|
+
*/
|
|
372
|
+
async addRule(dir, name, index, Class) {
|
|
373
|
+
lodash.forEach(Class.rule, val => {
|
|
374
|
+
if (!val.fnc)
|
|
375
|
+
return logger.error(`[${dir}][${name}] rule.fnc错误:${Class.name}`);
|
|
376
|
+
this.PluginList[index].rule.push({
|
|
377
|
+
reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
|
|
378
|
+
fnc: val.fnc,
|
|
379
|
+
event: val.event,
|
|
380
|
+
permission: val.permission || "all" /* Permission.All */,
|
|
381
|
+
log: val.log === false ? (id, log) => logger.debug('mark', id, log) : (id, log) => logger.bot('mark', id, log),
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* 新增task fnc模式
|
|
387
|
+
*/
|
|
388
|
+
addTaskFnc(dir, App) {
|
|
389
|
+
const task = [];
|
|
390
|
+
lodash.forEach(App.task, val => {
|
|
391
|
+
task.push({
|
|
392
|
+
name: val.name,
|
|
393
|
+
cron: val.cron,
|
|
394
|
+
fnc: val.fnc,
|
|
395
|
+
log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
|
|
396
|
+
schedule: schedule.scheduleJob(val.cron, async () => {
|
|
397
|
+
try {
|
|
398
|
+
typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`);
|
|
399
|
+
if (typeof val.fnc === 'function')
|
|
400
|
+
await val.fnc();
|
|
401
|
+
typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
logger.error(`[定时任务][${dir}][${val.name}] 执行报错`);
|
|
405
|
+
logger.error(error);
|
|
406
|
+
}
|
|
407
|
+
}),
|
|
408
|
+
});
|
|
409
|
+
return true;
|
|
410
|
+
});
|
|
411
|
+
return task;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* 新增task
|
|
415
|
+
*/
|
|
416
|
+
async addTask(dir, name, index, Class, App) {
|
|
417
|
+
/** 定时任务 */
|
|
418
|
+
lodash.forEach(Class.task, val => {
|
|
419
|
+
if (!val.name)
|
|
420
|
+
return logger.error(`[${dir}][${name}] 定时任务name错误`);
|
|
421
|
+
if (!val.cron)
|
|
422
|
+
return logger.error(`[${dir}][${name}] 定时任务cron错误:${Class.name}`);
|
|
423
|
+
this.PluginList[index].task.push({
|
|
424
|
+
name: val.name,
|
|
425
|
+
cron: val.cron,
|
|
426
|
+
fnc: val.fnc,
|
|
427
|
+
log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
|
|
428
|
+
schedule: schedule.scheduleJob(val.cron, async () => {
|
|
429
|
+
try {
|
|
430
|
+
typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`);
|
|
431
|
+
if (typeof val.fnc === 'function') {
|
|
432
|
+
await val.fnc();
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
const cla = new App();
|
|
436
|
+
await cla[val.fnc]();
|
|
437
|
+
}
|
|
438
|
+
typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`);
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
logger.error(`[定时任务][${dir}][${val.name}] 执行报错`);
|
|
442
|
+
logger.error(error);
|
|
443
|
+
}
|
|
444
|
+
}),
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* 新增accept、handler
|
|
450
|
+
*/
|
|
451
|
+
async addAccept(index, Class) {
|
|
452
|
+
if ('accept' in Class && typeof Class.accept === 'function') {
|
|
453
|
+
this.PluginList[index].accept = true;
|
|
454
|
+
this.acceptIds.push(index);
|
|
455
|
+
}
|
|
456
|
+
await handler.add(index + '', Class);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* 新增button
|
|
460
|
+
*/
|
|
461
|
+
async addButton(dir, name, index, Class) {
|
|
462
|
+
lodash.forEach(Class.button, val => {
|
|
463
|
+
if (!val.fnc)
|
|
464
|
+
return logger.error(`[${dir}][${name}] button.fnc错误:${Class.name}`);
|
|
465
|
+
this.PluginList[index].button.push({
|
|
466
|
+
reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
|
|
467
|
+
fnc: val.fnc,
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* 执行初始化
|
|
473
|
+
*/
|
|
474
|
+
async addInit(Class) {
|
|
475
|
+
'init' in Class && typeof Class.init === 'function' && await Class.init();
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* 卸载插件
|
|
479
|
+
*/
|
|
480
|
+
uninstallApp(dir, name) {
|
|
481
|
+
const index = [];
|
|
482
|
+
Object.keys(this.PluginList).forEach(key => {
|
|
483
|
+
const info = this.PluginList[key];
|
|
484
|
+
/** 停止定时任务 */
|
|
485
|
+
info.task.forEach(val => val.schedule?.cancel());
|
|
486
|
+
if (info.file.dir === dir && info.file.name === name) {
|
|
487
|
+
index.push(key);
|
|
488
|
+
delete this.PluginList[key];
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
/** 删除handler */
|
|
492
|
+
index.forEach(key => handler.del(key));
|
|
493
|
+
/** 重新排序 */
|
|
494
|
+
this.orderBy();
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* 监听单个文件热更新
|
|
498
|
+
*/
|
|
499
|
+
watch(dir, name) {
|
|
500
|
+
if (this.watcher.get(`${dir}.${name}`))
|
|
501
|
+
return;
|
|
502
|
+
const file = `./plugins/${dir}/${name}`;
|
|
503
|
+
const watcher = chokidar.watch(file);
|
|
504
|
+
/** 监听修改 */
|
|
505
|
+
watcher.on('change', async () => {
|
|
506
|
+
/** 卸载 */
|
|
507
|
+
this.uninstallApp(dir, name);
|
|
508
|
+
/** 载入插件 */
|
|
509
|
+
await this.createdApp(dir, name, true);
|
|
510
|
+
logger.mark(`[修改插件][${dir}][${name}]`);
|
|
511
|
+
});
|
|
512
|
+
/** 监听删除 */
|
|
513
|
+
watcher.on('unlink', async () => {
|
|
514
|
+
/** 卸载 */
|
|
515
|
+
this.uninstallApp(dir, name);
|
|
516
|
+
this.watcher.delete(`${dir}.${name}`);
|
|
517
|
+
logger.mark(`[卸载插件][${dir}][${name}]`);
|
|
518
|
+
/** 卸载之后停止监听 */
|
|
519
|
+
watcher.close();
|
|
520
|
+
});
|
|
521
|
+
this.watcher.set(`${dir}.${name}`, watcher);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* 监听文件夹更新
|
|
525
|
+
*/
|
|
526
|
+
async watchDir(dir) {
|
|
527
|
+
if (this.watcher.get(dir))
|
|
528
|
+
return;
|
|
529
|
+
const file = `${this.dir}/${dir}/`;
|
|
530
|
+
const watcher = chokidar.watch(file);
|
|
531
|
+
await common.sleep(1000);
|
|
532
|
+
watcher.on('add', async (_path) => {
|
|
533
|
+
/** 排除掉不符合规则文件新增 */
|
|
534
|
+
const ext = this.isTs ? ['.js', '.ts'] : ['.js'];
|
|
535
|
+
if (!ext.some(ext => _path.endsWith(ext)))
|
|
536
|
+
return;
|
|
537
|
+
const name = path.basename(_path);
|
|
538
|
+
await this.createdApp(dir, name, true);
|
|
539
|
+
logger.mark(`[新增插件][${dir}][${name}]`);
|
|
540
|
+
return true;
|
|
541
|
+
});
|
|
542
|
+
watcher.on('change', async (_path) => {
|
|
543
|
+
/** 排除掉不符合规则文件新增 */
|
|
544
|
+
const ext = this.isTs ? ['.js', '.ts'] : ['.js'];
|
|
545
|
+
if (!ext.some(ext => _path.endsWith(ext)))
|
|
546
|
+
return;
|
|
547
|
+
const name = path.basename(_path);
|
|
548
|
+
/** 卸载 */
|
|
549
|
+
this.uninstallApp(dir, name);
|
|
550
|
+
/** 载入插件 */
|
|
551
|
+
await this.createdApp(dir, name, true);
|
|
552
|
+
logger.mark(`[修改插件][${dir}][${name}]`);
|
|
553
|
+
return true;
|
|
554
|
+
});
|
|
555
|
+
watcher.on('unlink', async (_path) => {
|
|
556
|
+
/** 排除掉不符合规则文件新增 */
|
|
557
|
+
const ext = this.isTs ? ['.js', '.ts'] : ['.js'];
|
|
558
|
+
if (!ext.some(ext => _path.endsWith(ext)))
|
|
559
|
+
return;
|
|
560
|
+
const name = path.basename(_path);
|
|
561
|
+
/** 卸载 */
|
|
562
|
+
this.uninstallApp(dir, name);
|
|
563
|
+
logger.mark(`[卸载插件][${dir}][${name}]`);
|
|
564
|
+
return true;
|
|
565
|
+
});
|
|
566
|
+
/** 这里需要检查一下是否已经存在,已经存在就关掉之前的监听 */
|
|
567
|
+
const isExist = this.watcher.get(dir);
|
|
568
|
+
if (isExist) {
|
|
569
|
+
isExist.close();
|
|
570
|
+
this.watcher.delete(dir);
|
|
571
|
+
}
|
|
572
|
+
this.watcher.set(dir, watcher);
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* 加载插件
|
|
578
|
+
*/
|
|
579
|
+
export const pluginLoader = new PluginLoader();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { listener } from '../listener/listener.js';
|
|
2
|
+
import { logger, common, config } from '../../utils/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* 处理基本事件
|
|
5
|
+
*/
|
|
6
|
+
export default class Process {
|
|
7
|
+
/**
|
|
8
|
+
* 进程初始化
|
|
9
|
+
*/
|
|
10
|
+
static async process() {
|
|
11
|
+
/**
|
|
12
|
+
* 监听挂起信号 在终端关闭时触发
|
|
13
|
+
*/
|
|
14
|
+
process.once('SIGHUP', async (code) => await this.exit(code));
|
|
15
|
+
/**
|
|
16
|
+
* 监听中断信号 用户按下 Ctrl + C 时触发
|
|
17
|
+
*/
|
|
18
|
+
process.once('SIGINT', async (code) => await this.exit(code));
|
|
19
|
+
/**
|
|
20
|
+
* 监听终止信号 程序正常退出时触发
|
|
21
|
+
*/
|
|
22
|
+
process.once('SIGTERM', async (code) => await this.exit(code));
|
|
23
|
+
/**
|
|
24
|
+
* 监听退出信号 windows下程序正常退出时触发
|
|
25
|
+
*/
|
|
26
|
+
process.once('SIGBREAK', async (code) => await this.exit(code));
|
|
27
|
+
/**
|
|
28
|
+
* 监听退出信号 与 SIGINT 类似,但会生成核心转储
|
|
29
|
+
*/
|
|
30
|
+
process.once('SIGQUIT', async (code) => await this.exit(code));
|
|
31
|
+
/**
|
|
32
|
+
* 监听退出信号 Node.js进程退出时触发
|
|
33
|
+
*/
|
|
34
|
+
process.once('exit', async (code) => await this.exit(code));
|
|
35
|
+
/**
|
|
36
|
+
* 捕获警告
|
|
37
|
+
*/
|
|
38
|
+
process.on('warning', warning => listener.emit('warn', warning));
|
|
39
|
+
/**
|
|
40
|
+
* 捕获错误
|
|
41
|
+
*/
|
|
42
|
+
process.on('uncaughtException', error => listener.emit('error', error));
|
|
43
|
+
/**
|
|
44
|
+
* 捕获未处理的Promise错误
|
|
45
|
+
*/
|
|
46
|
+
process.on('unhandledRejection', error => listener.emit('error', error));
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 检查后台进程
|
|
51
|
+
*/
|
|
52
|
+
static async check() {
|
|
53
|
+
const host = `http://127.0.0.1:${config.Server.http.port}/api`;
|
|
54
|
+
/**
|
|
55
|
+
* 使用api来检查后台
|
|
56
|
+
*/
|
|
57
|
+
const res = await common.axios(host + '/ping', 'get', { timeout: 100 });
|
|
58
|
+
if (!res)
|
|
59
|
+
return this;
|
|
60
|
+
logger.mark(logger.red('检测到后台进程 正在关闭'));
|
|
61
|
+
/** 发退出信号 */
|
|
62
|
+
await common.axios(host + '/exit', 'get', { timeout: 10 });
|
|
63
|
+
for (let i = 0; i < 50; i++) {
|
|
64
|
+
const res = await common.axios(host + '/ping', 'get', { timeout: 100 });
|
|
65
|
+
/** 请求成功继续循环 */
|
|
66
|
+
if (res)
|
|
67
|
+
continue;
|
|
68
|
+
/** 请求异常即代表后台进程已关闭 */
|
|
69
|
+
logger.mark(logger.green('后台进程已关闭'));
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 走到这里说明后台关闭失败
|
|
74
|
+
* 根据配置文件判断是否继续
|
|
75
|
+
*/
|
|
76
|
+
logger.error(logger.red(`后台进程关闭失败,请检查是否有进程正在占用端口${config.Server.http.port}`));
|
|
77
|
+
if (!config.Config.multi_progress) {
|
|
78
|
+
logger.error(logger.red('当前配置不允许多进程运行,程序即将退出'));
|
|
79
|
+
await this.exit(1);
|
|
80
|
+
}
|
|
81
|
+
logger.error(logger.red('当前配置允许多进程运行,程序继续运行'));
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 退出Karin
|
|
86
|
+
* @param code 退出码
|
|
87
|
+
*/
|
|
88
|
+
static async exit(code = 0) {
|
|
89
|
+
try {
|
|
90
|
+
const { redis } = await import('../../db/index.js');
|
|
91
|
+
if (redis && redis.save)
|
|
92
|
+
await redis.save();
|
|
93
|
+
logger.mark(`Karin 已停止运行 运行时间:${common.uptime()} 退出码:${code || '未知'}`);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
process.exit();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
Process.process();
|