Add support for different message types and call message.
This commit is contained in:
parent
69c506d7ae
commit
a128249b63
@ -25,6 +25,7 @@ from .server import Server
|
||||
from .member import Member
|
||||
from .message import Message
|
||||
from .errors import *
|
||||
from .calls import CallMessage
|
||||
from .permissions import Permissions, PermissionOverwrite
|
||||
from .role import Role
|
||||
from .colour import Color, Colour
|
||||
@ -32,7 +33,7 @@ from .invite import Invite
|
||||
from .object import Object
|
||||
from . import utils, opus, compat
|
||||
from .voice_client import VoiceClient
|
||||
from .enums import ChannelType, ServerRegion, Status
|
||||
from .enums import ChannelType, ServerRegion, Status, MessageType
|
||||
from collections import namedtuple
|
||||
|
||||
import logging
|
||||
|
48
discord/calls.py
Normal file
48
discord/calls.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2016 Rapptz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
from . import utils
|
||||
|
||||
class CallMessage:
|
||||
"""Represents a group call from Discord.
|
||||
|
||||
This is only received in cases where the message type is equivalent to
|
||||
:attr:`MessageType.call`.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
ended_timestamp: Optional[datetime.datetime]
|
||||
A naive UTC datetime object that represents the time that the call has ended.
|
||||
participants: List[:class:`User`]
|
||||
The list of users that are participating in this call.
|
||||
channel: :class:`PrivateChannel`
|
||||
The private channel associated with this call.
|
||||
"""
|
||||
|
||||
def __init__(self, channel, **kwargs):
|
||||
self.channel = channel
|
||||
self.ended_timestamp = utils.parse_time(kwargs.get('ended_timestamp'))
|
||||
self.participants = kwargs.get('participants')
|
@ -301,6 +301,8 @@ class PrivateChannel(Hashable):
|
||||
----------
|
||||
recipients: list of :class:`User`
|
||||
The users you are participating with in the private channel.
|
||||
me: :class:`User`
|
||||
The user presenting yourself.
|
||||
id: str
|
||||
The private channel ID.
|
||||
is_private: bool
|
||||
@ -318,11 +320,12 @@ class PrivateChannel(Hashable):
|
||||
:attr:`ChannelType.group` then this is always ``None``.
|
||||
"""
|
||||
|
||||
__slots__ = ['id', 'is_private', 'recipients', 'type', 'owner', 'icon', 'name']
|
||||
__slots__ = ['id', 'is_private', 'recipients', 'type', 'owner', 'icon', 'name', 'me']
|
||||
|
||||
def __init__(self, me, **kwargs):
|
||||
self.recipients = [User(**u) for u in kwargs['recipients']]
|
||||
self.id = kwargs['id']
|
||||
self.me = me
|
||||
self.is_private = True
|
||||
self.type = ChannelType(kwargs['type'])
|
||||
self._update_group(**kwargs)
|
||||
|
@ -35,6 +35,14 @@ class ChannelType(Enum):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class MessageType(Enum):
|
||||
default = 0
|
||||
recipient_add = 1
|
||||
recipient_remove = 2
|
||||
call = 3
|
||||
channel_name_change = 4
|
||||
channel_icon_change = 5
|
||||
|
||||
class ServerRegion(Enum):
|
||||
us_west = 'us-west'
|
||||
us_east = 'us-east'
|
||||
|
@ -27,7 +27,9 @@ DEALINGS IN THE SOFTWARE.
|
||||
from . import utils
|
||||
from .user import User
|
||||
from .object import Object
|
||||
from .calls import CallMessage
|
||||
import re
|
||||
from .enums import MessageType, try_enum
|
||||
|
||||
class Message:
|
||||
"""Represents a message from Discord.
|
||||
@ -42,6 +44,9 @@ class Message:
|
||||
A naive UTC datetime object containing the time the message was created.
|
||||
tts : bool
|
||||
Specifies if the message was done with text-to-speech.
|
||||
type: :class:`MessageType`
|
||||
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`.
|
||||
author
|
||||
A :class:`Member` that sent the message. If :attr:`channel` is a
|
||||
private channel, then it is a :class:`User` instead.
|
||||
@ -62,6 +67,9 @@ class Message:
|
||||
For the sake of convenience, this :class:`Object` instance has an attribute ``is_private`` set to ``True``.
|
||||
server : Optional[:class:`Server`]
|
||||
The server that the message belongs to. If not applicable (i.e. a PM) then it's None instead.
|
||||
call: Optional[:class:`CallMessage`]
|
||||
The call that the message refers to. This is only applicable to messages of type
|
||||
:attr:`MessageType.call`.
|
||||
mention_everyone : bool
|
||||
Specifies if the message mentions everyone.
|
||||
|
||||
@ -71,9 +79,11 @@ class Message:
|
||||
Rather this boolean indicates if the ``@everyone`` text is in the message
|
||||
**and** it did end up mentioning everyone.
|
||||
|
||||
mentions : list
|
||||
mentions: list
|
||||
A list of :class:`Member` that were mentioned. If the message is in a private message
|
||||
then the list is always empty.
|
||||
then the list will be of :class:`User` instead. For messages that are not of type
|
||||
:attr:`MessageType.default`\, this array can be used to aid in system messages.
|
||||
For more information, see :attr:`system_content`.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -98,7 +108,8 @@ class Message:
|
||||
'mention_everyone', 'embeds', 'id', 'mentions', 'author',
|
||||
'channel_mentions', 'server', '_raw_mentions', 'attachments',
|
||||
'_clean_content', '_raw_channel_mentions', 'nonce', 'pinned',
|
||||
'role_mentions', '_raw_role_mentions' ]
|
||||
'role_mentions', '_raw_role_mentions', 'type', 'call',
|
||||
'_system_content' ]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._update(**kwargs)
|
||||
@ -120,8 +131,10 @@ class Message:
|
||||
self.author = User(**data.get('author', {}))
|
||||
self.nonce = data.get('nonce')
|
||||
self.attachments = data.get('attachments')
|
||||
self.type = try_enum(MessageType, data.get('type'))
|
||||
self._handle_upgrades(data.get('channel_id'))
|
||||
self._handle_mentions(data.get('mentions', []), data.get('mention_roles', []))
|
||||
self._handle_call(data.get('call'))
|
||||
|
||||
# clear the cached properties
|
||||
cached = filter(lambda attr: attr[0] == '_', self.__slots__)
|
||||
@ -136,16 +149,16 @@ class Message:
|
||||
self.channel_mentions = []
|
||||
self.role_mentions = []
|
||||
if getattr(self.channel, 'is_private', True):
|
||||
self.mentions = [User(**m) for m in mentions]
|
||||
return
|
||||
|
||||
if self.channel is not None:
|
||||
if self.server is not None:
|
||||
for mention in mentions:
|
||||
id_search = mention.get('id')
|
||||
member = self.server.get_member(id_search)
|
||||
if member is not None:
|
||||
self.mentions.append(member)
|
||||
|
||||
if self.server is not None:
|
||||
it = filter(None, map(lambda m: self.server.get_channel(m), self.raw_channel_mentions))
|
||||
self.channel_mentions = utils._unique(it)
|
||||
|
||||
@ -154,6 +167,26 @@ class Message:
|
||||
if role is not None:
|
||||
self.role_mentions.append(role)
|
||||
|
||||
def _handle_call(self, call):
|
||||
if call is None or self.type is not MessageType.call:
|
||||
self.call = None
|
||||
return
|
||||
|
||||
# we get the participant source from the mentions array or
|
||||
# the author
|
||||
|
||||
participants = []
|
||||
for uid in call.get('participants', []):
|
||||
if uid == self.author.id:
|
||||
participants.append(self.author)
|
||||
else:
|
||||
user = utils.find(lambda u: u.id == uid, self.mentions)
|
||||
if user is not None:
|
||||
participants.append(user)
|
||||
|
||||
call['participants'] = participants
|
||||
self.call = CallMessage(channel=self.channel, **call)
|
||||
|
||||
@utils.cached_slot_property('_raw_mentions')
|
||||
def raw_mentions(self):
|
||||
"""A property that returns an array of user IDs matched with
|
||||
@ -248,3 +281,41 @@ class Message:
|
||||
found = self.server.get_member(self.author.id)
|
||||
if found is not None:
|
||||
self.author = found
|
||||
|
||||
@utils.cached_slot_property('_system_content')
|
||||
def system_content(self):
|
||||
"""A property that returns the content that is rendered
|
||||
regardless of the :attr:`Message.type`.
|
||||
|
||||
In the case of :attr:`MessageType.default`\, this just returns the
|
||||
regular :attr:`Message.content`. Otherwise this returns an English
|
||||
message denoting the contents of the system message.
|
||||
"""
|
||||
|
||||
if self.type is MessageType.default:
|
||||
return self.content
|
||||
|
||||
if self.type is MessageType.recipient_add:
|
||||
return '{0.name} added {1.name} to the group.'.format(self.author, self.mentions[0])
|
||||
|
||||
if self.type is MessageType.recipient_remove:
|
||||
return '{0.name} removed {1.name} from the group.'.format(self.author, self.mentions[0])
|
||||
|
||||
if self.type is MessageType.channel_name_change:
|
||||
return '{0.author.name} changed the channel name: {0.content}'.format(self)
|
||||
|
||||
if self.type is MessageType.channel_icon_change:
|
||||
return '{0.author.name} changed the channel icon.'.format(self)
|
||||
|
||||
# we're at the call message type now, which is a bit more complicated.
|
||||
# we can make the assumption that Message.channel is a PrivateChannel
|
||||
# with the type ChannelType.group or ChannelType.private
|
||||
call_ended = self.call.ended_timestamp is not None
|
||||
|
||||
if call_ended:
|
||||
if self.channel.me in self.call.participants:
|
||||
return '{0.author.name} started a call.'.format(self)
|
||||
else:
|
||||
return 'You missed a call from {0.author.name}'.format(self)
|
||||
else:
|
||||
return '{0.author.name} started a call \N{EM DASH} Join the call.'.format(self)
|
||||
|
@ -238,7 +238,10 @@ class ConnectionState:
|
||||
message = self._get_message(data.get('id'))
|
||||
if message is not None:
|
||||
older_message = copy.copy(message)
|
||||
if 'content' not in data:
|
||||
if 'call' in data:
|
||||
# call state message edit
|
||||
message._handle_call(data['call'])
|
||||
elif 'content' not in data:
|
||||
# embed only edit
|
||||
message.embeds = data['embeds']
|
||||
else:
|
||||
|
33
docs/api.rst
33
docs/api.rst
@ -398,6 +398,33 @@ All enumerations are subclasses of `enum`_.
|
||||
|
||||
A private group text channel.
|
||||
|
||||
.. class:: MessageType
|
||||
|
||||
Specifies the type of :class:`Message`. This is used to denote if a message
|
||||
is to be interpreted as a system message or a regular message.
|
||||
|
||||
.. attribute:: default
|
||||
|
||||
The default message type. This is the same as regular messages.
|
||||
.. attribute:: recipient_add
|
||||
|
||||
The system message when a recipient is added to a group private
|
||||
message, i.e. a private channel of type :attr:`ChannelType.group`.
|
||||
.. attribute:: recipient_remove
|
||||
|
||||
The system message when a recipient is removed from a group private
|
||||
message, i.e. a private channel of type :attr:`ChannelType.group`.
|
||||
.. attribute:: call
|
||||
|
||||
The system message denoting call state, e.g. missed call, started call,
|
||||
etc.
|
||||
.. attribute:: channel_name_change
|
||||
|
||||
The system message denoting that a channel's name has been changed.
|
||||
.. attribute:: channel_icon_change
|
||||
|
||||
The system message denoting that a channel's icon has been changed.
|
||||
|
||||
.. class:: ServerRegion
|
||||
|
||||
Specifies the region a :class:`Server`'s voice server belongs to.
|
||||
@ -488,6 +515,12 @@ Message
|
||||
.. autoclass:: Message
|
||||
:members:
|
||||
|
||||
CallMessage
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: CallMessage
|
||||
:members:
|
||||
|
||||
Server
|
||||
~~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user