Exemplo n.º 1
0
    async def change_overwrite(self, overwrite: 'dt_permissions.Overwrite'):
        """
        Changes an overwrite for this channel.

        This overwrite must be an instance of :class:`~.Overwrite`.

        :param overwrite: The specific overwrite to use.
            If this is None, the overwrite will be deleted.
        """
        if not self.guild:
            raise PermissionsError("manage_roles")

        if not self.permissions(self.guild.me).manage_roles:
            raise PermissionsError("manage_roles")

        target = overwrite.target

        if isinstance(target, dt_member.Member):
            type_ = "member"
        else:
            type_ = "role"

        if overwrite is None:
            # Delete the overwrite instead.
            coro = self._bot.http.remove_overwrite(channel_id=self.id,
                                                   target_id=target.id)

            async def _listener(before, after):
                if after.id != self.id:
                    return False

                # probably right /shrug
                return True

            listener = await curio.spawn(
                self._bot.wait_for("channel_update", _listener))
        else:
            coro = self._bot.http.edit_overwrite(
                self.id,
                target.id,
                type_,
                allow=overwrite.allow.bitfield,
                deny=overwrite.deny.bitfield)

            async def _listener(before, after):
                return after.id == self.id

            listener = await curio.spawn(
                self._bot.wait_for("channel_update", _listener))

        try:
            await coro
        except:
            await listener.cancel()
            raise
        await listener.join()
        return self
Exemplo n.º 2
0
    async def send(self,
                   content: str = None,
                   *,
                   tts: bool = False,
                   embed: 'Embed' = None) -> 'dt_message.Message':
        """
        Sends a message to this channel.

        This requires SEND_MESSAGES permission in the channel.
        If the content is not a string, it will be automatically stringified.

        .. code:: python

            await channel.send("Hello, world!")

        :param content: The content of the message to send.
        :param tts: Should this message be text to speech?
        :param embed: An embed object to send with this message.
        :return: A new :class:`.Message` object.
        """
        if not self.channel.type.has_messages():
            raise CuriousError("Cannot send messages to a voice channel")

        if self.channel.guild:
            if not self.channel.permissions(
                    self.channel.guild.me).send_messages:
                raise PermissionsError("send_messages")

        if not isinstance(content, str) and content is not None:
            content = str(content)

        # check for empty messages
        if not content:
            if not embed:
                raise CuriousError("Cannot send an empty message")

            if self.channel.guild and not \
                    self.channel.permissions(self.channel.guild.me).embed_links:
                raise PermissionsError("embed_links")
        else:
            if content and len(content) > 2000:
                raise CuriousError("Content must be less than 2000 characters")

        if embed is not None:
            embed = embed.to_dict()

        data = await self.channel._bot.http.send_message(self.channel.id,
                                                         content,
                                                         tts=tts,
                                                         embed=embed)
        obb = self.channel._bot.state.make_message(data, cache=True)

        return obb
Exemplo n.º 3
0
    async def get_message(self, message_id: int) -> 'dt_message.Message':
        """
        Gets a single message from this channel.

        :param message_id: The message ID to retrieve.
        :return: A new :class:`.Message` object.
        """
        if self.guild:
            if not self.permissions(self.guild.me).read_message_history:
                raise PermissionsError("read_message_history")

        if self._bot.user.bot:
            data = await self._bot.http.get_message(self.id, message_id)
        else:
            data = await self._bot.http.get_message_history(self.id,
                                                            around=message_id,
                                                            limit=1)
            if not data:
                raise CuriousError("No messages found for this ID")
            else:
                data = data[0]

        msg = self._bot.state.make_message(data)

        return msg
Exemplo n.º 4
0
    async def add(self, *roles: 'dt_role.Role'):
        """
        Adds roles to this member.

        :param roles: The :class:`.Role` objects to add to this member's role list.
        """

        if not self._member.guild.me.guild_permissions.manage_roles:
            raise PermissionsError("manage_roles")

        # Ensure we can add all of these roles.
        for _r in roles:
            if _r >= self._member.guild.me.top_role:
                msg = "Cannot add role {} - it has a higher or equal position to our top role" \
                    .format(_r.name)
                raise HierarchyError(msg)

        async def _listener(before, after: Member):
            if after.id != self._member.id:
                return False

            if not all(role in after.roles for role in roles):
                return False

            return True

        async with self._member._bot.events.wait_for_manager(
                "guild_member_update", _listener):
            role_ids = set([_r.id for _r in self._member.roles] +
                           [_r.id for _r in roles])
            await self._member._bot.http.edit_member_roles(
                self._member.guild_id, self._member.id, role_ids)
Exemplo n.º 5
0
    async def modify_guild(self, *, afk_channel: 'dt_channel.Channel' = None,
                           verification_level: VerificationLevel = None,
                           content_filter_level: ContentFilterLevel = None,
                           notification_level: NotificationLevel = None,
                           **kwargs):
        """
        Edits this guild.

        For a list of available arguments, see 
        https://discordapp.com/developers/docs/resources/guild#modify-guild.
        
        :param afk_channel: The :class:`.Channel` that represents the AFK voice channel.
        :param verification_level: The :class:`.VerificationLevel` to use for this guild.
        :param content_filter_level: The :class:`.ContentFilterLevel` to use for this guild.
        :param notification_level: The :class:`.NotificationLevel` to use for this guild.
        """
        if not self.me.guild_permissions.manage_server:
            raise PermissionsError("manage_server")

        if afk_channel is not None:
            kwargs["afk_channel_id"] = afk_channel.id

        if verification_level is not None:
            kwargs["verification_level"] = verification_level.value

        if notification_level is not None:
            kwargs["default_message_notifications"] = notification_level.value

        if content_filter_level is not None:
            kwargs["explicit_content_filter"] = content_filter_level.value

        await self._bot.http.edit_guild(self.id, **kwargs)
        return self
Exemplo n.º 6
0
    async def unreact(self,
                      reaction: 'typing.Union[dt_emoji.Emoji, str]',
                      victim: 'dt_member.Member' = None):
        """
        Removes a reaction from a user.

        :param reaction: The reaction to remove.
        :param victim: The victim to remove the reaction of. Can be None to signify ourselves.
        """
        if not self.guild:
            if victim and victim != self:
                raise CuriousError("Cannot delete other reactions in a DM")

        if victim and victim != self:
            if not self.channel.effective_permissions(
                    self.guild.me).manage_messages:
                raise PermissionsError("manage_messages")

        if isinstance(reaction, dt_emoji.Emoji):
            emoji = "{}:{}".format(reaction.name, reaction.id)
        else:
            emoji = reaction

        await self._bot.http.delete_reaction(
            self.channel.id,
            self.id,
            emoji,
            victim=victim.id if victim else None)
Exemplo n.º 7
0
    async def get(self, message_id: int) -> 'dt_message.Message':
        """
        Gets a single message from this channel.

        .. versionchanged:: 0.7.0

            Errors raised are now consistent across bots and userbots.

        :param message_id: The message ID to retrieve.
        :return: A new :class:`.Message` object.
        :raises CuriousError: If the message could not be found.
        """
        if self.channel.guild:
            if not self.channel.permissions(
                    self.channel.guild.me).read_message_history:
                raise PermissionsError("read_message_history")

        cached_message = self.channel._bot.state.find_message(message_id)
        if cached_message is not None:
            return cached_message

        try:
            data = await self.channel._bot.http.get_message(
                self.channel.id, message_id)
        except HTTPException as e:
            # transform into a CuriousError if it wasn't found
            if e.error_code == ErrorCode.UNKNOWN_MESSAGE:
                raise CuriousError("No message found for this ID") from e

            raise

        msg = self.channel._bot.state.make_message(data)

        return msg
Exemplo n.º 8
0
    async def remove(self, *roles: 'dt_role.Role'):
        """
        Removes roles from this member.

        :param roles: The roles to remove.
        """
        if not self._member.guild.me.guild_permissions.manage_roles:
            raise PermissionsError("manage_roles")

        for _r in roles:
            if _r >= self._member.guild.me.top_role:
                msg = "Cannot remove role {} - it has a higher or equal position to our top role" \
                    .format(_r.name)
                raise HierarchyError(msg)

        async def _listener(before, after: Member):
            if after.id != self._member.id:
                return False

            if not all(role not in after.roles for role in roles):
                return False

            return True

        # Calculate the roles to keep.
        to_keep = set(self._member.roles) - set(roles)

        async with self._member._bot.events.wait_for_manager(
                "guild_member_update", _listener):
            role_ids = set([_r.id for _r in to_keep])
            await self._member._bot.http.edit_member_roles(
                self._member.guild_id, self._member.id, role_ids)
Exemplo n.º 9
0
    def get_history(self,
                    before: int = None,
                    after: int = None,
                    limit: int = 100) -> HistoryIterator:
        """
        Gets history for this channel.

        This is *not* a coroutine - it returns a :class:`HistoryIterator` which can be async 
        iterated over to get message history.

        .. code-block:: python3
        
            async for message in channel.get_history(limit=1000):
                print(message.content, "by", message.author.user.name)

        :param limit: The maximum number of messages to get.
        :param before: The snowflake ID to get messages before.
        :param after: The snowflake ID to get messages after.
        """
        if self.guild:
            if not self.permissions(self.guild.me).read_message_history:
                raise PermissionsError("read_message_history")

        return HistoryIterator(self,
                               self._bot,
                               before=before,
                               after=after,
                               max_messages=limit)
Exemplo n.º 10
0
    async def remove(self, user: '******', *,
                     reason: str = None) -> None:
        """
        Unbans a user from this guild.
        Example for unbanning the first banned user:

        .. code-block:: python3

            user = next(await guild.get_bans())
            await guild.unban(user)

        To unban an arbitrary user, use :meth:`.Client.get_user`.

        .. code-block:: python3

            user = await client.get_user(66237334693085184)
            await guild.unban(user)

        .. note::

            This does not take :class:`.Member` objects, as members cannot be in a guild and
            banned from the guild.

        :param user: The :class:`.User` to forgive and unban.
        :param reason: The reason given for unbanning.
        """
        if not self._guild.me.guild_permissions.ban_members:
            raise PermissionsError("ban_members")

        forgiven_id = user.id

        await self._guild._bot.http.unban_user(self._guild.id, forgiven_id, reason=reason)
Exemplo n.º 11
0
    async def delete(self) -> 'Role':
        """
        Deletes this role.
        """
        if not self.me.guild_permissions.manage_roles:
            raise PermissionsError("manage_roles")

        await self._bot.http.delete_role(self.guild.id, self.id)
        return self
Exemplo n.º 12
0
    async def delete(self) -> 'Channel':
        """
        Deletes this channel.
        """
        if not self.permissions(self.guild.me).manage_channels:
            raise PermissionsError("manaqe_channels")

        await self._bot.http.delete_channel(self.id)
        return self
Exemplo n.º 13
0
    async def upload_file(self,
                          filename: str,
                          *,
                          message_content: str = None) -> 'dt_message.Message':
        """
        A higher level interface to ``send_file``.

        This allows you to specify one of the following to upload:

            - A filename (str)
            - A file-like object
            - A path-like object

        This will open the file, read it in binary, and upload it to the channel.

        :param filename: The file to send, in the formats specified above.
        :param message_content: Any extra content to be sent with the message.
        :return: The new :class:`~.Message` created.
        """
        if self.type == ChannelType.VOICE:
            raise CuriousError("Cannot send messages to a voice channel")

        if self.guild:
            if not self.permissions(self.guild.me).send_messages:
                raise PermissionsError("send_messages")

            if not self.permissions(self.guild.me).attach_files:
                raise PermissionsError("attach_files")

        if hasattr(filename, "read"):
            # file-like
            file_data = filename.read()
            name = getattr(filename, "name", None)
        else:
            # assume it's pathlike
            path = pathlib.Path(filename)
            name = path.parts[-1]

            with open(path, mode='rb') as f:
                file_data = f.read()

        return await self.send_file(file_data,
                                    name,
                                    message_content=message_content)
Exemplo n.º 14
0
    async def set(self, new_nickname: str) -> 'Nickname':
        """
        Sets the nickname of the username.

        :param new_nickname: The new nickname of this user. If None, will reset the nickname.
        """

        # Ensure we don't try and set a bad nickname, which makes an empty listener.
        if new_nickname == self:
            return self

        guild: dt_guild.Guild = self.parent.guild

        me = False
        if self.parent == self.parent.guild.me:
            me = True
            if not guild.me.guild_permissions.change_nickname:
                raise PermissionsError("change_nickname")
        else:
            if not guild.me.guild_permissions.manage_nicknames:
                raise PermissionsError("manage_nicknames")

            # we can't change the owner nickname, unless we are the owner
            if guild.owner == self.parent:
                raise HierarchyError("Cannot change the nickname of the owner")

        if self.parent.top_role >= guild.me.top_role and self.parent != guild.me:
            raise HierarchyError(
                "Top role is equal to or lower than victim's top role")

        if new_nickname is not None and len(new_nickname) > 32:
            raise ValueError("Nicknames cannot be longer than 32 characters")

        async def _listener(before, after):
            return after.guild == guild and after.id == self.parent.id

        async with self.parent._bot.events.wait_for_manager(
                "guild_member_update", _listener):
            await self.parent._bot.http.change_nickname(
                guild.id, new_nickname, member_id=self.parent.id, me=me)

        # the wait_for means at this point the nickname has been changed
        return self.parent.nickname
Exemplo n.º 15
0
    async def create_invite(self, **kwargs) -> 'dt_invite.Invite':
        """
        Creates an invite in this channel.

        :param max_age: The maximum age of the invite.
        :param max_uses: The maximum uses of the invite.
        :param temporary: Is this invite temporary?
        :param unique: Is this invite unique?
        """
        if not self.guild:
            raise PermissionsError("create_instant_invite")

        if not self.permissions(self.guild.me).create_instant_invite:
            raise PermissionsError("create_instant_invite")

        inv = await self._bot.http.create_invite(self.id, **kwargs)
        invite = dt_invite.Invite(self._bot, **inv)

        return invite
Exemplo n.º 16
0
    async def delete_webhook(self, webhook: 'dt_webhook.Webhook'):
        """
        Deletes a webhook in this guild.

        :param webhook: The :class:`.Webhook` to delete.
        """
        if not self.me.guild_permissions.manage_webhooks:
            raise PermissionsError("manage_webhooks")

        await self._bot.http.delete_webhook(webhook.id)
Exemplo n.º 17
0
    async def change_icon(self, icon_content: bytes):
        """
        Changes the icon for this guild.

        :param icon_content: The bytes that represent the icon of the guild.
        """
        if not self.me.guild_permissions.manage_server:
            raise PermissionsError("manage_server")

        image = base64ify(icon_content)
        await self._bot.http.edit_guild(self.id, icon_content=image)
Exemplo n.º 18
0
    async def ban(self,
                  victim: 'typing.Union[dt_member.Member, dt_user.User]',
                  *,
                  delete_message_days: int = 7):
        """
        Bans somebody from the guild.

        This can either ban a :class:`~.Member`, in which they must be in the guild. 
        Or this can ban a :class:`~.User`, which does not need to be in the guild.

        Example for banning a member:

        .. code:: python

            member = guild.members[66237334693085184]
            await guild.ban(member)

        Example for banning a user:

        .. code:: python

            user = await client.get_user(66237334693085184)
            await guild.ban(user)

        :param victim: The :class:`.Member` or :class:`.User` object to ban.
        :param delete_message_days: The number of days to delete messages.
        """
        if not self.me.guild_permissions.ban_members:
            raise PermissionsError("ban_members")

        if isinstance(victim, dt_member.Member):
            if self.owner == victim:
                raise HierarchyError("Cannot ban the owner")

            if victim.guild_id != self.id:
                raise ValueError(
                    "Member must be from this guild (try `member.user` instead)"
                )

            if victim.top_role >= self.me.top_role:
                raise HierarchyError(
                    "Top role is equal to or lower than victim's top role")

            victim_id = victim.user.id

        elif isinstance(victim, dt_user.User):
            victim_id = victim.id

        else:
            raise TypeError("Victim must be a Member or a User")

        await self._bot.http.ban_user(guild_id=self.id,
                                      user_id=victim_id,
                                      delete_message_days=delete_message_days)
Exemplo n.º 19
0
    async def remove_all_reactions(self) -> None:
        """
        Removes all reactions from a message.
        """
        if not self.guild:
            raise CuriousError("Cannot delete other reactions in a DM")

        if not self.channel.permissions(self.guild.me).manage_messages:
            raise PermissionsError("manage_messages")

        await self._bot.http.delete_all_reactions(self.channel.id, self.id)
Exemplo n.º 20
0
    async def delete(self):
        """
        Deletes this invite.

        You must have MANAGE_CHANNELS permission in the guild to delete the invite.
        """
        guild = self.guild
        if guild != self._invite_guild:
            if not guild.me.guild_permissions.manage_channels:
                raise PermissionsError("manage_channels")

        await self._bot.http.delete_invite(self.code)
Exemplo n.º 21
0
    async def pin(self) -> 'Message':
        """
        Pins this message.

        You must have MANAGE_MESSAGES in the channel to pin the message.
        """
        if self.guild is not None:
            if not self.channel.permissions(self.guild.me).manage_messages:
                raise PermissionsError("manage_messages")

        await self._bot.http.pin_message(self.channel.id, self.id)
        return self
Exemplo n.º 22
0
    async def send_typing(self) -> None:
        """
        Starts typing in the channel for 5 seconds.
        """
        if self.type == ChannelType.VOICE:
            raise CuriousError("Cannot send messages to a voice channel")

        if self.guild:
            if not self.permissions(self.guild.me).send_messages:
                raise PermissionsError("send_message")

        await self._bot.http.send_typing(self.id)
Exemplo n.º 23
0
    async def send_typing(self) -> None:
        """
        Starts typing in the channel for 5 seconds.
        """
        if not self.type.has_messages():
            raise CuriousError("Cannot send messages to this channel")

        if self.guild:
            if not self.effective_permissions(self.guild.me).send_messages:
                raise PermissionsError("send_message")

        await self._bot.http.send_typing(self.id)
Exemplo n.º 24
0
    async def send_file(
            self,
            file_content: bytes,
            filename: str,
            *,
            message_content: _typing.Optional[str] = None
    ) -> 'dt_message.Message':
        """
        Uploads a message to this channel.

        This requires SEND_MESSAGES and ATTACH_FILES permission in the channel.

        .. code:: python

            with open("/tmp/emilia_best_girl.jpg", 'rb') as f:
                await channel.send_file(f.read(), "my_waifu.jpg")

        :param file_content: The bytes-like file content to upload.
            This **cannot** be a file-like object.
            
        :param filename: The filename of the file.
        :param message_content: Optional: Any extra content to be sent with the message.
        :return: The new :class:`~.Message` created.
        """
        if self.type == ChannelType.VOICE:
            raise CuriousError("Cannot send messages to a voice channel")

        if self.guild:
            if not self.permissions(self.guild.me).send_messages:
                raise PermissionsError("send_messages")

            if not self.permissions(self.guild.me).attach_files:
                raise PermissionsError("attach_files")

        data = await self._bot.http.send_file(self.id,
                                              file_content,
                                              filename=filename,
                                              content=message_content)
        obb = self._bot.state.make_message(data, cache=False)
        return obb
Exemplo n.º 25
0
    async def unpin(self) -> 'Message':
        """
        Unpins this message.

        You must have MANAGE_MESSAGES in this channel to unpin the message.
        Additionally, the message must already be pinned.
        """
        if self.guild is not None:
            if not self.channel.permissions(self.guild.me).manage_messages:
                raise PermissionsError("manage_messages")

        await self._bot.http.unpin_message(self.channel.id, self.id)
        return self
Exemplo n.º 26
0
    async def create(self, **kwargs) -> 'dt_role.Role':
        """
        Creates a new role in this guild.

        :return: A new :class:`.Role`.
        """
        if not self._guild.me.guild_permissions.manage_roles:
            raise PermissionsError("manage_roles")

        role_obb = dt_role.Role(client=self._guild._bot,
                                **(await self._guild._bot.http.create_role(self._guild.id)))
        self._roles[role_obb.id] = role_obb
        role_obb.guild_id = self._guild.id
        return await role_obb.edit(**kwargs)
Exemplo n.º 27
0
    async def edit(self, **kwargs) -> 'Channel':
        """
        Edits this channel.
        """
        if self.guild is None:
            raise CuriousError("Can only edit guild channels")

        if not self.permissions(self.guild.me).manage_channels:
            raise PermissionsError("manage_channels")

        if "parent" in kwargs:
            kwargs["parent_id"] = kwargs["parent"].id

        await self._bot.http.edit_channel(self.id, **kwargs)
        return self
Exemplo n.º 28
0
    async def __aiter__(self) -> 'typing.AsyncGenerator[GuildBan]':
        if not self._guild.me.guild_permissions.ban_members:
            raise PermissionsError("ban_members")

        bans = await self._guild._bot.http.get_bans(self._guild.id)

        for ban in bans:
            user_data = ban.get("user", None)
            if user_data is None:
                continue

            user = self._guild._bot.state.make_user(user_data)
            self._guild._bot.state._check_decache_user(user.id)
            ban = GuildBan(reason=ban.get("reason", None), user=user)
            yield ban
Exemplo n.º 29
0
    async def delete(self) -> None:
        """
        Deletes this message.

        You must have MANAGE_MESSAGE permissions to delete this message, or have it be your own 
        message.
        """
        if self.guild is None:
            me = self._bot.user.id
            has_manage_messages = False
        else:
            me = self.guild.me.id
            has_manage_messages = self.channel.permissions(self.guild.me).manage_messages

        if self.id != me and not has_manage_messages:
            raise PermissionsError("manage_messages")

        await self._bot.http.delete_message(self.channel.id, self.id)
Exemplo n.º 30
0
    async def kick(self, victim: 'dt_member.Member'):
        """
        Kicks somebody from the guild.

        :param victim: The :class:`.Member` to kick.
        """
        if not self.me.guild_permissions.kick_members:
            raise PermissionsError("kick_members")

        if victim.guild != self:
            raise ValueError("Member must be from this guild (try `member.user` instead)")

        if victim.top_role >= self.me.top_role:
            raise HierarchyError("Top role is equal to or lower than victim's top role")

        victim_id = victim.user.id

        await self._bot.http.kick_member(self.id, victim_id)