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
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_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
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)
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
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(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 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)
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)
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)
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
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
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)
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
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
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)
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)
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)
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)
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)
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
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 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_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
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
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)
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 __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
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)
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)