@@ -22,10 +22,12 @@ class SlashContext:
2222 :ivar subcommand_group: Subcommand group of the command.
2323 :ivar interaction_id: Interaction ID of the command message.
2424 :ivar command_id: ID of the command.
25- :ivar _http: :class:`.http.SlashCommandRequest` of the client.
2625 :ivar bot: discord.py client.
27- :ivar logger: Logger instance.
28- :ivar sent: Whether you sent the initial response.
26+ :ivar _http: :class:`.http.SlashCommandRequest` of the client.
27+ :ivar _logger: Logger instance.
28+ :ivar deffered: Whether the command is current deffered (loading state)
29+ :ivar _deffered_hidden: Internal var to check that state stays the same
30+ :ivar responded: Whether you have responded with a message to the interaction.
2931 :ivar guild_id: Guild ID of the command message. If the command was invoked in DM, then it is ``None``
3032 :ivar author_id: User ID representing author of the command message.
3133 :ivar channel_id: Channel ID representing channel of the command message.
@@ -46,8 +48,10 @@ def __init__(self,
4648 self .command_id = _json ["data" ]["id" ]
4749 self ._http = _http
4850 self .bot = _discord
49- self .logger = logger
50- self .sent = False
51+ self ._logger = logger
52+ self .deffered = False
53+ self .responded = False
54+ self ._deffered_hidden = False # To check if the patch to the deffered response matches
5155 self .guild_id = int (_json ["guild_id" ]) if "guild_id" in _json .keys () else None
5256 self .author_id = int (_json ["member" ]["user" ]["id" ] if "member" in _json .keys () else _json ["user" ]["id" ])
5357 self .channel_id = int (_json ["channel_id" ])
@@ -76,39 +80,20 @@ def channel(self) -> typing.Optional[typing.Union[discord.TextChannel, discord.D
7680 """
7781 return self .bot .get_channel (self .channel_id )
7882
79- async def respond (self , eat : bool = False ):
83+ async def defer (self , hidden : bool = False ):
8084 """
81- Sends command invoke response.\n
82- You should call this first.
85+ 'Deferes' the response, showing a loading state to the user
8386
84- .. note::
85- - If `eat` is ``False``, there is a chance that ``message`` variable is present.
86- - While it is recommended to be manually called, this will still be automatically called
87- if this isn't called but :meth:`.send()` is called.
88-
89- :param eat: Whether to eat user's input. Default ``False``.
87+ :param hidden: Whether the deffered response should be ephemeral . Default ``False``.
9088 """
91- base = {"type" : 2 if eat else 5 }
92- _task = self .bot .loop .create_task (self ._http .post (base , self .interaction_id , self .__token , True ))
93- self .sent = True
94- if not eat and (not self .guild_id or (self .channel and self .channel .permissions_for (self .guild .me ).view_channel )):
95- with suppress (asyncio .TimeoutError ):
96- def check (message : discord .Message ):
97- user_id = self .author_id
98- is_author = message .author .id == user_id
99- channel_id = self .channel_id
100- is_channel = channel_id == message .channel .id
101- is_user_input = message .type == 20
102- is_correct_command = message .content .startswith (f"</{ self .name } :{ self .command_id } >" )
103- return is_author and is_channel and is_user_input and is_correct_command
104-
105- self .message = await self .bot .wait_for ("message" , timeout = 3 , check = check )
106- await _task
107-
108- @property
109- def ack (self ):
110- """Alias of :meth:`.respond`."""
111- return self .respond
89+ if self .deffered or self .responded :
90+ raise error .AlreadyResponded ("You have already responded to this command!" )
91+ base = {"type" : 5 }
92+ if hidden :
93+ base ["data" ] = {"flags" : 64 }
94+ self ._deffered_hidden = True
95+ await self ._http .post_initial_response (base , self .interaction_id , self .__token )
96+ self .deffered = True
11297
11398 async def send (self ,
11499 content : str = "" , * ,
@@ -129,6 +114,7 @@ async def send(self,
129114 .. warning::
130115 - Since Release 1.0.9, this is completely changed. If you are migrating from older version, please make sure to fix the usage.
131116 - You can't use both ``embed`` and ``embeds`` at the same time, also applies to ``file`` and ``files``.
117+ - You cannot send files in the initial response
132118
133119 :param content: Content of the response.
134120 :type content: str
@@ -150,15 +136,6 @@ async def send(self,
150136 :type delete_after: float
151137 :return: Union[discord.Message, dict]
152138 """
153- if isinstance (content , int ) and 2 <= content <= 5 :
154- raise error .IncorrectFormat ("`.send` Method is rewritten at Release 1.0.9. Please read the docs and fix all the usages." )
155- if not self .sent :
156- self .logger .info (f"At command `{ self .name } `: It is recommended to call `.respond()` first!" )
157- await self .respond (eat = hidden )
158- if hidden :
159- if embeds or embed or files or file :
160- self .logger .warning ("Embed/File is not supported for `hidden`!" )
161- return await self .send_hidden (content )
162139 if embed and embeds :
163140 raise error .IncorrectFormat ("You can't use both `embed` and `embeds`!" )
164141 if embed :
@@ -172,6 +149,8 @@ async def send(self,
172149 raise error .IncorrectFormat ("You can't use both `file` and `files`!" )
173150 if file :
174151 files = [file ]
152+ if delete_after and hidden :
153+ raise error .IncorrectFormat ("You can't delete a hidden message!" )
175154
176155 base = {
177156 "content" : content ,
@@ -180,30 +159,47 @@ async def send(self,
180159 "allowed_mentions" : allowed_mentions .to_dict () if allowed_mentions
181160 else self .bot .allowed_mentions .to_dict () if self .bot .allowed_mentions else {}
182161 }
183-
184- resp = await self ._http .post (base , self .interaction_id , self .__token , files = files )
185- smsg = model .SlashMessage (state = self .bot ._connection ,
186- data = resp ,
187- channel = self .channel or discord .Object (id = self .channel_id ),
188- _http = self ._http ,
189- interaction_token = self .__token )
190- if delete_after :
191- self .bot .loop .create_task (smsg .delete (delay = delete_after ))
192- return smsg
193-
194- def send_hidden (self , content : str = "" ):
195- """
196- Sends hidden response.\n
197- This is automatically used if you pass ``hidden=True`` at :meth:`.send`.
198-
199- .. note::
200- This is not intended to be manually called. Please use :meth:`.send` instead.
201-
202- :param content: Message content.
203- :return: Coroutine
204- """
205- base = {
206- "content" : content ,
207- "flags" : 64
208- }
209- return self ._http .post (base , self .interaction_id , self .__token )
162+ if hidden :
163+ if embeds or files :
164+ self ._logger .warning ("Embed/File is not supported for `hidden`!" )
165+ base ["flags" ] = 64
166+
167+ initial_message = False
168+ if not self .responded :
169+ initial_message = True
170+ if files :
171+ raise error .IncorrectFormat ("You cannot send files in the initial response!" )
172+ if self .deffered :
173+ if self ._deffered_hidden != hidden :
174+ self ._logger .warning (
175+ "Deffered response might not be what you set it to! (hidden / visible) "
176+ "This is because it was deffered in a different state"
177+ )
178+ resp = await self ._http .edit (base , self .__token )
179+ self .deffered = False
180+ else :
181+ json_data = {
182+ "type" : 4 ,
183+ "data" : base
184+ }
185+ await self ._http .post_initial_response (json_data , self .interaction_id , self .__token )
186+ if not hidden :
187+ resp = await self ._http .edit ({}, self .__token )
188+ else :
189+ resp = {}
190+ self .responded = True
191+ else :
192+ resp = await self ._http .post_followup (base , self .__token , files = files )
193+ if not hidden :
194+ smsg = model .SlashMessage (state = self .bot ._connection ,
195+ data = resp ,
196+ channel = self .channel or discord .Object (id = self .channel_id ),
197+ _http = self ._http ,
198+ interaction_token = self .__token )
199+ if delete_after :
200+ self .bot .loop .create_task (smsg .delete (delay = delete_after ))
201+ if initial_message :
202+ self .message = smsg
203+ return smsg
204+ else :
205+ return resp
0 commit comments