Skip to content

Using autocomplete

Autocomplete lets you have options where you can give suggestions to the user while they type, the framework allows you to return a collection of choices, choice-compatible types such as String, Long and Double, or even custom types, all of which can be cached.

Note

Autocompleted options do not force the user to choose one of the returned choices, they can still type anything.

Creating autocomplete handlers

You will have to implement the AutocompleteHandlerProvider, enabling you to declare autocomplete handlers using the manager.

You can optionally put a name on the handler, if you plan on using autocompleteByName, however, that's not necessary when using autocompleteByFunction.

@BService
class SlashWordAutocompleteDsl : AutocompleteHandlerProvider {
    // https://en.wikipedia.org/wiki/Dolch_word_list#Dolch_list:_Nouns
    // but 30 words
    private val words = listOf(
        "apple", "baby", "back", "ball", "bear", "bed", "bell", "bird", "birthday", "boat",
        "box", "boy", "bread", "brother", "cake", "car", "cat", "chair", "chicken", "children",
        "Christmas", "coat", "corn", "cow", "day", "dog", "doll", "door", "duck", "egg"
    )

    // You can also make this return a collection of Choice, see the AutocompleteManager#autocomplete docs
    fun onWordAutocomplete(event: CommandAutoCompleteInteractionEvent): Collection<String> {
        // Here you would typically filter the words based on what the user inputs,
        // but it is already done when you return a Collection<String>
        return words
    }

    // All autocomplete declarations run before any command is registered,
    // so you can, in theory, add autocomplete handlers anywhere,
    // and use them in any command.
    override fun declareAutocomplete(manager: AutocompleteManager) {
        manager.autocomplete(::onWordAutocomplete)
    }
}

You will have to use @AutocompleteHandler, give it a unique name, I'd recommend using one similar to ClassName: optionName, it will be useful to reference it in commands later on.

Info

An annotated autocomplete handler can still be referenced by name and by function in code-declared commands.

@Handler // Required by the AutocompleteHandler annotation, can be replaced with @Command
class SlashWordAutocomplete {
    // https://en.wikipedia.org/wiki/Dolch_word_list#Dolch_list:_Nouns
    // but 30 words
    private val words = listOf(
        "apple", "baby", "back", "ball", "bear", "bed", "bell", "bird", "birthday", "boat",
        "box", "boy", "bread", "brother", "cake", "car", "cat", "chair", "chicken", "children",
        "Christmas", "coat", "corn", "cow", "day", "dog", "doll", "door", "duck", "egg"
    )

    // You can also make this return a collection of Choice, see the annotation docs
    @AutocompleteHandler(WORD_AUTOCOMPLETE_NAME)
    fun onWordAutocomplete(event: CommandAutoCompleteInteractionEvent): Collection<String> {
        // Here you would typically filter the words based on what the user inputs,
        // but it is already done when you return a Collection<String>
        return words
    }

    companion object {
        const val WORD_AUTOCOMPLETE_NAME = "SlashWord: word"
    }
}
@Handler // Required by the AutocompleteHandler annotation, can be replaced with @Command
public class SlashWordAutocomplete extends ApplicationCommand {
    // https://en.wikipedia.org/wiki/Dolch_word_list#Dolch_list:_Nouns
    // but 30 words
    private static final List<String> WORDS = List.of(
            "apple", "baby", "back", "ball", "bear", "bed", "bell", "bird", "birthday", "boat",
            "box", "boy", "bread", "brother", "cake", "car", "cat", "chair", "chicken", "children",
            "Christmas", "coat", "corn", "cow", "day", "dog", "doll", "door", "duck", "egg"
    );
    public static final String WORD_AUTOCOMPLETE_NAME = "SlashWord: word";

    // You can also make this return a collection of Choice, see the annotation docs
    @AutocompleteHandler(WORD_AUTOCOMPLETE_NAME)
    public Collection<String> onWordAutocomplete(CommandAutoCompleteInteractionEvent event) {
        // Here you would typically filter the words based on what the user inputs,
        // but it is already done when you return a Collection<String>
        return WORDS;
    }
}

You may also configure other properties:

  • showUserInput: Makes the first choice be the user's own input
  • mode: Lets you configure out the automatic choice sorter (for String/Long/Double only)

Sorting autocomplete results of Choice and custom types

Sorting results by relevancy is a tricky task, while it can be as simple as myItemName.startsWith(input) you can try to use AutocompleteAlgorithms to easily sort the results. This is what gets applied on primitive types, but the results won't always be the best.

It may sometimes makes more sense to use one "similarity" algorithm over another, depending on what user input you expect, and what the source items are, you can experiment different algos from the java-string-similarity library, already included in the framework.

You are encouraged to to try inputs against different algos, and find what works the best, which one could filter the results of the previous algo, etc.

Caching

When the results are stable, you can enable autocomplete caching, saving time when a user types the same query.

To enable it, configure the cache using the cache configurer.

By default it will cache by using the user input, but you can add arguments to the cache key by:

To enable it, configure the cache using @CacheAutocomplete.

By default it will cache by using the user input, but you can add arguments to the cache key by:

Note

If the outputs for the same input are stable but may rarely change (think, a list that updates daily), you can invalidate autocomplete caches when it eventually does.

Tip

You can also disable the autocomplete cache while developing your bot with the disableAutocompleteCache property, this should help you test your handler live, using hotswap.

Transforming elements into choices

If you wish to return collections of anything but the default supported types, you will need to create a service which transforms those objects into choices, by implementing AutocompleteTranformer.

Example

FullName.kt
data class FullName(val firstName: String, val secondName: String)
FullNameTransformer.kt
@BService
class FullNameTransformer : AutocompleteTransformer<FullName> {
    override val elementType: Class<FullName> = FullName::class.java

    override fun apply(e: FullName): Command.Choice {
        return Command.Choice("${e.firstName} ${e.secondName}", "${e.firstName}|${e.secondName}")
    }
}
FullName.java
public record FullName(String firstName, String secondName) { }
FullNameTransformer.java
@BService
public class FullNameTransformer implements AutocompleteTransformer<FullName> {
    @NotNull
    @Override
    public Class<FullName> getElementType() {
        return FullName.class;
    }

    @NotNull
    @Override
    public Command.Choice apply(@NotNull FullName fullName) {
        return new Command.Choice(
                "%s %s".formatted(fullName.firstName(), fullName.secondName()),
                "%s|%s".formatted(fullName, fullName.secondName())
        );
    }
}

Usage in commands

Discover how to use your autocomplete handlers on: