Skip to content

Commit 3817291

Browse files
committed
Include bot in interaction context
Currently incoming application commands have no direct reference to the bot instance. This change includes the bot instance in our interaction context, much like how we do it in the commands extension. Having a reference to the bot is pretty handy, and fits well with the existing design of the library. A reference to the bot will also allow us to more easily reference a potential before_invoke or after_invoke hook from within discord.app.commands. Finally, by exposing a public get_context method, we open up the ability to define a custom context, a feature currently supported by the commands extension.
1 parent 4211ecc commit 3817291

File tree

3 files changed

+54
-27
lines changed

3 files changed

+54
-27
lines changed

discord/app/commands.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from typing import Callable, Dict, List, Optional, Union
3131

3232
from ..enums import SlashCommandOptionType
33-
from ..interactions import Interaction
3433
from ..member import Member
3534
from ..user import User
3635
from ..message import Message
@@ -138,12 +137,10 @@ def __eq__(self, other) -> bool:
138137
and other.description == self.description
139138
)
140139

141-
async def invoke(self, interaction) -> None:
140+
async def invoke(self, ctx: InteractionContext) -> None:
142141
# TODO: Parse the args better, apply custom converters etc.
143-
ctx = InteractionContext(interaction)
144-
145142
kwargs = {}
146-
for arg in interaction.data.get("options", []):
143+
for arg in ctx.interaction.data.get("options", []):
147144
op = find(lambda x: x.name == arg["name"], self.options)
148145
arg = arg["value"]
149146

@@ -257,11 +254,11 @@ def command_group(self, name, description) -> SubCommandGroup:
257254
self.subcommands.append(sub_command_group)
258255
return sub_command_group
259256

260-
async def invoke(self, interaction: Interaction) -> None:
261-
option = interaction.data["options"][0]
257+
async def invoke(self, ctx: InteractionContext) -> None:
258+
option = ctx.interaction.data["options"][0]
262259
command = find(lambda x: x.name == option["name"], self.subcommands)
263-
interaction.data = option
264-
await command.invoke(interaction)
260+
ctx.interaction.data = option
261+
await command.invoke(ctx)
265262

266263

267264
class UserCommand(ApplicationCommand):
@@ -290,29 +287,28 @@ def __init__(self, func: Callable, *args, **kwargs) -> None:
290287
def to_dict(self) -> Dict[str, Union[str, int]]:
291288
return {"name": self.name, "description": self.description, "type": self.type}
292289

293-
async def invoke(self, interaction: Interaction) -> None:
294-
if "members" not in interaction.data["resolved"]:
295-
_data = interaction.data["resolved"]["users"]
290+
async def invoke(self, ctx: InteractionContext) -> None:
291+
if "members" not in ctx.interaction.data["resolved"]:
292+
_data = ctx.interaction.data["resolved"]["users"]
296293
for i, v in _data.items():
297294
v["id"] = int(i)
298295
user = v
299-
target = User(state=interaction._state, data=user)
296+
target = User(state=ctx.interaction._state, data=user)
300297
else:
301-
_data = interaction.data["resolved"]["members"]
298+
_data = ctx.interaction.data["resolved"]["members"]
302299
for i, v in _data.items():
303300
v["id"] = int(i)
304301
member = v
305-
_data = interaction.data["resolved"]["users"]
302+
_data = ctx.interaction.data["resolved"]["users"]
306303
for i, v in _data.items():
307304
v["id"] = int(i)
308305
user = v
309306
member["user"] = user
310307
target = Member(
311308
data=member,
312-
guild=interaction._state._get_guild(interaction.guild_id),
313-
state=interaction._state,
309+
guild=ctx.interaction._state._get_guild(ctx.interaction.guild_id),
310+
state=ctx.interaction._state,
314311
)
315-
ctx = InteractionContext(interaction)
316312
await self.callback(ctx, target)
317313

318314

@@ -340,18 +336,17 @@ def __init__(self, func, *args, **kwargs):
340336
def to_dict(self):
341337
return {"name": self.name, "description": self.description, "type": self.type}
342338

343-
async def invoke(self, interaction):
344-
_data = interaction.data["resolved"]["messages"]
339+
async def invoke(self, ctx: InteractionContext):
340+
_data = ctx.interaction.data["resolved"]["messages"]
345341
for i, v in _data.items():
346342
v["id"] = int(i)
347343
message = v
348-
channel = interaction._state.get_channel(int(message["channel_id"]))
344+
channel = ctx.interaction._state.get_channel(int(message["channel_id"]))
349345
if channel is None:
350-
data = await interaction._state.http.start_private_message(
346+
data = await ctx.interaction._state.http.start_private_message(
351347
int(message["author"]["id"])
352348
)
353-
channel = interaction._state.add_dm_channel(data)
349+
channel = ctx.interaction._state.add_dm_channel(data)
354350

355-
target = Message(state=interaction._state, channel=channel, data=message)
356-
ctx = InteractionContext(interaction)
351+
target = Message(state=ctx.interaction._state, channel=channel, data=message)
357352
await self.callback(ctx, target)

discord/app/context.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
DEALINGS IN THE SOFTWARE.
2323
"""
2424

25+
from typing import TYPE_CHECKING
26+
27+
if TYPE_CHECKING:
28+
import discord
29+
2530
from ..interactions import Interaction
2631
from ..utils import cached_property
2732

@@ -35,7 +40,8 @@ class InteractionContext:
3540
.. versionadded:: 2.0
3641
"""
3742

38-
def __init__(self, interaction: Interaction):
43+
def __init__(self, bot: "discord.Bot", interaction: Interaction):
44+
self.bot = bot
3945
self.interaction = interaction
4046

4147
@cached_property

discord/bot.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
MessageCommand,
3939
UserCommand,
4040
ApplicationCommand,
41+
InteractionContext,
4142
)
4243
from .errors import Forbidden
4344
from .interactions import Interaction
@@ -245,7 +246,8 @@ async def handle_interaction(self, interaction: Interaction) -> None:
245246
except KeyError:
246247
self.dispatch("unknown_command", interaction)
247248
else:
248-
await command.invoke(interaction)
249+
context = await self.get_application_context(interaction)
250+
await command.invoke(context)
249251

250252
def slash_command(self, **kwargs) -> SlashCommand:
251253
"""A shortcut decorator that invokes :func:`.ApplicationCommandMixin.command` and adds it to
@@ -336,6 +338,30 @@ def command_group(self, name: str, description: str, guild_ids=None) -> SubComma
336338
self.add_application_command(group)
337339
return group
338340

341+
async def get_application_context(
342+
self, interaction: Interaction, cls=None
343+
) -> InteractionContext:
344+
r"""|coro|
345+
346+
Returns the invocation context from the interaction.
347+
348+
This is a more low-level counter-part for :meth:`.handle_interaction`
349+
to allow users more fine grained control over the processing.
350+
351+
Parameters
352+
-----------
353+
interaction: :class:`discord.Interaction`
354+
The interaction to get the invocation context from.
355+
356+
Returns
357+
--------
358+
:class:`.InteractionContext`
359+
The invocation context.
360+
"""
361+
if cls == None:
362+
cls = InteractionContext
363+
return cls(self, interaction)
364+
339365

340366
class BotBase(ApplicationCommandMixin): # To Insert: CogMixin
341367
# TODO I think

0 commit comments

Comments
 (0)