@satorijs/adapter-discord 4.1.2 → 4.1.4

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