Skip to content

Commit cbf960c

Browse files
authored
Merge branch 'Pycord-Development:master' into master
2 parents 46abcac + b905d5c commit cbf960c

File tree

10 files changed

+217
-70
lines changed

10 files changed

+217
-70
lines changed

.github/workflows/bypass-review.yml

Lines changed: 0 additions & 31 deletions
This file was deleted.

discord/bot.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
from .client import Client
4747
from .shard import AutoShardedClient
48-
from .utils import MISSING, get, async_all
48+
from .utils import MISSING, get, find, async_all
4949
from .commands import (
5050
SlashCommand,
5151
SlashCommandGroup,
@@ -59,6 +59,7 @@
5959

6060
from .errors import Forbidden, DiscordException
6161
from .interactions import Interaction
62+
from .enums import InteractionType
6263

6364
CoroFunc = Callable[..., Coroutine[Any, Any, Any]]
6465
CFT = TypeVar('CFT', bound=CoroFunc)
@@ -86,7 +87,7 @@ def pending_application_commands(self):
8687
return self._pending_application_commands
8788

8889
@property
89-
def commands(self) -> List[Union[ApplicationCommand, ...]]:
90+
def commands(self) -> List[Union[ApplicationCommand, Any]]:
9091
commands = list(self.application_commands.values())
9192
if self._supports_prefixed_commands:
9293
commands += self.prefixed_commands
@@ -188,7 +189,7 @@ async def register_commands(self) -> None:
188189
cmd = get(
189190
self.pending_application_commands,
190191
name=i["name"],
191-
description=i["description"],
192+
guild_ids=None,
192193
type=i["type"],
193194
)
194195
self.application_commands[i["id"]] = cmd
@@ -224,12 +225,7 @@ async def register_commands(self) -> None:
224225
raise
225226
else:
226227
for i in cmds:
227-
cmd = get(
228-
self.pending_application_commands,
229-
name=i["name"],
230-
description=i["description"],
231-
type=i["type"],
232-
)
228+
cmd = find(lambda cmd: cmd.name == i["name"] and cmd.type == i["type"] and int(i["guild_id"]) in cmd.guild_ids, self.pending_application_commands)
233229
self.application_commands[i["id"]] = cmd
234230

235231
# Permissions
@@ -364,14 +360,20 @@ async def process_application_commands(self, interaction: Interaction) -> None:
364360
interaction: :class:`discord.Interaction`
365361
The interaction to process
366362
"""
367-
if not interaction.is_command():
363+
if interaction.type not in (
364+
InteractionType.application_command,
365+
InteractionType.auto_complete
366+
):
368367
return
369368

370369
try:
371370
command = self.application_commands[interaction.data["id"]]
372371
except KeyError:
373372
self.dispatch("unknown_command", interaction)
374373
else:
374+
if interaction.type is InteractionType.auto_complete:
375+
return await command.invoke_autocomplete_callback(interaction)
376+
375377
ctx = await self.get_application_context(interaction)
376378
ctx.command = command
377379
self.dispatch("application_command", ctx)
@@ -456,7 +458,7 @@ def command(self, **kwargs):
456458
457459
.. note::
458460
459-
This decorator is overridden by :class:`commands.Bot`.
461+
This decorator is overridden by :class:`discord.ext.commands.Bot`.
460462
461463
.. versionadded:: 2.0
462464

discord/commands/commands.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import functools
3131
import inspect
3232
from collections import OrderedDict
33-
from typing import Any, Callable, Dict, List, Optional, Union
33+
from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
3434

3535
from ..enums import SlashCommandOptionType, ChannelType
3636
from ..member import Member
@@ -60,6 +60,9 @@
6060
"MessageCommand",
6161
)
6262

63+
if TYPE_CHECKING:
64+
from ..interactions import Interaction
65+
6366
def wrap_callback(coro):
6467
@functools.wraps(coro)
6568
async def wrapped(*args, **kwargs):
@@ -351,7 +354,7 @@ def __init__(self, func: Callable, *args, **kwargs) -> None:
351354
self.cog = None
352355

353356
params = self._get_signature_parameters()
354-
self.options = self._parse_options(params)
357+
self.options: List[Option] = self._parse_options(params)
355358

356359
try:
357360
checks = func.__commands_checks__
@@ -487,6 +490,17 @@ async def _invoke(self, ctx: ApplicationContext) -> None:
487490
else:
488491
await self.callback(ctx, **kwargs)
489492

493+
async def invoke_autocomplete_callback(self, interaction: Interaction):
494+
for op in interaction.data.get("options", []):
495+
if op.get("focused", False):
496+
option = find(lambda o: o.name == op["name"], self.options)
497+
result = await option.autocomplete(interaction, op.get("value", None))
498+
choices = [
499+
o if isinstance(o, OptionChoice) else OptionChoice(o)
500+
for o in result
501+
]
502+
await interaction.response.send_autocomplete_result(choices=choices)
503+
490504
def qualified_name(self):
491505
return self.name
492506

@@ -581,13 +595,21 @@ def __init__(
581595
if not (isinstance(self.max_value, minmax_types) or self.min_value is None):
582596
raise TypeError(f"Expected {minmax_typehint} for max_value, got \"{type(self.max_value).__name__}\"")
583597

598+
self.autocomplete = kwargs.pop("autocomplete", None)
599+
if (
600+
self.autocomplete and
601+
not asyncio.iscoroutinefunction(self.autocomplete)
602+
):
603+
raise TypeError("Autocomplete callback must be a coroutine.")
604+
584605
def to_dict(self) -> Dict:
585606
as_dict = {
586607
"name": self.name,
587608
"description": self.description,
588609
"type": self.input_type.value,
589610
"required": self.required,
590611
"choices": [c.to_dict() for c in self.choices],
612+
"autocomplete": bool(self.autocomplete)
591613
}
592614
if self.channel_types:
593615
as_dict["channel_types"] = [t.value for t in self.channel_types]
@@ -722,6 +744,13 @@ async def _invoke(self, ctx: ApplicationContext) -> None:
722744
ctx.interaction.data = option
723745
await command.invoke(ctx)
724746

747+
async def invoke_autocomplete_callback(self, interaction: Interaction) -> None:
748+
option = interaction.data["options"][0]
749+
command = find(lambda x: x.name == option["name"], self.subcommands)
750+
interaction.data = option
751+
await command.invoke_autocomplete_callback(interaction)
752+
753+
725754
class ContextMenuCommand(ApplicationCommand):
726755
r"""A class that implements the protocol for context menu commands.
727756

discord/guild.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ class Guild(Hashable):
239239
The number goes from 0 to 3 inclusive.
240240
premium_subscription_count: :class:`int`
241241
The number of "boosts" this guild currently has.
242+
premium_progress_bar_enabled: :class:`bool`
243+
Indicates if the guild has premium progress bar enabled.
244+
245+
.. versionadded:: 2.0
242246
preferred_locale: Optional[:class:`str`]
243247
The preferred locale for the guild. Used when filtering Server Discovery
244248
results to a specific language.
@@ -269,6 +273,7 @@ class Guild(Hashable):
269273
'max_video_channel_users',
270274
'premium_tier',
271275
'premium_subscription_count',
276+
'premium_progress_bar_enabled',
272277
'preferred_locale',
273278
'nsfw_level',
274279
'_members',
@@ -449,6 +454,7 @@ def _from_data(self, guild: GuildPayload) -> None:
449454
self.max_video_channel_users: Optional[int] = guild.get('max_video_channel_users')
450455
self.premium_tier: int = guild.get('premium_tier', 0)
451456
self.premium_subscription_count: int = guild.get('premium_subscription_count') or 0
457+
self.premium_progress_bar_enabled: bool = guild.get('premium_progress_bar_enabled') or False
452458
self._system_channel_flags: int = guild.get('system_channel_flags', 0)
453459
self.preferred_locale: Optional[str] = guild.get('preferred_locale')
454460
self._discovery_splash: Optional[str] = guild.get('discovery_splash')
@@ -1370,6 +1376,7 @@ async def edit(
13701376
preferred_locale: str = MISSING,
13711377
rules_channel: Optional[TextChannel] = MISSING,
13721378
public_updates_channel: Optional[TextChannel] = MISSING,
1379+
premium_progress_bar_enabled: bool = MISSING,
13731380
) -> Guild:
13741381
r"""|coro|
13751382
@@ -1447,6 +1454,8 @@ async def edit(
14471454
The new channel that is used for public updates from Discord. This is only available to
14481455
guilds that contain ``PUBLIC`` in :attr:`Guild.features`. Could be ``None`` for no
14491456
public updates channel.
1457+
premium_progress_bar_enabled: :class:`bool`
1458+
Whether the guild should have premium progress bar enabled.
14501459
reason: Optional[:class:`str`]
14511460
The reason for editing this guild. Shows up on the audit log.
14521461
@@ -1577,6 +1586,9 @@ async def edit(
15771586
)
15781587

15791588
fields['features'] = features
1589+
1590+
if premium_progress_bar_enabled is not MISSING:
1591+
fields['premium_progress_bar_enabled'] = premium_progress_bar_enabled
15801592

15811593
data = await http.edit_guild(self.id, reason=reason, **fields)
15821594
return Guild(data=data, state=self._state)

discord/http.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ def edit_guild(self, guild_id: Snowflake, *, reason: Optional[str] = None, **fie
10771077
'rules_channel_id',
10781078
'public_updates_channel_id',
10791079
'preferred_locale',
1080+
'premium_progress_bar_enabled',
10801081
)
10811082

10821083
payload = {k: v for k, v in fields.items() if k in valid_keys}

discord/interactions.py

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@
6161
from .ui.view import View
6262
from .channel import VoiceChannel, StageChannel, TextChannel, CategoryChannel, StoreChannel, PartialMessageable
6363
from .threads import Thread
64+
from .commands import OptionChoice
6465

6566
InteractionChannel = Union[
6667
VoiceChannel, StageChannel, TextChannel, CategoryChannel, StoreChannel, Thread, PartialMessageable
6768
]
6869

70+
6971
MISSING: Any = utils.MISSING
7072

7173

@@ -570,21 +572,6 @@ async def send_message(
570572
elif not all(isinstance(file, File) for file in files):
571573
raise InvalidArgument('files parameter must be a list of File')
572574

573-
if file is not None and files is not None:
574-
raise InvalidArgument('cannot pass both file and files parameter to send()')
575-
576-
if file is not None:
577-
if not isinstance(file, File):
578-
raise InvalidArgument('file parameter must be File')
579-
else:
580-
files = [file]
581-
582-
if files is not None:
583-
if len(files) > 10:
584-
raise InvalidArgument('files parameter must be a list of up to 10 elements')
585-
elif not all(isinstance(file, File) for file in files):
586-
raise InvalidArgument('files parameter must be a list of File')
587-
588575
parent = self._parent
589576
adapter = async_context.get()
590577
try:
@@ -707,7 +694,49 @@ async def edit_message(
707694

708695
self._responded = True
709696

697+
async def send_autocomplete_result(
698+
self,
699+
*,
700+
choices: List[OptionChoice],
701+
) -> None:
702+
"""|coro|
703+
Responds to this interaction by sending the autocomplete choices.
704+
705+
Parameters
706+
-----------
707+
choices: List[:class:`OptionChoice`]
708+
A list of choices.
710709
710+
Raises
711+
-------
712+
HTTPException
713+
Sending the result failed.
714+
InteractionResponded
715+
This interaction has already been responded to before.
716+
"""
717+
if self._responded:
718+
raise InteractionResponded(self._parent)
719+
720+
parent = self._parent
721+
722+
if parent.type is not InteractionType.auto_complete:
723+
return
724+
725+
payload = {
726+
"choices": [c.to_dict() for c in choices]
727+
}
728+
729+
adapter = async_context.get()
730+
await adapter.create_interaction_response(
731+
parent.id,
732+
parent.token,
733+
session=parent._session,
734+
type=InteractionResponseType.auto_complete_result.value,
735+
data=payload,
736+
)
737+
738+
self._responded = True
739+
711740
class _InteractionMessageState:
712741
__slots__ = ('_parent', '_interaction')
713742

@@ -840,4 +869,4 @@ async def inner_call(delay: float = delay):
840869

841870
asyncio.create_task(inner_call())
842871
else:
843-
await self._state._interaction.delete_original_message()
872+
await self._state._interaction.delete_original_message()

discord/types/guild.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class _GuildOptional(TypedDict, total=False):
6666
max_presences: Optional[int]
6767
max_members: int
6868
premium_subscription_count: int
69+
premium_progress_bar_enabled: bool
6970
max_video_channel_users: int
7071

7172

0 commit comments

Comments
 (0)