@satorijs/adapter-discord 4.1.3 → 4.1.5

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 (42) hide show
  1. package/lib/index.js +15 -21
  2. package/lib/index.js.map +2 -3
  3. package/lib/types/internal.d.ts +3 -2
  4. package/lib/utils.d.ts +1 -1
  5. package/package.json +5 -4
  6. package/src/bot.ts +218 -0
  7. package/src/index.ts +27 -0
  8. package/src/message.ts +444 -0
  9. package/src/types/.eslintrc.yml +2 -0
  10. package/src/types/application-role-connection.ts +61 -0
  11. package/src/types/application.ts +120 -0
  12. package/src/types/audit-log.ts +219 -0
  13. package/src/types/auto-moderation.ts +189 -0
  14. package/src/types/ban.ts +92 -0
  15. package/src/types/channel.ts +501 -0
  16. package/src/types/command.ts +320 -0
  17. package/src/types/component.ts +125 -0
  18. package/src/types/device.ts +44 -0
  19. package/src/types/emoji.ts +96 -0
  20. package/src/types/gateway.ts +334 -0
  21. package/src/types/guild-member.ts +260 -0
  22. package/src/types/guild-template.ts +109 -0
  23. package/src/types/guild.ts +476 -0
  24. package/src/types/index.ts +49 -0
  25. package/src/types/integration.ts +130 -0
  26. package/src/types/interaction.ts +283 -0
  27. package/src/types/internal.ts +43 -0
  28. package/src/types/invite.ts +192 -0
  29. package/src/types/message.ts +470 -0
  30. package/src/types/presence.ts +163 -0
  31. package/src/types/reaction.ts +139 -0
  32. package/src/types/role.ts +252 -0
  33. package/src/types/scheduled-event.ts +200 -0
  34. package/src/types/stage-instance.ts +98 -0
  35. package/src/types/sticker.ts +179 -0
  36. package/src/types/team.ts +33 -0
  37. package/src/types/thread.ts +298 -0
  38. package/src/types/user.ts +154 -0
  39. package/src/types/voice.ts +124 -0
  40. package/src/types/webhook.ts +246 -0
  41. package/src/utils.ts +391 -0
  42. package/src/ws.ts +122 -0
package/src/message.ts ADDED
@@ -0,0 +1,444 @@
1
+ import { Context, Dict, h, MessageEncoder, Quester, Schema, Universal } from '@satorijs/satori'
2
+ import FormData from 'form-data'
3
+ import { DiscordBot } from './bot'
4
+ import { ActionRow, Button, ButtonStyles, Channel, ComponentType, Message } from './types'
5
+ import { decodeMessage, sanitize } from './utils'
6
+
7
+ type RenderMode = 'default' | 'figure'
8
+
9
+ class State {
10
+ author: Partial<Universal.User> = {}
11
+ quote: Partial<Universal.Message> = {}
12
+ channel: Partial<Channel> = {}
13
+ fakeMessageMap: Record<string, Universal.Message[]> = {} // [userInput] = discord messages
14
+ threadCreated = false // forward: send the first message and create a thread
15
+
16
+ constructor(public type: 'message' | 'forward') { }
17
+ }
18
+
19
+ export class DiscordMessageEncoder<C extends Context = Context> extends MessageEncoder<C, DiscordBot<C>> {
20
+ private stack: State[] = [new State('message')]
21
+ private buffer: string = ''
22
+ private addition: Dict = {}
23
+ private figure: h = null
24
+ private mode: RenderMode = 'default'
25
+ private listType: 'ol' | 'ul' = null
26
+ private rows: ActionRow[] = []
27
+ private async getUrl() {
28
+ const input = this.options?.session?.discord
29
+ if (input?.t === 'INTERACTION_CREATE') {
30
+ // 消息交互
31
+ return `/webhooks/${input.d.application_id}/${input.d.token}`
32
+ } else if (this.stack[0].type === 'forward' && this.stack[0].channel?.id) {
33
+ // 发送到子区
34
+ if (this.stack[1].author.name || this.stack[1].author.avatar) {
35
+ const webhook = await this.ensureWebhook()
36
+ return `/webhooks/${webhook.id}/${webhook.token}?wait=true&thread_id=${this.stack[0].channel?.id}`
37
+ } else {
38
+ return `/channels/${this.stack[0].channel.id}/messages`
39
+ }
40
+ } else {
41
+ if (this.stack[0].author.name || this.stack[0].author.avatar || (this.stack[0].type === 'forward' && !this.stack[0].threadCreated)) {
42
+ const webhook = await this.ensureWebhook()
43
+ return `/webhooks/${webhook.id}/${webhook.token}?wait=true`
44
+ } else {
45
+ return `/channels/${this.channelId}/messages`
46
+ }
47
+ }
48
+ }
49
+
50
+ async post(data?: any, headers?: any) {
51
+ try {
52
+ const url = await this.getUrl()
53
+ const result = await this.bot.http.post<Message>(url, data, { headers })
54
+ const session = this.bot.session()
55
+ const message = await decodeMessage(this.bot, result, session.event.message = {}, session.event)
56
+ session.app.emit(session, 'send', session)
57
+ this.results.push(session.event.message)
58
+ Object.defineProperty(session.event.message, 'channel', {
59
+ configurable: true,
60
+ get: () => session.event.channel,
61
+ })
62
+
63
+ if (this.stack[0].type === 'forward' && !this.stack[0].threadCreated) {
64
+ this.stack[0].threadCreated = true
65
+ const thread = await this.bot.internal.startThreadFromMessage(this.channelId, result.id, {
66
+ name: 'Forward',
67
+ auto_archive_duration: 60,
68
+ })
69
+ this.stack[0].channel = thread
70
+ }
71
+
72
+ return message
73
+ } catch (e) {
74
+ if (Quester.isAxiosError(e) && e.response) {
75
+ if (e.response.data?.code === 10015) {
76
+ this.bot.logger.debug('webhook has been deleted, recreating..., %o', e.response.data)
77
+ if (!this.bot.webhookLock[this.channelId]) this.bot.webhooks[this.channelId] = null
78
+ await this.ensureWebhook()
79
+ return this.post(data, headers)
80
+ } else {
81
+ e = new Error(`[${e.response.status}] ${JSON.stringify(e.response.data)}`)
82
+ }
83
+ }
84
+ this.errors.push(e)
85
+ }
86
+ }
87
+
88
+ async sendEmbed(attrs: Dict, payload: Dict) {
89
+ const { filename, data, mime } = await this.bot.ctx.http.file(attrs.url, attrs)
90
+ const form = new FormData()
91
+ // https://github.com/form-data/form-data/issues/468
92
+ const value = process.env.KOISHI_ENV === 'browser'
93
+ ? new Blob([data], { type: mime })
94
+ : Buffer.from(data)
95
+ form.append('file', value, attrs.file || filename)
96
+ form.append('payload_json', JSON.stringify(payload))
97
+ return this.post(form, form.getHeaders())
98
+ }
99
+
100
+ async sendAsset(type: string, attrs: Dict<string>, addition: Dict) {
101
+ const { handleMixedContent, handleExternalAsset } = this.bot.config as DiscordMessageEncoder.Config
102
+
103
+ if (handleMixedContent === 'separate' && addition.content) {
104
+ await this.post(addition)
105
+ addition.content = ''
106
+ }
107
+
108
+ const sendDirect = async () => {
109
+ if (addition.content) {
110
+ await this.post(addition)
111
+ }
112
+ return this.post({ ...addition, content: attrs.url })
113
+ }
114
+
115
+ if (await this.bot.http.isPrivate(attrs.url)) {
116
+ return await this.sendEmbed(attrs, addition)
117
+ }
118
+
119
+ const mode = attrs.mode as DiscordMessageEncoder.HandleExternalAsset || handleExternalAsset
120
+ if (mode === 'download' || handleMixedContent === 'attach' && addition.content || type === 'file') {
121
+ return this.sendEmbed(attrs, addition)
122
+ } else if (mode === 'direct') {
123
+ return sendDirect()
124
+ }
125
+
126
+ // auto mode
127
+ if (await this.checkMediaType(attrs.url, type)) {
128
+ return sendDirect()
129
+ } else {
130
+ return this.sendEmbed(attrs, addition)
131
+ }
132
+ }
133
+
134
+ checkMediaType(url: string, type: string) {
135
+ if (url.startsWith('https://cdn.discordapp.com/')) return true
136
+ return this.bot.ctx.http.head(url, {
137
+ headers: { accept: type + '/*' },
138
+ timeout: 1000,
139
+ }).then(
140
+ (headers) => headers['content-type'].startsWith(type),
141
+ () => false,
142
+ )
143
+ }
144
+
145
+ async ensureWebhook() {
146
+ return this.bot.ensureWebhook(this.channelId)
147
+ }
148
+
149
+ async flush() {
150
+ const content = this.buffer.trim()
151
+ this.trimButtons()
152
+ if (!content && !this.rows.length) return
153
+ this.addition.components = this.rows
154
+ await this.post({ ...this.addition, content })
155
+ this.buffer = ''
156
+ this.addition = {}
157
+ this.rows = []
158
+ }
159
+
160
+ decodeButton(attrs: Dict, label: string): Button {
161
+ let style = ButtonStyles.PRIMARY
162
+ if (attrs.class === 'secondary') style = ButtonStyles.SECONDARY
163
+ if (attrs.class === 'danger') style = ButtonStyles.DANGER
164
+ if (attrs.class === 'success') style = ButtonStyles.SUCCESS
165
+ if (attrs.type === 'link') {
166
+ return {
167
+ type: ComponentType.BUTTON,
168
+ url: attrs.href,
169
+ label,
170
+ style: ButtonStyles.LINK,
171
+ }
172
+ } else if (attrs.type === 'input') {
173
+ return {
174
+ type: ComponentType.BUTTON,
175
+ custom_id: `input${attrs.id}:${attrs.text}`,
176
+ label,
177
+ style,
178
+ }
179
+ } else {
180
+ return {
181
+ type: ComponentType.BUTTON,
182
+ custom_id: attrs.id,
183
+ label,
184
+ style,
185
+ }
186
+ }
187
+ }
188
+
189
+ lastRow() {
190
+ if (!this.rows.length) {
191
+ this.rows.push({
192
+ type: ComponentType.ACTION_ROW,
193
+ components: [],
194
+ })
195
+ }
196
+ let last = this.rows[this.rows.length - 1]
197
+ if (last.components.length >= 5) {
198
+ this.rows.push({
199
+ type: ComponentType.ACTION_ROW,
200
+ components: [],
201
+ })
202
+ last = this.rows[this.rows.length - 1]
203
+ }
204
+ return last
205
+ }
206
+
207
+ trimButtons() {
208
+ if (this.rows.length && this.rows[this.rows.length - 1].components.length === 0) this.rows.pop()
209
+ }
210
+
211
+ async visit(element: h) {
212
+ const { type, attrs, children } = element
213
+ if (type === 'text') {
214
+ this.buffer += sanitize(attrs.content)
215
+ } else if (type === 'b' || type === 'strong') {
216
+ this.buffer += '**'
217
+ await this.render(children)
218
+ this.buffer += '**'
219
+ } else if (type === 'i' || type === 'em') {
220
+ this.buffer += '*'
221
+ await this.render(children)
222
+ this.buffer += '*'
223
+ } else if (type === 'u' || type === 'ins') {
224
+ this.buffer += '__'
225
+ await this.render(children)
226
+ this.buffer += '__'
227
+ } else if (type === 's' || type === 'del') {
228
+ this.buffer += '~~'
229
+ await this.render(children)
230
+ this.buffer += '~~'
231
+ } else if (type === 'spl') {
232
+ this.buffer += '||'
233
+ await this.render(children)
234
+ this.buffer += '||'
235
+ } else if (type === 'code') {
236
+ this.buffer += '`'
237
+ await this.render(children)
238
+ this.buffer += '`'
239
+ } else if (type === 'a') {
240
+ await this.render(children)
241
+ if (this.options.linkPreview) {
242
+ this.buffer += ` (${attrs.href}) `
243
+ } else {
244
+ this.buffer += ` (<${attrs.href}>) `
245
+ }
246
+ } else if (type === 'br') {
247
+ this.buffer += '\n'
248
+ } else if (type === 'p') {
249
+ if (!this.buffer.endsWith('\n')) this.buffer += '\n'
250
+ await this.render(children)
251
+ if (!this.buffer.endsWith('\n')) this.buffer += '\n'
252
+ } else if (type === 'blockquote') {
253
+ if (!this.buffer.endsWith('\n')) this.buffer += '\n'
254
+ this.buffer += '> '
255
+ await this.render(children)
256
+ this.buffer += '\n'
257
+ } else if (type === 'ul' || type === 'ol') {
258
+ this.listType = type
259
+ await this.render(children)
260
+ this.listType = null
261
+ } else if (type === 'li') {
262
+ if (!this.buffer.endsWith('\n')) this.buffer += '\n'
263
+ if (this.listType === 'ol') {
264
+ this.buffer += '0. '
265
+ } else if (this.listType === 'ul') {
266
+ this.buffer += '- '
267
+ }
268
+ this.render(children)
269
+ this.buffer += '\n'
270
+ } else if (type === 'at') {
271
+ if (attrs.id) {
272
+ this.buffer += `<@${attrs.id}>`
273
+ } else if (attrs.type === 'all') {
274
+ this.buffer += `@everyone`
275
+ } else if (attrs.type === 'here') {
276
+ this.buffer += `@here`
277
+ }
278
+ } else if (type === 'sharp' && attrs.id) {
279
+ this.buffer += `<#${attrs.id}>`
280
+ } else if (type === 'face') {
281
+ if (attrs.platform && attrs.platform !== this.bot.platform) {
282
+ return this.render(children)
283
+ } else {
284
+ this.buffer += `<${attrs.animated ? 'a' : ''}:${attrs.name}:${attrs.id}>`
285
+ }
286
+ } else if ((type === 'image' || type === 'video') && attrs.url) {
287
+ if (this.mode === 'figure') {
288
+ this.figure = element
289
+ } else {
290
+ await this.sendAsset(type, attrs, {
291
+ ...this.addition,
292
+ content: this.buffer.trim(),
293
+ })
294
+ this.buffer = ''
295
+ }
296
+ } else if (type === 'share') {
297
+ await this.flush()
298
+ await this.post({
299
+ ...this.addition,
300
+ embeds: [{ ...attrs }],
301
+ })
302
+ } else if (type === 'audio') {
303
+ await this.sendAsset('file', attrs, {
304
+ ...this.addition,
305
+ content: this.buffer.trim(),
306
+ })
307
+ this.buffer = ''
308
+ } else if (type === 'author') {
309
+ const { avatar, nickname } = attrs
310
+ if (avatar) this.addition.avatar_url = avatar
311
+ if (nickname) this.addition.username = nickname
312
+ if (this.stack[0].type === 'message') {
313
+ this.stack[0].author = attrs
314
+ }
315
+ if (this.stack[0].type === 'forward') {
316
+ this.stack[1].author = attrs
317
+ }
318
+ } else if (type === 'quote') {
319
+ await this.flush()
320
+ const parse = (val: string) => val.replace(/\\([\\*_`~|()\[\]])/g, '$1')
321
+
322
+ const message = this.stack[this.stack[0].type === 'forward' ? 1 : 0]
323
+ if (!message.author.avatar && !message.author.name && this.stack[0].type !== 'forward') {
324
+ // no quote and author, send by bot
325
+ await this.flush()
326
+ this.addition.message_reference = {
327
+ message_id: attrs.id,
328
+ }
329
+ } else {
330
+ // quote
331
+ let replyId = attrs.id, channelId = this.channelId
332
+ if (this.stack[0].type === 'forward' && this.stack[0].fakeMessageMap[attrs.id]?.length >= 1) {
333
+ // quote to fake message, eg. 1st message has id (in channel or thread), later message quote to it
334
+ replyId = this.stack[0].fakeMessageMap[attrs.id][0].id
335
+ channelId = this.stack[0].fakeMessageMap[attrs.id][0].channel.id
336
+ }
337
+ const quote = await this.bot.getMessage(channelId, replyId)
338
+ this.addition.embeds = [{
339
+ description: [
340
+ sanitize(parse(quote.elements.filter(v => v.type === 'text').join('')).slice(0, 30)),
341
+ `<t:${Math.ceil(quote.timestamp / 1000)}:R> [[ ↑ ]](https://discord.com/channels/${this.guildId}/${channelId}/${replyId})`,
342
+ ].join('\n\n'),
343
+ author: {
344
+ name: quote.user.name,
345
+ icon_url: quote.user.avatar,
346
+ },
347
+ }]
348
+ }
349
+ } else if (type === 'figure') {
350
+ await this.flush()
351
+ this.mode = 'figure'
352
+ await this.render(children)
353
+ await this.sendAsset(this.figure.type, this.figure.attrs, {
354
+ ...this.addition,
355
+ content: this.buffer.trim(),
356
+ })
357
+ this.buffer = ''
358
+ this.mode = 'default'
359
+ } else if (type === 'message' && !attrs.forward) {
360
+ if (this.mode === 'figure') {
361
+ await this.render(children)
362
+ this.buffer += '\n'
363
+ } else {
364
+ const resultLength = +this.results.length
365
+ await this.flush()
366
+
367
+ await this.render(children)
368
+ await this.flush()
369
+ const newLength = +this.results.length
370
+ const sentMessages = this.results.slice(resultLength, newLength)
371
+ if (this.stack[0].type === 'forward' && attrs.id) {
372
+ this.stack[0].fakeMessageMap[attrs.id] = sentMessages
373
+ }
374
+ if (this.stack[0].type === 'message') {
375
+ this.stack[0].author = {}
376
+ }
377
+ if (this.stack[0].type === 'forward') {
378
+ this.stack[1].author = {}
379
+ }
380
+ }
381
+ } else if (type === 'button') {
382
+ const last = this.lastRow()
383
+ last.components.push(this.decodeButton(
384
+ attrs, children.join(''),
385
+ ))
386
+ } else if (type === 'button-group') {
387
+ this.rows.push({
388
+ type: ComponentType.ACTION_ROW,
389
+ components: [],
390
+ })
391
+ await this.render(children)
392
+ this.rows.push({
393
+ type: ComponentType.ACTION_ROW,
394
+ components: [],
395
+ })
396
+ } else if (type === 'message' && attrs.forward) {
397
+ this.stack.unshift(new State('forward'))
398
+ await this.render(children)
399
+ await this.flush()
400
+ await this.bot.internal.modifyChannel(this.stack[0].channel.id, {
401
+ archived: true,
402
+ locked: true,
403
+ })
404
+ this.stack.shift()
405
+ } else {
406
+ await this.render(children)
407
+ }
408
+ }
409
+ }
410
+
411
+ export namespace DiscordMessageEncoder {
412
+ export type HandleExternalAsset = 'auto' | 'download' | 'direct'
413
+ export type HandleMixedContent = 'auto' | 'separate' | 'attach'
414
+
415
+ export interface Config {
416
+ /**
417
+ * 发送外链资源时采用的方式
418
+ * - download:先下载后发送
419
+ * - direct:直接发送链接
420
+ * - auto:发送一个 HEAD 请求,如果返回的 Content-Type 正确,则直接发送链接,否则先下载后发送(默认)
421
+ */
422
+ handleExternalAsset?: HandleExternalAsset
423
+ /**
424
+ * 发送图文等混合内容时采用的方式
425
+ * - separate:将每个不同形式的内容分开发送
426
+ * - attach:图片前如果有文本内容,则将文本作为图片的附带信息进行发送
427
+ * - auto:如果图片本身采用直接发送则与前面的文本分开,否则将文本作为图片的附带信息发送(默认)
428
+ */
429
+ handleMixedContent?: HandleMixedContent
430
+ }
431
+
432
+ export const Config: Schema<DiscordMessageEncoder.Config> = Schema.object({
433
+ handleExternalAsset: Schema.union([
434
+ Schema.const('download').description('先下载后发送'),
435
+ Schema.const('direct').description('直接发送链接'),
436
+ Schema.const('auto').description('发送一个 HEAD 请求,根据返回的 Content-Type 决定发送方式'),
437
+ ]).role('radio').description('发送外链资源时采用的方式。').default('auto'),
438
+ handleMixedContent: Schema.union([
439
+ Schema.const('separate').description('将每个不同形式的内容分开发送'),
440
+ Schema.const('attach').description('图片前如果有文本内容,则将文本作为图片的附带信息进行发送'),
441
+ Schema.const('auto').description('如果图片本身采用直接发送则与前面的文本分开,否则将文本作为图片的附带信息发送'),
442
+ ]).role('radio').description('发送图文等混合内容时采用的方式。').default('auto'),
443
+ }).description('发送设置')
444
+ }
@@ -0,0 +1,2 @@
1
+ rules:
2
+ max-len: off
@@ -0,0 +1,61 @@
1
+ import { Internal, Locale } from '.'
2
+
3
+ export namespace ApplicationRoleConnection {
4
+ /** https://discord.com/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-structure */
5
+ export interface Metadata {
6
+ /** type of metadata value */
7
+ type: MetadataType
8
+ /** dictionary key for the metadata field (must be a-z, 0-9, or _ characters; 1-50 characters) */
9
+ key: string
10
+ /** name of the metadata field (1-100 characters) */
11
+ name: string
12
+ /** translations of the name */
13
+ name_localizations?: Record<Locale, string>
14
+ /** description of the metadata field (1-200 characters) */
15
+ description: string
16
+ /** translations of the description */
17
+ description_localizations?: Record<Locale, string>
18
+ }
19
+
20
+ /** https://discord.com/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type */
21
+ export enum MetadataType {
22
+ /** the metadata value (integer) is less than or equal to the guild's configured value (integer) */
23
+ INTEGER_LESS_THAN_OR_EQUAL = 1,
24
+ /** the metadata value (integer) is greater than or equal to the guild's configured value (integer) */
25
+ INTEGER_GREATER_THAN_OR_EQUAL = 2,
26
+ /** the metadata value (integer) is equal to the guild's configured value (integer) */
27
+ INTEGER_EQUAL = 3,
28
+ /** the metadata value (integer) is not equal to the guild's configured value (integer) */
29
+ INTEGER_NOT_EQUAL = 4,
30
+ /** the metadata value (ISO8601 string) is less than or equal to the guild's configured value (integer; days before current date) */
31
+ DATETIME_LESS_THAN_OR_EQUAL = 5,
32
+ /** the metadata value (ISO8601 string) is greater than or equal to the guild's configured value (integer; days before current date) */
33
+ DATETIME_GREATER_THAN_OR_EQUAL = 6,
34
+ /** the metadata value (integer) is equal to the guild's configured value (integer; 1) */
35
+ BOOLEAN_EQUAL = 7,
36
+ /** the metadata value (integer) is not equal to the guild's configured value (integer; 1) */
37
+ BOOLEAN_NOT_EQUAL = 8,
38
+ }
39
+ }
40
+
41
+ declare module './internal' {
42
+ interface Internal {
43
+ /**
44
+ * Returns a list of application role connection metadata objects for the given application.
45
+ * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records
46
+ */
47
+ getApplicationRoleConnectionMetadataRecords(): Promise<ApplicationRoleConnection.Metadata[]>
48
+ /**
49
+ * Updates and returns a list of application role connection metadata objects for the given application.
50
+ * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records
51
+ */
52
+ updateApplicationRoleConnectionMetadataRecords(): Promise<ApplicationRoleConnection.Metadata[]>
53
+ }
54
+ }
55
+
56
+ Internal.define({
57
+ '/applications/{application.id}/role-connections/metadata': {
58
+ GET: 'getApplicationRoleConnectionMetadataRecords',
59
+ PUT: 'updateApplicationRoleConnectionMetadataRecords',
60
+ },
61
+ })
@@ -0,0 +1,120 @@
1
+ import { Guild, integer, Internal, snowflake, Team, User } from '.'
2
+
3
+ /** https://discord.com/developers/docs/resources/application#application-object-application-structure */
4
+ export interface Application {
5
+ /** the id of the app */
6
+ id: snowflake
7
+ /** the name of the app */
8
+ name: string
9
+ /** the icon hash of the app */
10
+ icon?: string
11
+ /** the description of the app */
12
+ description: string
13
+ /** an array of rpc origin urls, if rpc is enabled */
14
+ rpc_origins?: string[]
15
+ /** when false only app owner can join the app's bot to guilds */
16
+ bot_public: boolean
17
+ /** when true the app's bot will only join upon completion of the full oauth2 code grant flow */
18
+ bot_require_code_grant: boolean
19
+ /** the url of the app's terms of service */
20
+ terms_of_service_url?: string
21
+ /** the url of the app's privacy policy */
22
+ privacy_policy_url?: string
23
+ /** partial user object containing info on the owner of the application */
24
+ owner?: Partial<User>
25
+ /** deprecated, if this application is a game sold on Discord, this field will be the summary field for the store page of its primary sku */
26
+ summary: string
27
+ /** the hex encoded key for verification in interactions and the GameSDK's GetTicket */
28
+ verify_key: string
29
+ /** if the application belongs to a team, this will be a list of the members of that team */
30
+ team?: Team
31
+ /** if this application is a game sold on Discord, this field will be the guild to which it has been linked */
32
+ guild_id?: snowflake
33
+ /** if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists */
34
+ primary_sku_id?: snowflake
35
+ /** if this application is a game sold on Discord, this field will be the URL slug that links to the store page */
36
+ slug?: string
37
+ /** the application's default rich presence invite cover image hash */
38
+ cover_image?: string
39
+ /** the application's public flags */
40
+ flags?: integer
41
+ /** up to 5 tags describing the content and functionality of the application */
42
+ tags?: [string, string?, string?, string?, string?]
43
+ /** settings for the application's default in-app authorization link, if enabled */
44
+ install_params?: InstallParams
45
+ /** the application's default custom authorization link, if enabled */
46
+ custom_install_url?: string
47
+ /** the application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration */
48
+ role_connections_verification_url?: string
49
+ }
50
+
51
+ export interface InstallParams {
52
+ /** the scopes to add the application to the server with */
53
+ scopes: string[]
54
+ /** the permissions to request for the bot role */
55
+ permissions: string
56
+ }
57
+
58
+ /** https://discord.com/developers/docs/resources/application#application-object-application-flags */
59
+ export enum ApplicationFlag {
60
+ GATEWAY_PRESENCE = 1 << 12,
61
+ GATEWAY_PRESENCE_LIMITED = 1 << 13,
62
+ GATEWAY_GUILD_MEMBERS = 1 << 14,
63
+ GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15,
64
+ VERIFICATION_PENDING_GUILD_LIMIT = 1 << 16,
65
+ EMBEDDED = 1 << 17,
66
+ GATEWAY_MESSAGE_CONTENT = 1 << 18,
67
+ GATEWAY_MESSAGE_CONTENT_LIMITED = 1 << 19,
68
+ APPLICATION_COMMAND_BADGE = 1 << 23,
69
+ }
70
+
71
+ /** https://discord.com/developers/docs/topics/gateway-events#ready-ready-event-fields */
72
+ export interface ReadyEvent {
73
+ /** gateway version */
74
+ v: integer
75
+ /** information about the user including email */
76
+ user: User
77
+ /** the guilds the user is in */
78
+ guilds: Partial<Guild>[]
79
+ /** used for resuming connections */
80
+ session_id: string
81
+ /** gateway URL for resuming connections */
82
+ resume_gateway_url: string
83
+ /** the shard information associated with this session, if sent when identifying */
84
+ shard?: [shard_id: integer, num_shards: integer]
85
+ /** contains id and flags */
86
+ application: Partial<Application>
87
+ }
88
+
89
+ declare module './gateway' {
90
+ interface GatewayEvents {
91
+ /** contains the initial state information */
92
+ READY: ReadyEvent
93
+ /** response to Resume */
94
+ RESUMED: {}
95
+ }
96
+ }
97
+
98
+ declare module './internal' {
99
+ interface Internal {
100
+ /**
101
+ * Returns the bot's application object.
102
+ * @see https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information
103
+ */
104
+ getCurrentBotApplicationInformation(): Promise<Application>
105
+ /**
106
+ * Returns info about the current authorization. Requires authentication with a bearer token.
107
+ * @see https://discord.com/developers/docs/topics/oauth2#get-current-authorization-information
108
+ */
109
+ getCurrentAuthorizationInformation(): Promise<any>
110
+ }
111
+ }
112
+
113
+ Internal.define({
114
+ '/oauth2/applications/@me': {
115
+ GET: 'getCurrentBotApplicationInformation',
116
+ },
117
+ '/oauth2/@me': {
118
+ GET: 'getCurrentAuthorizationInformation',
119
+ },
120
+ })