@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/utils.ts ADDED
@@ -0,0 +1,391 @@
1
+ import { Context, Dict, h, pick, Session, Universal, valueMap } from '@satorijs/satori'
2
+ import { DiscordBot } from './bot'
3
+ import * as Discord from './types'
4
+
5
+ export * from './types'
6
+
7
+ export const sanitize = (val: string) =>
8
+ val
9
+ .replace(/[\\*_`~|()\[\]]/g, '\\$&')
10
+ .replace(/@everyone/g, () => '\\@everyone')
11
+ .replace(/@here/g, () => '\\@here')
12
+
13
+ export const decodeUser = (user: Discord.User): Universal.User => ({
14
+ id: user.id,
15
+ name: user.username,
16
+ userId: user.id,
17
+ avatar: user.avatar && `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`,
18
+ username: user.username,
19
+ discriminator: user.discriminator,
20
+ isBot: user.bot || false,
21
+ })
22
+
23
+ export const decodeGuildMember = (member: Partial<Discord.GuildMember>): Universal.GuildMember => ({
24
+ user: member.user && decodeUser(member.user),
25
+ nick: member.nick,
26
+ roles: member.roles,
27
+ joinedAt: member.joined_at && new Date(member.joined_at).valueOf(),
28
+ })
29
+
30
+ export const decodeGuild = (data: Discord.Guild): Universal.Guild => ({
31
+ id: data.id,
32
+ name: data.name,
33
+ })
34
+
35
+ export const decodeChannel = (data: Discord.Channel): Universal.Channel => ({
36
+ id: data.id,
37
+ name: data.name,
38
+ type: data.type === Discord.Channel.Type.DM ? Universal.Channel.Type.DIRECT : Universal.Channel.Type.TEXT,
39
+ })
40
+
41
+ export const decodeRole = (role: Discord.Role): Universal.GuildRole => ({
42
+ ...role,
43
+ permissions: BigInt(role.permissions),
44
+ })
45
+
46
+ export const encodeRole = (role: Partial<Universal.GuildRole>): Partial<Discord.Role> => ({
47
+ ...role,
48
+ permissions: role.permissions && '' + role.permissions,
49
+ })
50
+
51
+ export async function decodeMessage(
52
+ bot: DiscordBot,
53
+ data: Discord.Message,
54
+ message: Universal.Message,
55
+ payload: Universal.MessageLike = message,
56
+ details = true,
57
+ ) {
58
+ const { platform } = bot
59
+
60
+ message.id = message.messageId = data.id
61
+ // https://discord.com/developers/docs/reference#message-formatting
62
+ message.content = ''
63
+ if (data.content) {
64
+ message.content = data.content
65
+ .replace(/<@[!&]?(.+?)>/g, (_, id) => {
66
+ if (data.mention_roles.includes(id)) {
67
+ return h('at', { role: id }).toString()
68
+ } else {
69
+ const user = data.mentions?.find(u => u.id === id || `${u.username}#${u.discriminator}` === id)
70
+ return h.at(id, { name: user?.username }).toString()
71
+ }
72
+ })
73
+ .replace(/<a?:(.*):(.+?)>/g, (_, name, id) => {
74
+ const animated = _[1] === 'a'
75
+ return h('face', { id, name, animated, platform }, [
76
+ h.image(`https://cdn.discordapp.com/emojis/${id}.gif?quality=lossless`),
77
+ ]).toString()
78
+ })
79
+ .replace(/@everyone/g, () => h('at', { type: 'all' }).toString())
80
+ .replace(/@here/g, () => h('at', { type: 'here' }).toString())
81
+ .replace(/<#(.+?)>/g, (_, id) => {
82
+ const channel = data.mention_channels?.find(c => c.id === id)
83
+ return h.sharp(id, { name: channel?.name }).toString()
84
+ })
85
+ }
86
+
87
+ // embed 的 update event 太阴间了 只有 id embeds channel_id guild_id 四个成员
88
+ if (data.attachments?.length) {
89
+ if (message.content) message.content += ' '
90
+ message.content += data.attachments.map(v => {
91
+ if (v.height && v.width && v.content_type?.startsWith('image/')) {
92
+ return h('image', {
93
+ url: v.url,
94
+ proxy_url: v.proxy_url,
95
+ file: v.filename,
96
+ })
97
+ } else if (v.height && v.width && v.content_type?.startsWith('video/')) {
98
+ return h('video', {
99
+ url: v.url,
100
+ proxy_url: v.proxy_url,
101
+ file: v.filename,
102
+ })
103
+ } else if (v.content_type?.startsWith('audio/')) {
104
+ return h('record', {
105
+ url: v.url,
106
+ proxy_url: v.proxy_url,
107
+ file: v.filename,
108
+ })
109
+ } else {
110
+ return h('file', {
111
+ url: v.url,
112
+ proxy_url: v.proxy_url,
113
+ file: v.filename,
114
+ })
115
+ }
116
+ }).join('')
117
+ }
118
+ for (const embed of data.embeds) {
119
+ // not using embed types
120
+ // https://discord.com/developers/docs/resources/channel#embed-object-embed-types
121
+ if (embed.image) {
122
+ message.content += h('image', { url: embed.image.url, proxy_url: embed.image.proxy_url })
123
+ }
124
+ if (embed.thumbnail) {
125
+ message.content += h('image', { url: embed.thumbnail.url, proxy_url: embed.thumbnail.proxy_url })
126
+ }
127
+ if (embed.video) {
128
+ message.content += h('video', { url: embed.video.url, proxy_url: embed.video.proxy_url })
129
+ }
130
+ }
131
+ message.elements = h.parse(message.content)
132
+ // 遇到过 cross post 的消息在这里不会传消息 id
133
+ if (details && data.message_reference) {
134
+ const { message_id, channel_id } = data.message_reference
135
+ message.quote = await bot.getMessage(channel_id, message_id)
136
+ }
137
+
138
+ if (!payload) return message
139
+ payload.channel = {
140
+ id: data.channel_id,
141
+ type: data.member ? Universal.Channel.Type.TEXT : Universal.Channel.Type.DIRECT,
142
+ }
143
+ payload.user = decodeUser(data.author)
144
+ payload.member = data.member && decodeGuildMember(data.member)
145
+ payload.timestamp = new Date(data.timestamp).valueOf() || Date.now()
146
+ return message
147
+ }
148
+
149
+ export function setupMessageGuildId(session: Session, guildId: string) {
150
+ session.guildId = guildId
151
+ session.isDirect = !guildId
152
+ session.subtype = guildId ? 'group' : 'private'
153
+ }
154
+
155
+ type ReactionEvent = Partial<
156
+ & Discord.Reaction.Event.Add
157
+ & Discord.Reaction.Event.Remove
158
+ & Discord.Reaction.Event.RemoveAll
159
+ & Discord.Reaction.Event.RemoveEmoji>
160
+
161
+ function setupReaction(session: Session, data: ReactionEvent) {
162
+ session.userId = data.user_id
163
+ session.messageId = data.message_id
164
+ session.guildId = data.guild_id
165
+ session.channelId = data.channel_id
166
+ session.isDirect = !data.guild_id
167
+ session.subtype = data.guild_id ? 'group' : 'private'
168
+ if (!data.emoji) return
169
+ const { id, name } = data.emoji
170
+ session.content = id ? `${name}:${id}` : name
171
+ }
172
+
173
+ export async function adaptSession<C extends Context>(bot: DiscordBot<C>, input: Discord.Gateway.Payload) {
174
+ const session = bot.session()
175
+ session.setInternal('discord', input)
176
+ if (input.t === 'MESSAGE_CREATE') {
177
+ setupMessageGuildId(session, input.d.guild_id)
178
+ if (input.d.webhook_id && !session.isDirect) {
179
+ try {
180
+ // 403 Missing Permissions
181
+ const webhook = await bot.ensureWebhook(input.d.channel_id)
182
+ // koishi's webhook
183
+ if (webhook.id === input.d.webhook_id) return
184
+ } catch (e) { }
185
+ }
186
+ session.type = 'message'
187
+ await decodeMessage(bot, input.d, session.event.message = {}, session.event)
188
+ // dc 情况特殊 可能有 embeds 但是没有消息主体
189
+ // if (!session.content) return
190
+ } else if (input.t === 'MESSAGE_UPDATE') {
191
+ session.type = 'message-updated'
192
+ const message = await bot.internal.getChannelMessage(input.d.channel_id, input.d.id)
193
+ // Unlike creates, message updates may contain only a subset of the full message object payload
194
+ // https://discord.com/developers/docs/topics/gateway-events#message-update
195
+ await decodeMessage(bot, message, session.event.message = {}, session.event)
196
+ const channel = await bot.internal.getChannel(input.d.channel_id)
197
+ setupMessageGuildId(session, channel.guild_id)
198
+ // if (!session.content) return
199
+ } else if (input.t === 'MESSAGE_DELETE') {
200
+ session.type = 'message-deleted'
201
+ session.messageId = input.d.id
202
+ session.channelId = input.d.channel_id
203
+ setupMessageGuildId(session, input.d.guild_id)
204
+ } else if (input.t === 'MESSAGE_REACTION_ADD') {
205
+ session.type = 'reaction-added'
206
+ setupReaction(session, input.d)
207
+ } else if (input.t === 'MESSAGE_REACTION_REMOVE') {
208
+ session.type = 'reaction-deleted'
209
+ session.subtype = 'one'
210
+ setupReaction(session, input.d)
211
+ } else if (input.t === 'MESSAGE_REACTION_REMOVE_ALL') {
212
+ session.type = 'reaction-deleted'
213
+ session.subtype = 'all'
214
+ setupReaction(session, input.d)
215
+ } else if (input.t === 'MESSAGE_REACTION_REMOVE_EMOJI') {
216
+ session.type = 'reaction-deleted'
217
+ session.subtype = 'emoji'
218
+ setupReaction(session, input.d)
219
+ } else if (input.t === 'GUILD_ROLE_CREATE') {
220
+ session.type = 'guild-role-added'
221
+ session.guildId = input.d.guild_id
222
+ session.roleId = input.d.role.id
223
+ session.event.role = decodeRole(input.d.role)
224
+ } else if (input.t === 'GUILD_ROLE_UPDATE') {
225
+ session.type = 'guild-role-updated'
226
+ session.guildId = input.d.guild_id
227
+ session.roleId = input.d.role.id
228
+ session.event.role = decodeRole(input.d.role)
229
+ } else if (input.t === 'GUILD_ROLE_DELETE') {
230
+ session.type = 'guild-role-added'
231
+ session.guildId = input.d.guild_id
232
+ session.roleId = input.d.role_id
233
+ } else if (input.t === 'INTERACTION_CREATE' && input.d.type === Discord.Interaction.Type.APPLICATION_COMMAND) {
234
+ const data = input.d.data as Discord.InteractionData.ApplicationCommand
235
+ const command = bot.commands.find(cmd => cmd.name === data.name)
236
+ if (!command) return
237
+ await bot.internal.createInteractionResponse(input.d.id, input.d.token, {
238
+ type: Discord.Interaction.CallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
239
+ })
240
+ session.type = 'interaction/command'
241
+ session.isDirect = !input.d.guild_id
242
+ session.subtype = input.d.guild_id ? 'group' : 'private'
243
+ session.channelId = input.d.channel_id
244
+ session.guildId = input.d.guild_id
245
+ session.userId = session.isDirect ? input.d.user.id : input.d.member.user.id
246
+ session.messageId = input.d.id
247
+ session.content = ''
248
+ session.event.argv = decodeArgv(data, command)
249
+ } else if (input.t === 'INTERACTION_CREATE' && input.d.type === Discord.Interaction.Type.MODAL_SUBMIT) {
250
+ const data = input.d.data as Discord.InteractionData.ModalSubmit
251
+ if (!data.custom_id.startsWith('input') && !data.custom_id.includes(':')) return
252
+ // @ts-ignore
253
+ const user_input = data.components[0].components[0].value
254
+ await bot.internal.createInteractionResponse(input.d.id, input.d.token, {
255
+ type: Discord.Interaction.CallbackType.DEFERRED_UPDATE_MESSAGE,
256
+ })
257
+ session.type = 'interaction/command'
258
+ session.isDirect = !input.d.guild_id
259
+ session.subtype = input.d.guild_id ? 'group' : 'private'
260
+ session.channelId = input.d.channel_id
261
+ session.guildId = input.d.guild_id
262
+ session.userId = session.isDirect ? input.d.user.id : input.d.member.user.id
263
+ session.messageId = input.d.id
264
+ session.content = user_input
265
+ } else if (input.t === 'INTERACTION_CREATE' && input.d.type === Discord.Interaction.Type.MESSAGE_COMPONENT) {
266
+ const id = (input.d.data as Discord.InteractionData.MessageComponent).custom_id
267
+ if (id.startsWith('input') && id.includes(':')) {
268
+ await bot.internal.createInteractionResponse(input.d.id, input.d.token, {
269
+ type: Discord.Interaction.CallbackType.MODAL,
270
+ data: {
271
+ custom_id: id,
272
+ title: 'Input',
273
+ components: [{
274
+ type: Discord.ComponentType.ACTION_ROW,
275
+ components: [{
276
+ custom_id: id,
277
+ type: Discord.ComponentType.TEXT_INPUT,
278
+ label: 'Command',
279
+ value: id.slice(id.indexOf(':') + 1),
280
+ style: 1,
281
+ }],
282
+ }],
283
+ },
284
+ })
285
+ } else {
286
+ await bot.internal.createInteractionResponse(input.d.id, input.d.token, {
287
+ type: Discord.Interaction.CallbackType.DEFERRED_UPDATE_MESSAGE,
288
+ })
289
+ }
290
+ session.type = 'interaction/button'
291
+ session.isDirect = !input.d.guild_id
292
+ session.channelId = input.d.channel_id
293
+ session.guildId = input.d.guild_id
294
+ session.userId = session.isDirect ? input.d.user.id : input.d.member.user.id
295
+ session.messageId = input.d.id
296
+ session.content = ''
297
+ session.event.button = {
298
+ id,
299
+ }
300
+ } else if (input.t === 'CHANNEL_UPDATE') {
301
+ session.type = 'channel-updated'
302
+ session.guildId = input.d.guild_id
303
+ session.subtype = input.d.guild_id ? 'group' : 'private'
304
+ session.channelId = input.d.id
305
+ } else {
306
+ return
307
+ }
308
+ return session
309
+ }
310
+
311
+ const types = {
312
+ text: Discord.ApplicationCommand.OptionType.STRING,
313
+ string: Discord.ApplicationCommand.OptionType.STRING,
314
+ boolean: Discord.ApplicationCommand.OptionType.BOOLEAN,
315
+ number: Discord.ApplicationCommand.OptionType.NUMBER,
316
+ integer: Discord.ApplicationCommand.OptionType.INTEGER,
317
+ posint: Discord.ApplicationCommand.OptionType.INTEGER,
318
+ user: Discord.ApplicationCommand.OptionType.STRING,
319
+ channel: Discord.ApplicationCommand.OptionType.STRING,
320
+ guild: Discord.ApplicationCommand.OptionType.STRING,
321
+ }
322
+
323
+ interface Description {
324
+ name: string
325
+ description: Dict<string>
326
+ }
327
+
328
+ const trimDescription = (source: string) => {
329
+ if (!source || source.length < 96) return source
330
+ return source.slice(0, 93) + '...'
331
+ }
332
+
333
+ const encodeDescription = (object: Description) => ({
334
+ description: trimDescription(object.description[''] || object.name),
335
+ description_localizations: valueMap(pick(object.description, Discord.Locale), trimDescription),
336
+ })
337
+
338
+ export const encodeCommand = (cmd: Universal.Command): Discord.ApplicationCommand.Params.Create => ({
339
+ ...encodeDescription(cmd),
340
+ name: cmd.name,
341
+ type: Discord.ApplicationCommand.Type.CHAT_INPUT,
342
+ options: encodeCommandOptions(cmd),
343
+ })
344
+
345
+ const decodeArgv = (data: Discord.InteractionData.ApplicationCommand, command: Universal.Command) => {
346
+ const result = { name: data.name, arguments: [], options: {} } as Universal.Argv
347
+ for (const argument of command.arguments) {
348
+ const value = data.options?.find(opt => opt.name === argument.name)?.value
349
+ if (value !== undefined) result.arguments.push(value)
350
+ }
351
+ for (const option of command.options) {
352
+ const value = data.options?.find(opt => opt.name === option.name)?.value
353
+ if (value !== undefined) result.options[option.name] = value
354
+ }
355
+ return result
356
+ }
357
+
358
+ export function encodeCommandOptions(cmd: Universal.Command): Discord.ApplicationCommand.Option[] {
359
+ const result: Discord.ApplicationCommand.Option[] = []
360
+ if (cmd.children.length) {
361
+ result.push(...cmd.children.map(child => ({
362
+ name: child.name.slice(cmd.name.length + 1),
363
+ type: child.children.length
364
+ ? Discord.ApplicationCommand.OptionType.SUB_COMMAND_GROUP
365
+ : Discord.ApplicationCommand.OptionType.SUB_COMMAND,
366
+ options: encodeCommandOptions(child),
367
+ description: cmd.description[''] || child.name,
368
+ description_localizations: pick(cmd.description, Discord.Locale),
369
+ })))
370
+ } else {
371
+ // `getGlobalApplicationCommands()` does not return `required` property.
372
+ for (const arg of cmd.arguments) {
373
+ result.push({
374
+ ...encodeDescription(arg),
375
+ name: arg.name.toLowerCase().replace(/[^a-z0-9]/g, ''),
376
+ type: types[arg.type] ?? types.text,
377
+ // required: arg.required ?? false,
378
+ })
379
+ }
380
+ for (const option of cmd.options) {
381
+ result.push({
382
+ ...encodeDescription(option),
383
+ name: option.name.toLowerCase(),
384
+ type: types[option.type] ?? types.text,
385
+ // required: option.required ?? false,
386
+ min_value: option.type === 'posint' ? 1 : undefined,
387
+ })
388
+ }
389
+ }
390
+ return result.sort((a, b) => +b.required - +a.required)
391
+ }
package/src/ws.ts ADDED
@@ -0,0 +1,124 @@
1
+ import { Adapter, Context, Logger, Schema } from '@satorijs/satori'
2
+ import { Gateway } from './types'
3
+ import { adaptSession, decodeUser } from './utils'
4
+ import { DiscordBot } from './bot'
5
+
6
+ const logger = new Logger('discord')
7
+
8
+ export class WsClient<C extends Context = Context> extends Adapter.WsClient<C, DiscordBot<C>> {
9
+ _d = 0
10
+ _ping: NodeJS.Timeout
11
+ _sessionId = ''
12
+ _resumeUrl: string
13
+
14
+ async prepare() {
15
+ if (this._resumeUrl) {
16
+ return this.bot.http.ws(this._resumeUrl + '/?v=10&encoding=json')
17
+ }
18
+ const { url } = await this.bot.internal.getGatewayBot()
19
+ return this.bot.http.ws(url + '/?v=10&encoding=json')
20
+ }
21
+
22
+ heartbeat() {
23
+ logger.debug(`heartbeat d ${this._d}`)
24
+ this.socket.send(JSON.stringify({
25
+ op: Gateway.Opcode.HEARTBEAT,
26
+ d: this._d,
27
+ }))
28
+ }
29
+
30
+ accept() {
31
+ this.socket.addEventListener('message', async ({ data }) => {
32
+ let parsed: Gateway.Payload
33
+ try {
34
+ parsed = JSON.parse(data.toString())
35
+ } catch (error) {
36
+ return logger.warn('cannot parse message', data)
37
+ }
38
+ logger.debug(parsed)
39
+ if (parsed.s) {
40
+ this._d = parsed.s
41
+ }
42
+
43
+ // https://discord.com/developers/docs/topics/gateway#connection-lifecycle
44
+ if (parsed.op === Gateway.Opcode.HELLO) {
45
+ this._ping = setInterval(() => this.heartbeat(), parsed.d.heartbeat_interval)
46
+ if (this._sessionId) {
47
+ logger.debug('resuming')
48
+ this.socket.send(JSON.stringify({
49
+ op: Gateway.Opcode.RESUME,
50
+ d: {
51
+ token: this.bot.config.token,
52
+ session_id: this._sessionId,
53
+ seq: this._d,
54
+ },
55
+ }))
56
+ } else {
57
+ this.socket.send(JSON.stringify({
58
+ op: Gateway.Opcode.IDENTIFY,
59
+ d: {
60
+ token: this.bot.config.token,
61
+ properties: {},
62
+ compress: false,
63
+ intents: this.bot.config.intents,
64
+ },
65
+ }))
66
+ }
67
+ }
68
+
69
+ if (parsed.op === Gateway.Opcode.INVALID_SESSION) {
70
+ if (parsed.d) return
71
+ this._sessionId = ''
72
+ logger.warn('offline: invalid session')
73
+ this.socket?.close()
74
+ }
75
+
76
+ if (parsed.op === Gateway.Opcode.DISPATCH) {
77
+ this.bot.dispatch(this.bot.session({
78
+ type: 'internal',
79
+ _type: 'discord/' + parsed.t.toLowerCase().replace(/_/g, '-'),
80
+ _data: parsed,
81
+ }))
82
+ if (parsed.t === 'READY') {
83
+ this._sessionId = parsed.d.session_id
84
+ this._resumeUrl = parsed.d.resume_gateway_url
85
+ this.bot.user = decodeUser(parsed.d.user)
86
+ logger.debug('session_id ' + this._sessionId)
87
+ return this.bot.online()
88
+ }
89
+ if (parsed.t === 'RESUMED') {
90
+ return this.bot.online()
91
+ }
92
+ const session = await adaptSession(this.bot, parsed)
93
+ if (session) this.bot.dispatch(session)
94
+ }
95
+
96
+ if (parsed.op === Gateway.Opcode.RECONNECT) {
97
+ logger.warn('offline: discord request reconnect')
98
+ this.socket?.close()
99
+ }
100
+ })
101
+
102
+ this.socket.addEventListener('close', () => {
103
+ clearInterval(this._ping)
104
+ })
105
+ }
106
+ }
107
+
108
+ export namespace WsClient {
109
+ export interface Config extends Adapter.WsClientConfig {
110
+ intents?: number
111
+ }
112
+
113
+ export const Config: Schema<Config> = Schema.intersect([
114
+ Schema.object({
115
+ intents: Schema.bitset(Gateway.Intent).description('需要订阅的机器人事件。').default(0
116
+ | Gateway.Intent.GUILD_MESSAGES
117
+ | Gateway.Intent.GUILD_MESSAGE_REACTIONS
118
+ | Gateway.Intent.DIRECT_MESSAGES
119
+ | Gateway.Intent.DIRECT_MESSAGE_REACTIONS
120
+ | Gateway.Intent.MESSAGE_CONTENT),
121
+ }).description('推送设置'),
122
+ Adapter.WsClientConfig,
123
+ ] as const)
124
+ }