class ChannelUpdate(ChannelCreate): """ Sent when a channel is updated. Attributes ----- channel : :class:`disco.types.channel.Channel` The channel which was updated. """ overwrites = AutoDictField(PermissionOverwrite, 'id', alias='permission_overwrites')
class GuildPreview(SlottedModel): id = Field(int) name = Field(str) icon = Field(str) splash = Field(str) discovery_splash = Field(str) emojis = AutoDictField(GuildEmoji, 'id') features = ListField(str) approximate_member_count = Field(int) approximate_presence_count = Field(int) description = Field(str)
class Guild(SlottedModel, Permissible): """ A guild object. Attributes ---------- id : snowflake The id of this guild. owner_id : snowflake The id of the owner. afk_channel_id : snowflake The id of the afk channel. embed_channel_id : snowflake The id of the embed channel. name : str Guild's name. icon : str Guild's icon (as PNG binary data). splash : str Guild's splash image (as PNG binary data). region : str Voice region. afk_timeout : int Delay after which users are automatically moved to the afk channel. embed_enabled : bool Whether the guild's embed is enabled. verification_level : int The verification level used by the guild. mfa_level : int The MFA level used by the guild. features : list(str) Extra features enabled for this guild. members : dict(snowflake, :class:`GuildMember`) All of the guild's members. channels : dict(snowflake, :class:`disco.types.channel.Channel`) All of the guild's channels. roles : dict(snowflake, :class:`Role`) All of the guild's roles. emojis : dict(snowflake, :class:`GuildEmoji`) All of the guild's emojis. voice_states : dict(str, :class:`disco.types.voice.VoiceState`) All of the guild's voice states. """ id = Field(snowflake) owner_id = Field(snowflake) afk_channel_id = Field(snowflake) embed_channel_id = Field(snowflake) name = Field(text) icon = Field(binary) splash = Field(binary) region = Field(str) afk_timeout = Field(int) embed_enabled = Field(bool) verification_level = Field(enum(VerificationLevel)) mfa_level = Field(int) features = ListField(str) members = AutoDictField(GuildMember, 'id') channels = AutoDictField(Channel, 'id') roles = AutoDictField(Role, 'id') emojis = AutoDictField(GuildEmoji, 'id') voice_states = AutoDictField(VoiceState, 'session_id') member_count = Field(int) synced = Field(bool, default=False) def __init__(self, *args, **kwargs): super(Guild, self).__init__(*args, **kwargs) self.attach(six.itervalues(self.channels), {'guild_id': self.id}) self.attach(six.itervalues(self.members), {'guild_id': self.id}) self.attach(six.itervalues(self.roles), {'guild_id': self.id}) self.attach(six.itervalues(self.emojis), {'guild_id': self.id}) self.attach(six.itervalues(self.voice_states), {'guild_id': self.id}) @cached_property def owner(self): return self.members.get(self.owner_id) def get_permissions(self, member): """ Get the permissions a user has in this guild. Returns ------- :class:`disco.types.permissions.PermissionValue` Computed permission value for the user. """ if not isinstance(member, GuildMember): member = self.get_member(member) # Owner has all permissions if self.owner_id == member.id: return PermissionValue(Permissions.ADMINISTRATOR) value = PermissionValue(self.roles.get(self.id).permissions) for role in map(self.roles.get, member.roles): value += role.permissions return value def get_voice_state(self, user): """ Attempt to get a voice state for a given user (who should be a member of this guild). Returns ------- :class:`disco.types.voice.VoiceState` The voice state for the user in this guild. """ user = to_snowflake(user) for state in six.itervalues(self.voice_states): if state.user_id == user: return state def get_member(self, user): """ Attempt to get a member from a given user. Returns ------- :class:`GuildMember` The guild member object for the given user. """ user = to_snowflake(user) if user not in self.members: try: self.members[user] = self.client.api.guilds_members_get( self.id, user) except APIException: return return self.members.get(user) def create_role(self): """ Create a new role. Returns ------- :class:`Role` The newly created role. """ return self.client.api.guilds_roles_create(self.id) def delete_role(self, role): """ Delete a role. """ self.client.api.guilds_roles_delete(self.id, to_snowflake(role)) def update_role(self, role): return self.client.api.guilds_roles_modify( self.id, role.id, **{ 'name': role.name, 'permissions': role.permissions.value, 'position': role.position, 'color': role.color, 'hoist': role.hoist, 'mentionable': role.mentionable, }) def sync(self): if self.synced: return self.synced = True self.client.gw.send(OPCode.REQUEST_GUILD_MEMBERS, { 'guild_id': self.id, 'query': '', 'limit': 0, }) def get_bans(self): return self.client.api.guilds_bans_list(self.id) def delete_ban(self, user): self.client.api.guilds_bans_delete(self.id, to_snowflake(user)) def create_ban(self, user, delete_message_days=0): self.client.api.guilds_bans_create(self.id, to_snowflake(user), delete_message_days) def create_channel(self, *args, **kwargs): return self.client.api.guilds_channels_create(self.id, *args, **kwargs) def leave(self): return self.client.api.users_me_guilds_delete(self.id)
class Message(SlottedModel): """ Represents a Message created within a Channel on Discord. Attributes ---------- id : snowflake The ID of this message. channel_id : snowflake The channel ID this message was sent in. type : `MessageType` Type of the message. author : :class:`disco.types.user.User` The author of this message. content : str The unicode contents of this message. nonce : str The nonce of this message. timestamp : datetime When this message was created. edited_timestamp : datetime? When this message was last edited. tts : bool Whether this is a TTS (text-to-speech) message. mention_everyone : bool Whether this message has an @everyone which mentions everyone. pinned : bool Whether this message is pinned in the channel. mentions : dict[snowflake, `User`] Users mentioned within this message. mention_roles : list[snowflake] IDs for roles mentioned within this message. embeds : list[`MessageEmbed`] Embeds for this message. attachments : list[`MessageAttachment`] Attachments for this message. reactions : list[`MessageReaction`] Reactions for this message. """ id = Field(snowflake) channel_id = Field(snowflake) webhook_id = Field(snowflake) type = Field(enum(MessageType)) author = Field(User) content = Field(text) nonce = Field(snowflake) timestamp = Field(datetime) edited_timestamp = Field(datetime) tts = Field(bool) mention_everyone = Field(bool) pinned = Field(bool) mentions = AutoDictField(User, 'id') mention_roles = ListField(snowflake) embeds = ListField(MessageEmbed) attachments = AutoDictField(MessageAttachment, 'id') reactions = ListField(MessageReaction) def __str__(self): return '<Message {} ({})>'.format(self.id, self.channel_id) @cached_property def guild(self): """ Returns ------- `Guild` The guild (if applicable) this message was created in. """ return self.channel.guild @cached_property def member(self): """ Returns ------- `GuildMember` The guild member (if applicable) that sent this message. """ return self.channel.guild.get_member(self.author) @cached_property def channel(self): """ Returns ------- `Channel` The channel this message was created in. """ return self.client.state.channels.get(self.channel_id) def pin(self): """ Pins the message to the channel it was created in. """ self.channel.create_pin(self) def unpin(self): """ Unpins the message from the channel it was created in. """ self.channel.delete_pin(self) def reply(self, *args, **kwargs): """ Reply to this message (see `Channel.send_message`). Returns ------- `Message` The created message object. """ return self.channel.send_message(*args, **kwargs) def edit(self, *args, **kwargs): """ Edit this message. Args ---- content : str The new edited contents of the message. Returns ------- `Message` The edited message object. """ return self.client.api.channels_messages_modify( self.channel_id, self.id, *args, **kwargs) def delete(self): """ Delete this message. Returns ------- `Message` The deleted message object. """ return self.client.api.channels_messages_delete( self.channel_id, self.id) def get_reactors(self, emoji, *args, **kwargs): """ Returns an iterator which paginates the reactors for the given emoji. Returns ------- `Paginator`(`User`) An iterator which handles pagination of reactors. """ if isinstance(emoji, Emoji): emoji = emoji.to_string() return Paginator(self.client.api.channels_messages_reactions_get, 'after', self.channel_id, self.id, emoji, *args, **kwargs) def create_reaction(self, emoji): warnings.warn( 'Message.create_reaction will be deprecated soon, use Message.add_reaction', DeprecationWarning) return self.add_reaction(emoji) def add_reaction(self, emoji): """ Adds a reaction to the message. Parameters ---------- emoji : `Emoji`|str An emoji or string representing an emoji """ if isinstance(emoji, Emoji): emoji = emoji.to_string() self.client.api.channels_messages_reactions_create( self.channel_id, self.id, emoji) def delete_reaction(self, emoji, user=None): """ Deletes a reaction from the message. """ if isinstance(emoji, Emoji): emoji = emoji.to_string() if user: user = to_snowflake(user) self.client.api.channels_messages_reactions_delete( self.channel_id, self.id, emoji, user) def is_mentioned(self, entity): """ Returns ------- bool Whether the give entity was mentioned. """ entity = to_snowflake(entity) return entity in self.mentions or entity in self.mention_roles @cached_property def without_mentions(self, valid_only=False): """ Returns ------- str the message contents with all mentions removed. """ return self.replace_mentions(lambda u: '', lambda r: '', lambda c: '', nonexistant=not valid_only) @cached_property def with_proper_mentions(self): """ Returns ------- str The message with mentions replaced w/ their proper form. """ def replace_user(u): return u'@' + six.text_type(u) def replace_role(r): return u'@' + six.text_type(r) def replace_channel(c): return six.text_type(c) return self.replace_mentions(replace_user, replace_role, replace_channel) def replace_mentions(self, user_replace=None, role_replace=None, channel_replace=None, nonexistant=False): """ Replaces user and role mentions with the result of a given lambda/function. Args ---- user_replace : function A function taking a single argument, the user object mentioned, and returning a valid string. role_replace : function A function taking a single argument, the role ID mentioned, and returning a valid string. Returns ------- str The message contents with all valid mentions replaced. """ def replace(getter, func, match): oid = int(match.group(2)) obj = getter(oid) if obj or nonexistant: return func(obj or oid) or match.group(0) return match.group(0) content = self.content if user_replace: replace_user = functools.partial(replace, self.mentions.get, user_replace) content = re.sub('(<@!?([0-9]+)>)', replace_user, content) if role_replace: replace_role = functools.partial( replace, lambda v: (self.guild and self.guild.roles.get(v)), role_replace) content = re.sub('(<@&([0-9]+)>)', replace_role, content) if channel_replace: replace_channel = functools.partial(replace, self.client.state.channels.get, channel_replace) content = re.sub('(<#([0-9]+)>)', replace_channel, content) return content
class Guild(SlottedModel, Permissible): """ A guild object. Attributes ---------- id : snowflake The id of this guild. owner_id : snowflake The id of the owner. afk_channel_id : snowflake The id of the afk channel. embed_channel_id : snowflake The id of the embed channel. system_channel_id : snowflake The id of the system channel. name : str Guild's name. icon : str Guild's icon hash splash : str Guild's splash image hash region : str Voice region. afk_timeout : int Delay after which users are automatically moved to the afk channel. embed_enabled : bool Whether the guild's embed is enabled. verification_level : int The verification level used by the guild. mfa_level : int The MFA level used by the guild. features : list(str) Extra features enabled for this guild. members : dict(snowflake, :class:`GuildMember`) All of the guild's members. channels : dict(snowflake, :class:`disco.types.channel.Channel`) All of the guild's channels. roles : dict(snowflake, :class:`Role`) All of the guild's roles. emojis : dict(snowflake, :class:`GuildEmoji`) All of the guild's emojis. voice_states : dict(str, :class:`disco.types.voice.VoiceState`) All of the guild's voice states. """ id = Field(snowflake) owner_id = Field(snowflake) afk_channel_id = Field(snowflake) embed_channel_id = Field(snowflake) system_channel_id = Field(snowflake) name = Field(text) icon = Field(text) splash = Field(text) region = Field(text) afk_timeout = Field(int) embed_enabled = Field(bool) verification_level = Field(enum(VerificationLevel)) explicit_content_filter = Field(enum(ExplicitContentFilterLevel)) default_message_notifications = Field( enum(DefaultMessageNotificationsLevel)) mfa_level = Field(int) features = ListField(str) members = AutoDictField(GuildMember, 'id') channels = AutoDictField(Channel, 'id') roles = AutoDictField(Role, 'id') emojis = AutoDictField(GuildEmoji, 'id') voice_states = AutoDictField(VoiceState, 'session_id') member_count = Field(int) def __init__(self, *args, **kwargs): super(Guild, self).__init__(*args, **kwargs) self.attach(six.itervalues(self.channels), {'guild_id': self.id}) self.attach(six.itervalues(self.members), {'guild_id': self.id}) self.attach(six.itervalues(self.roles), {'guild_id': self.id}) self.attach(six.itervalues(self.emojis), {'guild_id': self.id}) self.attach(six.itervalues(self.voice_states), {'guild_id': self.id}) @cached_property def owner(self): return self.members.get(self.owner_id) def get_permissions(self, member): """ Get the permissions a user has in this guild. Returns ------- :class:`disco.types.permissions.PermissionValue` Computed permission value for the user. """ if not isinstance(member, GuildMember): member = self.get_member(member) # Owner has all permissions if self.owner_id == member.id: return PermissionValue(Permissions.ADMINISTRATOR) # Our value starts with the guilds default (@everyone) role permissions value = PermissionValue(self.roles.get(self.id).permissions) # Iterate over all roles the user has (plus the @everyone role) for role in map(self.roles.get, member.roles + [self.id]): value += role.permissions return value def get_voice_state(self, user): """ Attempt to get a voice state for a given user (who should be a member of this guild). Returns ------- :class:`disco.types.voice.VoiceState` The voice state for the user in this guild. """ user = to_snowflake(user) for state in six.itervalues(self.voice_states): if state.user_id == user: return state def get_member(self, user): """ Attempt to get a member from a given user. Returns ------- :class:`GuildMember` The guild member object for the given user. """ user = to_snowflake(user) if user not in self.members: try: self.members[user] = self.client.api.guilds_members_get( self.id, user) except APIException: return return self.members.get(user) def create_role(self, **kwargs): """ Create a new role. Returns ------- :class:`Role` The newly created role. """ return self.client.api.guilds_roles_create(self.id, **kwargs) def delete_role(self, role, **kwargs): """ Delete a role. """ self.client.api.guilds_roles_delete(self.id, to_snowflake(role), **kwargs) def update_role(self, role, **kwargs): if 'permissions' in kwargs and isinstance(kwargs['permissions'], PermissionValue): kwargs['permissions'] = kwargs['permissions'].value return self.client.api.guilds_roles_modify(self.id, to_snowflake(role), **kwargs) def request_guild_members(self, query=None, limit=0): self.client.gw.request_guild_members(self.id, query, limit) def sync(self): warnings.warn( 'Guild.sync has been deprecated in place of Guild.request_guild_members', DeprecationWarning) self.request_guild_members() def get_bans(self): return self.client.api.guilds_bans_list(self.id) def delete_ban(self, user, **kwargs): self.client.api.guilds_bans_delete(self.id, to_snowflake(user), **kwargs) def create_ban(self, user, *args, **kwargs): self.client.api.guilds_bans_create(self.id, to_snowflake(user), *args, **kwargs) def create_channel(self, *args, **kwargs): warnings.warn( 'Guild.create_channel will be deprecated soon, please use:' ' Guild.create_text_channel or Guild.create_category or Guild.create_voice_channel', DeprecationWarning) return self.client.api.guilds_channels_create(self.id, *args, **kwargs) def create_category(self, name, permission_overwrites=[], position=None, reason=None): """ Creates a category within the guild. """ return self.client.api.guilds_channels_create( self.id, ChannelType.GUILD_CATEGORY, name=name, permission_overwrites=permission_overwrites, position=position, reason=reason, ) def create_text_channel(self, name, permission_overwrites=[], parent_id=None, nsfw=None, position=None, reason=None): """ Creates a text channel within the guild. """ return self.client.api.guilds_channels_create( self.id, ChannelType.GUILD_TEXT, name=name, permission_overwrites=permission_overwrites, parent_id=parent_id, nsfw=nsfw, position=position, reason=reason, ) def create_voice_channel(self, name, permission_overwrites=[], parent_id=None, bitrate=None, user_limit=None, position=None, reason=None): """ Creates a voice channel within the guild. """ return self.client.api.guilds_channels_create( self.id, ChannelType.GUILD_VOICE, name=name, permission_overwrites=permission_overwrites, parent_id=parent_id, bitrate=bitrate, user_limit=user_limit, position=position, reason=None) def leave(self): return self.client.api.users_me_guilds_delete(self.id) def get_invites(self): return self.client.api.guilds_invites_list(self.id) def get_emojis(self): return self.client.api.guilds_emojis_list(self.id) def get_icon_url(self, fmt='webp', size=1024): if not self.icon: return '' return 'https://media.discordapp.net/icons/{}/{}.{}?size={}'.format( self.id, self.icon, fmt, size) def get_splash_url(self, fmt='webp', size=1024): if not self.splash: return '' return 'https://media.discordapp.net/splashes/{}/{}.{}?size={}'.format( self.id, self.splash, fmt, size) @property def icon_url(self): return self.get_icon_url() @property def splash_url(self): return self.get_splash_url() @property def system_channel(self): return self.channels.get(self.system_channel_id) @property def audit_log(self): return self.audit_log_iter() def audit_log_iter(self, **kwargs): return Paginator(self.client.api.guilds_auditlogs_list, 'before', self.id, **kwargs) def get_audit_log_entries(self, *args, **kwargs): return self.client.api.guilds_auditlogs_list(self.id, *args, **kwargs)
class Channel(SlottedModel, Permissible): """ Represents a Discord Channel. Attributes ---------- id : snowflake The channel ID. guild_id : Optional[snowflake] The guild id this channel is part of. name : str The channel's name. topic : str The channel's topic. position : int The channel's position. bitrate : int The channel's bitrate. recipients: list(:class:`disco.types.user.User`) Members of this channel (if this is a DM channel). type : :const:`ChannelType` The type of this channel. overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`) Channel permissions overwrites. """ id = Field(snowflake) guild_id = Field(snowflake) name = Field(text) topic = Field(text) last_message_id = Field(snowflake) position = Field(int) bitrate = Field(int) recipients = AutoDictField(User, 'id') nsfw = Field(bool) type = Field(enum(ChannelType)) overwrites = AutoDictField(PermissionOverwrite, 'id', alias='permission_overwrites') parent_id = Field(snowflake) def __init__(self, *args, **kwargs): super(Channel, self).__init__(*args, **kwargs) self.after_load() def after_load(self): # TODO: hackfix self.attach(six.itervalues(self.overwrites), { 'channel_id': self.id, 'channel': self }) def __str__(self): return u'#{}'.format(self.name) if self.name else six.text_type( self.id) def __repr__(self): return u'<Channel {} ({})>'.format(self.id, self) def get_permissions(self, user): """ Get the permissions a user has in the channel. Returns ------- :class:`disco.types.permissions.PermissionValue` Computed permission value for the user. """ if not self.guild_id: return Permissions.ADMINISTRATOR member = self.guild.get_member(user) base = self.guild.get_permissions(member) ow_everyone = self.overwrites.get(self.guild_id) if ow_everyone: base += ow_everyone.compiled for role_id in member.roles: ow_role = self.overwrites.get(role_id) if ow_role: base += ow_role.compiled ow_member = self.overwrites.get(member.user.id) if ow_member: base += ow_member.compiled return base @property def mention(self): return '<#{}>'.format(self.id) @property def is_guild(self): """ Whether this channel belongs to a guild. """ return self.type in (ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE, ChannelType.GUILD_CATEGORY) @property def is_dm(self): """ Whether this channel is a DM (does not belong to a guild). """ return self.type in (ChannelType.DM, ChannelType.GROUP_DM) @property def is_nsfw(self): """ Whether this channel is an NSFW channel. """ return self.type == ChannelType.GUILD_TEXT and ( self.nsfw or NSFW_RE.match(self.name)) @property def is_voice(self): """ Whether this channel supports voice. """ return self.type in (ChannelType.GUILD_VOICE, ChannelType.GROUP_DM) @property def messages(self): """ A default `MessageIterator` for the channel, can be used to quickly and easily iterate over the channels entire message history. For more control, use `Channel.messages_iter`. """ return self.messages_iter() @cached_property def guild(self): """ Guild this channel belongs to (or None if not applicable). """ return self.client.state.guilds.get(self.guild_id) @cached_property def parent(self): """ Parent this channel belongs to (or None if not applicable). """ return self.guild.channels.get(self.parent_id) def messages_iter(self, **kwargs): """ Creates a new `MessageIterator` for the channel with the given keyword arguments. """ return MessageIterator(self.client, self, **kwargs) def get_message(self, message): """ Attempts to fetch and return a `Message` from the message object or id. Returns ------- `Message` The fetched message """ return self.client.api.channels_messages_get(self.id, to_snowflake(message)) def get_invites(self): """ Returns ------- list(`Invite`) Returns a list of all invites for this channel. """ return self.client.api.channels_invites_list(self.id) def create_invite(self, *args, **kwargs): """ Attempts to create a new invite with the given arguments. For more information see `Invite.create_for_channel`. Returns ------- `Invite` """ from disco.types.invite import Invite return Invite.create_for_channel(self, *args, **kwargs) def get_pins(self): """ Returns ------- list(`Message`) Returns a list of all pinned messages for this channel. """ return self.client.api.channels_pins_list(self.id) def create_pin(self, message): """ Pins the given message to the channel. Params ------ message : `Message`|snowflake The message or message ID to pin. """ self.client.api.channels_pins_create(self.id, to_snowflake(message)) def delete_pin(self, message): """ Unpins the given message from the channel. Params ------ message : `Message`|snowflake The message or message ID to pin. """ self.client.api.channels_pins_delete(self.id, to_snowflake(message)) def get_webhooks(self): """ Returns ------- list(`Webhook`) Returns a list of all webhooks for this channel. """ return self.client.api.channels_webhooks_list(self.id) def create_webhook(self, *args, **kwargs): """ Creates a webhook for this channel. See `APIClient.channels_webhooks_create` for more information. Returns ------- `Webhook` The created webhook. """ return self.client.api.channels_webhooks_create( self.id, *args, **kwargs) def send_message(self, *args, **kwargs): """ Send a message to this channel. See `APIClient.channels_messages_create` for more information. Returns ------- `disco.types.message.Message` The created message. """ return self.client.api.channels_messages_create( self.id, *args, **kwargs) def send_typing(self): """ Sends a typing event to this channel. See `APIClient.channels_typing` for more information. """ self.client.api.channels_typing(self.id) def connect(self, *args, **kwargs): """ Connect to this channel over voice. """ from disco.voice.client import VoiceClient assert self.is_voice, 'Channel must support voice to connect' vc = VoiceClient(self) vc.connect(*args, **kwargs) return vc def create_overwrite(self, *args, **kwargs): """ Creates a `PermissionOverwrite` for this channel. See `PermissionOverwrite.create_for_channel` for more information. """ return PermissionOverwrite.create_for_channel(self, *args, **kwargs) def delete_message(self, message): """ Deletes a single message from this channel. Args ---- message : snowflake|`Message` The message to delete. """ self.client.api.channels_messages_delete(self.id, to_snowflake(message)) @one_or_many def delete_messages(self, messages): """ Deletes a set of messages using the correct API route based on the number of messages passed. Args ---- messages : list(snowflake|`Message`) List of messages (or message ids) to delete. All messages must originate from this channel. """ message_ids = list(map(to_snowflake, messages)) if not message_ids: return if self.can(self.client.state.me, Permissions.MANAGE_MESSAGES) and len(messages) > 2: for chunk in chunks(message_ids, 100): self.client.api.channels_messages_delete_bulk(self.id, chunk) else: for msg in messages: self.delete_message(msg) def delete(self, **kwargs): assert (self.is_dm or self.guild.can( self.client.state.me, Permissions.MANAGE_CHANNELS)), 'Invalid Permissions' self.client.api.channels_delete(self.id, **kwargs) def close(self): """ Closes a DM channel. This is intended as a safer version of `delete`, enforcing that the channel is actually a DM. """ assert self.is_dm, 'Cannot close non-DM channel' self.delete() def set_topic(self, topic, reason=None): """ Sets the channels topic. """ return self.client.api.channels_modify(self.id, topic=topic, reason=reason) def set_name(self, name, reason=None): """ Sets the channels name. """ return self.client.api.channels_modify(self.id, name=name, reason=reason) def set_position(self, position, reason=None): """ Sets the channels position. """ return self.client.api.channels_modify(self.id, position=position, reason=reason) def set_nsfw(self, value, reason=None): """ Sets whether the channel is NSFW. """ assert (self.type == ChannelType.GUILD_TEXT) return self.client.api.channels_modify(self.id, nsfw=value, reason=reason) def set_bitrate(self, bitrate, reason=None): """ Sets the channels bitrate. """ assert (self.is_voice) return self.client.api.channels_modify(self.id, bitrate=bitrate, reason=reason) def set_user_limit(self, user_limit, reason=None): """ Sets the channels user limit. """ assert (self.is_voice) return self.client.api.channels_modify(self.id, user_limit=user_limit, reason=reason) def set_parent(self, parent, reason=None): """ Sets the channels parent. """ assert (self.is_guild) return self.client.api.channels_modify( self.id, parent_id=to_snowflake(parent) if parent else parent, reason=reason) def create_text_channel(self, *args, **kwargs): """ Creates a sub-text-channel in this category. See `Guild.create_text_channel` for arguments and more information. """ if self.type != ChannelType.GUILD_CATEGORY: raise ValueError( 'Cannot create a sub-channel on a non-category channel') kwargs['parent_id'] = self.id return self.guild.create_text_channel(*args, **kwargs) def create_voice_channel(self, *args, **kwargs): """ Creates a sub-voice-channel in this category. See `Guild.create_voice_channel` for arguments and more information. """ if self.type != ChannelType.GUILD_CATEGORY: raise ValueError( 'Cannot create a sub-channel on a non-category channel') kwargs['parent_id'] = self.id return self.guild.create_voice_channel(*args, **kwargs)
class Channel(SlottedModel, Permissible): """ Represents a Discord Channel. Attributes ---------- id : snowflake The channel ID. guild_id : Optional[snowflake] The guild id this channel is part of. name : str The channel's name. topic : str The channel's topic. position : int The channel's position. bitrate : int The channel's bitrate. recipients: list(:class:`disco.types.user.User`) Members of this channel (if this is a DM channel). type : :const:`ChannelType` The type of this channel. overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`) Channel permissions overwrites. """ id = Field(snowflake) guild_id = Field(snowflake) name = Field(text) topic = Field(text) last_message_id = Field(snowflake) position = Field(int) bitrate = Field(int) recipients = AutoDictField(User, 'id') type = Field(enum(ChannelType)) overwrites = AutoDictField(PermissionOverwrite, 'id', alias='permission_overwrites') def __init__(self, *args, **kwargs): super(Channel, self).__init__(*args, **kwargs) self.after_load() def after_load(self): # TODO: hackfix self.attach(six.itervalues(self.overwrites), { 'channel_id': self.id, 'channel': self }) def __str__(self): return u'#{}'.format(self.name) def __repr__(self): return u'<Channel {} ({})>'.format(self.id, self) def get_permissions(self, user): """ Get the permissions a user has in the channel. Returns ------- :class:`disco.types.permissions.PermissionValue` Computed permission value for the user. """ if not self.guild_id: return Permissions.ADMINISTRATOR member = self.guild.get_member(user) base = self.guild.get_permissions(member) for ow in six.itervalues(self.overwrites): if ow.id != user.id and ow.id not in member.roles: continue base -= ow.deny base += ow.allow return base @property def mention(self): return '<#{}>'.format(self.id) @property def is_guild(self): """ Whether this channel belongs to a guild. """ return self.type in (ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE) @property def is_dm(self): """ Whether this channel is a DM (does not belong to a guild). """ return self.type in (ChannelType.DM, ChannelType.GROUP_DM) @property def is_voice(self): """ Whether this channel supports voice. """ return self.type in (ChannelType.GUILD_VOICE, ChannelType.GROUP_DM) @property def messages(self): """ a default :class:`MessageIterator` for the channel. """ return self.messages_iter() @cached_property def guild(self): """ Guild this channel belongs to (if relevant). """ return self.client.state.guilds.get(self.guild_id) def messages_iter(self, **kwargs): """ Creates a new :class:`MessageIterator` for the channel with the given keyword arguments. """ return MessageIterator(self.client, self, **kwargs) def get_message(self, message): return self.client.api.channels_messages_get(self.id, to_snowflake(message)) def get_invites(self): """ Returns ------- list(:class:`disco.types.invite.Invite`) All invites for this channel. """ return self.client.api.channels_invites_list(self.id) def create_invite(self, *args, **kwargs): from disco.types.invite import Invite return Invite.create(self, *args, **kwargs) def get_pins(self): """ Returns ------- list(:class:`disco.types.message.Message`) All pinned messages for this channel. """ return self.client.api.channels_pins_list(self.id) def create_pin(self, message): self.client.api.channels_pins_create(self.id, to_snowflake(message)) def delete_pin(self, message): self.client.api.channels_pins_delete(self.id, to_snowflake(message)) def get_webhooks(self): return self.client.api.channels_webhooks_list(self.id) def create_webhook(self, name=None, avatar=None): return self.client.api.channels_webhooks_create(self.id, name, avatar) def send_typing(self): """ Indicate typing on a channel for 10 seconds """ self.client.api.channels_typing(self.id) def send_message(self, content, nonce=None, tts=False, attachment=None, embed=None): """ Send a message in this channel. Parameters ---------- content : str The message contents to send. nonce : Optional[snowflake] The nonce to attach to the message. tts : Optional[bool] Whether this is a TTS message. Returns ------- :class:`disco.types.message.Message` The created message. """ return self.client.api.channels_messages_create( self.id, content, nonce, tts, attachment, embed) def connect(self, *args, **kwargs): """ Connect to this channel over voice. """ assert self.is_voice, 'Channel must support voice to connect' vc = VoiceClient(self) vc.connect(*args, **kwargs) return vc def create_overwrite(self, *args, **kwargs): return PermissionOverwrite.create(self, *args, **kwargs) def delete_message(self, message): """ Deletes a single message from this channel. Args ---- message : snowflake|:class:`disco.types.message.Message` The message to delete. """ self.client.api.channels_messages_delete(self.id, to_snowflake(message)) @one_or_many def delete_messages(self, messages): """ Deletes a set of messages using the correct API route based on the number of messages passed. Args ---- messages : list[snowflake|:class:`disco.types.message.Message`] List of messages (or message ids) to delete. All messages must originate from this channel. """ message_ids = list(map(to_snowflake, messages)) if not message_ids: return if self.can(self.client.state.me, Permissions.MANAGE_MESSAGES) and len(messages) > 2: for chunk in chunks(message_ids, 100): self.client.api.channels_messages_delete_bulk(self.id, chunk) else: for msg in messages: self.delete_message(msg) def delete(self): assert (self.is_dm or self.guild.can( self.client.state.me, Permissions.MANAGE_GUILD)), 'Invalid Permissions' self.client.api.channels_delete(self.id) def close(self): """ Closes a DM channel. This is intended as a safer version of `delete`, enforcing that the channel is actually a DM. """ assert self.is_dm, 'Cannot close non-DM channel' self.delete()