Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement autocomplete models #9

Closed
baptiste0928 opened this issue Mar 13, 2022 · 10 comments
Closed

Implement autocomplete models #9

baptiste0928 opened this issue Mar 13, 2022 · 10 comments
Labels
breaking Introduce breaking changes. enhancement New feature or request

Comments

@baptiste0928
Copy link
Owner

Autocomplete support has been removed in twilight-interactions 0.10 because the existing implementation was incorrect. It is necessary to re-implement this feature in a next release.

The main difference with regular command models is that autocomplete models receive partial and not validated data. In fact, some validation is performed client-side, but all fields, no matter their type, are sent by Discord as strings. Furthermore, all fields should be considered as optional except the current focused field.

Proposed implementation

The implementation that seems most suitable is the creation of a new AutocompleteModel trait with associated derive macros. This trait would allow creating models of autocomplete interactions, with the support for a focused attribute that specify which field expect to be focused (optional). It is also necessary to support subcommands.

#[derive(AutocompleteModel)]
pub struct HelloCommand {
    user: Option<String>,
    #[command(focused = true)]
    message: String,  // Not wrapped into an Option because it is the focused field
}

#[derive(AutocompleteModel)]
pub enum SubCommand {
    #[command(name = "hello")]
    Hello(HelloCommand),
}
@baptiste0928 baptiste0928 added enhancement New feature or request breaking Introduce breaking changes. labels Mar 13, 2022
@baptiste0928
Copy link
Owner Author

I started working on an implementation in the autocomplete-refactor branch. It's very messy and I don't have much time to work on it. If anyone else wants to continue working on it, please let me know 😄

@damccull
Copy link

How would you handle a case where there are multiple options on a command and any of them could be focused? In the example your decorator is a compile-time macro, so you wouldn't be able to shift to another field on the fly right?

@baptiste0928
Copy link
Owner Author

Yes, this could be a problem in these cases. Another implementation was proposed by @itohatweb on Discord:

enum AutocompleteValue<T> {
    None,
    Focused(String),
    Completed(T)
}

#[derive(CommandModel, Debug)]
#[command(partial = true)]
struct AutocompleteCmd {
    num: AutocompleteValue<i32>,
}

This would solve the problem, but the use would be a bit more tedious. But it also seems easier to implement, I'll try to look at this during the week.

@damccull
Copy link

I'm currently manually handling this with something I saw @laralove143 do, though I modified it to make it generic. I think I'd add a focused field to this for more than one field needed during autocompletes though.

#[derive(Clone, Debug)]
pub enum InteractionAutocompleteOption<T> {
    /// The interaction is sent completely, as if a complete command.
    Complete(T),
    /// The interaction is sent partially, as if being autocompleted.
    Partial(String),
}

This is used on my individual command structs with a impl TryFrom<Vec<ApplicationCommandAutocompleteDataOption>> and impl TryFrom<Vec<CommandDataOption>>.

#[derive(Clone, Debug)]
pub struct AddCommand {
    pub ship_model: InteractionAutocompleteOption<String>,
    pub ship_name: Option<String>,
}

In the autocomplete case try_from returns the enum variant Partial, and in the command case try_from returns the Complete variant. I have to do all this manually, of course, and this command isn't even using twilight_interactions at all, but maybe this will give you some brainstorming power.

@baptiste0928
Copy link
Owner Author

Blocking on twilight-rs/twilight#1614

@CircuitSacul
Copy link
Contributor

CircuitSacul commented Jun 24, 2022

[EDIT] Nevermind I don't think this is a good idea lol

I don't know how hard this would be, but would something like this be possible:

#[derive(CommandModel, CreateCommand)]
#[command(name = "search", desc = "Search the web.")]
pub struct Hello {
    #[command(autocomplete=true, desc="The service to use.")]
    service: String,
    #[command(autocomplete=true, desc="The query.")]
    query: String,
    #[command(autocomplete=true, desc="Maximum results to return.")]
    max_results: Option<i32>,
}

Then, you can match the InteractionCreate:

int: InteractionCreate;

match int.0 {
    Interaction::ApplicationCommand(data) => {
        let cmd = Hello::from_interaction(data)?;
        // Handle the cmd. All the fields are as they're defined
        // on the struct.
    }
    Interaction::ApplicationCommandAutocomplete(data) => {
        let cmd = from_autocomplete!(Hello, data);
        // Create some sort of anonymous struct, kinda like
        // AutocompleteData {
        //     command_parents: Vec<String> // 1 or 2 parents, if this command is a subcommand
        //     command_name: String,
        //     focused_field: String,
        //     focused_data: String,
        //     options: PartialCommand {
        //         service: AutocompleteValue<String>,
        //         query: AutocompleteValue<String>,
        //         max_results: AutocompleteValue<Option<i32>>,
        //     }
        // }
        // Where AutocompleteValue is
        // enum AutoCompleteValue<T> {
        //     NoData, // No data yet
        //     Complete(T), // This field has been completed
        //     Focused(String), // This is the field currently being autocompleted
        // }
    }
}

I have seen sqlx create anonymous structs, so I know that part is possible, but I don't know how hard it would be to create an anonymous struct from an existing struct, by changing the column types.

This may be entirely impossible or just too much work, but I thought I'd share my idea anyways.

@baptiste0928
Copy link
Owner Author

Autocomplete are now supported in the next branch (a7191c1), that depend on the not yet released refactor of autocomplete interactions handling in twilight (twilight-rs/twilight#1614).

It requires using the next branch from twilight too (which has major breaking changes regarding interactions), but I would be happy to have your opinion on the implementation I made.

use twilight_interactions::command::{AutocompleteValue, CommandModel, ResolvedUser};

#[derive(CommandModel)]
#[command(autocomplete = true)]
struct HelloCommand {
    message: AutocompleteValue<String>,
    user: Option<ResolvedUser>,
}

Autocomplete interactions are supported with the #[command(autocomplete = true)] attribute and the AutocompleteValue type, defined roughly like this:

pub enum AutocompleteValue<T> {
    None,
    Focused(String),
    Completed(T),
}

Autocomplete interactions have some restrictions compared to "default" ones: all fields must be AutocompleteValue or Options and CreateCommand cannot be implemented on these types. The parsing is also partial, so you can skip fields you don't need for autocomplete (but still need to handle the full command).

@CircuitSacul
Copy link
Contributor

I found a way that's easy for my use case (where I don't need access to any of the variables). My command structs look like this:

#[derive(CommandModel, CreateCommand)]
#[command(name="name", desc="desc")]
struct MyCommand {
    /// Option name
    #[command(autocomplete=true)]
    option: String
}

Will this still work after the next release?

@baptiste0928
Copy link
Owner Author

@CircuitSacul Autocomplete models are separate from the "normal" ones, it will not affect your existing models at all.

@baptiste0928
Copy link
Owner Author

Released with twilight-interactions 0.12.0 (full changelog).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Introduce breaking changes. enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants