@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.
- package/lib/index.js +15 -21
- package/lib/index.js.map +2 -3
- package/lib/types/internal.d.ts +3 -2
- package/lib/utils.d.ts +1 -1
- package/package.json +5 -4
- package/src/bot.ts +218 -0
- package/src/index.ts +27 -0
- package/src/message.ts +444 -0
- package/src/types/.eslintrc.yml +2 -0
- package/src/types/application-role-connection.ts +61 -0
- package/src/types/application.ts +120 -0
- package/src/types/audit-log.ts +219 -0
- package/src/types/auto-moderation.ts +189 -0
- package/src/types/ban.ts +92 -0
- package/src/types/channel.ts +501 -0
- package/src/types/command.ts +320 -0
- package/src/types/component.ts +125 -0
- package/src/types/device.ts +44 -0
- package/src/types/emoji.ts +96 -0
- package/src/types/gateway.ts +334 -0
- package/src/types/guild-member.ts +260 -0
- package/src/types/guild-template.ts +109 -0
- package/src/types/guild.ts +476 -0
- package/src/types/index.ts +49 -0
- package/src/types/integration.ts +130 -0
- package/src/types/interaction.ts +283 -0
- package/src/types/internal.ts +43 -0
- package/src/types/invite.ts +192 -0
- package/src/types/message.ts +470 -0
- package/src/types/presence.ts +163 -0
- package/src/types/reaction.ts +139 -0
- package/src/types/role.ts +252 -0
- package/src/types/scheduled-event.ts +200 -0
- package/src/types/stage-instance.ts +98 -0
- package/src/types/sticker.ts +179 -0
- package/src/types/team.ts +33 -0
- package/src/types/thread.ts +298 -0
- package/src/types/user.ts +154 -0
- package/src/types/voice.ts +124 -0
- package/src/types/webhook.ts +246 -0
- package/src/utils.ts +391 -0
- 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,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
|
+
})
|