async def set_vanity_invite(self, url: str) -> 'typing.Union[dt_invite.Invite, None]': """ Sets the vanity :class:`.Invite` for this guild. :param url: The code to use for this guild. :return: The :class:`.Invite` produced. """ if 'vanity-url' not in self.features: raise CuriousError("This guild has no vanity URL") try: resp = await self._bot.http.edit_vanity_url(self.id, url) except HTTPException as e: if e.error_code != 50020: raise raise CuriousError("This guild has no vanity URL") code = resp.get("code", None) if code is None: return None invite_data = await self._bot.http.get_invite(code) invite = dt_invite.Invite(self._bot, **invite_data) return invite
async def unblock(self): """ Unblocks this user. """ if self._bot.is_bot: raise CuriousError("Bots cannot have blocks") if self.type_ != FriendType.BLOCKED: raise CuriousError("This user is not blocked") await self._bot.http.remove_relationship(self.id)
async def remove_friend(self): """ Removes this user as a friend. """ if self._bot.is_bot: raise CuriousError("Bots cannot have friends") if self.type_ != FriendType.FRIEND: raise CuriousError("This user is not your friend") await self._bot.http.remove_relationship(self.id)
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
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
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)
async def get_recent_mentions( self, *, guild: 'dt_guild.Guild' = None, limit: int = 25, role_mentions: bool = True, everyone_mentions: bool = True) -> 'Sequence[dt_message.Message]': """ Gets a list of recent mentions for this user. :param guild: The guild to limit to. If this is None, it will return ALL recent mentions. :param limit: The maximum amount of mentions to return. :param role_mentions: Should role mentions be included? :param everyone_mentions: Should everyone mentions be included? :return: A list of :class:`~.Message` that represents the recent mentions. """ if self.bot: raise CuriousError("Bot accounts cannot use this function") if guild is not None: guild_id = guild.id else: guild_id = None mentions = await self._bot.http.get_mentions( guild_id=guild_id, limit=limit, roles=role_mentions, everyone=everyone_mentions) messages = [] for mention in mentions: messages.append(self._bot.state.make_message(mention)) return messages
async def edit(self, new_content: str = None, *, embed: Embed = None) -> 'Message': """ Edits this message. You must be the owner of this message to edit it. :param new_content: The new content for this message. :param embed: The new embed to provide. :return: This message, but edited with the new content. """ if self.guild is None: is_me = self.author not in self.channel.recipients else: is_me = self.guild.me == self.author if not is_me: raise CuriousError("Cannot edit messages from other users") if embed: embed = embed.to_dict() # Prevent race conditions by spawning a listener, then waiting for the task once we've # sent the HTTP request. t = await curio.spawn(self._bot.wait_for("message_update", predicate=lambda o, n: n.id == self.id)) try: await self._bot.http.edit_message(self.channel.id, self.id, content=new_content, embed=embed) except: await t.cancel() raise old, new = await t.join() return new
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
def _update_overwrites(self, overwrites: _typing.List[dict]): """ Updates the overwrites for this channel. :param overwrites: A list of overwrite dicts. """ if not self.guild_id: raise CuriousError( "A channel without a guild cannot have overwrites") self._overwrites = {} for overwrite in overwrites: id_ = int(overwrite["id"]) type_ = overwrite["type"] if type_ == "member": obb = self.guild._members.get(id_) else: obb = self.guild._roles.get(id_) self._overwrites[id_] = dt_permissions.Overwrite( allow=overwrite["allow"], deny=overwrite["deny"], obb=obb, channel_id=self.id) self._overwrites[id_]._immutable = True
async def edit(self, new_content: str = None, *, embed: Embed = None) -> 'Message': """ Edits this message. You must be the owner of this message to edit it. :param new_content: The new content for this message. :param embed: The new embed to provide. :return: This message, but edited with the new content. """ if self.guild is None: is_me = self.author not in self.channel.recipients else: is_me = self.guild.me == self.author if not is_me: raise CuriousError("Cannot edit messages from other users") if embed: embed = embed.to_dict() async with self._bot.events.wait_for_manager( "message_update", lambda o, n: n.id == self.id): await self._bot.http.edit_message(self.channel.id, self.id, content=new_content, embed=embed) return self
def friends(self) -> typing.Mapping[int, 'RelationshipUser']: """ :return: A mapping of :class:`~.RelationshipUser` that represents the friends for this user. """ if self.bot: raise CuriousError("Bots cannot have friends") return MappingProxyType(self._bot.state._friends)
def delete(self, role: 'dt_role.Role'): """ Deletes a role. """ if role.id not in self._roles: raise CuriousError("This role is not part of this guild") return role.delete()
async def connect(self): """ Connects to voice in this channel. """ if self.type != ChannelType.VOICE: raise CuriousError("Cannot connect to a text channel") return await self.guild.connect_to_voice(self)
def edit(self, channel: 'dt_channel.Channel', **kwargs): """ Edits a channel. """ if channel.id not in self._channels: raise CuriousError("This channel is not part of this guild") return channel.edit(**kwargs)
def delete(self, channel: 'dt_channel.Channel'): """ Deletes a channel. """ if channel.id not in self._channels: raise CuriousError("This channel is not part of this guild") return channel.delete()
def edit(self, role: 'dt_role.Role', **kwargs): """ Edits a role. """ if role.id not in self._roles: raise CuriousError("This role is not part of this guild") return role.edit(**kwargs)
def messages(self) -> 'ChannelMessageWrapper': """ :return: The :class:`.ChannelMessageWrapper` for this channel, if applicable. """ if not self.type.has_messages(): raise CuriousError("This channel does not have messages") if self._messages is None: self._messages = ChannelMessageWrapper(self) return self._messages
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)
def voice_members(self) -> '_typing.List[dt_member.Member]': """ :return: A list of members that are in this voice channel. """ if self.type != ChannelType.VOICE: raise CuriousError( "No members for channels that aren't voice channels") return list( filter(lambda member: member.voice.channel == self, self.guild.members.values()))
def voice_members(self) -> '_typing.List[dt_member.Member]': """ :return: A list of members that are in this voice channel. """ if self.type != ChannelType.VOICE: raise CuriousError( "No members for channels that aren't voice channels") return [ state.member for state in self.guild._voice_states.values() if state.channel_id == self.id ]
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)
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)
async def change_voice_state(self, member: 'dt_member.Member', *, deaf: bool = None, mute: bool = None): """ Changes the voice state of a member. :param member: The :class:`.Member` to change the voice state of. :param deaf: Should this member be deafened? :param mute: Should this member be muted? """ if member.voice is None: raise CuriousError("Cannot change voice state of member not in voice") await self._bot.http.edit_member_voice_state(self.id, member.id, deaf=deaf, mute=mute) return member.voice
async def connect_to_voice( self, channel: 'channel.Channel') -> 'voice_client.VoiceClient': """ Connects to a voice channel in this guild. :param channel: The :class:`~.Channel` to connect to. :return: The :class:`VoiceClient` that was connected to this guild. """ if voice_client is None: raise RuntimeError( "Cannot to voice - voice support is not installed") if channel.guild != self: raise CuriousError("Cannot use channel from a different guild") if self.voice_client is not None and self.voice_client.open: raise CuriousError("Voice client already exists in this guild") gw = self._bot._gateways[self.shard_id] self.voice_client = await voice_client.VoiceClient.create( self._bot, gw, channel) await self.voice_client.connect() return self.voice_client
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
async def add_to_guild(self, guild: 'dt_guild.Guild', *, permissions: int = 0): """ Authorizes this bot to join a guild. This requires a userbot client to be used. """ if self._bot.is_bot: raise CuriousError("Bots cannot add other bots") if self.bot is None: raise CuriousError("This application has no bot associated") if self.owner is None and not self.public: raise CuriousError("This bot is not public") if self.requires_code_grant: raise CuriousError("This bot requires code grant") await self._bot.http.authorize_bot(self.client_id, guild.id, permissions=permissions)
def get_recent_mentions(self, *, limit: int = 25, everyone_mentions: bool = True, role_mentions: bool = True): """ Gets mentions in this guild. """ if self.me.user.bot is True: raise CuriousError("Cannot get recent mentions on bot accounts") return self.me.user.get_recent_mentions( guild=self, limit=limit, everyone_mentions=everyone_mentions, role_mentions=role_mentions)
async def open_private_channel(self) -> 'dt_channel.Channel': """ Opens a private channel with a user. :return: The newly created private channel. """ if self.discriminator == "0000": raise CuriousError("Cannot open a private channel with a webhook") # First, try and access the channel from the channel cache. original_channel = self._bot.state.find_channel(self.id) if original_channel: return original_channel # Failing that, open a new private channel. channel_data = await self._bot.http.create_private_channel(self.id) channel = self._bot.state.make_private_channel(channel_data) return channel
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)