node-karin 0.0.3 → 0.1.0
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 -674
- package/README.md +57 -57
- package/config/defSet/App.yaml +37 -37
- package/config/defSet/config.yaml +43 -43
- package/config/defSet/group.yaml +18 -18
- package/config/defSet/pm2.yaml +21 -21
- package/config/defSet/server.yaml +42 -42
- package/config/view/App.yaml +74 -74
- package/config/view/config.yaml +100 -100
- package/config/view/group.yaml +62 -62
- package/config/view/pm2.yaml +41 -41
- package/config/view/redis.yaml +25 -25
- package/config/view/server.yaml +93 -93
- package/lib/adapter/onebot/onebot11.d.ts +430 -430
- package/lib/adapter/onebot/onebot11.js +1265 -1302
- package/lib/core/init.js +4 -4
- package/lib/core/karin.d.ts +72 -72
- package/lib/core/karin.js +51 -51
- package/lib/core/listener.d.ts +121 -121
- package/lib/core/listener.js +178 -178
- package/lib/core/plugin.app.d.ts +15 -15
- package/lib/core/plugin.app.js +18 -18
- package/lib/core/plugin.d.ts +182 -182
- package/lib/core/plugin.js +132 -132
- package/lib/core/plugin.loader.d.ts +149 -149
- package/lib/core/plugin.loader.js +451 -451
- package/lib/core/server.d.ts +26 -26
- package/lib/core/server.js +209 -209
- package/lib/db/level.d.ts +20 -20
- package/lib/db/level.js +36 -36
- package/lib/db/redis.d.ts +41 -41
- package/lib/db/redis.js +131 -131
- package/lib/db/redis_level.d.ts +113 -113
- package/lib/db/redis_level.js +274 -274
- package/lib/event/event.d.ts +138 -138
- package/lib/event/event.handler.d.ts +29 -29
- package/lib/event/event.handler.js +138 -138
- package/lib/event/event.js +120 -120
- package/lib/event/message.d.ts +102 -102
- package/lib/event/message.handler.d.ts +25 -25
- package/lib/event/message.handler.js +237 -237
- package/lib/event/message.js +69 -69
- package/lib/event/notice.d.ts +49 -49
- package/lib/event/notice.js +14 -14
- package/lib/event/request.d.ts +49 -49
- package/lib/event/request.js +14 -14
- package/lib/event/review.handler.d.ts +54 -54
- package/lib/event/review.handler.js +374 -374
- package/lib/index.d.ts +23 -23
- package/lib/index.js +39 -40
- package/lib/renderer/app.d.ts +53 -53
- package/lib/renderer/app.js +88 -88
- package/lib/renderer/base.d.ts +30 -30
- package/lib/renderer/base.js +68 -68
- package/lib/renderer/client.d.ts +30 -30
- package/lib/renderer/client.js +155 -155
- package/lib/renderer/http.d.ts +19 -19
- package/lib/renderer/http.js +50 -50
- package/lib/renderer/server.d.ts +42 -42
- package/lib/renderer/server.js +110 -110
- package/lib/renderer/wormhole.d.ts +1 -1
- package/lib/renderer/wormhole.js +154 -154
- package/lib/types/adapter.d.ts +575 -575
- package/lib/types/adapter.js +1 -1
- package/lib/types/config.d.ts +327 -327
- package/lib/types/config.js +1 -1
- package/lib/types/element.d.ts +576 -576
- package/lib/types/element.js +1 -1
- package/lib/types/index.d.ts +8 -8
- package/lib/types/index.js +8 -8
- package/lib/types/logger.d.ts +109 -109
- package/lib/types/logger.js +1 -1
- package/lib/types/onebots11.d.ts +1371 -1371
- package/lib/types/onebots11.js +1 -1
- package/lib/types/plugin.d.ts +282 -282
- package/lib/types/plugin.js +1 -1
- package/lib/types/render.d.ts +111 -111
- package/lib/types/render.js +1 -1
- package/lib/types/reply.d.ts +40 -40
- package/lib/types/reply.js +1 -1
- package/lib/types/types.d.ts +898 -898
- package/lib/types/types.js +1 -1
- package/lib/utils/YamlEditor.d.ts +62 -62
- package/lib/utils/YamlEditor.js +197 -197
- package/lib/utils/button.d.ts +49 -49
- package/lib/utils/button.js +75 -75
- package/lib/utils/common.d.ts +123 -123
- package/lib/utils/common.js +396 -396
- package/lib/utils/config.d.ts +72 -72
- package/lib/utils/config.js +254 -254
- package/lib/utils/exec.d.ts +22 -22
- package/lib/utils/exec.js +36 -36
- package/lib/utils/ffmpeg.d.ts +12 -12
- package/lib/utils/ffmpeg.js +25 -25
- package/lib/utils/handler.d.ts +76 -76
- package/lib/utils/handler.js +98 -98
- package/lib/utils/logger.d.ts +3 -3
- package/lib/utils/logger.js +104 -104
- package/lib/utils/segment.d.ts +276 -276
- package/lib/utils/segment.js +420 -420
- package/lib/utils/update.d.ts +69 -69
- package/lib/utils/update.js +145 -145
- package/lib/utils/updateVersion.d.ts +33 -33
- package/lib/utils/updateVersion.js +140 -140
- package/package.json +92 -91
package/lib/utils/common.js
CHANGED
|
@@ -1,413 +1,413 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import axios from 'axios'
|
|
4
|
-
import { promisify } from 'util'
|
|
5
|
-
import logger from '../utils/logger.js'
|
|
6
|
-
import segment from '../utils/segment.js'
|
|
7
|
-
import { pipeline, Readable } from 'stream'
|
|
8
|
-
import lodash from 'lodash'
|
|
9
|
-
import { fileURLToPath } from 'url'
|
|
10
|
-
/**
|
|
11
|
-
* 常用方法
|
|
12
|
-
*/
|
|
13
|
-
export default new (class Common {
|
|
14
|
-
streamPipeline
|
|
15
|
-
constructor () {
|
|
16
|
-
this.streamPipeline = promisify(pipeline)
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
import { promisify } from 'util'
|
|
5
|
+
import logger from '../utils/logger.js'
|
|
6
|
+
import segment from '../utils/segment.js'
|
|
7
|
+
import { pipeline, Readable } from 'stream'
|
|
8
|
+
import lodash from 'lodash'
|
|
9
|
+
import { fileURLToPath } from 'url'
|
|
10
|
+
/**
|
|
11
|
+
* 常用方法
|
|
12
|
+
*/
|
|
13
|
+
export default new (class Common {
|
|
14
|
+
streamPipeline
|
|
15
|
+
constructor () {
|
|
16
|
+
this.streamPipeline = promisify(pipeline)
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 休眠函数
|
|
21
|
-
* @param ms 毫秒
|
|
22
|
-
*/
|
|
23
|
-
sleep (ms) {
|
|
24
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 休眠函数
|
|
21
|
+
* @param ms 毫秒
|
|
22
|
+
*/
|
|
23
|
+
sleep (ms) {
|
|
24
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 下载保存文件
|
|
29
|
-
* @param fileUrl 下载地址
|
|
30
|
-
* @param savePath 保存路径
|
|
31
|
-
* @param param axios参数
|
|
32
|
-
*/
|
|
33
|
-
async downFile (fileUrl, savePath, param = {}) {
|
|
34
|
-
try {
|
|
35
|
-
this.mkdir(path.dirname(savePath))
|
|
36
|
-
logger && logger.info(`[下载文件] ${fileUrl}`)
|
|
37
|
-
const response = await axios.get(fileUrl, { ...param, responseType: 'stream' })
|
|
38
|
-
await this.streamPipeline(response.data, fs.createWriteStream(savePath))
|
|
39
|
-
return true
|
|
40
|
-
} catch (err) {
|
|
41
|
-
logger && logger.error(`下载文件错误:${err}`)
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 下载保存文件
|
|
29
|
+
* @param fileUrl 下载地址
|
|
30
|
+
* @param savePath 保存路径
|
|
31
|
+
* @param param axios参数
|
|
32
|
+
*/
|
|
33
|
+
async downFile (fileUrl, savePath, param = {}) {
|
|
34
|
+
try {
|
|
35
|
+
this.mkdir(path.dirname(savePath))
|
|
36
|
+
logger && logger.info(`[下载文件] ${fileUrl}`)
|
|
37
|
+
const response = await axios.get(fileUrl, { ...param, responseType: 'stream' })
|
|
38
|
+
await this.streamPipeline(response.data, fs.createWriteStream(savePath))
|
|
39
|
+
return true
|
|
40
|
+
} catch (err) {
|
|
41
|
+
logger && logger.error(`下载文件错误:${err}`)
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 递归创建目录
|
|
48
|
-
* @param dirname - 要创建的文件夹路径
|
|
49
|
-
*/
|
|
50
|
-
mkdir (dirname) {
|
|
51
|
-
if (fs.existsSync(dirname)) { return true }
|
|
52
|
-
/** 递归自调用 */
|
|
53
|
-
if (this.mkdir(path.dirname(dirname))) { fs.mkdirSync(dirname) }
|
|
54
|
-
return true
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 递归创建目录
|
|
48
|
+
* @param dirname - 要创建的文件夹路径
|
|
49
|
+
*/
|
|
50
|
+
mkdir (dirname) {
|
|
51
|
+
if (fs.existsSync(dirname)) { return true }
|
|
52
|
+
/** 递归自调用 */
|
|
53
|
+
if (this.mkdir(path.dirname(dirname))) { fs.mkdirSync(dirname) }
|
|
54
|
+
return true
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 标准化文件路径
|
|
59
|
-
* @param file - 相对路径
|
|
60
|
-
* @param isDir - 返回绝对路径
|
|
61
|
-
* @param isFile - 添加file://前缀
|
|
62
|
-
* @returns 标准化后的路径
|
|
63
|
-
*/
|
|
64
|
-
absPath (file, isDir = true, isFile = false) {
|
|
65
|
-
file = file.replace(/\\/g, '/')
|
|
66
|
-
if (file.startsWith('file://')) {
|
|
67
|
-
/** linux */
|
|
68
|
-
if (path.sep === '/') {
|
|
69
|
-
file = file.replace('file://', '')
|
|
70
|
-
} else {
|
|
71
|
-
/** windows */
|
|
72
|
-
file = file.replace(/^file:[/]{2,3}/g, '')
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
file = path.normalize(file)
|
|
76
|
-
/** 判断路径是否为绝对路径 否则转为绝对路径 */
|
|
77
|
-
if (isDir && !path.isAbsolute(file)) {
|
|
78
|
-
file = path.resolve(file)
|
|
79
|
-
}
|
|
80
|
-
if (isFile) { file = 'file://' + file }
|
|
81
|
-
file = file.replace(/\\/g, '/')
|
|
82
|
-
return file
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 标准化文件路径
|
|
59
|
+
* @param file - 相对路径
|
|
60
|
+
* @param isDir - 返回绝对路径
|
|
61
|
+
* @param isFile - 添加file://前缀
|
|
62
|
+
* @returns 标准化后的路径
|
|
63
|
+
*/
|
|
64
|
+
absPath (file, isDir = true, isFile = false) {
|
|
65
|
+
file = file.replace(/\\/g, '/')
|
|
66
|
+
if (file.startsWith('file://')) {
|
|
67
|
+
/** linux */
|
|
68
|
+
if (path.sep === '/') {
|
|
69
|
+
file = file.replace('file://', '')
|
|
70
|
+
} else {
|
|
71
|
+
/** windows */
|
|
72
|
+
file = file.replace(/^file:[/]{2,3}/g, '')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
file = path.normalize(file)
|
|
76
|
+
/** 判断路径是否为绝对路径 否则转为绝对路径 */
|
|
77
|
+
if (isDir && !path.isAbsolute(file)) {
|
|
78
|
+
file = path.resolve(file)
|
|
79
|
+
}
|
|
80
|
+
if (isFile) { file = 'file://' + file }
|
|
81
|
+
file = file.replace(/\\/g, '/')
|
|
82
|
+
return file
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 判断是否为文件夹
|
|
87
|
-
* @param path - 路径
|
|
88
|
-
* @returns 返回true为文件夹
|
|
89
|
-
*/
|
|
90
|
-
isDir (path) {
|
|
91
|
-
try {
|
|
92
|
-
return fs.statSync(path).isDirectory()
|
|
93
|
-
} catch {
|
|
94
|
-
return false
|
|
95
|
-
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 判断是否为文件夹
|
|
87
|
+
* @param path - 路径
|
|
88
|
+
* @returns 返回true为文件夹
|
|
89
|
+
*/
|
|
90
|
+
isDir (path) {
|
|
91
|
+
try {
|
|
92
|
+
return fs.statSync(path).isDirectory()
|
|
93
|
+
} catch {
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 判断是否为插件包
|
|
100
|
-
* @param path - 路径
|
|
101
|
-
* @returns 返回true为插件包
|
|
102
|
-
*/
|
|
103
|
-
isPlugin (path) {
|
|
104
|
-
return this.exists(`${path}/package.json`)
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 判断是否为插件包
|
|
100
|
+
* @param path - 路径
|
|
101
|
+
* @returns 返回true为插件包
|
|
102
|
+
*/
|
|
103
|
+
isPlugin (path) {
|
|
104
|
+
return this.exists(`${path}/package.json`)
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 判断路径是否存在
|
|
109
|
-
* @param path - 路径
|
|
110
|
-
* @returns 返回true为存在
|
|
111
|
-
*/
|
|
112
|
-
exists (path) {
|
|
113
|
-
try {
|
|
114
|
-
return fs.existsSync(path)
|
|
115
|
-
} catch {
|
|
116
|
-
return false
|
|
117
|
-
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 判断路径是否存在
|
|
109
|
+
* @param path - 路径
|
|
110
|
+
* @returns 返回true为存在
|
|
111
|
+
*/
|
|
112
|
+
exists (path) {
|
|
113
|
+
try {
|
|
114
|
+
return fs.existsSync(path)
|
|
115
|
+
} catch {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
118
|
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 根据文件后缀名从指定路径下读取符合要求的文件
|
|
122
|
-
* @param path - 路径
|
|
123
|
-
* @param ext - 后缀名、或后缀名列表
|
|
124
|
-
* @example common.readDir('./plugins', '.js')
|
|
125
|
-
* @example common.readDir('./plugins', ['.js', '.ts'])
|
|
126
|
-
*/
|
|
127
|
-
readDir (_path, ext) {
|
|
128
|
-
if (!this.isDir(_path)) { return null }
|
|
129
|
-
const files = fs.readdirSync(_path, { withFileTypes: true })
|
|
130
|
-
const list = []
|
|
131
|
-
if (!Array.isArray(ext)) { ext = [ext] }
|
|
132
|
-
// 排除文件夹 和不符合后缀名的文件
|
|
133
|
-
files.forEach(v => {
|
|
134
|
-
if (v.isDirectory()) { return }
|
|
135
|
-
if (ext.includes(path.extname(v.name))) { list.push(v.name) }
|
|
136
|
-
})
|
|
137
|
-
return list
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 根据文件后缀名从指定路径下读取符合要求的文件
|
|
122
|
+
* @param path - 路径
|
|
123
|
+
* @param ext - 后缀名、或后缀名列表
|
|
124
|
+
* @example common.readDir('./plugins', '.js')
|
|
125
|
+
* @example common.readDir('./plugins', ['.js', '.ts'])
|
|
126
|
+
*/
|
|
127
|
+
readDir (_path, ext) {
|
|
128
|
+
if (!this.isDir(_path)) { return null }
|
|
129
|
+
const files = fs.readdirSync(_path, { withFileTypes: true })
|
|
130
|
+
const list = []
|
|
131
|
+
if (!Array.isArray(ext)) { ext = [ext] }
|
|
132
|
+
// 排除文件夹 和不符合后缀名的文件
|
|
133
|
+
files.forEach(v => {
|
|
134
|
+
if (v.isDirectory()) { return }
|
|
135
|
+
if (ext.includes(path.extname(v.name))) { list.push(v.name) }
|
|
136
|
+
})
|
|
137
|
+
return list
|
|
138
138
|
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 传入 import.meta.url 自动构建../
|
|
142
|
-
* @param url - import.meta.url
|
|
143
|
-
*/
|
|
144
|
-
urlToPath (url) {
|
|
145
|
-
// 获取当前文件的绝对路径
|
|
146
|
-
const filePath = fileURLToPath(url)
|
|
147
|
-
// 获取当前文件所在目录的绝对路径
|
|
148
|
-
const dirPath = path.dirname(filePath)
|
|
149
|
-
// 获取项目根目录
|
|
150
|
-
const rootPath = process.cwd()
|
|
151
|
-
// 计算当前文件到项目根目录的相对路径
|
|
152
|
-
const relativePath = path.relative(dirPath, rootPath)
|
|
153
|
-
// 计算返回上一级目录的数量
|
|
154
|
-
const upLevelsCount = relativePath.split(path.sep).length
|
|
155
|
-
// 生成相应数量的 '../'
|
|
156
|
-
const upPath = lodash.repeat('../', upLevelsCount)
|
|
157
|
-
return upPath
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 传入 import.meta.url 自动构建../
|
|
142
|
+
* @param url - import.meta.url
|
|
143
|
+
*/
|
|
144
|
+
urlToPath (url) {
|
|
145
|
+
// 获取当前文件的绝对路径
|
|
146
|
+
const filePath = fileURLToPath(url)
|
|
147
|
+
// 获取当前文件所在目录的绝对路径
|
|
148
|
+
const dirPath = path.dirname(filePath)
|
|
149
|
+
// 获取项目根目录
|
|
150
|
+
const rootPath = process.cwd()
|
|
151
|
+
// 计算当前文件到项目根目录的相对路径
|
|
152
|
+
const relativePath = path.relative(dirPath, rootPath)
|
|
153
|
+
// 计算返回上一级目录的数量
|
|
154
|
+
const upLevelsCount = relativePath.split(path.sep).length
|
|
155
|
+
// 生成相应数量的 '../'
|
|
156
|
+
const upPath = lodash.repeat('../', upLevelsCount)
|
|
157
|
+
return upPath
|
|
158
158
|
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* 将文件转换为不带前缀的base64字符串
|
|
162
|
-
* @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
|
|
163
|
-
* @param - 附加数据
|
|
164
|
-
* @param - 为true时,http地址会直接返回,否则会下载文件并转换为base64字符串
|
|
165
|
-
* @returns 返回base64字符串
|
|
166
|
-
*/
|
|
167
|
-
async base64 (file, options = { http: false }) {
|
|
168
|
-
/** 先判断是否非字符串情况 */
|
|
169
|
-
if (typeof file !== 'string') {
|
|
170
|
-
/** buffer */
|
|
171
|
-
if (Buffer.isBuffer(file)) {
|
|
172
|
-
return file.toString('base64')
|
|
173
|
-
}
|
|
174
|
-
/** 可读流 */
|
|
175
|
-
if (file instanceof Readable) {
|
|
176
|
-
const data_1 = await this.stream(file)
|
|
177
|
-
return data_1.toString('base64')
|
|
178
|
-
}
|
|
179
|
-
/** 未知类型 */
|
|
180
|
-
throw new Error('未知类型')
|
|
181
|
-
}
|
|
182
|
-
/** base64:// */
|
|
183
|
-
if (file.startsWith('base64://')) {
|
|
184
|
-
return file.replace('base64://', '')
|
|
185
|
-
}
|
|
186
|
-
/** url */
|
|
187
|
-
if (file.startsWith('http://') || file.startsWith('https://')) {
|
|
188
|
-
if (options.http) { return file }
|
|
189
|
-
const response = await axios.get(file, { responseType: 'arraybuffer' })
|
|
190
|
-
return Buffer.from(response.data, 'binary').toString('base64')
|
|
191
|
-
}
|
|
192
|
-
/** file:/// */
|
|
193
|
-
if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
|
|
194
|
-
file = file.replace(/^file:\/\/\//, '')
|
|
195
|
-
return fs.readFileSync(file).toString('base64')
|
|
196
|
-
}
|
|
197
|
-
/** file:// */
|
|
198
|
-
if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
|
|
199
|
-
file = file.replace(/^file:\/\//, '')
|
|
200
|
-
return fs.readFileSync(file).toString('base64')
|
|
201
|
-
}
|
|
202
|
-
/** 无前缀base64:// */
|
|
203
|
-
const buffer = Buffer.from(file, 'base64').toString('base64') === file
|
|
204
|
-
if (buffer) { return file }
|
|
205
|
-
throw new Error('未知类型')
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 将文件转换为不带前缀的base64字符串
|
|
162
|
+
* @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
|
|
163
|
+
* @param - 附加数据
|
|
164
|
+
* @param - 为true时,http地址会直接返回,否则会下载文件并转换为base64字符串
|
|
165
|
+
* @returns 返回base64字符串
|
|
166
|
+
*/
|
|
167
|
+
async base64 (file, options = { http: false }) {
|
|
168
|
+
/** 先判断是否非字符串情况 */
|
|
169
|
+
if (typeof file !== 'string') {
|
|
170
|
+
/** buffer */
|
|
171
|
+
if (Buffer.isBuffer(file)) {
|
|
172
|
+
return file.toString('base64')
|
|
173
|
+
}
|
|
174
|
+
/** 可读流 */
|
|
175
|
+
if (file instanceof Readable) {
|
|
176
|
+
const data_1 = await this.stream(file)
|
|
177
|
+
return data_1.toString('base64')
|
|
178
|
+
}
|
|
179
|
+
/** 未知类型 */
|
|
180
|
+
throw new Error('未知类型')
|
|
181
|
+
}
|
|
182
|
+
/** base64:// */
|
|
183
|
+
if (file.startsWith('base64://')) {
|
|
184
|
+
return file.replace('base64://', '')
|
|
185
|
+
}
|
|
186
|
+
/** url */
|
|
187
|
+
if (file.startsWith('http://') || file.startsWith('https://')) {
|
|
188
|
+
if (options.http) { return file }
|
|
189
|
+
const response = await axios.get(file, { responseType: 'arraybuffer' })
|
|
190
|
+
return Buffer.from(response.data, 'binary').toString('base64')
|
|
191
|
+
}
|
|
192
|
+
/** file:/// */
|
|
193
|
+
if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
|
|
194
|
+
file = file.replace(/^file:\/\/\//, '')
|
|
195
|
+
return fs.readFileSync(file).toString('base64')
|
|
196
|
+
}
|
|
197
|
+
/** file:// */
|
|
198
|
+
if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
|
|
199
|
+
file = file.replace(/^file:\/\//, '')
|
|
200
|
+
return fs.readFileSync(file).toString('base64')
|
|
201
|
+
}
|
|
202
|
+
/** 无前缀base64:// */
|
|
203
|
+
const buffer = Buffer.from(file, 'base64').toString('base64') === file
|
|
204
|
+
if (buffer) { return file }
|
|
205
|
+
throw new Error('未知类型')
|
|
206
206
|
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 将数据流对象转换为Buffer对象
|
|
210
|
-
* @param {stream.Readable} stream - 要转换的数据流对象
|
|
211
|
-
* @returns {Promise<Buffer>} - 返回Buffer
|
|
212
|
-
*/
|
|
213
|
-
stream (stream) {
|
|
214
|
-
return new Promise((resolve, reject) => {
|
|
215
|
-
const chunks = []
|
|
216
|
-
stream.on('data', chunk => chunks.push(chunk))
|
|
217
|
-
stream.on('end', () => resolve(Buffer.concat(chunks)))
|
|
218
|
-
stream.on('error', error => reject(error))
|
|
219
|
-
})
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 将数据流对象转换为Buffer对象
|
|
210
|
+
* @param {stream.Readable} stream - 要转换的数据流对象
|
|
211
|
+
* @returns {Promise<Buffer>} - 返回Buffer
|
|
212
|
+
*/
|
|
213
|
+
stream (stream) {
|
|
214
|
+
return new Promise((resolve, reject) => {
|
|
215
|
+
const chunks = []
|
|
216
|
+
stream.on('data', chunk => chunks.push(chunk))
|
|
217
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)))
|
|
218
|
+
stream.on('error', error => reject(error))
|
|
219
|
+
})
|
|
220
220
|
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 将文件转换为Buffer对象
|
|
224
|
-
* @param {string|Buffer|http|stream.Readable} file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
|
|
225
|
-
* @param {object} options - 附加数据
|
|
226
|
-
* @param {boolean} options.http - 为true时,http地址会直接返回,否则会下载文件并转换为Buffer对象
|
|
227
|
-
* @returns {Promise<Buffer>} - 返回Buffer对象
|
|
228
|
-
*/
|
|
229
|
-
async buffer (file, options = { http: false }) {
|
|
230
|
-
if (typeof file !== 'string') {
|
|
231
|
-
if (Buffer.isBuffer(file)) { return file }
|
|
232
|
-
if (file instanceof Readable) { return this.stream(file) }
|
|
233
|
-
throw new Error('未知文件类型:' + file)
|
|
234
|
-
}
|
|
235
|
-
if (file.startsWith('base64://')) { return Buffer.from(file.replace('base64://', ''), 'base64') }
|
|
236
|
-
if (file.startsWith('http://') || file.startsWith('https://')) {
|
|
237
|
-
if (options.http) { return file }
|
|
238
|
-
const response = await axios.get(file, { responseType: 'arraybuffer' })
|
|
239
|
-
return Buffer.from(response.data)
|
|
240
|
-
}
|
|
241
|
-
/** file:/// */
|
|
242
|
-
if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
|
|
243
|
-
file = file.replace(/^file:\/\/\//, '')
|
|
244
|
-
return fs.readFileSync(file)
|
|
245
|
-
}
|
|
246
|
-
/** file:// */
|
|
247
|
-
if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
|
|
248
|
-
file = file.replace(/^file:\/\//, '')
|
|
249
|
-
return fs.readFileSync(file)
|
|
250
|
-
}
|
|
251
|
-
/** 无前缀base64:// */
|
|
252
|
-
const buffer = Buffer.from(file, 'base64')
|
|
253
|
-
if (buffer.toString('base64') === file) { return buffer }
|
|
254
|
-
throw new Error('未知类型')
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 将文件转换为Buffer对象
|
|
224
|
+
* @param {string|Buffer|http|stream.Readable} file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
|
|
225
|
+
* @param {object} options - 附加数据
|
|
226
|
+
* @param {boolean} options.http - 为true时,http地址会直接返回,否则会下载文件并转换为Buffer对象
|
|
227
|
+
* @returns {Promise<Buffer>} - 返回Buffer对象
|
|
228
|
+
*/
|
|
229
|
+
async buffer (file, options = { http: false }) {
|
|
230
|
+
if (typeof file !== 'string') {
|
|
231
|
+
if (Buffer.isBuffer(file)) { return file }
|
|
232
|
+
if (file instanceof Readable) { return this.stream(file) }
|
|
233
|
+
throw new Error('未知文件类型:' + file)
|
|
234
|
+
}
|
|
235
|
+
if (file.startsWith('base64://')) { return Buffer.from(file.replace('base64://', ''), 'base64') }
|
|
236
|
+
if (file.startsWith('http://') || file.startsWith('https://')) {
|
|
237
|
+
if (options.http) { return file }
|
|
238
|
+
const response = await axios.get(file, { responseType: 'arraybuffer' })
|
|
239
|
+
return Buffer.from(response.data)
|
|
240
|
+
}
|
|
241
|
+
/** file:/// */
|
|
242
|
+
if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
|
|
243
|
+
file = file.replace(/^file:\/\/\//, '')
|
|
244
|
+
return fs.readFileSync(file)
|
|
245
|
+
}
|
|
246
|
+
/** file:// */
|
|
247
|
+
if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
|
|
248
|
+
file = file.replace(/^file:\/\//, '')
|
|
249
|
+
return fs.readFileSync(file)
|
|
250
|
+
}
|
|
251
|
+
/** 无前缀base64:// */
|
|
252
|
+
const buffer = Buffer.from(file, 'base64')
|
|
253
|
+
if (buffer.toString('base64') === file) { return buffer }
|
|
254
|
+
throw new Error('未知类型')
|
|
255
255
|
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 标准化发送的消息内容
|
|
259
|
-
* @param elements - 消息内容
|
|
260
|
-
*/
|
|
261
|
-
makeMessage (elements) {
|
|
262
|
-
/** 将msg格式化为数组 */
|
|
263
|
-
if (!Array.isArray(elements)) { elements = [elements] }
|
|
264
|
-
const message = []
|
|
265
|
-
elements.forEach(v => {
|
|
266
|
-
if (typeof v === 'string') {
|
|
267
|
-
message.push(segment.text(v))
|
|
268
|
-
} else {
|
|
269
|
-
message.push(v)
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
return message
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 标准化发送的消息内容
|
|
259
|
+
* @param elements - 消息内容
|
|
260
|
+
*/
|
|
261
|
+
makeMessage (elements) {
|
|
262
|
+
/** 将msg格式化为数组 */
|
|
263
|
+
if (!Array.isArray(elements)) { elements = [elements] }
|
|
264
|
+
const message = []
|
|
265
|
+
elements.forEach(v => {
|
|
266
|
+
if (typeof v === 'string') {
|
|
267
|
+
message.push(segment.text(v))
|
|
268
|
+
} else {
|
|
269
|
+
message.push(v)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
return message
|
|
273
273
|
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* 制作简单转发,返回segment.node[]。仅简单包装node,也可以自己组装
|
|
277
|
-
* @param {Array<{object}> | object} elements
|
|
278
|
-
* @param fakeUin 用户id
|
|
279
|
-
* @param fakeNick 用户昵称
|
|
280
|
-
* @return {Array<KarinNodeElement>}
|
|
281
|
-
*/
|
|
282
|
-
makeForward (elements, fakeUin, fakeNick) {
|
|
283
|
-
if (!Array.isArray(elements)) { elements = [elements] }
|
|
284
|
-
return elements.map(element => {
|
|
285
|
-
const NodeElement = this.makeMessage(element)
|
|
286
|
-
return segment.node(fakeUin, fakeNick, NodeElement)
|
|
287
|
-
})
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 制作简单转发,返回segment.node[]。仅简单包装node,也可以自己组装
|
|
277
|
+
* @param {Array<{object}> | object} elements
|
|
278
|
+
* @param fakeUin 用户id
|
|
279
|
+
* @param fakeNick 用户昵称
|
|
280
|
+
* @return {Array<KarinNodeElement>}
|
|
281
|
+
*/
|
|
282
|
+
makeForward (elements, fakeUin, fakeNick) {
|
|
283
|
+
if (!Array.isArray(elements)) { elements = [elements] }
|
|
284
|
+
return elements.map(element => {
|
|
285
|
+
const NodeElement = this.makeMessage(element)
|
|
286
|
+
return segment.node(fakeUin, fakeNick, NodeElement)
|
|
287
|
+
})
|
|
288
288
|
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* 获取所有插件列表
|
|
292
|
-
* @param isDir - 返回绝对路径
|
|
293
|
-
* @param isPack - 屏蔽不带packageon的插件
|
|
294
|
-
*/
|
|
295
|
-
getPlugins (isDir = false, isPack = false) {
|
|
296
|
-
const dir = this.absPath('./plugins', isDir)
|
|
297
|
-
let list = fs.readdirSync(dir, { withFileTypes: true })
|
|
298
|
-
// 忽略非文件夹、非 karin-plugin-、karin-adapter- 开头的文件夹
|
|
299
|
-
list = list.filter(v => v.isDirectory() && v.name.startsWith('karin-plugin-'))
|
|
300
|
-
if (isPack) { list = list.filter(v => fs.existsSync(`${dir}/${v.name}/packageon`)) }
|
|
301
|
-
const arr = []
|
|
302
|
-
list.map(v => arr.push(v.name))
|
|
303
|
-
return arr
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 获取所有插件列表
|
|
292
|
+
* @param isDir - 返回绝对路径
|
|
293
|
+
* @param isPack - 屏蔽不带packageon的插件
|
|
294
|
+
*/
|
|
295
|
+
getPlugins (isDir = false, isPack = false) {
|
|
296
|
+
const dir = this.absPath('./plugins', isDir)
|
|
297
|
+
let list = fs.readdirSync(dir, { withFileTypes: true })
|
|
298
|
+
// 忽略非文件夹、非 karin-plugin-、karin-adapter- 开头的文件夹
|
|
299
|
+
list = list.filter(v => v.isDirectory() && v.name.startsWith('karin-plugin-'))
|
|
300
|
+
if (isPack) { list = list.filter(v => fs.existsSync(`${dir}/${v.name}/packageon`)) }
|
|
301
|
+
const arr = []
|
|
302
|
+
list.map(v => arr.push(v.name))
|
|
303
|
+
return arr
|
|
304
304
|
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 获取运行时间
|
|
308
|
-
*/
|
|
309
|
-
uptime () {
|
|
310
|
-
const time = process.uptime()
|
|
311
|
-
const day = Math.floor(time / 86400)
|
|
312
|
-
const hour = Math.floor((time % 86400) / 3600)
|
|
313
|
-
const min = Math.floor((time % 3600) / 60)
|
|
314
|
-
const sec = Math.floor(time % 60)
|
|
315
|
-
const parts = [day ? `${day}天` : '', hour ? `${hour}小时` : '', min ? `${min}分钟` : '', !day && sec ? `${sec}秒` : '']
|
|
316
|
-
return parts.filter(Boolean).join('')
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 获取运行时间
|
|
308
|
+
*/
|
|
309
|
+
uptime () {
|
|
310
|
+
const time = process.uptime()
|
|
311
|
+
const day = Math.floor(time / 86400)
|
|
312
|
+
const hour = Math.floor((time % 86400) / 3600)
|
|
313
|
+
const min = Math.floor((time % 3600) / 60)
|
|
314
|
+
const sec = Math.floor(time % 60)
|
|
315
|
+
const parts = [day ? `${day}天` : '', hour ? `${hour}小时` : '', min ? `${min}分钟` : '', !day && sec ? `${sec}秒` : '']
|
|
316
|
+
return parts.filter(Boolean).join('')
|
|
317
317
|
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* 构建消息体日志
|
|
321
|
-
* @param - 消息体
|
|
322
|
-
*/
|
|
323
|
-
makeMessageLog (message) {
|
|
324
|
-
const logs = []
|
|
325
|
-
for (const val of message) {
|
|
326
|
-
switch (val.type) {
|
|
327
|
-
case 'text':
|
|
328
|
-
logs.push(val.text)
|
|
329
|
-
break
|
|
330
|
-
case 'face':
|
|
331
|
-
logs.push(`[face:${val.id}]`)
|
|
332
|
-
break
|
|
333
|
-
case 'video':
|
|
334
|
-
case 'image':
|
|
335
|
-
case 'voice':
|
|
336
|
-
case 'record':
|
|
337
|
-
case 'file': {
|
|
338
|
-
let file
|
|
339
|
-
if (Buffer.isBuffer(val.file)) {
|
|
340
|
-
file = 'Buffer://...'
|
|
341
|
-
} else if (/^http|^file/.test(val.file)) {
|
|
342
|
-
file = val.file
|
|
343
|
-
} else {
|
|
344
|
-
file = 'base64://...'
|
|
345
|
-
}
|
|
346
|
-
logs.push(`[${val.type}:${file}]`)
|
|
347
|
-
break
|
|
348
|
-
}
|
|
349
|
-
case 'at':
|
|
350
|
-
logs.push(`[at:${val.uid}]`)
|
|
351
|
-
break
|
|
352
|
-
case 'rps':
|
|
353
|
-
logs.push(`[rps:${val.id}]`)
|
|
354
|
-
break
|
|
355
|
-
case 'dice':
|
|
356
|
-
logs.push(`[dice:${val.id}]`)
|
|
357
|
-
break
|
|
358
|
-
case 'poke':
|
|
359
|
-
logs.push(`[poke:${val.id}]`)
|
|
360
|
-
break
|
|
361
|
-
case 'share':
|
|
362
|
-
logs.push(`[share:${JSON.stringify(val)}]`)
|
|
363
|
-
break
|
|
364
|
-
case 'contact':
|
|
365
|
-
logs.push(`[contact:${JSON.stringify(val)}]`)
|
|
366
|
-
break
|
|
367
|
-
case 'location':
|
|
368
|
-
logs.push(`[location:${JSON.stringify(val)}]`)
|
|
369
|
-
break
|
|
370
|
-
case 'music':
|
|
371
|
-
logs.push(`[music:${JSON.stringify(val)}]`)
|
|
372
|
-
break
|
|
373
|
-
case 'reply':
|
|
374
|
-
logs.push(`[reply:${val.message_id}]`)
|
|
375
|
-
break
|
|
376
|
-
case 'forward':
|
|
377
|
-
logs.push(`[forward:${val.res_id}]`)
|
|
378
|
-
break
|
|
379
|
-
case 'xml':
|
|
380
|
-
logs.push(`[xml:${val.data}]`)
|
|
381
|
-
break
|
|
382
|
-
case 'json':
|
|
383
|
-
logs.push(`[json:${val.data}]`)
|
|
384
|
-
break
|
|
385
|
-
case 'markdown': {
|
|
386
|
-
if (val.content) {
|
|
387
|
-
logs.push(`[markdown:${val.content}]`)
|
|
388
|
-
} else {
|
|
389
|
-
const content = { id: val.custom_template_id }
|
|
390
|
-
for (const v of val.params) { content[v.key] = v.values[0] }
|
|
391
|
-
logs.push(`[markdown:${JSON.stringify(content)}]`)
|
|
392
|
-
}
|
|
393
|
-
break
|
|
394
|
-
}
|
|
395
|
-
case 'rows': {
|
|
396
|
-
const rows = []
|
|
397
|
-
for (const v of val.rows) { rows.push(JSON.stringify(v.data)) }
|
|
398
|
-
logs.push(`[rows:${JSON.stringify(rows)}]`)
|
|
399
|
-
break
|
|
400
|
-
}
|
|
401
|
-
case 'button':
|
|
402
|
-
logs.push(`[button:${JSON.stringify(val.data)}]`)
|
|
403
|
-
break
|
|
404
|
-
case 'long_msg':
|
|
405
|
-
logs.push(`[long_msg:${val.id}]`)
|
|
406
|
-
break
|
|
407
|
-
default:
|
|
408
|
-
logs.push(`[未知:${JSON.stringify(val)}]`)
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
return logs.join('')
|
|
412
|
-
}
|
|
413
|
-
})()
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 构建消息体日志
|
|
321
|
+
* @param - 消息体
|
|
322
|
+
*/
|
|
323
|
+
makeMessageLog (message) {
|
|
324
|
+
const logs = []
|
|
325
|
+
for (const val of message) {
|
|
326
|
+
switch (val.type) {
|
|
327
|
+
case 'text':
|
|
328
|
+
logs.push(val.text)
|
|
329
|
+
break
|
|
330
|
+
case 'face':
|
|
331
|
+
logs.push(`[face:${val.id}]`)
|
|
332
|
+
break
|
|
333
|
+
case 'video':
|
|
334
|
+
case 'image':
|
|
335
|
+
case 'voice':
|
|
336
|
+
case 'record':
|
|
337
|
+
case 'file': {
|
|
338
|
+
let file
|
|
339
|
+
if (Buffer.isBuffer(val.file)) {
|
|
340
|
+
file = 'Buffer://...'
|
|
341
|
+
} else if (/^http|^file/.test(val.file)) {
|
|
342
|
+
file = val.file
|
|
343
|
+
} else {
|
|
344
|
+
file = 'base64://...'
|
|
345
|
+
}
|
|
346
|
+
logs.push(`[${val.type}:${file}]`)
|
|
347
|
+
break
|
|
348
|
+
}
|
|
349
|
+
case 'at':
|
|
350
|
+
logs.push(`[at:${val.uid}]`)
|
|
351
|
+
break
|
|
352
|
+
case 'rps':
|
|
353
|
+
logs.push(`[rps:${val.id}]`)
|
|
354
|
+
break
|
|
355
|
+
case 'dice':
|
|
356
|
+
logs.push(`[dice:${val.id}]`)
|
|
357
|
+
break
|
|
358
|
+
case 'poke':
|
|
359
|
+
logs.push(`[poke:${val.id}]`)
|
|
360
|
+
break
|
|
361
|
+
case 'share':
|
|
362
|
+
logs.push(`[share:${JSON.stringify(val)}]`)
|
|
363
|
+
break
|
|
364
|
+
case 'contact':
|
|
365
|
+
logs.push(`[contact:${JSON.stringify(val)}]`)
|
|
366
|
+
break
|
|
367
|
+
case 'location':
|
|
368
|
+
logs.push(`[location:${JSON.stringify(val)}]`)
|
|
369
|
+
break
|
|
370
|
+
case 'music':
|
|
371
|
+
logs.push(`[music:${JSON.stringify(val)}]`)
|
|
372
|
+
break
|
|
373
|
+
case 'reply':
|
|
374
|
+
logs.push(`[reply:${val.message_id}]`)
|
|
375
|
+
break
|
|
376
|
+
case 'forward':
|
|
377
|
+
logs.push(`[forward:${val.res_id}]`)
|
|
378
|
+
break
|
|
379
|
+
case 'xml':
|
|
380
|
+
logs.push(`[xml:${val.data}]`)
|
|
381
|
+
break
|
|
382
|
+
case 'json':
|
|
383
|
+
logs.push(`[json:${val.data}]`)
|
|
384
|
+
break
|
|
385
|
+
case 'markdown': {
|
|
386
|
+
if (val.content) {
|
|
387
|
+
logs.push(`[markdown:${val.content}]`)
|
|
388
|
+
} else {
|
|
389
|
+
const content = { id: val.custom_template_id }
|
|
390
|
+
for (const v of val.params) { content[v.key] = v.values[0] }
|
|
391
|
+
logs.push(`[markdown:${JSON.stringify(content)}]`)
|
|
392
|
+
}
|
|
393
|
+
break
|
|
394
|
+
}
|
|
395
|
+
case 'rows': {
|
|
396
|
+
const rows = []
|
|
397
|
+
for (const v of val.rows) { rows.push(JSON.stringify(v.data)) }
|
|
398
|
+
logs.push(`[rows:${JSON.stringify(rows)}]`)
|
|
399
|
+
break
|
|
400
|
+
}
|
|
401
|
+
case 'button':
|
|
402
|
+
logs.push(`[button:${JSON.stringify(val.data)}]`)
|
|
403
|
+
break
|
|
404
|
+
case 'long_msg':
|
|
405
|
+
logs.push(`[long_msg:${val.id}]`)
|
|
406
|
+
break
|
|
407
|
+
default:
|
|
408
|
+
logs.push(`[未知:${JSON.stringify(val)}]`)
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return logs.join('')
|
|
412
|
+
}
|
|
413
|
+
})()
|