node-karin 0.6.9 → 0.6.11

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.
@@ -5,6 +5,7 @@ import { contact, KarinElement } from '../../types/index.js';
5
5
  */
6
6
  export declare class AdapterInput implements KarinAdapter {
7
7
  #private;
8
+ token: string;
8
9
  socket: WebSocket;
9
10
  account: KarinAdapter['account'];
10
11
  adapter: KarinAdapter['adapter'];
@@ -4,36 +4,11 @@ import { listener } from '../../core/index.js';
4
4
  import { KarinMessage } from '../../event/index.js';
5
5
  import { config, common, YamlEditor } from '../../utils/index.js';
6
6
  const { enable, msgToFile, token: oldToken, ip } = config.Config.AdapterInput;
7
- let token = oldToken;
8
- if (oldToken === 'AdapterInput') {
9
- try {
10
- token = randomUUID();
11
- const yaml = new YamlEditor('./config/config/config.yaml');
12
- const data = yaml.get('AdapterInput');
13
- if (!data) {
14
- const yaml1 = new YamlEditor('./config/defSet/config.yaml');
15
- const data1 = yaml1.get('AdapterInput');
16
- data1.token = token;
17
- yaml.set('AdapterInput', data1);
18
- }
19
- else {
20
- data.token = token;
21
- yaml.set('AdapterInput', data);
22
- }
23
- yaml.save();
24
- }
25
- catch (e) {
26
- logger.error('AdapterInput token更换失败,请手动更换token');
27
- }
28
- }
29
- // 清空文件夹
30
- fs.readdirSync('./temp/input').forEach((file) => {
31
- fs.unlinkSync(`./temp/input/${file}`);
32
- });
33
7
  /**
34
8
  * - 标准输入输出适配器
35
9
  */
36
10
  export class AdapterInput {
11
+ token;
37
12
  #stdin;
38
13
  socket;
39
14
  account;
@@ -41,8 +16,9 @@ export class AdapterInput {
41
16
  version;
42
17
  constructor() {
43
18
  this.#stdin = false;
19
+ this.token = oldToken;
44
20
  this.account = { uid: 'input', uin: 'input', name: 'input' };
45
- this.adapter = { id: 'shell', name: 'input', type: 'internal', sub_type: 'internal', start_time: Date.now(), connect: '' };
21
+ this.adapter = { id: 'shell', name: 'input', type: 'internal', sub_type: 'internal', start_time: Date.now(), connect: '', index: 0 };
46
22
  this.version = { name: 'input', app_name: 'input', version: '1.0.0' };
47
23
  }
48
24
  get self_id() {
@@ -52,6 +28,35 @@ export class AdapterInput {
52
28
  if (this.#stdin)
53
29
  return;
54
30
  this.#stdin = true;
31
+ if (oldToken === 'AdapterInput') {
32
+ try {
33
+ this.token = randomUUID();
34
+ const yaml = new YamlEditor('./config/config/config.yaml');
35
+ const data = yaml.get('AdapterInput');
36
+ if (!data) {
37
+ const yaml1 = new YamlEditor('./config/defSet/config.yaml');
38
+ const data1 = yaml1.get('AdapterInput');
39
+ data1.token = this.token;
40
+ yaml.set('AdapterInput', data1);
41
+ }
42
+ else {
43
+ data.token = this.token;
44
+ yaml.set('AdapterInput', data);
45
+ }
46
+ yaml.save();
47
+ }
48
+ catch (e) {
49
+ logger.error('AdapterInput token更换失败,请手动更换token');
50
+ }
51
+ }
52
+ // 清空文件夹
53
+ fs.readdirSync('./temp/input').forEach((file) => {
54
+ fs.unlinkSync(`./temp/input/${file}`);
55
+ });
56
+ /** 注册bot */
57
+ const index = listener.addBot({ bot: this, type: this.adapter.type });
58
+ if (index)
59
+ this.adapter.index = index;
55
60
  process.stdin.on('data', data => this.#input(data.toString()));
56
61
  process.once('stdin.close', () => process.stdin.removeAllListeners('data'));
57
62
  }
@@ -108,7 +113,7 @@ export class AdapterInput {
108
113
  const name = `${Date.now()}.${type === 'image' ? 'jpg' : type === 'voice' ? 'mp3' : 'file'}`;
109
114
  // 写入文件
110
115
  fs.writeFileSync(`./temp/input/${name}`, buffer);
111
- return `[${type === 'image' ? '图片' : '语音'}: http://${ip}:${config.Server.http.port}/api/input?name=${name}&token=${token} ]`;
116
+ return `[${type === 'image' ? '图片' : '语音'}: http://${ip}:${config.Server.http.port}/api/input?name=${name}&token=${this.token} ]`;
112
117
  }
113
118
  async GetVersion() {
114
119
  const data = this.version;
@@ -181,9 +186,5 @@ export class AdapterInput {
181
186
  async GetGroupMemberList() { throw new Error('Method not implemented.'); }
182
187
  async GetGroupHonor() { throw new Error('Method not implemented.'); }
183
188
  }
184
- if (enable) {
185
- const bot = new AdapterInput();
186
- bot.stdin();
187
- /** 注册bot */
188
- listener.emit('bot', { type: 'internal', bot });
189
- }
189
+ if (enable)
190
+ new AdapterInput().stdin();
@@ -30,4 +30,3 @@ export declare class KritorGrpc {
30
30
  */
31
31
  AdapterConvertKarin(data: Array<kritor.common.Element>): Array<KarinElement>;
32
32
  }
33
- export declare const grpcServer: KritorGrpc;
@@ -721,7 +721,9 @@ export class KritorGrpc {
721
721
  const bot = new AdapterKritor(grpc, uid, uin + '');
722
722
  this.BotMap.set(uid, bot);
723
723
  /** 注册bot */
724
- listener.emit('bot', { type: 'grpc', bot });
724
+ const index = listener.addBot({ type: bot.adapter.type, bot });
725
+ if (index)
726
+ bot.adapter.index = index;
725
727
  },
726
728
  };
727
729
  this.#server.addService(coreProtoGrpcType.kritor.core.CoreService.service, authenticationServer);
@@ -977,5 +979,3 @@ export class KritorGrpc {
977
979
  return elements;
978
980
  }
979
981
  }
980
- export const grpcServer = new KritorGrpc();
981
- grpcServer.init();
@@ -1,5 +1,6 @@
1
1
  import { kritor } from 'kritor-proto';
2
2
  import { logger, config, segment } from '../../utils/index.js';
3
+ import { listener } from '../../core/index.js';
3
4
  /**
4
5
  * @extends KarinAdapter
5
6
  */
@@ -21,7 +22,7 @@ export default class AdapterKritor {
21
22
  */
22
23
  grpc, uid, uin) {
23
24
  this.account = { uid, uin, name: '' };
24
- this.adapter = { id: 'QQ', name: 'Kritor', type: 'grpc', sub_type: 'server', start_time: Date.now(), connect: '' };
25
+ this.adapter = { id: 'QQ', name: 'Kritor', type: 'grpc', sub_type: 'server', start_time: Date.now(), connect: '', index: 0 };
25
26
  this.version = { name: '', app_name: '', version: '' };
26
27
  this.grpc = grpc;
27
28
  /** 自增 */
@@ -29,7 +30,11 @@ export default class AdapterKritor {
29
30
  /** 监听响应事件 */
30
31
  this.grpc.on('data', data => this.grpc.emit(data.seq, data));
31
32
  /** 监听关闭事件 */
32
- this.grpc.on('end', () => this.logger('warn', '连接已断开'));
33
+ this.grpc.once('end', () => {
34
+ this.logger('warn', '[反向gRPC] 连接已断开');
35
+ this.grpc.removeAllListeners();
36
+ this.adapter.index && listener.delBot(this.adapter.index);
37
+ });
33
38
  this.#init();
34
39
  }
35
40
  get self_id() {
@@ -31,8 +31,6 @@ export declare class AdapterOneBot11 implements KarinAdapter {
31
31
  * 获取当前登录号信息
32
32
  */
33
33
  getSelf(): Promise<void>;
34
- /** 是否初始化 */
35
- get isInit(): Promise<unknown>;
36
34
  /**
37
35
  * onebot11转karin
38
36
  * @return karin格式消息
@@ -23,7 +23,7 @@ export class AdapterOneBot11 {
23
23
  constructor() {
24
24
  this.index = 0;
25
25
  this.account = { uid: '', uin: '', name: '' };
26
- this.adapter = { id: 'QQ', name: 'OneBot11', type: 'ws', sub_type: 'internal', start_time: Date.now(), connect: '' };
26
+ this.adapter = { id: 'QQ', name: 'OneBot11', type: 'ws', sub_type: 'internal', start_time: Date.now(), connect: '', index: 0 };
27
27
  this.version = { name: '', app_name: '', version: '' };
28
28
  }
29
29
  /**
@@ -54,15 +54,6 @@ export class AdapterOneBot11 {
54
54
  this.index = 0;
55
55
  this.#initListener(connect);
56
56
  });
57
- /** 监听断开 */
58
- this.socket.on('close', async () => {
59
- this.index++;
60
- logger.warn(`[正向WS][重连次数:${this.index}] 连接断开,将在5秒后重连:${connect}`);
61
- /** 停止全部监听 */
62
- this.socket.removeAllListeners();
63
- await common.sleep(5000);
64
- this.client(connect);
65
- });
66
57
  }
67
58
  /**
68
59
  * 初始化监听事件
@@ -92,6 +83,8 @@ export class AdapterOneBot11 {
92
83
  this.logger('warn', `[${type}] 连接断开:${connect}`);
93
84
  /** 停止全部监听 */
94
85
  this.socket.removeAllListeners();
86
+ /** 注销bot */
87
+ this.adapter.index && listener.delBot(this.adapter.index);
95
88
  /** 正向ws需要重连 */
96
89
  if (this.adapter.sub_type === 'client') {
97
90
  this.index++;
@@ -129,20 +122,9 @@ export class AdapterOneBot11 {
129
122
  this.account.name = data.account_name;
130
123
  this.logger('info', `[加载完成][app_name:${this.version.name}][version:${this.version.version}] ` + logger.green(this.adapter.connect));
131
124
  /** 注册bot */
132
- listener.emit('bot', { type: 'websocket', bot: this });
133
- }
134
- /** 是否初始化 */
135
- get isInit() {
136
- return new Promise(resolve => {
137
- const timer = setInterval(() => {
138
- if (this.account.name) {
139
- const { name, version } = this.version;
140
- this.logger('info', `建立连接成功:[${name}(${version})] ${this.adapter.connect}`);
141
- clearInterval(timer);
142
- resolve(true);
143
- }
144
- }, 100);
145
- });
125
+ const index = listener.addBot({ type: this.adapter.type, bot: this });
126
+ if (index)
127
+ this.adapter.index = index;
146
128
  }
147
129
  /**
148
130
  * 处理事件
@@ -151,20 +133,17 @@ export class AdapterOneBot11 {
151
133
  #event(data) {
152
134
  switch (data.post_type) {
153
135
  case 'meta_event': {
154
- switch (data.meta_event_type) {
155
- case 'heartbeat':
156
- this.logger('trace', `[心跳]:${JSON.stringify(data.status)}`);
157
- break;
158
- case 'lifecycle': {
159
- const typeMap = {
160
- enable: 'OneBot启用',
161
- disable: 'OneBot停用',
162
- connect: 'WebSocket连接成功',
163
- };
164
- const sub_type = data.sub_type;
165
- this.logger('debug', `[生命周期]:${typeMap[sub_type]}`);
166
- break;
167
- }
136
+ if (data.meta_event_type === 'heartbeat') {
137
+ this.logger('trace', `[心跳]:${JSON.stringify(data.status)}`);
138
+ }
139
+ else {
140
+ const typeMap = {
141
+ enable: 'OneBot启用',
142
+ disable: 'OneBot停用',
143
+ connect: 'WebSocket连接成功',
144
+ };
145
+ const sub_type = data.sub_type;
146
+ this.logger('debug', `[生命周期]:${typeMap[sub_type]}`);
168
147
  }
169
148
  listener.emit('meta_event', data);
170
149
  return;
package/lib/core/karin.js CHANGED
@@ -11,44 +11,32 @@ export class Karin {
11
11
  */
12
12
 
13
13
  command (reg, second, options = {}) {
14
- const Reg = typeof reg === 'string' ? new RegExp(reg) : reg
14
+ reg = typeof reg === 'string' ? new RegExp(reg) : reg
15
+ const fnc = typeof second === 'function'
16
+ ? second
17
+ : async (e) => {
18
+ const element = typeof second === 'number' ? String(second) : second
19
+ if ('delay' in options && options.delay) { await common.sleep(options.delay) }
20
+ await e.reply(element, {
21
+ at: ('at' in options && options.at) || false,
22
+ reply: ('reply' in options && options.reply) || false,
23
+ recallMsg: ('recallMsg' in options && Number(options.recallMsg)) || 0,
24
+ })
25
+ return !('stop' in options && !options.stop)
26
+ }
15
27
  const data = {
16
28
  name: options.name || 'function',
17
29
  priority: options.priority,
18
30
  rule: [
19
31
  {
20
- reg: Reg,
21
- fnc: second,
32
+ reg,
33
+ fnc,
22
34
  permission: options.permission || 'all',
23
35
  log: options.log ?? true,
24
36
  },
25
37
  ],
26
38
  }
27
- switch (typeof second) {
28
- case 'function': {
29
- return PluginApp(data)
30
- }
31
- case 'string':
32
- case 'number':
33
- case 'object': {
34
- const element = common.makeMessage(typeof second === 'number' ? String(second) : second)
35
- const fnc = async (e) => {
36
- if ('delay' in options && options.delay) { await common.sleep(options.delay) }
37
- await e.reply(element, {
38
- at: ('at' in options && options.at) || false,
39
- reply: ('reply' in options && options.reply) || false,
40
- recallMsg: ('recallMsg' in options && Number(options.recallMsg)) || 0,
41
- })
42
- if ('stop' in options && !options.stop) { return false }
43
- return true
44
- }
45
- data.rule[0].fnc = fnc
46
- return PluginApp(data)
47
- }
48
- default: {
49
- throw new Error('command: second argument must be a function or string')
50
- }
51
- }
39
+ return PluginApp(data)
52
40
  }
53
41
 
54
42
  /**
@@ -31,14 +31,9 @@ class Listeners extends EventEmitter {
31
31
  let path = data.path || '无'
32
32
  if (path && data.type !== 'grpc') { path = `ws://127.0.0.1:/${config.Server.http.port}${data.path}` }
33
33
  path = logger.green(path)
34
- logger.info(`[适配器][注册][${data.type}] ` + path)
34
+ logger.info(`[适配器][注册][${data.type}]: ` + path)
35
35
  this.addAdapter(data)
36
36
  })
37
- this.on('bot', data => {
38
- if (!this.addBot(data)) { return }
39
- logger.info(`[机器人][注册][${data.type}] ` + logger.green(`[account:${data.bot.account.uid || data.bot.account.uin}(${data.bot.account.name})]`))
40
- this.emit('karin:online', data.bot.account.uid || data.bot.account.uin)
41
- })
42
37
  this.on('message', data => new MessageHandler(data))
43
38
  this.on('notice', data => new NoticeHandler(data))
44
39
  this.on('request', data => new RequestHandler(data))
@@ -55,6 +50,9 @@ class Listeners extends EventEmitter {
55
50
  return false
56
51
  }
57
52
  this.list.push({ index, type: data.type, bot: data.bot })
53
+ logger.info(`[机器人][注册][${data.type}] ` + logger.green(`[account:${data.bot.account.uid || data.bot.account.uin}(${data.bot.account.name})]`))
54
+ this.emit('karin:online', data.bot.account.uid || data.bot.account.uin)
55
+ logger.debug('注册', this.list)
58
56
  return index
59
57
  }
60
58
 
@@ -64,6 +62,7 @@ class Listeners extends EventEmitter {
64
62
  */
65
63
  delBot (index) {
66
64
  this.list = this.list.filter(item => item.index !== index)
65
+ logger.debug('[机器人][卸载] ', this.list)
67
66
  }
68
67
 
69
68
  /**
@@ -4,8 +4,8 @@ import { PluginType, KarinElement, KarinNodeElement, EventType, KarinNoticeEvent
4
4
  */
5
5
  export declare class Plugin implements PluginType {
6
6
  e: EventType<this>;
7
- init?: () => void;
8
- accept?: (e: any) => Promise<void>;
7
+ init?: () => Promise<any>;
8
+ accept?: (e: any) => Promise<any>;
9
9
  replyCallback: PluginType['replyCallback'];
10
10
  /**
11
11
  * @param name - 插件名称
@@ -2,11 +2,8 @@ import chokidar from 'chokidar';
2
2
  import schedule from 'node-schedule';
3
3
  import { Plugin } from './plugin.js';
4
4
  import { PluginApps, PluginTask, dirName, fileName, AppInfo } from '../types/index.js';
5
- /**
6
- * 加载插件
7
- */
8
- export declare const pluginLoader: {
9
- dir: "./plugins";
5
+ declare class PluginLoader {
6
+ dir: './plugins';
10
7
  dirPath: string;
11
8
  /**
12
9
  * - 插件索引ID
@@ -42,6 +39,10 @@ export declare const pluginLoader: {
42
39
  * - 是否ts环境
43
40
  */
44
41
  isTs: boolean;
42
+ /**
43
+ * - 是否开发环境
44
+ */
45
+ isDev: boolean;
45
46
  /**
46
47
  * - 最终缓存的插件列表 通过index索引
47
48
  */
@@ -70,10 +71,11 @@ export declare const pluginLoader: {
70
71
  dir: dirName;
71
72
  name?: fileName;
72
73
  }>;
74
+ constructor();
73
75
  /**
74
76
  * 插件初始化
75
77
  */
76
- load(): Promise<any>;
78
+ load(): Promise<this>;
77
79
  /**
78
80
  * 获取所有插件
79
81
  */
@@ -83,13 +85,13 @@ export declare const pluginLoader: {
83
85
  * @param path - 插件路径
84
86
  * @param lang - 语言环境
85
87
  */
86
- getIndex(path: string, lang: "js" | "ts"): string | boolean;
88
+ getIndex(path: string, lang: 'js' | 'ts'): string | boolean;
87
89
  /**
88
90
  * 获取指定文件夹下的所有插件
89
91
  * @param dir - 插件包名称
90
92
  * @param isTs - 是否获取ts插件
91
93
  */
92
- getApps(dir: dirName, isTs: boolean): void;
94
+ getApps(dir: dirName, isTs: boolean, isWatch?: boolean): void;
93
95
  /**
94
96
  * 排序并打印插件加兹安信息
95
97
  * @param isPrint - 是否打印
@@ -99,9 +101,33 @@ export declare const pluginLoader: {
99
101
  * 新增插件
100
102
  * @param dir - 插件包路径
101
103
  * @param name - 插件名称
102
- * @param isOrderBy - 是否重新导入
104
+ * @param isOrderBy - 是否为动态导入 默认为静态导入
103
105
  */
104
106
  createdApp(dir: dirName, name: fileName, isOrderBy?: boolean): Promise<boolean>;
107
+ /**
108
+ * 新增rule
109
+ */
110
+ addRule(dir: dirName, name: fileName, index: number, Class: Plugin): Promise<void>;
111
+ /**
112
+ * 新增task fnc模式
113
+ */
114
+ addTaskFnc(dir: dirName, App: PluginApps): PluginTask[];
115
+ /**
116
+ * 新增task
117
+ */
118
+ addTask(dir: dirName, name: fileName, index: number, Class: Plugin, App: new () => Plugin): Promise<void>;
119
+ /**
120
+ * 新增accept、handler
121
+ */
122
+ addAccept(index: number, Class: Plugin): Promise<void>;
123
+ /**
124
+ * 新增button
125
+ */
126
+ addButton(dir: dirName, name: fileName, index: number, Class: Plugin): Promise<void>;
127
+ /**
128
+ * 执行初始化
129
+ */
130
+ addInit(Class: Plugin): Promise<void>;
105
131
  /**
106
132
  * 卸载插件
107
133
  */
@@ -114,4 +140,15 @@ export declare const pluginLoader: {
114
140
  * 监听文件夹更新
115
141
  */
116
142
  watchDir(dir: dirName): Promise<true | undefined>;
117
- };
143
+ watcAdd(dir: dirName, name: fileName): Promise<boolean | undefined>;
144
+ /**
145
+ * 传入文件名称 返回是否符合当前环境规则
146
+ * @param name - 文件名称
147
+ */
148
+ isApp(name: fileName): boolean;
149
+ }
150
+ /**
151
+ * 加载插件
152
+ */
153
+ export declare const pluginLoader: PluginLoader;
154
+ export {};
@@ -7,10 +7,7 @@ import { listener } from './listener.js'
7
7
  import PluginApp from './plugin.app.js'
8
8
  import { render } from '../render/index.js'
9
9
  import { common, handler, logger } from '../utils/index.js'
10
- /**
11
- * 加载插件
12
- */
13
- export const pluginLoader = new (class PluginLoader {
10
+ class PluginLoader {
14
11
  dir
15
12
  dirPath
16
13
  /**
@@ -41,6 +38,10 @@ export const pluginLoader = new (class PluginLoader {
41
38
  * - 是否ts环境
42
39
  */
43
40
  isTs
41
+ /**
42
+ * - 是否开发环境
43
+ */
44
+ isDev
44
45
  /**
45
46
  * - 最终缓存的插件列表 通过index索引
46
47
  */
@@ -62,6 +63,7 @@ export const pluginLoader = new (class PluginLoader {
62
63
  this.dir = './plugins'
63
64
  this.dirPath = common.urlToPath(import.meta.url)
64
65
  this.isTs = process.env.karin_app_lang === 'ts'
66
+ this.isDev = process.env.karin_app_mode === 'dev'
65
67
  this.watcher = new Map()
66
68
  this.watchList = []
67
69
  this.FileList = []
@@ -79,7 +81,10 @@ export const pluginLoader = new (class PluginLoader {
79
81
  async load () {
80
82
  this.getPlugins()
81
83
  listener.once('plugin.watch', () => {
82
- for (const v of this.watchList) { v.name ? this.watch(v.dir, v.name) : this.watchDir(v.dir) }
84
+ for (const v of this.watchList) {
85
+ v.name ? this.watch(v.dir, v.name) : this.watchDir(v.dir)
86
+ logger.debug(`[热更新][${v.dir}]${v.name ? `[${v.name}]` : ''} 监听中...`)
87
+ }
83
88
  })
84
89
  logger.info(logger.green('-----------'))
85
90
  logger.info('加载插件中..')
@@ -115,21 +120,14 @@ export const pluginLoader = new (class PluginLoader {
115
120
  */
116
121
  /** 非插件包 加载该文件夹下全部js 视语言环境加载ts */
117
122
  if (!common.isPlugin(PluginPath)) {
118
- this.watchList.push({ dir })
119
- const list = fs.readdirSync(`${this.dir}/${dir}`, { withFileTypes: true })
120
- for (const file of list) {
121
- /** 忽略不符合规则的文件 */
122
- const ext = this.isTs ? ['.js', '.ts'] : ['.js']
123
- if (!ext.includes(path.extname(file.name))) { continue }
124
- this.FileList.push({ dir, name: file.name })
125
- }
123
+ this.getApps(dir, this.isTs, true)
126
124
  continue
127
125
  }
128
126
  /** 入口文件 */
129
127
  const index = this.getIndex(PluginPath, process.env.karin_app_lang)
130
128
  if (index) {
131
- this.watchList.push({ dir, name: index })
132
129
  this.FileList.push({ dir, name: index })
130
+ this.isDev && this.watchList.push({ dir, name: index })
133
131
  }
134
132
  /** 检查是否存在karin.apps */
135
133
  const pack = common.readJson(`${PluginPath}/package.json`)
@@ -181,8 +179,8 @@ export const pluginLoader = new (class PluginLoader {
181
179
  * @param dir - 插件包名称
182
180
  * @param isTs - 是否获取ts插件
183
181
  */
184
- getApps (dir, isTs) {
185
- this.watchList.push({ dir })
182
+ getApps (dir, isTs, isWatch = false) {
183
+ isWatch && this.watchList.push({ dir })
186
184
  const ext = isTs ? ['.js', '.ts'] : ['.js']
187
185
  const list = fs.readdirSync(`${this.dir}/${dir}`, { withFileTypes: true })
188
186
  for (const file of list) {
@@ -234,10 +232,11 @@ export const pluginLoader = new (class PluginLoader {
234
232
  * 新增插件
235
233
  * @param dir - 插件包路径
236
234
  * @param name - 插件名称
237
- * @param isOrderBy - 是否重新导入
235
+ * @param isOrderBy - 是否为动态导入 默认为静态导入
238
236
  */
239
237
  async createdApp (dir, name, isOrderBy = false) {
240
238
  try {
239
+ const list = []
241
240
  let path = `${this.dirPath}plugins/${dir}/${name}`
242
241
  if (isOrderBy) { path = path + `?${Date.now()}` }
243
242
  const tmp = await import(path)
@@ -249,29 +248,9 @@ export const pluginLoader = new (class PluginLoader {
249
248
  App.file.dir = dir
250
249
  App.file.name = name
251
250
  /** handler */
252
- handler.add(index + '', App)
253
- const task = []
254
- /** 定时任务 */
255
- lodash.forEach(App.task, val => {
256
- task.push({
257
- name: val.name,
258
- cron: val.cron,
259
- fnc: val.fnc,
260
- log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
261
- schedule: schedule.scheduleJob(val.cron, async () => {
262
- try {
263
- typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`)
264
- if (typeof val.fnc === 'function') { await val.fnc() }
265
- typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`)
266
- } catch (error) {
267
- logger.error(`[定时任务][${dir}][${val.name}] 执行报错`)
268
- logger.error(error)
269
- }
270
- }),
271
- })
272
- })
273
- App.task = task
251
+ App.task = this.addTaskFnc(dir, App)
274
252
  this.PluginList[index] = App
253
+ handler.add(index + '', App)
275
254
  if (App.accept) { this.acceptIds.push(index) }
276
255
  return true
277
256
  }
@@ -279,72 +258,22 @@ export const pluginLoader = new (class PluginLoader {
279
258
  const Class = new App()
280
259
  if (!Class.name) { return logger.error(`[${dir}][${name}] 插件名称错误`) }
281
260
  logger.debug(`载入插件 [${name}][${Class.name}]`)
282
- const info = PluginApp({
283
- file: {
284
- dir,
285
- name,
286
- type: 'class',
287
- fnc: App,
288
- },
261
+ this.PluginList[index] = PluginApp({
262
+ file: { dir, name, type: 'class', fnc: App },
289
263
  name: Class.name,
290
264
  event: Class.event,
291
265
  priority: Class.priority,
292
- accept: Class.accept && typeof Class.accept === 'function',
293
- })
294
- /** 定时任务 */
295
- lodash.forEach(Class.task, val => {
296
- if (!val.name) { return logger.error(`[${dir}][${name}] 定时任务name错误`) }
297
- if (!val.cron) { return logger.error(`[${dir}][${name}] 定时任务cron错误:${Class.name}`) }
298
- info.task.push({
299
- name: val.name,
300
- cron: val.cron,
301
- fnc: val.fnc,
302
- log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
303
- schedule: schedule.scheduleJob(val.cron, async () => {
304
- try {
305
- typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`)
306
- if (typeof val.fnc === 'function') {
307
- await val.fnc()
308
- } else {
309
- const cla = new App()
310
- await cla[val.fnc]()
311
- }
312
- typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`)
313
- } catch (error) {
314
- logger.error(`[定时任务][${dir}][${val.name}] 执行报错`)
315
- logger.error(error)
316
- }
317
- }),
318
- })
319
- })
320
- /** rule */
321
- lodash.forEach(Class.rule, val => {
322
- if (!val.fnc) { return logger.error(`[${dir}][${name}] rule.fnc错误:${Class.name}`) }
323
- info.rule.push({
324
- reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
325
- fnc: val.fnc,
326
- event: val.event,
327
- permission: val.permission || 'all',
328
- log: val.log === false ? (id, log) => logger.debug('mark', id, log) : (id, log) => logger.bot('mark', id, log),
329
- })
330
- })
331
- /** button */
332
- lodash.forEach(Class.button, val => {
333
- if (!val.fnc) { return logger.error(`[${dir}][${name}] rule.fnc错误:${Class.name}`) }
334
- info.button.push({
335
- reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
336
- fnc: val.fnc,
337
- })
266
+ accept: false,
338
267
  })
339
- /** accept */
340
- if (Class.accept && typeof Class.accept === 'function') { this.acceptIds.push(index) }
341
- /** handler */
342
- handler.add(index + '', Class)
343
- /** 执行初始化 */
344
- Class.init && Class.init()
345
- this.PluginList[index] = info
268
+ /** 异步收集 加载速度 */
269
+ list.push(this.addRule(dir, name, index, Class))
270
+ list.push(this.addTask(dir, name, index, Class, App))
271
+ list.push(this.addAccept(index, Class))
272
+ list.push(this.addButton(dir, name, index, Class))
273
+ list.push(this.addInit(Class))
346
274
  return true
347
275
  })
276
+ await Promise.all(list)
348
277
  // rule收集并排序
349
278
  if (isOrderBy) { this.orderBy() }
350
279
  return true
@@ -366,6 +295,112 @@ export const pluginLoader = new (class PluginLoader {
366
295
  }
367
296
  }
368
297
 
298
+ /**
299
+ * 新增rule
300
+ */
301
+ async addRule (dir, name, index, Class) {
302
+ lodash.forEach(Class.rule, val => {
303
+ if (!val.fnc) { return logger.error(`[${dir}][${name}] rule.fnc错误:${Class.name}`) }
304
+ this.PluginList[index].rule.push({
305
+ reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
306
+ fnc: val.fnc,
307
+ event: val.event,
308
+ permission: val.permission || 'all',
309
+ log: val.log === false ? (id, log) => logger.debug('mark', id, log) : (id, log) => logger.bot('mark', id, log),
310
+ })
311
+ })
312
+ }
313
+
314
+ /**
315
+ * 新增task fnc模式
316
+ */
317
+ addTaskFnc (dir, App) {
318
+ const task = []
319
+ lodash.forEach(App.task, val => {
320
+ task.push({
321
+ name: val.name,
322
+ cron: val.cron,
323
+ fnc: val.fnc,
324
+ log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
325
+ schedule: schedule.scheduleJob(val.cron, async () => {
326
+ try {
327
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`)
328
+ if (typeof val.fnc === 'function') { await val.fnc() }
329
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`)
330
+ } catch (error) {
331
+ logger.error(`[定时任务][${dir}][${val.name}] 执行报错`)
332
+ logger.error(error)
333
+ }
334
+ }),
335
+ })
336
+ return true
337
+ })
338
+ return task
339
+ }
340
+
341
+ /**
342
+ * 新增task
343
+ */
344
+ async addTask (dir, name, index, Class, App) {
345
+ /** 定时任务 */
346
+ lodash.forEach(Class.task, val => {
347
+ if (!val.name) { return logger.error(`[${dir}][${name}] 定时任务name错误`) }
348
+ if (!val.cron) { return logger.error(`[${dir}][${name}] 定时任务cron错误:${Class.name}`) }
349
+ this.PluginList[index].task.push({
350
+ name: val.name,
351
+ cron: val.cron,
352
+ fnc: val.fnc,
353
+ log: val.log === false ? (log) => logger.debug(log) : (log) => logger.mark(log),
354
+ schedule: schedule.scheduleJob(val.cron, async () => {
355
+ try {
356
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 开始执行`)
357
+ if (typeof val.fnc === 'function') {
358
+ await val.fnc()
359
+ } else {
360
+ const cla = new App()
361
+ await cla[val.fnc]()
362
+ }
363
+ typeof val.log === 'function' && val.log(`[定时任务][${dir}][${val.name}] 执行完毕`)
364
+ } catch (error) {
365
+ logger.error(`[定时任务][${dir}][${val.name}] 执行报错`)
366
+ logger.error(error)
367
+ }
368
+ }),
369
+ })
370
+ })
371
+ }
372
+
373
+ /**
374
+ * 新增accept、handler
375
+ */
376
+ async addAccept (index, Class) {
377
+ if (Class.accept && typeof Class.accept === 'function') {
378
+ this.PluginList[index].accept = true
379
+ this.acceptIds.push(index)
380
+ }
381
+ await handler.add(index + '', Class)
382
+ }
383
+
384
+ /**
385
+ * 新增button
386
+ */
387
+ async addButton (dir, name, index, Class) {
388
+ lodash.forEach(Class.button, val => {
389
+ if (!val.fnc) { return logger.error(`[${dir}][${name}] button.fnc错误:${Class.name}`) }
390
+ this.PluginList[index].button.push({
391
+ reg: val.reg instanceof RegExp ? val.reg : new RegExp(val.reg),
392
+ fnc: val.fnc,
393
+ })
394
+ })
395
+ }
396
+
397
+ /**
398
+ * 执行初始化
399
+ */
400
+ async addInit (Class) {
401
+ Class.init && await Class.init()
402
+ }
403
+
369
404
  /**
370
405
  * 卸载插件
371
406
  */
@@ -480,4 +515,41 @@ export const pluginLoader = new (class PluginLoader {
480
515
  this.watcher.set(dir, watcher)
481
516
  return true
482
517
  }
483
- })()
518
+
519
+ async watcAdd (dir, name) {
520
+ const file = `${this.dir}/${dir}/`
521
+ const filePath = `${file}/${name}`
522
+ logger.debug(`[热更新][新增插件] ${filePath}`)
523
+ if (!this.isApp(name)) {
524
+ logger.debug(`[热更新][新增插件] ${filePath} 文件类型错误 不符合当前环境规则 已忽略`)
525
+ return false
526
+ }
527
+ /** 载入插件 */
528
+ const res = await this.createdApp(dir, name, true)
529
+ if (!res) { return }
530
+ /** 延迟1秒 等待卸载完成 */
531
+ await common.sleep(1000)
532
+ /** 新增插件之后重新监听文件夹 */
533
+ this.watcher.delete(dir)
534
+ this.watchDir(dir)
535
+ logger.mark(`[新增插件][${dir}][${name}]`)
536
+ return true
537
+ }
538
+
539
+ /**
540
+ * 传入文件名称 返回是否符合当前环境规则
541
+ * @param name - 文件名称
542
+ */
543
+ isApp (name) {
544
+ /** 任何环境都支持js */
545
+ if (name.endsWith('.js')) { return true }
546
+ /** ts环境支持ts */
547
+ if (name.endsWith('.ts') && this.isTs) { return true }
548
+ /** 其他情况返回false */
549
+ return false
550
+ }
551
+ }
552
+ /**
553
+ * 加载插件
554
+ */
555
+ export const pluginLoader = new PluginLoader()
@@ -72,7 +72,7 @@ export default class Process {
72
72
  * 走到这里说明后台关闭失败
73
73
  * 根据配置文件判断是否继续
74
74
  */
75
- logger.error(logger.red('后台进程关闭失败,请手动关闭'))
75
+ logger.error(logger.red(`后台进程关闭失败,请检查是否有进程正在占用端口${config.Server.http.port}`))
76
76
  if (!config.Config.multi_progress) {
77
77
  logger.error(logger.red('当前配置不允许多进程运行,程序即将退出'))
78
78
  await this.exit(1)
@@ -21,5 +21,5 @@ export declare const server: {
21
21
  */
22
22
  staticPath(): void;
23
23
  /** 重启当前HTTP服务器 */
24
- "__#14@#restartServer"(): Promise<void>;
24
+ "__#17@#restartServer"(): Promise<void>;
25
25
  };
@@ -7,6 +7,7 @@ import express from 'express'
7
7
  import { exec, config, logger, common } from '../utils/index.js'
8
8
  import { render, HttpRenderer, Wormhole, RenderClient } from '../render/index.js'
9
9
  import { AdapterOneBot11 } from '../adapter/onebot/onebot11.js'
10
+ import { KritorGrpc } from '../adapter/index.js'
10
11
  export const server = new (class Server {
11
12
  reg
12
13
  list
@@ -28,8 +29,10 @@ export const server = new (class Server {
28
29
  */
29
30
  async init () {
30
31
  try {
31
- // 防止多进程端口冲突 启动失败
32
+ /** 防止多进程端口冲突 启动失败 */
32
33
  await Process.check()
34
+ /** 初始化gRPC服务器 */
35
+ new KritorGrpc().init()
33
36
  this.WebSocketServer.on('connection', (socket, request) => {
34
37
  const path = request.url
35
38
  const headers = request.headers
package/lib/db/redis.js CHANGED
@@ -37,7 +37,7 @@ class Redis {
37
37
  }
38
38
  /** 第一次连接失败尝试拉起 windows直接降级 */
39
39
  if (process.platform === 'win32') {
40
- logger.error(`Redis 建立连接失败:${logger.red(data)}`)
40
+ // logger.info(`Redis 建立连接失败:${data}`)
41
41
  return await this.LevelDB()
42
42
  }
43
43
  this.RunCmd = 'redis-server --save 900 1 --save 300 10 --daemonize yes' + (await this.aarch64())
@@ -50,10 +50,10 @@ class Redis {
50
50
  logger.info('Redis 连接成功')
51
51
  return data
52
52
  }
53
- logger.error(`Redis 二次建立连接失败:${logger.red(data)}`)
53
+ logger.warn(`Redis 二次建立连接失败:${logger.red(data)}`)
54
54
  return false
55
55
  } catch (error) {
56
- logger.error(`Redis 启动失败:${logger.red(data)}`)
56
+ logger.warn(`Redis 启动失败:${logger.red(data)}`)
57
57
  return await this.LevelDB()
58
58
  }
59
59
  }
@@ -63,9 +63,8 @@ class Redis {
63
63
  */
64
64
  async LevelDB () {
65
65
  try {
66
- logger.mark(logger.red('正在降级为 LevelDB 代替 Redis 只能使用基础功能'))
66
+ logger.info('使用LevelDB代替Redis实现基础Api')
67
67
  const redis = new RedisLevel()
68
- logger.info('LevelDB 降级成功')
69
68
  return redis
70
69
  } catch (error) {
71
70
  logger.error('降级为 LevelDB 失败')
@@ -43,6 +43,10 @@ export interface KarinAdapter {
43
43
  * - 适配器信息
44
44
  */
45
45
  adapter: {
46
+ /**
47
+ * - 适配器注册之后的索引
48
+ */
49
+ index: number;
46
50
  /**
47
51
  * - 适配器ID
48
52
  */
@@ -8,7 +8,7 @@ export declare const handler: {
8
8
  * @param index 插件索引
9
9
  * @param Class 插件类
10
10
  */
11
- add(index: string, Class: PluginType | PluginApps): void;
11
+ add(index: string, Class: PluginType | PluginApps): Promise<void>;
12
12
  /**
13
13
  * 删除事件处理器
14
14
  * 如果不传参数则删除所有处理器
@@ -10,7 +10,7 @@ export const handler = new (class EventHandler {
10
10
  * @param index 插件索引
11
11
  * @param Class 插件类
12
12
  */
13
- add (index, Class) {
13
+ async add (index, Class) {
14
14
  lodash.forEach(Class.handler, val => {
15
15
  if (!val.key) { logger.error(`[Handler][Add]: [${Class.name}] 缺少 key`) }
16
16
  if (!val.fnc) { logger.error(`[Handler][Add]: [${Class.name}] 缺少 fnc`) }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-karin",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "private": false,
5
5
  "description": "基于 Kritor 进行开发的nodejs机器人框架",
6
6
  "homepage": "https://github.com/KarinJS/Karin",
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "scripts": {
38
38
  "app": "node .",
39
- "build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json && npm run fix && npm run cp",
39
+ "build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json && npm run cp && npm run fix",
40
40
  "cp": "cp ./lib/modules.d.ts ./modules.d.ts && cp ./lib/modules.js ./modules.js",
41
41
  "delete": "pm2 delete ./config/config/pm2.yaml",
42
42
  "dev": "tsx ./lib/index.js --dev",