noot
This commit is contained in:
parent
7cc1c4ab72
commit
97d85d7fc9
@ -63,6 +63,52 @@ const TEST_COMMANDS = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'admin',
|
||||||
|
description: 'Admin commands',
|
||||||
|
type: ApplicationCommandTypes.ChatInput,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
description: 'User management',
|
||||||
|
type: ApplicationCommandOptionTypes.SubCommandGroup,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'ban',
|
||||||
|
description: 'Ban a user',
|
||||||
|
type: ApplicationCommandOptionTypes.SubCommand,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
description: 'User to ban',
|
||||||
|
type: ApplicationCommandOptionTypes.User,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reason',
|
||||||
|
description: 'Ban reason',
|
||||||
|
type: ApplicationCommandOptionTypes.String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'kick',
|
||||||
|
description: 'Kick a user',
|
||||||
|
type: ApplicationCommandOptionTypes.SubCommand,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
description: 'User to kick',
|
||||||
|
type: ApplicationCommandOptionTypes.User,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
] as const satisfies CreateApplicationCommand[]
|
] as const satisfies CreateApplicationCommand[]
|
||||||
|
|
||||||
describe('createCommandHandler', () => {
|
describe('createCommandHandler', () => {
|
||||||
@ -77,6 +123,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -95,6 +147,7 @@ describe('createCommandHandler', () => {
|
|||||||
it('should handle a simple command with optional arguments', async () => {
|
it('should handle a simple command with optional arguments', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const simpleHandler = vi.fn()
|
||||||
const notFoundHandler = vi.fn()
|
const notFoundHandler = vi.fn()
|
||||||
|
let a: ExtractCommands<typeof TEST_COMMANDS>
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
||||||
handler: {
|
handler: {
|
||||||
@ -103,6 +156,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -132,6 +191,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: listHandler,
|
list: listHandler,
|
||||||
create: createHandler,
|
create: createHandler,
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -167,6 +232,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -191,6 +262,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -221,6 +298,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
})
|
})
|
||||||
@ -232,6 +315,55 @@ describe('createCommandHandler', () => {
|
|||||||
expect(notFoundHandler).toHaveBeenCalledTimes(2)
|
expect(notFoundHandler).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should handle subcommand groups recursively', async () => {
|
||||||
|
const banHandler = vi.fn()
|
||||||
|
const kickHandler = vi.fn()
|
||||||
|
const notFoundHandler = vi.fn()
|
||||||
|
|
||||||
|
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
||||||
|
handler: {
|
||||||
|
simple: vi.fn(),
|
||||||
|
complex: {
|
||||||
|
list: vi.fn(),
|
||||||
|
create: vi.fn(),
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: banHandler,
|
||||||
|
kick: kickHandler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notFoundHandler,
|
||||||
|
})
|
||||||
|
|
||||||
|
const interactionData: InteractionData = {
|
||||||
|
name: 'admin',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
type: ApplicationCommandOptionTypes.SubCommandGroup,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'ban',
|
||||||
|
type: ApplicationCommandOptionTypes.SubCommand,
|
||||||
|
options: [
|
||||||
|
{ name: 'user', type: ApplicationCommandOptionTypes.User, value: '123456789' },
|
||||||
|
{ name: 'reason', type: ApplicationCommandOptionTypes.String, value: 'Spam' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await handler(interactionData)
|
||||||
|
|
||||||
|
expect(banHandler).toHaveBeenCalledWith({ user: '123456789', reason: 'Spam' })
|
||||||
|
expect(kickHandler).not.toHaveBeenCalled()
|
||||||
|
expect(notFoundHandler).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('should handle commands without options', async () => {
|
it('should handle commands without options', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const simpleHandler = vi.fn()
|
||||||
|
|
||||||
@ -242,6 +374,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler: vi.fn(),
|
notFoundHandler: vi.fn(),
|
||||||
})
|
})
|
||||||
@ -266,6 +404,12 @@ describe('createCommandHandler', () => {
|
|||||||
list: vi.fn(),
|
list: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
notFoundHandler: vi.fn(),
|
notFoundHandler: vi.fn(),
|
||||||
})
|
})
|
||||||
@ -313,6 +457,22 @@ describe('ExtractCommands type utility', () => {
|
|||||||
expect(enabled).toBeDefined()
|
expect(enabled).toBeDefined()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: async (args) => {
|
||||||
|
// TypeScript should know that args has user as required string and reason as optional string
|
||||||
|
const user: string = args.user
|
||||||
|
const reason: string | undefined = args.reason
|
||||||
|
expect(user).toBeDefined()
|
||||||
|
expect(reason).toBeDefined()
|
||||||
|
},
|
||||||
|
kick: async (args) => {
|
||||||
|
// TypeScript should know that args has user as required string
|
||||||
|
const user: string = args.user
|
||||||
|
expect(user).toBeDefined()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is just to satisfy TypeScript - the real test is that the above compiles
|
// This is just to satisfy TypeScript - the real test is that the above compiles
|
||||||
@ -347,6 +507,23 @@ describe('ExtractCommands type utility', () => {
|
|||||||
expect(isEnabled).toBeDefined()
|
expect(isEnabled).toBeDefined()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: async (args) => {
|
||||||
|
// This would error if args.user wasn't a string
|
||||||
|
const userId = args.user.toLowerCase()
|
||||||
|
// This would error if args.reason wasn't string | undefined
|
||||||
|
const reasonLength = args.reason?.length || 0
|
||||||
|
expect(userId).toBeDefined()
|
||||||
|
expect(reasonLength).toBeDefined()
|
||||||
|
},
|
||||||
|
kick: async (args) => {
|
||||||
|
// This would error if args.user wasn't a string
|
||||||
|
const userId = args.user.toLowerCase()
|
||||||
|
expect(userId).toBeDefined()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ApplicationCommandOptionTypes, type CreateApplicationCommand, type DiscordInteractionDataOption } from '@discordeno/types'
|
import { ApplicationCommandOptionTypes, DiscordApplicationCommandOption, type CreateApplicationCommand, type DiscordInteractionDataOption } from '@discordeno/types'
|
||||||
import type { InteractionData } from '..'
|
import type { InteractionData } from '..'
|
||||||
import type { SLASH_COMMANDS } from './slash_commands'
|
import type { SLASH_COMMANDS } from './slash_commands'
|
||||||
|
|
||||||
@ -16,10 +16,10 @@ type OptionTypeMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper type to get option by name
|
// Helper type to get option by name
|
||||||
type GetOption<Options, Name> = Options extends readonly any[] ? (Options[number] extends infer O ? (O extends { name: Name } ? O : never) : never) : never
|
type GetOption<Options, Name> = Options extends readonly DiscordApplicationCommandOption[] ? (Options[number] extends infer O ? (O extends { name: Name } ? O : never) : never) : never
|
||||||
|
|
||||||
// Extract the argument types from command options
|
// Extract the argument types from command options
|
||||||
export type ExtractArgs<Options extends readonly any[]> = {
|
export type ExtractArgs<Options extends readonly DiscordApplicationCommandOption[]> = {
|
||||||
[K in Options[number]['name']]: GetOption<Options, K> extends { type: infer T; required?: infer R }
|
[K in Options[number]['name']]: GetOption<Options, K> extends { type: infer T; required?: infer R }
|
||||||
? T extends keyof OptionTypeMap
|
? T extends keyof OptionTypeMap
|
||||||
? R extends true
|
? R extends true
|
||||||
@ -30,55 +30,73 @@ export type ExtractArgs<Options extends readonly any[]> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handler function type that accepts typed arguments
|
// Handler function type that accepts typed arguments
|
||||||
type HandlerFunction<Args = {}> = (args: Args) => Promise<void> | void
|
type HandlerFunction<Args = Record<string, never>> = (args: Args) => Promise<void> | void
|
||||||
|
|
||||||
// Get subcommand by name
|
|
||||||
type GetSubcommand<Options, Name> = Options extends readonly any[]
|
|
||||||
|
// Get subcommand or subcommand group by name
|
||||||
|
type GetSubcommandOrGroup<Options, Name> = Options extends readonly DiscordApplicationCommandOption[]
|
||||||
? Options[number] extends infer O
|
? Options[number] extends infer O
|
||||||
? O extends { name: Name; type: ApplicationCommandOptionTypes.SubCommand }
|
? O extends { name: Name; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
||||||
? O
|
? O
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
|
|
||||||
// Check if all options are subcommands
|
// Check if all options are subcommands or subcommand groups
|
||||||
type HasOnlySubcommands<Options extends readonly any[]> = Options[number] extends { type: ApplicationCommandOptionTypes.SubCommand } ? true : false
|
type HasOnlySubcommands<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup } ? true : false
|
||||||
|
|
||||||
// Extract subcommand names from options
|
// Extract subcommand or subcommand group names from options
|
||||||
type SubcommandNames<Options extends readonly any[]> = Options[number] extends { name: infer N; type: ApplicationCommandOptionTypes.SubCommand }
|
type SubcommandNames<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { name: infer N; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
||||||
? N extends string
|
? N extends string
|
||||||
? N
|
? N
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
|
|
||||||
// Type to extract subcommand handlers
|
// Type to extract subcommand handlers (recursive for groups)
|
||||||
export type SubcommandHandlers<Options extends readonly any[]> = {
|
export type SubcommandHandlers<Options extends readonly DiscordApplicationCommandOption[]> = {
|
||||||
[K in SubcommandNames<Options>]: GetSubcommand<Options, K> extends { options?: infer SubOpts }
|
[K in SubcommandNames<Options>]: GetSubcommandOrGroup<Options, K> extends { type: infer T; options?: infer SubOpts }
|
||||||
? SubOpts extends readonly any[]
|
? T extends ApplicationCommandOptionTypes.SubCommandGroup
|
||||||
? HandlerFunction<ExtractArgs<SubOpts>>
|
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
||||||
: HandlerFunction<{}>
|
? SubcommandHandlers<SubOpts>
|
||||||
: HandlerFunction<{}>
|
: never
|
||||||
|
: T extends ApplicationCommandOptionTypes.SubCommand
|
||||||
|
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
||||||
|
? HandlerFunction<ExtractArgs<SubOpts>>
|
||||||
|
: HandlerFunction<Record<string, never>>
|
||||||
|
: never
|
||||||
|
: never
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get command by name from array
|
// Get command by name from array
|
||||||
type GetCommand<Commands extends readonly any[], Name> = Commands[number] extends infer C ? (C extends { name: Name } ? C : never) : never
|
type GetCommand<Commands extends readonly any[], Name> = Commands[number] extends infer C ? (C extends { name: Name } ? C : never) : never
|
||||||
|
|
||||||
// Main type to extract command handlers from slash commands
|
// Main type to extract command handlers from slash commands
|
||||||
export type ExtractCommands<T extends readonly any[]> = {
|
export type ExtractCommands<T extends readonly CreateApplicationCommand[]> = {
|
||||||
[Name in T[number]['name']]: GetCommand<T, Name> extends { options?: infer Options }
|
[Name in T[number]['name']]: GetCommand<T, Name> extends { options?: infer Options }
|
||||||
? Options extends readonly any[]
|
? Options extends readonly DiscordApplicationCommandOption[]
|
||||||
? HasOnlySubcommands<Options> extends true
|
? HasOnlySubcommands<Options> extends true
|
||||||
? SubcommandHandlers<Options>
|
? SubcommandHandlers<Options>
|
||||||
: HandlerFunction<ExtractArgs<Options>>
|
: HandlerFunction<ExtractArgs<Options>>
|
||||||
: HandlerFunction<{}>
|
: HandlerFunction<Record<string, never>>
|
||||||
: HandlerFunction<{}>
|
: HandlerFunction<Record<string, never>>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type representing the possible output of ExtractCommands
|
||||||
|
export type CommandHandler = HandlerFunction<any> | CommandHandlerMap
|
||||||
|
export type CommandHandlerMap = {
|
||||||
|
[key: string]: CommandHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual command handler type based on SLASH_COMMANDS
|
// The actual command handler type based on SLASH_COMMANDS
|
||||||
export type CommandHandlers = ExtractCommands<typeof SLASH_COMMANDS>
|
export type CommandHandlers = ExtractCommands<typeof SLASH_COMMANDS>
|
||||||
|
|
||||||
// Helper function to parse option values from interaction data
|
// Helper function to parse option values from interaction data. there is sort of a hack here
|
||||||
function parseOptions(options?: DiscordInteractionDataOption[]): Record<string, any> {
|
// so the actual type we need to parse comes from the parsing of DiscordApplicationCommandOption
|
||||||
|
// but this is parsing from the DiscordInteractionDataOption type, which is just the generic data response.
|
||||||
|
// technically, at runtime, with the SLASH_COMMANDS object, it's possible to validate this struct, and produce a better type than any
|
||||||
|
// but that is... a lot of work, so we are just going to do this for now and leave validation for another day.
|
||||||
|
function parseOptions<T extends DiscordInteractionDataOption[]>(options?: T): any {
|
||||||
if (!options) return {}
|
if (!options) return {}
|
||||||
|
|
||||||
const args: Record<string, any> = {}
|
const args: Record<string, any> = {}
|
||||||
@ -87,60 +105,66 @@ function parseOptions(options?: DiscordInteractionDataOption[]): Record<string,
|
|||||||
if (option.type === ApplicationCommandOptionTypes.SubCommand || option.type === ApplicationCommandOptionTypes.SubCommandGroup) {
|
if (option.type === ApplicationCommandOptionTypes.SubCommand || option.type === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
args[option.name] = option.value
|
args[option.name] = option.value
|
||||||
}
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified recursive handler for commands, subcommands, and subcommand groups
|
||||||
|
async function handleCommands(
|
||||||
|
handler: CommandHandler,
|
||||||
|
options: DiscordInteractionDataOption[] | undefined,
|
||||||
|
notFoundHandler: HandlerFunction<{path?: string}>
|
||||||
|
): Promise<void> {
|
||||||
|
// If handler is a function, execute it with parsed options
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
const args = parseOptions(options)
|
||||||
|
await handler(args)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is a map of subcommands/groups
|
||||||
|
const subcommand = options?.find(
|
||||||
|
(opt) => opt.type === ApplicationCommandOptionTypes.SubCommand || opt.type === ApplicationCommandOptionTypes.SubCommandGroup
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!subcommand) {
|
||||||
|
await notFoundHandler({})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const subHandler = handler[subcommand.name]
|
||||||
|
if (!subHandler) {
|
||||||
|
await notFoundHandler({})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively handle the subcommand/group
|
||||||
|
await handleCommands(subHandler, subcommand.options, notFoundHandler)
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to create command handlers with type safety
|
// Helper function to create command handlers with type safety
|
||||||
export function createCommandHandler<T extends readonly CreateApplicationCommand[]>({
|
export function createCommandHandler<T extends readonly CreateApplicationCommand[]>({
|
||||||
handler,
|
handler,
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
}: {
|
}: {
|
||||||
|
commands: T,
|
||||||
handler: ExtractCommands<T>
|
handler: ExtractCommands<T>
|
||||||
notFoundHandler: HandlerFunction<{}>
|
notFoundHandler: HandlerFunction<{path?: string}>
|
||||||
}) {
|
}) {
|
||||||
return async (data: InteractionData): Promise<void> => {
|
return async (data: InteractionData): Promise<void> => {
|
||||||
if (!data || !data.name) {
|
if (!data || !data.name) {
|
||||||
await notFoundHandler({})
|
await notFoundHandler({})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const commandName = data.name as keyof typeof handler
|
const commandName = data.name as keyof typeof handler
|
||||||
const commandHandler = handler[commandName]
|
const commandHandler = handler[commandName]
|
||||||
|
|
||||||
if (!commandHandler) {
|
if (!commandHandler) {
|
||||||
await notFoundHandler({})
|
await notFoundHandler({})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Use unified handler for all command types
|
||||||
// Check if it's a direct command or has subcommands
|
await handleCommands(commandHandler, data.options, notFoundHandler)
|
||||||
if (typeof commandHandler === 'function') {
|
|
||||||
// Parse arguments from top-level options
|
|
||||||
const args = parseOptions(data.options)
|
|
||||||
await commandHandler(args)
|
|
||||||
} else {
|
|
||||||
// Handle subcommands
|
|
||||||
const subcommand = data.options?.find(
|
|
||||||
(opt) => opt.type === ApplicationCommandOptionTypes.SubCommand || opt.type === ApplicationCommandOptionTypes.SubCommandGroup
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!subcommand) {
|
|
||||||
await notFoundHandler({})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const subHandler = commandHandler[subcommand.name as keyof typeof commandHandler]
|
|
||||||
if (!subHandler || typeof subHandler !== 'function') {
|
|
||||||
await notFoundHandler({})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse arguments from subcommand options
|
|
||||||
const args = parseOptions(subcommand.options)
|
|
||||||
await (subHandler as HandlerFunction<any>)(args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { InteractionTypes } from '@discordeno/types'
|
|||||||
import { proxyActivities, startChild, workflowInfo } from '@temporalio/workflow'
|
import { proxyActivities, startChild, workflowInfo } from '@temporalio/workflow'
|
||||||
import type * as activities from '#/activities'
|
import type * as activities from '#/activities'
|
||||||
import type { InteractionCreatePayload } from '#/discord'
|
import type { InteractionCreatePayload } from '#/discord'
|
||||||
import { createCommandHandler } from '#/discord/botevent/command_parser'
|
import { CommandHandlers, createCommandHandler } from '#/discord/botevent/command_parser'
|
||||||
import type { SLASH_COMMANDS } from '#/discord/botevent/slash_commands'
|
import { SLASH_COMMANDS } from '#/discord/botevent/slash_commands'
|
||||||
import { handleCommandGuildInfo, handleCommandGuildLeaderboard, handleCommandGuildOnline } from './guild_messages'
|
import { handleCommandGuildInfo, handleCommandGuildLeaderboard, handleCommandGuildOnline } from './guild_messages'
|
||||||
import { handleCommandPlayerLookup } from './player_messages'
|
import { handleCommandPlayerLookup } from './player_messages'
|
||||||
|
|
||||||
@ -29,7 +29,8 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
|
|||||||
await notFoundHandler(`Invalid command data`)
|
await notFoundHandler(`Invalid command data`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const commandHandler = createCommandHandler<typeof SLASH_COMMANDS>({
|
const commandHandler = createCommandHandler({
|
||||||
|
commands: SLASH_COMMANDS,
|
||||||
notFoundHandler: async () => {
|
notFoundHandler: async () => {
|
||||||
await notFoundHandler(`command not found`)
|
await notFoundHandler(`command not found`)
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user