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
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\")")
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), }, )
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
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
def id(self, value: str): if self.style == ButtonStyle.URL: raise InvalidArgument("button style is URL") self._id = id
def url(self, value: str): if self.style != ButtonStyle.URL: raise InvalidArgument("button style is not URL") self._url = value
def label(self, value: str): if not len(value): raise InvalidArgument("Label musn't be empty") self._label = value
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 }, )
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
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
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