Browse Source

Add typings for message related classes

pull/6836/head
Zomatree 4 years ago
committed by GitHub
parent
commit
3381d1e089
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 205
      discord/message.py
  2. 1
      discord/types/message.py

205
discord/message.py

@ -22,10 +22,14 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations
import asyncio import asyncio
import datetime import datetime
import re import re
import io import io
from os import PathLike
from typing import TYPE_CHECKING, Union, List, Optional, Any, Callable, Tuple, ClassVar
from . import utils from . import utils
from .reaction import Reaction from .reaction import Reaction
@ -42,6 +46,26 @@ from .guild import Guild
from .mixins import Hashable from .mixins import Hashable
from .sticker import Sticker from .sticker import Sticker
if TYPE_CHECKING:
from .types.message import (
Message as MessagePayload,
Attachment as AttachmentPayload,
MessageReference as MessageReferencePayload,
MessageApplication as MessageApplicationPayload,
MessageActivity as MessageActivityPayload,
Reaction as ReactionPayload,
)
from .types.member import Member as MemberPayload
from .types.user import User as UserPayload
from .types.embed import Embed as EmbedPayload
from .abc import Snowflake
from .abc import GuildChannel, PrivateChannel, Messageable
from .state import ConnectionState
from .channel import TextChannel, GroupChannel, DMChannel
EmojiInputType = Union[Emoji, PartialEmoji, str]
__all__ = ( __all__ = (
'Attachment', 'Attachment',
'Message', 'Message',
@ -116,7 +140,7 @@ class Attachment(Hashable):
__slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type') __slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type')
def __init__(self, *, data, state): def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
self.id = int(data['id']) self.id = int(data['id'])
self.size = data['size'] self.size = data['size']
self.height = data.get('height') self.height = data.get('height')
@ -127,17 +151,17 @@ class Attachment(Hashable):
self._http = state.http self._http = state.http
self.content_type = data.get('content_type') self.content_type = data.get('content_type')
def is_spoiler(self): def is_spoiler(self) -> bool:
""":class:`bool`: Whether this attachment contains a spoiler.""" """:class:`bool`: Whether this attachment contains a spoiler."""
return self.filename.startswith('SPOILER_') return self.filename.startswith('SPOILER_')
def __repr__(self): def __repr__(self) -> str:
return f'<Attachment id={self.id} filename={self.filename!r} url={self.url!r}>' return f'<Attachment id={self.id} filename={self.filename!r} url={self.url!r}>'
def __str__(self): def __str__(self) -> str:
return self.url or '' return self.url or ''
async def save(self, fp, *, seek_begin=True, use_cached=False): async def save(self, fp: Union[io.BufferedIOBase, PathLike], *, seek_begin: bool = True, use_cached: bool = False) -> int:
"""|coro| """|coro|
Saves this attachment into a file-like object. Saves this attachment into a file-like object.
@ -181,7 +205,7 @@ class Attachment(Hashable):
with open(fp, 'wb') as f: with open(fp, 'wb') as f:
return f.write(data) return f.write(data)
async def read(self, *, use_cached=False): async def read(self, *, use_cached: bool = False) -> bytes:
"""|coro| """|coro|
Retrieves the content of this attachment as a :class:`bytes` object. Retrieves the content of this attachment as a :class:`bytes` object.
@ -216,7 +240,7 @@ class Attachment(Hashable):
data = await self._http.get_from_cdn(url) data = await self._http.get_from_cdn(url)
return data return data
async def to_file(self, *, use_cached=False, spoiler=False): async def to_file(self, *, use_cached: bool = False, spoiler: bool = False) -> File:
"""|coro| """|coro|
Converts the attachment into a :class:`File` suitable for sending via Converts the attachment into a :class:`File` suitable for sending via
@ -258,8 +282,8 @@ class Attachment(Hashable):
data = await self.read(use_cached=use_cached) data = await self.read(use_cached=use_cached)
return File(io.BytesIO(data), filename=self.filename, spoiler=spoiler) return File(io.BytesIO(data), filename=self.filename, spoiler=spoiler)
def to_dict(self): def to_dict(self) -> AttachmentPayload:
result = { result: AttachmentPayload = {
'filename': self.filename, 'filename': self.filename,
'id': self.id, 'id': self.id,
'proxy_url': self.proxy_url, 'proxy_url': self.proxy_url,
@ -287,24 +311,24 @@ class DeletedReferencedMessage:
__slots__ = ('_parent') __slots__ = ('_parent')
def __init__(self, parent): def __init__(self, parent: MessageReference):
self._parent = parent self._parent = parent
def __repr__(self): def __repr__(self) -> str:
return f"<DeletedReferencedMessage id={self.id} channel_id={self.channel_id} guild_id={self.guild_id!r}>" return f"<DeletedReferencedMessage id={self.id} channel_id={self.channel_id} guild_id={self.guild_id!r}>"
@property @property
def id(self): def id(self) -> int:
""":class:`int`: The message ID of the deleted referenced message.""" """:class:`int`: The message ID of the deleted referenced message."""
return self._parent.message_id return self._parent.message_id
@property @property
def channel_id(self): def channel_id(self) -> int:
""":class:`int`: The channel ID of the deleted referenced message.""" """:class:`int`: The channel ID of the deleted referenced message."""
return self._parent.channel_id return self._parent.channel_id
@property @property
def guild_id(self): def guild_id(self) -> Optional[int]:
"""Optional[:class:`int`]: The guild ID of the deleted referenced message.""" """Optional[:class:`int`]: The guild ID of the deleted referenced message."""
return self._parent.guild_id return self._parent.guild_id
@ -345,16 +369,16 @@ class MessageReference:
__slots__ = ('message_id', 'channel_id', 'guild_id', 'fail_if_not_exists', 'resolved', '_state') __slots__ = ('message_id', 'channel_id', 'guild_id', 'fail_if_not_exists', 'resolved', '_state')
def __init__(self, *, message_id, channel_id, guild_id=None, fail_if_not_exists=True): def __init__(self, *, message_id: int, channel_id: int, guild_id: Optional[int] = None, fail_if_not_exists: bool = True):
self._state = None self._state: Optional[ConnectionState] = None
self.resolved = None self.resolved: Optional[Union[Message, DeletedReferencedMessage]] = None
self.message_id = message_id self.message_id = message_id
self.channel_id = channel_id self.channel_id = channel_id
self.guild_id = guild_id self.guild_id = guild_id
self.fail_if_not_exists = fail_if_not_exists self.fail_if_not_exists = fail_if_not_exists
@classmethod @classmethod
def with_state(cls, state, data): def with_state(cls, state: ConnectionState, data: MessageReferencePayload) -> MessageReference:
self = cls.__new__(cls) self = cls.__new__(cls)
self.message_id = utils._get_as_snowflake(data, 'message_id') self.message_id = utils._get_as_snowflake(data, 'message_id')
self.channel_id = int(data.pop('channel_id')) self.channel_id = int(data.pop('channel_id'))
@ -365,7 +389,7 @@ class MessageReference:
return self return self
@classmethod @classmethod
def from_message(cls, message, *, fail_if_not_exists=True): def from_message(cls, message: Message, *, fail_if_not_exists: bool = True):
"""Creates a :class:`MessageReference` from an existing :class:`~discord.Message`. """Creates a :class:`MessageReference` from an existing :class:`~discord.Message`.
.. versionadded:: 1.6 .. versionadded:: 1.6
@ -390,12 +414,12 @@ class MessageReference:
return self return self
@property @property
def cached_message(self): def cached_message(self) -> Optional[Message]:
"""Optional[:class:`~discord.Message`]: The cached message, if found in the internal message cache.""" """Optional[:class:`~discord.Message`]: The cached message, if found in the internal message cache."""
return self._state._get_message(self.message_id) return self._state and self._state._get_message(self.message_id)
@property @property
def jump_url(self): def jump_url(self) -> str:
""":class:`str`: Returns a URL that allows the client to jump to the referenced message. """:class:`str`: Returns a URL that allows the client to jump to the referenced message.
.. versionadded:: 1.7 .. versionadded:: 1.7
@ -403,11 +427,11 @@ class MessageReference:
guild_id = self.guild_id if self.guild_id is not None else '@me' guild_id = self.guild_id if self.guild_id is not None else '@me'
return f'https://discord.com/channels/{guild_id}/{self.channel_id}/{self.message_id}' return f'https://discord.com/channels/{guild_id}/{self.channel_id}/{self.message_id}'
def __repr__(self): def __repr__(self) -> str:
return f'<MessageReference message_id={self.message_id!r} channel_id={self.channel_id!r} guild_id={self.guild_id!r}>' return f'<MessageReference message_id={self.message_id!r} channel_id={self.channel_id!r} guild_id={self.guild_id!r}>'
def to_dict(self): def to_dict(self) -> MessageReferencePayload:
result = {'message_id': self.message_id} if self.message_id is not None else {} result: MessageReferencePayload = {'message_id': self.message_id} if self.message_id is not None else {}
result['channel_id'] = self.channel_id result['channel_id'] = self.channel_id
if self.guild_id is not None: if self.guild_id is not None:
result['guild_id'] = self.guild_id result['guild_id'] = self.guild_id
@ -460,17 +484,17 @@ class Message(Hashable):
type: :class:`MessageType` type: :class:`MessageType`
The type of message. In most cases this should not be checked, but it is helpful The type of message. In most cases this should not be checked, but it is helpful
in cases where it might be a system message for :attr:`system_content`. in cases where it might be a system message for :attr:`system_content`.
author: :class:`abc.User` author: Union[:class:`Member`, :class:`abc.User`]
A :class:`Member` that sent the message. If :attr:`channel` is a A :class:`Member` that sent the message. If :attr:`channel` is a
private channel or the user has the left the guild, then it is a :class:`User` instead. private channel or the user has the left the guild, then it is a :class:`User` instead.
content: :class:`str` content: :class:`str`
The actual contents of the message. The actual contents of the message.
nonce nonce: Union[:class:`str`, :class:`int`]
The value used by the discord guild and the client to verify that the message is successfully sent. The value used by the discord guild and the client to verify that the message is successfully sent.
This is not stored long term within Discord's servers and is only used ephemerally. This is not stored long term within Discord's servers and is only used ephemerally.
embeds: List[:class:`Embed`] embeds: List[:class:`Embed`]
A list of embeds the message has. A list of embeds the message has.
channel: Union[:class:`abc.Messageable`] channel: Union[:class:`TextChannel`, :class:`DMChannel`, :class:`GroupChannel`]
The :class:`TextChannel` that the message was sent from. The :class:`TextChannel` that the message was sent from.
Could be a :class:`DMChannel` or :class:`GroupChannel` if it's a private message. Could be a :class:`DMChannel` or :class:`GroupChannel` if it's a private message.
reference: Optional[:class:`~discord.MessageReference`] reference: Optional[:class:`~discord.MessageReference`]
@ -552,7 +576,10 @@ class Message(Hashable):
'_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference', '_cs_system_content', '_cs_guild', '_state', 'reactions', 'reference',
'application', 'activity', 'stickers') 'application', 'activity', 'stickers')
def __init__(self, *, state, channel, data): _HANDLERS: ClassVar[List[Tuple[str, Callable[..., None]]]]
_CACHED_SLOTS: ClassVar[List[str]]
def __init__(self, *, state: ConnectionState, channel: Union[TextChannel, DMChannel, GroupChannel], data: MessagePayload):
self._state = state self._state = state
self.id = int(data['id']) self.id = int(data['id'])
self.webhook_id = utils._get_as_snowflake(data, 'webhook_id') self.webhook_id = utils._get_as_snowflake(data, 'webhook_id')
@ -600,10 +627,10 @@ class Message(Hashable):
except KeyError: except KeyError:
continue continue
def __repr__(self): def __repr__(self) -> str:
return f'<Message id={self.id} channel={self.channel!r} type={self.type!r} author={self.author!r} flags={self.flags!r}>' return f'<Message id={self.id} channel={self.channel!r} type={self.type!r} author={self.author!r} flags={self.flags!r}>'
def _try_patch(self, data, key, transform=None): def _try_patch(self, data, key, transform=None) -> None:
try: try:
value = data[key] value = data[key]
except KeyError: except KeyError:
@ -614,7 +641,7 @@ class Message(Hashable):
else: else:
setattr(self, key, transform(value)) setattr(self, key, transform(value))
def _add_reaction(self, data, emoji, user_id): def _add_reaction(self, data, emoji, user_id) -> Reaction:
reaction = utils.find(lambda r: r.emoji == emoji, self.reactions) reaction = utils.find(lambda r: r.emoji == emoji, self.reactions)
is_me = data['me'] = user_id == self._state.self_id is_me = data['me'] = user_id == self._state.self_id
@ -628,7 +655,7 @@ class Message(Hashable):
return reaction return reaction
def _remove_reaction(self, data, emoji, user_id): def _remove_reaction(self, data: ReactionPayload, emoji: EmojiInputType, user_id: int) -> Reaction:
reaction = utils.find(lambda r: r.emoji == emoji, self.reactions) reaction = utils.find(lambda r: r.emoji == emoji, self.reactions)
if reaction is None: if reaction is None:
@ -647,7 +674,7 @@ class Message(Hashable):
return reaction return reaction
def _clear_emoji(self, emoji): def _clear_emoji(self, emoji) -> Optional[Reaction]:
to_check = str(emoji) to_check = str(emoji)
for index, reaction in enumerate(self.reactions): for index, reaction in enumerate(self.reactions):
if str(reaction.emoji) == to_check: if str(reaction.emoji) == to_check:
@ -679,50 +706,50 @@ class Message(Hashable):
except AttributeError: except AttributeError:
pass pass
def _handle_edited_timestamp(self, value): def _handle_edited_timestamp(self, value: str) -> None:
self._edited_timestamp = utils.parse_time(value) self._edited_timestamp = utils.parse_time(value)
def _handle_pinned(self, value): def _handle_pinned(self, value: int) -> None:
self.pinned = value self.pinned = value
def _handle_flags(self, value): def _handle_flags(self, value: int) -> None:
self.flags = MessageFlags._from_value(value) self.flags = MessageFlags._from_value(value)
def _handle_application(self, value): def _handle_application(self, value: MessageApplicationPayload) -> None:
self.application = value self.application = value
def _handle_activity(self, value): def _handle_activity(self, value: MessageActivityPayload) -> None:
self.activity = value self.activity = value
def _handle_mention_everyone(self, value): def _handle_mention_everyone(self, value: bool) -> None:
self.mention_everyone = value self.mention_everyone = value
def _handle_tts(self, value): def _handle_tts(self, value: bool) -> None:
self.tts = value self.tts = value
def _handle_type(self, value): def _handle_type(self, value: int) -> None:
self.type = try_enum(MessageType, value) self.type = try_enum(MessageType, value)
def _handle_content(self, value): def _handle_content(self, value: str) -> None:
self.content = value self.content = value
def _handle_attachments(self, value): def _handle_attachments(self, value: List[AttachmentPayload]) -> None:
self.attachments = [Attachment(data=a, state=self._state) for a in value] self.attachments = [Attachment(data=a, state=self._state) for a in value]
def _handle_embeds(self, value): def _handle_embeds(self, value: List[EmbedPayload]) -> None:
self.embeds = [Embed.from_dict(data) for data in value] self.embeds = [Embed.from_dict(data) for data in value]
def _handle_nonce(self, value): def _handle_nonce(self, value: Union[str, int]) -> None:
self.nonce = value self.nonce = value
def _handle_author(self, author): def _handle_author(self, author: UserPayload) -> None:
self.author = self._state.store_user(author) self.author = self._state.store_user(author)
if isinstance(self.guild, Guild): if isinstance(self.guild, Guild):
found = self.guild.get_member(self.author.id) found = self.guild.get_member(self.author.id)
if found is not None: if found is not None:
self.author = found self.author = found
def _handle_member(self, member): def _handle_member(self, member: MemberPayload) -> None:
# The gateway now gives us full Member objects sometimes with the following keys # The gateway now gives us full Member objects sometimes with the following keys
# deaf, mute, joined_at, roles # deaf, mute, joined_at, roles
# For the sake of performance I'm going to assume that the only # For the sake of performance I'm going to assume that the only
@ -732,13 +759,13 @@ class Message(Hashable):
author = self.author author = self.author
try: try:
# Update member reference # Update member reference
author._update_from_message(member) author._update_from_message(member) # type: ignore
except AttributeError: except AttributeError:
# It's a user here # It's a user here
# TODO: consider adding to cache here # TODO: consider adding to cache here
self.author = Member._from_message(message=self, data=member) self.author = Member._from_message(message=self, data=member)
def _handle_mentions(self, mentions): def _handle_mentions(self, mentions: List[UserPayload]) -> None:
self.mentions = r = [] self.mentions = r = []
guild = self.guild guild = self.guild
state = self._state state = self._state
@ -754,7 +781,7 @@ class Message(Hashable):
else: else:
r.append(Member._try_upgrade(data=mention, guild=guild, state=state)) r.append(Member._try_upgrade(data=mention, guild=guild, state=state))
def _handle_mention_roles(self, role_mentions): def _handle_mention_roles(self, role_mentions: List[int]) -> None:
self.role_mentions = [] self.role_mentions = []
if isinstance(self.guild, Guild): if isinstance(self.guild, Guild):
for role_id in map(int, role_mentions): for role_id in map(int, role_mentions):
@ -762,21 +789,21 @@ class Message(Hashable):
if role is not None: if role is not None:
self.role_mentions.append(role) self.role_mentions.append(role)
def _rebind_channel_reference(self, new_channel): def _rebind_channel_reference(self, new_channel: Union[TextChannel, DMChannel, GroupChannel]) -> None:
self.channel = new_channel self.channel = new_channel
try: try:
del self._cs_guild del self._cs_guild # type: ignore
except AttributeError: except AttributeError:
pass pass
@utils.cached_slot_property('_cs_guild') @utils.cached_slot_property('_cs_guild')
def guild(self): def guild(self) -> Optional[Guild]:
"""Optional[:class:`Guild`]: The guild that the message belongs to, if applicable.""" """Optional[:class:`Guild`]: The guild that the message belongs to, if applicable."""
return getattr(self.channel, 'guild', None) return getattr(self.channel, 'guild', None)
@utils.cached_slot_property('_cs_raw_mentions') @utils.cached_slot_property('_cs_raw_mentions')
def raw_mentions(self): def raw_mentions(self) -> List[int]:
"""List[:class:`int`]: A property that returns an array of user IDs matched with """List[:class:`int`]: A property that returns an array of user IDs matched with
the syntax of ``<@user_id>`` in the message content. the syntax of ``<@user_id>`` in the message content.
@ -786,28 +813,28 @@ class Message(Hashable):
return [int(x) for x in re.findall(r'<@!?([0-9]{15,20})>', self.content)] return [int(x) for x in re.findall(r'<@!?([0-9]{15,20})>', self.content)]
@utils.cached_slot_property('_cs_raw_channel_mentions') @utils.cached_slot_property('_cs_raw_channel_mentions')
def raw_channel_mentions(self): def raw_channel_mentions(self) -> List[int]:
"""List[:class:`int`]: A property that returns an array of channel IDs matched with """List[:class:`int`]: A property that returns an array of channel IDs matched with
the syntax of ``<#channel_id>`` in the message content. the syntax of ``<#channel_id>`` in the message content.
""" """
return [int(x) for x in re.findall(r'<#([0-9]{15,20})>', self.content)] return [int(x) for x in re.findall(r'<#([0-9]{15,20})>', self.content)]
@utils.cached_slot_property('_cs_raw_role_mentions') @utils.cached_slot_property('_cs_raw_role_mentions')
def raw_role_mentions(self): def raw_role_mentions(self) -> List[int]:
"""List[:class:`int`]: A property that returns an array of role IDs matched with """List[:class:`int`]: A property that returns an array of role IDs matched with
the syntax of ``<@&role_id>`` in the message content. the syntax of ``<@&role_id>`` in the message content.
""" """
return [int(x) for x in re.findall(r'<@&([0-9]{15,20})>', self.content)] return [int(x) for x in re.findall(r'<@&([0-9]{15,20})>', self.content)]
@utils.cached_slot_property('_cs_channel_mentions') @utils.cached_slot_property('_cs_channel_mentions')
def channel_mentions(self): def channel_mentions(self) -> List[GuildChannel]:
if self.guild is None: if self.guild is None:
return [] return []
it = filter(None, map(self.guild.get_channel, self.raw_channel_mentions)) it = filter(None, map(self.guild.get_channel, self.raw_channel_mentions))
return utils._unique(it) return utils._unique(it)
@utils.cached_slot_property('_cs_clean_content') @utils.cached_slot_property('_cs_clean_content')
def clean_content(self): def clean_content(self) -> str:
""":class:`str`: A property that returns the content in a "cleaned up" """:class:`str`: A property that returns the content in a "cleaned up"
manner. This basically means that mentions are transformed manner. This basically means that mentions are transformed
into the way the client shows it. e.g. ``<#id>`` will transform into the way the client shows it. e.g. ``<#id>`` will transform
@ -857,22 +884,22 @@ class Message(Hashable):
return escape_mentions(result) return escape_mentions(result)
@property @property
def created_at(self): def created_at(self) -> datetime.datetime:
""":class:`datetime.datetime`: The message's creation time in UTC.""" """:class:`datetime.datetime`: The message's creation time in UTC."""
return utils.snowflake_time(self.id) return utils.snowflake_time(self.id)
@property @property
def edited_at(self): def edited_at(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: An aware UTC datetime object containing the edited time of the message.""" """Optional[:class:`datetime.datetime`]: An aware UTC datetime object containing the edited time of the message."""
return self._edited_timestamp return self._edited_timestamp
@property @property
def jump_url(self): def jump_url(self) -> str:
""":class:`str`: Returns a URL that allows the client to jump to this message.""" """:class:`str`: Returns a URL that allows the client to jump to this message."""
guild_id = getattr(self.guild, 'id', '@me') guild_id = getattr(self.guild, 'id', '@me')
return f'https://discord.com/channels/{guild_id}/{self.channel.id}/{self.id}' return f'https://discord.com/channels/{guild_id}/{self.channel.id}/{self.id}'
def is_system(self): def is_system(self) -> bool:
""":class:`bool`: Whether the message is a system message. """:class:`bool`: Whether the message is a system message.
.. versionadded:: 1.3 .. versionadded:: 1.3
@ -943,7 +970,7 @@ class Message(Hashable):
return f'{self.author.name} has added {self.content} to this channel' return f'{self.author.name} has added {self.content} to this channel'
if self.type is MessageType.guild_stream: if self.type is MessageType.guild_stream:
return f'{self.author.name} is live! Now streaming {self.author.activity.name}' return f'{self.author.name} is live! Now streaming {self.author.activity.name}' # type: ignore
if self.type is MessageType.guild_discovery_disqualified: if self.type is MessageType.guild_discovery_disqualified:
return 'This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details.' return 'This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details.'
@ -960,7 +987,7 @@ class Message(Hashable):
if self.type is MessageType.guild_invite_reminder: if self.type is MessageType.guild_invite_reminder:
return 'Wondering who to invite?\nStart by inviting anyone who can help you build the server!' return 'Wondering who to invite?\nStart by inviting anyone who can help you build the server!'
async def delete(self, *, delay=None): async def delete(self, *, delay: Optional[float] = None) -> None:
"""|coro| """|coro|
Deletes the message. Deletes the message.
@ -988,18 +1015,18 @@ class Message(Hashable):
Deleting the message failed. Deleting the message failed.
""" """
if delay is not None: if delay is not None:
async def delete(): async def delete(delay: float):
await asyncio.sleep(delay) await asyncio.sleep(delay)
try: try:
await self._state.http.delete_message(self.channel.id, self.id) await self._state.http.delete_message(self.channel.id, self.id)
except HTTPException: except HTTPException:
pass pass
asyncio.create_task(delete()) asyncio.create_task(delete(delay))
else: else:
await self._state.http.delete_message(self.channel.id, self.id) await self._state.http.delete_message(self.channel.id, self.id)
async def edit(self, **fields): async def edit(self, **fields: Any) -> None:
"""|coro| """|coro|
Edits the message. Edits the message.
@ -1102,7 +1129,7 @@ class Message(Hashable):
if delete_after is not None: if delete_after is not None:
await self.delete(delay=delete_after) await self.delete(delay=delete_after)
async def publish(self): async def publish(self) -> None:
"""|coro| """|coro|
Publishes this message to your announcement channel. Publishes this message to your announcement channel.
@ -1120,7 +1147,7 @@ class Message(Hashable):
await self._state.http.publish_message(self.channel.id, self.id) await self._state.http.publish_message(self.channel.id, self.id)
async def pin(self, *, reason=None): async def pin(self, *, reason: Optional[str] = None) -> None:
"""|coro| """|coro|
Pins the message. Pins the message.
@ -1149,7 +1176,7 @@ class Message(Hashable):
await self._state.http.pin_message(self.channel.id, self.id, reason=reason) await self._state.http.pin_message(self.channel.id, self.id, reason=reason)
self.pinned = True self.pinned = True
async def unpin(self, *, reason=None): async def unpin(self, *, reason: Optional[str] = None) -> None:
"""|coro| """|coro|
Unpins the message. Unpins the message.
@ -1177,7 +1204,7 @@ class Message(Hashable):
await self._state.http.unpin_message(self.channel.id, self.id, reason=reason) await self._state.http.unpin_message(self.channel.id, self.id, reason=reason)
self.pinned = False self.pinned = False
async def add_reaction(self, emoji): async def add_reaction(self, emoji: EmojiInputType) -> None:
"""|coro| """|coro|
Add a reaction to the message. Add a reaction to the message.
@ -1208,7 +1235,7 @@ class Message(Hashable):
emoji = convert_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: Union[EmojiInputType, Reaction], member: Snowflake) -> None:
"""|coro| """|coro|
Remove a reaction by the member from the message. Remove a reaction by the member from the message.
@ -1247,7 +1274,7 @@ class Message(Hashable):
else: else:
await self._state.http.remove_reaction(self.channel.id, self.id, emoji, member.id) await self._state.http.remove_reaction(self.channel.id, self.id, emoji, member.id)
async def clear_reaction(self, emoji): async def clear_reaction(self, emoji: Union[EmojiInputType, Reaction]) -> None:
"""|coro| """|coro|
Clears a specific reaction from the message. Clears a specific reaction from the message.
@ -1278,7 +1305,7 @@ class Message(Hashable):
emoji = convert_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)
async def clear_reactions(self): async def clear_reactions(self) -> None:
"""|coro| """|coro|
Removes all the reactions from the message. Removes all the reactions from the message.
@ -1294,7 +1321,7 @@ class Message(Hashable):
""" """
await self._state.http.clear_reactions(self.channel.id, self.id) await self._state.http.clear_reactions(self.channel.id, self.id)
async def reply(self, content=None, **kwargs): async def reply(self, content: Optional[str] = None, **kwargs) -> Message:
"""|coro| """|coro|
A shortcut method to :meth:`.abc.Messageable.send` to reply to the A shortcut method to :meth:`.abc.Messageable.send` to reply to the
@ -1320,7 +1347,7 @@ class Message(Hashable):
return await self.channel.send(content, reference=self, **kwargs) return await self.channel.send(content, reference=self, **kwargs)
def to_reference(self, *, fail_if_not_exists=True): def to_reference(self, *, fail_if_not_exists: bool = True) -> MessageReference:
"""Creates a :class:`~discord.MessageReference` from the current message. """Creates a :class:`~discord.MessageReference` from the current message.
.. versionadded:: 1.6 .. versionadded:: 1.6
@ -1341,8 +1368,8 @@ class Message(Hashable):
return MessageReference.from_message(self, fail_if_not_exists=fail_if_not_exists) return MessageReference.from_message(self, fail_if_not_exists=fail_if_not_exists)
def to_message_reference_dict(self): def to_message_reference_dict(self) -> MessageReferencePayload:
data = { data: MessageReferencePayload = {
'message_id': self.id, 'message_id': self.id,
'channel_id': self.channel.id, 'channel_id': self.channel.id,
} }
@ -1411,7 +1438,7 @@ class PartialMessage(Hashable):
'to_message_reference_dict', 'to_message_reference_dict',
) )
def __init__(self, *, channel, id): def __init__(self, *, channel: Union[GuildChannel, PrivateChannel], id: int):
if channel.type not in (ChannelType.text, ChannelType.news, ChannelType.private): if channel.type not in (ChannelType.text, ChannelType.news, ChannelType.private):
raise TypeError(f'Expected TextChannel or DMChannel not {type(channel)!r}') raise TypeError(f'Expected TextChannel or DMChannel not {type(channel)!r}')
@ -1419,29 +1446,29 @@ class PartialMessage(Hashable):
self._state = channel._state self._state = channel._state
self.id = id self.id = id
def _update(self, data): def _update(self, data) -> None:
# This is used for duck typing purposes. # This is used for duck typing purposes.
# Just do nothing with the data. # Just do nothing with the data.
pass pass
# Also needed for duck typing purposes # Also needed for duck typing purposes
# n.b. not exposed # n.b. not exposed
pinned = property(None, lambda x, y: ...) pinned = property(None, lambda x, y: None)
def __repr__(self): def __repr__(self) -> str:
return f'<PartialMessage id={self.id} channel={self.channel!r}>' return f'<PartialMessage id={self.id} channel={self.channel!r}>'
@property @property
def created_at(self): def created_at(self) -> datetime.datetime:
""":class:`datetime.datetime`: The partial message's creation time in UTC.""" """:class:`datetime.datetime`: The partial message's creation time in UTC."""
return utils.snowflake_time(self.id) return utils.snowflake_time(self.id)
@utils.cached_slot_property('_cs_guild') @utils.cached_slot_property('_cs_guild')
def guild(self): def guild(self) -> Optional[Guild]:
"""Optional[:class:`Guild`]: The guild that the partial message belongs to, if applicable.""" """Optional[:class:`Guild`]: The guild that the partial message belongs to, if applicable."""
return getattr(self.channel, 'guild', None) return getattr(self.channel, 'guild', None)
async def fetch(self): async def fetch(self) -> Message:
"""|coro| """|coro|
Fetches the partial message to a full :class:`Message`. Fetches the partial message to a full :class:`Message`.
@ -1464,7 +1491,7 @@ class PartialMessage(Hashable):
data = await self._state.http.get_message(self.channel.id, self.id) data = await self._state.http.get_message(self.channel.id, self.id)
return self._state.create_message(channel=self.channel, data=data) return self._state.create_message(channel=self.channel, data=data)
async def edit(self, **fields): async def edit(self, **fields: Any) -> Optional[Message]:
"""|coro| """|coro|
Edits the message. Edits the message.
@ -1558,7 +1585,7 @@ class PartialMessage(Hashable):
data = await self._state.http.edit_message(self.channel.id, self.id, **fields) data = await self._state.http.edit_message(self.channel.id, self.id, **fields)
if delete_after is not None: if delete_after is not None:
await self.delete(delay=delete_after) await self.delete(delay=delete_after) # type: ignore
if fields: if fields:
return self._state.create_message(channel=self.channel, data=data) return self._state.create_message(channel=self.channel, data=data) # type: ignore

1
discord/types/message.py

@ -51,6 +51,7 @@ class _AttachmentOptional(TypedDict, total=False):
height: Optional[int] height: Optional[int]
width: Optional[int] width: Optional[int]
content_type: str content_type: str
spoiler: bool
class Attachment(_AttachmentOptional): class Attachment(_AttachmentOptional):

Loading…
Cancel
Save