disagreement 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- disagreement/__init__.py +1 -1
- disagreement/ext/__init__.py +0 -0
- disagreement/ext/app_commands/__init__.py +46 -0
- disagreement/ext/app_commands/commands.py +513 -0
- disagreement/ext/app_commands/context.py +556 -0
- disagreement/ext/app_commands/converters.py +478 -0
- disagreement/ext/app_commands/decorators.py +569 -0
- disagreement/ext/app_commands/handler.py +627 -0
- disagreement/ext/commands/__init__.py +49 -0
- disagreement/ext/commands/cog.py +155 -0
- disagreement/ext/commands/converters.py +175 -0
- disagreement/ext/commands/core.py +490 -0
- disagreement/ext/commands/decorators.py +150 -0
- disagreement/ext/commands/errors.py +76 -0
- disagreement/ext/commands/help.py +37 -0
- disagreement/ext/commands/view.py +103 -0
- disagreement/ext/loader.py +43 -0
- disagreement/ext/tasks.py +89 -0
- disagreement/gateway.py +11 -8
- {disagreement-0.0.1.dist-info → disagreement-0.0.2.dist-info}/METADATA +39 -32
- {disagreement-0.0.1.dist-info → disagreement-0.0.2.dist-info}/RECORD +24 -7
- {disagreement-0.0.1.dist-info → disagreement-0.0.2.dist-info}/WHEEL +0 -0
- {disagreement-0.0.1.dist-info → disagreement-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {disagreement-0.0.1.dist-info → disagreement-0.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,556 @@
|
|
1
|
+
# disagreement/ext/app_commands/context.py
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING, Optional, List, Union, Any, Dict
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from disagreement.client import Client
|
9
|
+
from disagreement.interactions import (
|
10
|
+
Interaction,
|
11
|
+
InteractionCallbackData,
|
12
|
+
InteractionResponsePayload,
|
13
|
+
Snowflake,
|
14
|
+
)
|
15
|
+
from disagreement.enums import InteractionCallbackType, MessageFlags
|
16
|
+
from disagreement.models import (
|
17
|
+
User,
|
18
|
+
Member,
|
19
|
+
Message,
|
20
|
+
Channel,
|
21
|
+
ActionRow,
|
22
|
+
)
|
23
|
+
from disagreement.ui.view import View
|
24
|
+
|
25
|
+
# For full model hints, these would be imported from disagreement.models when defined:
|
26
|
+
Embed = Any
|
27
|
+
PartialAttachment = Any
|
28
|
+
Guild = Any # from disagreement.models import Guild
|
29
|
+
TextChannel = Any # from disagreement.models import TextChannel, etc.
|
30
|
+
from .commands import AppCommand
|
31
|
+
|
32
|
+
from disagreement.enums import InteractionCallbackType, MessageFlags
|
33
|
+
from disagreement.interactions import (
|
34
|
+
Interaction,
|
35
|
+
InteractionCallbackData,
|
36
|
+
InteractionResponsePayload,
|
37
|
+
Snowflake,
|
38
|
+
)
|
39
|
+
from disagreement.models import Message
|
40
|
+
from disagreement.typing import Typing
|
41
|
+
|
42
|
+
|
43
|
+
class AppCommandContext:
|
44
|
+
"""
|
45
|
+
Represents the context in which an application command is being invoked.
|
46
|
+
Provides methods to respond to the interaction.
|
47
|
+
"""
|
48
|
+
|
49
|
+
def __init__(
|
50
|
+
self,
|
51
|
+
bot: "Client",
|
52
|
+
interaction: "Interaction",
|
53
|
+
command: Optional["AppCommand"] = None,
|
54
|
+
):
|
55
|
+
self.bot: "Client" = bot
|
56
|
+
self.interaction: "Interaction" = interaction
|
57
|
+
self.command: Optional["AppCommand"] = command # The command that was invoked
|
58
|
+
|
59
|
+
self._responded: bool = False
|
60
|
+
self._deferred: bool = False
|
61
|
+
|
62
|
+
@property
|
63
|
+
def token(self) -> str:
|
64
|
+
"""The interaction token."""
|
65
|
+
return self.interaction.token
|
66
|
+
|
67
|
+
@property
|
68
|
+
def interaction_id(self) -> "Snowflake":
|
69
|
+
"""The interaction ID."""
|
70
|
+
return self.interaction.id
|
71
|
+
|
72
|
+
@property
|
73
|
+
def application_id(self) -> "Snowflake":
|
74
|
+
"""The application ID of the interaction."""
|
75
|
+
return self.interaction.application_id
|
76
|
+
|
77
|
+
@property
|
78
|
+
def guild_id(self) -> Optional["Snowflake"]:
|
79
|
+
"""The ID of the guild where the interaction occurred, if any."""
|
80
|
+
return self.interaction.guild_id
|
81
|
+
|
82
|
+
@property
|
83
|
+
def channel_id(self) -> Optional["Snowflake"]:
|
84
|
+
"""The ID of the channel where the interaction occurred."""
|
85
|
+
return self.interaction.channel_id
|
86
|
+
|
87
|
+
@property
|
88
|
+
def author(self) -> Optional[Union["User", "Member"]]:
|
89
|
+
"""The user or member who invoked the interaction."""
|
90
|
+
return self.interaction.member or self.interaction.user
|
91
|
+
|
92
|
+
@property
|
93
|
+
def user(self) -> Optional["User"]:
|
94
|
+
"""The user who invoked the interaction.
|
95
|
+
If in a guild, this is the user part of the member.
|
96
|
+
If in a DM, this is the top-level user.
|
97
|
+
"""
|
98
|
+
return self.interaction.user
|
99
|
+
|
100
|
+
@property
|
101
|
+
def member(self) -> Optional["Member"]:
|
102
|
+
"""The member who invoked the interaction, if this occurred in a guild."""
|
103
|
+
return self.interaction.member
|
104
|
+
|
105
|
+
@property
|
106
|
+
def locale(self) -> Optional[str]:
|
107
|
+
"""The selected language of the invoking user."""
|
108
|
+
return self.interaction.locale
|
109
|
+
|
110
|
+
@property
|
111
|
+
def guild_locale(self) -> Optional[str]:
|
112
|
+
"""The guild's preferred language, if applicable."""
|
113
|
+
return self.interaction.guild_locale
|
114
|
+
|
115
|
+
@property
|
116
|
+
async def guild(self) -> Optional["Guild"]:
|
117
|
+
"""The guild object where the interaction occurred, if available."""
|
118
|
+
|
119
|
+
if not self.guild_id:
|
120
|
+
return None
|
121
|
+
|
122
|
+
guild = None
|
123
|
+
if hasattr(self.bot, "get_guild"):
|
124
|
+
guild = self.bot.get_guild(self.guild_id)
|
125
|
+
|
126
|
+
if not guild and hasattr(self.bot, "fetch_guild"):
|
127
|
+
try:
|
128
|
+
guild = await self.bot.fetch_guild(self.guild_id)
|
129
|
+
except Exception:
|
130
|
+
guild = None
|
131
|
+
|
132
|
+
return guild
|
133
|
+
|
134
|
+
@property
|
135
|
+
async def channel(self) -> Optional[Any]:
|
136
|
+
"""The channel object where the interaction occurred, if available."""
|
137
|
+
|
138
|
+
if not self.channel_id:
|
139
|
+
return None
|
140
|
+
|
141
|
+
channel = None
|
142
|
+
if hasattr(self.bot, "get_channel"):
|
143
|
+
channel = self.bot.get_channel(self.channel_id)
|
144
|
+
elif hasattr(self.bot, "_channels"):
|
145
|
+
channel = self.bot._channels.get(self.channel_id)
|
146
|
+
|
147
|
+
if not channel and hasattr(self.bot, "fetch_channel"):
|
148
|
+
try:
|
149
|
+
channel = await self.bot.fetch_channel(self.channel_id)
|
150
|
+
except Exception:
|
151
|
+
channel = None
|
152
|
+
|
153
|
+
return channel
|
154
|
+
|
155
|
+
async def _send_response(
|
156
|
+
self,
|
157
|
+
response_type: "InteractionCallbackType",
|
158
|
+
data: Optional[Dict[str, Any]] = None,
|
159
|
+
) -> None:
|
160
|
+
"""Internal helper to send interaction responses."""
|
161
|
+
if (
|
162
|
+
self._responded
|
163
|
+
and not self._deferred
|
164
|
+
and response_type
|
165
|
+
!= InteractionCallbackType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT
|
166
|
+
):
|
167
|
+
# If already responded and not deferred, subsequent responses must be followups
|
168
|
+
# (unless it's an autocomplete result which is a special case)
|
169
|
+
# For now, let's assume followups are handled by separate methods.
|
170
|
+
# This logic might need refinement based on how followups are exposed.
|
171
|
+
raise RuntimeError(
|
172
|
+
"Interaction has already been responded to. Use send_followup()."
|
173
|
+
)
|
174
|
+
|
175
|
+
callback_data = InteractionCallbackData(data) if data else None
|
176
|
+
payload = InteractionResponsePayload(type=response_type, data=callback_data)
|
177
|
+
|
178
|
+
await self.bot._http.create_interaction_response(
|
179
|
+
interaction_id=self.interaction_id,
|
180
|
+
interaction_token=self.token,
|
181
|
+
payload=payload,
|
182
|
+
)
|
183
|
+
if (
|
184
|
+
response_type
|
185
|
+
!= InteractionCallbackType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT
|
186
|
+
):
|
187
|
+
self._responded = True
|
188
|
+
if (
|
189
|
+
response_type
|
190
|
+
== InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
|
191
|
+
or response_type == InteractionCallbackType.DEFERRED_UPDATE_MESSAGE
|
192
|
+
):
|
193
|
+
self._deferred = True
|
194
|
+
|
195
|
+
async def defer(self, ephemeral: bool = False, thinking: bool = True) -> None:
|
196
|
+
"""
|
197
|
+
Defers the interaction response.
|
198
|
+
|
199
|
+
This is typically used when your command might take longer than 3 seconds to process.
|
200
|
+
You must send a followup message within 15 minutes.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
ephemeral (bool): Whether the subsequent followup response should be ephemeral.
|
204
|
+
Only applicable if `thinking` is True.
|
205
|
+
thinking (bool): If True (default), responds with a "Bot is thinking..." message
|
206
|
+
(DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE).
|
207
|
+
If False, responds with DEFERRED_UPDATE_MESSAGE (for components).
|
208
|
+
"""
|
209
|
+
if self._responded:
|
210
|
+
raise RuntimeError("Interaction has already been responded to or deferred.")
|
211
|
+
|
212
|
+
response_type = (
|
213
|
+
InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
|
214
|
+
if thinking
|
215
|
+
else InteractionCallbackType.DEFERRED_UPDATE_MESSAGE
|
216
|
+
)
|
217
|
+
data = None
|
218
|
+
if ephemeral and thinking:
|
219
|
+
data = {
|
220
|
+
"flags": MessageFlags.EPHEMERAL.value
|
221
|
+
} # Assuming MessageFlags enum exists
|
222
|
+
|
223
|
+
await self._send_response(response_type, data)
|
224
|
+
self._deferred = True # Mark as deferred
|
225
|
+
|
226
|
+
async def send(
|
227
|
+
self,
|
228
|
+
content: Optional[str] = None,
|
229
|
+
embed: Optional["Embed"] = None, # Convenience for single embed
|
230
|
+
embeds: Optional[List["Embed"]] = None,
|
231
|
+
*,
|
232
|
+
tts: bool = False,
|
233
|
+
files: Optional[List[Any]] = None,
|
234
|
+
components: Optional[List[ActionRow]] = None,
|
235
|
+
view: Optional[View] = None,
|
236
|
+
allowed_mentions: Optional[Dict[str, Any]] = None,
|
237
|
+
ephemeral: bool = False,
|
238
|
+
flags: Optional[int] = None,
|
239
|
+
) -> Optional[
|
240
|
+
"Message"
|
241
|
+
]: # Returns Message if not ephemeral and response was not deferred
|
242
|
+
"""
|
243
|
+
Sends a response to the interaction.
|
244
|
+
If the interaction was previously deferred, this will edit the original deferred response.
|
245
|
+
Otherwise, it sends an initial response.
|
246
|
+
|
247
|
+
Args:
|
248
|
+
content (Optional[str]): The message content.
|
249
|
+
embed (Optional[Embed]): A single embed to send. If `embeds` is also provided, this is ignored.
|
250
|
+
embeds (Optional[List[Embed]]): A list of embeds to send (max 10).
|
251
|
+
ephemeral (bool): Whether the message should be ephemeral (only visible to the invoker).
|
252
|
+
flags (Optional[int]): Additional message flags to apply.
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
Optional[Message]: The sent message object if a new message was created and not ephemeral.
|
256
|
+
None if the response was ephemeral or an edit to a deferred message.
|
257
|
+
"""
|
258
|
+
if not self._responded and self._deferred: # Editing a deferred response
|
259
|
+
# Use edit_original_interaction_response
|
260
|
+
payload: Dict[str, Any] = {}
|
261
|
+
if content is not None:
|
262
|
+
payload["content"] = content
|
263
|
+
|
264
|
+
if tts:
|
265
|
+
payload["tts"] = True
|
266
|
+
|
267
|
+
actual_embeds = embeds
|
268
|
+
if embed and not embeds:
|
269
|
+
actual_embeds = [embed]
|
270
|
+
if actual_embeds:
|
271
|
+
payload["embeds"] = [e.to_dict() for e in actual_embeds]
|
272
|
+
|
273
|
+
if view:
|
274
|
+
await view._start(self.bot)
|
275
|
+
payload["components"] = view.to_components_payload()
|
276
|
+
elif components:
|
277
|
+
payload["components"] = [c.to_dict() for c in components]
|
278
|
+
|
279
|
+
if files is not None:
|
280
|
+
payload["attachments"] = [
|
281
|
+
f.to_dict() if hasattr(f, "to_dict") else f for f in files
|
282
|
+
]
|
283
|
+
|
284
|
+
if allowed_mentions is not None:
|
285
|
+
payload["allowed_mentions"] = allowed_mentions
|
286
|
+
|
287
|
+
# Flags (like ephemeral) cannot be set when editing the original deferred response this way.
|
288
|
+
# Ephemeral for deferred must be set during defer().
|
289
|
+
|
290
|
+
msg_data = await self.bot._http.edit_original_interaction_response(
|
291
|
+
application_id=self.application_id,
|
292
|
+
interaction_token=self.token,
|
293
|
+
payload=payload,
|
294
|
+
)
|
295
|
+
self._responded = True # Ensure it's marked as fully responded
|
296
|
+
if view and msg_data and "id" in msg_data:
|
297
|
+
view.message_id = msg_data["id"]
|
298
|
+
self.bot._views[msg_data["id"]] = view
|
299
|
+
# Construct and return Message object if needed, for now returns None for edits
|
300
|
+
return None
|
301
|
+
|
302
|
+
elif not self._responded: # Sending an initial response
|
303
|
+
data: Dict[str, Any] = {}
|
304
|
+
if content is not None:
|
305
|
+
data["content"] = content
|
306
|
+
|
307
|
+
if tts:
|
308
|
+
data["tts"] = True
|
309
|
+
|
310
|
+
actual_embeds = embeds
|
311
|
+
if embed and not embeds:
|
312
|
+
actual_embeds = [embed]
|
313
|
+
if actual_embeds:
|
314
|
+
data["embeds"] = [
|
315
|
+
e.to_dict() for e in actual_embeds
|
316
|
+
] # Assuming embeds have to_dict()
|
317
|
+
|
318
|
+
if view:
|
319
|
+
await view._start(self.bot)
|
320
|
+
data["components"] = view.to_components_payload()
|
321
|
+
elif components:
|
322
|
+
data["components"] = [c.to_dict() for c in components]
|
323
|
+
|
324
|
+
if files is not None:
|
325
|
+
data["attachments"] = [
|
326
|
+
f.to_dict() if hasattr(f, "to_dict") else f for f in files
|
327
|
+
]
|
328
|
+
|
329
|
+
if allowed_mentions is not None:
|
330
|
+
data["allowed_mentions"] = allowed_mentions
|
331
|
+
|
332
|
+
flags_value = 0
|
333
|
+
if ephemeral:
|
334
|
+
flags_value |= MessageFlags.EPHEMERAL.value
|
335
|
+
if flags:
|
336
|
+
flags_value |= flags
|
337
|
+
if flags_value:
|
338
|
+
data["flags"] = flags_value
|
339
|
+
|
340
|
+
await self._send_response(
|
341
|
+
InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE, data
|
342
|
+
)
|
343
|
+
|
344
|
+
if view and not ephemeral:
|
345
|
+
try:
|
346
|
+
msg_data = await self.bot._http.get_original_interaction_response(
|
347
|
+
application_id=self.application_id,
|
348
|
+
interaction_token=self.token,
|
349
|
+
)
|
350
|
+
if msg_data and "id" in msg_data:
|
351
|
+
view.message_id = msg_data["id"]
|
352
|
+
self.bot._views[msg_data["id"]] = view
|
353
|
+
except Exception:
|
354
|
+
pass
|
355
|
+
if not ephemeral:
|
356
|
+
return None
|
357
|
+
return None
|
358
|
+
else:
|
359
|
+
# If already responded and not deferred, this should be a followup.
|
360
|
+
# This method is for initial response or editing deferred.
|
361
|
+
raise RuntimeError(
|
362
|
+
"Interaction has already been responded to. Use send_followup()."
|
363
|
+
)
|
364
|
+
|
365
|
+
async def send_followup(
|
366
|
+
self,
|
367
|
+
content: Optional[str] = None,
|
368
|
+
embed: Optional["Embed"] = None,
|
369
|
+
embeds: Optional[List["Embed"]] = None,
|
370
|
+
*,
|
371
|
+
ephemeral: bool = False,
|
372
|
+
tts: bool = False,
|
373
|
+
files: Optional[List[Any]] = None,
|
374
|
+
components: Optional[List["ActionRow"]] = None,
|
375
|
+
view: Optional[View] = None,
|
376
|
+
allowed_mentions: Optional[Dict[str, Any]] = None,
|
377
|
+
flags: Optional[int] = None,
|
378
|
+
) -> Optional["Message"]:
|
379
|
+
"""
|
380
|
+
Sends a followup message to an interaction.
|
381
|
+
This can be used after an initial response or a deferred response.
|
382
|
+
|
383
|
+
Args:
|
384
|
+
content (Optional[str]): The message content.
|
385
|
+
embed (Optional[Embed]): A single embed to send.
|
386
|
+
embeds (Optional[List[Embed]]): A list of embeds to send.
|
387
|
+
ephemeral (bool): Whether the followup message should be ephemeral.
|
388
|
+
flags (Optional[int]): Additional message flags to apply.
|
389
|
+
|
390
|
+
Returns:
|
391
|
+
Message: The sent followup message object.
|
392
|
+
"""
|
393
|
+
if not self._responded:
|
394
|
+
raise RuntimeError(
|
395
|
+
"Must acknowledge or defer the interaction before sending a followup."
|
396
|
+
)
|
397
|
+
|
398
|
+
payload: Dict[str, Any] = {}
|
399
|
+
if content is not None:
|
400
|
+
payload["content"] = content
|
401
|
+
|
402
|
+
if tts:
|
403
|
+
payload["tts"] = True
|
404
|
+
|
405
|
+
actual_embeds = embeds
|
406
|
+
if embed and not embeds:
|
407
|
+
actual_embeds = [embed]
|
408
|
+
if actual_embeds:
|
409
|
+
payload["embeds"] = [
|
410
|
+
e.to_dict() for e in actual_embeds
|
411
|
+
] # Assuming embeds have to_dict()
|
412
|
+
|
413
|
+
if view:
|
414
|
+
await view._start(self.bot)
|
415
|
+
payload["components"] = view.to_components_payload()
|
416
|
+
elif components:
|
417
|
+
payload["components"] = [c.to_dict() for c in components]
|
418
|
+
|
419
|
+
if files is not None:
|
420
|
+
payload["attachments"] = [
|
421
|
+
f.to_dict() if hasattr(f, "to_dict") else f for f in files
|
422
|
+
]
|
423
|
+
|
424
|
+
if allowed_mentions is not None:
|
425
|
+
payload["allowed_mentions"] = allowed_mentions
|
426
|
+
|
427
|
+
flags_value = 0
|
428
|
+
if ephemeral:
|
429
|
+
flags_value |= MessageFlags.EPHEMERAL.value
|
430
|
+
if flags:
|
431
|
+
flags_value |= flags
|
432
|
+
if flags_value:
|
433
|
+
payload["flags"] = flags_value
|
434
|
+
|
435
|
+
# Followup messages are sent to a webhook endpoint
|
436
|
+
message_data = await self.bot._http.create_followup_message(
|
437
|
+
application_id=self.application_id,
|
438
|
+
interaction_token=self.token,
|
439
|
+
payload=payload,
|
440
|
+
)
|
441
|
+
if view and message_data and "id" in message_data:
|
442
|
+
view.message_id = message_data["id"]
|
443
|
+
self.bot._views[message_data["id"]] = view
|
444
|
+
from disagreement.models import Message # Ensure Message is available
|
445
|
+
|
446
|
+
return Message(data=message_data, client_instance=self.bot)
|
447
|
+
|
448
|
+
async def edit(
|
449
|
+
self,
|
450
|
+
message_id: "Snowflake" = "@original", # Defaults to editing the original response
|
451
|
+
content: Optional[str] = None,
|
452
|
+
embed: Optional["Embed"] = None,
|
453
|
+
embeds: Optional[List["Embed"]] = None,
|
454
|
+
*,
|
455
|
+
components: Optional[List["ActionRow"]] = None,
|
456
|
+
attachments: Optional[List[Any]] = None,
|
457
|
+
allowed_mentions: Optional[Dict[str, Any]] = None,
|
458
|
+
) -> Optional["Message"]:
|
459
|
+
"""
|
460
|
+
Edits a message previously sent in response to this interaction.
|
461
|
+
Can edit the original response or a followup message.
|
462
|
+
|
463
|
+
Args:
|
464
|
+
message_id (Snowflake): The ID of the message to edit. Defaults to "@original"
|
465
|
+
to edit the initial interaction response.
|
466
|
+
content (Optional[str]): The new message content.
|
467
|
+
embed (Optional[Embed]): A single new embed.
|
468
|
+
embeds (Optional[List[Embed]]): A list of new embeds.
|
469
|
+
|
470
|
+
Returns:
|
471
|
+
Optional[Message]: The edited message object if available.
|
472
|
+
"""
|
473
|
+
if not self._responded:
|
474
|
+
raise RuntimeError(
|
475
|
+
"Cannot edit response if interaction hasn't been responded to or deferred."
|
476
|
+
)
|
477
|
+
|
478
|
+
payload: Dict[str, Any] = {}
|
479
|
+
if content is not None:
|
480
|
+
payload["content"] = content # Use None to clear
|
481
|
+
|
482
|
+
actual_embeds = embeds
|
483
|
+
if embed and not embeds:
|
484
|
+
actual_embeds = [embed]
|
485
|
+
if actual_embeds is not None: # Allow passing empty list to clear embeds
|
486
|
+
payload["embeds"] = [
|
487
|
+
e.to_dict() for e in actual_embeds
|
488
|
+
] # Assuming embeds have to_dict()
|
489
|
+
|
490
|
+
if components is not None:
|
491
|
+
payload["components"] = [c.to_dict() for c in components]
|
492
|
+
|
493
|
+
if attachments is not None:
|
494
|
+
payload["attachments"] = [
|
495
|
+
a.to_dict() if hasattr(a, "to_dict") else a for a in attachments
|
496
|
+
]
|
497
|
+
|
498
|
+
if allowed_mentions is not None:
|
499
|
+
payload["allowed_mentions"] = allowed_mentions
|
500
|
+
|
501
|
+
if message_id == "@original":
|
502
|
+
edited_message_data = (
|
503
|
+
await self.bot._http.edit_original_interaction_response(
|
504
|
+
application_id=self.application_id,
|
505
|
+
interaction_token=self.token,
|
506
|
+
payload=payload,
|
507
|
+
)
|
508
|
+
)
|
509
|
+
else:
|
510
|
+
edited_message_data = await self.bot._http.edit_followup_message(
|
511
|
+
application_id=self.application_id,
|
512
|
+
interaction_token=self.token,
|
513
|
+
message_id=message_id,
|
514
|
+
payload=payload,
|
515
|
+
)
|
516
|
+
# The HTTP methods used in tests return minimal data that is insufficient
|
517
|
+
# to construct a full ``Message`` instance, so we simply return ``None``
|
518
|
+
# rather than attempting to parse the response.
|
519
|
+
return None
|
520
|
+
|
521
|
+
async def delete(self, message_id: "Snowflake" = "@original") -> None:
|
522
|
+
"""
|
523
|
+
Deletes a message previously sent in response to this interaction.
|
524
|
+
Can delete the original response or a followup message.
|
525
|
+
|
526
|
+
Args:
|
527
|
+
message_id (Snowflake): The ID of the message to delete. Defaults to "@original"
|
528
|
+
to delete the initial interaction response.
|
529
|
+
"""
|
530
|
+
if not self._responded:
|
531
|
+
# If not responded, there's nothing to delete via this interaction's lifecycle.
|
532
|
+
# Deferral doesn't create a message to delete until a followup is sent.
|
533
|
+
raise RuntimeError(
|
534
|
+
"Cannot delete response if interaction hasn't been responded to."
|
535
|
+
)
|
536
|
+
|
537
|
+
if message_id == "@original":
|
538
|
+
await self.bot._http.delete_original_interaction_response(
|
539
|
+
application_id=self.application_id, interaction_token=self.token
|
540
|
+
)
|
541
|
+
else:
|
542
|
+
await self.bot._http.delete_followup_message(
|
543
|
+
application_id=self.application_id,
|
544
|
+
interaction_token=self.token,
|
545
|
+
message_id=message_id,
|
546
|
+
)
|
547
|
+
# After deleting the original response, further followups might be problematic.
|
548
|
+
# Discord docs: "Once the original message is deleted, you can no longer edit the message or send followups."
|
549
|
+
# Consider implications for context state.
|
550
|
+
|
551
|
+
def typing(self) -> Typing:
|
552
|
+
"""Return a typing context manager for this interaction's channel."""
|
553
|
+
|
554
|
+
if not self.channel_id:
|
555
|
+
raise RuntimeError("Cannot send typing indicator without a channel.")
|
556
|
+
return self.bot.typing(self.channel_id)
|