mirai-js 2.8.3 → 2.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. package/1.mp3 +0 -0
  2. package/README.md +2 -0
  3. package/demo.ts +3 -6
  4. package/dist/browser/mirai-js.js +1 -1
  5. package/dist/node/borwserEntry.js +21 -0
  6. package/dist/node/core/uploadVoice.js +1 -1
  7. package/dist/node/lib/index.ts +0 -0
  8. package/index.ts +36 -3
  9. package/package.json +1 -1
  10. package/src/borwserEntry.js +11 -0
  11. package/src/core/uploadVoice.js +1 -1
  12. package/src/lib/index.ts +0 -0
  13. package/srcold/BaseType.d.ts +419 -0
  14. package/srcold/Bot.d.ts +567 -0
  15. package/srcold/Bot.js +1208 -0
  16. package/srcold/FileManager.js +270 -0
  17. package/srcold/Message.d.ts +66 -0
  18. package/srcold/Message.js +314 -0
  19. package/srcold/Middleware.d.ts +170 -0
  20. package/srcold/Middleware.js +657 -0
  21. package/srcold/Waiter.d.ts +13 -0
  22. package/srcold/Waiter.js +24 -0
  23. package/srcold/core/anno/deleteAnno.js +43 -0
  24. package/srcold/core/anno/getAnno.js +44 -0
  25. package/srcold/core/anno/publishAnno.js +44 -0
  26. package/srcold/core/auth.js +40 -0
  27. package/srcold/core/fs/deleteGroupFile.js +45 -0
  28. package/srcold/core/fs/getGroupFileInfo.js +46 -0
  29. package/srcold/core/fs/getGroupFileList.js +47 -0
  30. package/srcold/core/fs/makeGroupDir.js +45 -0
  31. package/srcold/core/fs/moveGroupFile.js +47 -0
  32. package/srcold/core/fs/renameGroupFile.js +44 -0
  33. package/srcold/core/fs/uploadGroupFIle.js +58 -0
  34. package/srcold/core/getFriendList.js +37 -0
  35. package/srcold/core/getGroupConfig.js +37 -0
  36. package/srcold/core/getGroupList.js +37 -0
  37. package/srcold/core/getMemberInfo.js +41 -0
  38. package/srcold/core/getMemberList.js +49 -0
  39. package/srcold/core/getSessionConfig.js +39 -0
  40. package/srcold/core/getUserProfile.js +40 -0
  41. package/srcold/core/messageFromId.js +40 -0
  42. package/srcold/core/mute.js +41 -0
  43. package/srcold/core/muteAll.js +39 -0
  44. package/srcold/core/quitGroup.js +40 -0
  45. package/srcold/core/recall.js +39 -0
  46. package/srcold/core/releaseSession.js +41 -0
  47. package/srcold/core/removeFriend.js +40 -0
  48. package/srcold/core/removeMember.js +42 -0
  49. package/srcold/core/responseBotInvitedJoinGroupRequest.js +46 -0
  50. package/srcold/core/responseFirendRequest.js +45 -0
  51. package/srcold/core/responseMemberJoinRequest.js +47 -0
  52. package/srcold/core/sendCommand.js +41 -0
  53. package/srcold/core/sendFriendMessage.js +43 -0
  54. package/srcold/core/sendGroupMessage.js +43 -0
  55. package/srcold/core/sendImageMessage.js +4 -0
  56. package/srcold/core/sendNudge.js +43 -0
  57. package/srcold/core/sendTempMessage.js +55 -0
  58. package/srcold/core/setEssence.js +44 -0
  59. package/srcold/core/setGroupConfig.js +58 -0
  60. package/srcold/core/setMemberAdmin.js +44 -0
  61. package/srcold/core/setMemberInfo.js +48 -0
  62. package/srcold/core/setSessionConfig.js +41 -0
  63. package/srcold/core/startListeningBrowser.js +62 -0
  64. package/srcold/core/startListeningNode.js +74 -0
  65. package/srcold/core/stopListeningBrowser.js +34 -0
  66. package/srcold/core/stopListeningNode.js +34 -0
  67. package/srcold/core/unmute.js +40 -0
  68. package/srcold/core/unmuteAll.js +39 -0
  69. package/srcold/core/uploadImage.js +55 -0
  70. package/srcold/core/uploadVoice.js +54 -0
  71. package/srcold/core/verify.js +41 -0
  72. package/srcold/index.d.ts +10 -0
  73. package/srcold/index.js +21 -0
  74. package/srcold/interface.js +20 -0
  75. package/srcold/polyfill/URL.js +5 -0
  76. package/srcold/polyfill/wsListener.js +6 -0
  77. package/srcold/typeHelpers.d.ts +2 -0
  78. package/srcold/util/errCode.js +23 -0
  79. package/srcold/util/errorHandler.js +24 -0
  80. package/srcold/util/getInvalidParamsString.js +12 -0
  81. package/srcold/util/isBrowserEnv.js +3 -0
  82. package/srcold/util/random.js +6 -0
  83. package/webpack.config.js +3 -2
package/srcold/Bot.js ADDED
@@ -0,0 +1,1208 @@
1
+ // 引入核心功能,前缀下划线时为了与方法名区别 (视觉上的区别)
2
+ const _releaseSession = require('./core/releaseSession');
3
+ const _verify = require('./core/auth');
4
+ const _bind = require('./core/verify');
5
+ const _sendCommand = require('./core/sendCommand');
6
+ const _sendFriendMessage = require('./core/sendFriendMessage');
7
+ const _sendGroupMessage = require('./core/sendGroupMessage');
8
+ const _sendTempMessage = require('./core/sendTempMessage');
9
+ const _sendNudge = require('./core/sendNudge');
10
+ const _getSessionConfig = require('./core/getSessionConfig');
11
+ const _setSessionConfig = require('./core/setSessionConfig');
12
+ const _uploadImage = require('./core/uploadImage');
13
+ const _uploadVoice = require('./core/uploadVoice');
14
+ const _getFriendList = require('./core/getFriendList');
15
+ const _getGroupList = require('./core/getGroupList');
16
+ const _getMemberList = require('./core/getMemberList');
17
+ const _getMemberInfo = require('./core/getMemberInfo');
18
+ const _getUserProfile = require('./core/getUserProfile');
19
+ const _setMemberInfo = require('./core/setMemberInfo');
20
+ const _setMemberAdmin = require('./core/setMemberAdmin');
21
+ const _getAnnoList = require('./core/anno/getAnno');
22
+ const _publishAnno = require('./core/anno/publishAnno');
23
+ const _deleteAnno = require('./core/anno/deleteAnno');
24
+ const _recall = require('./core/recall');
25
+ const _mute = require('./core/mute');
26
+ const _muteAll = require('./core/muteAll');
27
+ const _unmute = require('./core/unmute');
28
+ const _unmuteAll = require('./core/unmuteAll');
29
+ const _removeMember = require('./core/removeMember');
30
+ const _removeFriend = require('./core/removeFriend');
31
+ const _quitGroup = require('./core/quitGroup');
32
+ const _getGroupConfig = require('./core/getGroupConfig');
33
+ const _setGroupConfig = require('./core/setGroupConfig');
34
+ const _setEssence = require('./core/setEssence');
35
+ const _messageFromId = require('./core/messageFromId');
36
+ const { wsStartListening: _startListening, wsStopListening: _stopListening } = require('./polyfill/wsListener');
37
+
38
+ // 其他
39
+ const random = require('./util/random')(0, 2E16);
40
+ const getInvalidParamsString = require('./util/getInvalidParamsString');
41
+ const { Waiter } = require('./Waiter');
42
+ const { FileManager } = require('./FileManager');
43
+ const { errCodeEnum } = require('./util/errCode');
44
+ const { isBrowserEnv } = require('./util/isBrowserEnv');
45
+
46
+ const fs = isBrowserEnv() ? null : require('fs');
47
+ const { promisify } = isBrowserEnv() ? { promisify: null } : require('util');
48
+
49
+ // 扩展接口
50
+ const { MessageChainGetable, BotConfigGetable } = require('./interface');
51
+
52
+ /**
53
+ * @field config 包含 baseUrl verifyKey qq
54
+ * @field eventProcessorMap 事件处理器 map
55
+ * @field wsConnection 建立连接的 WebSocket 实例
56
+ * @field waiter 内部类单例,提供同步 io 机制
57
+ */
58
+ class Bot extends BotConfigGetable {
59
+ constructor() {
60
+ super();
61
+ this.waiter = new Waiter(this);
62
+ this.config = undefined;
63
+ this.eventProcessorMap = {};
64
+ this.wsConnection = undefined;
65
+ }
66
+
67
+ /**
68
+ * 实现 BotConfigGetable 接口
69
+ */
70
+ getBaseUrl() { return this.config.baseUrl; }
71
+ getQQ() { return this.config.qq; }
72
+ getVerifyKey() { return this.config.verifyKey; }
73
+ getSessionKey() { return this.config.sessionKey; }
74
+
75
+ /**
76
+ * @description 连接到 mirai-api-http,并开启一个会话,重复调用意为重建会话
77
+ * open 方法 1. 建立会话 2. 绑定 qq 3. 与服务端建立 WebSocket 连接
78
+ * @param {string} baseUrl 必选,mirai-api-http server 的地址
79
+ * @param {string} verifyKey 必选,mirai-api-http server 设置的 verifyKey
80
+ * @param {number} qq 必选,欲绑定的 qq 号,需要确保该 qq 号已在 mirai-console 登陆
81
+ * @param {boolean} singleMode 可选,mirai-api-http server 是否启用了 singleMode
82
+ * @returns {void}
83
+ */
84
+ async open({ baseUrl, qq, verifyKey, singleMode } = {}) {
85
+ // 若 config 存在,则认为该对象已经 open 过
86
+ // ,此处应该先令对象回到初始状态,然后重建会话
87
+ if (this.config) {
88
+ await this.close({ keepProcessor: true, keepConfig: true });
89
+ }
90
+
91
+ // 设置对象状态
92
+ // 若开发者重复调用 open,仅更新已提供的值
93
+ this.config = {
94
+ baseUrl: this.config?.baseUrl ?? baseUrl,
95
+ qq: this.config?.qq ?? qq,
96
+ verifyKey: this.config?.verifyKey ?? verifyKey,
97
+ sessionKey: this.config?.sessionKey ?? '',
98
+ };
99
+
100
+ // 事件处理器 map
101
+ // 如果重复调用 open 则保留事件处理器
102
+ this.eventProcessorMap = this.eventProcessorMap ?? {};
103
+
104
+ // 需要使用的参数
105
+ ({ baseUrl, qq, verifyKey } = this.config);
106
+
107
+ // 检查参数
108
+ if (!this.config.baseUrl || !this.config.qq || !this.config.verifyKey) {
109
+ throw new Error(`open 缺少必要的 ${getInvalidParamsString({
110
+ baseUrl, qq, verifyKey,
111
+ })} 参数`);
112
+ }
113
+
114
+ // 创建会话
115
+ const sessionKey = this.config.sessionKey = await _verify({ baseUrl, verifyKey });
116
+
117
+ // 绑定到一个 qq, 若开启了 singleMode 则需要跳过绑定
118
+ !singleMode && await _bind({ baseUrl, sessionKey, qq });
119
+
120
+ // 配置服务端 websocket 状态
121
+ // await _setSessionConfig({ baseUrl, sessionKey, enableWebsocket: true });
122
+
123
+ // 开始监听事件
124
+ await this.__wsListen();
125
+
126
+ }
127
+
128
+ /**
129
+ * @private
130
+ * @description 监听 ws 消息
131
+ */
132
+ async __wsListen() {
133
+ const { baseUrl, sessionKey, verifyKey } = this.config;
134
+ this.wsConnection = await _startListening({
135
+ baseUrl,
136
+ sessionKey,
137
+ verifyKey,
138
+ message: data => {
139
+ // 如果当前到达的事件拥有处理器,则依次调用所有该事件的处理器
140
+ if (data.type in this.eventProcessorMap) {
141
+ data.bot = this;
142
+ return Object.values(this.eventProcessorMap[data.type])
143
+ .forEach(processor => processor(data));
144
+ }
145
+ },
146
+ error: err => {
147
+ const type = 'error';
148
+ if (type in this.eventProcessorMap) {
149
+ err.bot = this;
150
+ return Object.values(this.eventProcessorMap[type])
151
+ .forEach(processor => processor(err));
152
+ }
153
+ try {
154
+ console.log(`ws error\n${JSON.stringify(err)}`);
155
+ } catch (error) { } // eslint-disable-line no-empty
156
+ },
157
+ close: (obj) => {
158
+ const type = 'close';
159
+ if (type in this.eventProcessorMap) {
160
+ obj.bot = this;
161
+ return Object.values(this.eventProcessorMap[type])
162
+ .forEach(processor => processor(obj));
163
+ }
164
+ try {
165
+ console.log(`ws close\n${JSON.stringify(obj)}`);
166
+ } catch (error) { }// eslint-disable-line no-empty
167
+ },
168
+ unexpectedResponse: (obj) => {
169
+ const type = 'unexpected-response';
170
+ if (type in this.eventProcessorMap) {
171
+ obj.bot = this;
172
+ return Object.values(this.eventProcessorMap[type])
173
+ .forEach(processor => processor(obj));
174
+ }
175
+ try {
176
+ console.log(`ws unexpectedResponse\n${JSON.stringify(obj)}`);
177
+ } catch (error) { }// eslint-disable-line no-empty
178
+ }
179
+ });
180
+ }
181
+
182
+
183
+ /**
184
+ * @description 关闭会话
185
+ * @param {boolean} keepProcessor 可选,是否保留事件处理器,默认值为 false,不保留
186
+ * @param {boolean} keepConfig 可选,是否保留 session baseUrl qq verifyKey,默认值为 false,不保留
187
+ * @returns {void}
188
+ */
189
+ async close({ keepProcessor = false, keepConfig = false } = {}) {
190
+ // 检查对象状态
191
+ if (!this.config) {
192
+ throw new Error('close 请先调用 open,建立一个会话');
193
+ }
194
+
195
+ // 需要使用的参数
196
+ const { baseUrl, sessionKey, qq } = this.config;
197
+
198
+ // 关闭 ws 连接
199
+ await _stopListening(this.wsConnection);
200
+
201
+ // 释放会话
202
+ await _releaseSession({ baseUrl, sessionKey, qq });
203
+
204
+ // 初始化对象状态
205
+ if (!keepProcessor) {
206
+ this.eventProcessorMap = {};
207
+ }
208
+ if (!keepConfig) {
209
+ this.config = undefined;
210
+ }
211
+ this.wsConnection = undefined;
212
+ }
213
+
214
+ /**
215
+ * ! messageChain 将在未来被移除
216
+ * @description 向 qq 好友 或 qq 群发送消息,若同时提供,则优先向好友发送消息
217
+ * @param {boolean} temp 可选,是否是临时会话,默认为 false
218
+ * @param {number} friend 二选一,好友 qq 号
219
+ * @param {number} group 二选一,群号
220
+ * @param {number} quote 可选,消息引用,使用发送时返回的 messageId
221
+ * @param {Message} message 必选,Message 实例或 MessageType 数组
222
+ * @returns {number} messageId
223
+ */
224
+ async sendMessage({ temp = false, friend, group, quote, message, messageChain }) {
225
+ // 检查对象状态
226
+ if (!this.config) {
227
+ throw new Error('sendMessage 请先调用 open,建立一个会话');
228
+ }
229
+
230
+ // 检查参数
231
+ if (!friend && !group | !message && !messageChain) {
232
+ throw new Error(`sendMessage 缺少必要的 ${getInvalidParamsString({
233
+ 'friend 或 group': friend || group,
234
+ 'message 或 messageChain': message || messageChain,
235
+ })} 参数`);
236
+ }
237
+
238
+ if (messageChain) {
239
+ console.log('warning: 现在 sendMessage 方法的 message 参数可以同时接收 Message 实例或 messageChain,messageChain 参数将在未来被移除');
240
+ }
241
+
242
+ // 需要使用的参数
243
+ const { baseUrl, sessionKey } = this.config;
244
+
245
+ // 处理 message,兼容存在 messageChain 参数的版本
246
+ messageChain = messageChain ?? message;
247
+
248
+ if (messageChain instanceof MessageChainGetable) {
249
+ messageChain = messageChain.getMessageChain();
250
+ } else if (typeof messageChain === 'string') {
251
+ messageChain = [{
252
+ type: 'Plain',
253
+ text: messageChain,
254
+ }];
255
+ }
256
+
257
+ // 根据 temp、friend、group 参数的情况依次调用
258
+ if (temp) {
259
+ // 临时会话的接口,好友和群是在一起的,在内部做了参数判断并抛出异常
260
+ // 而正常的好友和群的发送消息接口是分开的,所以在外面做了参数判断并抛出异常,格式相同
261
+ return await _sendTempMessage({
262
+ baseUrl, sessionKey, qq: friend, group, quote, messageChain
263
+ });
264
+ } else {
265
+ if (friend) {
266
+ return await _sendFriendMessage({
267
+ baseUrl, sessionKey, target: friend, quote, messageChain
268
+ });
269
+ } else if (group) {
270
+ return await _sendGroupMessage({
271
+ baseUrl, sessionKey, target: group, quote, messageChain
272
+ });
273
+ } else {
274
+ throw { message: 'sendGroupMessage 缺少必要的 qq 或 group 参数' };
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * @description 向好友或群成员发送戳一戳
281
+ * 如果提供了 group 参数则忽略 friend
282
+ * mirai-api-http-v1.10.1 feature
283
+ * @param {number} friend 二选一,好友 qq 号
284
+ * @param {number} group 二选一,群成员所在群
285
+ * @param {number} target 必选,目标 qq 号
286
+ */
287
+ async sendNudge({ friend, group, target }) {
288
+ // 检查对象状态
289
+ if (!this.config) {
290
+ throw new Error('sendNudge 请先调用 open,建立一个会话');
291
+ }
292
+
293
+ // 检查参数
294
+ if (!((group || friend) && target)) {
295
+ throw new Error(`sendNudge 缺少必要的 ${getInvalidParamsString({
296
+ 'group 或 friend': group || friend,
297
+ 'target': target,
298
+ })} 参数`);
299
+ }
300
+
301
+ // 需要使用的参数
302
+ const { baseUrl, sessionKey } = this.config;
303
+
304
+ // 发给群成员
305
+ if (group) {
306
+ await _sendNudge({
307
+ baseUrl, sessionKey,
308
+ target,
309
+ subject: group,
310
+ kind: 'Group',
311
+ });
312
+ }
313
+ // 发给好友
314
+ else if (friend) {
315
+ await _sendNudge({
316
+ baseUrl, sessionKey,
317
+ target,
318
+ subject: friend,
319
+ kind: 'Friend',
320
+ });
321
+ }
322
+ }
323
+
324
+ /**
325
+ * @description 添加一个事件处理器
326
+ * 框架维护的 WebSocket 实例会在 ws 的事件 message 下分发 Mirai http server 的消息
327
+ * 回调函数 (data) => void,data 的结构取决于消息类型,详见 mirai-api-http 的文档
328
+ * 而对于 ws 的其他事件 error, close, unexpectedResponse,其回调函数分别为
329
+ * - 'error': (err: Error) => void
330
+ * - 'close': (code: number, message: string) => void
331
+ * - 'unexpected-response': (request: http.ClientRequest, response: http.IncomingMessage) => void
332
+ * @param {string | string[]} eventType 必选,事件类型
333
+ * @param {function} callback 必选,回调函数
334
+ * @returns {number | string[]} 事件处理器的标识,用于移除该处理器
335
+ */
336
+ on(eventType, callback) {
337
+ // 检查对象状态
338
+ if (!this.config) {
339
+ throw new Error('on 请先调用 open,建立一个会话');
340
+ }
341
+
342
+ // 检查参数
343
+ if (!eventType || !callback) {
344
+ throw new Error(`on 缺少必要的 ${getInvalidParamsString({ eventType, callback })} 参数`);
345
+
346
+ }
347
+
348
+ // 适配 eventType 数组
349
+ if (Array.isArray(eventType)) {
350
+ return eventType.map(event => this.on(event, callback));
351
+ }
352
+
353
+ // 为没有任何事件处理器的事件生成一个空对象 (空对象 {},而不是 null)
354
+ if (!(eventType in this.eventProcessorMap)) {
355
+ this.eventProcessorMap[eventType] = {};
356
+ }
357
+
358
+ // 生成一个唯一的 handle,作为当前
359
+ // processor 的标识,用于移除该处理器
360
+ let handle = random();
361
+ while (handle in this.eventProcessorMap[eventType]) {
362
+ handle = random();
363
+ }
364
+
365
+ // processor
366
+ // 每个事件对应多个 processor,这些 processor 和
367
+ // handle 分别作为 value 和 key 包含在一个大对象中
368
+ let processor = callback;
369
+
370
+ // 添加事件处理器
371
+ this.eventProcessorMap[eventType][handle] = processor;
372
+ return handle;
373
+ }
374
+
375
+ /**
376
+ * @description 添加一个一次性事件处理器,回调一次后自动移除
377
+ * @param {string | string[]} eventType 必选,事件类型
378
+ * @param {function} callback 必选,回调函数
379
+ * @param {boolean} strict 可选,是否严格检测调用,由于消息可能会被中间件拦截
380
+ * 当为 true 时,只有开发者的处理器结束后才会移除该处理器
381
+ * 当为 false 时,即使消息被拦截,也会移除该处理器
382
+ * @returns {void}
383
+ */
384
+ one(eventType, callback, strict = false) {
385
+ // 检查对象状态
386
+ if (!this.config) {
387
+ throw new Error('one 请先调用 open,建立一个会话');
388
+ }
389
+
390
+ // 检查参数
391
+ if (!eventType || !callback) {
392
+ throw new Error(`one 缺少必要的 ${getInvalidParamsString({ eventType, callback })} 参数`);
393
+
394
+ }
395
+
396
+ // 适配 eventType 数组
397
+ if (Array.isArray(eventType)) {
398
+ eventType.map(event => this.one(event, callback));
399
+ return;
400
+ }
401
+
402
+ // 为没有任何事件处理器的事件生成一个空对象 (空对象 {},而不是 null)
403
+ if (!(eventType in this.eventProcessorMap)) {
404
+ this.eventProcessorMap[eventType] = {};
405
+ }
406
+
407
+ // 生成一个唯一的 handle,作为当前
408
+ // processor 的标识,用于移除该处理器
409
+ let handle = random();
410
+ while (handle in this.eventProcessorMap[eventType]) {
411
+ handle = random();
412
+ }
413
+
414
+ // processor
415
+ // 每个事件对应多个 processor,这些 processor 和 h
416
+ // andle 分别作为 value 和 key 包含在一个大对象中
417
+ const processor = async (data) => {
418
+ if (strict) {
419
+ // 严格检测回调
420
+ // 当开发者的处理器结束后才移除该处理器,这里等待异步回调
421
+ await callback(data);
422
+ if (handle in this.eventProcessorMap[eventType]) {
423
+ delete this.eventProcessorMap[eventType][handle];
424
+ }
425
+ } else {
426
+ // 不严格检测,直接移除处理器
427
+ // 从 field eventProcessorMap 中移除 handle 指定的事件处理器
428
+ if (handle in this.eventProcessorMap[eventType]) {
429
+ delete this.eventProcessorMap[eventType][handle];
430
+ }
431
+
432
+ // 调用开发者提供的回调
433
+ callback(data);
434
+ }
435
+ };
436
+
437
+ // 添加事件处理器
438
+ this.eventProcessorMap[eventType][handle] = processor;
439
+ }
440
+
441
+ /**
442
+ * @description 移除一个事件处理器
443
+ * @param {string} eventType 必选,事件类型
444
+ * @param {number | number[]} handle
445
+ * 可选,事件处理器标识(或数组),由 on 方法返回,未提供时将移除该事件下的所有处理器
446
+ * @returns {void}
447
+ */
448
+ off(eventType, handle) {
449
+ // 检查对象状态
450
+ if (!this.config) {
451
+ throw new Error('off 请先调用 open,建立一个会话');
452
+ }
453
+
454
+ // 检查参数
455
+ if (!eventType) {
456
+ throw new Error('off 缺少必要的 eventType 参数');
457
+ }
458
+
459
+
460
+ if (handle) {
461
+ // 从 field eventProcessorMap 中移除 handle 指定的事件处理器
462
+ if (handle.forEach) {
463
+ // 可迭代
464
+ handle.forEach(hd => {
465
+ if (hd in this.eventProcessorMap[eventType]) {
466
+ delete this.eventProcessorMap[eventType][hd];
467
+ }
468
+ });
469
+ } else {
470
+ // 不可迭代,认为是单个标识
471
+ if (handle in this.eventProcessorMap[eventType]) {
472
+ delete this.eventProcessorMap[eventType][handle];
473
+ }
474
+ }
475
+ } else {
476
+ // 未提供 handle,移除所有
477
+ if (eventType in this.eventProcessorMap) {
478
+ delete this.eventProcessorMap[eventType];
479
+ }
480
+ }
481
+
482
+ }
483
+
484
+ /**
485
+ * @description 移除所有事件处理器
486
+ * @param {string | string[]} eventType 可选,事件类型(或数组)
487
+ * @returns {void}
488
+ */
489
+ offAll(eventType) {
490
+ // 检查对象状态
491
+ if (!this.config) {
492
+ throw new Error('offAll 请先调用 open,建立一个会话');
493
+ }
494
+
495
+ if (eventType) {
496
+ // 提供了特定的 eventType 参数
497
+ if (eventType.forEach) {
498
+ // 可迭代
499
+ eventType.forEach(evtType => {
500
+ if (evtType in this.eventProcessorMap) {
501
+ delete this.eventProcessorMap[evtType];
502
+ }
503
+ });
504
+ } else {
505
+ // 不可迭代
506
+ if (eventType in this.eventProcessorMap) {
507
+ delete this.eventProcessorMap[eventType];
508
+ }
509
+ }
510
+ } else {
511
+ // 未提供参数,全部移除
512
+ this.eventProcessorMap = {};
513
+ }
514
+ }
515
+
516
+ /**
517
+ * @description 获取 config
518
+ * @returns {Object} 结构 { cacheSize, enableWebsocket }
519
+ */
520
+ async getSessionConfig() {
521
+ // 检查对象状态
522
+ if (!this.config) {
523
+ throw new Error('getConfig 请先调用 open,建立一个会话');
524
+ }
525
+
526
+ const { baseUrl, sessionKey } = this.config;
527
+ return await _getSessionConfig({ baseUrl, sessionKey });
528
+ }
529
+
530
+ /**
531
+ * @description 设置 config
532
+ * @param {number} cacheSize 可选,插件缓存大小
533
+ * @param {boolean} enableWebsocket 可选,websocket 状态
534
+ * @returns {void}
535
+ */
536
+ async setSessionConfig({ cacheSize, enableWebsocket }) {
537
+ // 检查对象状态
538
+ if (!this.config) {
539
+ throw new Error('setConfig 请先调用 open,建立一个会话');
540
+ }
541
+
542
+ const { baseUrl, sessionKey } = this.config;
543
+ await _setSessionConfig({ baseUrl, sessionKey, cacheSize, enableWebsocket });
544
+ }
545
+
546
+ /**
547
+ * @description 撤回由 messageId 确定的消息
548
+ * @param {number} messageId 欲撤回消息的 messageId
549
+ * @returns {void}
550
+ */
551
+ async recall({ messageId }) {
552
+ // 检查对象状态
553
+ if (!this.config) {
554
+ throw new Error('recall 请先调用 open,建立一个会话');
555
+ }
556
+
557
+ // 检查参数
558
+ if (!messageId) {
559
+ throw new Error('recall 缺少必要的 messageId 参数');
560
+ }
561
+
562
+ const { baseUrl, sessionKey } = this.config;
563
+ // 撤回消息
564
+ await _recall({ baseUrl, sessionKey, target: messageId });
565
+ }
566
+
567
+ /**
568
+ * FIXME: type 指定为 'friend' 或 'temp' 时发送的图片显示红色感叹号,无法加载,group 则正常
569
+ * @description 上传图片至服务器,返回指定 type 的 imageId,url,及 path
570
+ * @param {string} type 可选,"friend" 或 "group" 或 "temp",默认为 "group"
571
+ * @param {Buffer} img 二选一,图片二进制数据
572
+ * @param {string} filename 二选一,图片文件路径
573
+ * @returns {Object} 结构 { imageId, url, path }
574
+ */
575
+ async uploadImage({ type = 'group', img, filename }) {
576
+ if (isBrowserEnv()) { throw new Error('uploadImage 在浏览器环境下不可用'); }
577
+ // 检查对象状态
578
+ if (!this.config) {
579
+ throw new Error('uploadImage 请先调用 open,建立一个会话');
580
+ }
581
+
582
+ // 检查参数
583
+ if (isBrowserEnv() && filename) {
584
+ throw new Error('uploadImage 浏览器端不支持 filename 参数');
585
+ }
586
+ if (!img && !filename) {
587
+ throw new Error('uploadImage 缺少必要的 img 或 filename 参数');
588
+ }
589
+
590
+ // 若传入 filename 则统一转换为 Buffer
591
+ if (filename) {
592
+ // 优先使用 img 的原值
593
+ img = img ?? await promisify(fs.readFile)(filename);
594
+ }
595
+
596
+ const { baseUrl, sessionKey } = this.config;
597
+ return await _uploadImage({ baseUrl, sessionKey, type, img });
598
+ }
599
+
600
+ /**
601
+ * FIXME: 目前该功能返回的 voiceId 无法正常使用,无法
602
+ * 发送给好友,提示 message is empty,发到群里则是 1s 的无声语音
603
+ * @description 上传语音至服务器,返回 voiceId, url 及 path
604
+ * @param {string} type TODO: 目前仅支持 "group",请忽略该参数
605
+ * @param {Buffer} voice 二选一,语音二进制数据
606
+ * @param {string} filename 二选一,语音文件路径
607
+ * @returns {Object} 结构 { voiceId, url, path }
608
+ */
609
+ async uploadVoice({ type = 'group', voice, filename }) {
610
+ if (isBrowserEnv()) { throw new Error('uploadVoice 在浏览器环境下不可用'); }
611
+ // 检查对象状态
612
+ if (!this.config) {
613
+ throw new Error('uploadVoice 请先调用 open,建立一个会话');
614
+ }
615
+
616
+ // 检查参数
617
+ if (isBrowserEnv() && filename) {
618
+ throw new Error('uploadVoice 浏览器端不支持 filename 参数');
619
+ }
620
+ if (!voice && !filename) {
621
+ throw new Error('uploadVoice 缺少必要的 voice 或 filename 参数');
622
+ }
623
+
624
+ // 若传入 filename 则统一转换为 Buffer
625
+ if (filename) {
626
+ // 优先使用 img 的原值
627
+ voice = voice ?? await promisify(fs.readFile)(filename);
628
+ }
629
+
630
+ const { baseUrl, sessionKey } = this.config;
631
+ return await _uploadVoice({ baseUrl, sessionKey, type, voice });
632
+ }
633
+
634
+ /**
635
+ * @description 获取好友列表
636
+ * @returns {Object[]} 结构 array[...{ id, name, remark }]
637
+ */
638
+ async getFriendList() {
639
+ // 检查对象状态
640
+ if (!this.config) {
641
+ throw new Error('getFriendList 请先调用 open,建立一个会话');
642
+ }
643
+
644
+ const { baseUrl, sessionKey } = this.config;
645
+
646
+ // 获取列表
647
+ const friendList = await _getFriendList({ baseUrl, sessionKey });
648
+
649
+ // 这里希望所有列表应该具有相同的属性名
650
+ friendList.map((value) => {
651
+ value.name = value.nickname;
652
+ delete value.nickname;
653
+ });
654
+ return friendList;
655
+ }
656
+
657
+ /**
658
+ * @description 获取群列表
659
+ * @returns {Object[]} 结构 array[...{ id, name, permission }]
660
+ */
661
+ async getGroupList() {
662
+ // 检查对象状态
663
+ if (!this.config) {
664
+ throw new Error('getGroupList 请先调用 open,建立一个会话');
665
+ }
666
+
667
+ const { baseUrl, sessionKey } = this.config;
668
+ return await _getGroupList({ baseUrl, sessionKey });
669
+ }
670
+
671
+ /**
672
+ * @description 获取指定群的成员列表
673
+ * @param {number} group 必选,欲获取成员列表的群号
674
+ * @returns {Object[]} 结构 array[...{ id, name, permission }]
675
+ */
676
+ async getMemberList({ group }) {
677
+ // 检查对象状态
678
+ if (!this.config) {
679
+ throw new Error('getMemberList 请先调用 open,建立一个会话');
680
+ }
681
+
682
+ // 检查参数
683
+ if (!group) {
684
+ throw new Error('getMemberList 缺少必要的 group 参数');
685
+ }
686
+
687
+ // 获取列表
688
+ const { baseUrl, sessionKey } = this.config;
689
+ const memberList = await _getMemberList({ baseUrl, sessionKey, target: group });
690
+
691
+ // 这里希望所有列表应该具有相同的属性名
692
+ memberList.map((value) => {
693
+ value.name = value.memberName;
694
+ delete value.group;
695
+ delete value.memberName;
696
+ });
697
+ return memberList;
698
+ }
699
+
700
+ /**
701
+ * @description 获取群成员信息
702
+ * @param {number} group 必选,群成员所在群号
703
+ * @param {number} qq 必选,群成员的 qq 号
704
+ * @returns {Object}
705
+ */
706
+ async getMemberInfo({ group, qq }) {
707
+ // 检查对象状态
708
+ if (!this.config) {
709
+ throw new Error('getMemberInfo 请先调用 open,建立一个会话');
710
+ }
711
+
712
+ // 检查参数
713
+ if (!group || !qq) {
714
+ throw new Error(`getMemberInfo 缺少必要的 ${getInvalidParamsString({
715
+ group, qq
716
+ })} 参数`);
717
+ }
718
+
719
+ // 获取列表
720
+ const { baseUrl, sessionKey } = this.config;
721
+ const memberInfo = await _getMemberInfo({
722
+ baseUrl, sessionKey, target: group, memberId: qq
723
+ });
724
+
725
+ // 将 specialTitle 改为 title,在 setMemberInfo 也保持一致
726
+ memberInfo.title = memberInfo.specialTitle;
727
+ delete memberInfo.specialTitle;
728
+
729
+ return memberInfo;
730
+ }
731
+
732
+ /**
733
+ * @description 获取群成员信息
734
+ * @param {number} qq 必选,用户的 qq 号
735
+ * @returns {Object} 结构 { nickname, email, age, level, sign, sex }
736
+ */
737
+ async getUserProfile({ qq }) {
738
+ // 检查对象状态
739
+ if (!this.config) {
740
+ throw new Error('getUserProfile 请先调用 open,建立一个会话');
741
+ }
742
+
743
+ // 检查参数
744
+ if (!qq) {
745
+ throw new Error('getUserProfile 缺少必要的 qq 参数');
746
+ }
747
+
748
+ const { baseUrl, sessionKey } = this.config;
749
+ return await _getUserProfile({ baseUrl, sessionKey, target: qq });
750
+ }
751
+
752
+ /**
753
+ * @description 设置群成员信息
754
+ * @param {number} group 必选,群成员所在群号
755
+ * @param {number} qq 必选,群成员的 qq 号
756
+ * @param {string} name 可选,要设置的群名片
757
+ * @param {string} title 可选,要设置的群头衔
758
+ * @param {boolean} permission 可选,要设置的权限,
759
+ * 使用枚举值:Bot.Permission.Admin, Bot.Permission.Member
760
+ * @returns {void}
761
+ */
762
+ async setMemberInfo({ group, qq, name, title, permission }) {
763
+ // 检查对象状态
764
+ if (!this.config) {
765
+ throw new Error('setMemberInfo 请先调用 open,建立一个会话');
766
+ }
767
+
768
+ // 检查参数
769
+ if (!group || !qq) {
770
+ throw new Error(`setMemberInfo 缺少必要的 ${getInvalidParamsString({
771
+ group, qq
772
+ })} 参数`);
773
+ }
774
+ if (permission != undefined
775
+ && permission != Bot.groupPermission.ADMINISTRATOR
776
+ && permission != Bot.groupPermission.MEMBER) {
777
+ throw new Error('setMemberInfo admin 参数只能是 Bot.groupPermission.ADMINISTRATOR 或 Bot.groupPermission.Member');
778
+ }
779
+
780
+ // setMemberInfo
781
+ const { baseUrl, sessionKey } = this.config;
782
+ if (name != undefined || title != undefined) {
783
+ await _setMemberInfo({
784
+ baseUrl, sessionKey, target: group, memberId: qq,
785
+ name, specialTitle: title,
786
+ });
787
+ }
788
+
789
+ // setPermission
790
+ if (permission != undefined) {
791
+ await _setMemberAdmin({
792
+ baseUrl, sessionKey, target: group, memberId: qq,
793
+ assign: permission == Bot.groupPermission.ADMINISTRATOR ? true : false,
794
+ });
795
+ }
796
+ }
797
+
798
+ /**
799
+ * @description 获取群公告列表迭代器
800
+ * @param {number} group 必选,群号
801
+ * @returns 迭代器
802
+ */
803
+ async *getAnnoIter({ group }) {
804
+ // 检查对象状态
805
+ if (!this.config) {
806
+ throw new Error('getAnno 请先调用 open,建立一个会话');
807
+ }
808
+
809
+ // 检查参数
810
+ if (!group) {
811
+ throw new Error('getAnno 缺少必要的 group 参数');
812
+ }
813
+
814
+ // 获取列表
815
+ const { baseUrl, sessionKey } = this.config;
816
+ let offset = 0;
817
+ let annoList = await _getAnnoList({ baseUrl, sessionKey, id: group, offset, size: 10 });
818
+ while (annoList.length > 0) {
819
+ for (const anno of annoList) {
820
+ yield anno;
821
+ }
822
+
823
+ // 获取下一页
824
+ offset += 10;
825
+ annoList = await _getAnnoList({ baseUrl, sessionKey, id: group, offset, size: 10 });
826
+ }
827
+
828
+ return;
829
+ }
830
+
831
+ /**
832
+ * @description 发布群公告
833
+ * @param {number} group 必选,群号
834
+ * @param {string} content 必选,公告内容
835
+ * @returns {void}
836
+ */
837
+ async publishAnno({ group, content, pinned }) {
838
+ // 检查对象状态
839
+ if (!this.config) {
840
+ throw new Error('publishAllo 请先调用 open,建立一个会话');
841
+ }
842
+
843
+ // 检查参数
844
+ if (!group || !content) {
845
+ throw new Error(`publishAllo 缺少必要的 ${getInvalidParamsString({
846
+ group, content
847
+ })} 参数`);
848
+ }
849
+
850
+ // 发布公告
851
+ const { baseUrl, sessionKey } = this.config;
852
+ await _publishAnno({ baseUrl, sessionKey, target: group, content, pinned });
853
+ }
854
+
855
+ /**
856
+ * @description 删除群公告
857
+ * @param {number} group 必选,群号
858
+ * @param {string} fid 必选,公告 id
859
+ * @reaturns {void}
860
+ */
861
+ async deleteAnno({ group, fid }) {
862
+ // 检查对象状态
863
+ if (!this.config) {
864
+ throw new Error('deleteAnno 请先调用 open,建立一个会话');
865
+ }
866
+
867
+ // 检查参数
868
+ if (!group || !fid) {
869
+ throw new Error(`deleteAnno 缺少必要的 ${getInvalidParamsString({
870
+ group, fid
871
+ })} 参数`);
872
+ }
873
+
874
+ // 发布公告
875
+ const { baseUrl, sessionKey } = this.config;
876
+ await _deleteAnno({ baseUrl, sessionKey, id: group, fid });
877
+ }
878
+
879
+ /**
880
+ * @description 禁言群成员
881
+ * @param {number} group 必选,欲禁言成员所在群号
882
+ * @param {number} qq 必选,欲禁言成员 qq 号
883
+ * @param {number} time 必选,禁言时长,单位: s (秒)
884
+ * @returns {void}
885
+ */
886
+ async mute({ group, qq, time }) {
887
+ // 检查对象状态
888
+ if (!this.config) {
889
+ throw new Error('mute 请先调用 open,建立一个会话');
890
+ }
891
+
892
+ // 检查参数
893
+ if (!group || !qq || !time) {
894
+ throw new Error(`mute 缺少必要的 ${getInvalidParamsString({ group, qq, time })} 参数`);
895
+ }
896
+
897
+ const { baseUrl, sessionKey } = this.config;
898
+ // 禁言
899
+ await _mute({ baseUrl, sessionKey, target: group, memberId: qq, time });
900
+ }
901
+
902
+ /**
903
+ * @description 全员禁言
904
+ * @param {number} group 必选,欲全员禁言的群号
905
+ * @returns {void}
906
+ */
907
+ async muteAll({ group }) {
908
+ // 检查对象状态
909
+ if (!this.config) {
910
+ throw new Error('muteAll 请先调用 open,建立一个会话');
911
+ }
912
+
913
+ // 检查参数
914
+ if (!group) {
915
+ throw new Error('muteAll 缺少必要的 group 参数');
916
+ }
917
+
918
+ const { baseUrl, sessionKey } = this.config;
919
+ // 禁言
920
+ await _muteAll({ baseUrl, sessionKey, target: group });
921
+ }
922
+
923
+ /**
924
+ * @description 解除禁言
925
+ * @param {number} group 必选,欲解除禁言的成员所在群号
926
+ * @param {number} qq 必选,欲解除禁言的成员 qq 号
927
+ * @returns {void}
928
+ */
929
+ async unmute({ group, qq }) {
930
+ // 检查对象状态
931
+ if (!this.config) {
932
+ throw new Error('unmute 请先调用 open,建立一个会话');
933
+ }
934
+
935
+ // 检查参数
936
+ if (!group || !qq) {
937
+ throw new Error(`unmute 缺少必要的 ${getInvalidParamsString({ group, qq })} 参数`);
938
+ }
939
+
940
+ const { baseUrl, sessionKey } = this.config;
941
+ // 禁言
942
+ await _unmute({ baseUrl, sessionKey, target: group, memberId: qq });
943
+ }
944
+
945
+ /**
946
+ * @description 解除全员禁言
947
+ * @param {number} group 必选,欲解除全员禁言的群号
948
+ * @returns {void}
949
+ */
950
+ async unmuteAll({ group }) {
951
+ // 检查对象状态
952
+ if (!this.config) {
953
+ throw new Error('unmute 请先调用 open,建立一个会话');
954
+ }
955
+
956
+ // 检查参数
957
+ if (!group) {
958
+ throw new Error('unmute 缺少必要的 group 参数');
959
+ }
960
+
961
+ const { baseUrl, sessionKey } = this.config;
962
+ // 禁言
963
+ await _unmuteAll({ baseUrl, sessionKey, target: group });
964
+ }
965
+
966
+ /**
967
+ * @description 移除群成员
968
+ * @param {number} group 必选,欲移除的成员所在群号
969
+ * @param {number} qq 必选,欲移除的成员 qq 号
970
+ * @param {string} message 可选,默认为空串 "",信息
971
+ * @returns {void}
972
+ */
973
+ async removeMember({ group, qq, message = '' }) {
974
+ // 检查对象状态
975
+ if (!this.config) {
976
+ throw new Error('removeMember 请先调用 open,建立一个会话');
977
+ }
978
+
979
+ // 检查参数
980
+ if (!group || !qq) {
981
+ throw new Error(`removeMember 缺少必要的 ${getInvalidParamsString({ group, qq })} 参数`);
982
+ }
983
+
984
+ const { baseUrl, sessionKey } = this.config;
985
+ // 禁言
986
+ await _removeMember({ baseUrl, sessionKey, target: group, memberId: qq, msg: message });
987
+ }
988
+
989
+ /**
990
+ * @description 删除好友
991
+ * @param {*} qq 欲删除的好友 qq 号
992
+ * @returns {void}
993
+ */
994
+ async removeFriend({ qq }) {
995
+ // 检查对象状态
996
+ if (!this.config) {
997
+ throw new Error('removeFriend 请先调用 open,建立一个会话');
998
+ }
999
+
1000
+ // 检查参数
1001
+ if (!qq) {
1002
+ throw new Error('removeFriend 缺少必要的 qq 参数');
1003
+ }
1004
+
1005
+ const { baseUrl, sessionKey } = this.config;
1006
+
1007
+ // 删除好友
1008
+ await _removeFriend({ baseUrl, sessionKey, target: qq });
1009
+ }
1010
+
1011
+ /**
1012
+ * @description 移除群成员
1013
+ * @param {number} group 必选,欲移除的成员所在群号
1014
+ * @returns {void}
1015
+ */
1016
+ async quitGroup({ group }) {
1017
+ // 检查对象状态
1018
+ if (!this.config) {
1019
+ throw new Error('quitGroup 请先调用 open,建立一个会话');
1020
+ }
1021
+
1022
+ // 检查参数
1023
+ if (!group) {
1024
+ throw new Error('quitGroup 缺少必要的 group 参数');
1025
+ }
1026
+
1027
+ const { baseUrl, sessionKey } = this.config;
1028
+ // 禁言
1029
+ await _quitGroup({ baseUrl, sessionKey, target: group });
1030
+ }
1031
+
1032
+ /**
1033
+ * @description 获取群配置
1034
+ * @param {number} group 必选,群号
1035
+ * @returns {Object}
1036
+ */
1037
+ async getGroupConfig({ group }) {
1038
+ // 检查对象状态
1039
+ if (!this.config) {
1040
+ throw new Error('getGroupConfig 请先调用 open,建立一个会话');
1041
+ }
1042
+
1043
+ // 检查参数
1044
+ if (!group) {
1045
+ throw new Error('getGroupConfig 缺少必要的 group 参数');
1046
+ }
1047
+
1048
+ const { baseUrl, sessionKey } = this.config;
1049
+ return await _getGroupConfig({ baseUrl, sessionKey, target: group });
1050
+ }
1051
+
1052
+ /**
1053
+ * @description 设置群配置
1054
+ * @param {number} group 必选,群号
1055
+ * @param {string} name 可选,群名
1056
+ * @param {string} announcement 可选,群公告
1057
+ * @param {boolean} confessTalk 可选,是否开启坦白说
1058
+ * @param {boolean} allowMemberInvite 可选,是否允许群员邀请
1059
+ * @param {boolean} autoApprove 可选,是否开启自动审批入群
1060
+ * @param {boolean} anonymousChat 可选,是否允许匿名聊天
1061
+ * @returns {void}
1062
+ */
1063
+ async setGroupConfig({
1064
+ group,
1065
+ name, announcement, confessTalk, allowMemberInvite, autoApprove, anonymousChat,
1066
+ }) {
1067
+ // 检查对象状态
1068
+ if (!this.config) {
1069
+ throw new Error('setGroupConfig 请先调用 open,建立一个会话');
1070
+ }
1071
+
1072
+ // 检查参数
1073
+ if (!group) {
1074
+ throw new Error('setGroupConfig 缺少必要的 group 参数');
1075
+ }
1076
+
1077
+
1078
+ const { baseUrl, sessionKey } = this.config;
1079
+ await _setGroupConfig({
1080
+ baseUrl, sessionKey, target: group,
1081
+ name, announcement,
1082
+ confessTalk,
1083
+ allowMemberInvite,
1084
+ autoApprove,
1085
+ anonymousChat,
1086
+ });
1087
+ }
1088
+
1089
+ /**
1090
+ * @description 文件管理器的工厂方法
1091
+ * @param {number} group 群号
1092
+ * @returns {FileManager} 文件管理器实例
1093
+ */
1094
+ fileManager({ group }) {
1095
+ return new FileManager({ bot: this, group });
1096
+ }
1097
+
1098
+ /**
1099
+ * @description 设置群精华消息
1100
+ * @param {number} messageId 必选,消息 id
1101
+ * @returns {void}
1102
+ */
1103
+ async setEssence({ messageId }) {
1104
+ // 检查对象状态
1105
+ if (!this.config) {
1106
+ throw new Error('setEssence 请先调用 open,建立一个会话');
1107
+ }
1108
+
1109
+ // 检查参数
1110
+ if (!messageId) {
1111
+ throw new Error('setEssence 缺少必要的 messageId 参数');
1112
+ }
1113
+
1114
+ const { baseUrl, sessionKey } = this.config;
1115
+ await _setEssence({ baseUrl, sessionKey, target: messageId });
1116
+ }
1117
+
1118
+ /**
1119
+ * @description 向 mirai-console 发送指令
1120
+ * @param {string[]} command 必选,指令和参数
1121
+ * @returns {Object} 结构 { message },注意查看 message 的内容,已知的问题:
1122
+ * 'Login failed: Mirai 无法完成滑块验证. 使用协议 ANDROID_PHONE 强制要求滑块验证,
1123
+ * 请更换协议后重试. 另请参阅: https://github.com/project-mirai/mirai-login-solver-selenium'
1124
+ */
1125
+ async sendCommand({ command }) {
1126
+ // 检查对象状态
1127
+ if (!this.config) {
1128
+ throw new Error('setEssence 请先调用 open,建立一个会话');
1129
+ }
1130
+
1131
+ // 检查参数
1132
+ if (!command) {
1133
+ throw new Error(`sendCommand 缺少必要的 ${getInvalidParamsString({ command })} 参数`);
1134
+ }
1135
+
1136
+ const { Message } = require('./Message');
1137
+ const { baseUrl, sessionKey } = this.config;
1138
+ return await _sendCommand({
1139
+ baseUrl, sessionKey,
1140
+ command: command
1141
+ .map((v) => v?.toString() ?? '')
1142
+ .reduce((acc, cur) => acc.addText(cur), new Message)
1143
+ .messageChain
1144
+ });
1145
+ }
1146
+
1147
+ /**
1148
+ * @description 通过 messageId 获取消息
1149
+ * @param {number} target 可选, 目标 qq 号/群号, mah v2.6.0+ 新增该参数
1150
+ * @param {number} messageId 必选, 消息 id
1151
+ * @returns {Object} 结构 { type, messageChain, sender }
1152
+ */
1153
+ async getMessageById({ messageId, target }) {
1154
+ // 检查对象状态
1155
+ if (!this.config) {
1156
+ throw new Error('getMessageById 请先调用 open,建立一个会话');
1157
+ }
1158
+
1159
+ // 检查参数
1160
+ if (!messageId) {
1161
+ throw new Error('getMessageById 缺少必要的 messageId 参数');
1162
+ }
1163
+
1164
+ const { baseUrl, sessionKey } = this.config;
1165
+ return await _messageFromId({ baseUrl, sessionKey, target, messageId });
1166
+ }
1167
+
1168
+ /**
1169
+ * @description 检测该账号是否已经在 mirai-console 登录
1170
+ * @param {string} baseUrl 必选,mirai-api-http server 的地址
1171
+ * @param {string} verifyKey 必选,mirai-api-http server 设置的 verifyKey
1172
+ * @param {number} qq 必选,qq 号
1173
+ * @returns
1174
+ */
1175
+ static async isBotLoggedIn({ baseUrl, verifyKey, qq }) {
1176
+ // 检查参数
1177
+ if (!baseUrl || !verifyKey || !qq) {
1178
+ throw new Error(`isBotLoggedIn 缺少必要的 ${getInvalidParamsString({ baseUrl, verifyKey, qq })} 参数`);
1179
+ }
1180
+
1181
+ const sessionKey = await _verify({ baseUrl, verifyKey });
1182
+ const { code } = await _bind({ baseUrl, sessionKey, qq, throwable: false });
1183
+
1184
+ if (code == errCodeEnum.BOT_NOT_FOUND) {
1185
+ return false;
1186
+ } else {
1187
+ _releaseSession({ baseUrl, sessionKey, qq });
1188
+ return true;
1189
+ }
1190
+ }
1191
+
1192
+ }
1193
+
1194
+
1195
+ // 静态属性: 群成员的权限
1196
+ Bot.groupPermission = {
1197
+ get OWNER() {
1198
+ return 'OWNER';
1199
+ },
1200
+ get ADMINISTRATOR() {
1201
+ return 'ADMINISTRATOR';
1202
+ },
1203
+ get MEMBER() {
1204
+ return 'MEMBER';
1205
+ },
1206
+ };
1207
+
1208
+ module.exports = { Bot };