Source code for byte_bot.byte.views.abstract_views

"""Inheritable views that include extra functionality for base Views classes."""

from __future__ import annotations

from copy import deepcopy
from typing import TYPE_CHECKING, Any, Literal, ParamSpec, TypedDict

from discord import ButtonStyle, Colour, Embed, Interaction
from discord.ui import Button, View, button

if TYPE_CHECKING:
    from datetime import datetime
    from typing import NotRequired, Self

    from discord.ext.commands import Bot

__all__ = ("ButtonEmbedView", "ExtendedEmbed", "Field")

P = ParamSpec("P")


[docs] class ButtonEmbedView(View): """Base view including common buttons."""
[docs] def __init__( self, author: int, bot: Bot, original_embed: Embed, minified_embed: Embed, *args, **kwargs, # type: ignore[misc] ) -> None: """Initialize the view. Args: author: Author ID. bot: Bot object. original_embed: The original embed to display. minified_embed: The minified embed to display. *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. """ super().__init__(*args, **kwargs) self.author_id = author self.bot = bot self.original_embed = original_embed self.minified_embed = minified_embed
[docs] async def delete_interaction_check(self, interaction: Interaction) -> bool: """Check if the user is the author or a guild admin. .. note:: Only checks for the ``delete`` button, as we want to expose the ``learn more`` button to anyone. Args: interaction: Interaction object. Returns: True if the user is the author or a guild admin, False otherwise. """ if interaction.user.id == self.author_id or ( getattr(interaction.user, "guild_permissions", None) and interaction.user.guild_permissions.administrator # type: ignore[attr-defined] ): return True await interaction.response.send_message( "You do not have permission to interact with this message.", ephemeral=True ) return False
[docs] async def delete_button_callback(self, interaction: Interaction) -> None: """Delete the message this view is attached to. Args: interaction: Interaction object. """ if await self.delete_interaction_check(interaction): await interaction.message.delete()
[docs] async def learn_more_button_callback(self, interaction: Interaction) -> None: """Send the original embed to the user privately. Args: interaction: Interaction object. """ await interaction.response.send_message(embed=self.original_embed, ephemeral=True)
[docs] @button(label="Delete", style=ButtonStyle.red, custom_id="delete_button") async def delete_button(self, interaction: Interaction, _: Button[Self]) -> None: """Button to delete the message this view is attached to. Args: interaction: Interaction object. _: Button object. """ await self.delete_button_callback(interaction)
[docs] @button(label="Learn More", style=ButtonStyle.green, custom_id="learn_more_button") async def learn_more_button(self, interaction: Interaction, _: Button[Self]) -> None: """Button to privately message the requesting user the full embed. Args: interaction: Interaction object. _: Button object. """ await self.learn_more_button_callback(interaction)
[docs] class Field(TypedDict): """Field type for ``ExtendedEmbed``. .. note:: types are matching the ones in ``Embed.add_fields``. """ name: Any value: Any inline: NotRequired[bool]
[docs] class ExtendedEmbed(Embed): """Extended Embed class for discord.py."""
[docs] def add_field_dict(self, field: Field) -> Self: """Add a field to the embed. Args: field (Field): The field to add to the embed. Returns: Self: The embed with the field added. """ self.add_field(**field) return self
[docs] def add_field_dicts(self, fields: list[Field]) -> Self: """Add multiple fields to the embed. Args: fields (list[Field]): A list of fields to add to the embed. Returns: Self: The embed with the fields added. """ for field in fields: self.add_field_dict(field) return self
[docs] @classmethod def from_field_dicts( cls, colour: int | Colour | None = None, color: int | Colour | None = None, title: Any | None = None, type: Literal["rich", "image", "video", "gifv", "article", "link"] = "rich", # noqa: A002 url: Any | None = None, description: Any | None = None, timestamp: datetime | None = None, fields: list[Field] | None = None, ) -> Self: """Create an embed from a list of fields. Args: colour (int | Colour | None): The colour of the embed. color (int | Colour | None): The colour of the embed. title (Any | None): The title of the embed. type (Literal["rich", "image", "video", "gifv", "article", "link"]): The type of the embed. url (Any | None): The URL of the embed. description (Any | None): The description of the embed. timestamp (datetime | None): The timestamp of the embed. fields (list[Field] | None): A list of fields to add to the embed. Returns: Self: The embed with the fields added. """ embed = cls( colour=colour, color=color, title=title, type=type, url=url, description=description, timestamp=timestamp, ) embed.add_field_dicts(fields or []) return embed
[docs] def deepcopy(self) -> Self: """Create a deep copy of the embed. Returns: Self: A deep copy of the embed. """ return deepcopy(self)