pythonnextcord

Nextcord Dynamically Get Choices


I am trying to use nextcord to get all the choices from a user's inventory, but I am having trouble getting the user_id for my fetch_choices function. See below for some more details.

My slash command looks like this, (... is where I want userid):

async def duel(interaction: nextcord.Interaction, item: str = nextcord.SlashOption("item", choices=fetch_choices(...)), quantity: int = 1, side: str = "h"):

This is my fetch_choices code:

def fetch_choices(user_id):
    c.execute("SELECT * FROM items WHERE user_id=?", (user_id,))
    rows = c.fetchall()
    items = {}
    for row in rows:
        item = row[1],
        items.update({item[0]: item[0]})
    return items

My question is how can I get the user id, and pass it to fetch_choices?


Solution

  • Unfortunately, this isn't possible at the moment. It would be great if we could somehow pass the interaction into the SlashOption class and use it to generate a different default value for every user, but as a comment on this post states, default values in python are calculated one time. Ideally, nextcord would allow us to provide a callback function, which could generate these options on-the-fly for each interaction, but that feature doesn't exist yet.

    BUT

    We can get almost the same experience by using the autocomplete feature, instead of choices:

    async def duel(interaction: nextcord.Interaction, item: str = nextcord.SlashOption("item"), quantity: int = 1, side: str = "h"):
       pass
    
    @duel.on_autocomplete("item")
    async def item_autocomplete(interaction: nextcord.Interaction, item: str):
        choices = fetch_choices(interaction.user.id, item)
        await interaction.response.send_autocomplete(choices)
    
    def fetch_choices(user_id, name_substr=''):
        pattern = f'%{name_substr}%'
        c.execute("SELECT * FROM items WHERE user_id=? AND name LIKE ?", (user_id, pattern))
        rows = c.fetchall()
        items = []
        for row in rows:
            items.append(row[1])
        return items
    

    A couple caveats:

    1. The fetch_choices function will run every time a character is typed into the slash option. If number of queries isn't an issue, this is probably fine. Otherwise, some caching might be a good idea.
    2. If any items contain the % character (or any other special sqlite pattern characters), pattern = f'%{name_substr}%' might not always work. There are other options, like using instr, but I figured I'd give the simplest solution first.

    At the time of writing, this works almost exactly the same as providing choices to SlashOption. However, it takes a bit longer for the options to appear, and there is no error for invalid items, since autocomplete is just a suggestion. Of course, you can handle invalid arguments in your slash function, but you won't get the nice red box that discord displays for an invalid SlashOption.

    screenshot of discord text input showing a slash option configured with autocomplete with a normal border, and a slash option configured with choices with a red border