Add support for different message types and call message.
This commit is contained in:
		@@ -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:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user