@alemonjs/qq-bot 2.1.33 → 2.1.35

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.
Files changed (132) hide show
  1. package/README.md +46 -8
  2. package/dist/assets/index.css +1 -1
  3. package/dist/assets/index.js +12 -5
  4. package/dist/index.html +1 -1
  5. package/lib/config.d.ts +44 -19
  6. package/lib/config.js +39 -35
  7. package/lib/desktop.d.ts +4 -1
  8. package/lib/desktop.js +53 -57
  9. package/lib/format.d.ts +26 -5
  10. package/lib/format.js +84 -101
  11. package/lib/hook.d.ts +82 -65
  12. package/lib/hook.js +35 -32
  13. package/lib/index.d.ts +6 -4
  14. package/lib/index.js +13 -16
  15. package/lib/index.webhook.d.ts +4 -1
  16. package/lib/index.webhook.js +12 -14
  17. package/lib/index.websoket.d.ts +6 -3
  18. package/lib/index.websoket.js +131 -41
  19. package/lib/message/AT_MESSAGE_CREATE.d.ts +36 -33
  20. package/lib/message/AT_MESSAGE_CREATE.js +1 -1
  21. package/lib/message/CHANNEL_CREATE.d.ts +17 -14
  22. package/lib/message/CHANNEL_CREATE.js +1 -1
  23. package/lib/message/CHANNEL_DELETE.d.ts +22 -14
  24. package/lib/message/CHANNEL_DELETE.js +1 -1
  25. package/lib/message/CHANNEL_UPDATE.d.ts +22 -14
  26. package/lib/message/CHANNEL_UPDATE.js +1 -1
  27. package/lib/message/DIRECT_MESSAGE_CREATE.d.ts +35 -27
  28. package/lib/message/DIRECT_MESSAGE_CREATE.js +1 -1
  29. package/lib/message/DIRECT_MESSAGE_DELETE.d.ts +23 -15
  30. package/lib/message/DIRECT_MESSAGE_DELETE.js +1 -1
  31. package/lib/message/ERROR.d.ts +4 -1
  32. package/lib/message/ERROR.js +1 -1
  33. package/lib/message/GUILD_CREATE.d.ts +22 -14
  34. package/lib/message/GUILD_CREATE.js +1 -1
  35. package/lib/message/GUILD_DELETE.d.ts +22 -14
  36. package/lib/message/GUILD_DELETE.js +1 -1
  37. package/lib/message/GUILD_MEMBER_ADD.d.ts +21 -13
  38. package/lib/message/GUILD_MEMBER_ADD.js +1 -1
  39. package/lib/message/GUILD_MEMBER_REMOVE.d.ts +21 -13
  40. package/lib/message/GUILD_MEMBER_REMOVE.js +1 -1
  41. package/lib/message/GUILD_MEMBER_UPDATE.d.ts +21 -13
  42. package/lib/message/GUILD_MEMBER_UPDATE.js +1 -1
  43. package/lib/message/GUILD_UPDATE.d.ts +22 -14
  44. package/lib/message/GUILD_UPDATE.js +1 -1
  45. package/lib/message/INTERACTION_CREATE.d.ts +52 -44
  46. package/lib/message/INTERACTION_CREATE.js +1 -1
  47. package/lib/message/MESSAGE_CREATE.d.ts +12 -1
  48. package/lib/message/MESSAGE_CREATE.js +1 -1
  49. package/lib/message/MESSAGE_DELETE.d.ts +12 -1
  50. package/lib/message/MESSAGE_DELETE.js +1 -1
  51. package/lib/message/MESSAGE_REACTION_ADD.d.ts +15 -12
  52. package/lib/message/MESSAGE_REACTION_ADD.js +1 -1
  53. package/lib/message/MESSAGE_REACTION_REMOVE.d.ts +15 -12
  54. package/lib/message/MESSAGE_REACTION_REMOVE.js +1 -1
  55. package/lib/message/PUBLIC_MESSAGE_DELETE.d.ts +20 -13
  56. package/lib/message/PUBLIC_MESSAGE_DELETE.js +1 -1
  57. package/lib/message/READY.d.ts +11 -5
  58. package/lib/message/READY.js +1 -1
  59. package/lib/message/group/C2C_MESSAGE_CREATE.d.ts +18 -15
  60. package/lib/message/group/C2C_MESSAGE_CREATE.js +1 -1
  61. package/lib/message/group/C2C_MSG_RECEIVE.d.ts +13 -3
  62. package/lib/message/group/C2C_MSG_RECEIVE.js +1 -1
  63. package/lib/message/group/C2C_MSG_REJECT.d.ts +9 -3
  64. package/lib/message/group/C2C_MSG_REJECT.js +1 -1
  65. package/lib/message/group/FRIEND_ADD.d.ts +13 -3
  66. package/lib/message/group/FRIEND_ADD.js +1 -1
  67. package/lib/message/group/FRIEND_DEL.d.ts +9 -3
  68. package/lib/message/group/FRIEND_DEL.js +1 -1
  69. package/lib/message/group/GROUP_ADD_ROBOT.d.ts +23 -4
  70. package/lib/message/group/GROUP_ADD_ROBOT.js +1 -1
  71. package/lib/message/group/GROUP_AT_MESSAGE_CREATE.d.ts +38 -32
  72. package/lib/message/group/GROUP_AT_MESSAGE_CREATE.js +1 -1
  73. package/lib/message/group/GROUP_DEL_ROBOT.d.ts +19 -4
  74. package/lib/message/group/GROUP_DEL_ROBOT.js +1 -1
  75. package/lib/message/group/GROUP_JOIN_REQUEST.d.ts +83 -22
  76. package/lib/message/group/GROUP_JOIN_REQUEST.js +1 -1
  77. package/lib/message/group/GROUP_MEMBER_ADD.d.ts +8 -5
  78. package/lib/message/group/GROUP_MEMBER_ADD.js +1 -1
  79. package/lib/message/group/GROUP_MEMBER_REMOVE.d.ts +8 -5
  80. package/lib/message/group/GROUP_MEMBER_REMOVE.js +1 -1
  81. package/lib/message/group/GROUP_MESSAGE_CREATE.d.ts +38 -32
  82. package/lib/message/group/GROUP_MESSAGE_CREATE.js +1 -1
  83. package/lib/message/group/GROUP_MSG_RECEIVE.d.ts +14 -4
  84. package/lib/message/group/GROUP_MSG_RECEIVE.js +1 -1
  85. package/lib/message/group/GROUP_MSG_REJECT.d.ts +10 -4
  86. package/lib/message/group/GROUP_MSG_REJECT.js +1 -1
  87. package/lib/message/group/MESSAGE_AUDIT_PASS.d.ts +12 -6
  88. package/lib/message/group/MESSAGE_AUDIT_PASS.js +1 -1
  89. package/lib/message/group/MESSAGE_AUDIT_REJECT.d.ts +12 -6
  90. package/lib/message/group/MESSAGE_AUDIT_REJECT.js +1 -1
  91. package/lib/register.d.ts +12 -9
  92. package/lib/register.js +1246 -1404
  93. package/lib/sdk/api.d.ts +937 -306
  94. package/lib/sdk/api.js +1481 -893
  95. package/lib/sdk/client.webhook.d.ts +25 -8
  96. package/lib/sdk/client.webhook.js +185 -189
  97. package/lib/sdk/client.websoket.d.ts +31 -9
  98. package/lib/sdk/client.websoket.js +369 -392
  99. package/lib/sdk/config.d.ts +6 -2
  100. package/lib/sdk/config.js +9 -6
  101. package/lib/sdk/instance.d.ts +11 -3
  102. package/lib/sdk/instance.js +72 -84
  103. package/lib/sdk/intents.d.ts +89 -3
  104. package/lib/sdk/intents.js +116 -33
  105. package/lib/sdk/message.d.ts +7 -4
  106. package/lib/sdk/message.group.d.ts +38 -35
  107. package/lib/sdk/message.group.js +1 -1
  108. package/lib/sdk/message.guild.d.ts +40 -37
  109. package/lib/sdk/message.guild.js +1 -1
  110. package/lib/sdk/message.js +1 -1
  111. package/lib/sdk/message.public.d.ts +8 -5
  112. package/lib/sdk/message.public.js +1 -1
  113. package/lib/sdk/qr-auth.d.ts +55 -0
  114. package/lib/sdk/qr-auth.js +166 -0
  115. package/lib/sdk/registry.d.ts +34 -24
  116. package/lib/sdk/registry.js +134 -114
  117. package/lib/sdk/session.d.ts +19 -15
  118. package/lib/sdk/session.js +51 -54
  119. package/lib/sdk/typing.d.ts +410 -184
  120. package/lib/sdk/typing.js +1 -1
  121. package/lib/sdk/webhook.secret.d.ts +33 -13
  122. package/lib/sdk/webhook.secret.js +50 -36
  123. package/lib/sends.d.ts +42 -25
  124. package/lib/sends.js +519 -541
  125. package/lib/types.d.ts +52 -41
  126. package/lib/types.js +1 -1
  127. package/lib/upload.d.ts +47 -18
  128. package/lib/upload.js +80 -51
  129. package/lib/utils.d.ts +9 -1
  130. package/lib/utils.js +10 -5
  131. package/package.json +10 -7
  132. package/LICENSE +0 -21
package/lib/sdk/api.js CHANGED
@@ -1,895 +1,1483 @@
1
- import axios from 'axios';
2
- import { QQBotConfig } from './config.js';
3
- import FormData from 'form-data';
4
- import { createPicFrom } from 'alemonjs/utils';
5
- import { createAxiosInstance } from './instance.js';
6
- import { randomUUID, createHash } from 'node:crypto';
7
- import { createReadStream } from 'node:fs';
8
- import { fileDataToBuffer, CHUNK_THRESHOLD, chunkedUpload } from '../upload.js';
1
+ import { CHUNK_THRESHOLD, chunkedUpload, fileDataToBuffer } from "../upload.js";
2
+ import { QQBotConfig } from "./config.js";
3
+ import { createAxiosInstance } from "./instance.js";
4
+ import axios from "axios";
5
+ import { createHash, randomUUID } from "node:crypto";
6
+ import { createReadStream } from "node:fs";
7
+ import FormData from "form-data";
8
+ import { createPicFrom } from "alemonjs/utils";
9
9
 
10
- const BOTS_API_RUL = 'https://api.bot.qq.com';
11
- const API_URL_SANDBOX = 'https://sandbox.api.sgroup.qq.com';
12
- const API_URL = 'https://api.bot.qq.com';
13
- class QQBotAPI {
14
- config;
15
- #msgMap = new Map();
16
- #streams = new Map();
17
- #onStreamClosed;
18
- #connectionStatus = {
19
- state: 'idle',
20
- transport: null,
21
- reconnectAttempts: 0,
22
- heartbeatAcknowledged: false,
23
- resumed: false
24
- };
25
- constructor(options = {}) {
26
- this.config = new QQBotConfig(options);
27
- }
28
- getConnectionStatus() {
29
- return { ...this.#connectionStatus };
30
- }
31
- updateConnectionStatus(patch) {
32
- this.#connectionStatus = { ...this.#connectionStatus, ...patch };
33
- }
34
- setStreamLifecycleListener(listener) {
35
- this.#onStreamClosed = listener;
36
- }
37
- sendTyping(params) {
38
- if (!params.userOpenId)
39
- throw new Error('userOpenId is required');
40
- return this.groupService({
41
- url: `/v2/users/${params.userOpenId}/input_notify`,
42
- method: 'post',
43
- data: {
44
- ...(params.msgId && { msg_id: params.msgId }),
45
- input_second: Math.max(1, Math.min(Number(params.durationSec) || 30, 60))
46
- }
47
- });
48
- }
49
- streamOpen(params) {
50
- if (!params.userOpenId || !params.msgId)
51
- throw new Error('C2C streaming requires userOpenId and msgId');
52
- const streamId = randomUUID();
53
- const stream = {
54
- userOpenId: params.userOpenId,
55
- msgId: params.msgId,
56
- eventId: params.eventId || params.msgId,
57
- msgSeq: this.getMessageSeq(params.msgId),
58
- index: 0,
59
- lastSentAt: 0,
60
- lastSentText: ''
61
- };
62
- stream.timer = setTimeout(() => this.streamCancel(streamId), 5 * 60 * 1000);
63
- this.#streams.set(streamId, stream);
64
- return { streamId };
65
- }
66
- async streamUpdate(streamId, fullText) {
67
- const stream = this.#streams.get(streamId);
68
- if (!stream)
69
- throw new Error('Unknown or expired streamId');
70
- stream.latest = fullText;
71
- if (!stream.sendTask) {
72
- stream.sendTask = (async () => {
73
- let result;
74
- while (stream.latest !== undefined && this.#streams.get(streamId) === stream) {
75
- const content = stream.latest;
76
- stream.latest = undefined;
77
- const wait = Math.max(0, 500 - (Date.now() - stream.lastSentAt));
78
- if (wait)
79
- await new Promise(resolve => setTimeout(resolve, wait));
80
- if (this.#streams.get(streamId) !== stream)
81
- break;
82
- stream.lastSentAt = Date.now();
83
- const response = await this.groupService({
84
- url: `/v2/users/${stream.userOpenId}/stream_messages`,
85
- method: 'post',
86
- data: {
87
- msg_id: stream.msgId,
88
- event_id: stream.eventId,
89
- msg_seq: stream.msgSeq,
90
- index: stream.index++,
91
- input_mode: 'replace',
92
- input_state: 1,
93
- content_type: 'markdown',
94
- content_raw: content,
95
- ...(stream.streamMessageId && { stream_msg_id: stream.streamMessageId })
96
- }
97
- });
98
- if (response?.id && !stream.streamMessageId)
99
- stream.streamMessageId = response.id;
100
- stream.lastSentText = content;
101
- result = response;
102
- }
103
- return result;
104
- })().finally(() => {
105
- if (this.#streams.get(streamId) === stream)
106
- stream.sendTask = undefined;
107
- });
108
- }
109
- return stream.sendTask;
110
- }
111
- async streamComplete(streamId) {
112
- const stream = this.#streams.get(streamId);
113
- if (!stream)
114
- throw new Error('Unknown or expired streamId');
115
- try {
116
- await stream.sendTask;
117
- return await this.groupService({
118
- url: `/v2/users/${stream.userOpenId}/stream_messages`,
119
- method: 'post',
120
- data: {
121
- msg_id: stream.msgId,
122
- event_id: stream.eventId,
123
- msg_seq: stream.msgSeq,
124
- index: stream.index++,
125
- input_mode: 'replace',
126
- input_state: 10,
127
- content_type: 'markdown',
128
- content_raw: stream.latest ?? stream.lastSentText,
129
- ...(stream.streamMessageId && { stream_msg_id: stream.streamMessageId })
130
- }
131
- });
132
- }
133
- finally {
134
- this.streamCancel(streamId);
135
- }
136
- }
137
- streamCancel(streamId) {
138
- const stream = this.#streams.get(streamId);
139
- if (stream?.timer)
140
- clearTimeout(stream.timer);
141
- if (this.#streams.delete(streamId))
142
- this.#onStreamClosed?.(streamId);
143
- }
144
- cancelStreams() {
145
- for (const streamId of this.#streams.keys())
146
- this.streamCancel(streamId);
147
- }
148
- getAuthentication() {
149
- const app_id = this.config.get('app_id');
150
- const secret = this.config.get('secret');
151
- const baseUrlAppAccessToken = this.config.get('base_url_app_access_token');
152
- const params = {
153
- url: '/app/getAppAccessToken'
154
- };
155
- if (baseUrlAppAccessToken) {
156
- params.baseURL = baseUrlAppAccessToken;
157
- }
158
- const service = axios.create({
159
- baseURL: BOTS_API_RUL,
160
- timeout: 20000
161
- });
162
- return createAxiosInstance(service, {
163
- ...params,
164
- method: 'post',
165
- data: {
166
- appId: `${app_id}`,
167
- clientSecret: `${secret}`
168
- }
169
- });
170
- }
171
- groupService(options) {
172
- const app_id = this.config.get('app_id');
173
- const token = this.config.get('access_token');
174
- const sandbox = this.config.get('sandbox');
175
- const service = axios.create({
176
- baseURL: sandbox ? API_URL_SANDBOX : API_URL,
177
- timeout: 20000,
178
- headers: {
179
- 'X-Union-Appid': app_id,
180
- Authorization: `QQBot ${token}`
181
- }
182
- });
183
- return createAxiosInstance(service, options);
184
- }
185
- guildServer(options) {
186
- return this.groupService(options);
187
- }
188
- gateway() {
189
- const baseUrlGateway = this.config.get('base_url_gateway');
190
- const params = {
191
- url: '/gateway'
192
- };
193
- if (baseUrlGateway) {
194
- params.baseURL = baseUrlGateway;
195
- }
196
- return this.groupService(params);
197
- }
198
- usersOpenMessages(openid, data) {
199
- const db = {
200
- ...(data.event_id
201
- ? { event_id: data.event_id }
202
- : {
203
- msg_seq: this.getMessageSeq(data.msg_id || '')
204
- }),
205
- ...data
206
- };
207
- return this.groupService({
208
- url: `/v2/users/${openid}/messages`,
209
- method: 'post',
210
- data: db
211
- });
212
- }
213
- getMessageSeq(MessageId) {
214
- let seq = this.#msgMap.get(MessageId) || 0;
215
- seq++;
216
- this.#msgMap.set(MessageId, seq);
217
- if (this.#msgMap.size > 100) {
218
- const firstKey = this.#msgMap.keys().next().value;
219
- if (firstKey) {
220
- this.#msgMap.delete(firstKey);
221
- }
222
- }
223
- return seq;
224
- }
225
- groupOpenMessages(group_openid, data) {
226
- const db = {
227
- ...(data.event_id
228
- ? { event_id: data.event_id }
229
- : {
230
- msg_seq: this.getMessageSeq(data.msg_id || '')
231
- }),
232
- ...data
233
- };
234
- return this.groupService({
235
- url: `/v2/groups/${group_openid}/messages`,
236
- method: 'post',
237
- data: db
238
- });
239
- }
240
- postRichMediaByUser(openid, data) {
241
- const fileBuffer = fileDataToBuffer(data.file_data);
242
- if (!data.upload_id && fileBuffer && fileBuffer.byteLength > CHUNK_THRESHOLD) {
243
- return chunkedUpload(this, 'user', openid, fileBuffer, { file_type: data.file_type, file_name: data.file_name, srv_send_msg: data.srv_send_msg });
244
- }
245
- return this.groupService({
246
- url: `/v2/users/${openid}/files`,
247
- method: 'post',
248
- data: data
249
- });
250
- }
251
- postRichMediaByGroup(openid, data) {
252
- const fileBuffer = fileDataToBuffer(data.file_data);
253
- if (!data.upload_id && fileBuffer && fileBuffer.byteLength > CHUNK_THRESHOLD) {
254
- return chunkedUpload(this, 'group', openid, fileBuffer, { file_type: data.file_type, file_name: data.file_name, srv_send_msg: data.srv_send_msg });
255
- }
256
- return this.groupService({
257
- url: `/v2/groups/${openid}/files`,
258
- method: 'post',
259
- data: {
260
- srv_send_msg: false,
261
- ...data
262
- }
263
- });
264
- }
265
- async getMediaFileHashes(filePath) {
266
- const sha256 = createHash('sha256');
267
- const md5 = createHash('md5');
268
- const sha1 = createHash('sha1');
269
- const md5_10m = createHash('md5');
270
- let firstBytes = 0;
271
- const firstLimit = 10_002_432;
272
- for await (const chunk of createReadStream(filePath)) {
273
- const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
274
- sha256.update(data);
275
- md5.update(data);
276
- sha1.update(data);
277
- if (firstBytes < firstLimit) {
278
- const length = Math.min(data.length, firstLimit - firstBytes);
279
- md5_10m.update(data.subarray(0, length));
280
- firstBytes += length;
281
- }
282
- }
283
- return {
284
- sha256: sha256.digest('hex'),
285
- md5: md5.digest('hex'),
286
- sha1: sha1.digest('hex'),
287
- md5_10m: md5_10m.digest('hex')
288
- };
289
- }
290
- async postChunkedRichMedia(params) {
291
- const hasBuffer = Buffer.isBuffer(params.data);
292
- const hasFile = Boolean(params.filePath);
293
- if (hasBuffer === hasFile)
294
- throw new Error('Chunked media requires exactly one of data or filePath');
295
- const total = hasBuffer ? params.data.length : Number(params.size);
296
- if (!Number.isSafeInteger(total) || total < 1)
297
- throw new Error('Chunked media requires a valid file size');
298
- const hashes = params.hashes || (hasBuffer ? this.#getMediaBufferHashes(params.data) : await this.getMediaFileHashes(params.filePath));
299
- const prefix = params.scope === 'group' ? `/v2/groups/${params.targetId}` : `/v2/users/${params.targetId}`;
300
- const prepared = await this.groupService({
301
- url: `${prefix}/upload_prepare`,
302
- method: 'post',
303
- data: {
304
- file_type: params.fileType,
305
- file_name: params.name || 'file',
306
- file_size: total,
307
- md5: hashes.md5,
308
- sha1: hashes.sha1,
309
- md5_10m: hashes.md5_10m
310
- }
311
- });
312
- let uploaded = 0;
313
- for (const part of prepared.parts || []) {
314
- const offset = (part.index - 1) * prepared.block_size;
315
- const length = Math.min(prepared.block_size, total - offset);
316
- if (length <= 0)
317
- throw new Error(`QQ returned invalid upload part index: ${part.index}`);
318
- const body = hasBuffer ? params.data.subarray(offset, offset + length) : undefined;
319
- const partMd5 = body ? createHash('md5').update(body).digest('hex') : await this.#getFilePartHash(params.filePath, offset, length);
320
- await this.#retry(() => axios.put(part.presigned_url, body || createReadStream(params.filePath, { start: offset, end: offset + length - 1 }), {
321
- timeout: 300_000,
322
- maxBodyLength: Infinity,
323
- maxContentLength: Infinity,
324
- headers: { 'Content-Length': String(length) }
325
- }));
326
- await this.#retry(() => this.groupService({
327
- url: `${prefix}/upload_part_finish`,
328
- method: 'post',
329
- data: { upload_id: prepared.upload_id, part_index: part.index, block_size: length, md5: partMd5 }
330
- }));
331
- uploaded += length;
332
- params.onProgress?.(uploaded, total);
333
- }
334
- return this.#retry(() => this.groupService({
335
- url: `${prefix}/complete_upload`,
336
- method: 'post',
337
- data: { upload_id: prepared.upload_id }
338
- }));
339
- }
340
- #getMediaBufferHashes(data) {
341
- return {
342
- sha256: createHash('sha256').update(data).digest('hex'),
343
- md5: createHash('md5').update(data).digest('hex'),
344
- sha1: createHash('sha1').update(data).digest('hex'),
345
- md5_10m: createHash('md5').update(data.subarray(0, 10_002_432)).digest('hex')
346
- };
347
- }
348
- async #getFilePartHash(filePath, start, length) {
349
- const md5 = createHash('md5');
350
- for await (const chunk of createReadStream(filePath, { start, end: start + length - 1 }))
351
- md5.update(chunk);
352
- return md5.digest('hex');
353
- }
354
- async #retry(operation, attempts = 3) {
355
- let lastError;
356
- for (let attempt = 0; attempt < attempts; attempt++) {
357
- try {
358
- return await operation();
359
- }
360
- catch (error) {
361
- lastError = error;
362
- if (attempt + 1 < attempts)
363
- await new Promise(resolve => setTimeout(resolve, 250 * 2 ** attempt));
364
- }
365
- }
366
- throw lastError;
367
- }
368
- userMessageDelete(openid, message_id) {
369
- return this.groupService({
370
- url: `/v2/users/${openid}/messages/${message_id}`,
371
- method: 'delete'
372
- });
373
- }
374
- groupMessageDelete(group_openid, message_id) {
375
- return this.groupService({
376
- url: `/v2/groups/${group_openid}/messages/${message_id}`,
377
- method: 'delete'
378
- });
379
- }
380
- grouMessageDelte(group_openid, message_id) {
381
- return this.groupMessageDelete(group_openid, message_id);
382
- }
383
- groupsInfo(group_openid) {
384
- return this.groupService({
385
- url: `/v2/groups/${group_openid}/info`,
386
- method: 'get'
387
- });
388
- }
389
- groupsBotState(group_openid) {
390
- return this.groupService({
391
- url: `/v2/groups/${group_openid}/bot_state`,
392
- method: 'get'
393
- });
394
- }
395
- groupsMembersMessage(group_openid, member_openid) {
396
- return this.groupService({
397
- url: `/v2/groups/${group_openid}/members/${member_openid}`,
398
- method: 'get'
399
- });
400
- }
401
- groupsJoinRequestList(group_openid, params) {
402
- return this.groupService({
403
- url: `/v2/groups/${group_openid}/join_request_list`,
404
- method: 'get',
405
- params
406
- });
407
- }
408
- groupsApprovalJoinRequest(group_openid, member_openid, data) {
409
- return this.groupService({
410
- url: `/v2/groups/${group_openid}/approval_join_request/${member_openid}`,
411
- method: 'post',
412
- data
413
- });
414
- }
415
- groupsRestrictChatSetting(group_openid) {
416
- return this.groupService({
417
- url: `/v2/groups/${group_openid}/restrict_chat_setting`,
418
- method: 'get'
419
- });
420
- }
421
- groupsRestrictChatSettingPost(group_openid, data) {
422
- return this.groupService({
423
- url: `/v2/groups/${group_openid}/restrict_chat_setting`,
424
- method: 'post',
425
- data
426
- });
427
- }
428
- groupsJoinApprovalStrategies(params) {
429
- return this.groupService({
430
- url: '/v2/groups/join_approval_strategy',
431
- method: 'get',
432
- params
433
- });
434
- }
435
- groupsJoinApprovalStrategyCreate(data) {
436
- return this.groupService({
437
- url: '/v2/groups/join_approval_strategy',
438
- method: 'post',
439
- data
440
- });
441
- }
442
- groupsJoinApprovalStrategyPatch(strategy_id, data) {
443
- return this.groupService({
444
- url: `/v2/groups/join_approval_strategy/${strategy_id}`,
445
- method: 'patch',
446
- data
447
- });
448
- }
449
- groupsJoinApprovalStrategyDelete(strategy_id) {
450
- return this.groupService({
451
- url: `/v2/groups/join_approval_strategy/${strategy_id}`,
452
- method: 'delete'
453
- });
454
- }
455
- groupsJoinApprovalStrategyExecute(strategy_id) {
456
- return this.groupService({
457
- url: `/v2/groups/join_approval_strategy/${strategy_id}/execute`,
458
- method: 'post'
459
- });
460
- }
461
- groupsJoinApprovalStrategyWhitelistUsers(strategy_id, data) {
462
- return this.groupService({
463
- url: `/v2/groups/join_approval_strategy/${strategy_id}/whitelist_users`,
464
- method: 'post',
465
- data
466
- });
467
- }
468
- usersUploadPrepare(userId, data) {
469
- return this.groupService({
470
- url: `/v2/users/${userId}/upload_prepare`,
471
- method: 'post',
472
- data
473
- });
474
- }
475
- groupUploadPrepare(groupId, data) {
476
- return this.groupService({
477
- url: `/v2/groups/${groupId}/upload_prepare`,
478
- method: 'post',
479
- data
480
- });
481
- }
482
- usersUploadPartFinish(userId, data) {
483
- return this.groupService({
484
- url: `/v2/users/${userId}/upload_part_finish`,
485
- method: 'post',
486
- data
487
- });
488
- }
489
- groupUploadPartFinish(groupId, data) {
490
- return this.groupService({
491
- url: `/v2/groups/${groupId}/upload_part_finish`,
492
- method: 'post',
493
- data
494
- });
495
- }
496
- uploadPartDirect(presignedUrl, part) {
497
- return axios.put(presignedUrl, part);
498
- }
499
- streamMessages(userId, data) {
500
- return this.groupService({
501
- url: `/v2/users/${userId}/stream_messages`,
502
- method: 'post',
503
- data
504
- });
505
- }
506
- async channelsMessages(channel_id, message, image) {
507
- const formdata = new FormData();
508
- for (const key in message) {
509
- if (message[key] !== undefined) {
510
- formdata.append(key, message[key]);
511
- }
512
- }
513
- if (image) {
514
- const from = await createPicFrom({ image });
515
- if (from) {
516
- const { picData, name } = from;
517
- formdata.append('file_image', picData, name);
518
- }
519
- }
520
- const dary = formdata.getBoundary();
521
- return this.guildServer({
522
- method: 'post',
523
- url: `/channels/${channel_id}/messages`,
524
- headers: {
525
- 'Content-Type': `multipart/form-data; boundary=${dary}`
526
- },
527
- data: formdata
528
- });
529
- }
530
- async dmsMessages(guild_id, message, image) {
531
- const formdata = new FormData();
532
- for (const key in message) {
533
- if (message[key] !== undefined) {
534
- formdata.append(key, message[key]);
535
- }
536
- }
537
- if (image) {
538
- const from = await createPicFrom({ image });
539
- if (from) {
540
- const { picData, name } = from;
541
- formdata.append('file_image', picData, name);
542
- }
543
- }
544
- const dary = formdata.getBoundary();
545
- return this.guildServer({
546
- method: 'post',
547
- url: `/dms/${guild_id}/messages`,
548
- headers: {
549
- 'Content-Type': `multipart/form-data; boundary=${dary}`
550
- },
551
- data: formdata
552
- });
553
- }
554
- usersMe() {
555
- return this.guildServer({
556
- method: 'get',
557
- url: '/users/@me'
558
- });
559
- }
560
- usersMeGuilds(params) {
561
- return this.guildServer({
562
- method: 'get',
563
- url: '/users/@me/guilds',
564
- params
565
- });
566
- }
567
- guilds(guild_id) {
568
- return this.guildServer({
569
- method: 'get',
570
- url: `/guilds/${guild_id}`
571
- });
572
- }
573
- guildsChannels(guild_id) {
574
- return this.guildServer({
575
- method: 'get',
576
- url: `/guilds/${guild_id}/channels`
577
- });
578
- }
579
- channels(channel_id) {
580
- return this.guildServer({
581
- method: 'get',
582
- url: `/channels/${channel_id}`
583
- });
584
- }
585
- guildsChannelsCreate(guild_id, data) {
586
- return this.guildServer({
587
- method: 'post',
588
- url: `/guilds/${guild_id}/channels`,
589
- data
590
- });
591
- }
592
- guildsChannelsUpdate(channel_id, data) {
593
- return this.guildServer({
594
- method: 'PATCH',
595
- url: `/channels/${channel_id}`,
596
- data
597
- });
598
- }
599
- guildsChannelsdelete(channel_id, data) {
600
- return this.guildServer({
601
- method: 'DELETE',
602
- url: `/channels/${channel_id}`,
603
- data
604
- });
605
- }
606
- channelsChannelOnlineNums(channel_id) {
607
- return this.guildServer({
608
- method: 'GET',
609
- url: `/channels/${channel_id}/online_nums`
610
- });
611
- }
612
- guildsMembers(guild_id, params) {
613
- return this.guildServer({
614
- method: 'GET',
615
- url: `/guilds/${guild_id}/members`,
616
- params
617
- });
618
- }
619
- guildsRolesMembers(guild_id, role_id, params) {
620
- return this.guildServer({
621
- method: 'GET',
622
- url: `/guilds/${guild_id}/roles/${role_id}/members`,
623
- params
624
- });
625
- }
626
- guildsMembersMessage(guild_id, user_id) {
627
- return this.guildServer({
628
- method: 'GET',
629
- url: `/guilds/${guild_id}/members/${user_id}`
630
- });
631
- }
632
- guildsMembersDelete(guild_id, user_id) {
633
- return this.guildServer({
634
- method: 'DELETE',
635
- url: `/guilds/${guild_id}/members/${user_id}`
636
- });
637
- }
638
- channelsMessagesById(channel_id, message_id) {
639
- return this.guildServer({
640
- method: 'GET',
641
- url: `/channels/${channel_id}/messages/${message_id}`
642
- });
643
- }
644
- channelsMessagesDelete(channel_id, message_id, hidetip = true) {
645
- return this.guildServer({
646
- method: 'DELETE',
647
- url: `/channels/${channel_id}/messages/${message_id}?hidetip=${hidetip}`
648
- });
649
- }
650
- guildsRoles(guild_id) {
651
- return this.guildServer({
652
- method: 'GET',
653
- url: `/guilds/${guild_id}/roles`
654
- });
655
- }
656
- guildsRolesPost(guild_id, data) {
657
- return this.guildServer({
658
- method: 'POST',
659
- url: `/guilds/${guild_id}/roles`,
660
- data
661
- });
662
- }
663
- guildsRolesPatch(guild_id, role_id, data) {
664
- return this.guildServer({
665
- method: 'PATCH',
666
- url: `/guilds/${guild_id}/roles/${role_id}`,
667
- data
668
- });
669
- }
670
- guildsRolesDelete(guild_id, role_id) {
671
- return this.guildServer({
672
- method: 'DELETE',
673
- url: `/guilds/${guild_id}/roles/${role_id}`
674
- });
675
- }
676
- guildsRolesMembersPut(guild_id, channel_id, user_id, role_id) {
677
- return this.guildServer({
678
- method: 'PUT',
679
- url: `/guilds/${guild_id}/members/${user_id}/roles/${role_id}`,
680
- data: {
681
- channel: {
682
- id: channel_id
683
- }
684
- }
685
- });
686
- }
687
- guildsRolesMembersDelete(guild_id, channel_id, user_id, role_id) {
688
- return this.guildServer({
689
- method: 'DELETE',
690
- url: `/guilds/${guild_id}/members/${user_id}/roles/${role_id}`,
691
- data: {
692
- channel: {
693
- id: channel_id
694
- }
695
- }
696
- });
697
- }
698
- channelsPermissions(channel_id, user_id) {
699
- return this.guildServer({
700
- method: 'GET',
701
- url: `/channels/${channel_id}/members/${user_id}/permissions`
702
- });
703
- }
704
- channelsPermissionsPut(channel_id, user_id, add, remove) {
705
- return this.guildServer({
706
- method: 'PUT',
707
- url: `/channels/${channel_id}/members/${user_id}/permissions`,
708
- data: {
709
- add,
710
- remove
711
- }
712
- });
713
- }
714
- guildsMessageSetting(guild_id) {
715
- return this.guildServer({
716
- method: 'GET',
717
- url: `/guilds/${guild_id}/message/setting`
718
- });
719
- }
720
- usersMeDms() {
721
- return this.guildServer({
722
- method: 'POST',
723
- url: '/users/@me/dms'
724
- });
725
- }
726
- dmsMessageDelete(guild_id, message_id, hidetip = true) {
727
- return this.guildServer({
728
- method: 'DELETE',
729
- url: `/dms/${guild_id}/messages/${message_id}?hidetip=${hidetip}`
730
- });
731
- }
732
- guildsMuteAll(guild_id, data) {
733
- return this.guildServer({
734
- method: 'PATCH',
735
- url: `/guilds/${guild_id}/mute`,
736
- data
737
- });
738
- }
739
- guildsMemberMute(guild_id, user_id, data) {
740
- return this.guildServer({
741
- method: 'PATCH',
742
- url: `/guilds/${guild_id}/members/${user_id}/mute`,
743
- data
744
- });
745
- }
746
- guildsMute(guild_id, data) {
747
- return this.guildServer({
748
- method: 'PATCH',
749
- url: `/guilds/${guild_id}/mute`,
750
- data
751
- });
752
- }
753
- guildsAnnounces(guild_id, data) {
754
- return this.guildServer({
755
- method: 'POST',
756
- url: `/guilds/${guild_id}/announces`,
757
- data
758
- });
759
- }
760
- guildsAnnouncesDelete(guild_id, message_id) {
761
- return this.guildServer({
762
- method: 'DELETE',
763
- url: `/guilds/${guild_id}/announces/${message_id}`
764
- });
765
- }
766
- channelsPinsPut(channel_id, message_id) {
767
- return this.guildServer({
768
- method: 'PUT',
769
- url: `/channels/${channel_id}/pins/${message_id}`
770
- });
771
- }
772
- channelsPinsDelete(channel_id, message_id) {
773
- return this.guildServer({
774
- method: 'DELETE',
775
- url: `/channels/${channel_id}/pins/${message_id}`
776
- });
777
- }
778
- channelsPins(channel_id) {
779
- return this.guildServer({
780
- method: 'GET',
781
- url: `/channels/${channel_id}/pins`
782
- });
783
- }
784
- channelsSchedules(channel_id) {
785
- return this.guildServer({
786
- method: 'GET',
787
- url: `/channels/${channel_id}/schedules`
788
- });
789
- }
790
- channelsSchedulesSchedule(channel_id, schedule_id) {
791
- return this.guildServer({
792
- method: 'GET',
793
- url: `/channels/${channel_id}/schedules/${schedule_id}`
794
- });
795
- }
796
- channelsSchedulesPost(channel_id, data) {
797
- return this.guildServer({
798
- method: 'POST',
799
- url: `/channels/${channel_id}/schedules`,
800
- data
801
- });
802
- }
803
- channelsSchedulesSchedulePatch(channel_id, schedule_id, data) {
804
- return this.guildServer({
805
- method: 'PATCH',
806
- url: `/channels/${channel_id}/schedules/${schedule_id}`,
807
- data
808
- });
809
- }
810
- channelsSchedulesScheduleDelete(channel_id, schedule_id) {
811
- return this.guildServer({
812
- method: 'DELETE',
813
- url: `/channels/${channel_id}/schedules/${schedule_id}`
814
- });
815
- }
816
- channelsMessagesReactionsPut(channel_id, message_id, type, id) {
817
- return this.guildServer({
818
- method: 'PUT',
819
- url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`
820
- });
821
- }
822
- channelsMessagesReactionsDelete(channel_id, message_id, type, id) {
823
- return this.guildServer({
824
- method: 'DELETE',
825
- url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`
826
- });
827
- }
828
- channelsMessagesReactionsUsers(channel_id, message_id, type, id, data) {
829
- return this.guildServer({
830
- method: 'GET',
831
- url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`,
832
- data
833
- });
834
- }
835
- channelsAudioPost(channel_id, data) {
836
- return this.guildServer({
837
- method: 'POST',
838
- url: `/channels/${channel_id}/audio`,
839
- data
840
- });
841
- }
842
- channelsMicPut(channel_id) {
843
- return this.guildServer({
844
- method: 'PUT',
845
- url: `/channels/${channel_id}/mic`
846
- });
847
- }
848
- channelsMicDelete(channel_id) {
849
- return this.guildServer({
850
- method: 'DELETE',
851
- url: `/channels/${channel_id}/mic`
852
- });
853
- }
854
- channelsThreads(channel_id) {
855
- return this.guildServer({
856
- method: 'GET',
857
- url: `/channels/${channel_id}/threads`
858
- });
859
- }
860
- channelsThreadsThread(channel_id, thread_id) {
861
- return this.guildServer({
862
- method: 'GET',
863
- url: `/channels/${channel_id}/threads/${thread_id}`
864
- });
865
- }
866
- channelsThreadsPut(channel_id, data) {
867
- return this.guildServer({
868
- method: 'PUT',
869
- url: `/channels/${channel_id}/threads`,
870
- data
871
- });
872
- }
873
- channelsThreadsDelete(channel_id, thread_id) {
874
- return this.guildServer({
875
- method: 'DELETE',
876
- url: `/channels/${channel_id}/threads/${thread_id}`
877
- });
878
- }
879
- guildApiPermission(guild_id) {
880
- return this.guildServer({
881
- url: `/guilds/${guild_id}/api_permission`
882
- });
883
- }
884
- interactionResponse(_mode, interaction_id, code) {
885
- return this.groupService({
886
- method: 'PUT',
887
- url: `/interactions/${interaction_id}`,
888
- data: {
889
- code: code || 0
890
- }
891
- });
892
- }
893
- }
10
+ //#region src/sdk/api.ts
11
+ /** 获取访问凭证域名(统一为 api.bot.qq.com,同接口调用域名) */
12
+ const BOTS_API_RUL = "https://api.bot.qq.com";
13
+ const API_URL_SANDBOX = "https://sandbox.api.sgroup.qq.com";
14
+ /** 接口调用域名 */
15
+ const API_URL = "https://api.bot.qq.com";
16
+ var QQBotAPI = class {
17
+ config;
18
+ #msgMap = /* @__PURE__ */ new Map();
19
+ #streams = /* @__PURE__ */ new Map();
20
+ #onStreamClosed;
21
+ #onConnectionStatusChanged;
22
+ #connectionStatus = {
23
+ state: "idle",
24
+ transport: null,
25
+ reconnectAttempts: 0,
26
+ heartbeatAcknowledged: false,
27
+ resumed: false
28
+ };
29
+ constructor(options = {}) {
30
+ this.config = new QQBotConfig(options);
31
+ }
32
+ /** Read-only runtime snapshot, available through useClient<API>(). */
33
+ getConnectionStatus() {
34
+ return { ...this.#connectionStatus };
35
+ }
36
+ updateConnectionStatus(patch) {
37
+ const previous = this.#connectionStatus;
38
+ this.#connectionStatus = {
39
+ ...previous,
40
+ ...patch
41
+ };
42
+ this.#onConnectionStatusChanged?.({ ...this.#connectionStatus }, { ...previous });
43
+ }
44
+ /** Subscribe to transport lifecycle changes without coupling consumers to a concrete QQ transport. */
45
+ setConnectionStatusListener(listener) {
46
+ this.#onConnectionStatusChanged = listener;
47
+ }
48
+ /** Internal transport hook used by the multi-bot registry to discard stale routes. */
49
+ setStreamLifecycleListener(listener) {
50
+ this.#onStreamClosed = listener;
51
+ }
52
+ /** C2C-only input indicator. This is intentionally a QQ extension, not a core action. */
53
+ sendTyping(params) {
54
+ if (!params.userOpenId) throw new Error("userOpenId is required");
55
+ return this.groupService({
56
+ url: `/v2/users/${params.userOpenId}/input_notify`,
57
+ method: "post",
58
+ data: {
59
+ ...params.msgId && { msg_id: params.msgId },
60
+ input_second: Math.max(1, Math.min(Number(params.durationSec) || 30, 60))
61
+ }
62
+ });
63
+ }
64
+ streamOpen(params) {
65
+ if (!params.userOpenId || !params.msgId) throw new Error("C2C streaming requires userOpenId and msgId");
66
+ const streamId = randomUUID();
67
+ const stream = {
68
+ userOpenId: params.userOpenId,
69
+ msgId: params.msgId,
70
+ eventId: params.eventId || params.msgId,
71
+ msgSeq: this.getMessageSeq(params.msgId),
72
+ index: 0,
73
+ lastSentAt: 0,
74
+ lastSentText: ""
75
+ };
76
+ stream.timer = setTimeout(() => this.streamCancel(streamId), 3e5);
77
+ this.#streams.set(streamId, stream);
78
+ return { streamId };
79
+ }
80
+ async streamUpdate(streamId, fullText) {
81
+ const stream = this.#streams.get(streamId);
82
+ if (!stream) throw new Error("Unknown or expired streamId");
83
+ stream.latest = fullText;
84
+ if (!stream.sendTask) stream.sendTask = (async () => {
85
+ let result;
86
+ while (stream.latest !== void 0 && this.#streams.get(streamId) === stream) {
87
+ const content = stream.latest;
88
+ stream.latest = void 0;
89
+ const wait = Math.max(0, 500 - (Date.now() - stream.lastSentAt));
90
+ if (wait) await new Promise((resolve) => setTimeout(resolve, wait));
91
+ if (this.#streams.get(streamId) !== stream) break;
92
+ stream.lastSentAt = Date.now();
93
+ const response = await this.groupService({
94
+ url: `/v2/users/${stream.userOpenId}/stream_messages`,
95
+ method: "post",
96
+ data: {
97
+ msg_id: stream.msgId,
98
+ event_id: stream.eventId,
99
+ msg_seq: stream.msgSeq,
100
+ index: stream.index++,
101
+ input_mode: "replace",
102
+ input_state: 1,
103
+ content_type: "markdown",
104
+ content_raw: content,
105
+ ...stream.streamMessageId && { stream_msg_id: stream.streamMessageId }
106
+ }
107
+ });
108
+ if (response?.id && !stream.streamMessageId) stream.streamMessageId = response.id;
109
+ stream.lastSentText = content;
110
+ result = response;
111
+ }
112
+ return result;
113
+ })().finally(() => {
114
+ if (this.#streams.get(streamId) === stream) stream.sendTask = void 0;
115
+ });
116
+ return stream.sendTask;
117
+ }
118
+ async streamComplete(streamId) {
119
+ const stream = this.#streams.get(streamId);
120
+ if (!stream) throw new Error("Unknown or expired streamId");
121
+ try {
122
+ await stream.sendTask;
123
+ return await this.groupService({
124
+ url: `/v2/users/${stream.userOpenId}/stream_messages`,
125
+ method: "post",
126
+ data: {
127
+ msg_id: stream.msgId,
128
+ event_id: stream.eventId,
129
+ msg_seq: stream.msgSeq,
130
+ index: stream.index++,
131
+ input_mode: "replace",
132
+ input_state: 10,
133
+ content_type: "markdown",
134
+ content_raw: stream.latest ?? stream.lastSentText,
135
+ ...stream.streamMessageId && { stream_msg_id: stream.streamMessageId }
136
+ }
137
+ });
138
+ } finally {
139
+ this.streamCancel(streamId);
140
+ }
141
+ }
142
+ streamCancel(streamId) {
143
+ const stream = this.#streams.get(streamId);
144
+ if (stream?.timer) clearTimeout(stream.timer);
145
+ if (this.#streams.delete(streamId)) this.#onStreamClosed?.(streamId);
146
+ }
147
+ /** Called by transports during shutdown so C2C streams cannot leak timers. */
148
+ cancelStreams() {
149
+ for (const streamId of this.#streams.keys()) this.streamCancel(streamId);
150
+ }
151
+ /**
152
+ * 得到鉴权
153
+ * @param app_id
154
+ * @param clientSecret
155
+ * @returns
156
+ */
157
+ getAuthentication() {
158
+ const app_id = this.config.get("app_id");
159
+ const secret = this.config.get("secret");
160
+ const baseUrlAppAccessToken = this.config.get("base_url_app_access_token");
161
+ const params = { url: "/app/getAppAccessToken" };
162
+ if (baseUrlAppAccessToken) params.baseURL = baseUrlAppAccessToken;
163
+ const service = axios.create({
164
+ baseURL: BOTS_API_RUL,
165
+ timeout: 2e4
166
+ });
167
+ return createAxiosInstance(service, {
168
+ ...params,
169
+ method: "post",
170
+ data: {
171
+ appId: `${app_id}`,
172
+ clientSecret: `${secret}`
173
+ }
174
+ });
175
+ }
176
+ /**
177
+ * 统一鉴权请求(QQBot AccessToken)
178
+ * @param options
179
+ * @returns
180
+ */
181
+ groupService(options) {
182
+ const app_id = this.config.get("app_id");
183
+ const token = this.config.get("access_token");
184
+ const sandbox = this.config.get("sandbox");
185
+ const service = axios.create({
186
+ baseURL: sandbox ? API_URL_SANDBOX : API_URL,
187
+ timeout: 2e4,
188
+ headers: {
189
+ "X-Union-Appid": app_id,
190
+ Authorization: `QQBot ${token}`
191
+ }
192
+ });
193
+ return createAxiosInstance(service, options);
194
+ }
195
+ /**
196
+ * @deprecated 使用 groupService 代替,鉴权方式已统一
197
+ */
198
+ guildServer(options) {
199
+ return this.groupService(options);
200
+ }
201
+ /**
202
+ * 得到鉴权
203
+ * @returns
204
+ */
205
+ gateway() {
206
+ const baseUrlGateway = this.config.get("base_url_gateway");
207
+ const params = { url: "/gateway" };
208
+ if (baseUrlGateway) params.baseURL = baseUrlGateway;
209
+ return this.groupService(params);
210
+ }
211
+ /**
212
+ * 发送私聊消息
213
+ * @param openid
214
+ * @param content
215
+ * @param msg_id
216
+ * @returns
217
+ * 0 文本 1 图文 2 md 3 ark 4 embed
218
+ */
219
+ usersOpenMessages(openid, data) {
220
+ const db = {
221
+ ...data.event_id ? { event_id: data.event_id } : { msg_seq: this.getMessageSeq(data.msg_id || "") },
222
+ ...data
223
+ };
224
+ return this.groupService({
225
+ url: `/v2/users/${openid}/messages`,
226
+ method: "post",
227
+ data: db
228
+ });
229
+ }
230
+ /**
231
+ * 得到消息序列
232
+ * @param MessageId
233
+ * @returns
234
+ */
235
+ getMessageSeq(MessageId) {
236
+ let seq = this.#msgMap.get(MessageId) || 0;
237
+ seq++;
238
+ this.#msgMap.set(MessageId, seq);
239
+ if (this.#msgMap.size > 100) {
240
+ const firstKey = this.#msgMap.keys().next().value;
241
+ if (firstKey) this.#msgMap.delete(firstKey);
242
+ }
243
+ return seq;
244
+ }
245
+ /**
246
+ * 发送群聊消息
247
+ * @param group_openid
248
+ * @param data
249
+ * @returns
250
+ */
251
+ groupOpenMessages(group_openid, data) {
252
+ const db = {
253
+ ...data.event_id ? { event_id: data.event_id } : { msg_seq: this.getMessageSeq(data.msg_id || "") },
254
+ ...data
255
+ };
256
+ return this.groupService({
257
+ url: `/v2/groups/${group_openid}/messages`,
258
+ method: "post",
259
+ data: db
260
+ });
261
+ }
262
+ /**
263
+ * 发送私聊富媒体文件
264
+ * @param openid
265
+ * @param data
266
+ * @returns
267
+ * 1 图文 2 视频 3 语言 4 文件
268
+ * 图片:png/jpg,视频:mp4,语音:silk
269
+ */
270
+ postRichMediaByUser(openid, data) {
271
+ const fileBuffer = fileDataToBuffer(data.file_data);
272
+ if (!data.upload_id && fileBuffer && fileBuffer.byteLength > 10002432) return chunkedUpload(this, "user", openid, fileBuffer, {
273
+ file_type: data.file_type,
274
+ file_name: data.file_name,
275
+ srv_send_msg: data.srv_send_msg
276
+ });
277
+ return this.groupService({
278
+ url: `/v2/users/${openid}/files`,
279
+ method: "post",
280
+ data
281
+ });
282
+ }
283
+ /**
284
+ * 发送群里文件
285
+ * @param openid
286
+ * @param data
287
+ * @returns
288
+ * 1 图文 2 视频 3 语言 4 文件
289
+ * 图片:png/jpg,视频:mp4,语音:silk
290
+ */
291
+ postRichMediaByGroup(openid, data) {
292
+ const fileBuffer = fileDataToBuffer(data.file_data);
293
+ if (!data.upload_id && fileBuffer && fileBuffer.byteLength > 10002432) return chunkedUpload(this, "group", openid, fileBuffer, {
294
+ file_type: data.file_type,
295
+ file_name: data.file_name,
296
+ srv_send_msg: data.srv_send_msg
297
+ });
298
+ return this.groupService({
299
+ url: `/v2/groups/${openid}/files`,
300
+ method: "post",
301
+ data: {
302
+ srv_send_msg: false,
303
+ ...data
304
+ }
305
+ });
306
+ }
307
+ /** Produces upload and cache hashes without materialising the file in memory. */
308
+ async getMediaFileHashes(filePath) {
309
+ const sha256 = createHash("sha256");
310
+ const md5 = createHash("md5");
311
+ const sha1 = createHash("sha1");
312
+ const md5_10m = createHash("md5");
313
+ let firstBytes = 0;
314
+ const firstLimit = 10002432;
315
+ for await (const chunk of createReadStream(filePath)) {
316
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
317
+ sha256.update(data);
318
+ md5.update(data);
319
+ sha1.update(data);
320
+ if (firstBytes < firstLimit) {
321
+ const length = Math.min(data.length, firstLimit - firstBytes);
322
+ md5_10m.update(data.subarray(0, length));
323
+ firstBytes += length;
324
+ }
325
+ }
326
+ return {
327
+ sha256: sha256.digest("hex"),
328
+ md5: md5.digest("hex"),
329
+ sha1: sha1.digest("hex"),
330
+ md5_10m: md5_10m.digest("hex")
331
+ };
332
+ }
333
+ /**
334
+ * QQ's large-file protocol: prepare -> COS PUT -> part finish -> complete.
335
+ * A local path is read one part at a time, including retry attempts.
336
+ */
337
+ async postChunkedRichMedia(params) {
338
+ const hasBuffer = Buffer.isBuffer(params.data);
339
+ if (hasBuffer === Boolean(params.filePath)) throw new Error("Chunked media requires exactly one of data or filePath");
340
+ const total = hasBuffer ? params.data.length : Number(params.size);
341
+ if (!Number.isSafeInteger(total) || total < 1) throw new Error("Chunked media requires a valid file size");
342
+ const hashes = params.hashes || (hasBuffer ? this.#getMediaBufferHashes(params.data) : await this.getMediaFileHashes(params.filePath));
343
+ const prefix = params.scope === "group" ? `/v2/groups/${params.targetId}` : `/v2/users/${params.targetId}`;
344
+ const prepared = await this.groupService({
345
+ url: `${prefix}/upload_prepare`,
346
+ method: "post",
347
+ data: {
348
+ file_type: params.fileType,
349
+ file_name: params.name || "file",
350
+ file_size: total,
351
+ md5: hashes.md5,
352
+ sha1: hashes.sha1,
353
+ md5_10m: hashes.md5_10m
354
+ }
355
+ });
356
+ let uploaded = 0;
357
+ for (const part of prepared.parts || []) {
358
+ const offset = (part.index - 1) * prepared.block_size;
359
+ const length = Math.min(prepared.block_size, total - offset);
360
+ if (length <= 0) throw new Error(`QQ returned invalid upload part index: ${part.index}`);
361
+ const body = hasBuffer ? params.data.subarray(offset, offset + length) : void 0;
362
+ const partMd5 = body ? createHash("md5").update(body).digest("hex") : await this.#getFilePartHash(params.filePath, offset, length);
363
+ await this.#retry(() => axios.put(part.presigned_url, body || createReadStream(params.filePath, {
364
+ start: offset,
365
+ end: offset + length - 1
366
+ }), {
367
+ timeout: 3e5,
368
+ maxBodyLength: Infinity,
369
+ maxContentLength: Infinity,
370
+ headers: { "Content-Length": String(length) }
371
+ }));
372
+ await this.#retry(() => this.groupService({
373
+ url: `${prefix}/upload_part_finish`,
374
+ method: "post",
375
+ data: {
376
+ upload_id: prepared.upload_id,
377
+ part_index: part.index,
378
+ block_size: length,
379
+ md5: partMd5
380
+ }
381
+ }));
382
+ uploaded += length;
383
+ params.onProgress?.(uploaded, total);
384
+ }
385
+ return this.#retry(() => this.groupService({
386
+ url: `${prefix}/complete_upload`,
387
+ method: "post",
388
+ data: { upload_id: prepared.upload_id }
389
+ }));
390
+ }
391
+ #getMediaBufferHashes(data) {
392
+ return {
393
+ sha256: createHash("sha256").update(data).digest("hex"),
394
+ md5: createHash("md5").update(data).digest("hex"),
395
+ sha1: createHash("sha1").update(data).digest("hex"),
396
+ md5_10m: createHash("md5").update(data.subarray(0, 10002432)).digest("hex")
397
+ };
398
+ }
399
+ async #getFilePartHash(filePath, start, length) {
400
+ const md5 = createHash("md5");
401
+ for await (const chunk of createReadStream(filePath, {
402
+ start,
403
+ end: start + length - 1
404
+ })) md5.update(chunk);
405
+ return md5.digest("hex");
406
+ }
407
+ async #retry(operation, attempts = 3) {
408
+ let lastError;
409
+ for (let attempt = 0; attempt < attempts; attempt++) try {
410
+ return await operation();
411
+ } catch (error) {
412
+ lastError = error;
413
+ if (attempt + 1 < attempts) await new Promise((resolve) => setTimeout(resolve, 250 * 2 ** attempt));
414
+ }
415
+ throw lastError;
416
+ }
417
+ /**
418
+ *
419
+ * @param openid
420
+ * @param message_id
421
+ * @returns
422
+ */
423
+ userMessageDelete(openid, message_id) {
424
+ return this.groupService({
425
+ url: `/v2/users/${openid}/messages/${message_id}`,
426
+ method: "delete"
427
+ });
428
+ }
429
+ /**
430
+ * 群聊-撤回消息
431
+ * @param group_openid 群 OpenID
432
+ * @param message_id 消息 ID
433
+ */
434
+ groupMessageDelete(group_openid, message_id) {
435
+ return this.groupService({
436
+ url: `/v2/groups/${group_openid}/messages/${message_id}`,
437
+ method: "delete"
438
+ });
439
+ }
440
+ /**
441
+ * @deprecated 方法名拼写错误,请使用 groupMessageDelete 代替
442
+ * @param group_openid 群 OpenID
443
+ * @param message_id 消息 ID
444
+ */
445
+ grouMessageDelte(group_openid, message_id) {
446
+ return this.groupMessageDelete(group_openid, message_id);
447
+ }
448
+ /**
449
+ * 获取群基本信息
450
+ * @param group_openid 群 OpenID
451
+ */
452
+ groupsInfo(group_openid) {
453
+ return this.groupService({
454
+ url: `/v2/groups/${group_openid}/info`,
455
+ method: "get"
456
+ });
457
+ }
458
+ /**
459
+ * 获取机器人群内状态
460
+ * @param group_openid 群 OpenID
461
+ */
462
+ groupsBotState(group_openid) {
463
+ return this.groupService({
464
+ url: `/v2/groups/${group_openid}/bot_state`,
465
+ method: "get"
466
+ });
467
+ }
468
+ /**
469
+ * 获取群成员详情
470
+ * @param group_openid 群 OpenID
471
+ * @param member_openid 群成员的 openid
472
+ */
473
+ groupsMembersMessage(group_openid, member_openid) {
474
+ return this.groupService({
475
+ url: `/v2/groups/${group_openid}/members/${member_openid}`,
476
+ method: "get"
477
+ });
478
+ }
479
+ /**
480
+ * 拉取入群申请列表
481
+ * @param group_openid 群 OpenID
482
+ * @param params 分页参数
483
+ */
484
+ groupsJoinRequestList(group_openid, params) {
485
+ return this.groupService({
486
+ url: `/v2/groups/${group_openid}/join_request_list`,
487
+ method: "get",
488
+ params
489
+ });
490
+ }
491
+ /**
492
+ * 审批入群申请
493
+ * @param group_openid 群 OpenID
494
+ * @param member_openid 成员 OpenID
495
+ * @param data 审批参数
496
+ */
497
+ groupsApprovalJoinRequest(group_openid, member_openid, data) {
498
+ return this.groupService({
499
+ url: `/v2/groups/${group_openid}/approval_join_request/${member_openid}`,
500
+ method: "post",
501
+ data
502
+ });
503
+ }
504
+ /**
505
+ * 查询群禁言状态
506
+ * @param group_openid 群 OpenID
507
+ */
508
+ groupsRestrictChatSetting(group_openid) {
509
+ return this.groupService({
510
+ url: `/v2/groups/${group_openid}/restrict_chat_setting`,
511
+ method: "get"
512
+ });
513
+ }
514
+ /**
515
+ * 设置群成员禁言
516
+ * @param group_openid 群 OpenID
517
+ * @param data 禁言设置
518
+ */
519
+ groupsRestrictChatSettingPost(group_openid, data) {
520
+ return this.groupService({
521
+ url: `/v2/groups/${group_openid}/restrict_chat_setting`,
522
+ method: "post",
523
+ data
524
+ });
525
+ }
526
+ /**
527
+ * 查询入群自动审批策略列表
528
+ * @param params 分页参数
529
+ */
530
+ groupsJoinApprovalStrategies(params) {
531
+ return this.groupService({
532
+ url: "/v2/groups/join_approval_strategy",
533
+ method: "get",
534
+ params
535
+ });
536
+ }
537
+ /**
538
+ * 创建入群自动审批策略
539
+ * @param data 策略参数
540
+ */
541
+ groupsJoinApprovalStrategyCreate(data) {
542
+ return this.groupService({
543
+ url: "/v2/groups/join_approval_strategy",
544
+ method: "post",
545
+ data
546
+ });
547
+ }
548
+ /**
549
+ * 修改入群自动审批策略
550
+ * @param strategy_id 策略 ID
551
+ * @param data 修改参数
552
+ */
553
+ groupsJoinApprovalStrategyPatch(strategy_id, data) {
554
+ return this.groupService({
555
+ url: `/v2/groups/join_approval_strategy/${strategy_id}`,
556
+ method: "patch",
557
+ data
558
+ });
559
+ }
560
+ /**
561
+ * 删除入群自动审批策略
562
+ * @param strategy_id 策略 ID
563
+ */
564
+ groupsJoinApprovalStrategyDelete(strategy_id) {
565
+ return this.groupService({
566
+ url: `/v2/groups/join_approval_strategy/${strategy_id}`,
567
+ method: "delete"
568
+ });
569
+ }
570
+ /**
571
+ * 执行入群自动审批策略
572
+ * @param strategy_id 策略 ID
573
+ */
574
+ groupsJoinApprovalStrategyExecute(strategy_id) {
575
+ return this.groupService({
576
+ url: `/v2/groups/join_approval_strategy/${strategy_id}/execute`,
577
+ method: "post"
578
+ });
579
+ }
580
+ /**
581
+ * 修改入群自动审批策略白名单
582
+ * @param strategy_id 策略 ID
583
+ * @param data 白名单参数
584
+ */
585
+ groupsJoinApprovalStrategyWhitelistUsers(strategy_id, data) {
586
+ return this.groupService({
587
+ url: `/v2/groups/join_approval_strategy/${strategy_id}/whitelist_users`,
588
+ method: "post",
589
+ data
590
+ });
591
+ }
592
+ /**
593
+ * 单聊-分片上传准备
594
+ * @param userId 用户 OpenID
595
+ * @param data 上传准备参数
596
+ */
597
+ usersUploadPrepare(userId, data) {
598
+ return this.groupService({
599
+ url: `/v2/users/${userId}/upload_prepare`,
600
+ method: "post",
601
+ data
602
+ });
603
+ }
604
+ /**
605
+ * 群聊-分片上传准备
606
+ * @param groupId 群 OpenID
607
+ * @param data 上传准备参数
608
+ */
609
+ groupUploadPrepare(groupId, data) {
610
+ return this.groupService({
611
+ url: `/v2/groups/${groupId}/upload_prepare`,
612
+ method: "post",
613
+ data
614
+ });
615
+ }
616
+ /**
617
+ * 单聊-分片完成
618
+ * @param userId 用户 OpenID
619
+ * @param data 分片完成参数
620
+ */
621
+ usersUploadPartFinish(userId, data) {
622
+ return this.groupService({
623
+ url: `/v2/users/${userId}/upload_part_finish`,
624
+ method: "post",
625
+ data
626
+ });
627
+ }
628
+ /**
629
+ * 群聊-分片完成
630
+ * @param groupId 群 OpenID
631
+ * @param data 分片完成参数
632
+ */
633
+ groupUploadPartFinish(groupId, data) {
634
+ return this.groupService({
635
+ url: `/v2/groups/${groupId}/upload_part_finish`,
636
+ method: "post",
637
+ data
638
+ });
639
+ }
640
+ /**
641
+ * 分片字节直传(COS 预签名地址)
642
+ * 注意:不能用 groupService,预签名地址已自带鉴权,
643
+ * 额外携带 QQ 鉴权头会导致 COS 签名校验失败
644
+ * @param presignedUrl 预签名上传地址
645
+ * @param part 分片内容
646
+ */
647
+ uploadPartDirect(presignedUrl, part) {
648
+ return axios.put(presignedUrl, part);
649
+ }
650
+ /**
651
+ * 单聊-流式消息
652
+ * @param userId 用户 OpenID
653
+ * @param data 流式消息参数
654
+ */
655
+ streamMessages(userId, data) {
656
+ return this.groupService({
657
+ url: `/v2/users/${userId}/stream_messages`,
658
+ method: "post",
659
+ data
660
+ });
661
+ }
662
+ /**
663
+ * ************
664
+ * 消息-图片接口
665
+ * ***********
666
+ */
667
+ /**
668
+ *
669
+ * @param channel_id
670
+ * @param message
671
+ * @param image
672
+ * @returns
673
+ */
674
+ async channelsMessages(channel_id, message, image) {
675
+ const formdata = new FormData();
676
+ for (const key in message) if (message[key] !== void 0) formdata.append(key, message[key]);
677
+ if (image) {
678
+ const from = await createPicFrom({ image });
679
+ if (from) {
680
+ const { picData, name } = from;
681
+ formdata.append("file_image", picData, name);
682
+ }
683
+ }
684
+ const dary = formdata.getBoundary();
685
+ return this.guildServer({
686
+ method: "post",
687
+ url: `/channels/${channel_id}/messages`,
688
+ headers: { "Content-Type": `multipart/form-data; boundary=${dary}` },
689
+ data: formdata
690
+ });
691
+ }
692
+ /**
693
+ * 私聊发送
694
+ * @param id 私信传频道id,公信传子频道id
695
+ * @param message {消息编号,图片,内容}
696
+ * @returns
697
+ */
698
+ async dmsMessages(guild_id, message, image) {
699
+ const formdata = new FormData();
700
+ for (const key in message) if (message[key] !== void 0) formdata.append(key, message[key]);
701
+ if (image) {
702
+ const from = await createPicFrom({ image });
703
+ if (from) {
704
+ const { picData, name } = from;
705
+ formdata.append("file_image", picData, name);
706
+ }
707
+ }
708
+ const dary = formdata.getBoundary();
709
+ return this.guildServer({
710
+ method: "post",
711
+ url: `/dms/${guild_id}/messages`,
712
+ headers: { "Content-Type": `multipart/form-data; boundary=${dary}` },
713
+ data: formdata
714
+ });
715
+ }
716
+ /**
717
+ * ********
718
+ * 用户api
719
+ * *******
720
+ */
721
+ /**
722
+ * 获取用户详情
723
+ * @param message
724
+ * @returns
725
+ */
726
+ usersMe() {
727
+ return this.guildServer({
728
+ method: "get",
729
+ url: "/users/@me"
730
+ });
731
+ }
732
+ /**
733
+ * 获取用户频道列表
734
+ * @param message
735
+ * @returns
736
+ */
737
+ usersMeGuilds(params) {
738
+ return this.guildServer({
739
+ method: "get",
740
+ url: "/users/@me/guilds",
741
+ params
742
+ });
743
+ }
744
+ /**
745
+ * **********
746
+ * 频道api
747
+ * **********
748
+ */
749
+ /**
750
+ * 获取频道详细
751
+ * @param guild_id
752
+ * @returns
753
+ */
754
+ guilds(guild_id) {
755
+ return this.guildServer({
756
+ method: "get",
757
+ url: `/guilds/${guild_id}`
758
+ });
759
+ }
760
+ /**
761
+ * ************
762
+ * 子频道api
763
+ * ***********
764
+ */
765
+ /**
766
+ * 获取子频道列表
767
+ * @param guild_id
768
+ * @returns
769
+ */
770
+ guildsChannels(guild_id) {
771
+ return this.guildServer({
772
+ method: "get",
773
+ url: `/guilds/${guild_id}/channels`
774
+ });
775
+ }
776
+ /**
777
+ * 获取子频道详情
778
+ * @param channel_id
779
+ * @returns
780
+ */
781
+ channels(channel_id) {
782
+ return this.guildServer({
783
+ method: "get",
784
+ url: `/channels/${channel_id}`
785
+ });
786
+ }
787
+ /**
788
+ * 创建子频道
789
+ * @param guild_id
790
+ * @returns
791
+ */
792
+ guildsChannelsCreate(guild_id, data) {
793
+ return this.guildServer({
794
+ method: "post",
795
+ url: `/guilds/${guild_id}/channels`,
796
+ data
797
+ });
798
+ }
799
+ /**
800
+ * 创建子频道
801
+ * @param channel_id
802
+ * @returns
803
+ */
804
+ guildsChannelsUpdate(channel_id, data) {
805
+ return this.guildServer({
806
+ method: "PATCH",
807
+ url: `/channels/${channel_id}`,
808
+ data
809
+ });
810
+ }
811
+ /**
812
+ * 删除子频道
813
+ * @param channel_id
814
+ * @param data
815
+ * @returns
816
+ */
817
+ guildsChannelsdelete(channel_id, data) {
818
+ return this.guildServer({
819
+ method: "DELETE",
820
+ url: `/channels/${channel_id}`,
821
+ data
822
+ });
823
+ }
824
+ /**
825
+ * 获取在线成员数
826
+ * @param channel_id
827
+ * @returns
828
+ */
829
+ channelsChannelOnlineNums(channel_id) {
830
+ return this.guildServer({
831
+ method: "GET",
832
+ url: `/channels/${channel_id}/online_nums`
833
+ });
834
+ }
835
+ /**
836
+ * *********
837
+ * 成员api
838
+ * *********
839
+ */
840
+ /**
841
+ * 获取频道成员列表
842
+ * @param guild_id
843
+ * @returns
844
+ */
845
+ guildsMembers(guild_id, params) {
846
+ return this.guildServer({
847
+ method: "GET",
848
+ url: `/guilds/${guild_id}/members`,
849
+ params
850
+ });
851
+ }
852
+ /**
853
+ * 获取频道身份组成员列表
854
+ * @param guild_id
855
+ * @param role_id
856
+ * @param params
857
+ * @returns
858
+ */
859
+ guildsRolesMembers(guild_id, role_id, params) {
860
+ return this.guildServer({
861
+ method: "GET",
862
+ url: `/guilds/${guild_id}/roles/${role_id}/members`,
863
+ params
864
+ });
865
+ }
866
+ /**
867
+ * 获取成员详情
868
+ * @param guild_id
869
+ * @param user_id
870
+ * @returns
871
+ */
872
+ guildsMembersMessage(guild_id, user_id) {
873
+ return this.guildServer({
874
+ method: "GET",
875
+ url: `/guilds/${guild_id}/members/${user_id}`
876
+ });
877
+ }
878
+ /**
879
+ * 删除频道成员
880
+ * @param guild_id
881
+ * @param user_id
882
+ * @returns
883
+ */
884
+ guildsMembersDelete(guild_id, user_id) {
885
+ return this.guildServer({
886
+ method: "DELETE",
887
+ url: `/guilds/${guild_id}/members/${user_id}`
888
+ });
889
+ }
890
+ /**
891
+ * 获取指定消息
892
+ * @param channel_id
893
+ * @param message_id
894
+ * @returns
895
+ */
896
+ channelsMessagesById(channel_id, message_id) {
897
+ return this.guildServer({
898
+ method: "GET",
899
+ url: `/channels/${channel_id}/messages/${message_id}`
900
+ });
901
+ }
902
+ /**
903
+ * 撤回消息
904
+ * @param channel_id
905
+ * @param message_id
906
+ * @param hidetip
907
+ * @returns
908
+ */
909
+ channelsMessagesDelete(channel_id, message_id, hidetip = true) {
910
+ return this.guildServer({
911
+ method: "DELETE",
912
+ url: `/channels/${channel_id}/messages/${message_id}?hidetip=${hidetip}`
913
+ });
914
+ }
915
+ /**
916
+ * ***********
917
+ * 频道身份api
918
+ * ***********
919
+ */
920
+ /**
921
+ * 获取频道身份组列表
922
+ * @param guild_id 频道id
923
+ * @returns
924
+ */
925
+ guildsRoles(guild_id) {
926
+ return this.guildServer({
927
+ method: "GET",
928
+ url: `/guilds/${guild_id}/roles`
929
+ });
930
+ }
931
+ /**
932
+ * 创建频道身份组
933
+ * @param guild_id 频道id
934
+ * @param {object} data 参数
935
+ * @param {object?} data.name 身份组名称
936
+ * @param {object?} data.color ARGB 的 HEX 十六进制颜色值转换后的十进制数值
937
+ * @param {object?} data.hoist 在成员列表中单独展示: 0-否, 1-是
938
+ * @returns
939
+ */
940
+ guildsRolesPost(guild_id, data) {
941
+ return this.guildServer({
942
+ method: "POST",
943
+ url: `/guilds/${guild_id}/roles`,
944
+ data
945
+ });
946
+ }
947
+ /**
948
+ * 修改频道身份组
949
+ * @param guild_id 频道id
950
+ * @param {object} data 参数
951
+ * @param {object?} data.name 身份组名称
952
+ * @param {object?} data.color ARGB 的 HEX 十六进制颜色值转换后的十进制数值
953
+ * @param {object?} data.hoist 在成员列表中单独展示: 0-否, 1-是
954
+ * @returns
955
+ */
956
+ guildsRolesPatch(guild_id, role_id, data) {
957
+ return this.guildServer({
958
+ method: "PATCH",
959
+ url: `/guilds/${guild_id}/roles/${role_id}`,
960
+ data
961
+ });
962
+ }
963
+ /**
964
+ * 删除频道身份组
965
+ * @param guild_id 频道id
966
+ * @param role_id 身份组id
967
+ */
968
+ guildsRolesDelete(guild_id, role_id) {
969
+ return this.guildServer({
970
+ method: "DELETE",
971
+ url: `/guilds/${guild_id}/roles/${role_id}`
972
+ });
973
+ }
974
+ /**
975
+ * 将成员添加到频道身份组
976
+ * @param guild_id 频道id
977
+ * @param channel_id 子频道id
978
+ * @param user_id 用户id
979
+ * @param role_id 身份组id
980
+ * @returns
981
+ */
982
+ guildsRolesMembersPut(guild_id, channel_id, user_id, role_id) {
983
+ return this.guildServer({
984
+ method: "PUT",
985
+ url: `/guilds/${guild_id}/members/${user_id}/roles/${role_id}`,
986
+ data: { channel: { id: channel_id } }
987
+ });
988
+ }
989
+ /**
990
+ * 将成员从频道身份组移除
991
+ * @param guild_id 频道id
992
+ * @param channel_id 子频道id
993
+ * @param user_id 用户id
994
+ * @param role_id 身份组id
995
+ * @returns
996
+ */
997
+ guildsRolesMembersDelete(guild_id, channel_id, user_id, role_id) {
998
+ return this.guildServer({
999
+ method: "DELETE",
1000
+ url: `/guilds/${guild_id}/members/${user_id}/roles/${role_id}`,
1001
+ data: { channel: { id: channel_id } }
1002
+ });
1003
+ }
1004
+ /**
1005
+ * **********
1006
+ * 子频道权限api
1007
+ * **********
1008
+ */
1009
+ /**
1010
+ * 获取子频道用户权限
1011
+ * @param channel_id 子频道id
1012
+ * @param user_id 用户id
1013
+ */
1014
+ channelsPermissions(channel_id, user_id) {
1015
+ return this.guildServer({
1016
+ method: "GET",
1017
+ url: `/channels/${channel_id}/members/${user_id}/permissions`
1018
+ });
1019
+ }
1020
+ /**
1021
+ * 修改子频道用户权限
1022
+ * @param channel_id 子频道id
1023
+ * @param user_id 用户id
1024
+ * @param 参数包括add和remove两个字段分别表示授予的权限以及删除的权限。要授予用户权限即把add对应位置 1,删除用户权限即把remove对应位置 1。当两个字段同一位都为 1,表现为删除权限。
1025
+ */
1026
+ channelsPermissionsPut(channel_id, user_id, add, remove) {
1027
+ return this.guildServer({
1028
+ method: "PUT",
1029
+ url: `/channels/${channel_id}/members/${user_id}/permissions`,
1030
+ data: {
1031
+ add,
1032
+ remove
1033
+ }
1034
+ });
1035
+ }
1036
+ /**
1037
+ * *******
1038
+ * 消息api
1039
+ * ********
1040
+ */
1041
+ /**
1042
+ * ************
1043
+ * 消息频率api
1044
+ * **********
1045
+ */
1046
+ /**
1047
+ * 查询频道消息频率限制
1048
+ * @param guild_id 频道id
1049
+ * @returns
1050
+ */
1051
+ guildsMessageSetting(guild_id) {
1052
+ return this.guildServer({
1053
+ method: "GET",
1054
+ url: `/guilds/${guild_id}/message/setting`
1055
+ });
1056
+ }
1057
+ /**
1058
+ * ***********
1059
+ * 私信api
1060
+ * **********
1061
+ */
1062
+ /**
1063
+ * 创建私信会话
1064
+ * @param recipient_id 接收者 id
1065
+ * @param source_guild_id 源频道 id
1066
+ * @returns
1067
+ */
1068
+ usersMeDms() {
1069
+ return this.guildServer({
1070
+ method: "POST",
1071
+ url: "/users/@me/dms"
1072
+ });
1073
+ }
1074
+ /**
1075
+ * 撤回私信
1076
+ * @param guild_id
1077
+ * @param data
1078
+ * @returns
1079
+ */
1080
+ dmsMessageDelete(guild_id, message_id, hidetip = true) {
1081
+ return this.guildServer({
1082
+ method: "DELETE",
1083
+ url: `/dms/${guild_id}/messages/${message_id}?hidetip=${hidetip}`
1084
+ });
1085
+ }
1086
+ /**
1087
+ * *********
1088
+ * 禁言api
1089
+ * *******
1090
+ */
1091
+ /**
1092
+ * 全体禁言(非管理员)
1093
+ * @param guild_id 频道id
1094
+ * @param data { mute_end_timestamp:禁言结束时间戳, mute_seconds:禁言时长 } 两个参数必须传一个 优先级 mute_end_timestamp > mute_seconds
1095
+ * 将mute_end_timestamp或mute_seconds传值为字符串'0',则表示解除全体禁言
1096
+ */
1097
+ guildsMuteAll(guild_id, data) {
1098
+ return this.guildServer({
1099
+ method: "PATCH",
1100
+ url: `/guilds/${guild_id}/mute`,
1101
+ data
1102
+ });
1103
+ }
1104
+ /**
1105
+ * 频道指定成员禁言
1106
+ * @param guild_id 频道id
1107
+ * @param user_id 用户id
1108
+ * @param data { mute_end_timestamp:禁言结束时间戳, mute_seconds:禁言时长 } 两个参数必须传一个 优先级 mute_end_timestamp > mute_seconds
1109
+ * 将mute_end_timestamp或mute_seconds传值为字符串'0',则表示解除禁言
1110
+ * @returns
1111
+ */
1112
+ guildsMemberMute(guild_id, user_id, data) {
1113
+ return this.guildServer({
1114
+ method: "PATCH",
1115
+ url: `/guilds/${guild_id}/members/${user_id}/mute`,
1116
+ data
1117
+ });
1118
+ }
1119
+ /**
1120
+ * 频道批量禁言
1121
+ * @param guild_id 频道id
1122
+ * @param data { mute_end_timestamp:禁言结束时间戳, mute_seconds:禁言时长, user_ids:用户id数组 } 两个参数必须传一个 优先级 mute_end_timestamp > mute_seconds
1123
+ * 将mute_end_timestamp或mute_seconds传值为字符串'0',则表示解除禁言
1124
+ */
1125
+ guildsMute(guild_id, data) {
1126
+ return this.guildServer({
1127
+ method: "PATCH",
1128
+ url: `/guilds/${guild_id}/mute`,
1129
+ data
1130
+ });
1131
+ }
1132
+ /**
1133
+ * *******
1134
+ * 公告api
1135
+ * *******
1136
+ */
1137
+ /**
1138
+ * 创建频道公告
1139
+ * 公告类型分为 消息类型的频道公告 和 推荐子频道类型的频道公告
1140
+ * 详见 https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/announces/post_guild_announces.html#%E5%8A%9F%E8%83%BD%E6%8F%8F%E8%BF%B0
1141
+ * @param guild_id 频道id
1142
+ * @param data { message_id:消息id, channel_id:频道id, announces_type:公告类型, recommend_channels:推荐频道id数组 }
1143
+ * @param channel_id 子频道id 消息id存在时必须传
1144
+ * @param announces_type 0:成员公告 1:欢迎公告 默认为 0
1145
+ * @param recommend_channels 推荐频道id数组 "recommend_channels": [{ "channel_id": "xxxx","introduce": "推荐语" }]
1146
+ * @returns 返回Announces 对象 (https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/announces/model.html#Announces)
1147
+ */
1148
+ guildsAnnounces(guild_id, data) {
1149
+ return this.guildServer({
1150
+ method: "POST",
1151
+ url: `/guilds/${guild_id}/announces`,
1152
+ data
1153
+ });
1154
+ }
1155
+ /**
1156
+ * 删除频道公告
1157
+ * @param guild_id 频道id
1158
+ * @param message_id 消息id message_id 有值时,会校验 message_id 合法性,若不校验校验 message_id,请将 message_id 设置为 all
1159
+ * @returns
1160
+ */
1161
+ guildsAnnouncesDelete(guild_id, message_id) {
1162
+ return this.guildServer({
1163
+ method: "DELETE",
1164
+ url: `/guilds/${guild_id}/announces/${message_id}`
1165
+ });
1166
+ }
1167
+ /**
1168
+ * **********
1169
+ * 精华消息api
1170
+ * **********
1171
+ */
1172
+ /**
1173
+ * 添加精华消息
1174
+ * @param channel_id 频道id
1175
+ * @param message_id 消息id
1176
+ * @returns 返回 PinsMessage对象 { "guild_id": "xxxxxx", "channel_id": "xxxxxx", "message_ids": ["xxxxx"]}
1177
+ * @returns message_ids 为当前请求后子频道内所有精华消息 message_id 数组
1178
+ */
1179
+ channelsPinsPut(channel_id, message_id) {
1180
+ return this.guildServer({
1181
+ method: "PUT",
1182
+ url: `/channels/${channel_id}/pins/${message_id}`
1183
+ });
1184
+ }
1185
+ /**
1186
+ * 删除精华消息
1187
+ * @param channel_id 子频道id
1188
+ * @param message_id 消息id
1189
+ * 删除子频道内全部精华消息,请将 message_id 设置为 all
1190
+ * @returns
1191
+ */
1192
+ channelsPinsDelete(channel_id, message_id) {
1193
+ return this.guildServer({
1194
+ method: "DELETE",
1195
+ url: `/channels/${channel_id}/pins/${message_id}`
1196
+ });
1197
+ }
1198
+ /**
1199
+ * 获取精华消息
1200
+ * @param channel_id 子频道id
1201
+ * @returns 返回 PinsMessage对象 { "guild_id": "xxxxxx", "channel_id": "xxxxxx", "message_ids": ["xxxxx"]}
1202
+ * @returns message_ids 为当前请求后子频道内所有精华消息 message_id 数组
1203
+ */
1204
+ channelsPins(channel_id) {
1205
+ return this.guildServer({
1206
+ method: "GET",
1207
+ url: `/channels/${channel_id}/pins`
1208
+ });
1209
+ }
1210
+ /**
1211
+ * ********
1212
+ * 日程api
1213
+ * *******
1214
+ */
1215
+ /**
1216
+ * 获取频道日程列表
1217
+ * @param channel_id 子频道id
1218
+ * @returns 返回 Schedule 对象数组(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/schedule/model.html#schedule)
1219
+ */
1220
+ channelsSchedules(channel_id) {
1221
+ return this.guildServer({
1222
+ method: "GET",
1223
+ url: `/channels/${channel_id}/schedules`
1224
+ });
1225
+ }
1226
+ /**
1227
+ * 获取频道日程详情
1228
+ * @param channel_id 子频道id
1229
+ * @param schedule_id 日程id
1230
+ * @returns 返回 Schedule 对象(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/schedule/model.html#schedule)
1231
+ */
1232
+ channelsSchedulesSchedule(channel_id, schedule_id) {
1233
+ return this.guildServer({
1234
+ method: "GET",
1235
+ url: `/channels/${channel_id}/schedules/${schedule_id}`
1236
+ });
1237
+ }
1238
+ /**
1239
+ * 创建频道日程
1240
+ * @param channel_id 子频道id
1241
+ * @param name 日程名称
1242
+ * @param description 日程描述
1243
+ * @param start_timestamp 日程开始时间戳
1244
+ * @param end_timestamp 日程结束时间戳
1245
+ * @param jump_channel_id 日程开始时跳转的子频道id
1246
+ * @param remind_type 日程提醒类型
1247
+ * 0不提醒
1248
+ * 1开始时提醒
1249
+ * 2开始前 5 分钟提醒
1250
+ * 3开始前 15 分钟提醒
1251
+ * 4开始前 30 分钟提醒
1252
+ * 5开始前 60 分钟提醒
1253
+ * @returns 返回 Schedule 对象(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/schedule/model.html#schedule)
1254
+ */
1255
+ channelsSchedulesPost(channel_id, data) {
1256
+ return this.guildServer({
1257
+ method: "POST",
1258
+ url: `/channels/${channel_id}/schedules`,
1259
+ data
1260
+ });
1261
+ }
1262
+ /**
1263
+ * 修改频道日程
1264
+ * @param channel_id 子频道id
1265
+ * @param schedule_id 日程id
1266
+ * @param name 日程名称
1267
+ * @param description 日程描述
1268
+ * @param start_timestamp 日程开始时间戳
1269
+ * @param end_timestamp 日程结束时间戳
1270
+ * @param jump_channel_id 日程开始时跳转的子频道id
1271
+ * @param remind_type 日程提醒类型
1272
+ * 0不提醒
1273
+ * 1开始时提醒
1274
+ * 2开始前 5 分钟提醒
1275
+ * 3开始前 15 分钟提醒
1276
+ * 4开始前 30 分钟提醒
1277
+ * 5开始前 60 分钟提醒
1278
+ * @returns 返回 Schedule 对象(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/schedule/model.html#schedule)
1279
+ */
1280
+ channelsSchedulesSchedulePatch(channel_id, schedule_id, data) {
1281
+ return this.guildServer({
1282
+ method: "PATCH",
1283
+ url: `/channels/${channel_id}/schedules/${schedule_id}`,
1284
+ data
1285
+ });
1286
+ }
1287
+ /**
1288
+ * 删除频道日程
1289
+ * @param channel_id 子频道id
1290
+ * @param schedule_id 日程id
1291
+ * @returns
1292
+ */
1293
+ channelsSchedulesScheduleDelete(channel_id, schedule_id) {
1294
+ return this.guildServer({
1295
+ method: "DELETE",
1296
+ url: `/channels/${channel_id}/schedules/${schedule_id}`
1297
+ });
1298
+ }
1299
+ /**
1300
+ * ***********
1301
+ * 表情表态api
1302
+ * ***********
1303
+ */
1304
+ /**
1305
+ * 机器人发表表情表态
1306
+ * @param channel_id 子频道id
1307
+ * @param message_id 消息id
1308
+ * @param type 表情类型 1:系统表情 2:emoji表情
1309
+ * @param id 表情id 参考https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#Emoji%20%E5%88%97%E8%A1%A8
1310
+ * @returns
1311
+ */
1312
+ channelsMessagesReactionsPut(channel_id, message_id, type, id) {
1313
+ return this.guildServer({
1314
+ method: "PUT",
1315
+ url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`
1316
+ });
1317
+ }
1318
+ /**
1319
+ * 删除机器人发表的表情表态
1320
+ * @param channel_id 子频道id
1321
+ * @param message_id 消息id
1322
+ * @param type 表情类型 1:系统表情 2:emoji表情
1323
+ * @param id 表情id 参考https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#Emoji%20%E5%88%97%E8%A1%A8
1324
+ * @returns
1325
+ */
1326
+ channelsMessagesReactionsDelete(channel_id, message_id, type, id) {
1327
+ return this.guildServer({
1328
+ method: "DELETE",
1329
+ url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`
1330
+ });
1331
+ }
1332
+ /**
1333
+ * 获取消息表情表态的用户列表
1334
+ * @param channel_id 子频道id
1335
+ * @param message_id 消息id
1336
+ * @param type 表情类型 1:系统表情 2:emoji表情
1337
+ * @param id 表情id 参考https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#Emoji%20%E5%88%97%E8%A1%A8
1338
+ * @param {object} data
1339
+ * @param {object} data.cookie 返回的cookie 第一次请求不传,后续请求传上次返回的cookie
1340
+ * @param {object} data.limit 返回的用户数量 默认20 最大50
1341
+ * @returns data:{ users:User[], cookie:string,is_end:true|false }
1342
+ */
1343
+ channelsMessagesReactionsUsers(channel_id, message_id, type, id, data) {
1344
+ return this.guildServer({
1345
+ method: "GET",
1346
+ url: `/channels/${channel_id}/messages/${message_id}/reactions/${type}/${id}`,
1347
+ data
1348
+ });
1349
+ }
1350
+ /**
1351
+ * ***********
1352
+ * 音频api
1353
+ * 音频接口:仅限音频类机器人才能使用,后续会根据机器人类型自动开通接口权限,现如需调用,需联系平台申请权限
1354
+ * **********
1355
+ */
1356
+ /**
1357
+ * 音频控制
1358
+ * @param channel_id 子频道id
1359
+ * @param audio_url 音频url status为0时传
1360
+ * @param status 0:开始 1:暂停 2:继续 3:停止
1361
+ * @param text 状态文本(比如:简单爱-周杰伦),可选,status为0时传,其他操作不传
1362
+ * @returns
1363
+ */
1364
+ channelsAudioPost(channel_id, data) {
1365
+ return this.guildServer({
1366
+ method: "POST",
1367
+ url: `/channels/${channel_id}/audio`,
1368
+ data
1369
+ });
1370
+ }
1371
+ /**
1372
+ * 机器人上麦
1373
+ * @param channel_id 语音子频道id
1374
+ * @returns {}
1375
+ */
1376
+ channelsMicPut(channel_id) {
1377
+ return this.guildServer({
1378
+ method: "PUT",
1379
+ url: `/channels/${channel_id}/mic`
1380
+ });
1381
+ }
1382
+ /**
1383
+ * 机器人下麦
1384
+ * @param channel_id 语音子频道id
1385
+ * @returns {}
1386
+ */
1387
+ channelsMicDelete(channel_id) {
1388
+ return this.guildServer({
1389
+ method: "DELETE",
1390
+ url: `/channels/${channel_id}/mic`
1391
+ });
1392
+ }
1393
+ /**
1394
+ * **********
1395
+ * 帖子api
1396
+ * 注意
1397
+ * 公域机器人暂不支持申请,仅私域机器人可用,选择私域机器人后默认开通。
1398
+ * 注意: 开通后需要先将机器人从频道移除,然后重新添加,方可生效。
1399
+ * **********
1400
+ */
1401
+ /**
1402
+ * 获取帖子列表
1403
+ * @param channel_id 子频道id
1404
+ * @returns {threads:Thread[],is_finish:0|1}
1405
+ * @returns 返回 Thread 对象数组(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/forum/model.html#Thread)
1406
+ * @returns is_finish 为 1 时,表示已拉取完 为 0 时,表示未拉取完
1407
+ */
1408
+ channelsThreads(channel_id) {
1409
+ return this.guildServer({
1410
+ method: "GET",
1411
+ url: `/channels/${channel_id}/threads`
1412
+ });
1413
+ }
1414
+ /**
1415
+ * 获取帖子详情
1416
+ * @param channel_id 子频道id
1417
+ * @param thread_id 帖子id
1418
+ * @returns 返回 帖子详情对象(详见https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/forum/model.html#ThreadInfo)
1419
+ * 其中content字段可参考 https://bot.q.qq.com/wiki/develop/api-v2/server-inter/channel/content/forum/model.html#RichText
1420
+ */
1421
+ channelsThreadsThread(channel_id, thread_id) {
1422
+ return this.guildServer({
1423
+ method: "GET",
1424
+ url: `/channels/${channel_id}/threads/${thread_id}`
1425
+ });
1426
+ }
1427
+ /**
1428
+ * 发表帖子
1429
+ * @param channel_id 子频道id
1430
+ * @param title 帖子标题
1431
+ * @param content 帖子内容
1432
+ * @param format 帖子内容格式 1:纯文本 2:HTML 3:Markdown 4:JSON
1433
+ * @returns 返回 {task_id:string,create_time:string} 其中 task_id 为帖子id,create_time 发帖时间戳
1434
+ */
1435
+ channelsThreadsPut(channel_id, data) {
1436
+ return this.guildServer({
1437
+ method: "PUT",
1438
+ url: `/channels/${channel_id}/threads`,
1439
+ data
1440
+ });
1441
+ }
1442
+ /**
1443
+ * 删除帖子
1444
+ * @param channel_id 子频道id
1445
+ * @param thread_id 帖子id
1446
+ * @returns
1447
+ */
1448
+ channelsThreadsDelete(channel_id, thread_id) {
1449
+ return this.guildServer({
1450
+ method: "DELETE",
1451
+ url: `/channels/${channel_id}/threads/${thread_id}`
1452
+ });
1453
+ }
1454
+ /**
1455
+ * ********
1456
+ * 接口权限api
1457
+ * **********
1458
+ */
1459
+ /**
1460
+ * 获得频道可用权限列表
1461
+ * @param guild_id
1462
+ * @returns
1463
+ */
1464
+ guildApiPermission(guild_id) {
1465
+ return this.guildServer({ url: `/guilds/${guild_id}/api_permission` });
1466
+ }
1467
+ /**
1468
+ * 交互事件回应
1469
+ * @param interaction_id
1470
+ * @param code
1471
+ * @returns
1472
+ */
1473
+ interactionResponse(_mode, interaction_id, code) {
1474
+ return this.groupService({
1475
+ method: "PUT",
1476
+ url: `/interactions/${interaction_id}`,
1477
+ data: { code: code || 0 }
1478
+ });
1479
+ }
1480
+ };
894
1481
 
895
- export { API_URL, API_URL_SANDBOX, BOTS_API_RUL, QQBotAPI };
1482
+ //#endregion
1483
+ export { API_URL, API_URL_SANDBOX, BOTS_API_RUL, QQBotAPI };