Beispiel #1
0
class Message(Model):
    """
    Represents a Message created within a Channel on Discord.

    Attributes
    ----------
    id : snowflake
        The ID of this message.
    channel_id : snowflake
        The channel ID this message was sent in.
    type : ``MessageType``
        Type of the message.
    author : :class:`disco.types.user.User`
        The author of this message.
    content : str
        The unicode contents of this message.
    nonce : str
        The nonce of this message.
    timestamp : datetime
        When this message was created.
    edited_timestamp : Optional[datetime]
        When this message was last edited.
    tts : bool
        Whether this is a TTS (text-to-speech) message.
    mention_everyone : bool
        Whether this message has an @everyone which mentions everyone.
    pinned : bool
        Whether this message is pinned in the channel.
    mentions : dict(snowflake, :class:`disco.types.user.User`)
        All users mentioned within this message.
    mention_roles : list(snowflake)
        All roles mentioned within this message.
    embeds : list(:class:`MessageEmbed`)
        All embeds for this message.
    attachments : list(:class:`MessageAttachment`)
        All attachments for this message.
    """
    id = Field(snowflake)
    channel_id = Field(snowflake)
    type = Field(enum(MessageType))
    author = Field(User)
    content = Field(text)
    nonce = Field(snowflake)
    timestamp = Field(datetime)
    edited_timestamp = Field(datetime)
    tts = Field(bool)
    mention_everyone = Field(bool)
    pinned = Field(bool)
    mentions = Field(dictof(User, key='id'))
    mention_roles = Field(listof(snowflake))
    embeds = Field(listof(MessageEmbed))
    attachments = Field(dictof(MessageAttachment, key='id'))

    def __str__(self):
        return '<Message {} ({})>'.format(self.id, self.channel_id)

    @cached_property
    def guild(self):
        """
        Returns
        -------
        :class:`disco.types.guild.Guild`
            The guild (if applicable) this message was created in.
        """
        return self.channel.guild

    @cached_property
    def member(self):
        """
        Returns
        -------
        :class:`disco.types.guild.GuildMember`
            The guild member (if applicable) that sent this message.
        """
        return self.channel.guild.get_member(self.author)

    @cached_property
    def channel(self):
        """
        Returns
        -------
        :class:`disco.types.channel.Channel`
            The channel this message was created in.
        """
        return self.client.state.channels.get(self.channel_id)

    def reply(self, *args, **kwargs):
        """
        Reply to this message (proxys arguments to
        :func:`disco.types.channel.Channel.send_message`)

        Returns
        -------
        :class:`Message`
            The created message object.
        """
        return self.channel.send_message(*args, **kwargs)

    def edit(self, content):
        """
        Edit this message

        Args
        ----
        content : str
            The new edited contents of the message.

        Returns
        -------
        :class:`Message`
            The edited message object.
        """
        return self.client.api.channels_messages_modify(self.channel_id, self.id, content)

    def delete(self):
        """
        Delete this message.

        Returns
        -------
        :class:`Message`
            The deleted message object.
        """
        return self.client.api.channels_messages_delete(self.channel_id, self.id)

    def is_mentioned(self, entity):
        """
        Returns
        -------
        bool
            Whether the give entity was mentioned.
        """
        id = to_snowflake(entity)
        return id in self.mentions or id in self.mention_roles

    @cached_property
    def without_mentions(self):
        """
        Returns
        -------
        str
            the message contents with all valid mentions removed.
        """
        return self.replace_mentions(
            lambda u: '',
            lambda r: '')

    def replace_mentions(self, user_replace, role_replace):
        """
        Replaces user and role mentions with the result of a given lambda/function.

        Args
        ----
        user_replace : function
            A function taking a single argument, the user object mentioned, and
            returning a valid string.
        role_replace : function
            A function taking a single argument, the role ID mentioned, and
            returning a valid string.

        Returns
        -------
        str
            The message contents with all valid mentions replaced.
        """
        if not self.mentions and not self.mention_roles:
            return

        def replace(match):
            id = match.group(0)
            if id in self.mention_roles:
                return role_replace(id)
            else:
                return user_replace(self.mentions.get(id))

        return re.sub('<@!?([0-9]+)>', replace, self.content)
Beispiel #2
0
class Channel(Model, Permissible):
    """
    Represents a Discord Channel

    Attributes
    ----------
    id : snowflake
        The channel ID.
    guild_id : Optional[snowflake]
        The guild id this channel is part of.
    name : str
        The channels name.
    topic : str
        The channels topic.
    position : int
        The channels position.
    bitrate : int
        The channels bitrate.
    recipients: list(:class:`disco.types.user.User`)
        Members of this channel (if this is a DM channel).
    type : :const:`ChannelType`
        The type of this channel.
    overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`)
        Channel permissions overwrites.
    """
    id = Field(snowflake)
    guild_id = Field(snowflake)
    name = Field(text)
    topic = Field(text)
    last_message_id = Field(snowflake)
    position = Field(int)
    bitrate = Field(int)
    recipients = Field(listof(User))
    type = Field(enum(ChannelType))
    overwrites = Field(dictof(PermissionOverwrite, key='id'),
                       alias='permission_overwrites')

    def __init__(self, *args, **kwargs):
        super(Channel, self).__init__(*args, **kwargs)

        self.attach(six.itervalues(self.overwrites), {
            'channel_id': self.id,
            'channel': self
        })

    def get_permissions(self, user):
        """
        Get the permissions a user has in the channel

        Returns
        -------
        :class:`disco.types.permissions.PermissionValue`
            Computed permission value for the user.
        """
        if not self.guild_id:
            return Permissions.ADMINISTRATOR

        member = self.guild.members.get(user.id)
        base = self.guild.get_permissions(user)

        for ow in six.itervalues(self.overwrites):
            if ow.id != user.id and ow.id not in member.roles:
                continue

            base -= ow.deny
            base += ow.allow

        return base

    @property
    def is_guild(self):
        """
        Whether this channel belongs to a guild
        """
        return self.type in (ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE)

    @property
    def is_dm(self):
        """
        Whether this channel is a DM (does not belong to a guild)
        """
        return self.type in (ChannelType.DM, ChannelType.GROUP_DM)

    @property
    def is_voice(self):
        """
        Whether this channel supports voice
        """
        return self.type in (ChannelType.GUILD_VOICE, ChannelType.GROUP_DM)

    @property
    def messages(self):
        """
        a default :class:`MessageIterator` for the channel
        """
        return self.messages_iter()

    def messages_iter(self, **kwargs):
        """
        Creates a new :class:`MessageIterator` for the channel with the given
        keyword arguments
        """
        return MessageIterator(self.client, self.id, **kwargs)

    @cached_property
    def guild(self):
        """
        Guild this channel belongs to (if relevant)
        """
        return self.client.state.guilds.get(self.guild_id)

    def get_invites(self):
        """
        Returns
        -------
        list(:class:`disco.types.invite.Invite`)
            All invites for this channel.
        """
        return self.client.api.channels_invites_list(self.id)

    def get_pins(self):
        """
        Returns
        -------
        list(:class:`disco.types.message.Message`)
            All pinned messages for this channel.
        """
        return self.client.api.channels_pins_list(self.id)

    def send_message(self, content, nonce=None, tts=False):
        """
        Send a message in this channel

        Parameters
        ----------
        content : str
            The message contents to send.
        nonce : Optional[snowflake]
            The nonce to attach to the message.
        tts : Optional[bool]
            Whether this is a TTS message.

        Returns
        -------
        :class:`disco.types.message.Message`
            The created message.
        """
        return self.client.api.channels_messages_create(
            self.id, content, nonce, tts)

    def connect(self, *args, **kwargs):
        """
        Connect to this channel over voice
        """
        assert self.is_voice, 'Channel must support voice to connect'
        vc = VoiceClient(self)
        vc.connect(*args, **kwargs)
        return vc

    def create_overwrite(self, entity, allow=0, deny=0):
        from disco.types.guild import Role

        type = PermissionOverwriteType.ROLE if isinstance(
            entity, Role) else PermissionOverwriteType.MEMBER
        ow = PermissionOverwrite(id=entity.id,
                                 type=type,
                                 allow=allow,
                                 deny=deny)

        ow.channel_id = self.id
        ow.channel = self

        return self.update_overwrite(ow)

    def update_overwrite(self, ow):
        self.client.api.channels_permissions_modify(
            self.id, ow.id, ow.allow.value if ow.allow else 0,
            ow.deny.value if ow.deny else 0, ow.type.name)
        return ow

    def delete_overwrite(self, ow):
        self.client.api.channels_permissions_delete(self.id, ow.id)

    def delete_message(self, message):
        """
        Deletes a single message from this channel.

        Args
        ----
        message : snowflake|:class:`disco.types.message.Message`
            The message to delete.
        """
        self.client.api.channels_messages_delete(self.id,
                                                 to_snowflake(message))

    @one_or_many
    def delete_messages(self, messages):
        """
        Deletes a set of messages using the correct API route based on the number
        of messages passed.

        Args
        ----
        messages : list[snowflake|:class:`disco.types.message.Message`]
            List of messages (or message ids) to delete. All messages must originate
            from this channel.
        """
        messages = map(to_snowflake, messages)

        if not messages:
            return

        if len(messages) <= 2:
            for msg in messages:
                self.delete_message(msg)
        else:
            for chunk in chunks(messages, 100):
                self.client.api.channels_messages_delete_bulk(self.id, chunk)
Beispiel #3
0
class Guild(SlottedModel, Permissible):
    """
    A guild object.

    Attributes
    ----------
    id : snowflake
        The id of this guild.
    owner_id : snowflake
        The id of the owner.
    afk_channel_id : snowflake
        The id of the afk channel.
    embed_channel_id : snowflake
        The id of the embed channel.
    name : str
        Guild's name.
    icon : str
        Guild's icon (as PNG binary data).
    splash : str
        Guild's splash image (as PNG binary data).
    region : str
        Voice region.
    afk_timeout : int
        Delay after which users are automatically moved to the afk channel.
    embed_enabled : bool
        Whether the guild's embed is enabled.
    verification_level : int
        The verification level used by the guild.
    mfa_level : int
        The MFA level used by the guild.
    features : list(str)
        Extra features enabled for this guild.
    members : dict(snowflake, :class:`GuildMember`)
        All of the guild's members.
    channels : dict(snowflake, :class:`disco.types.channel.Channel`)
        All of the guild's channels.
    roles : dict(snowflake, :class:`Role`)
        All of the guild's roles.
    emojis : dict(snowflake, :class:`GuildEmoji`)
        All of the guild's emojis.
    voice_states : dict(str, :class:`disco.types.voice.VoiceState`)
        All of the guild's voice states.
    """
    id = Field(snowflake)
    owner_id = Field(snowflake)
    afk_channel_id = Field(snowflake)
    embed_channel_id = Field(snowflake)
    name = Field(text)
    icon = Field(binary)
    splash = Field(binary)
    region = Field(str)
    afk_timeout = Field(int)
    embed_enabled = Field(bool)
    verification_level = Field(enum(VerificationLevel))
    mfa_level = Field(int)
    features = Field(listof(str))
    members = Field(dictof(GuildMember, key='id'))
    channels = Field(dictof(Channel, key='id'))
    roles = Field(dictof(Role, key='id'))
    emojis = Field(dictof(GuildEmoji, key='id'))
    voice_states = Field(dictof(VoiceState, key='session_id'))
    member_count = Field(int)
    presences = Field(listof(Presence))

    synced = Field(bool, default=False)

    def __init__(self, *args, **kwargs):
        super(Guild, self).__init__(*args, **kwargs)

        self.attach(six.itervalues(self.channels), {'guild_id': self.id})
        self.attach(six.itervalues(self.members), {'guild_id': self.id})
        self.attach(six.itervalues(self.roles), {'guild_id': self.id})
        self.attach(six.itervalues(self.emojis), {'guild_id': self.id})
        self.attach(six.itervalues(self.voice_states), {'guild_id': self.id})

    def get_permissions(self, user):
        """
        Get the permissions a user has in this guild.

        Returns
        -------
        :class:`disco.types.permissions.PermissionValue`
            Computed permission value for the user.
        """
        if self.owner_id == user.id:
            return PermissionValue(Permissions.ADMINISTRATOR)

        member = self.get_member(user)
        value = PermissionValue(self.roles.get(self.id).permissions)

        for role in map(self.roles.get, member.roles):
            value += role.permissions

        return value

    def get_voice_state(self, user):
        """
        Attempt to get a voice state for a given user (who should be a member of
        this guild).

        Returns
        -------
        :class:`disco.types.voice.VoiceState`
            The voice state for the user in this guild.
        """
        user = to_snowflake(user)

        for state in six.itervalues(self.voice_states):
            if state.user_id == user:
                return state

    def get_member(self, user):
        """
        Attempt to get a member from a given user.

        Returns
        -------
        :class:`GuildMember`
            The guild member object for the given user.
        """
        user = to_snowflake(user)

        if user not in self.members:
            try:
                self.members[user] = self.client.api.guilds_members_get(self.id, user)
            except APIException:
                return

        return self.members.get(user)

    def create_role(self):
        """
        Create a new role.

        Returns
        -------
        :class:`Role`
            The newly created role.
        """
        return self.client.api.guilds_roles_create(self.id)

    def delete_role(self, role):
        """
        Delete a role.
        """
        self.client.api.guilds_roles_delete(self.id, to_snowflake(role))

    def update_role(self, role):
        return self.client.api.guilds_roles_modify(self.id, role.id, **{
            'name': role.name,
            'permissions': role.permissions.value,
            'position': role.position,
            'color': role.color,
            'hoist': role.hoist,
            'mentionable': role.mentionable,
        })

    def sync(self):
        if self.synced:
            return

        self.synced = True
        self.client.gw.send(OPCode.REQUEST_GUILD_MEMBERS, {
            'guild_id': self.id,
            'query': '',
            'limit': 0,
        })

    def get_bans(self):
        return self.client.api.guilds_bans_list(self.id)

    def delete_ban(self, user):
        self.client.api.guilds_bans_delete(self.id, to_snowflake(user))

    def create_ban(self, user, delete_message_days=0):
        self.client.api.guilds_bans_create(self.id, to_snowflake(user), delete_message_days)