@alemonjs/qq-bot 2.1.31 → 2.1.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/sdk/api.d.ts CHANGED
@@ -1,9 +1,51 @@
1
1
  import { type AxiosRequestConfig } from 'axios';
2
- import { ApiRequestData, FileType } from './typing.js';
3
- export declare const BOTS_API_RUL = "https://bots.qq.com";
2
+ import { ApiRequestData, FileType, GroupAction, SetMemberMuteState, StreamMessageData, UploadPartFinishData, UploadPrepareData } from './typing.js';
3
+ import { QQBotConfig } from './config.js';
4
+ export declare const BOTS_API_RUL = "https://api.bot.qq.com";
4
5
  export declare const API_URL_SANDBOX = "https://sandbox.api.sgroup.qq.com";
5
- export declare const API_URL = "https://api.sgroup.qq.com";
6
+ export declare const API_URL = "https://api.bot.qq.com";
7
+ export type QQBotConnectionState = 'idle' | 'connecting' | 'ready' | 'reconnecting' | 'offline' | 'stopped';
8
+ export type QQBotConnectionStatus = {
9
+ state: QQBotConnectionState;
10
+ transport: 'websocket' | 'webhook' | 'proxy' | null;
11
+ reconnectAttempts: number;
12
+ heartbeatAcknowledged: boolean;
13
+ sessionId?: string;
14
+ sequence?: number;
15
+ resumed: boolean;
16
+ lastError?: string;
17
+ };
18
+ export type QQBotMediaHashes = {
19
+ sha256: string;
20
+ md5: string;
21
+ sha1: string;
22
+ md5_10m: string;
23
+ };
6
24
  export declare class QQBotAPI {
25
+ #private;
26
+ protected readonly config: QQBotConfig;
27
+ constructor(options?: Record<string, unknown>);
28
+ getConnectionStatus(): QQBotConnectionStatus;
29
+ protected updateConnectionStatus(patch: Partial<QQBotConnectionStatus>): void;
30
+ setStreamLifecycleListener(listener?: (streamId: string) => void): void;
31
+ sendTyping(params: {
32
+ BotId?: string;
33
+ userOpenId: string;
34
+ msgId?: string;
35
+ durationSec?: number;
36
+ }): Promise<any>;
37
+ streamOpen(params: {
38
+ BotId?: string;
39
+ userOpenId: string;
40
+ msgId: string;
41
+ eventId?: string;
42
+ }): {
43
+ streamId: `${string}-${string}-${string}-${string}-${string}`;
44
+ };
45
+ streamUpdate(streamId: string, fullText: string): Promise<unknown>;
46
+ streamComplete(streamId: string): Promise<any>;
47
+ streamCancel(streamId: string): void;
48
+ protected cancelStreams(): void;
7
49
  getAuthentication(): Promise<any>;
8
50
  groupService(options: AxiosRequestConfig): Promise<any>;
9
51
  guildServer(options: AxiosRequestConfig): Promise<any>;
@@ -22,23 +64,94 @@ export declare class QQBotAPI {
22
64
  file_type: FileType;
23
65
  url?: string;
24
66
  file_data?: any;
67
+ upload_id?: string;
68
+ file_name?: string;
25
69
  }): Promise<{
26
70
  file_uuid: string;
27
71
  file_info: string;
28
72
  ttl: number;
73
+ id?: string;
74
+ raw_url?: string;
29
75
  }>;
30
76
  postRichMediaByGroup(openid: string, data: {
31
77
  srv_send_msg?: boolean;
32
78
  file_type: FileType;
33
79
  url?: string;
34
80
  file_data?: any;
81
+ upload_id?: string;
82
+ file_name?: string;
83
+ }): Promise<{
84
+ file_uuid: string;
85
+ file_info: string;
86
+ ttl: number;
87
+ id?: string;
88
+ raw_url?: string;
89
+ }>;
90
+ getMediaFileHashes(filePath: string): Promise<QQBotMediaHashes>;
91
+ postChunkedRichMedia(params: {
92
+ scope: 'group' | 'c2c';
93
+ targetId: string;
94
+ fileType: FileType;
95
+ data?: Buffer;
96
+ filePath?: string;
97
+ size?: number;
98
+ hashes?: QQBotMediaHashes;
99
+ name?: string;
100
+ onProgress?: (uploaded: number, total: number) => void;
35
101
  }): Promise<{
36
102
  file_uuid: string;
37
103
  file_info: string;
38
104
  ttl: number;
39
105
  }>;
40
106
  userMessageDelete(openid: string, message_id: string): Promise<any>;
107
+ groupMessageDelete(group_openid: string, message_id: string): Promise<any>;
41
108
  grouMessageDelte(group_openid: string, message_id: string): Promise<any>;
109
+ groupsInfo(group_openid: string): Promise<any>;
110
+ groupsBotState(group_openid: string): Promise<any>;
111
+ groupsMembersMessage(group_openid: string, member_openid: string): Promise<any>;
112
+ groupsJoinRequestList(group_openid: string, params?: {
113
+ cursor?: string;
114
+ limit?: number;
115
+ }): Promise<any>;
116
+ groupsApprovalJoinRequest(group_openid: string, member_openid: string, data: {
117
+ op: 'approve' | 'decline';
118
+ join_request_id?: string;
119
+ reject_reason?: string;
120
+ add_to_member_blacklist?: boolean;
121
+ }): Promise<any>;
122
+ groupsRestrictChatSetting(group_openid: string): Promise<any>;
123
+ groupsRestrictChatSettingPost(group_openid: string, data: {
124
+ members?: SetMemberMuteState[];
125
+ }): Promise<any>;
126
+ groupsJoinApprovalStrategies(params?: {
127
+ cursor?: string;
128
+ limit?: number;
129
+ }): Promise<any>;
130
+ groupsJoinApprovalStrategyCreate(data: {
131
+ group_openids?: string[];
132
+ group_ids?: string[];
133
+ is_enable?: 'on' | 'off';
134
+ expire_at?: string;
135
+ remark?: string;
136
+ }): Promise<any>;
137
+ groupsJoinApprovalStrategyPatch(strategy_id: string, data: {
138
+ is_enable?: 'on' | 'off';
139
+ expire_at?: string;
140
+ group_action?: GroupAction;
141
+ remark?: string;
142
+ }): Promise<any>;
143
+ groupsJoinApprovalStrategyDelete(strategy_id: string): Promise<any>;
144
+ groupsJoinApprovalStrategyExecute(strategy_id: string): Promise<any>;
145
+ groupsJoinApprovalStrategyWhitelistUsers(strategy_id: string, data: {
146
+ op: 'add' | 'del';
147
+ whitelist_users: string[];
148
+ }): Promise<any>;
149
+ usersUploadPrepare(userId: string, data: UploadPrepareData): Promise<any>;
150
+ groupUploadPrepare(groupId: string, data: UploadPrepareData): Promise<any>;
151
+ usersUploadPartFinish(userId: string, data: UploadPartFinishData): Promise<any>;
152
+ groupUploadPartFinish(groupId: string, data: UploadPartFinishData): Promise<any>;
153
+ uploadPartDirect(presignedUrl: string, part: Buffer): Promise<import("axios").AxiosResponse<any, any, {}>>;
154
+ streamMessages(userId: string, data: StreamMessageData): Promise<any>;
42
155
  channelsMessages(channel_id: string, message: {
43
156
  content?: string;
44
157
  embed?: any;
package/lib/sdk/api.js CHANGED
@@ -1,18 +1,154 @@
1
1
  import axios from 'axios';
2
- import { config } from './config.js';
2
+ import { QQBotConfig } from './config.js';
3
3
  import FormData from 'form-data';
4
4
  import { createPicFrom } from 'alemonjs/utils';
5
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';
6
9
 
7
- const BOTS_API_RUL = 'https://bots.qq.com';
10
+ const BOTS_API_RUL = 'https://api.bot.qq.com';
8
11
  const API_URL_SANDBOX = 'https://sandbox.api.sgroup.qq.com';
9
- const API_URL = 'https://api.sgroup.qq.com';
10
- const msgMap = new Map();
12
+ const API_URL = 'https://api.bot.qq.com';
11
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
+ }
12
148
  getAuthentication() {
13
- const app_id = config.get('app_id');
14
- const secret = config.get('secret');
15
- const baseUrlAppAccessToken = config.get('base_url_app_access_token');
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');
16
152
  const params = {
17
153
  url: '/app/getAppAccessToken'
18
154
  };
@@ -33,9 +169,9 @@ class QQBotAPI {
33
169
  });
34
170
  }
35
171
  groupService(options) {
36
- const app_id = config.get('app_id');
37
- const token = config.get('access_token');
38
- const sandbox = config.get('sandbox');
172
+ const app_id = this.config.get('app_id');
173
+ const token = this.config.get('access_token');
174
+ const sandbox = this.config.get('sandbox');
39
175
  const service = axios.create({
40
176
  baseURL: sandbox ? API_URL_SANDBOX : API_URL,
41
177
  timeout: 20000,
@@ -50,7 +186,7 @@ class QQBotAPI {
50
186
  return this.groupService(options);
51
187
  }
52
188
  gateway() {
53
- const baseUrlGateway = config.get('base_url_gateway');
189
+ const baseUrlGateway = this.config.get('base_url_gateway');
54
190
  const params = {
55
191
  url: '/gateway'
56
192
  };
@@ -75,13 +211,13 @@ class QQBotAPI {
75
211
  });
76
212
  }
77
213
  getMessageSeq(MessageId) {
78
- let seq = msgMap.get(MessageId) || 0;
214
+ let seq = this.#msgMap.get(MessageId) || 0;
79
215
  seq++;
80
- msgMap.set(MessageId, seq);
81
- if (msgMap.size > 100) {
82
- const firstKey = msgMap.keys().next().value;
216
+ this.#msgMap.set(MessageId, seq);
217
+ if (this.#msgMap.size > 100) {
218
+ const firstKey = this.#msgMap.keys().next().value;
83
219
  if (firstKey) {
84
- msgMap.delete(firstKey);
220
+ this.#msgMap.delete(firstKey);
85
221
  }
86
222
  }
87
223
  return seq;
@@ -102,6 +238,10 @@ class QQBotAPI {
102
238
  });
103
239
  }
104
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
+ }
105
245
  return this.groupService({
106
246
  url: `/v2/users/${openid}/files`,
107
247
  method: 'post',
@@ -109,6 +249,10 @@ class QQBotAPI {
109
249
  });
110
250
  }
111
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
+ }
112
256
  return this.groupService({
113
257
  url: `/v2/groups/${openid}/files`,
114
258
  method: 'post',
@@ -118,18 +262,247 @@ class QQBotAPI {
118
262
  }
119
263
  });
120
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
+ }
121
368
  userMessageDelete(openid, message_id) {
122
369
  return this.groupService({
123
370
  url: `/v2/users/${openid}/messages/${message_id}`,
124
371
  method: 'delete'
125
372
  });
126
373
  }
127
- grouMessageDelte(group_openid, message_id) {
374
+ groupMessageDelete(group_openid, message_id) {
128
375
  return this.groupService({
129
376
  url: `/v2/groups/${group_openid}/messages/${message_id}`,
130
377
  method: 'delete'
131
378
  });
132
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
+ }
133
506
  async channelsMessages(channel_id, message, image) {
134
507
  const formdata = new FormData();
135
508
  for (const key in message) {