diff --git a/discord/ext/flow/controller.py b/discord/ext/flow/controller.py index e12448f..85f6eb6 100644 --- a/discord/ext/flow/controller.py +++ b/discord/ext/flow/controller.py @@ -2,19 +2,18 @@ from typing import TYPE_CHECKING -from discord import Interaction from discord.utils import maybe_coroutine -from .util import into_edit_kwargs, send_helper +from .util import send_helper from .view import _View if TYPE_CHECKING: from typing import Self - from discord import Client + from discord import Client, Interaction from discord.abc import Messageable - from .model import Message, ModelBase + from .model import ModelBase from .util import _Editable __all__ = ('Controller',) @@ -65,12 +64,12 @@ async def _send( msg = await maybe_coroutine(model.message) if msg.items is None: - await self._send_helper(messageable, msg, None, edit_target) + await send_helper(messageable, msg, None, edit_target) await maybe_coroutine(model.after_invoke) return None view = _View(await maybe_coroutine(model.view_config), msg.items) - message = await self._send_helper(messageable, msg, view, edit_target) + message = await send_helper(messageable, msg, view, edit_target) await view.wait() if msg.disable_items: @@ -81,24 +80,3 @@ async def _send( await maybe_coroutine(model.after_invoke) return None if view.result is None else (*view.result, message) - - async def _send_helper( - self, - messageable: Messageable | Interaction[Client], - message: Message, - view: _View | None, - edit_target: _Editable | None, - ) -> _Editable: - kwargs = message._to_dict() - if view is not None: - kwargs['view'] = view - - if message.edit_original: - if isinstance(messageable, Interaction) and not messageable.response.is_done(): - if messageable.message is not None: # Interaction.message is not None -> can edit - await messageable.response.edit_message(**into_edit_kwargs(**kwargs)) - return await messageable.original_response() # type: ignore[reportReturnType, return-value] - elif edit_target is not None: - return await edit_target.edit(**into_edit_kwargs(**kwargs)) - # fallback to send message - return await send_helper(messageable, **kwargs) diff --git a/discord/ext/flow/util.py b/discord/ext/flow/util.py index d5ec40a..ba0d643 100644 --- a/discord/ext/flow/util.py +++ b/discord/ext/flow/util.py @@ -6,13 +6,14 @@ if TYPE_CHECKING: from collections.abc import Sequence - from typing import Self, TypeVar, Unpack + from typing import Self, TypeVar from discord import AllowedMentions, Attachment, Client, Embed, File from discord.abc import Messageable from discord.ui import View - from .model import MessageKwargs + from .model import Message as MessageData, MessageKwargs + from .view import _View T = TypeVar('T') U = TypeVar('U') @@ -50,29 +51,26 @@ class _SendHelperKWType(TypedDict, total=False): silent: bool -async def send_helper( - messageable: Messageable | Interaction[Client], - *, - delete_after: float | None = None, - ephemeral: bool = False, - **kwargs: Unpack[_SendHelperKWType], -) -> _Editable: - """Helper function to send message. use messageable or interaction.""" - msg: Message - if isinstance(messageable, Interaction): - if messageable.response.is_done(): - msg = await messageable.followup.send(wait=True, ephemeral=ephemeral, **kwargs) - else: - await messageable.response.send_message(ephemeral=ephemeral, **kwargs) - msg = await messageable.original_response() - - if delete_after is not None: - await msg.delete(delay=delete_after) - else: - # type-ignore: can pass None to delete_after - msg = await messageable.send(delete_after=delete_after, **kwargs) # type: ignore[reportArgumentType, arg-type] - # type-ignore: return type is Message, InteractionMessage or WebhookMessage, which are also _Editable - return msg # type: ignore[reportReturnType, return-value] +def into_send_kwargs(kwargs: MessageKwargs) -> _SendHelperKWType: + """Convert MessageKwargs to send kwargs type.""" + kw: _SendHelperKWType = {} + if 'content' in kwargs: + kw['content'] = kwargs['content'] + if 'tts' in kwargs: + kw['tts'] = kwargs['tts'] + if 'embeds' in kwargs: + kw['embeds'] = kwargs['embeds'] + if 'files' in kwargs: + kw['files'] = kwargs['files'] + if 'allowed_mentions' in kwargs: + kw['allowed_mentions'] = kwargs['allowed_mentions'] + if 'view' in kwargs: + kw['view'] = kwargs['view'] + if 'suppress_embeds' in kwargs: + kw['suppress_embeds'] = kwargs['suppress_embeds'] + if 'silent' in kwargs: + kw['silent'] = kwargs['silent'] + return kw class _EditKWType(TypedDict, total=False): @@ -83,7 +81,7 @@ class _EditKWType(TypedDict, total=False): view: View -def into_edit_kwargs(**kwargs: Unpack[MessageKwargs]) -> _EditKWType: +def into_edit_kwargs(kwargs: MessageKwargs) -> _EditKWType: """Convert MessageKwargs to Message.edit kwargs type.""" kw: _EditKWType = {} if 'content' in kwargs: @@ -97,3 +95,45 @@ def into_edit_kwargs(**kwargs: Unpack[MessageKwargs]) -> _EditKWType: if 'view' in kwargs: kw['view'] = kwargs['view'] return kw + + +async def send_helper( + messageable: Messageable | Interaction[Client], + message: MessageData, + view: _View | None, + edit_target: _Editable | None, +) -> _Editable: + """Helper function to send message. use messageable or interaction.""" + kwargs = message._to_dict() + if view is not None: + kwargs['view'] = view + + # if edit + if message.edit_original: + if isinstance(messageable, Interaction) and not messageable.response.is_done(): + if messageable.message is not None: # Interaction.message is not None -> can edit + await messageable.response.edit_message(**into_edit_kwargs(kwargs)) + return await messageable.original_response() # type: ignore[reportReturnType, return-value] + elif edit_target is not None: + return await edit_target.edit(**into_edit_kwargs(kwargs)) + # fallback to send message + + # if send + msg: Message + delete_after = kwargs.get('delete_after', None) + ephemeral = kwargs.get('ephemeral', False) + kwargs = into_send_kwargs(kwargs) + if isinstance(messageable, Interaction): + if messageable.response.is_done(): + msg = await messageable.followup.send(wait=True, ephemeral=ephemeral, **kwargs) + else: + await messageable.response.send_message(ephemeral=ephemeral, **kwargs) + msg = await messageable.original_response() + + if delete_after is not None: + await msg.delete(delay=delete_after) + else: + # type-ignore: can pass None to delete_after + msg = await messageable.send(delete_after=delete_after, **kwargs) # type: ignore[reportArgumentType, arg-type] + # type-ignore: return type is Message, InteractionMessage or WebhookMessage, which are also _Editable + return msg # type: ignore[reportReturnType, return-value] diff --git a/discord/ext/flow/view.py b/discord/ext/flow/view.py index b866b89..fd54070 100644 --- a/discord/ext/flow/view.py +++ b/discord/ext/flow/view.py @@ -7,7 +7,7 @@ from .model import Button, ChannelSelect, Link, MentionableSelect, RoleSelect, Select, UserSelect from .result import _ResultTypeEnum -from .util import unwrap_or +from .util import send_helper, unwrap_or if TYPE_CHECKING: from collections.abc import Sequence @@ -174,19 +174,7 @@ async def set_result(self, result: Result, interaction: Interaction[Client]) -> msg = result._message self.clear_items() self.set_items(msg.items or ()) - if msg.edit_original: - await interaction.response.edit_message( - content=msg.content, - embeds=msg.embeds or (), - attachments=msg.files or (), - view=self, - allowed_mentions=msg.allowed_mentions, - delete_after=msg.delete_after, - ) - else: - kwargs = msg._to_dict() - kwargs['view'] = self - await interaction.response.send_message(**kwargs) + await send_helper(interaction, msg, self, None) if not msg.items: self.stop() case _ResultTypeEnum.MODEL: