Skip to content

Commit 824afca

Browse files
committed
feat: add guildOnly command and all commands are slash
1 parent 6be6d93 commit 824afca

File tree

11 files changed

+165
-227
lines changed

11 files changed

+165
-227
lines changed

src/define.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import {
99
} from 'discord.js'
1010
import type { Stream } from 'node:stream'
1111
import type {
12-
ArgsDef,
12+
CommandConfig,
1313
CommandExecute,
14-
CommandOptions,
1514
ContextMenuCallback,
16-
ContextMenuOptions,
15+
ContextMenuConfig,
1716
DefineContextMenu,
1817
DefineContextMenuWithOptions,
1918
DefineEvent,
@@ -29,6 +28,7 @@ import type {
2928
HarmonixEvent,
3029
HarmonixEvents,
3130
ModalOptions,
31+
OptionsDef,
3232
PreconditionCallback
3333
} from './types'
3434

@@ -64,41 +64,38 @@ export const defineEvent: DefineEvent & DefineEventWithOptions = <
6464
}
6565
}
6666

67-
export const defineCommand = <
68-
K extends boolean = boolean,
69-
T extends ArgsDef = ArgsDef
70-
>(
71-
options: CommandOptions<K> & { slash?: K; args?: T },
72-
execute: CommandExecute<K, T>
73-
): HarmonixCommand<K, T> => {
74-
return { options, execute }
67+
export const defineCommand = <T extends OptionsDef = OptionsDef>(
68+
config: CommandConfig & { options?: T },
69+
execute: CommandExecute<T>
70+
): HarmonixCommand<T> => {
71+
return { config, execute }
7572
}
7673

7774
export const defineContextMenu: DefineContextMenu &
7875
DefineContextMenuWithOptions = <Type extends 'message' | 'user'>(
7976
...args: [
80-
ContextMenuOptions | ContextMenuCallback<Type>,
77+
ContextMenuConfig | ContextMenuCallback<Type>,
8178
ContextMenuCallback<Type>?
8279
]
8380
): HarmonixContextMenu => {
84-
let options: ContextMenuOptions = {}
81+
let config: ContextMenuConfig = {}
8582

8683
if (args.length === 1) {
8784
const [callback] = args as [ContextMenuCallback<Type>]
8885

8986
return {
90-
options,
87+
config,
9188
callback
9289
}
9390
} else {
94-
const [opts, callback] = args as [
95-
ContextMenuOptions,
91+
const [cfg, callback] = args as [
92+
ContextMenuConfig,
9693
ContextMenuCallback<Type>
9794
]
9895

99-
options = opts
96+
config = cfg
10097
return {
101-
options,
98+
config,
10299
callback
103100
}
104101
}

src/discord.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createError, ctx } from './harmonix'
66
import { contextMenuToJSON, isHarmonixCommand, slashToJSON } from './utils'
77

88
export const initCient = (harmonixOptions: Harmonix['options']) => {
9-
const client = new Client(harmonixOptions.clientOptions)
9+
const client = new Client(harmonixOptions.client)
1010

1111
client.login(process.env.DISCORD_CLIENT_TOKEN)
1212

@@ -15,7 +15,7 @@ export const initCient = (harmonixOptions: Harmonix['options']) => {
1515

1616
export const refreshApplicationCommands = async (harmonix: Harmonix) => {
1717
const commands = [
18-
...harmonix.commands.filter((cmd) => cmd.options.slash).map((cmd) => cmd),
18+
...harmonix.commands.map((cmd) => cmd),
1919
...harmonix.contextMenus.map((cmd) => cmd)
2020
]
2121
const rest = new REST().setToken(process.env.DISCORD_CLIENT_TOKEN!)
@@ -26,18 +26,40 @@ export const refreshApplicationCommands = async (harmonix: Harmonix) => {
2626
await rest.put(
2727
Routes.applicationCommands(harmonix.options.clientId || client.user.id),
2828
{
29-
body: commands.map((cmd) =>
30-
isHarmonixCommand(cmd) ? slashToJSON(cmd) : contextMenuToJSON(cmd)
31-
)
29+
body: commands
30+
.filter((cmd) => !cmd.config.guildOnly)
31+
.map((cmd) =>
32+
isHarmonixCommand(cmd) ? slashToJSON(cmd) : contextMenuToJSON(cmd)
33+
)
3234
}
3335
)
36+
if (harmonix.options.guildId) {
37+
for (const guildId of harmonix.options.guildId) {
38+
await rest.put(
39+
Routes.applicationGuildCommands(
40+
harmonix.options.clientId || client.user.id,
41+
guildId
42+
),
43+
{
44+
body: commands
45+
.filter((cmd) => cmd.config.guildOnly)
46+
.map((cmd) =>
47+
isHarmonixCommand(cmd)
48+
? slashToJSON(cmd)
49+
: contextMenuToJSON(cmd)
50+
)
51+
}
52+
)
53+
}
54+
}
3455
consola.success('Successfully reloaded application commands.\n')
3556
const readyEvent = harmonix.events.get('ready')
3657

3758
if (readyEvent) {
3859
ctx.call(harmonix, () => readyEvent.callback(client))
3960
}
4061
} catch (error: any) {
62+
console.log(error)
4163
createError(error.message)
4264
}
4365
})

src/harmonix.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ import {
1818
} from './load'
1919
import { initCient, refreshApplicationCommands } from './discord'
2020
import {
21-
registerMessageCommands,
2221
registerContextMenu,
2322
registerEvents,
24-
registerSlashCommands
23+
registerCommands
2524
} from './register'
2625
import {
27-
resolveMessageCommand,
26+
resolveCommand,
2827
resolveContextMenu,
2928
resolveEvent,
3029
resolvePrecondition
@@ -51,9 +50,7 @@ export const createHarmonix = async (
5150

5251
const scannedCommands = await scanCommands(harmonix)
5352
const _commands = [...(harmonix.options.commands || []), ...scannedCommands]
54-
const commands = _commands.map((cmd) =>
55-
resolveMessageCommand(cmd, harmonix.options)
56-
)
53+
const commands = _commands.map((cmd) => resolveCommand(cmd, harmonix.options))
5754

5855
const scannedEvents = await scanEvents(harmonix)
5956
const _events = [...(harmonix.options.events || []), ...scannedEvents]
@@ -93,8 +90,7 @@ export const createHarmonix = async (
9390

9491
registerEvents(harmonix)
9592
await refreshApplicationCommands(harmonix)
96-
registerMessageCommands(harmonix)
97-
registerSlashCommands(harmonix)
93+
registerCommands(harmonix)
9894
registerContextMenu(harmonix)
9995

10096
return harmonix

src/load.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const loadCommands = (
1717
commands: HarmonixCommand[]
1818
) => {
1919
for (const cmd of commands) {
20-
harmonix.commands.set(cmd.options.name!, cmd)
20+
harmonix.commands.set(cmd.config.name!, cmd)
2121
}
2222
}
2323

@@ -26,7 +26,7 @@ export const loadContextMenus = (
2626
contextMenus: HarmonixContextMenu[]
2727
) => {
2828
for (const ctm of contextMenus) {
29-
harmonix.contextMenus.set(ctm.options.name!, ctm)
29+
harmonix.contextMenus.set(ctm.config.name!, ctm)
3030
}
3131
}
3232

src/options.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ export const loadOptions = async (
4141
options.scanDirs = [...new Set(options.scanDirs)]
4242
options.defaultPrefix = options.defaultPrefix || '!'
4343

44-
const intents = options.clientOptions?.intents || [
44+
const intents = options.client?.intents || [
4545
'Guilds',
4646
'GuildMessages',
4747
'MessageContent',
4848
'GuildMembers'
4949
]
5050

51-
options.clientOptions = options.clientOptions || {}
52-
options.clientOptions.intents = intents
51+
options.client = options.client || {}
52+
options.client.intents = intents
5353

5454
return options
5555
}

src/register.ts

Lines changed: 22 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Events } from 'discord.js'
22
import consola from 'consola'
33
import { colors } from 'consola/utils'
4-
import { optionToArg, resolveArgument } from './utils'
4+
import { toOption, resolveOption } from './utils'
55
import { ctx } from './harmonix'
6-
import type { Harmonix, HarmonixEvents, ParsedArgs } from './types'
6+
import type { Harmonix, HarmonixEvents, ParsedOptions } from './types'
77

88
export const registerEvents = (harmonix: Harmonix) => {
99
for (const [, event] of harmonix.events.filter((evt) => !evt.options.type)) {
@@ -36,86 +36,32 @@ export const registerEvents = (harmonix: Harmonix) => {
3636
})
3737
}
3838

39-
export const registerMessageCommands = (harmonix: Harmonix) => {
40-
harmonix.client?.on(Events.MessageCreate, async (message) => {
41-
if (message.author.bot) return
42-
const prefix = harmonix.options.defaultPrefix
43-
const rawArgs = message.content.slice(prefix.length).trim().split(/ +/)
44-
const command = rawArgs.shift()?.toLowerCase()
45-
46-
if (!command) return
47-
const cmd = harmonix.commands
48-
.filter((cmd) => !cmd.options.slash)
49-
.find((cmd) => cmd.options.name === command)
39+
export const registerCommands = (harmonix: Harmonix) => {
40+
harmonix.client?.on(Events.InteractionCreate, async (interaction) => {
41+
if (!interaction.isChatInputCommand()) return
42+
const cmd = harmonix.commands.find(
43+
(cmd) => cmd.config.name === interaction.commandName
44+
)
5045

5146
if (!cmd) return
52-
const args = await Object.keys(cmd.options.args || {}).reduce<
53-
Promise<ParsedArgs>
54-
>(async (acc, arg, index) => {
47+
const options = await interaction.options.data.reduce<
48+
Promise<ParsedOptions>
49+
>(async (acc, opt) => {
5550
const resolvedAcc = await acc
56-
const resolvedArg = await resolveArgument(
57-
message,
58-
cmd.options.args![arg].type,
59-
rawArgs[index]
51+
const resolvedOption = await resolveOption(
52+
interaction,
53+
toOption(opt.type),
54+
String(opt.value)
6055
)
6156

6257
return {
6358
...resolvedAcc,
64-
[arg]: resolvedArg
59+
[opt.name]: resolvedOption
6560
}
6661
}, Promise.resolve({}))
6762

68-
if (cmd.options.preconditions) {
69-
for (const prc of cmd.options.preconditions) {
70-
const precondition = harmonix.preconditions.get(prc)
71-
72-
if (!precondition) {
73-
consola.warn(`Precondition ${colors.cyan(prc)} not found.`)
74-
continue
75-
}
76-
const result = ctx.call(harmonix, () => {
77-
return precondition.callback({
78-
type: 'message',
79-
message
80-
})
81-
})
82-
83-
if (!result) return
84-
}
85-
}
86-
ctx.call(harmonix, () => {
87-
cmd.execute(harmonix.client!, message, { slash: false, args })
88-
})
89-
})
90-
}
91-
92-
export const registerSlashCommands = (harmonix: Harmonix) => {
93-
harmonix.client?.on(Events.InteractionCreate, async (interaction) => {
94-
if (!interaction.isChatInputCommand()) return
95-
const cmd = harmonix.commands
96-
.filter((cmd) => cmd.options.slash)
97-
.find((cmd) => cmd.options.name === interaction.commandName)
98-
99-
if (!cmd) return
100-
const args = await interaction.options.data.reduce<Promise<ParsedArgs>>(
101-
async (acc, opt) => {
102-
const resolvedAcc = await acc
103-
const resolvedArg = await resolveArgument(
104-
interaction,
105-
optionToArg(opt.type),
106-
String(opt.value)
107-
)
108-
109-
return {
110-
...resolvedAcc,
111-
[opt.name]: resolvedArg
112-
}
113-
},
114-
Promise.resolve({})
115-
)
116-
117-
if (cmd.options.preconditions) {
118-
for (const prc of cmd.options.preconditions) {
63+
if (cmd.config.preconditions) {
64+
for (const prc of cmd.config.preconditions) {
11965
const precondition = harmonix.preconditions.get(prc)
12066

12167
if (!precondition) {
@@ -134,8 +80,7 @@ export const registerSlashCommands = (harmonix: Harmonix) => {
13480
}
13581
ctx.call(harmonix, () => {
13682
cmd.execute(harmonix.client!, interaction, {
137-
slash: false,
138-
args
83+
options
13984
})
14085
})
14186
})
@@ -145,12 +90,12 @@ export const registerContextMenu = (harmonix: Harmonix) => {
14590
harmonix.client?.on(Events.InteractionCreate, async (interaction) => {
14691
if (!interaction.isContextMenuCommand()) return
14792
const ctm = harmonix.contextMenus.find(
148-
(ctm) => ctm.options.name === interaction.commandName
93+
(ctm) => ctm.config.name === interaction.commandName
14994
)
15095

15196
if (!ctm) return
152-
if (ctm.options.preconditions) {
153-
for (const prc of ctm.options.preconditions) {
97+
if (ctm.config.preconditions) {
98+
for (const prc of ctm.config.preconditions) {
15499
const precondition = harmonix.preconditions.get(prc)
155100

156101
if (!precondition) {

0 commit comments

Comments
 (0)