node-karin 0.8.14 → 0.10.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.
@@ -40,7 +40,8 @@ ffmpeg_path:
40
40
  ffprobe_path:
41
41
 
42
42
  # 主人列表 主权限
43
- master: []
43
+ master:
44
+ - input
44
45
 
45
46
  # 管理员列表 子权限
46
47
  admin: []
@@ -384,6 +384,7 @@ export declare class AdapterOneBot11 implements KarinAdapter {
384
384
  uid: string;
385
385
  uin: string;
386
386
  nick: string;
387
+ role: Role;
387
388
  age: number;
388
389
  unique_title: string;
389
390
  unique_title_expire_time: number;
@@ -406,6 +407,7 @@ export declare class AdapterOneBot11 implements KarinAdapter {
406
407
  uid: string;
407
408
  uin: string;
408
409
  nick: string;
410
+ role: Role;
409
411
  age: number;
410
412
  unique_title: string;
411
413
  unique_title_expire_time: number;
@@ -697,6 +697,7 @@ export class AdapterOneBot11 {
697
697
  uid: target_uid_or_uin,
698
698
  uin: target_uid_or_uin,
699
699
  nick: info.nickname,
700
+ role: info.role,
700
701
  age: info.age,
701
702
  unique_title: info.title,
702
703
  unique_title_expire_time: info.title_expire_time,
@@ -724,6 +725,7 @@ export class AdapterOneBot11 {
724
725
  uid: user_id,
725
726
  uin: user_id,
726
727
  nick: v.nickname,
728
+ role: v.role,
727
729
  age: v.age,
728
730
  unique_title: v.title,
729
731
  unique_title_expire_time: v.title_expire_time,
@@ -64,6 +64,7 @@ export const review = new (class Handler {
64
64
  /** 同时启用 */
65
65
  if (this.App.WhiteList.groups && this.App.BlackList.groups) {
66
66
  this.GroupEnable = e => {
67
+ if (!e.group_id) { return false }
67
68
  /** 白名单不为空 */
68
69
  if (Array.isArray(this.Config.WhiteList.groups) && this.Config.WhiteList.groups.length) {
69
70
  return this.Config.WhiteList.groups.includes(String(e.group_id))
@@ -2,7 +2,7 @@ import fs from 'fs'
2
2
  import WebSocket from 'ws'
3
3
  import { render } from './app.js'
4
4
  import { RenderBase } from './base.js'
5
- import { randomUUID } from 'crypto'
5
+ import { createHash, randomUUID } from 'crypto'
6
6
  import { listener } from '../core/index.js'
7
7
  import { common, logger } from '../utils/index.js'
8
8
  export class RenderClient extends RenderBase {
@@ -94,12 +94,20 @@ export class RenderClient extends RenderBase {
94
94
  const filePath = decodeURIComponent(data.params.file)
95
95
  logger.debug(`[渲染器:${this.id}][正向WS] 访问静态文件:${filePath}`)
96
96
  const file = fs.readFileSync('.' + filePath)
97
- const params = {
98
- echo: data.echo,
99
- action: 'static',
100
- status: 'ok',
101
- data: { file },
102
- }
97
+ const md5 = createHash('md5').update(file).digest('hex')
98
+ const params = data.params.md5?.includes(md5)
99
+ ? {
100
+ echo: data.echo,
101
+ action: 'static',
102
+ status: 'ok',
103
+ data: { verifiedMd5: md5 },
104
+ }
105
+ : {
106
+ echo: data.echo,
107
+ action: 'static',
108
+ status: 'ok',
109
+ data: { file },
110
+ }
103
111
  return this.ws.send(JSON.stringify(params))
104
112
  }
105
113
  /** 渲染结果 */
@@ -0,0 +1,30 @@
1
+ import WebSocket from 'ws';
2
+ import { RenderBase } from './base.js';
3
+ import { KarinRenderType, RenderResult } from '../types/render.js';
4
+ export declare class RenderClientEven extends RenderBase {
5
+ url: string;
6
+ type: string;
7
+ id: string;
8
+ index: number;
9
+ retry: number;
10
+ reg: RegExp;
11
+ ws: WebSocket;
12
+ constructor(url: string);
13
+ /**
14
+ * 初始化
15
+ */
16
+ start(): Promise<void>;
17
+ /**
18
+ * 创建连接
19
+ */
20
+ link(): Promise<void>;
21
+ /**
22
+ * 接受消息
23
+ */
24
+ message(str: string): Promise<void>;
25
+ /**
26
+ * 渲染标准方法
27
+ * @param options 渲染参数
28
+ */
29
+ render<T extends KarinRenderType>(options: T): Promise<RenderResult<T>>;
30
+ }
@@ -0,0 +1,163 @@
1
+ import fs from 'fs'
2
+ import axios from 'axios'
3
+ import WebSocket from 'ws'
4
+ import { render } from './app.js'
5
+ import { RenderBase } from './base.js'
6
+ import { createHash, randomUUID } from 'crypto'
7
+ import { listener } from '../core/index.js'
8
+ import { logger } from '../utils/index.js'
9
+ export class RenderClientEven extends RenderBase {
10
+ url
11
+ type
12
+ id
13
+ index
14
+ retry
15
+ reg
16
+ ws
17
+ constructor (url) {
18
+ super()
19
+ this.url = url
20
+ this.type = 'image'
21
+ this.id = 'puppeteer'
22
+ this.index = 0
23
+ this.retry = 0
24
+ this.reg = new RegExp(`(${process.cwd().replace(/\\/g, '\\\\')}|${process.cwd().replace(/\\/g, '/')})`, 'g')
25
+ }
26
+
27
+ /**
28
+ * 初始化
29
+ */
30
+ async start () {
31
+ try {
32
+ const response = await axios.head(this.url)
33
+ if (response.status === 200) {
34
+ logger.mark(`[渲染器:${this.id}][WebSocket] 注册渲染器:${logger.green(this.url)}`)
35
+ try {
36
+ this.index = render.app({ id: this.id, type: this.type, render: this.render.bind(this) })
37
+ } catch (error) {
38
+ logger.error(`[渲染器:${this.id}] 注册渲染器失败:`, error)
39
+ }
40
+ } else {
41
+ logger.error(`[渲染器:${this.id}] 注册渲染器失败:渲染器发生错误`)
42
+ }
43
+ } catch (error) {
44
+ logger.error(`[渲染器:${this.id}] 注册渲染器失败:`, error)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * 创建连接
50
+ */
51
+ async link () {
52
+ return new Promise((resolve, reject) => {
53
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
54
+ return resolve()
55
+ }
56
+ /** 连接ws */
57
+ this.ws = new WebSocket(this.url)
58
+ /** 建立连接 */
59
+ this.ws.on('open', () => {
60
+ logger.mark(`[渲染器:${this.id}][WebSocket] 建立连接:${logger.green(this.url)}`)
61
+ /** 监听消息 */
62
+ this.ws.on('message', data => this.message(data.toString()))
63
+ resolve()
64
+ })
65
+ /** 监听断开 */
66
+ this.ws.once('close', async () => {
67
+ /** 停止监听 */
68
+ this.ws.removeAllListeners()
69
+ })
70
+ /** 监听错误 */
71
+ this.ws.on('error', async (e) => {
72
+ this.ws.close()
73
+ })
74
+ })
75
+ }
76
+
77
+ /**
78
+ * 接受消息
79
+ */
80
+ async message (str) {
81
+ const data = JSON.parse(str)
82
+ switch (data.action) {
83
+ /** 静态文件 */
84
+ case 'static': {
85
+ const filePath = decodeURIComponent(data.params.file)
86
+ logger.debug(`[渲染器:${this.id}][正向WS] 访问静态文件:${filePath}`)
87
+ const file = fs.readFileSync('.' + filePath)
88
+ const md5 = createHash('md5').update(file).digest('hex')
89
+ const params = data.params.md5?.includes(md5)
90
+ ? {
91
+ echo: data.echo,
92
+ action: 'static',
93
+ status: 'ok',
94
+ data: { verifiedMd5: md5 },
95
+ }
96
+ : {
97
+ echo: data.echo,
98
+ action: 'static',
99
+ status: 'ok',
100
+ data: { file },
101
+ }
102
+ return this.ws.send(JSON.stringify(params))
103
+ }
104
+ /** 渲染结果 */
105
+ case 'renderRes': {
106
+ listener.emit(data.echo, data)
107
+ break
108
+ }
109
+ /** 超时 */
110
+ case 'timeout': {
111
+ logger.debug(`[渲染器:${this.id}][正向WS] 处理超时`)
112
+ break
113
+ }
114
+ /** 未知数据 */
115
+ default: {
116
+ logger.warn(`[渲染器:${this.id}] 收到未知数据:`, data)
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * 渲染标准方法
123
+ * @param options 渲染参数
124
+ */
125
+ async render (options) {
126
+ /** 渲染模板 */
127
+ let file = options.file
128
+ let action = 'renderHtml'
129
+ if (options.file.includes('http') || options.vue) {
130
+ action = 'render'
131
+ } else {
132
+ file = this.dealTpl(options)
133
+ /** 判断是本地karin-puppeteer还是远程 */
134
+ if (!/127\.0\.0\.1|localhost/.test(this.url)) {
135
+ file = fs.readFileSync(file, 'utf-8').replace(this.reg, '')
136
+ } else {
137
+ action = 'render'
138
+ file = 'file://' + file
139
+ }
140
+ }
141
+ if (!file) {
142
+ logger.error(`[渲染器:${this.id}:${this.index}] 渲染文件不存在:${options.file}`)
143
+ return ''
144
+ }
145
+ /** 编码 */
146
+ file = encodeURIComponent(file)
147
+ const data = options
148
+ const echo = randomUUID()
149
+ /** 移除掉模板参数 */
150
+ if (data.data) { delete data.data }
151
+ data.file = file
152
+ const req = JSON.stringify({ echo, action, data })
153
+ logger.debug(`[渲染器:${this.id}:${this.index}][正向WS] 请求:${this.url} \nhtml: ${options.file} \ndata: ${JSON.stringify(data)}`)
154
+ await this.link()
155
+ this.ws.send(req)
156
+ return new Promise((resolve, reject) => {
157
+ listener.once(echo, (data) => {
158
+ if (data.ok) { return resolve(data.data) }
159
+ reject(new Error(JSON.stringify(data)))
160
+ })
161
+ })
162
+ }
163
+ }
@@ -1,4 +1,4 @@
1
- import { Contact, KarinElement, Sender } from '../index.js';
1
+ import { Contact, KarinElement, Role, Sender } from '../index.js';
2
2
  /**
3
3
  * - 基本消息结构
4
4
  */
@@ -211,6 +211,10 @@ export interface GroupMemberInfo {
211
211
  * - 用户UIN
212
212
  */
213
213
  uin: string;
214
+ /**
215
+ * - 用户角色
216
+ */
217
+ role: Role;
214
218
  /**
215
219
  * - 用户昵称
216
220
  */
@@ -102,28 +102,29 @@ declare class Common {
102
102
  /**
103
103
  * 将文件转换为不带前缀的base64字符串
104
104
  * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
105
- * @param - 附加数据
106
- * @param - 为true时,http地址会直接返回,否则会下载文件并转换为base64字符串
105
+ * @param options - 选项 http为true时返回http地址
107
106
  * @returns 返回base64字符串
108
107
  */
109
- base64(file: any, options?: {
108
+ base64(file: string | Buffer | Readable, options?: {
110
109
  http: boolean;
111
110
  }): Promise<string>;
111
+ /**
112
+ * 将文件转换为Buffer对象 仅支持标准格式
113
+ * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
114
+ * @param options - 选项 http为true时返回http地址
115
+ * @returns - 返回Buffer对象
116
+ */
117
+ buffer<T extends {
118
+ http: boolean;
119
+ }>(file: string | Buffer | Readable, options?: T): Promise<T extends {
120
+ http: true;
121
+ } ? string : Buffer>;
112
122
  /**
113
123
  * 将数据流对象转换为Buffer对象
114
124
  * @param {stream.Readable} stream - 要转换的数据流对象
115
125
  * @returns {Promise<Buffer>} - 返回Buffer
116
126
  */
117
127
  stream(stream: Readable): Promise<Buffer>;
118
- /**
119
- * 将文件转换为Buffer对象
120
- * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
121
- * @param options - 选项
122
- * @returns - 返回Buffer对象
123
- */
124
- buffer(file: any, options?: {
125
- http: boolean;
126
- }): Promise<Buffer | Error | string>;
127
128
  /**
128
129
  * 标准化发送的消息内容
129
130
  * @param elements - 消息内容
@@ -33,12 +33,12 @@ class Common {
33
33
  async downFile (fileUrl, savePath, param = {}) {
34
34
  try {
35
35
  this.mkdir(path.dirname(savePath))
36
- logger && logger.info(`[下载文件] ${fileUrl}`)
36
+ logger.info(`[下载文件] ${fileUrl}`)
37
37
  const response = await axios.get(fileUrl, { ...param, responseType: 'stream' })
38
38
  await this.streamPipeline(response.data, fs.createWriteStream(savePath))
39
39
  return true
40
40
  } catch (err) {
41
- logger && logger.error(`下载文件错误:${err}`)
41
+ logger.error(`下载文件错误:${err}`)
42
42
  return false
43
43
  }
44
44
  }
@@ -242,49 +242,59 @@ class Common {
242
242
  /**
243
243
  * 将文件转换为不带前缀的base64字符串
244
244
  * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
245
- * @param - 附加数据
246
- * @param - 为true时,http地址会直接返回,否则会下载文件并转换为base64字符串
245
+ * @param options - 选项 http为true时返回http地址
247
246
  * @returns 返回base64字符串
248
247
  */
249
248
  async base64 (file, options = { http: false }) {
250
- /** 先判断是否非字符串情况 */
249
+ /** 非字符串 */
251
250
  if (typeof file !== 'string') {
252
- /** buffer */
253
- if (Buffer.isBuffer(file)) {
254
- return file.toString('base64')
255
- }
256
- /** 可读流 */
257
- if (file instanceof Readable) {
258
- const data_1 = await this.stream(file)
259
- return data_1.toString('base64')
260
- }
261
- /** 未知类型 */
262
- throw new Error('未知类型')
251
+ if (Buffer.isBuffer(file)) { return file.toString('base64') }
252
+ if (file instanceof Readable) { return (await this.stream(file)).toString('base64') }
253
+ return file
263
254
  }
264
255
  /** base64:// */
265
- if (file.startsWith('base64://')) {
266
- return file.replace('base64://', '')
267
- }
268
- /** url */
269
- if (file.startsWith('http://') || file.startsWith('https://')) {
256
+ if (file.startsWith('base64://')) { return file.replace('base64://', '') }
257
+ /** http */
258
+ if (file.startsWith('http')) {
270
259
  if (options.http) { return file }
271
260
  const response = await axios.get(file, { responseType: 'arraybuffer' })
272
261
  return Buffer.from(response.data, 'binary').toString('base64')
273
262
  }
274
- /** file:/// */
275
- if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
276
- file = file.replace(/^file:\/\/\//, '')
277
- return fs.readFileSync(file).toString('base64')
278
- }
279
263
  /** file:// */
280
- if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
281
- file = file.replace(/^file:\/\//, '')
282
- return fs.readFileSync(file).toString('base64')
264
+ const files = file.replace(/^file:\/\//, '')
265
+ if (fs.existsSync(files)) { return fs.readFileSync(file).toString('base64') }
266
+ /** 无前缀base64:// */
267
+ return Buffer.from(file, 'base64').toString('base64')
268
+ }
269
+
270
+ /**
271
+ * 将文件转换为Buffer对象 仅支持标准格式
272
+ * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
273
+ * @param options - 选项 http为true时返回http地址
274
+ * @returns - 返回Buffer对象
275
+ */
276
+ async buffer (file, options) {
277
+ /** 非字符串 */
278
+ if (typeof file !== 'string') {
279
+ if (Buffer.isBuffer(file)) { return file }
280
+ if (file instanceof Readable) { return await this.stream(file) }
281
+ return file
282
+ }
283
+ /** base64 */
284
+ if (file.startsWith('base64://')) {
285
+ return Buffer.from(file.replace('base64://', ''), 'base64')
286
+ }
287
+ /** http */
288
+ if (file.startsWith('http')) {
289
+ if (options?.http) { return file }
290
+ const response = await axios.get(file, { responseType: 'arraybuffer' })
291
+ return Buffer.from(response.data)
283
292
  }
293
+ /** file:// */
294
+ const files = file.replace(/^file:\/\//, '')
295
+ if (fs.existsSync(files)) { return fs.readFileSync(files) }
284
296
  /** 无前缀base64:// */
285
- const buffer = Buffer.from(file, 'base64').toString('base64') === file
286
- if (buffer) { return file }
287
- throw new Error('未知类型')
297
+ return Buffer.from(file, 'base64')
288
298
  }
289
299
 
290
300
  /**
@@ -301,40 +311,6 @@ class Common {
301
311
  })
302
312
  }
303
313
 
304
- /**
305
- * 将文件转换为Buffer对象
306
- * @param file - 文件路径或Buffer对象、可读流对象、http地址、base64://字符串
307
- * @param options - 选项
308
- * @returns - 返回Buffer对象
309
- */
310
- async buffer (file, options = { http: false }) {
311
- if (typeof file !== 'string') {
312
- if (Buffer.isBuffer(file)) { return file }
313
- if (file instanceof Readable) { return this.stream(file) }
314
- throw new Error('未知文件类型:' + file)
315
- }
316
- if (file.startsWith('base64://')) { return Buffer.from(file.replace('base64://', ''), 'base64') }
317
- if (file.startsWith('http://') || file.startsWith('https://')) {
318
- if (options.http) { return file }
319
- const response = await axios.get(file, { responseType: 'arraybuffer' })
320
- return Buffer.from(response.data)
321
- }
322
- /** file:/// */
323
- if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
324
- file = file.replace(/^file:\/\/\//, '')
325
- return fs.readFileSync(file)
326
- }
327
- /** file:// */
328
- if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
329
- file = file.replace(/^file:\/\//, '')
330
- return fs.readFileSync(file)
331
- }
332
- /** 无前缀base64:// */
333
- const buffer = Buffer.from(file, 'base64')
334
- if (buffer.toString('base64') === file) { return buffer }
335
- throw new Error('未知类型')
336
- }
337
-
338
314
  /**
339
315
  * 标准化发送的消息内容
340
316
  * @param elements - 消息内容
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-karin",
3
- "version": "0.8.14",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "基于 Kritor 进行开发的nodejs机器人框架",
6
6
  "homepage": "https://github.com/KarinJS/Karin",