Skip to content

Commit 6fbb08e

Browse files
authored
add enums, do command stuff, add context to interaction
1 parent 6f1ad85 commit 6fbb08e

File tree

4 files changed

+128
-16
lines changed

4 files changed

+128
-16
lines changed

discord/commands/core.py

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@
4747

4848
from ..channel import _threaded_guild_channel_factory
4949
from ..enums import Enum as DiscordEnum
50-
from ..enums import MessageType, SlashCommandOptionType, try_enum
50+
from ..enums import IntegrationType, InteractionContextType, MessageType, SlashCommandOptionType, try_enum
5151
from ..errors import (
5252
ApplicationCommandError,
5353
ApplicationCommandInvokeError,
5454
CheckFailure,
5555
ClientException,
56+
InvalidArgument,
5657
ValidationError,
5758
)
5859
from ..member import Member
@@ -61,7 +62,7 @@
6162
from ..role import Role
6263
from ..threads import Thread
6364
from ..user import User
64-
from ..utils import MISSING, async_all, find, maybe_coroutine, utcnow
65+
from ..utils import MISSING, async_all, find, maybe_coroutine, utcnow, warn_deprecated
6566
from .context import ApplicationContext, AutocompleteContext
6667
from .options import Option, OptionChoice
6768

@@ -226,11 +227,33 @@ def __init__(self, func: Callable, **kwargs) -> None:
226227
"__default_member_permissions__",
227228
kwargs.get("default_member_permissions", None),
228229
)
229-
self.guild_only: bool | None = getattr(
230-
func, "__guild_only__", kwargs.get("guild_only", None)
231-
)
232230
self.nsfw: bool | None = getattr(func, "__nsfw__", kwargs.get("nsfw", None))
233231

232+
integration_types = getattr(
233+
func, "__integration_types__", kwargs.get("integration_types", None)
234+
)
235+
contexts = getattr(
236+
func, "__contexts__", kwargs.get("contexts", None)
237+
)
238+
guild_only = getattr(
239+
func, "__guild_only__", kwargs.get("guild_only", MISSING)
240+
)
241+
if guild_only is not MISSING:
242+
warn_deprecated("guild_only", "contexts", "2.6")
243+
if contexts and guild_only:
244+
raise InvalidArgument("cannot pass both 'contexts' and 'guild_only' to ApplicationCommand")
245+
if self.guild_ids and ((contexts is not None) or guild_only or integration_types):
246+
raise InvalidArgument("the 'contexts' and 'integration_types' parameters are not available for guild commands")
247+
248+
self.contexts: set[InteractionContextType] = contexts or {
249+
InteractionContextType.guild,
250+
InteractionContextType.bot_dm,
251+
InteractionContextType.private_channel,
252+
}
253+
if guild_only:
254+
self.guild_only: bool | None = guild_only
255+
self.integration_types: set[IntegrationType] = integration_types or {IntegrationType.guild_install}
256+
234257
def __repr__(self) -> str:
235258
return f"<discord.commands.{self.__class__.__name__} name={self.name}>"
236259

@@ -274,6 +297,19 @@ def callback(
274297
unwrap = unwrap_function(function)
275298
self.module = unwrap.__module__
276299

300+
@property
301+
def guild_only(self) -> bool:
302+
warn_deprecated("guild_only", "contexts", "2.6")
303+
return InteractionContextType.guild in self.contexts and len(self.contexts) == 1
304+
305+
@guild_only.setter
306+
def guild_only(self, value: bool) -> None:
307+
warn_deprecated("guild_only", "contexts", "2.6")
308+
if value:
309+
self.contexts = {InteractionContextType.guild}
310+
else:
311+
self.contexts = {InteractionContextType.guild, InteractionContextType.bot_dm, InteractionContextType.private_channel}
312+
277313
def _prepare_cooldowns(self, ctx: ApplicationContext):
278314
if self._buckets.valid:
279315
current = datetime.datetime.now().timestamp()
@@ -631,6 +667,9 @@ class SlashCommand(ApplicationCommand):
631667
Returns a string that allows you to mention the slash command.
632668
guild_only: :class:`bool`
633669
Whether the command should only be usable inside a guild.
670+
671+
.. deprecated:: 2.6
672+
Use the ``contexts`` parameter instead.
634673
nsfw: :class:`bool`
635674
Whether the command should be restricted to 18+ channels and users.
636675
Apps intending to be listed in the App Directory cannot have NSFW commands.
@@ -654,6 +693,10 @@ class SlashCommand(ApplicationCommand):
654693
description_localizations: Dict[:class:`str`, :class:`str`]
655694
The description localizations for this command. The values of this should be ``"locale": "description"``.
656695
See `here <https://discord.com/developers/docs/reference#locales>`_ for a list of valid locales.
696+
integration_types: set[:class:`IntegrationType`]
697+
The installation contexts where this command is available. Cannot be set if this is a guild command.
698+
contexts: set[:class:`InteractionContextType`]
699+
The interaction contexts where this command is available. Cannot be set if this is a guild command.
657700
"""
658701

659702
type = 1
@@ -881,9 +924,6 @@ def to_dict(self) -> dict:
881924
if self.is_subcommand:
882925
as_dict["type"] = SlashCommandOptionType.sub_command.value
883926

884-
if self.guild_only is not None:
885-
as_dict["dm_permission"] = not self.guild_only
886-
887927
if self.nsfw is not None:
888928
as_dict["nsfw"] = self.nsfw
889929

@@ -892,6 +932,10 @@ def to_dict(self) -> dict:
892932
self.default_member_permissions.value
893933
)
894934

935+
if not self.guild_ids:
936+
as_dict["integration_types"] = [it.value for it in self.integration_types]
937+
as_dict["contexts"] = [ctx.value for ctx in self.contexts]
938+
895939
return as_dict
896940

897941
async def _invoke(self, ctx: ApplicationContext) -> None:
@@ -1100,6 +1144,9 @@ class SlashCommandGroup(ApplicationCommand):
11001144
isn't one.
11011145
guild_only: :class:`bool`
11021146
Whether the command should only be usable inside a guild.
1147+
1148+
.. deprecated:: 2.6
1149+
Use the ``contexts`` parameter instead.
11031150
nsfw: :class:`bool`
11041151
Whether the command should be restricted to 18+ channels and users.
11051152
Apps intending to be listed in the App Directory cannot have NSFW commands.
@@ -1118,6 +1165,10 @@ class SlashCommandGroup(ApplicationCommand):
11181165
description_localizations: Dict[:class:`str`, :class:`str`]
11191166
The description localizations for this command. The values of this should be ``"locale": "description"``.
11201167
See `here <https://discord.com/developers/docs/reference#locales>`_ for a list of valid locales.
1168+
integration_types: set[:class:`IntegrationType`]
1169+
The installation contexts where this command is available. Cannot be set if this is a guild command.
1170+
contexts: set[:class:`InteractionContextType`]
1171+
The interaction contexts where this command is available. Cannot be set if this is a guild command.
11211172
"""
11221173

11231174
__initial_commands__: list[SlashCommand | SlashCommandGroup]
@@ -1174,12 +1225,28 @@ def __init__(
11741225
self.id = None
11751226

11761227
# Permissions
1177-
self.default_member_permissions: Permissions | None = kwargs.get(
1178-
"default_member_permissions", None
1179-
)
1180-
self.guild_only: bool | None = kwargs.get("guild_only", None)
1228+
self.default_member_permissions: Permissions | None = kwargs.get("default_member_permissions", None)
11811229
self.nsfw: bool | None = kwargs.get("nsfw", None)
11821230

1231+
integration_types = kwargs.get("integration_types", None)
1232+
contexts = kwargs.get("contexts", None)
1233+
guild_only = kwargs.get("guild_only", MISSING)
1234+
if guild_only is not MISSING:
1235+
warn_deprecated("guild_only", "contexts", "2.6")
1236+
if contexts and guild_only:
1237+
raise InvalidArgument("cannot pass both 'contexts' and 'guild_only' to ApplicationCommand")
1238+
if self.guild_ids and ((contexts is not None) or guild_only or integration_types):
1239+
raise InvalidArgument("the 'contexts' and 'integration_types' parameters are not available for guild commands")
1240+
1241+
self.contexts: set[InteractionContextType] = contexts or {
1242+
InteractionContextType.guild,
1243+
InteractionContextType.bot_dm,
1244+
InteractionContextType.private_channel,
1245+
}
1246+
if guild_only:
1247+
self.guild_only: bool | None = guild_only
1248+
self.integration_types: set[IntegrationType] = integration_types or {IntegrationType.guild_install}
1249+
11831250
self.name_localizations: dict[str, str] = kwargs.get(
11841251
"name_localizations", MISSING
11851252
)
@@ -1218,6 +1285,19 @@ def __init__(
12181285
def module(self) -> str | None:
12191286
return self.__module__
12201287

1288+
@property
1289+
def guild_only(self) -> bool:
1290+
warn_deprecated("guild_only", "contexts", "2.6")
1291+
return InteractionContextType.guild in self.contexts and len(self.contexts) == 1
1292+
1293+
@guild_only.setter
1294+
def guild_only(self, value: bool) -> None:
1295+
warn_deprecated("guild_only", "contexts", "2.6")
1296+
if value:
1297+
self.contexts = {InteractionContextType.guild}
1298+
else:
1299+
self.contexts = {InteractionContextType.guild, InteractionContextType.bot_dm, InteractionContextType.private_channel}
1300+
12211301
def to_dict(self) -> dict:
12221302
as_dict = {
12231303
"name": self.name,
@@ -1232,9 +1312,6 @@ def to_dict(self) -> dict:
12321312
if self.parent is not None:
12331313
as_dict["type"] = self.input_type.value
12341314

1235-
if self.guild_only is not None:
1236-
as_dict["dm_permission"] = not self.guild_only
1237-
12381315
if self.nsfw is not None:
12391316
as_dict["nsfw"] = self.nsfw
12401317

@@ -1243,6 +1320,10 @@ def to_dict(self) -> dict:
12431320
self.default_member_permissions.value
12441321
)
12451322

1323+
if not self.guild_ids:
1324+
as_dict["integration_types"] = [it.value for it in self.integration_types]
1325+
as_dict["contexts"] = [ctx.value for ctx in self.contexts]
1326+
12461327
return as_dict
12471328

12481329
def add_command(self, command: SlashCommand) -> None:
@@ -1476,6 +1557,9 @@ class ContextMenuCommand(ApplicationCommand):
14761557
The ids of the guilds where this command will be registered.
14771558
guild_only: :class:`bool`
14781559
Whether the command should only be usable inside a guild.
1560+
1561+
.. deprecated:: 2.6
1562+
Use the ``contexts`` parameter instead.
14791563
nsfw: :class:`bool`
14801564
Whether the command should be restricted to 18+ channels and users.
14811565
Apps intending to be listed in the App Directory cannot have NSFW commands.
@@ -1496,6 +1580,10 @@ class ContextMenuCommand(ApplicationCommand):
14961580
name_localizations: Dict[:class:`str`, :class:`str`]
14971581
The name localizations for this command. The values of this should be ``"locale": "name"``. See
14981582
`here <https://discord.com/developers/docs/reference#locales>`_ for a list of valid locales.
1583+
integration_types: set[:class:`IntegrationType`]
1584+
The installation contexts where this command is available. Cannot be set if this is a guild command.
1585+
contexts: set[:class:`InteractionContextType`]
1586+
The interaction contexts where this command is available. Cannot be set if this is a guild command.
14991587
"""
15001588

15011589
def __new__(cls, *args, **kwargs) -> ContextMenuCommand:

discord/enums.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,22 @@ class EntitlementOwnerType(Enum):
10201020
user = 2
10211021

10221022

1023+
class IntegrationType(Enum):
1024+
"""The application's integration type"""
1025+
1026+
guild_install = 1
1027+
user_install = 2
1028+
1029+
1030+
class InteractionContextType(Enum):
1031+
"""The interaction's context type"""
1032+
1033+
guild = 0
1034+
bot_dm = 1
1035+
private_channel = 2
1036+
1037+
1038+
10231039
T = TypeVar("T")
10241040

10251041

discord/interactions.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
from . import utils
3232
from .channel import ChannelType, PartialMessageable, _threaded_channel_factory
33-
from .enums import InteractionResponseType, InteractionType, try_enum
33+
from .enums import InteractionResponseType, InteractionContextType, InteractionType, try_enum
3434
from .errors import ClientException, InteractionResponded, InvalidArgument
3535
from .file import File
3636
from .flags import MessageFlags
@@ -131,7 +131,12 @@ class Interaction:
131131
The guilds preferred locale, if invoked in a guild.
132132
custom_id: Optional[:class:`str`]
133133
The custom ID for the interaction.
134+
authorizing_integration_owners: Any
135+
TODO
136+
context: Optional[:class:`InteractionContextType`]
137+
The context in which this command was executed.
134138
"""
139+
# TODO: authorizing_integration_owners
135140

136141
__slots__: tuple[str, ...] = (
137142
"id",
@@ -149,6 +154,7 @@ class Interaction:
149154
"version",
150155
"custom_id",
151156
"entitlements",
157+
"context",
152158
"_channel_data",
153159
"_message_data",
154160
"_guild_data",
@@ -188,6 +194,7 @@ def _from_data(self, data: InteractionPayload):
188194
self.entitlements: list[Entitlement] = [
189195
Entitlement(data=e, state=self._state) for e in data.get("entitlements", [])
190196
]
197+
self.context: InteractionContextType | None = try_enum(InteractionContextType, data["context"]) if "context" in data else None
191198

192199
self.message: Message | None = None
193200
self.channel = None

discord/message.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ def __init__(
847847
self.interaction = MessageInteraction(data=data["interaction"], state=state)
848848
except KeyError:
849849
self.interaction = None
850+
# TODO: deprecate and replace with interaction_metadata
850851

851852
self.thread: Thread | None
852853
try:

0 commit comments

Comments
 (0)