Skip to content

Commit 9d89eac

Browse files
committed
feat: changed module options and added auto-defer option
1 parent 007c959 commit 9d89eac

File tree

7 files changed

+79
-49
lines changed

7 files changed

+79
-49
lines changed

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@ export default defineNuxtConfig({
2929
modules: ['nuxt-discord'],
3030
discord: {
3131
// Discord bot configuration
32-
intents: ['Guilds'], // Discord gateway intents
32+
client: {
33+
intents: ['Guilds'], // Discord gateway intents
34+
deferOnPromise: true, // Auto-defer interactions when command returns a promise
35+
},
3336
dir: 'discord', // Directory for Discord-related files
3437
autoStart: true, // Auto-start the bot on server startup
3538
watch: {
3639
enabled: true, // Enable HMR for commands
40+
port: 5720, // HMR server port
41+
showURL: false, // Show HMR server URL in console
3742
sync: {
3843
debounce: 1000 // Sync delay in milliseconds
3944
}
@@ -209,15 +214,19 @@ export default (message: string) => {
209214
// nuxt.config.ts
210215
export default defineNuxtConfig({
211216
discord: {
212-
intents: [
213-
'Guilds',
214-
'GuildMessages'
215-
],
217+
client: {
218+
intents: [
219+
'Guilds',
220+
'GuildMessages'
221+
],
222+
deferOnPromise: true, // Auto-defer interactions when command returns a promise
223+
},
216224
dir: 'discord',
217225
autoStart: true,
218226
watch: {
219227
enabled: true,
220-
port: 4222,
228+
port: 5720, // HMR server port
229+
showURL: false, // Show HMR server URL in console
221230
sync: {
222231
debounce: 1000
223232
}

src/module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ export default defineNuxtModule<NuxtDiscordOptions>({
1616
configKey: 'discord',
1717
},
1818
defaults: {
19-
intents: [GatewayIntentBits.Guilds],
19+
client: {
20+
intents: [GatewayIntentBits.Guilds],
21+
deferOnPromise: true,
22+
},
2023
dir: 'discord',
2124
autoStart: true,
2225
watch: {
2326
enabled: true,
24-
port: 4222,
27+
port: 5720,
2528
showURL: false,
2629
sync: {
2730
debounce: 1000, // milliseconds

src/runtime/server/plugins/discord.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { ClientOptions } from 'discord.js'
21
import type { WatchEvent } from 'nuxt/schema'
32
import type { SlashCommand, SlashCommandRuntime } from '~/src/types'
43
import { existsSync } from 'node:fs'
@@ -67,7 +66,6 @@ export default defineNitroPlugin(async (nitro) => {
6766
}
6867

6968
if (runtimeConfig.discord.autoStart) {
70-
const options: ClientOptions = { intents: runtimeConfig.discord.intents }
71-
await client.start(options)
69+
await client.start(runtimeConfig.discord.client)
7270
}
7371
})

src/runtime/server/utils/client.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { APIApplicationCommand, APIApplicationCommandOption, ChatInputCommandInteraction, ClientOptions, RESTGetAPIApplicationCommandsResult, RESTPatchAPIApplicationCommandResult, RESTPutAPIApplicationCommandsResult } from 'discord.js'
2-
import type { SlashCommandOption, SlashCommandOptionType, SlashCommandReturnType, SlashCommandRuntime } from '~/src/types'
1+
import type { APIApplicationCommand, APIApplicationCommandOption, ChatInputCommandInteraction, RESTGetAPIApplicationCommandsResult, RESTPatchAPIApplicationCommandResult, RESTPutAPIApplicationCommandsResult } from 'discord.js'
2+
import type { NuxtDiscordOptions, SlashCommandOption, SlashCommandOptionType, SlashCommandReturnType, SlashCommandRuntime } from '~/src/types'
33
import process from 'node:process'
44
import { ApplicationCommandOptionType, Events, Client as InternalClient, REST, Routes, SlashCommandBuilder } from 'discord.js'
55
import { useNitroApp } from 'nitropack/runtime'
@@ -76,9 +76,11 @@ export class DiscordClient {
7676
})
7777
}
7878

79-
public async start(options: ClientOptions): Promise<undefined> {
80-
await this.#nitro.hooks.callHook('discord:client:config', options)
81-
this.#client = new InternalClient(options)
79+
#clientOptions?: NuxtDiscordOptions['client']
80+
public async start(options: NuxtDiscordOptions['client']): Promise<undefined> {
81+
this.#clientOptions = { ...options }
82+
await this.#nitro.hooks.callHook('discord:client:config', this.#clientOptions)
83+
this.#client = new InternalClient(this.#clientOptions)
8284
this.#client.on(Events.InteractionCreate, (interaction) => {
8385
if (!interaction.isChatInputCommand())
8486
return
@@ -205,9 +207,14 @@ export class DiscordClient {
205207

206208
try {
207209
currentInteraction = interaction
208-
const result = await command.execute!(...args)
210+
const result = command.execute!(...args)
209211
currentInteraction = null
210-
this.#handleSlashCommandReturn(result, interaction, command)
212+
213+
if (this.#clientOptions?.deferOnPromise && isPromise(result)) {
214+
await interaction.deferReply()
215+
}
216+
217+
await this.#handleSlashCommandReturn(result, interaction, command)
211218
}
212219
catch (error) {
213220
this.#nitro.hooks.callHook('discord:client:error', {
@@ -222,11 +229,14 @@ export class DiscordClient {
222229

223230
async #handleSlashCommandReturn(
224231
// not sure why Awaited<SlashCommandReturnType> does't work here
225-
result: Exclude<SlashCommandReturnType, Promise<SlashCommandReturnType>>,
232+
result: SlashCommandReturnType,
226233
interaction: ChatInputCommandInteraction,
227234
command: SlashCommandRuntime,
228235
// TODO: type this
229236
): Promise<unknown> {
237+
// @ts-expect-error - i don't know why this error occurs
238+
result = await result
239+
230240
if (!result) {
231241
return
232242
}
@@ -237,7 +247,7 @@ export class DiscordClient {
237247
}
238248
else if (typeof result === 'function') {
239249
const newResult = result.call(this, interaction, this)
240-
return this.#handleSlashCommandReturn(await newResult, interaction, command)
250+
return this.#handleSlashCommandReturn(newResult, interaction, command)
241251
}
242252
else if (Symbol.iterator in result && typeof result[Symbol.iterator] === 'function') {
243253
const gen = result[Symbol.iterator]()
@@ -532,3 +542,8 @@ function optionEqual(a: SlashCommandOption, b: APIApplicationCommandOption): boo
532542

533543
return true
534544
}
545+
546+
// https://stackoverflow.com/a/53955664/14835397
547+
export function isPromise(value: any): value is Promise<unknown> {
548+
return typeof value?.then === 'function'
549+
}

src/types.ts

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@ import { ApplicationCommandOptionType } from 'discord.js'
66

77
export interface NuxtDiscordOptions {
88
/**
9-
* The intents to use for the Discord client. Use
10-
* the 'nitro:client:config' hook to configure other
11-
* options passed to the Discord client.
9+
* Options passed to the internal Discord client.
10+
* Use the 'nitro:client:config' hook to configure
11+
* client options in runtime.
1212
*
13-
* @default [GatewayIntentBits.Guilds]
13+
* @default { intents: [GatewayIntentBits.Guilds] }
1414
*/
15-
intents: ClientOptions['intents']
15+
client: ClientOptions & {
16+
/**
17+
* Whether to automatically defer the interaction reply
18+
* on slash command execution if the command returns a promise.
19+
*
20+
* @default true
21+
*/
22+
deferOnPromise?: boolean
23+
}
1624

1725
/**
1826
* The directory to scan for nuxt-discord related
@@ -32,35 +40,32 @@ export interface NuxtDiscordOptions {
3240
/**
3341
* HMR options
3442
*/
35-
watch:
36-
& {
43+
watch: Partial<ListenOptions> & {
44+
/**
45+
* Enable or disable HMR for slash commands.
46+
*
47+
* @default true
48+
*/
49+
enabled?: boolean
50+
51+
/**
52+
* Whether to automatically register and update
53+
* slash commands on the Discord client.
54+
*/
55+
sync?: false | {
3756
/**
38-
* Enable or disable HMR for slash commands.
57+
* Milliseconds to debounce before updating
58+
* on HMR events.
3959
*
40-
* @default true
60+
* @default 1000
4161
*/
42-
enabled?: boolean
62+
debounce?: number
4363
}
44-
& {
45-
/**
46-
* Whether to automatically register and update
47-
* slash commands on the Discord client.
48-
*/
49-
sync?: false | {
50-
/**
51-
* Milliseconds to debounce before updating
52-
* on HMR events.
53-
*
54-
* @default 1000
55-
*/
56-
debounce?: number
57-
}
58-
}
59-
& Partial<ListenOptions>
64+
}
6065
}
6166

6267
export interface DiscordRuntimeConfig {
63-
intents: ClientOptions['intents']
68+
client: NuxtDiscordOptions['client']
6469
autoStart: boolean
6570
sync: NuxtDiscordOptions['watch']['sync']
6671
dir: string

src/utils/runtimeConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export function prepareRuntimeConfig(ctx: NuxtDiscordContext) {
44
ctx.nuxt.hook('nitro:config', (config) => {
55
config.runtimeConfig ??= {}
66
config.runtimeConfig.discord ??= {}
7-
config.runtimeConfig.discord.intents = ctx.options.intents
7+
config.runtimeConfig.discord.client = ctx.options.client
88
config.runtimeConfig.discord.autoStart = ctx.options.autoStart
99
config.runtimeConfig.discord.sync = ctx.options.watch.sync ?? false
1010
config.runtimeConfig.discord.dir = ctx.resolve.root(ctx.options.dir)

src/utils/templates.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type integer = import('nuxt-discord').integer
2828
declare global {
2929
declare module 'nitropack/types' {
3030
interface NitroRuntimeHooks {
31-
'discord:client:config': (options: import('discord.js').ClientOptions) => void
31+
'discord:client:config': (options: import('nuxt-discord').NuxtDiscordOptions['client']) => void
3232
'discord:client:ready': (client: DiscordClient) => void
3333
'discord:client:error': (error: DiscordClientError) => void
3434
}

0 commit comments

Comments
 (0)