Add PartialMessage to allow working with channel/message_id pairs.
Fix #5905
This commit is contained in:
		@@ -517,6 +517,28 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
 | 
				
			|||||||
        data = await self._state.http.follow_webhook(self.id, webhook_channel_id=destination.id, reason=reason)
 | 
					        data = await self._state.http.follow_webhook(self.id, webhook_channel_id=destination.id, reason=reason)
 | 
				
			||||||
        return Webhook._as_follower(data, channel=destination, user=self._state.user)
 | 
					        return Webhook._as_follower(data, channel=destination, user=self._state.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_partial_message(self, message_id):
 | 
				
			||||||
 | 
					        """Creates a :class:`PartialMessage` from the message ID.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This is useful if you want to work with a message and only have its ID without
 | 
				
			||||||
 | 
					        doing an unnecessary API call.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters
 | 
				
			||||||
 | 
					        ------------
 | 
				
			||||||
 | 
					        message_id: :class:`int`
 | 
				
			||||||
 | 
					            The message ID to create a partial message for.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns
 | 
				
			||||||
 | 
					        ---------
 | 
				
			||||||
 | 
					        :class:`PartialMessage`
 | 
				
			||||||
 | 
					            The partial message.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from .message import PartialMessage
 | 
				
			||||||
 | 
					        return PartialMessage(channel=self, id=message_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
 | 
					class VoiceChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
 | 
				
			||||||
    """Represents a Discord guild voice channel.
 | 
					    """Represents a Discord guild voice channel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ from .reaction import Reaction
 | 
				
			|||||||
from .emoji import Emoji
 | 
					from .emoji import Emoji
 | 
				
			||||||
from .partial_emoji import PartialEmoji
 | 
					from .partial_emoji import PartialEmoji
 | 
				
			||||||
from .calls import CallMessage
 | 
					from .calls import CallMessage
 | 
				
			||||||
from .enums import MessageType, try_enum
 | 
					from .enums import MessageType, ChannelType, try_enum
 | 
				
			||||||
from .errors import InvalidArgument, ClientException, HTTPException
 | 
					from .errors import InvalidArgument, ClientException, HTTPException
 | 
				
			||||||
from .embeds import Embed
 | 
					from .embeds import Embed
 | 
				
			||||||
from .member import Member
 | 
					from .member import Member
 | 
				
			||||||
@@ -48,10 +48,26 @@ from .sticker import Sticker
 | 
				
			|||||||
__all__ = (
 | 
					__all__ = (
 | 
				
			||||||
    'Attachment',
 | 
					    'Attachment',
 | 
				
			||||||
    'Message',
 | 
					    'Message',
 | 
				
			||||||
 | 
					    'PartialMessage',
 | 
				
			||||||
    'MessageReference',
 | 
					    'MessageReference',
 | 
				
			||||||
    'DeletedReferencedMessage',
 | 
					    'DeletedReferencedMessage',
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_emoji_reaction(emoji):
 | 
				
			||||||
 | 
					    if isinstance(emoji, Reaction):
 | 
				
			||||||
 | 
					        emoji = emoji.emoji
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if isinstance(emoji, Emoji):
 | 
				
			||||||
 | 
					        return '%s:%s' % (emoji.name, emoji.id)
 | 
				
			||||||
 | 
					    if isinstance(emoji, PartialEmoji):
 | 
				
			||||||
 | 
					        return emoji._as_reaction()
 | 
				
			||||||
 | 
					    if isinstance(emoji, str):
 | 
				
			||||||
 | 
					        # Reactions can be in :name:id format, but not <:name:id>.
 | 
				
			||||||
 | 
					        # No existing emojis have <> in them, so this should be okay.
 | 
				
			||||||
 | 
					        return emoji.strip('<>')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raise InvalidArgument('emoji argument must be str, Emoji, or Reaction not {.__class__.__name__}.'.format(emoji))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Attachment:
 | 
					class Attachment:
 | 
				
			||||||
    """Represents an attachment from Discord.
 | 
					    """Represents an attachment from Discord.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1117,7 +1133,7 @@ class Message(Hashable):
 | 
				
			|||||||
            The emoji parameter is invalid.
 | 
					            The emoji parameter is invalid.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        emoji = self._emoji_reaction(emoji)
 | 
					        emoji = convert_emoji_reaction(emoji)
 | 
				
			||||||
        await self._state.http.add_reaction(self.channel.id, self.id, emoji)
 | 
					        await self._state.http.add_reaction(self.channel.id, self.id, emoji)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def remove_reaction(self, emoji, member):
 | 
					    async def remove_reaction(self, emoji, member):
 | 
				
			||||||
@@ -1152,7 +1168,7 @@ class Message(Hashable):
 | 
				
			|||||||
            The emoji parameter is invalid.
 | 
					            The emoji parameter is invalid.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        emoji = self._emoji_reaction(emoji)
 | 
					        emoji = convert_emoji_reaction(emoji)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if member.id == self._state.self_id:
 | 
					        if member.id == self._state.self_id:
 | 
				
			||||||
            await self._state.http.remove_own_reaction(self.channel.id, self.id, emoji)
 | 
					            await self._state.http.remove_own_reaction(self.channel.id, self.id, emoji)
 | 
				
			||||||
@@ -1187,25 +1203,9 @@ class Message(Hashable):
 | 
				
			|||||||
            The emoji parameter is invalid.
 | 
					            The emoji parameter is invalid.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        emoji = self._emoji_reaction(emoji)
 | 
					        emoji = convert_emoji_reaction(emoji)
 | 
				
			||||||
        await self._state.http.clear_single_reaction(self.channel.id, self.id, emoji)
 | 
					        await self._state.http.clear_single_reaction(self.channel.id, self.id, emoji)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def _emoji_reaction(emoji):
 | 
					 | 
				
			||||||
        if isinstance(emoji, Reaction):
 | 
					 | 
				
			||||||
            emoji = emoji.emoji
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if isinstance(emoji, Emoji):
 | 
					 | 
				
			||||||
            return '%s:%s' % (emoji.name, emoji.id)
 | 
					 | 
				
			||||||
        if isinstance(emoji, PartialEmoji):
 | 
					 | 
				
			||||||
            return emoji._as_reaction()
 | 
					 | 
				
			||||||
        if isinstance(emoji, str):
 | 
					 | 
				
			||||||
            # Reactions can be in :name:id format, but not <:name:id>.
 | 
					 | 
				
			||||||
            # No existing emojis have <> in them, so this should be okay.
 | 
					 | 
				
			||||||
            return emoji.strip('<>')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        raise InvalidArgument('emoji argument must be str, Emoji, or Reaction not {.__class__.__name__}.'.format(emoji))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async def clear_reactions(self):
 | 
					    async def clear_reactions(self):
 | 
				
			||||||
        """|coro|
 | 
					        """|coro|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1248,7 +1248,7 @@ class Message(Hashable):
 | 
				
			|||||||
        A shortcut method to :meth:`abc.Messageable.send` to reply to the
 | 
					        A shortcut method to :meth:`abc.Messageable.send` to reply to the
 | 
				
			||||||
        :class:`Message`.
 | 
					        :class:`Message`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .. versionadded:: 1.6
 | 
					        .. versionadded:: 1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Raises
 | 
					        Raises
 | 
				
			||||||
        --------
 | 
					        --------
 | 
				
			||||||
@@ -1291,3 +1291,101 @@ class Message(Hashable):
 | 
				
			|||||||
            data['guild_id'] = self.guild.id
 | 
					            data['guild_id'] = self.guild.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return data
 | 
					        return data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def implement_partial_methods(cls):
 | 
				
			||||||
 | 
					    msg = Message
 | 
				
			||||||
 | 
					    for name in cls._exported_names:
 | 
				
			||||||
 | 
					        func = getattr(msg, name)
 | 
				
			||||||
 | 
					        setattr(cls, name, func)
 | 
				
			||||||
 | 
					    return cls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@implement_partial_methods
 | 
				
			||||||
 | 
					class PartialMessage(Hashable):
 | 
				
			||||||
 | 
					    """Represents a partial message to aid with working messages when only
 | 
				
			||||||
 | 
					    a message and channel ID are present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    There are two ways to construct this class. The first one is through
 | 
				
			||||||
 | 
					    the constructor itself, and the second is via
 | 
				
			||||||
 | 
					    :meth:`TextChannel.get_partial_message`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Note that this class is trimmed down and has no rich attributes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Attributes
 | 
				
			||||||
 | 
					    -----------
 | 
				
			||||||
 | 
					    channel: :class:`TextChannel`
 | 
				
			||||||
 | 
					        The text channel associated with this partial message.
 | 
				
			||||||
 | 
					    id: :class:`int`
 | 
				
			||||||
 | 
					        The message ID.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __slots__ = ('channel', 'id', '_state')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _exported_names = (
 | 
				
			||||||
 | 
					        'jump_url',
 | 
				
			||||||
 | 
					        'delete',
 | 
				
			||||||
 | 
					        'edit',
 | 
				
			||||||
 | 
					        'publish',
 | 
				
			||||||
 | 
					        'pin',
 | 
				
			||||||
 | 
					        'unpin',
 | 
				
			||||||
 | 
					        'add_reaction',
 | 
				
			||||||
 | 
					        'remove_reaction',
 | 
				
			||||||
 | 
					        'clear_reaction',
 | 
				
			||||||
 | 
					        'clear_reactions',
 | 
				
			||||||
 | 
					        'reply',
 | 
				
			||||||
 | 
					        'to_reference',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *, channel, id):
 | 
				
			||||||
 | 
					        if channel.type not in (ChannelType.text, ChannelType.news):
 | 
				
			||||||
 | 
					            raise TypeError('Expected TextChannel not %r' % type(channel))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.channel = channel
 | 
				
			||||||
 | 
					        self._state = channel._state
 | 
				
			||||||
 | 
					        self.id = id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update(self, data):
 | 
				
			||||||
 | 
					        # This is used for duck typing purposes.
 | 
				
			||||||
 | 
					        # Just do nothing with the data.
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Also needed for duck typing purposes
 | 
				
			||||||
 | 
					    # n.b. not exposed
 | 
				
			||||||
 | 
					    pinned = property(None, lambda x, y: ...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        return '<PartialMessage id={0.id} channel={0.channel!r}>'.format(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def created_at(self):
 | 
				
			||||||
 | 
					        """:class:`datetime.datetime`: The partial message's creation time in UTC."""
 | 
				
			||||||
 | 
					        return utils.snowflake_time(self.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def guild(self):
 | 
				
			||||||
 | 
					        """:class:`Guild`: The guild that the partial message belongs to."""
 | 
				
			||||||
 | 
					        return self.channel.guild
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def fetch(self):
 | 
				
			||||||
 | 
					        """|coro|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Fetches the partial message to a full :class:`Message`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises
 | 
				
			||||||
 | 
					        --------
 | 
				
			||||||
 | 
					        NotFound
 | 
				
			||||||
 | 
					            The message was not found.
 | 
				
			||||||
 | 
					        Forbidden
 | 
				
			||||||
 | 
					            You do not have the permissions required to get a message.
 | 
				
			||||||
 | 
					        HTTPException
 | 
				
			||||||
 | 
					            Retrieving the message failed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns
 | 
				
			||||||
 | 
					        --------
 | 
				
			||||||
 | 
					        :class:`Message`
 | 
				
			||||||
 | 
					            The full message.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = await self._state.http.get_message(self.channel.id, self.id)
 | 
				
			||||||
 | 
					        return self._state.create_message(channel=self.channel, data=data)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2952,6 +2952,12 @@ MessageReference
 | 
				
			|||||||
.. autoclass:: MessageReference
 | 
					.. autoclass:: MessageReference
 | 
				
			||||||
    :members:
 | 
					    :members:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PartialMessage
 | 
				
			||||||
 | 
					~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: PartialMessage
 | 
				
			||||||
 | 
					    :members:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Intents
 | 
					Intents
 | 
				
			||||||
~~~~~~~~~~
 | 
					~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user