Skip to content

Adding option resolvers

Option resolvers help you support other types for your command options, such as TimeUnit, or any object of your own.

Slash command option resolvers specify which option type will be used on Discord, and will handle the conversion from the Discord value to the corresponding object.

The class implementing the resolver, or the function returning a resolver, must be annotated with @Resolver.

Note

@Resolver is one of the annotations that are considered as a service annotation. This means that it behaves exactly the same as if you had used @BService, except here the annotation is more meaningful.

Implementation

For that, you need a class annotated with @Resolver extending ClassParameterResolver, and implementing SlashParameterResolver.

The first type parameter is the type of your resolver implementation, and the second type is what the resolver returns.

A TimeUnit resolver

@Resolver
class TimeUnitResolver :
    ClassParameterResolver<TimeUnitResolver, TimeUnit>(TimeUnit::class),
    SlashParameterResolver<TimeUnitResolver, TimeUnit> {

    override val optionType: OptionType = OptionType.STRING

    // This is all you need to implement to support predefined choices
    override fun getPredefinedChoices(guild: Guild?): Collection<Choice> {
        return listOf(TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS)
            // The Resolvers class helps us by providing resolvers for any enum type.
            // We're just using the helper method to change an enum value to a more natural name.
            .map { Choice(it.toHumanName(), it.name) }
    }

    override suspend fun resolveSuspend(
        info: SlashCommandInfo,
        event: CommandInteractionPayload,
        optionMapping: OptionMapping
    ): TimeUnit = enumValueOf<TimeUnit>(optionMapping.asString)
}
@Resolver
public class TimeUnitResolver
        extends ClassParameterResolver<TimeUnitResolver, TimeUnit>
        implements SlashParameterResolver<TimeUnitResolver, TimeUnit> {

    public TimeUnitResolver() {
        super(TimeUnit.class);
    }

    @NotNull
    @Override
    public OptionType getOptionType() {
        return OptionType.STRING;
    }

    @NotNull
    @Override
    public Collection<Command.Choice> getPredefinedChoices(@Nullable Guild guild) {
        return Stream.of(TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS)
                // The Resolvers class helps us by providing resolvers for any enum type.
                // We're just using the helper method to change an enum value to a more natural name.
                .map(u -> new Command.Choice(Resolvers.toHumanName(u), u.name()))
                .toList();
    }

    @Nullable
    @Override
    public TimeUnit resolve(@NotNull SlashCommandInfo info, @NotNull CommandInteractionPayload event, @NotNull OptionMapping optionMapping) {
        return TimeUnit.valueOf(optionMapping.getAsString());
    }
}

As you can see, this defines the slash command's option to be a string, and provides predefined choices, letting you easily use them in your commands.

Built-in resolver generators

The framework also provides functions in Resolvers to do most of the work for some types, all you need to do is declare a service factory with @Resolver and use the provided methods.

Note

Currently there is only a factory for enum resolvers, but others might be added in the future.

How to easily make a resolver for an enum type

object TimeUnitResolverSimplified {
    // The displayed name should be lowercase with the first letter uppercase, see Resolvers#toHumanName
    @Resolver
    fun getTimeUnitResolverSimplified() = enumResolver<TimeUnit>(TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS)
}
As this functions as a service factory, the method needs to be in an object or have a no-arg constructor.

public class TimeUnitResolverSimplifiedJava {
    @Resolver
    public static ParameterResolver<?, TimeUnit> getTimeUnitResolverSimplified() {
        // The displayed name should be lowercase with the first letter uppercase, see Resolvers#toHumanName
        return Resolvers.enumResolver(TimeUnit.class, EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS)).build();
    }
}
As this functions as a service factory, the method needs to be static.