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

Add support for autocomplete #277

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add support for autocomplete
  • Loading branch information
CodeWithSwastik committed Oct 17, 2021
commit 8a4ab40477ccadbbcffdd00cae997ec29748c80c
18 changes: 17 additions & 1 deletion discord/app/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def __init__(self, func: Callable, *args, **kwargs) -> None:
self.is_subcommand: bool = False

params = OrderedDict(inspect.signature(self.callback).parameters)
self.options = self.parse_options(params)
self.options: List[Option] = self.parse_options(params)

def parse_options(self, params: OrderedDict) -> List[Option]:
final_options = []
Expand Down Expand Up @@ -167,6 +167,16 @@ async def invoke(self, ctx: InteractionContext) -> None:
kwargs[o.name] = o.default
await self.callback(ctx, **kwargs)

async def invoke_autocomplete_callback(self, interaction):
CodeWithSwastik marked this conversation as resolved.
Show resolved Hide resolved
for op in interaction.data.get("options", []):
if op.get("focused", False):
option = find(lambda o: o.name == op["name"], self.options)
result = await option.autocomplete_callback(interaction, op.get("value", None))
choices = [
o if isinstance(o, OptionChoice) else OptionChoice(o)
for o in result
]
await interaction.response.send_autocomplete_result(choices = choices)
CodeWithSwastik marked this conversation as resolved.
Show resolved Hide resolved

class Option:
def __init__(
Expand All @@ -183,6 +193,11 @@ def __init__(
for o in kwargs.pop("choices", list())
]
self.default = kwargs.pop("default", None)
self.autocomplete_callback = kwargs.pop("autocomplete", None)
CodeWithSwastik marked this conversation as resolved.
Show resolved Hide resolved
if self.autocomplete_callback:
if not asyncio.iscoroutinefunction(self.autocomplete_callback):
raise TypeError("Autocomplete callback must be a coroutine.")


def to_dict(self) -> Dict:
return {
Expand All @@ -191,6 +206,7 @@ def to_dict(self) -> Dict:
"type": self.input_type.value,
"required": self.required,
"choices": [c.to_dict() for c in self.choices],
"autocomplete": bool(self.autocomplete_callback)
}

def __repr__(self):
Expand Down
12 changes: 9 additions & 3 deletions discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
)
from .errors import Forbidden
from .interactions import Interaction

from .enums import InteractionType

def command(cls=SlashCommand, **attrs):
"""A decorator that transforms a function into an :class:`.ApplicationCommand`. More specifically,
Expand Down Expand Up @@ -240,13 +240,19 @@ async def handle_interaction(self, interaction: Interaction) -> None:
interaction: :class:`discord.Interaction`
The interaction to process
"""
if interaction.type not in (InteractionType.application_command, InteractionType.auto_complete):
return

try:
command = self.app_commands[interaction.data["id"]]
except KeyError:
self.dispatch("unknown_command", interaction)
else:
context = await self.get_application_context(interaction)
await command.invoke(context)
if interaction.type == InteractionType.application_command:
CodeWithSwastik marked this conversation as resolved.
Show resolved Hide resolved
context = await self.get_application_context(interaction)
await command.invoke(context)
else:
await command.invoke_autocomplete_callback(interaction)

def slash_command(self, **kwargs):
"""A shortcut decorator that invokes :func:`.ApplicationCommandMixin.command` and adds it to
Expand Down
44 changes: 44 additions & 0 deletions discord/interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
from .ui.view import View
from .channel import VoiceChannel, StageChannel, TextChannel, CategoryChannel, StoreChannel, PartialMessageable
from .threads import Thread
from .app import OptionChoice
BobDotCom marked this conversation as resolved.
Show resolved Hide resolved

InteractionChannel = Union[
VoiceChannel, StageChannel, TextChannel, CategoryChannel, StoreChannel, Thread, PartialMessageable
Expand Down Expand Up @@ -677,6 +678,49 @@ async def edit_message(

self._responded = True

async def send_autocomplete_result(
self,
*,
choices: List[OptionChoice],
) -> None:
"""|coro|

Responds to this interaction by sending the autocomplete choices.

Parameters
-----------
choices: List[:class:`OptionChoice`]
A list of choices.

Raises
-------
HTTPException
Sending the result failed.
InteractionResponded
This interaction has already been responded to before.
"""
if self._responded:
raise InteractionResponded(self._parent)

parent = self._parent

if parent.type is not InteractionType.auto_complete:
return

payload = {
"choices": [c.to_dict() for c in choices]
}

adapter = async_context.get()
await adapter.create_interaction_response(
parent.id,
parent.token,
session=parent._session,
type=InteractionResponseType.auto_complete_result.value,
data=payload,
)

self._responded = True

class _InteractionMessageState:
__slots__ = ('_parent', '_interaction')
Expand Down