Browse Source

chore: update ViewStore.dispatch_view

Since a time ago, the /interactions/{token}/callback route got a return type (finally), and this code updates the whole interaction_id dependency the current system had to always use the
resource id (in case it is a message) Discord provides.

discord.py always defaults to receiving a response on interaction callbacks, so it should not fail anymore.

Maybe needs some fixing, the testing I've done seems to be working fine, so I guess I'll leave it like that currently
pull/10166/head
DA-344 2 months ago
parent
commit
49125e8406
  1. 34
      discord/interactions.py
  2. 6
      discord/message.py
  3. 10
      discord/state.py
  4. 27
      discord/ui/view.py

34
discord/interactions.py

@ -1071,18 +1071,24 @@ class InteractionResponse(Generic[ClientT]):
proxy_auth=http.proxy_auth,
params=params,
)
self._response_type = InteractionResponseType.channel_message
ret = InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
if view is not MISSING and not view.is_finished():
if ephemeral and view.timeout is None:
view.timeout = 15 * 60.0
# If the interaction type isn't an application command then there's no way
# to obtain this interaction_id again, so just default to None
entity_id = parent.id if parent.type is InteractionType.application_command else None
# this assertion should never fail because the resource of a send_message
# response will always be an InteractionMessage
assert isinstance(ret.resource, InteractionMessage)
entity_id = ret.resource.id if parent.type is InteractionType.application_command else None
self._parent._state.store_view(view, entity_id)
self._response_type = InteractionResponseType.channel_message
if delete_after is not None:
async def inner_call(delay: float = delete_after):
@ -1094,12 +1100,7 @@ class InteractionResponse(Generic[ClientT]):
asyncio.create_task(inner_call())
return InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
return ret
@overload
async def edit_message(
@ -1208,14 +1209,7 @@ class InteractionResponse(Generic[ClientT]):
parent = self._parent
msg = parent.message
state = parent._state
if msg is not None:
message_id = msg.id
# If this was invoked via an application command then we can use its original interaction ID
# Since this is used as a cache key for view updates
original_interaction_id = msg.interaction_metadata.id if msg.interaction_metadata is not None else None
else:
message_id = None
original_interaction_id = None
message_id = msg and msg.id
if parent.type not in (InteractionType.component, InteractionType.modal_submit):
return
@ -1253,7 +1247,7 @@ class InteractionResponse(Generic[ClientT]):
)
if view and not view.is_finished():
state.store_view(view, message_id, interaction_id=original_interaction_id)
state.store_view(view, message_id)
self._response_type = InteractionResponseType.message_update

6
discord/message.py

@ -1444,11 +1444,7 @@ class PartialMessage(Hashable):
message = Message(state=self._state, channel=self.channel, data=data)
if view and not view.is_finished():
interaction: Optional[MessageInteraction] = getattr(self, 'interaction', None)
if interaction is not None:
self._state.store_view(view, self.id, interaction_id=interaction.id)
else:
self._state.store_view(view, self.id)
self._state.store_view(view, self.id)
if delete_after is not None:
await self.delete(delay=delete_after)

10
discord/state.py

@ -412,9 +412,7 @@ class ConnectionState(Generic[ClientT]):
self._stickers[sticker_id] = sticker = GuildSticker(state=self, data=data)
return sticker
def store_view(self, view: BaseView, message_id: Optional[int] = None, interaction_id: Optional[int] = None) -> None:
if interaction_id is not None:
self._view_store.remove_interaction_mapping(interaction_id)
def store_view(self, view: BaseView, message_id: Optional[int] = None) -> None:
self._view_store.add_view(view, message_id)
def prevent_view_updates_for(self, message_id: int) -> Optional[BaseView]:
@ -735,11 +733,7 @@ class ConnectionState(Generic[ClientT]):
self.dispatch('raw_message_edit', raw)
if 'components' in data:
try:
entity_id = int(data['interaction']['id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except (KeyError, ValueError):
entity_id = raw.message_id
entity_id = raw.message_id
if self._view_store.is_message_tracked(entity_id):
self._view_store.update_from_message(entity_id, data['components'])

27
discord/ui/view.py

@ -1162,15 +1162,12 @@ class ViewStore:
def dispatch_view(self, component_type: int, custom_id: str, interaction: Interaction) -> None:
self.dispatch_dynamic_items(component_type, custom_id, interaction)
interaction_id: Optional[int] = None
message_id: Optional[int] = None
# Realistically, in a component based interaction the Interaction.message will never be None
# However, this guard is just in case Discord screws up somehow
msg = interaction.message
if msg is not None:
message_id = msg.id
if msg.interaction_metadata:
interaction_id = msg.interaction_metadata.id
key = (component_type, custom_id)
@ -1179,27 +1176,10 @@ class ViewStore:
if message_id is not None:
item = self._views.get(message_id, {}).get(key)
if item is None and interaction_id is not None:
try:
items = self._views.pop(interaction_id)
except KeyError:
item = None
else:
item = items.get(key)
# If we actually got the items, then these keys should probably be moved
# to the proper message_id instead of the interaction_id as they are now.
# An interaction_id is only used as a temporary stop gap for
# InteractionResponse.send_message so multiple view instances do not
# override each other.
# NOTE: Fix this mess if /callback endpoint ever gets proper return types
self._views.setdefault(message_id, {}).update(items)
if item is None:
# Fallback to None message_id searches in case a persistent view
# was added without an associated message_id
item = self._views.get(None, {}).get(key)
# If 3 lookups failed at this point then just discard it
# If 2 lookups failed at this point then just discard it
if item is None:
return
@ -1219,11 +1199,6 @@ class ViewStore:
modal._dispatch_submit(interaction, components)
def remove_interaction_mapping(self, interaction_id: int) -> None:
# This is called before re-adding the view
self._views.pop(interaction_id, None)
self._synced_message_views.pop(interaction_id, None)
def is_message_tracked(self, message_id: int) -> bool:
return message_id in self._synced_message_views

Loading…
Cancel
Save