@discordeno/rest 19.0.0-next.0b02607 → 19.0.0-next.0b93eed

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/dist/manager.js CHANGED
@@ -1,34 +1,50 @@
1
- /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import { InteractionResponseTypes } from '@discordeno/types';
1
+ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import { Buffer } from 'node:buffer';
2
2
  import { calculateBits, camelize, camelToSnakeCase, delay, getBotIdFromToken, logger, processReactionString, urlToBase64 } from '@discordeno/utils';
3
- import fetch from 'node-fetch';
4
3
  import { createInvalidRequestBucket } from './invalidBucket.js';
5
4
  import { Queue } from './queue.js';
5
+ import { InteractionResponseTypes } from '@discordeno/types';
6
6
  import { createRoutes } from './routes.js';
7
7
  // TODO: make dynamic based on package.json file
8
8
  const version = '19.0.0-alpha.1';
9
+ export const DISCORD_API_VERSION = 10;
10
+ export const DISCORD_API_URL = 'https://discord.com/api';
11
+ export const AUDIT_LOG_REASON_HEADER = 'x-audit-log-reason';
12
+ export const RATE_LIMIT_REMAINING_HEADER = 'x-ratelimit-remaining';
13
+ export const RATE_LIMIT_RESET_AFTER_HEADER = 'x-ratelimit-reset-after';
14
+ export const RATE_LIMIT_GLOBAL_HEADER = 'x-ratelimit-global';
15
+ export const RATE_LIMIT_BUCKET_HEADER = 'x-ratelimit-bucket';
16
+ export const RATE_LIMIT_LIMIT_HEADER = 'x-ratelimit-limit';
17
+ export const RATE_LIMIT_SCOPE_HEADER = 'x-ratelimit-scope';
9
18
  export function createRestManager(options) {
10
19
  const applicationId = options.applicationId ? BigInt(options.applicationId) : options.token ? getBotIdFromToken(options.token) : undefined;
11
20
  if (!applicationId) {
12
21
  throw new Error('`applicationId` was not provided and was not able to extract the id from the bots token. Please explicitly pass `applicationId` to the rest manager.');
13
22
  }
14
- const baseUrl = options.proxy?.baseUrl ?? 'https://discord.com/api';
23
+ const baseUrl = options.proxy?.baseUrl ?? DISCORD_API_URL;
15
24
  const rest = {
16
- token: options.token,
17
25
  applicationId,
18
- version: options.version ?? 10,
26
+ authorization: options.proxy?.authorization,
27
+ authorizationHeader: options.proxy?.authorizationHeader ?? 'authorization',
19
28
  baseUrl,
20
- isProxied: !baseUrl.startsWith('https://discord.com/api'),
21
- maxRetryCount: Infinity,
29
+ deleteQueueDelay: 60000,
22
30
  globallyRateLimited: false,
31
+ invalidBucket: createInvalidRequestBucket({}),
32
+ isProxied: !baseUrl.startsWith(DISCORD_API_URL),
33
+ maxRetryCount: Infinity,
23
34
  processingRateLimitedPaths: false,
24
- deleteQueueDelay: 60000,
25
35
  queues: new Map(),
26
36
  rateLimitedPaths: new Map(),
27
- invalidBucket: createInvalidRequestBucket({}),
28
- authorization: options.proxy?.authorization,
37
+ token: options.token,
38
+ version: options.version ?? DISCORD_API_VERSION,
29
39
  routes: createRoutes(),
30
- checkRateLimits (url) {
31
- const ratelimited = rest.rateLimitedPaths.get(url);
40
+ createBaseHeaders () {
41
+ return {
42
+ 'user-agent': `DiscordBot (https://github.com/discordeno/discordeno, v${version})`
43
+ };
44
+ },
45
+ checkRateLimits (url, headers) {
46
+ const authHeader = headers?.authorization ?? '';
47
+ const ratelimited = rest.rateLimitedPaths.get(`${authHeader}${url}`);
32
48
  const global = rest.rateLimitedPaths.get('global');
33
49
  const now = Date.now();
34
50
  if (ratelimited && now < ratelimited.resetTimestamp) {
@@ -47,20 +63,27 @@ export function createRestManager(options) {
47
63
  }
48
64
  const newObj = {};
49
65
  for (const key of Object.keys(obj)){
50
- // Keys that dont require snake casing
51
- if ([
52
- 'permissions',
53
- 'allow',
54
- 'deny'
55
- ].includes(key)) {
56
- newObj[key] = calculateBits(obj[key]);
57
- continue;
66
+ const value = obj[key];
67
+ // Some falsy values should be allowed like null or 0
68
+ if (value !== undefined) {
69
+ switch(key){
70
+ case 'permissions':
71
+ case 'allow':
72
+ case 'deny':
73
+ newObj[key] = typeof value === 'string' ? value : calculateBits(value);
74
+ continue;
75
+ case 'defaultMemberPermissions':
76
+ newObj.default_member_permissions = typeof value === 'string' ? value : calculateBits(value);
77
+ continue;
78
+ case 'nameLocalizations':
79
+ newObj.name_localizations = value;
80
+ continue;
81
+ case 'descriptionLocalizations':
82
+ newObj.description_localizations = value;
83
+ continue;
84
+ }
58
85
  }
59
- if (key === 'defaultMemberPermissions') {
60
- newObj.default_member_permissions = calculateBits(obj[key]);
61
- continue;
62
- }
63
- newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(obj[key]);
86
+ newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(value);
64
87
  }
65
88
  return newObj;
66
89
  }
@@ -68,13 +91,11 @@ export function createRestManager(options) {
68
91
  return obj;
69
92
  },
70
93
  createRequestBody (method, options) {
71
- const headers = {
72
- 'user-agent': `DiscordBot (https://github.com/discordeno/discordeno, v${version})`
73
- };
74
- if (options?.unauthorized !== false) headers.authorization = `Bot ${rest.token}`;
94
+ const headers = this.createBaseHeaders();
95
+ if (options?.unauthorized !== true) headers.authorization = `Bot ${rest.token}`;
75
96
  // IF A REASON IS PROVIDED ENCODE IT IN HEADERS
76
97
  if (options?.reason !== undefined) {
77
- headers['x-audit-log-reason'] = encodeURIComponent(options?.reason);
98
+ headers[AUDIT_LOG_REASON_HEADER] = encodeURIComponent(options?.reason);
78
99
  }
79
100
  let body;
80
101
  // TODO: check if we need to add specific check for GET method
@@ -85,12 +106,21 @@ export function createRestManager(options) {
85
106
  for(let i = 0; i < options.files.length; ++i){
86
107
  form.append(`file${i}`, options.files[i].blob, options.files[i].name);
87
108
  }
88
- form.append('payload_json', JSON.stringify({
109
+ // Have to use changeToDiscordFormat or else JSON.stringify may throw an error for the presence of BigInt(s) in the json
110
+ form.append('payload_json', JSON.stringify(rest.changeToDiscordFormat({
89
111
  ...options.body,
90
112
  files: undefined
91
- }));
113
+ })));
114
+ // No need to set the `content-type` header since `fetch` does that automatically for us when we use a `FormData` object.
92
115
  body = form;
93
- // No need to set the `content-type` header since `fetch` does that automatically for us when we use a `FormData` object.
116
+ } else if (options?.body && options.headers && options.headers['content-type'] === 'application/x-www-form-urlencoded') {
117
+ // OAuth2 body handling
118
+ const formBody = [];
119
+ const discordBody = rest.changeToDiscordFormat(options.body);
120
+ for(const prop in discordBody){
121
+ formBody.push(`${encodeURIComponent(prop)}=${encodeURIComponent(discordBody[prop])}`);
122
+ }
123
+ body = formBody.join('&');
94
124
  } else if (options?.body !== undefined) {
95
125
  if (options.body instanceof FormData) {
96
126
  body = options.body;
@@ -137,17 +167,17 @@ export function createRestManager(options) {
137
167
  }, 1000);
138
168
  }
139
169
  },
140
- /** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ processHeaders (url, headers) {
170
+ /** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ processHeaders (url, headers, requestAuthorization) {
141
171
  let rateLimited = false;
142
172
  // GET ALL NECESSARY HEADERS
143
- const remaining = headers.get('x-ratelimit-remaining');
144
- const retryAfter = headers.get('x-ratelimit-reset-after');
173
+ const remaining = headers.get(RATE_LIMIT_REMAINING_HEADER);
174
+ const retryAfter = headers.get(RATE_LIMIT_RESET_AFTER_HEADER);
145
175
  const reset = Date.now() + Number(retryAfter) * 1000;
146
- const global = headers.get('x-ratelimit-global');
176
+ const global = headers.get(RATE_LIMIT_GLOBAL_HEADER);
147
177
  // undefined override null needed for typings
148
- const bucketId = headers.get('x-ratelimit-bucket') ?? undefined;
149
- const limit = headers.get('x-ratelimit-limit');
150
- rest.queues.get(url)?.handleCompletedRequest({
178
+ const bucketId = headers.get(RATE_LIMIT_BUCKET_HEADER) ?? undefined;
179
+ const limit = headers.get(RATE_LIMIT_LIMIT_HEADER);
180
+ rest.queues.get(`${requestAuthorization}${url}`)?.handleCompletedRequest({
151
181
  remaining: remaining ? Number(remaining) : undefined,
152
182
  interval: retryAfter ? Number(retryAfter) * 1000 : undefined,
153
183
  max: limit ? Number(limit) : undefined
@@ -156,14 +186,14 @@ export function createRestManager(options) {
156
186
  if (remaining === '0') {
157
187
  rateLimited = true;
158
188
  // SAVE THE URL AS LIMITED, IMPORTANT FOR NEW REQUESTS BY USER WITHOUT BUCKET
159
- rest.rateLimitedPaths.set(url, {
189
+ rest.rateLimitedPaths.set(`${requestAuthorization}${url}`, {
160
190
  url,
161
191
  resetTimestamp: reset,
162
192
  bucketId
163
193
  });
164
194
  // SAVE THE BUCKET AS LIMITED SINCE DIFFERENT URLS MAY SHARE A BUCKET
165
195
  if (bucketId) {
166
- rest.rateLimitedPaths.set(bucketId, {
196
+ rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
167
197
  url,
168
198
  resetTimestamp: reset,
169
199
  bucketId
@@ -172,8 +202,8 @@ export function createRestManager(options) {
172
202
  }
173
203
  // IF THERE IS NO REMAINING GLOBAL LIMIT, MARK IT RATE LIMITED GLOBALLY
174
204
  if (global) {
175
- const retryAfter = headers.get('retry-after');
176
- const globalReset = Date.now() + Number(retryAfter) * 1000;
205
+ const retryAfter = Number(headers.get('retry-after')) * 1000;
206
+ const globalReset = Date.now() + retryAfter;
177
207
  // rest.debug(
178
208
  // `[REST = Globally Rate Limited] URL: ${url} | Global Rest: ${globalReset}`
179
209
  // )
@@ -181,14 +211,14 @@ export function createRestManager(options) {
181
211
  rateLimited = true;
182
212
  setTimeout(()=>{
183
213
  rest.globallyRateLimited = false;
184
- }, globalReset);
214
+ }, retryAfter);
185
215
  rest.rateLimitedPaths.set('global', {
186
216
  url: 'global',
187
217
  resetTimestamp: globalReset,
188
218
  bucketId
189
219
  });
190
220
  if (bucketId) {
191
- rest.rateLimitedPaths.set(bucketId, {
221
+ rest.rateLimitedPaths.set(`${requestAuthorization}${bucketId}`, {
192
222
  url: 'global',
193
223
  resetTimestamp: globalReset,
194
224
  bucketId
@@ -203,17 +233,33 @@ export function createRestManager(options) {
203
233
  async sendRequest (options) {
204
234
  const url = `${rest.baseUrl}/v${rest.version}${options.route}`;
205
235
  const payload = rest.createRequestBody(options.method, options.requestBodyOptions);
236
+ const loggingHeaders = {
237
+ ...payload.headers
238
+ };
239
+ const authenticationScheme = payload.headers.authorization?.split(' ')[0];
240
+ if (payload.headers.authorization) {
241
+ loggingHeaders.authorization = `${authenticationScheme} tokenhere`;
242
+ }
206
243
  logger.debug(`sending request to ${url}`, 'with payload:', {
207
244
  ...payload,
208
- headers: {
209
- ...payload.headers,
210
- authorization: 'Bot tokenhere'
211
- }
245
+ headers: loggingHeaders
246
+ });
247
+ const response = await fetch(url, payload).catch(async (error)=>{
248
+ logger.error(error);
249
+ // Mark request and completed
250
+ rest.invalidBucket.handleCompletedRequest(999, false);
251
+ options.reject({
252
+ ok: false,
253
+ status: 999,
254
+ error: 'Possible network or request shape issue occurred. If this is rare, its a network glitch. If it occurs a lot something is wrong.'
255
+ });
256
+ throw error;
212
257
  });
213
- const response = await fetch(url, payload);
214
258
  logger.debug(`request fetched from ${url} with status ${response.status} & ${response.statusText}`);
259
+ // Mark request and completed
260
+ rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get(RATE_LIMIT_SCOPE_HEADER) === 'shared');
215
261
  // Set the bucket id if it was available on the headers
216
- const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers);
262
+ const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers, authenticationScheme === 'Bearer' ? payload.headers.authorization : '');
217
263
  if (bucketId) options.bucketId = bucketId;
218
264
  if (response.status < 200 || response.status >= 400) {
219
265
  logger.debug(`Request to ${url} failed.`);
@@ -238,9 +284,7 @@ export function createRestManager(options) {
238
284
  return;
239
285
  }
240
286
  options.retryCount += 1;
241
- // Rate limited, add back to queue
242
- rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get('X-RateLimit-Scope') === 'shared');
243
- const resetAfter = response.headers.get('x-ratelimit-reset-after');
287
+ const resetAfter = response.headers.get(RATE_LIMIT_RESET_AFTER_HEADER);
244
288
  if (resetAfter) await delay(Number(resetAfter) * 1000);
245
289
  // process the response to prevent mem leak
246
290
  await response.arrayBuffer();
@@ -275,36 +319,35 @@ export function createRestManager(options) {
275
319
  return parts.join('/');
276
320
  },
277
321
  async processRequest (request) {
278
- const route = request.route.substring(request.route.indexOf('api/'));
279
- const parts = route.split('/');
280
- // Remove the api/
281
- parts.shift();
282
- // Removes the /v#/
283
- if (parts[0]?.startsWith('v')) parts.shift();
284
- // Set the full url to discord api in case it was recieved in a proxy rest
285
- request.route = `${rest.baseUrl}/v${rest.version}/${parts.join('/')}`;
286
322
  const url = rest.simplifyUrl(request.route, request.method);
287
323
  if (request.runThroughQueue === false) {
288
324
  await rest.sendRequest(request);
289
325
  return;
290
326
  }
291
- const queue = rest.queues.get(url);
327
+ const authHeader = request.requestBodyOptions?.headers?.authorization ?? '';
328
+ const queue = rest.queues.get(`${authHeader}${url}`);
292
329
  if (queue !== undefined) {
293
330
  queue.makeRequest(request);
294
331
  } else {
295
332
  // CREATES A NEW QUEUE
296
333
  const bucketQueue = new Queue(rest, {
297
334
  url,
298
- deleteQueueDelay: rest.deleteQueueDelay
335
+ deleteQueueDelay: rest.deleteQueueDelay,
336
+ authentication: authHeader
299
337
  });
300
338
  // Add request to queue
301
339
  bucketQueue.makeRequest(request);
302
340
  // Save queue
303
- rest.queues.set(url, bucketQueue);
341
+ rest.queues.set(`${authHeader}${url}`, bucketQueue);
304
342
  }
305
343
  },
306
344
  async makeRequest (method, route, options) {
307
345
  if (rest.isProxied) {
346
+ if (rest.authorization !== undefined) {
347
+ options ??= {};
348
+ options.headers ??= {};
349
+ options.headers[rest.authorizationHeader] = rest.authorization;
350
+ }
308
351
  const result = await fetch(`${rest.baseUrl}/v${rest.version}${route}`, rest.createRequestBody(method, options));
309
352
  if (!result.ok) {
310
353
  const err = await result.json().catch(()=>{});
@@ -371,6 +414,11 @@ export function createRestManager(options) {
371
414
  async addThreadMember (channelId, userId) {
372
415
  await rest.put(rest.routes.channels.threads.user(channelId, userId));
373
416
  },
417
+ async addDmRecipient (channelId, userId, body) {
418
+ await rest.put(rest.routes.channels.dmRecipient(channelId, userId), {
419
+ body
420
+ });
421
+ },
374
422
  async createAutomodRule (guildId, body, reason) {
375
423
  return await rest.post(rest.routes.guilds.automod.rules(guildId), {
376
424
  body,
@@ -389,20 +437,34 @@ export function createRestManager(options) {
389
437
  reason
390
438
  });
391
439
  },
392
- async createGlobalApplicationCommand (body) {
393
- return await rest.post(rest.routes.interactions.commands.commands(rest.applicationId), {
440
+ async createGlobalApplicationCommand (body, options) {
441
+ const restOptions = {
394
442
  body
395
- });
443
+ };
444
+ if (options?.bearerToken) {
445
+ restOptions.unauthorized = true;
446
+ restOptions.headers = {
447
+ authorization: `Bearer ${options.bearerToken}`
448
+ };
449
+ }
450
+ return await rest.post(rest.routes.interactions.commands.commands(rest.applicationId), restOptions);
396
451
  },
397
452
  async createGuild (body) {
398
453
  return await rest.post(rest.routes.guilds.all(), {
399
454
  body
400
455
  });
401
456
  },
402
- async createGuildApplicationCommand (body, guildId) {
403
- return await rest.post(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), {
457
+ async createGuildApplicationCommand (body, guildId, options) {
458
+ const restOptions = {
404
459
  body
405
- });
460
+ };
461
+ if (options?.bearerToken) {
462
+ restOptions.unauthorized = true;
463
+ restOptions.headers = {
464
+ authorization: `Bearer ${options.bearerToken}`
465
+ };
466
+ }
467
+ return await rest.post(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions);
406
468
  },
407
469
  async createGuildFromTemplate (templateCode, body) {
408
470
  if (body.icon) {
@@ -595,7 +657,7 @@ export function createRestManager(options) {
595
657
  },
596
658
  async editBotProfile (options) {
597
659
  const avatar = options?.botAvatarURL ? await urlToBase64(options?.botAvatarURL) : options?.botAvatarURL;
598
- return await rest.patch(rest.routes.userBot(), {
660
+ return await rest.patch(rest.routes.currentUser(), {
599
661
  body: {
600
662
  username: options.username?.trim(),
601
663
  avatar
@@ -669,7 +731,8 @@ export function createRestManager(options) {
669
731
  },
670
732
  async editMessage (channelId, messageId, body) {
671
733
  return await rest.patch(rest.routes.channels.message(channelId, messageId), {
672
- body
734
+ body,
735
+ files: body.files
673
736
  });
674
737
  },
675
738
  async editOriginalInteractionResponse (token, body) {
@@ -771,14 +834,62 @@ export function createRestManager(options) {
771
834
  async getActiveThreads (guildId) {
772
835
  return await rest.get(rest.routes.channels.threads.active(guildId));
773
836
  },
774
- async getApplicationCommandPermission (guildId, commandId) {
775
- return await rest.get(rest.routes.interactions.commands.permission(rest.applicationId, guildId, commandId));
776
- },
777
- async getApplicationCommandPermissions (guildId) {
778
- return await rest.get(rest.routes.interactions.commands.permissions(rest.applicationId, guildId));
837
+ async getApplicationCommandPermission (guildId, commandId, options) {
838
+ const restOptions = {};
839
+ if (options?.accessToken) {
840
+ restOptions.unauthorized = true;
841
+ restOptions.headers = {
842
+ authorization: `Bearer ${options.accessToken}`
843
+ };
844
+ }
845
+ return await rest.get(rest.routes.interactions.commands.permission(options?.applicationId ?? rest.applicationId, guildId, commandId), restOptions);
846
+ },
847
+ async getApplicationCommandPermissions (guildId, options) {
848
+ const restOptions = {};
849
+ if (options?.accessToken) {
850
+ restOptions.unauthorized = true;
851
+ restOptions.headers = {
852
+ authorization: `Bearer ${options.accessToken}`
853
+ };
854
+ }
855
+ return await rest.get(rest.routes.interactions.commands.permissions(options?.applicationId ?? rest.applicationId, guildId), restOptions);
779
856
  },
780
857
  async getApplicationInfo () {
781
- return await rest.get(rest.routes.oauth2Application());
858
+ return await rest.get(rest.routes.oauth2.application());
859
+ },
860
+ async getCurrentAuthenticationInfo (token) {
861
+ return await rest.get(rest.routes.oauth2.currentAuthorization(), {
862
+ headers: {
863
+ authorization: `Bearer ${token}`
864
+ },
865
+ unauthorized: true
866
+ });
867
+ },
868
+ async exchangeToken (clientId, clientSecret, body) {
869
+ const basicCredentials = Buffer.from(`${clientId}:${clientSecret}`);
870
+ const restOptions = {
871
+ body,
872
+ headers: {
873
+ 'content-type': 'application/x-www-form-urlencoded',
874
+ authorization: `Basic ${basicCredentials.toString('base64')}`
875
+ },
876
+ unauthorized: true
877
+ };
878
+ if (body.grantType === 'client_credentials') {
879
+ restOptions.body.scope = body.scope.join(' ');
880
+ }
881
+ return await rest.post(rest.routes.oauth2.tokenExchange(), restOptions);
882
+ },
883
+ async revokeToken (clientId, clientSecret, body) {
884
+ const basicCredentials = Buffer.from(`${clientId}:${clientSecret}`);
885
+ await rest.post(rest.routes.oauth2.tokenRevoke(), {
886
+ body,
887
+ headers: {
888
+ 'content-type': 'application/x-www-form-urlencoded',
889
+ authorization: `Basic ${basicCredentials.toString('base64')}`
890
+ },
891
+ unauthorized: true
892
+ });
782
893
  },
783
894
  async getAuditLog (guildId, options) {
784
895
  return await rest.get(rest.routes.guilds.auditlogs(guildId, options));
@@ -817,6 +928,11 @@ export function createRestManager(options) {
817
928
  }
818
929
  });
819
930
  },
931
+ async getGroupDmChannel (body) {
932
+ return await rest.post(rest.routes.channels.dm(), {
933
+ body
934
+ });
935
+ },
820
936
  async getEmoji (guildId, emojiId) {
821
937
  return await rest.get(rest.routes.guilds.emoji(guildId, emojiId));
822
938
  },
@@ -842,6 +958,14 @@ export function createRestManager(options) {
842
958
  }) {
843
959
  return await rest.get(rest.routes.guilds.guild(guildId, options.counts));
844
960
  },
961
+ async getGuilds (token, options) {
962
+ return await rest.get(rest.routes.guilds.userGuilds(options), {
963
+ headers: {
964
+ authorization: `Bearer ${token}`
965
+ },
966
+ unauthorized: true
967
+ });
968
+ },
845
969
  async getGuildApplicationCommand (commandId, guildId) {
846
970
  return await rest.get(rest.routes.interactions.commands.guilds.one(rest.applicationId, guildId, commandId));
847
971
  },
@@ -937,6 +1061,30 @@ export function createRestManager(options) {
937
1061
  async getUser (id) {
938
1062
  return await rest.get(rest.routes.user(id));
939
1063
  },
1064
+ async getCurrentUser (token) {
1065
+ return await rest.get(rest.routes.currentUser(), {
1066
+ headers: {
1067
+ authorization: `Bearer ${token}`
1068
+ },
1069
+ unauthorized: true
1070
+ });
1071
+ },
1072
+ async getUserConnections (token) {
1073
+ return await rest.get(rest.routes.oauth2.connections(), {
1074
+ headers: {
1075
+ authorization: `Bearer ${token}`
1076
+ },
1077
+ unauthorized: true
1078
+ });
1079
+ },
1080
+ async getUserApplicationRoleConnection (token, applicationId) {
1081
+ return await rest.get(rest.routes.oauth2.roleConnections(applicationId), {
1082
+ headers: {
1083
+ authorization: `Bearer ${token}`
1084
+ },
1085
+ unauthorized: true
1086
+ });
1087
+ },
940
1088
  async getVanityUrl (guildId) {
941
1089
  return await rest.get(rest.routes.guilds.vanity(guildId));
942
1090
  },
@@ -981,6 +1129,9 @@ export function createRestManager(options) {
981
1129
  async removeThreadMember (channelId, userId) {
982
1130
  await rest.delete(rest.routes.channels.threads.user(channelId, userId));
983
1131
  },
1132
+ async removeDmRecipient (channelId, userId) {
1133
+ await rest.delete(rest.routes.channels.dmRecipient(channelId, userId));
1134
+ },
984
1135
  async sendFollowupMessage (token, options) {
985
1136
  return await rest.post(rest.routes.webhooks.webhook(rest.applicationId, token), {
986
1137
  body: options,
@@ -1038,6 +1189,14 @@ export function createRestManager(options) {
1038
1189
  async getMember (guildId, userId) {
1039
1190
  return await rest.get(rest.routes.guilds.members.member(guildId, userId));
1040
1191
  },
1192
+ async getCurrentMember (guildId, token) {
1193
+ return await rest.get(rest.routes.guilds.members.currentMember(guildId), {
1194
+ headers: {
1195
+ authorization: `Bearer ${token}`
1196
+ },
1197
+ unauthorized: true
1198
+ });
1199
+ },
1041
1200
  async getMembers (guildId, options) {
1042
1201
  return await rest.get(rest.routes.guilds.members.members(guildId, options));
1043
1202
  },
@@ -1073,15 +1232,62 @@ export function createRestManager(options) {
1073
1232
  async triggerTypingIndicator (channelId) {
1074
1233
  await rest.post(rest.routes.channels.typing(channelId));
1075
1234
  },
1076
- async upsertGlobalApplicationCommands (body) {
1077
- return await rest.put(rest.routes.interactions.commands.commands(rest.applicationId), {
1235
+ async upsertGlobalApplicationCommands (body, options) {
1236
+ const restOptions = {
1237
+ body
1238
+ };
1239
+ if (options?.bearerToken) {
1240
+ restOptions.unauthorized = true;
1241
+ restOptions.headers = {
1242
+ authorization: `Bearer ${options.bearerToken}`
1243
+ };
1244
+ }
1245
+ return await rest.put(rest.routes.interactions.commands.commands(rest.applicationId), restOptions);
1246
+ },
1247
+ async upsertGuildApplicationCommands (guildId, body, options) {
1248
+ const restOptions = {
1078
1249
  body
1250
+ };
1251
+ if (options?.bearerToken) {
1252
+ restOptions.unauthorized = true;
1253
+ restOptions.headers = {
1254
+ authorization: `Bearer ${options.bearerToken}`
1255
+ };
1256
+ }
1257
+ return await rest.put(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), restOptions);
1258
+ },
1259
+ async editUserApplicationRoleConnection (token, applicationId, body) {
1260
+ return await rest.put(rest.routes.oauth2.roleConnections(applicationId), {
1261
+ body,
1262
+ headers: {
1263
+ authorization: `Bearer ${token}`
1264
+ },
1265
+ unauthorized: true
1079
1266
  });
1080
1267
  },
1081
- async upsertGuildApplicationCommands (guildId, body) {
1082
- return await rest.put(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), {
1268
+ async addGuildMember (guildId, userId, body) {
1269
+ return await rest.put(rest.routes.guilds.members.member(guildId, userId), {
1083
1270
  body
1084
1271
  });
1272
+ },
1273
+ preferSnakeCase (enabled) {
1274
+ const camelizer = enabled ? (x)=>x : camelize;
1275
+ rest.get = async (url, options)=>{
1276
+ return camelizer(await rest.makeRequest('GET', url, options));
1277
+ };
1278
+ rest.post = async (url, options)=>{
1279
+ return camelizer(await rest.makeRequest('POST', url, options));
1280
+ };
1281
+ rest.delete = async (url, options)=>{
1282
+ camelizer(await rest.makeRequest('DELETE', url, options));
1283
+ };
1284
+ rest.patch = async (url, options)=>{
1285
+ return camelizer(await rest.makeRequest('PATCH', url, options));
1286
+ };
1287
+ rest.put = async (url, options)=>{
1288
+ return camelizer(await rest.makeRequest('PUT', url, options));
1289
+ };
1290
+ return rest;
1085
1291
  }
1086
1292
  };
1087
1293
  return rest;