diff --git a/packages/bot/src/commandOptionsParser.ts b/packages/bot/src/commandOptionsParser.ts index dcbc146eb..02f92873e 100644 --- a/packages/bot/src/commandOptionsParser.ts +++ b/packages/bot/src/commandOptionsParser.ts @@ -1,52 +1,85 @@ import { ApplicationCommandOptionTypes } from '@discordeno/types' -import type { Attachment, Channel, Interaction, InteractionDataOption, Member, Role, User } from './index.js' +import type { + Attachment, + Channel, + DesiredPropertiesBehavior, + Interaction, + InteractionDataOption, + Member, + Role, + SetupDesiredProps, + TransformProperty, + TransformersDesiredProperties, + User, + WithAtLeast, +} from './index.js' + +export function commandOptionsParser< + TProps extends WithAtLeast, + TBehavior extends DesiredPropertiesBehavior, +>(__interaction: SetupDesiredProps, options?: InteractionDataOption[]): ParsedInteractionOption { + // This is necessary as typescript gets really confused when using __interaction alone, as it will say that 'data' does not exist despite it surely exist since we have the WithAtLeast + const interaction = __interaction as SetupDesiredProps< + Interaction, + WithAtLeast, + DesiredPropertiesBehavior.RemoveKey + > -export function commandOptionsParser(interaction: Interaction, options?: InteractionDataOption[]): ParsedInteractionOption { if (!interaction.data) return {} if (!options) options = interaction.data.options ?? [] - const args: ParsedInteractionOption = {} + const args: ParsedInteractionOption = {} for (const option of options) { switch (option.type) { case ApplicationCommandOptionTypes.SubCommandGroup: case ApplicationCommandOptionTypes.SubCommand: - args[option.name] = commandOptionsParser(interaction, option.options) + args[option.name] = commandOptionsParser(interaction, option.options) as InteractionResolvedData break case ApplicationCommandOptionTypes.Channel: - args[option.name] = interaction.data.resolved?.channels?.get(BigInt(option.value!)) as InteractionResolvedChannel + args[option.name] = interaction.data.resolved?.channels?.get(BigInt(option.value!)) as InteractionResolvedData break case ApplicationCommandOptionTypes.Role: - args[option.name] = interaction.data.resolved?.roles?.get(BigInt(option.value!)) as Role + args[option.name] = interaction.data.resolved?.roles?.get(BigInt(option.value!)) as InteractionResolvedData break case ApplicationCommandOptionTypes.User: args[option.name] = { - user: interaction.data.resolved?.users?.get(BigInt(option.value!)) as User, - member: interaction.data.resolved?.members?.get(BigInt(option.value!)) as InteractionResolvedMember, + user: interaction.data.resolved?.users?.get(BigInt(option.value!)) as InteractionResolvedData, + member: interaction.data.resolved?.members?.get(BigInt(option.value!)) as InteractionResolvedData, } break case ApplicationCommandOptionTypes.Attachment: - args[option.name] = interaction.data.resolved?.attachments?.get(BigInt(option.value!)) as Attachment + args[option.name] = interaction.data.resolved?.attachments?.get(BigInt(option.value!)) as InteractionResolvedData break case ApplicationCommandOptionTypes.Mentionable: // Mentionable are roles or users - args[option.name] = (interaction.data.resolved?.roles?.get(BigInt(option.value!)) as Role) ?? { - user: interaction.data.resolved?.users?.get(BigInt(option.value!)) as User, - member: interaction.data.resolved?.members?.get(BigInt(option.value!)) as InteractionResolvedMember, + args[option.name] = (interaction.data.resolved?.roles?.get(BigInt(option.value!)) as ParsedInteractionOption[string]) ?? { + user: interaction.data.resolved?.users?.get(BigInt(option.value!)) as InteractionResolvedData, + member: interaction.data.resolved?.members?.get(BigInt(option.value!)) as InteractionResolvedData, } break default: - args[option.name] = option.value as ParsedInteractionOption[string] + args[option.name] = option.value as InteractionResolvedData } } return args } -export interface ParsedInteractionOption { - [key: string]: string | number | boolean | InteractionResolvedUser | InteractionResolvedChannel | Role | Attachment | ParsedInteractionOption +export interface ParsedInteractionOption { + [key: string]: InteractionResolvedData } +export type InteractionResolvedData = + | string + | number + | boolean + | TransformProperty + | TransformProperty + | TransformProperty + | TransformProperty + | ParsedInteractionOption + export interface InteractionResolvedUser { user: User member: InteractionResolvedMember diff --git a/packages/bot/src/desiredProperties.ts b/packages/bot/src/desiredProperties.ts index 3ab18f74f..9b04467c2 100644 --- a/packages/bot/src/desiredProperties.ts +++ b/packages/bot/src/desiredProperties.ts @@ -729,6 +729,10 @@ type Complete = { [K in keyof TObj]-?: undefined extends TObj[K] ? TDefault : Exclude } +export type WithAtLeast> = T & { + [K in keyof T]: K extends keyof AtLeast ? Complete & T[K] : T[K] +} + type JoinTuple = T extends readonly [infer F extends string, ...infer R extends string[]] ? R['length'] extends 0 ? F @@ -811,30 +815,36 @@ type GetErrorWhenUndesired< TransformersDesiredPropertiesMetadata[KeyByValue]['dependencies'], TProps[KeyByValue] >, -> = TIsDesired extends true ? TransformNestedProps : TIsDesired +> = TIsDesired extends true ? TransformProperty : TIsDesired + +type IsObject = T extends object ? (T extends Function ? false : true) : false // If the object is a transformed object, a collection of transformed object or an array of transformed objects we need to apply the desired props to them as well -type TransformNestedProps< +export type TransformProperty< T, TProps extends TransformersDesiredProperties, TBehavior extends DesiredPropertiesBehavior, > = T extends TransformersObjects[keyof TransformersObjects] // is T a transformed object? ? // Yes, apply the desired props SetupDesiredProps - : // No, is it a collection of transformed objects? - T extends Collection - ? // Yes, apply the desired props - Collection> - : // No, is it an array of transformed objects? - T extends Array + : // No, is it a collection? + T extends Collection + ? // Yes, check for nested proprieties + Collection> + : // No, is it an array? + T extends Array ? // Yes, apply the desired props - SetupDesiredProps[] + TransformProperty[] : // No, is it a Bot? T extends Bot ? // Yes, return a bot with the correct set of props & behavior Bot - : // No, this is a normal value such as string / bigint / number - T + : // No, is this a generic object? If so we need to ensure nested inside there aren't transformed objects + IsObject extends true + ? // Yes, check of nested proprieties + { [K in keyof T]: TransformProperty } + : // No, this is a normal value such as string / bigint / number + T export type SetupDesiredProps< T extends TransformersObjects[keyof TransformersObjects], @@ -847,7 +857,7 @@ export type SetupDesiredProps< : Key]: // When the behavior is to change the type we use the GetErrorWhenUndesired type helper else apply the desired props to the key and return TBehavior extends DesiredPropertiesBehavior.ChangeType ? GetErrorWhenUndesired - : TransformNestedProps + : TransformProperty } export type TransformersDesiredProperties = {