Exemple #1
0
    def __init__(
        self,
        *,
        label: str,
        style: int = ButtonStyle.gray,
        id: Optional[str] = None,
        url: Optional[str] = None,
        disabled: bool = False,
        emoji: Emoji = None,
    ):
        if style == ButtonStyle.URL and not url:
            raise InvalidArgument(
                "You must provide a URL when the style is URL")
        if style == ButtonStyle.URL and id:
            raise InvalidArgument("You musn't use both id and url")
        if not (1 <= style <= ButtonStyle.URL):
            raise InvalidArgument(
                f"style must be in between 1, {ButtonStyle.URL}")

        if not len(label):
            raise InvalidArgument("Label musn't be empty")

        self._style = style
        self._label = label
        self._url = url
        self._disabled = disabled
        self._emoji = emoji

        if not self.style == ButtonStyle.URL:
            self._id = id or str(uuid1())
        else:
            self._id = None
Exemple #2
0
    async def create_link(self, voiceChannelID, option):
        '''
        Generates a invite link to a VC with the Discord Party VC Feature.

        Parameters:
                voiceChannelID (int): ID of the voice channel to create the activity for
                option (str): A option amongst the predefined choices ("youtube","poker","betrayal","fishing","chess")

        Returns:
                invite_link (str): A discord invite link which, upon clicked, starts the custom activity in the VC.
        '''
        
        if option and (option.lower().replace(" ","") in defaultApplications.keys()):
            r = await self.session.post(f"https://discord.com/api/v8/channels/{voiceChannelID}/invites",
                                json={
                                    'max_age': 86400,
                                    'max_uses': 0,
                                    'target_application_id': defaultApplications[option],
                                    'target_type': 2,
                                    'temporary': False,
                                    'validate': None
                                }, 
                                headers = {
                                    'Authorization': f'Bot {self.client.http.token}',
                                    'Content-Type': 'application/json'
                                }
                            )
            result = await r.json()
            if ("errors" in result.keys()) or ("code" not in result.keys()):
                raise ConnectionError("An error occured while retrieving data from Discord API.")
            else:
                return f"https://discord.com/invite/{result['code']}"
        else:
            raise InvalidArgument("Invalid activity option chosen. You may only choose between (\"youtube\",\"poker\",\"chess\",\"fishing\",\"betrayal\")")
Exemple #3
0
    async def connect(self, channel):
        """
        Connect to a voice channel

        :param channel: The voice channel to connect to
        :return:
        """
        # We're using discord's websocket, not lavalink
        if not channel.guild == self.guild:
            raise InvalidArgument("The guild of the channel isn't the the same as the link's!")
        if channel.guild.unavailable:
            raise IllegalAction("Cannot connect to guild that is unavailable!")

        me = channel.guild.me
        permissions = me.permissions_in(channel)
        if not permissions.connect and not permissions.move_members:
            raise BotMissingPermissions(["connect"])

        self.set_state(State.CONNECTING)
        payload = {
            "op": 4,
            "d": {
                "guild_id": channel.guild.id,
                "channel_id": str(channel.id),
                "self_mute": False,
                "self_deaf": False
            }
        }

        await self.bot._connection._get_websocket(channel.guild.id).send_as_json(payload)
async def send(self, content=None, *, tts=False, embed=None, file=None, files=None, delete_after=None, nonce=None):
    channel = await self._get_channel()
    state = self._state
    content = str(content) if content is not None else None
    if embed is not None:
        embed = embed.to_dict()
    if file is not None and files is not None:
        raise InvalidArgument("cannot pass both file and files parameter to send()")
    if file is not None:
        files = [file]
    if files is None:
        files = []
    attachments = []
    some_random_id = 679460791094607952
    for file in files:
        attachment = {
            "id": str(some_random_id),
            "filename": file.filename,
            "size": 3,
            "url": f"https://cdn.discordapp.com/attachments/133251234164375552/679460791094607952/{file.filename}",
            "proxy_url": f"https://media.discordapp.net/attachments/133251234164375552/679460791094607952/{file.filename}",
        }
        attachments.append(attachment)
        some_random_id += 1
        file.close()
    return Message(
        state=state,
        channel=channel,
        data={
            "id": "679460791291871240",
            "type": 0,
            "content": content,
            "channel_id": str(channel.id),
            "author": {
                "id": str(state.user.id),
                "username": state.user.name,
                "avatar": None,
                "discriminator": state.user.discriminator,
                "bot": state.user.bot,
            },
            "attachments": attachments,
            "embeds": [embed] if embed is not None else [],
            "mentions": [],
            "mention_roles": [],
            "pinned": False,
            "mention_everyone": False,
            "tts": tts,
            "timestamp": "2020-02-18T22:54:36.415000+00:00",
            "edited_timestamp": None,
            "flags": 0,
            "nonce": str(nonce),
        },
    )
Exemple #5
0
    async def connect(self, channel):
        """
        Connect to a voice channel

        :param channel: The voice channel to connect to
        :return:
        """
        # We're using discord's websocket, not lavalink
        if channel.guild.id != self.guild_id:
            raise InvalidArgument(
                "The guild of the channel isn't the the same as the link's!")
        if channel.guild.unavailable:
            raise IllegalAction("Cannot connect to guild that is unavailable!")

        me = channel.guild.me
        permissions = me.permissions_in(channel)
        if (not permissions.connect or len(channel.members) >=
                channel.user_limit >= 1) and not permissions.move_members:
            raise BotMissingPermissions(["connect"])

        self.set_state(State.CONNECTING)
        # payload = {
        #     "op": 4,
        #     "d": {
        #         "guild_id": self.guild_id,
        #         "channel_id": str(channel.id),
        #         "self_mute": False,
        #         "self_deaf": False
        #     }
        # }
        # await self.bot._connection._get_websocket(self.guild_id).send_as_json(payload)
        await self._get_shard_socket(self.bot.shard_id
                                     ).voice_state(self.guild_id,
                                                   str(channel.id))

        start = time.monotonic()
        while not (me.voice and me.voice.channel):
            await asyncio.sleep(0.1)
            if start - time.monotonic() >= 10:
                raise IllegalAction(
                    "Couldn't connect to the channel within a reasonable timeframe!"
                )
 def on_command(self, cmd, arg,
                commands=None,
                client=None,
                voice=None,
                message=None,
                **kwargs):
     if ('move to' in commands and cmd in commands['move to']) \
             or cmd == 'move to':
         try:
             ch = client.get_channel(arg)
             if ch is None or ch.type is not ChannelType.voice:
                 raise InvalidArgument('Not a voice channel')
             print("\033[01m{}\033[00m requested me to move to "
                   "\033[01m{channel.name}\033[00m"
                   " on \033[01m{channel.server.name}\033[00m"
                   .format(message.author.name, channel=ch))
             yield from switch_voice_channel(ch)
             return True
         except InvalidArgument as e:
             yield from client.send_message(message.channel,
                                            '\n'.join(e.args))
     return False
Exemple #7
0
    def _handle_category_overwrites(options):
        overwrites = options.get('overwrites', None)
        if overwrites:
            perms = []
            for target, perm in overwrites.items():
                if not isinstance(perm, PermissionOverwrite):
                    raise InvalidArgument(
                        'Expected PermissionOverwrite received {0.__name__}'.
                        format(type(perm)))

                allow, deny = perm.pair()
                payload = {
                    'allow': allow.value,
                    'deny': deny.value,
                    'id': target.id
                }

                if isinstance(target, Role):
                    payload['type'] = 'role'
                else:
                    payload['type'] = 'member'

                perms.append(payload)
            options['permission_overwrites'] = perms
Exemple #8
0
    def id(self, value: str):
        if self.style == ButtonStyle.URL:
            raise InvalidArgument("button style is URL")

        self._id = id
Exemple #9
0
    def url(self, value: str):
        if self.style != ButtonStyle.URL:
            raise InvalidArgument("button style is not URL")

        self._url = value
Exemple #10
0
    def label(self, value: str):
        if not len(value):
            raise InvalidArgument("Label musn't be empty")

        self._label = value
Exemple #11
0
async def send(self,
               content=None,
               *,
               tts=False,
               embed=None,
               file=None,
               files=None,
               delete_after=None,
               nonce=None,
               allowed_mentions=None,
               reference=None,
               mention_author=None,
               components=None) -> ComponentMessage:
    """|coro|

            Sends a message to the destination with the content given.
            This replaces original implementation of 'discord.abc.Messageable.send' to support discord buttons feature.

            The content must be a type that can convert to a string through ``str(content)``.
            If the content is set to ``None`` (the default), then the ``embed`` parameter must
            be provided.

            To upload a single file, the ``file`` parameter should be used with a
            single :class:`~discord.File` object. To upload multiple files, the ``files``
            parameter should be used with a :class:`list` of :class:`~discord.File` objects.
            **Specifying both parameters will lead to an exception**.

            If the ``embed`` parameter is provided, it must be of type :class:`~discord.Embed` and
            it must be a rich embed type.

            Parameters
            ------------
            # Originated from discord.py
            content: :class:`str`
                The content of the message to send.
            tts: :class:`bool`
                Indicates if the message should be sent using text-to-speech.
            embed: :class:`~discord.Embed`
                The rich embed for the content.
            file: :class:`~discord.File`
                The file to upload.
            files: List[:class:`~discord.File`]
                A list of files to upload. Must be a maximum of 10.
            nonce: :class:`int`
                The nonce to use for sending this message. If the message was successfully sent,
                then the message will have a nonce with this value.
            delete_after: :class:`float`
                If provided, the number of seconds to wait in the background
                before deleting the message we just sent. If the deletion fails,
                then it is silently ignored.
            allowed_mentions: :class:`~discord.AllowedMentions`
                Controls the mentions being processed in this message. If this is
                passed, then the object is merged with :attr:`~discord.Client.allowed_mentions`.
                The merging behaviour only overrides attributes that have been explicitly passed
                to the object, otherwise it uses the attributes set in :attr:`~discord.Client.allowed_mentions`.
                If no object is passed at all then the defaults given by :attr:`~discord.Client.allowed_mentions`
                are used instead.

            reference: Union[:class:`~discord.Message`, :class:`~discord.MessageReference`]
                A reference to the :class:`~discord.Message` to which you are replying, this can be created using
                :meth:`~discord.Message.to_reference` or passed directly as a :class:`~discord.Message`. You can control
                whether this mentions the author of the referenced message using the :attr:`~discord.AllowedMentions.replied_user`
                attribute of ``allowed_mentions`` or by setting ``mention_author``.

            mention_author: Optional[:class:`bool`]
                If set, overrides the :attr:`~discord.AllowedMentions.replied_user` attribute of ``allowed_mentions``.

            # New parameters added in discord_buttons
            components: Optional[List[:class:'~discord_buttons.Button']]
                List of Button objects to send with message.

            Raises
            --------
            ~discord.HTTPException
                Sending the message failed.
            ~discord.Forbidden
                You do not have the proper permissions to send the message.
            ~discord.InvalidArgument
                The ``files`` list is not of the appropriate size,
                you specified both ``file`` and ``files``,
                or the ``reference`` object is not a :class:`~discord.Message`
                or :class:`~discord.MessageReference`.

            Returns
            ---------
            :class:`~discord.Message`
                The message that was sent.
    """

    channel = await self._get_channel()
    state = self._state
    content = str(content) if content is not None else None
    if embed is not None:
        embed = embed.to_dict()

    if allowed_mentions is not None:
        if state.allowed_mentions is not None:
            allowed_mentions = state.allowed_mentions.merge(
                allowed_mentions).to_dict()
        else:
            allowed_mentions = allowed_mentions.to_dict()
    else:
        allowed_mentions = state.allowed_mentions and state.allowed_mentions.to_dict(
        )

    if mention_author is not None:
        allowed_mentions = allowed_mentions or AllowedMentions().to_dict()
        allowed_mentions['replied_user'] = bool(mention_author)

    if reference is not None:
        try:
            reference = reference.to_message_reference_dict()
        except AttributeError:
            raise InvalidArgument(
                'reference parameter must be Message or MessageReference'
            ) from None

    # Added in discord_buttons to support discord buttons feature.
    # buttons : Union[List[Button], Button]
    parsed_components: List[JSON] = []
    if components is not None:
        btn_logger.debug(
            'discord.abc.Messageable.send#patched > Parsing components : {}'.
            format(components))
        if not len(components):
            parsed_components = []
        elif not components[
                0].type == ComponentType.Group:  # Not a nested component array.
            # Pack with list
            parsed_components = [{
                'type':
                ComponentType.Group.value,
                'components':
                [component.to_json() for component in components]
            }]
        else:
            parsed_components = [{
                'type':
                ComponentType.Group.value,
                'components': [component.to_json() for component in line]
            } for line in components]
        btn_logger.debug(
            'discord.abc.Messageable.send#patched > Parsed buttons into components : {}'
            .format(parsed_components))

    if file is not None and files is not None:
        raise InvalidArgument(
            'cannot pass both file and files parameter to send()')

    if file is not None:
        if not isinstance(file, File):
            raise InvalidArgument('file parameter must be File')

        try:
            data = await state.http.send_files(
                channel.id,
                files=[file],
                allowed_mentions=allowed_mentions,
                content=content,
                tts=tts,
                embed=embed,
                nonce=nonce,
                message_reference=reference,
                components=parsed_components)
        finally:
            file.close()

    elif files is not None:
        if len(files) > 10:
            raise InvalidArgument(
                'files parameter must be a list of up to 10 elements')
        elif not all(isinstance(file, File) for file in files):
            raise InvalidArgument('files parameter must be a list of File')

        try:
            data = await state.http.send_files(
                channel.id,
                files=files,
                content=content,
                tts=tts,
                embed=embed,
                nonce=nonce,
                allowed_mentions=allowed_mentions,
                message_reference=reference,
                components=parsed_components)
        finally:
            for f in files:
                f.close()
    else:
        data = await state.http.send_message(channel.id,
                                             content,
                                             tts=tts,
                                             embed=embed,
                                             nonce=nonce,
                                             allowed_mentions=allowed_mentions,
                                             message_reference=reference,
                                             components=parsed_components)

    ret = state.create_message(channel=channel, data=data)
    if delete_after is not None:
        await ret.delete(delay=delete_after)

    print('patched send debug : packing with ButtonMessage object...')
    print(json.dumps(data, ensure_ascii=False, indent=2))
    if 'message_reference' in data:
        # Issue : 'message_reference' object in message response data lacks 'channel_id', so copy it from object 'referenced_message'.
        data['message_reference']['channel_id'] = data['referenced_message'][
            'channel_id']

    btn_msg: ComponentMessage = ComponentMessage.fromMessage(ret, data)
    return btn_msg
    async def respond(
        self,
        *,
        type: int = InteractionType.IgnoredChannelMessageWithSource,
        content: str = None,
        embed: Embed = None,
        embeds: List[Embed] = [],
        allowed_mentions: AllowedMentions = None,
        tts: bool = False,
        flags: int = FlagsType.Ephemeral,
    ) -> None:
        """Function to send response to discord

        .. note::
            If you don't use this function after using `wait_for_button_click`, a interaction error will be raised

        :returns: :class:`None`

        Parameters
        ----------
        type: :class:`int`
            The interaction's type. (4 or more and 6 or less)
            Default 6 (InteractionType.IgnoredChannelMessageWithSource)
        content: Optional[:class:`str`]
            The response message's content
        embed: Optional[:class:`discord.Embed`]
            The response message's embed
        embeds: Optional[List[:class:`discord.Embed`]]
            The response message's embeds
        allowed_mentions: Optional[:class:`discord.AllowedMentions`]
            The response message's allowed mentions
        tts: Optional[:class:`bool`]
            The response message's tts (default False)
        flags: Optional[:class:`int`]
            The response message's flags (default 64)
        """
        state = self.bot._get_state()

        if embed and embeds:
            embeds.append(embed)
        elif embed:
            embeds = embed

        if len(embeds) > 0:
            raise InvalidArgument("Do not provide more than 10 embeds")
        if embeds:
            embeds = list(map(lambda x: x.to_dict(), embeds))

        if allowed_mentions:
            if state.allowed_mentions:
                allowed_mentions = state.allowed_mentions.merge(
                    allowed_mentions).to_dict()
            else:
                allowed_mentions = allowed_mentions.to_dict()
        else:
            allowed_mentions = state.allowed_mentions and state.allowed_mentions.to_dict(
            )

        data = {
            "content": content,
            "embeds": embeds,
            "allowed_mentions": allowed_mentions,
            "tts": tts,
            "flags": flags,
        }

        await self.bot.http.request(
            Route(
                "POST",
                f"/interactions/{self.interaction_id}/{self.interaction_token}/callback"
            ),
            json={
                "type": type,
                "data": data
            },
        )
Exemple #13
0
async def send(
    self,
    content=None,
    *,
    tts=None,
    embed=None,
    embeds=None,
    file=None,
    files=None,
    stickers=None,
    delete_after=None,
    nonce=None,
    allowed_mentions=None,
    reference=None,
    mention_author=None,
    view=None,
    components=None,
):
    """|coro|

    Sends a message to the destination with the content given.

    The content must be a type that can convert to a string through ``str(content)``.
    If the content is set to ``None`` (the default), then the ``embed`` parameter must
    be provided.

    To upload a single file, the ``file`` parameter should be used with a
    single :class:`~discord.File` object. To upload multiple files, the ``files``
    parameter should be used with a :class:`list` of :class:`~discord.File` objects.
    **Specifying both parameters will lead to an exception**.

    To upload a single embed, the ``embed`` parameter should be used with a
    single :class:`~discord.Embed` object. To upload multiple embeds, the ``embeds``
    parameter should be used with a :class:`list` of :class:`~discord.Embed` objects.
    **Specifying both parameters will lead to an exception**.

    Parameters
    ------------
    content: Optional[:class:`str`]
        The content of the message to send.
    tts: :class:`bool`
        Indicates if the message should be sent using text-to-speech.
    embed: :class:`~discord.Embed`
        The rich embed for the content.
    file: :class:`~discord.File`
        The file to upload.
    files: List[:class:`~discord.File`]
        A list of files to upload. Must be a maximum of 10.
    nonce: :class:`int`
        The nonce to use for sending this message. If the message was successfully sent,
        then the message will have a nonce with this value.
    delete_after: :class:`float`
        If provided, the number of seconds to wait in the background
        before deleting the message we just sent. If the deletion fails,
        then it is silently ignored.
    allowed_mentions: :class:`~discord.AllowedMentions`
        Controls the mentions being processed in this message. If this is
        passed, then the object is merged with :attr:`~discord.Client.allowed_mentions`.
        The merging behaviour only overrides attributes that have been explicitly passed
        to the object, otherwise it uses the attributes set in :attr:`~discord.Client.allowed_mentions`.
        If no object is passed at all then the defaults given by :attr:`~discord.Client.allowed_mentions`
        are used instead.

        .. versionadded:: 1.4

    reference: Union[:class:`~discord.Message`, :class:`~discord.MessageReference`, :class:`~discord.PartialMessage`]
        A reference to the :class:`~discord.Message` to which you are replying, this can be created using
        :meth:`~discord.Message.to_reference` or passed directly as a :class:`~discord.Message`. You can control
        whether this mentions the author of the referenced message using the :attr:`~discord.AllowedMentions.replied_user`
        attribute of ``allowed_mentions`` or by setting ``mention_author``.

        .. versionadded:: 1.6

    mention_author: Optional[:class:`bool`]
        If set, overrides the :attr:`~discord.AllowedMentions.replied_user` attribute of ``allowed_mentions``.

        .. versionadded:: 1.6
    view: :class:`discord.ui.View`
        A Discord UI View to add to the message.
    embeds: List[:class:`~discord.Embed`]
        A list of embeds to upload. Must be a maximum of 10.

        .. versionadded:: 2.0
    stickers: Sequence[Union[:class:`~discord.GuildSticker`, :class:`~discord.StickerItem`]]
        A list of stickers to upload. Must be a maximum of 3.

        .. versionadded:: 2.0

    Raises
    --------
    ~discord.HTTPException
        Sending the message failed.
    ~discord.Forbidden
        You do not have the proper permissions to send the message.
    ~discord.InvalidArgument
        The ``files`` list is not of the appropriate size,
        you specified both ``file`` and ``files``,
        or you specified both ``embed`` and ``embeds``,
        or the ``reference`` object is not a :class:`~discord.Message`,
        :class:`~discord.MessageReference` or :class:`~discord.PartialMessage`.

    Returns
    ---------
    :class:`~discord.Message`
        The message that was sent.
    """

    channel = await self._get_channel()
    state = self._state
    content = str(content) if content is not None else None
    _components = None

    if embed is not None and embeds is not None:
        raise InvalidArgument(
            'cannot pass both embed and embeds parameter to send()')

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

    elif embeds is not None:
        if len(embeds) > 10:
            raise InvalidArgument(
                'embeds parameter must be a list of up to 10 elements')
        embeds = [embed.to_dict() for embed in embeds]

    if stickers is not None:
        stickers = [sticker.id for sticker in stickers]

    if allowed_mentions is None:
        allowed_mentions = state.allowed_mentions and state.allowed_mentions.to_dict(
        )

    elif state.allowed_mentions is not None:
        allowed_mentions = state.allowed_mentions.merge(
            allowed_mentions).to_dict()
    else:
        allowed_mentions = allowed_mentions.to_dict()
    if mention_author is not None:
        allowed_mentions = allowed_mentions or AllowedMentions().to_dict()
        allowed_mentions['replied_user'] = bool(mention_author)

    if reference is not None:
        try:
            reference = reference.to_message_reference_dict()
        except AttributeError:
            raise InvalidArgument(
                'reference parameter must be Message, MessageReference, or PartialMessage'
            ) from None

    if view:
        if not hasattr(view, '__discord_ui_view__'):
            raise InvalidArgument(
                f'view parameter must be View not {view.__class__!r}')

        _components = view.to_components()
    else:
        _components = None

    if components:
        # This is not needed, but some users still prefer
        # dislash style of sending components so yeah
        if len(components) > 5:
            raise InvalidArgument(
                "components must be a list of up to 5 action rows")
        wrapped = []
        for comp in components:
            if isinstance(comp, ActionRow):
                wrapped.append(comp)
            else:
                wrapped.append(ActionRow(comp))
        components = _components or []
        for comp in wrapped:
            components.append(comp.to_dict())

    if file is not None and files is not None:
        raise InvalidArgument(
            'cannot pass both file and files parameter to send()')

    if file is not None:
        if not isinstance(file, File):
            raise InvalidArgument('file parameter must be File')

        try:
            data = await state.http.send_files(
                channel.id,
                files=[file],
                allowed_mentions=allowed_mentions,
                content=content,
                tts=tts,
                embed=embed,
                embeds=embeds,
                nonce=nonce,
                message_reference=reference,
                stickers=stickers,
                components=components,
            )
        finally:
            file.close()

    elif files is not None:
        if len(files) > 10:
            raise InvalidArgument(
                'files parameter must be a list of up to 10 elements')
        elif not all(isinstance(file, File) for file in files):
            raise InvalidArgument('files parameter must be a list of File')

        try:
            data = await state.http.send_files(
                channel.id,
                files=files,
                content=content,
                tts=tts,
                embed=embed,
                embeds=embeds,
                nonce=nonce,
                allowed_mentions=allowed_mentions,
                message_reference=reference,
                stickers=stickers,
                components=components,
            )
        finally:
            for f in files:
                f.close()
    else:
        data = await state.http.send_message(
            channel.id,
            content,
            tts=tts,
            embed=embed,
            embeds=embeds,
            nonce=nonce,
            allowed_mentions=allowed_mentions,
            message_reference=reference,
            stickers=stickers,
            components=components,
        )

    ret = state.create_message(channel=channel, data=data)
    if view:
        state.store_view(view, ret.id)

    if delete_after is not None:
        await ret.delete(delay=delete_after)
    return ret
Exemple #14
0
async def edit(
    self,
    content: Optional[str] = MISSING,
    embed: Optional[Embed] = MISSING,
    embeds: List[Embed] = MISSING,
    attachments: List[Attachment] = MISSING,
    suppress: bool = MISSING,
    delete_after: Optional[float] = None,
    allowed_mentions: Optional[AllowedMentions] = MISSING,
    view: Optional[View] = MISSING,
    components: Optional[list] = MISSING,
) -> None:
    """|coro|

    Edits the message.

    The content must be able to be transformed into a string via ``str(content)``.

    .. versionchanged:: 1.3
        The ``suppress`` keyword-only parameter was added.

    Parameters
    -----------
    content: Optional[:class:`str`]
        The new content to replace the message with.
        Could be ``None`` to remove the content.
    embed: Optional[:class:`Embed`]
        The new embed to replace the original with.
        Could be ``None`` to remove the embed.
    embeds: List[:class:`Embed`]
        The new embeds to replace the original with. Must be a maximum of 10.
        To remove all embeds ``[]`` should be passed.

        .. versionadded:: 2.0
    attachments: List[:class:`Attachment`]
        A list of attachments to keep in the message. If ``[]`` is passed
        then all attachments are removed.
    suppress: :class:`bool`
        Whether to suppress embeds for the message. This removes
        all the embeds if set to ``True``. If set to ``False``
        this brings the embeds back if they were suppressed.
        Using this parameter requires :attr:`~.Permissions.manage_messages`.
    delete_after: Optional[:class:`float`]
        If provided, the number of seconds to wait in the background
        before deleting the message we just edited. If the deletion fails,
        then it is silently ignored.
    allowed_mentions: Optional[:class:`~discord.AllowedMentions`]
        Controls the mentions being processed in this message. If this is
        passed, then the object is merged with :attr:`~discord.Client.allowed_mentions`.
        The merging behaviour only overrides attributes that have been explicitly passed
        to the object, otherwise it uses the attributes set in :attr:`~discord.Client.allowed_mentions`.
        If no object is passed at all then the defaults given by :attr:`~discord.Client.allowed_mentions`
        are used instead.

        .. versionadded:: 1.4
    view: Optional[:class:`~discord.ui.View`]
        The updated view to update this message with. If ``None`` is passed then
        the view is removed.

    Raises
    -------
    HTTPException
        Editing the message failed.
    Forbidden
        Tried to suppress a message without permissions or
        edited a message's content or embed that isn't yours.
    ~discord.InvalidArgument
        You specified both ``embed`` and ``embeds``
    """

    _components = None
    payload: Dict[str, Any] = {}
    if content is not MISSING:
        payload['content'] = str(content) if content is not None else None
    if embed is not MISSING and embeds is not MISSING:
        raise InvalidArgument(
            'cannot pass both embed and embeds parameter to edit()')

    if embed is not MISSING:
        payload['embeds'] = [] if embed is None else [embed.to_dict()]
    elif embeds is not MISSING:
        payload['embeds'] = [e.to_dict() for e in embeds]

    if suppress is not MISSING:
        flags = MessageFlags._from_value(self.flags.value)
        flags.suppress_embeds = suppress
        payload['flags'] = flags.value

    if allowed_mentions is MISSING:
        if self._state.allowed_mentions is not None and self.author.id == self._state.self_id:
            payload['allowed_mentions'] = self._state.allowed_mentions.to_dict(
            )
    elif allowed_mentions is not None:
        if self._state.allowed_mentions is not None:
            payload['allowed_mentions'] = self._state.allowed_mentions.merge(
                allowed_mentions).to_dict()
        else:
            payload['allowed_mentions'] = allowed_mentions.to_dict()

    if attachments is not MISSING:
        payload['attachments'] = [a.to_dict() for a in attachments]

    if view is not MISSING:
        self._state.prevent_view_updates_for(self.id)
        _components = view.to_components() if view else []
    if components is not MISSING:
        # Once again, this is for those who prefer dislash.py style
        if components:
            _components = _components or []
            for comp in components:
                if isinstance(comp, ActionRow):
                    _components.append(comp.to_dict())
                else:
                    _components.append(ActionRow(comp).to_dict())
        else:
            _components = _components or []

    payload["components"] = _components or []

    if payload:
        data = await self._state.http.edit_message(self.channel.id, self.id,
                                                   **payload)
        self._update(data)

    if view and not view.is_finished():
        self._state.store_view(view, self.id)

    if delete_after is not None:
        await self.delete(delay=delete_after)
async def send(self,
               content=None,
               *,
               tts=False,
               embed=None,
               file=None,
               files=None,
               delete_after=None,
               nonce=None,
               allowed_mentions=None,
               reference=None,
               mention_author=None,
               components=None):
    channel = await self._get_channel()
    state = self._state
    content = str(content) if content is not None else None
    if embed is not None:
        embed = embed.to_dict()

    if allowed_mentions is not None:
        if state.allowed_mentions is not None:
            allowed_mentions = state.allowed_mentions.merge(
                allowed_mentions).to_dict()
        else:
            allowed_mentions = allowed_mentions.to_dict()
    else:
        allowed_mentions = state.allowed_mentions and state.allowed_mentions.to_dict(
        )

    if mention_author is not None:
        allowed_mentions = allowed_mentions or discord.AllowedMentions(
        ).to_dict()
        allowed_mentions['replied_user'] = bool(mention_author)

    if reference is not None:
        try:
            reference = reference.to_message_reference_dict()
        except AttributeError:
            raise InvalidArgument(
                'reference parameter must be Message or MessageReference'
            ) from None

    if components:
        custom_ids = set()
        for action in components:
            for c in action:
                if c.custom_id in custom_ids:
                    raise discord.InvalidArgument(
                        'custom_ids have to be unique in one message')
                custom_ids.add(c.custom_id)
        components = [{
            'type': ComponentTypes.ActionRow,
            'components': [comp.to_dict() for comp in action]
        } for action in components]

    if file is not None and files is not None:
        raise InvalidArgument(
            'cannot pass both file and files parameter to send()')

    if file is not None:
        if not isinstance(file, discord.File):
            raise InvalidArgument('file parameter must be File')

        try:
            data = await state.http.send_files_components(
                channel.id,
                files=[file],
                allowed_mentions=allowed_mentions,
                content=content,
                tts=tts,
                embed=embed,
                nonce=nonce,
                message_reference=reference,
                components=components)
        finally:
            file.close()

    elif files is not None:
        if len(files) > 10:
            raise InvalidArgument(
                'files parameter must be a list of up to 10 elements')
        elif not all(isinstance(file, discord.File) for file in files):
            raise InvalidArgument('files parameter must be a list of File')

        try:
            data = await state.http.send_files_components(
                channel.id,
                files=files,
                content=content,
                tts=tts,
                embed=embed,
                nonce=nonce,
                allowed_mentions=allowed_mentions,
                message_reference=reference,
                components=components)
        finally:
            for f in files:
                f.close()
    else:
        data = await state.http.send_message_components(
            channel.id,
            content,
            tts=tts,
            embed=embed,
            nonce=nonce,
            allowed_mentions=allowed_mentions,
            message_reference=reference,
            components=components)

    ret = state.create_message(channel=channel, data=data)
    if delete_after is not None:
        await ret.delete(delay=delete_after)
    return ret
Exemple #16
0
async def send_with_components(messageable,
                               content=None,
                               *,
                               tts=False,
                               embed=None,
                               components=None,
                               file=None,
                               files=None,
                               delete_after=None,
                               nonce=None,
                               allowed_mentions=None,
                               reference=None,
                               mention_author=None):
    channel = await messageable._get_channel()
    state = messageable._state
    content = str(content) if content is not None else None
    if embed is not None:
        embed = embed.to_dict()

    if components is not None:
        if len(components) > 5:
            raise InvalidArgument(
                "components must be a list of up to 5 action rows")
        wrapped = []
        for comp in components:
            if isinstance(comp, ActionRow):
                wrapped.append(comp)
            else:
                wrapped.append(ActionRow(comp))
        components = [comp.to_dict() for comp in wrapped]

    if allowed_mentions is None:
        allowed_mentions = state.allowed_mentions and state.allowed_mentions.to_dict(
        )

    elif state.allowed_mentions is not None:
        allowed_mentions = state.allowed_mentions.merge(
            allowed_mentions).to_dict()
    else:
        allowed_mentions = allowed_mentions.to_dict()
    if mention_author is not None:
        allowed_mentions = allowed_mentions or AllowedMentions().to_dict()
        allowed_mentions['replied_user'] = bool(mention_author)

    if reference is not None:
        try:
            reference = reference.to_message_reference_dict()
        except AttributeError:
            raise InvalidArgument(
                'reference parameter must be Message or MessageReference'
            ) from None

    if file is not None and files is not None:
        raise InvalidArgument(
            'cannot pass both file and files parameter to send()')

    if file is not None:
        if not isinstance(file, File):
            raise InvalidArgument('file parameter must be File')

        try:
            data = await send_files(state.http,
                                    channel.id,
                                    files=[file],
                                    allowed_mentions=allowed_mentions,
                                    content=content,
                                    tts=tts,
                                    embed=embed,
                                    nonce=nonce,
                                    message_reference=reference,
                                    components=components)
        finally:
            file.close()

    elif files is not None:
        if len(files) > 10:
            raise InvalidArgument(
                'files parameter must be a list of up to 10 elements')
        elif not all(isinstance(file, File) for file in files):
            raise InvalidArgument('files parameter must be a list of File')

        try:
            data = await send_files(state.http,
                                    channel.id,
                                    files=files,
                                    content=content,
                                    tts=tts,
                                    embed=embed,
                                    nonce=nonce,
                                    allowed_mentions=allowed_mentions,
                                    message_reference=reference,
                                    components=components)
        finally:
            for f in files:
                f.close()
    else:
        data = await send_message(state.http,
                                  channel.id,
                                  content,
                                  tts=tts,
                                  embed=embed,
                                  nonce=nonce,
                                  allowed_mentions=allowed_mentions,
                                  message_reference=reference,
                                  components=components)

    ret = state.create_message(channel=channel, data=data)
    if delete_after is not None:
        await ret.delete(delay=delete_after)
    return ret