Source code for discohook.message

from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union

from .attachment import Attachment
from .embed import Embed
from .emoji import PartialEmoji
from .file import File
from .models import AllowedMentions, MessageReference
from .multipart import create_form
from .params import MISSING, handle_edit_params, merge_fields, handle_send_params
from .role import Role
from .user import User
from .view import View

if TYPE_CHECKING:
    from .client import Client


class MessageInteraction:
    """
    Represents a partial interaction received with message.
    """

    def __init__(self, client: "Client", payload: Dict[str, Any]) -> None:
        self.client = client
        self.data = payload

    @property
    def id(self) -> str:
        """
        The id of the interaction.
        """
        return self.data["id"]

    @property
    def token(self) -> str:
        """
        The token of the interaction.
        """
        return self.data["token"]

    @property
    def type(self) -> int:
        """
        The type of interaction.
        """
        return self.data["type"]

    @property
    def user(self) -> User:
        """
        The user who invoked the interaction.
        """
        return User(self.client, self.data["user"])


[docs]class Message: """ Represents a Discord message. Properties ---------- id: :class:`str` The id of the message. channel_id: :class:`str` The id of the channel the message was sent in. author: :class:`User` The author of the message. content: :class:`str` The content of the message. timestamp: :class:`str` The timestamp of the message. edited_timestamp: Optional[:class:`str`] The timestamp of when the message was last edited. tts: :class:`bool` Whether the message was sent using text-to-speech. mention_everyone: :class:`bool` Whether the message mentions everyone. mentions: List[:class:`User`] The users mentioned in the message. mention_roles: List[:class:`Role`] The roles mentioned in the message. mention_channels: Optional[:class:`dict`] The channels mentioned in the message. attachments: :class:`dict` The attachments in the message. embeds: :class:`list` The embeds in the message. reactions: Optional[:class:`list`] The reactions in the message. ... """ def __init__(self, client: "Client", payload: Dict[str, Any]) -> None: self.client = client self.data = payload @property def id(self) -> str: return self.data["id"] @property def type(self) -> int: return self.data["type"] @property def channel_id(self) -> str: return self.data["channel_id"] @property def author(self) -> User: return User(self.client, self.data["author"]) @property def content(self) -> Optional[str]: return self.data["content"] @property def timestamp(self) -> str: return self.data["timestamp"] @property def edited_timestamp(self) -> Optional[str]: return self.data.get("edited_timestamp") @property def tts(self) -> bool: return self.data.get("tts", False) @property def mention_everyone(self) -> bool: return self.data.get("mention_everyone", False) @property def mentions(self) -> List[User]: return [User(self.client, x) for x in self.data.get("mentions", [])] @property def mention_roles(self) -> List[Role]: return [Role(self.client, x) for x in self.data.get("mention_roles", [])] @property def mention_channels(self) -> Optional[dict]: return self.data.get("mention_channels") @property def attachments(self) -> Optional[List[Attachment]]: attachments = self.data.get("attachments") if not attachments: return return [Attachment(x) for x in attachments] @property def embeds(self) -> Optional[List[Embed]]: embeds = self.data.get("embeds") if not embeds: return return [Embed.from_dict(x) for x in embeds] @property def reactions(self) -> Optional[List[dict]]: return self.data.get("reactions") @property def nonce(self) -> Optional[str]: return self.data.get("nonce") @property def pinned(self) -> bool: return self.data.get("pinned", False) @property def webhook_id(self) -> Optional[str]: return self.data.get("webhook_id") @property def activity(self) -> Optional[dict]: return self.data.get("activity") @property def application(self) -> Optional[dict]: return self.data.get("application") @property def application_id(self) -> Optional[str]: return self.data.get("application_id") @property def message_reference(self) -> Optional[dict]: return self.data.get("message_reference") @property def flags(self) -> Optional[int]: return self.data.get("flags") @property def referenced_message(self) -> Optional[dict]: return self.data.get("referenced_message") @property def interaction(self) -> Optional[MessageInteraction]: data = self.data.get("interaction") if not data: return return MessageInteraction(self.client, data) @property def thread(self) -> Optional[dict]: return self.data.get("thread") @property def components(self) -> Optional[List[dict]]: return self.data.get("components") @property def sticker_items(self) -> Optional[List[dict]]: return self.data.get("sticker_items") @property def stickers(self) -> Optional[List[dict]]: return self.data.get("stickers") @property def position(self) -> Optional[int]: return self.data.get("position")
[docs] async def delete(self): """ Deletes the message. """ return await self.client.http.delete_channel_message(self.channel_id, self.id)
[docs] async def edit( self, content: Optional[str] = MISSING, *, embed: Optional[Embed] = MISSING, embeds: Optional[List[Embed]] = MISSING, view: Optional[View] = MISSING, tts: Optional[bool] = MISSING, file: Optional[File] = MISSING, files: Optional[List[File]] = MISSING, suppress_embeds: Optional[bool] = MISSING, ): """ Edits the message. Parameters ---------- content: Optional[str] The new content of the message. embed: Optional[Embed] The new embed of the message. embeds: Optional[List[Embed]] The new embeds of the message. view: Optional[View] The new view of the message. tts: Optional[bool] Whether the message should be sent with text-to-speech. file: Optional[File] A file to send with the message. files: Optional[List[File]] A list of files to send with the message. suppress_embeds: Optional[bool] Whether the embeds should be suppressed. """ data = handle_edit_params( content=content, embed=embed, embeds=embeds, view=view, tts=tts, file=file, files=files, suppress_embeds=suppress_embeds, ) if view and view is not MISSING: self.client.load_components(view) resp = await self.client.http.edit_channel_message( self.channel_id, self.id, create_form(data, merge_fields(file, files)) ) data = await resp.json() return Message(self.client, data)
[docs] async def pin(self): """ Pins the message to the channel. """ return await self.client.http.pin_channel_message(self.channel_id, self.id)
[docs] async def unpin(self): """ Unpins the message from the channel. """ return await self.client.http.unpin_channel_message(self.channel_id, self.id)
[docs] async def reply( self, content: Optional[str] = None, *, embed: Optional[Embed] = None, embeds: Optional[List[Embed]] = None, view: Optional[View] = None, tts: Optional[bool] = False, file: Optional[File] = None, files: Optional[List[File]] = None, allowed_mentions: Optional[AllowedMentions] = None, mention_author: Optional[bool] = None, ): """ Replies to the message. Parameters ---------- content: Optional[str] The content of the message. embed: Optional[Embed] The embed of the message. embeds: Optional[List[Embed]] The embeds of the message. view: Optional[View] The view of the message. tts: Optional[bool] Whether the message should be sent with text-to-speech. file: Optional[File] A file to send with the message. files: Optional[List[File]] A list of files to send with the message. allowed_mentions: Optional[AllowedMentions] The allowed mentions for the message. mention_author: Optional[bool] Whether the author should be mentioned. Returns ------- Message """ if mention_author is not None: if not allowed_mentions: allowed_mentions = AllowedMentions(replied_user=True) allowed_mentions["replied_user"] = True data = handle_send_params( content=content, embed=embed, embeds=embeds, view=view, tts=tts, file=file, files=files, allowed_mentions=allowed_mentions, message_reference=MessageReference(message_id=self.id, channel_id=self.channel_id) ) if view and view is not MISSING: self.client.load_components(view) resp = await self.client.http.send_message(self.channel_id, create_form(data, merge_fields(file, files))) data = await resp.json() return Message(self.client, data)
[docs] async def add_reaction(self, emoji: Union[PartialEmoji, str]): """ Creates a reaction on the message. Parameters ---------- emoji: Union[Emoji, str] The emoji to react with. """ if isinstance(emoji, PartialEmoji): encoded = f'{emoji.name}:{emoji.id}' else: encoded = "".join(f"%{byte:02x}" for byte in emoji.encode("utf-8")) return await self.client.http.create_message_reaction(self.channel_id, self.id, encoded)
[docs] async def remove_reaction(self, emoji: Union[PartialEmoji, str], user_id: Optional[str] = None): """ Removes a reaction on the message. Parameters ---------- emoji: Union[Emoji, str] The emoji to delete. user_id: Optional[str] The user to delete the reaction of. """ if isinstance(emoji, PartialEmoji): encoded = f'{emoji.name}:{emoji.id}' else: encoded = "".join(f"%{byte:02x}" for byte in emoji.encode("utf-8")) return await self.client.http.delete_message_reaction(self.channel_id, self.id, encoded, user_id or "@me")
[docs] async def remove_reactions(self, emoji: Optional[Union[PartialEmoji, str]] = None): """ Removes all reactions on the message. Parameters ---------- emoji: Union[Emoji, str, None] The emoji to remove reactions of. """ if isinstance(emoji, PartialEmoji): encoded = f'{emoji.name}:{emoji.id}' else: encoded = "".join(f"%{byte:02x}" for byte in emoji.encode("utf-8")) return await self.client.http.delete_all_message_reactions(self.id, self.channel_id, encoded)