Browse Source

Remove Message.timestamp and make Message.channel_mentions lazy.

Message.timestamp is replaced with Message.created_at. This commit
also affects how MESSAGE_UPDATE is handled by only assuming that two
keys are provided rather than overwriting data with missing keys.
pull/447/head
Rapptz 9 years ago
parent
commit
aa8a328f0a
  1. 106
      discord/message.py

106
discord/message.py

@ -39,11 +39,9 @@ class Message:
Attributes Attributes
----------- -----------
edited_timestamp : Optional[datetime.datetime] edited_timestamp: Optional[datetime.datetime]
A naive UTC datetime object containing the edited time of the message. A naive UTC datetime object containing the edited time of the message.
timestamp : datetime.datetime tts: bool
A naive UTC datetime object containing the time the message was created.
tts : bool
Specifies if the message was done with text-to-speech. Specifies if the message was done with text-to-speech.
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
@ -51,12 +49,12 @@ class Message:
author author
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, then it is a :class:`User` instead. private channel, then it is a :class:`User` instead.
content : str content: str
The actual contents of the message. The actual contents of the message.
nonce nonce
The value used by the discord server and the client to verify that the message is successfully sent. The value used by the discord server and the client to verify that the message is successfully sent.
This is typically non-important. This is typically non-important.
embeds : list embeds: list
A list of embedded objects. The elements are objects that meet oEmbed's specification_. A list of embedded objects. The elements are objects that meet oEmbed's specification_.
.. _specification: http://oembed.com/ .. _specification: http://oembed.com/
@ -66,12 +64,12 @@ class Message:
In :issue:`very rare cases <21>` this could be a :class:`Object` instead. In :issue:`very rare cases <21>` this could be a :class:`Object` instead.
For the sake of convenience, this :class:`Object` instance has an attribute ``is_private`` set to ``True``. For the sake of convenience, this :class:`Object` instance has an attribute ``is_private`` set to ``True``.
server : Optional[:class:`Server`] server: Optional[:class:`Server`]
The server that the message belongs to. If not applicable (i.e. a PM) then it's None instead. The server that the message belongs to. If not applicable (i.e. a PM) then it's None instead.
call: Optional[:class:`CallMessage`] call: Optional[:class:`CallMessage`]
The call that the message refers to. This is only applicable to messages of type The call that the message refers to. This is only applicable to messages of type
:attr:`MessageType.call`. :attr:`MessageType.call`.
mention_everyone : bool mention_everyone: bool
Specifies if the message mentions everyone. Specifies if the message mentions everyone.
.. note:: .. note::
@ -91,15 +89,18 @@ class Message:
The order of the mentions list is not in any particular order so you should The order of the mentions list is not in any particular order so you should
not rely on it. This is a discord limitation, not one with the library. not rely on it. This is a discord limitation, not one with the library.
channel_mentions : list channel_mentions: list
A list of :class:`Channel` that were mentioned. If the message is in a private message A list of :class:`Channel` that were mentioned. If the message is in a private message
then the list is always empty. then the list is always empty.
role_mentions : list role_mentions: list
A list of :class:`Role` that were mentioned. If the message is in a private message A list of :class:`Role` that were mentioned. If the message is in a private message
then the list is always empty. then the list is always empty.
id : str id: str
The message ID. The message ID.
attachments : list webhook_id: Optional[str]
If this message was sent by a webhook, then this is the webhook ID's that sent this
message.
attachments: list
A list of attachments given to a message. A list of attachments given to a message.
pinned: bool pinned: bool
Specifies if the message is currently pinned. Specifies if the message is currently pinned.
@ -107,9 +108,9 @@ class Message:
Reactions to a message. Reactions can be either custom emoji or standard unicode emoji. Reactions to a message. Reactions can be either custom emoji or standard unicode emoji.
""" """
__slots__ = ( 'edited_timestamp', 'timestamp', 'tts', 'content', 'channel', __slots__ = ( 'edited_timestamp', 'tts', 'content', 'channel', 'webhook_id',
'mention_everyone', 'embeds', 'id', 'mentions', 'author', 'mention_everyone', 'embeds', 'id', 'mentions', 'author',
'channel_mentions', 'server', '_cs_raw_mentions', 'attachments', '_cs_channel_mentions', 'server', '_cs_raw_mentions', 'attachments',
'_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned', '_cs_clean_content', '_cs_raw_channel_mentions', 'nonce', 'pinned',
'role_mentions', '_cs_raw_role_mentions', 'type', 'call', 'role_mentions', '_cs_raw_role_mentions', 'type', 'call',
'_cs_system_content', '_state', 'reactions' ) '_cs_system_content', '_state', 'reactions' )
@ -121,27 +122,31 @@ class Message:
reaction.message = self reaction.message = self
self._update(channel, data) self._update(channel, data)
def _try_patch(self, data, key, transform):
try:
value = data[key]
except KeyError:
pass
else:
setattr(self, key, transform(value))
def _update(self, channel, data): def _update(self, channel, data):
# at the moment, the timestamps seem to be naive so they have no time zone and operate on UTC time.
# we can use this to our advantage to use strptime instead of a complicated parsing routine.
# example timestamp: 2015-08-21T12:03:45.782000+00:00
# sometimes the .%f modifier is missing
self.edited_timestamp = utils.parse_time(data['edited_timestamp'])
self.timestamp = utils.parse_time(data['timestamp'])
self.tts = data.get('tts', False)
self.pinned = data.get('pinned', False)
self.content = data['content']
self.mention_everyone = data['mention_everyone']
self.embeds = data['embeds']
self.id = data['id']
self.channel = channel self.channel = channel
self.author = self._state.try_insert_user(data['author']) for handler in ('mentions', 'mention_roles', 'call'):
self.nonce = data.get('nonce') try:
self.attachments = data['attachments'] getattr(self, '_handle_%s' % handler)(data[handler])
self.type = try_enum(MessageType, data.get('type')) except KeyError:
self._handle_upgrades(data['channel_id']) continue
self._handle_mentions(data.get('mentions', []), data.get('mention_roles', []))
self._handle_call(data.get('call')) self._try_patch(data, 'edited_timestamp', utils.parse_time)
self._try_patch(data, 'author', self._state.try_insert_user)
self._try_patch(data, 'pinned', bool)
self._try_patch(data, 'mention_everyone', bool)
self._try_patch(data, 'tts', bool)
self._try_patch(data, 'content', str)
self._try_patch(data, 'attachments', lambda x: x)
self._try_patch(data, 'embeds', lambda x: x)
self._try_patch(data, 'nonce', lambda x: x)
# clear the cached properties # clear the cached properties
cached = filter(lambda attr: attr.startswith('_cs_'), self.__slots__) cached = filter(lambda attr: attr.startswith('_cs_'), self.__slots__)
@ -151,24 +156,21 @@ class Message:
except AttributeError: except AttributeError:
pass pass
def _handle_mentions(self, mentions, role_mentions): def _handle_mentions(self, mentions):
self.mentions = [] self.mentions = []
self.channel_mentions = [] if self.server is None:
self.role_mentions = []
if getattr(self.channel, 'is_private', True):
self.mentions = [self._state.try_insert_user(m) for m in mentions] self.mentions = [self._state.try_insert_user(m) for m in mentions]
return return
if self.server is not None: for mention in mentions:
for mention in mentions: id_search = mention['id']
id_search = mention.get('id') member = self.server.get_member(id_search)
member = self.server.get_member(id_search) if member is not None:
if member is not None: self.mentions.append(member)
self.mentions.append(member)
it = filter(None, map(lambda m: self.server.get_channel(m), self.raw_channel_mentions))
self.channel_mentions = utils._unique(it)
def _handle_mention_roles(self, role_mentions):
self.role_mentions = []
if self.server is not None:
for role_id in role_mentions: for role_id in role_mentions:
role = utils.get(self.server.roles, id=role_id) role = utils.get(self.server.roles, id=role_id)
if role is not None: if role is not None:
@ -218,6 +220,13 @@ class Message:
""" """
return re.findall(r'<@&([0-9]+)>', self.content) return re.findall(r'<@&([0-9]+)>', self.content)
@utils.cached_slot_property('_cs_channel_mentions')
def channel_mentions(self):
if self.server is None:
return []
it = filter(None, map(lambda m: self.server.get_channel(m), self.raw_channel_mentions))
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):
"""A property that returns the content in a "cleaned up" """A property that returns the content in a "cleaned up"
@ -289,6 +298,11 @@ class Message:
if found is not None: if found is not None:
self.author = found self.author = found
@property
def created_at(self):
"""Returns the message's creation time in UTC."""
return utils.snowflake_time(self.id)
@utils.cached_slot_property('_cs_system_content') @utils.cached_slot_property('_cs_system_content')
def system_content(self): def system_content(self):
"""A property that returns the content that is rendered """A property that returns the content that is rendered

Loading…
Cancel
Save