node-karin 0.0.3
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/LICENSE +674 -0
- package/README.md +57 -0
- package/config/defSet/App.yaml +37 -0
- package/config/defSet/config.yaml +43 -0
- package/config/defSet/group.yaml +18 -0
- package/config/defSet/pm2.yaml +21 -0
- package/config/defSet/redis.yaml +18 -0
- package/config/defSet/server.yaml +42 -0
- package/config/view/App.yaml +74 -0
- package/config/view/config.yaml +100 -0
- package/config/view/group.yaml +62 -0
- package/config/view/pm2.yaml +41 -0
- package/config/view/redis.yaml +25 -0
- package/config/view/server.yaml +93 -0
- package/lib/adapter/onebot/onebot11.d.ts +430 -0
- package/lib/adapter/onebot/onebot11.js +1302 -0
- package/lib/core/init.d.ts +0 -0
- package/lib/core/init.js +4 -0
- package/lib/core/karin.d.ts +72 -0
- package/lib/core/karin.js +51 -0
- package/lib/core/listener.d.ts +121 -0
- package/lib/core/listener.js +188 -0
- package/lib/core/plugin.app.d.ts +15 -0
- package/lib/core/plugin.app.js +18 -0
- package/lib/core/plugin.d.ts +182 -0
- package/lib/core/plugin.js +138 -0
- package/lib/core/plugin.loader.d.ts +149 -0
- package/lib/core/plugin.loader.js +462 -0
- package/lib/core/server.d.ts +26 -0
- package/lib/core/server.js +213 -0
- package/lib/db/level.d.ts +20 -0
- package/lib/db/level.js +38 -0
- package/lib/db/redis.d.ts +41 -0
- package/lib/db/redis.js +137 -0
- package/lib/db/redis_level.d.ts +113 -0
- package/lib/db/redis_level.js +290 -0
- package/lib/event/event.d.ts +138 -0
- package/lib/event/event.handler.d.ts +29 -0
- package/lib/event/event.handler.js +142 -0
- package/lib/event/event.js +120 -0
- package/lib/event/message.d.ts +102 -0
- package/lib/event/message.handler.d.ts +25 -0
- package/lib/event/message.handler.js +240 -0
- package/lib/event/message.js +70 -0
- package/lib/event/notice.d.ts +49 -0
- package/lib/event/notice.js +15 -0
- package/lib/event/request.d.ts +49 -0
- package/lib/event/request.js +15 -0
- package/lib/event/review.handler.d.ts +54 -0
- package/lib/event/review.handler.js +382 -0
- package/lib/index.d.ts +23 -0
- package/lib/index.js +40 -0
- package/lib/renderer/app.d.ts +53 -0
- package/lib/renderer/app.js +93 -0
- package/lib/renderer/base.d.ts +30 -0
- package/lib/renderer/base.js +71 -0
- package/lib/renderer/client.d.ts +30 -0
- package/lib/renderer/client.js +159 -0
- package/lib/renderer/http.d.ts +19 -0
- package/lib/renderer/http.js +51 -0
- package/lib/renderer/server.d.ts +42 -0
- package/lib/renderer/server.js +112 -0
- package/lib/renderer/wormhole.d.ts +1 -0
- package/lib/renderer/wormhole.js +154 -0
- package/lib/types/adapter.d.ts +575 -0
- package/lib/types/adapter.js +1 -0
- package/lib/types/config.d.ts +327 -0
- package/lib/types/config.js +1 -0
- package/lib/types/element.d.ts +576 -0
- package/lib/types/element.js +1 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +8 -0
- package/lib/types/logger.d.ts +109 -0
- package/lib/types/logger.js +1 -0
- package/lib/types/onebots11.d.ts +1371 -0
- package/lib/types/onebots11.js +1 -0
- package/lib/types/plugin.d.ts +282 -0
- package/lib/types/plugin.js +1 -0
- package/lib/types/render.d.ts +111 -0
- package/lib/types/render.js +1 -0
- package/lib/types/reply.d.ts +40 -0
- package/lib/types/reply.js +1 -0
- package/lib/types/types.d.ts +898 -0
- package/lib/types/types.js +1 -0
- package/lib/utils/YamlEditor.d.ts +62 -0
- package/lib/utils/YamlEditor.js +208 -0
- package/lib/utils/button.d.ts +49 -0
- package/lib/utils/button.js +79 -0
- package/lib/utils/common.d.ts +123 -0
- package/lib/utils/common.js +413 -0
- package/lib/utils/config.d.ts +72 -0
- package/lib/utils/config.js +254 -0
- package/lib/utils/exec.d.ts +22 -0
- package/lib/utils/exec.js +36 -0
- package/lib/utils/ffmpeg.d.ts +12 -0
- package/lib/utils/ffmpeg.js +25 -0
- package/lib/utils/handler.d.ts +76 -0
- package/lib/utils/handler.js +102 -0
- package/lib/utils/logger.d.ts +3 -0
- package/lib/utils/logger.js +104 -0
- package/lib/utils/segment.d.ts +276 -0
- package/lib/utils/segment.js +448 -0
- package/lib/utils/update.d.ts +69 -0
- package/lib/utils/update.js +151 -0
- package/lib/utils/updateVersion.d.ts +33 -0
- package/lib/utils/updateVersion.js +145 -0
- package/package.json +92 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import Yaml from 'yaml'
|
|
3
|
+
import chokidar from 'chokidar'
|
|
4
|
+
/** 配置文件 */
|
|
5
|
+
export default new (class Cfg {
|
|
6
|
+
dir
|
|
7
|
+
_path
|
|
8
|
+
_pathDef
|
|
9
|
+
change
|
|
10
|
+
watcher
|
|
11
|
+
review
|
|
12
|
+
loggger
|
|
13
|
+
constructor () {
|
|
14
|
+
this.dir = process.cwd()
|
|
15
|
+
this._path = this.dir + '/config/config'
|
|
16
|
+
this._pathDef = this.dir + '/config/defSet'
|
|
17
|
+
/** 缓存 */
|
|
18
|
+
this.change = new Map()
|
|
19
|
+
/** 监听文件 */
|
|
20
|
+
this.watcher = new Map()
|
|
21
|
+
/** 拦截器状态 */
|
|
22
|
+
this.review = false
|
|
23
|
+
this.initCfg()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 初始化配置 */
|
|
27
|
+
async initCfg () {
|
|
28
|
+
if (!fs.existsSync(this._path)) { fs.mkdirSync(this._path) }
|
|
29
|
+
const files = fs.readdirSync(this._pathDef).filter(file => file.endsWith('.yaml'))
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const path = `${this._path}/${file}`
|
|
32
|
+
const pathDef = `${this._pathDef}/${file}`
|
|
33
|
+
if (!fs.existsSync(path)) { fs.copyFileSync(pathDef, path) }
|
|
34
|
+
}
|
|
35
|
+
// 创建插件文件夹文件夹
|
|
36
|
+
const plugins = this.getPlugins()
|
|
37
|
+
this.dirPath('data', plugins)
|
|
38
|
+
this.dirPath('temp', plugins)
|
|
39
|
+
this.dirPath('resources', plugins)
|
|
40
|
+
this.dirPath('temp/html', plugins)
|
|
41
|
+
this.loggger = (await import('./logger.js')).default
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getPlugins () {
|
|
45
|
+
const files = fs.readdirSync('./plugins', { withFileTypes: true })
|
|
46
|
+
// 过滤掉非karin-plugin-开头或karin-adapter-开头的文件夹
|
|
47
|
+
return files.filter(file => file.isDirectory() && (file.name.startsWith('karin-plugin-') || file.name.startsWith('karin-adapter-'))).map(dir => dir.name)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 为每一个插件建立对应的文件夹
|
|
52
|
+
*/
|
|
53
|
+
async dirPath (name, plugins) {
|
|
54
|
+
name = `./${name}`
|
|
55
|
+
if (!fs.existsSync(name)) { fs.mkdirSync(name) }
|
|
56
|
+
for (const plugin of plugins) {
|
|
57
|
+
const path = `${name}/${plugin}`
|
|
58
|
+
if (!fs.existsSync(path)) { fs.mkdirSync(path) }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 超时时间
|
|
64
|
+
*/
|
|
65
|
+
timeout (type = 'ws') {
|
|
66
|
+
let timeout = 60
|
|
67
|
+
if (type === 'ws') {
|
|
68
|
+
timeout = this.Server.websocket.timeout
|
|
69
|
+
} else {
|
|
70
|
+
timeout = this.Server.grpc.timeout
|
|
71
|
+
}
|
|
72
|
+
return timeout || 60
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Redis 配置
|
|
77
|
+
* 采用实时读取优化性能
|
|
78
|
+
*/
|
|
79
|
+
get redis () {
|
|
80
|
+
const config = this.getYaml('config', 'redis', false)
|
|
81
|
+
const defSet = this.getYaml('defSet', 'redis', false)
|
|
82
|
+
const data = { ...defSet, ...config }
|
|
83
|
+
return data
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 主人列表
|
|
88
|
+
* @returns {string[]}
|
|
89
|
+
*/
|
|
90
|
+
get master () {
|
|
91
|
+
return this.Config.master || []
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 管理员列表
|
|
96
|
+
*/
|
|
97
|
+
get admin () {
|
|
98
|
+
return this.Config.admin || []
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** App管理 */
|
|
102
|
+
get App () {
|
|
103
|
+
const key = 'change.App'
|
|
104
|
+
const res = this.change.get(key)
|
|
105
|
+
/** 取缓存 */
|
|
106
|
+
if (res) { return res }
|
|
107
|
+
/** 取配置 */
|
|
108
|
+
const config = this.getYaml('config', 'App', true)
|
|
109
|
+
const defSet = this.getYaml('defSet', 'App', true)
|
|
110
|
+
const data = { ...defSet, ...config }
|
|
111
|
+
/** 缓存 */
|
|
112
|
+
this.change.set(key, data)
|
|
113
|
+
return data
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 基本配置
|
|
118
|
+
*/
|
|
119
|
+
get Config () {
|
|
120
|
+
const key = 'change.config'
|
|
121
|
+
const res = this.change.get(key)
|
|
122
|
+
/** 取缓存 */
|
|
123
|
+
if (res) { return res }
|
|
124
|
+
/** 取配置 */
|
|
125
|
+
const config = this.getYaml('config', 'config', true)
|
|
126
|
+
const defSet = this.getYaml('defSet', 'config', false)
|
|
127
|
+
const data = { ...defSet, ...config }
|
|
128
|
+
/** 缓存 */
|
|
129
|
+
this.change.set(key, data)
|
|
130
|
+
return data
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Server 配置文档
|
|
135
|
+
*/
|
|
136
|
+
get Server () {
|
|
137
|
+
const key = 'change.server'
|
|
138
|
+
/** 取缓存 */
|
|
139
|
+
const res = this.change.get(key)
|
|
140
|
+
if (res) { return res }
|
|
141
|
+
/** 取配置 */
|
|
142
|
+
const config = this.getYaml('config', 'server', true)
|
|
143
|
+
const defSet = this.getYaml('defSet', 'server', false)
|
|
144
|
+
const data = { ...defSet, ...config }
|
|
145
|
+
/** 缓存 */
|
|
146
|
+
this.change.set(key, data)
|
|
147
|
+
return data
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* packageon
|
|
152
|
+
* 实时获取packageon文件
|
|
153
|
+
*/
|
|
154
|
+
get package () {
|
|
155
|
+
const data = fs.readFileSync('./package.json', 'utf8')
|
|
156
|
+
const pack = JSON.parse(data)
|
|
157
|
+
return pack
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 获取群配置
|
|
162
|
+
*/
|
|
163
|
+
group (group_id = '') {
|
|
164
|
+
const key = 'change.group'
|
|
165
|
+
/** 取缓存 */
|
|
166
|
+
let res = this.change.get(key)
|
|
167
|
+
if (res) {
|
|
168
|
+
res = { ...res.defCfg.default, ...res.Config.default, ...(res.Config[group_id] || {}) }
|
|
169
|
+
return res
|
|
170
|
+
}
|
|
171
|
+
/** 取配置 */
|
|
172
|
+
const Config = this.getYaml('config', 'group')
|
|
173
|
+
const defCfg = this.getYaml('defSet', 'group')
|
|
174
|
+
const data = { Config, defCfg }
|
|
175
|
+
/** 缓存 */
|
|
176
|
+
res = this.change.set(key, data)
|
|
177
|
+
res = { ...defCfg.default, ...Config.default, ...(Config[group_id] || {}) }
|
|
178
|
+
return res
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 获取配置yaml
|
|
183
|
+
*/
|
|
184
|
+
getYaml (type, name, isWatch = false) {
|
|
185
|
+
/** 文件路径 */
|
|
186
|
+
const file = `./config/${type}/${name}.yaml`
|
|
187
|
+
/** 读取文件 */
|
|
188
|
+
const data = Yaml.parse(fs.readFileSync(file, 'utf8'))
|
|
189
|
+
/** 监听文件 */
|
|
190
|
+
if (isWatch) { this.watch(type, name, file) }
|
|
191
|
+
return data
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 监听配置文件
|
|
196
|
+
* @param {'defSet'|'config'} type 类型
|
|
197
|
+
* @param {string} name 文件名称 不带后缀
|
|
198
|
+
* @param {string} file 文件路径
|
|
199
|
+
*/
|
|
200
|
+
async watch (type, name, file) {
|
|
201
|
+
const key = `change.${name}`
|
|
202
|
+
/** 已经监听过了 */
|
|
203
|
+
const res = this.change.get(key)
|
|
204
|
+
if (res) { return true }
|
|
205
|
+
/** 监听文件 */
|
|
206
|
+
const watcher = chokidar.watch(file)
|
|
207
|
+
/** 监听文件变化 */
|
|
208
|
+
watcher.on('change', () => {
|
|
209
|
+
this.change.delete(key)
|
|
210
|
+
this.loggger.mark(`[修改配置文件][${type}][${name}]`)
|
|
211
|
+
/** 文件修改后调用对应的方法 */
|
|
212
|
+
switch (`change_${name}`) {
|
|
213
|
+
case 'change_App':
|
|
214
|
+
this.change_App()
|
|
215
|
+
break
|
|
216
|
+
case 'change_config':
|
|
217
|
+
this.change_config()
|
|
218
|
+
break
|
|
219
|
+
case 'change_group':
|
|
220
|
+
this.change_group()
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
/** 缓存 防止重复监听 */
|
|
225
|
+
this.watcher.set(key, watcher)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async change_App () {
|
|
229
|
+
await this.#review()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async change_config () {
|
|
233
|
+
/** 修改日志等级 */
|
|
234
|
+
this.loggger.level = this.Config.log_level
|
|
235
|
+
await this.#review()
|
|
236
|
+
// if (this.Server.HotUpdate) {
|
|
237
|
+
// const { Bot } = await import('../index.js')
|
|
238
|
+
// Bot.emit('restart_http', {})
|
|
239
|
+
// Bot.emit('restart_grpc', {})
|
|
240
|
+
// }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async change_group () {
|
|
244
|
+
await this.#review()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async #review () {
|
|
248
|
+
// if (this.review) return
|
|
249
|
+
// this.review = true
|
|
250
|
+
// const review = await import('../event/review')
|
|
251
|
+
// review.default.main()
|
|
252
|
+
// this.review = false
|
|
253
|
+
}
|
|
254
|
+
})()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行 shell 命令
|
|
3
|
+
* @param cmd 命令
|
|
4
|
+
* @param log 是否打印日志
|
|
5
|
+
* @param options 选项
|
|
6
|
+
*/
|
|
7
|
+
export declare const exec: (cmd: string, log?: boolean, options?: {
|
|
8
|
+
cwd: string;
|
|
9
|
+
encoding: string;
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
/**
|
|
12
|
+
* - 执行状态
|
|
13
|
+
*/
|
|
14
|
+
status: 'ok' | 'failed';
|
|
15
|
+
/**
|
|
16
|
+
* - 错误信息
|
|
17
|
+
*/
|
|
18
|
+
error: Error | null;
|
|
19
|
+
stdout: string | '';
|
|
20
|
+
stderr: string | '';
|
|
21
|
+
}>;
|
|
22
|
+
export default exec;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logger from './logger.js'
|
|
2
|
+
import { exec as execCmd } from 'child_process'
|
|
3
|
+
/**
|
|
4
|
+
* 执行 shell 命令
|
|
5
|
+
* @param cmd 命令
|
|
6
|
+
* @param log 是否打印日志
|
|
7
|
+
* @param options 选项
|
|
8
|
+
*/
|
|
9
|
+
export const exec = (cmd, log = true, options = { cwd: process.cwd(), encoding: 'utf-8' }) => {
|
|
10
|
+
return new Promise(resolve => {
|
|
11
|
+
const logMessage = (level, message) => {
|
|
12
|
+
if (log) { logger[level](message) }
|
|
13
|
+
}
|
|
14
|
+
const logType = (status) => {
|
|
15
|
+
switch (status) {
|
|
16
|
+
case '开始执行':
|
|
17
|
+
return logger.yellow('[exec][开始执行]')
|
|
18
|
+
case 'ok':
|
|
19
|
+
return logger.green('[exec][执行成功]')
|
|
20
|
+
case 'failed':
|
|
21
|
+
return logger.red('[exec][执行失败]')
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const formatMessage = (status, details) => [logType(status), `cmd: ${cmd}`, `cwd: ${options.cwd || process.cwd()}`, details, '--------'].join('\n')
|
|
25
|
+
logMessage('info', formatMessage('开始执行', ''))
|
|
26
|
+
execCmd(cmd, options, (error, stdout, stderr) => {
|
|
27
|
+
if (error) {
|
|
28
|
+
logMessage('error', formatMessage('failed', `Error: ${error.message || error.stack || error.toString()}`))
|
|
29
|
+
return resolve({ status: 'failed', error, stdout, stderr })
|
|
30
|
+
}
|
|
31
|
+
logMessage('mark', formatMessage('ok', `stdout: ${stdout}\nstderr: ${stderr}`))
|
|
32
|
+
resolve({ status: 'ok', error, stdout, stderr })
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
export default exec
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行 ffmpeg 命令
|
|
3
|
+
*/
|
|
4
|
+
export default function ffmpeg(): Promise<false | ((cmd: string, log?: boolean, options?: {
|
|
5
|
+
cwd: string;
|
|
6
|
+
encoding: string;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
status: "failed" | "ok";
|
|
9
|
+
error: Error | null;
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
}>)>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import exec from './exec.js'
|
|
2
|
+
import logger from './logger.js'
|
|
3
|
+
import Config from './config.js'
|
|
4
|
+
/**
|
|
5
|
+
* 执行 ffmpeg 命令
|
|
6
|
+
*/
|
|
7
|
+
export default async function ffmpeg () {
|
|
8
|
+
let ffmpeg = 'ffmpeg'
|
|
9
|
+
const { status } = await exec('ffmpeg -version', false)
|
|
10
|
+
if (status !== 'ok') {
|
|
11
|
+
logger.debug('ffmpeg 未安装,开始尝试读取配置文件是否存在ffmpeg路径')
|
|
12
|
+
const ffmpegPath = Config.Config.ffmpeg_path
|
|
13
|
+
if (!ffmpegPath) {
|
|
14
|
+
logger.warn('ffmpeg 未安装,请安装 ffmpeg 或手动配置 ffmpeg 路径后重启')
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
ffmpeg = `"${ffmpegPath}"`
|
|
18
|
+
}
|
|
19
|
+
// 返回函数
|
|
20
|
+
return async (cmd, log = true, options = { cwd: process.cwd(), encoding: 'utf-8' }) => {
|
|
21
|
+
cmd = cmd.replace(/^ffmpeg/, '').trim()
|
|
22
|
+
cmd = `${ffmpeg} ${cmd}`
|
|
23
|
+
return await exec(cmd, log, options)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import Plugin from '../core/plugin.js';
|
|
2
|
+
import { dirName, fileName } from '../types/plugin.js';
|
|
3
|
+
/**
|
|
4
|
+
* 事件处理器类
|
|
5
|
+
*/
|
|
6
|
+
declare const _default: {
|
|
7
|
+
events: {
|
|
8
|
+
[key: string]: {
|
|
9
|
+
/**
|
|
10
|
+
* - 文件信息
|
|
11
|
+
*/
|
|
12
|
+
file: {
|
|
13
|
+
/**
|
|
14
|
+
* - 插件包名称
|
|
15
|
+
*/
|
|
16
|
+
dir: dirName;
|
|
17
|
+
/**
|
|
18
|
+
* - 文件名称
|
|
19
|
+
*/
|
|
20
|
+
name: fileName;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* - 事件class
|
|
24
|
+
*/
|
|
25
|
+
App: new () => Plugin;
|
|
26
|
+
/**
|
|
27
|
+
* - 事件键
|
|
28
|
+
*/
|
|
29
|
+
key: string;
|
|
30
|
+
/**
|
|
31
|
+
* - 事件处理函数名称
|
|
32
|
+
*/
|
|
33
|
+
fnc: string;
|
|
34
|
+
/**
|
|
35
|
+
* - 优先级
|
|
36
|
+
*/
|
|
37
|
+
priority: number;
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* 添加事件处理器
|
|
42
|
+
* @param {Object} config 配置对象
|
|
43
|
+
* @param {string} config.name 处理器名称
|
|
44
|
+
* @param {string} config.dir 处理器所在目录
|
|
45
|
+
* @param {Function} config.App 应用构造函数
|
|
46
|
+
* @param {Object} config.Class 类配置
|
|
47
|
+
*/
|
|
48
|
+
add({ name, dir, App, Class }: {
|
|
49
|
+
dir: dirName;
|
|
50
|
+
name: fileName;
|
|
51
|
+
App: new () => Plugin;
|
|
52
|
+
Class: Plugin;
|
|
53
|
+
}): void;
|
|
54
|
+
/**
|
|
55
|
+
* 删除事件处理器
|
|
56
|
+
*/
|
|
57
|
+
del({ dir, name, key, }: {
|
|
58
|
+
dir: dirName | '';
|
|
59
|
+
name: fileName | '';
|
|
60
|
+
/**
|
|
61
|
+
* 事件键 未传入则删除所有处理器
|
|
62
|
+
*/
|
|
63
|
+
key: string | '';
|
|
64
|
+
}): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 调用事件处理器
|
|
67
|
+
* @param key 事件键
|
|
68
|
+
* @param args 自定义参数 一般用来传递e之类的
|
|
69
|
+
*/
|
|
70
|
+
call(key: string, args?: {}): Promise<any>;
|
|
71
|
+
/**
|
|
72
|
+
* 检查是否存在指定键的事件处理器
|
|
73
|
+
*/
|
|
74
|
+
has(key: string): boolean;
|
|
75
|
+
};
|
|
76
|
+
export default _default;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import util from 'util'
|
|
2
|
+
import lodash from 'lodash'
|
|
3
|
+
import logger from './logger.js'
|
|
4
|
+
/**
|
|
5
|
+
* 事件处理器类
|
|
6
|
+
*/
|
|
7
|
+
export default new (class EventHandler {
|
|
8
|
+
events
|
|
9
|
+
constructor () {
|
|
10
|
+
this.events = {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 添加事件处理器
|
|
15
|
+
* @param {Object} config 配置对象
|
|
16
|
+
* @param {string} config.name 处理器名称
|
|
17
|
+
* @param {string} config.dir 处理器所在目录
|
|
18
|
+
* @param {Function} config.App 应用构造函数
|
|
19
|
+
* @param {Object} config.Class 类配置
|
|
20
|
+
*/
|
|
21
|
+
add ({ name, dir, App, Class }) {
|
|
22
|
+
for (const cfg of Class.handler) {
|
|
23
|
+
const { key = '', fnc = '', priority = 2000 } = cfg
|
|
24
|
+
if (!key) {
|
|
25
|
+
return logger.error(`[Handler][Add]: [${name}] 缺少 key`)
|
|
26
|
+
}
|
|
27
|
+
if (!fnc) {
|
|
28
|
+
return logger.error(`[Handler][Add]: [${name}] 缺少 fnc`)
|
|
29
|
+
}
|
|
30
|
+
logger.debug(`[Handler][Reg]: [${name}][${key}]`)
|
|
31
|
+
if (!Array.isArray(this.events[key])) { this.events[key] = [] }
|
|
32
|
+
this.events[key].push({ file: { name, dir }, App, key, fnc, priority })
|
|
33
|
+
this.events[key] = lodash.orderBy(this.events[key], ['priority'], ['asc'])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 删除事件处理器
|
|
39
|
+
*/
|
|
40
|
+
del ({ dir = '', name = '', key = '' }) {
|
|
41
|
+
/** 这里是删除所有 全部重新初始化 */
|
|
42
|
+
if (!key && !dir && !name) {
|
|
43
|
+
this.events = {}
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
/** 热重载 删除指定目录 */
|
|
47
|
+
if (!key) {
|
|
48
|
+
for (const v of Object.keys(this.events)) {
|
|
49
|
+
this.events[v] = this.events[v].filter(v => v.file.dir !== dir || v.file.name !== name)
|
|
50
|
+
// 如果处理器为空则删除键
|
|
51
|
+
if (!this.events[v].length) {
|
|
52
|
+
delete this.events[v]
|
|
53
|
+
} else {
|
|
54
|
+
this.events[v] = lodash.orderBy(this.events[v], ['priority'], ['asc'])
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
if (!this.events[key]) { return false }
|
|
60
|
+
this.events[key] = this.events[key].filter(v => v.file.dir !== dir || v.file.name !== name)
|
|
61
|
+
this.events[key] = lodash.orderBy(this.events[key], ['priority'], ['asc'])
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 调用事件处理器
|
|
67
|
+
* @param key 事件键
|
|
68
|
+
* @param args 自定义参数 一般用来传递e之类的
|
|
69
|
+
*/
|
|
70
|
+
async call (key, args = {}) {
|
|
71
|
+
let ret
|
|
72
|
+
for (const v of this.events[key] || []) {
|
|
73
|
+
const App = new v.App()
|
|
74
|
+
if ('e' in args && args.e) { App.e = args.e }
|
|
75
|
+
let done = true
|
|
76
|
+
// 标记函数,用于标记处理器是否执行成功,由处理器自行调用,如果未调用则认为处理器未执行成功
|
|
77
|
+
const reject = (msg = '') => {
|
|
78
|
+
if (msg) { logger.mark(`[Handler][Reject]: [${v.file.dir}][${v.file.name}][${key}] ${msg}`) }
|
|
79
|
+
done = false
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
ret = await App[v.fnc](args, reject)
|
|
83
|
+
if (util.types.isPromise(ret)) { ret = await ret }
|
|
84
|
+
if (done) {
|
|
85
|
+
logger.mark(`[Handler][Done]: [${v.file.dir}][${v.file.name}][${key}]`)
|
|
86
|
+
return ret
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// 产生错误继续下一个处理器
|
|
90
|
+
logger.error(`[Handler][Error]: [${v.file.dir}][${v.file.name}][${key}] ${e}`)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return ret
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 检查是否存在指定键的事件处理器
|
|
98
|
+
*/
|
|
99
|
+
has (key) {
|
|
100
|
+
return !!this.events[key]
|
|
101
|
+
}
|
|
102
|
+
})()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import log4js from 'log4js'
|
|
4
|
+
import Cfg from './config.js'
|
|
5
|
+
const logsDir = './logs'
|
|
6
|
+
if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir) }
|
|
7
|
+
const { log_level, log_days_Keep, log4jsCfg } = Cfg.Config
|
|
8
|
+
const level = log_level || log4jsCfg.level || 'info'
|
|
9
|
+
const daysToKeep = log_days_Keep || log4jsCfg.daysToKeep || 7
|
|
10
|
+
const { overall, fragments, maxLogSize } = log4jsCfg
|
|
11
|
+
const defaultOptions = { appenders: ['console'], level, enableCallStack: process.env.KarinMode === 'dev' }
|
|
12
|
+
const options = {
|
|
13
|
+
appenders: {
|
|
14
|
+
console: {
|
|
15
|
+
type: 'console',
|
|
16
|
+
layout: {
|
|
17
|
+
type: 'pattern',
|
|
18
|
+
pattern: `%[[Karin][%d{hh:mm:ss.SSS}][%4.4p]%] ${process.env.KarinMode === 'dev' ? '[%f{3}:%l] ' : ''}%m`,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
categories: { default: defaultOptions },
|
|
23
|
+
}
|
|
24
|
+
if (overall) {
|
|
25
|
+
defaultOptions.appenders.unshift('overall')
|
|
26
|
+
options.appenders.overall = {
|
|
27
|
+
/** 输出到文件 */
|
|
28
|
+
type: 'file',
|
|
29
|
+
filename: 'logs/logger',
|
|
30
|
+
pattern: 'yyyy-MM-dd.log',
|
|
31
|
+
/** 日期后缀 */
|
|
32
|
+
keepFileExt: true,
|
|
33
|
+
/** 日志文件名中包含日期模式 */
|
|
34
|
+
alwaysIncludePattern: true,
|
|
35
|
+
/** 日志文件保留天数 */
|
|
36
|
+
daysToKeep,
|
|
37
|
+
/** 日志输出格式 */
|
|
38
|
+
layout: {
|
|
39
|
+
type: 'pattern',
|
|
40
|
+
pattern: '[%d{hh:mm:ss.SSS}][%4.4p] %m',
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (fragments) {
|
|
45
|
+
defaultOptions.appenders.unshift('fragments')
|
|
46
|
+
options.appenders.fragments = {
|
|
47
|
+
type: 'file',
|
|
48
|
+
filename: 'logs/app.log',
|
|
49
|
+
pattern: 'MM-dd.log',
|
|
50
|
+
keepFileExt: true,
|
|
51
|
+
alwaysIncludePattern: true,
|
|
52
|
+
daysToKeep,
|
|
53
|
+
maxLogSize: (maxLogSize || 30) * 1024 * 1024,
|
|
54
|
+
/** 最大文件数 */
|
|
55
|
+
numBackups: 9999999,
|
|
56
|
+
/** 日志输出格式 */
|
|
57
|
+
layout: {
|
|
58
|
+
type: 'pattern',
|
|
59
|
+
pattern: '[%d{hh:mm:ss.SSS}][%4.4p] %m',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
log4js.configure(options)
|
|
64
|
+
const logger = log4js.getLogger('default')
|
|
65
|
+
logger.chalk = chalk
|
|
66
|
+
logger.red = chalk.red
|
|
67
|
+
logger.green = chalk.green
|
|
68
|
+
logger.yellow = chalk.yellow
|
|
69
|
+
logger.blue = chalk.blue
|
|
70
|
+
logger.magenta = chalk.magenta
|
|
71
|
+
logger.cyan = chalk.cyan
|
|
72
|
+
logger.white = chalk.white
|
|
73
|
+
logger.gray = chalk.gray
|
|
74
|
+
logger.violet = chalk.hex('#868ECC')
|
|
75
|
+
logger.fnc = chalk.hex(Cfg.Config.log_color || '#FFFF00')
|
|
76
|
+
logger.bot = (level, id, ...args) => {
|
|
77
|
+
switch (level) {
|
|
78
|
+
case 'trace':
|
|
79
|
+
logger.trace(logger.violet(`[Bot:${id}] `), ...args)
|
|
80
|
+
break
|
|
81
|
+
case 'debug':
|
|
82
|
+
logger.debug(logger.violet(`[Bot:${id}] `), ...args)
|
|
83
|
+
break
|
|
84
|
+
case 'mark':
|
|
85
|
+
logger.mark(logger.violet(`[Bot:${id}] `), ...args)
|
|
86
|
+
break
|
|
87
|
+
case 'info':
|
|
88
|
+
logger.info(logger.violet(`[Bot:${id}] `), ...args)
|
|
89
|
+
break
|
|
90
|
+
case 'warn':
|
|
91
|
+
logger.warn(logger.violet(`[Bot:${id}] `), ...args)
|
|
92
|
+
break
|
|
93
|
+
case 'error':
|
|
94
|
+
logger.error(logger.violet(`[Bot:${id}] `), ...args)
|
|
95
|
+
break
|
|
96
|
+
case 'fatal':
|
|
97
|
+
logger.fatal(logger.violet(`[Bot:${id}] `), ...args)
|
|
98
|
+
break
|
|
99
|
+
default:
|
|
100
|
+
logger.info(logger.violet(`[Bot:${id}] `), ...args)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
global.logger = logger
|
|
104
|
+
export default logger
|