def get_category_ch_update_embed( cls, before: discord.CategoryChannel, after: discord.CategoryChannel) -> Optional[discord.Embed]: """Constructs the embed for the 'on_guild_channel_update' event for categories.""" embed = discord.Embed( title="Category Updated", description=f"Update info for category <#{after.id}>", timestamp=datetime.utcnow(), color=discord.Color.blurple()) embed.set_footer(text=f"Category ID: {after.id}") if before.name != after.name: embed.add_field( name="Name Changed:", value= f"{before_txt}**{before.name}**\n{now_txt}**{after.name}**") if before.is_nsfw() != after.is_nsfw(): before_nsfw = "Yes" if before.is_nsfw() else "No" after_nsfw = "Yes" if after.is_nsfw() else "No" embed.add_field( name="NSFW Status Changed:", value= f"{before_txt}**{before_nsfw}**\n{now_txt}**{after_nsfw}**") if before.overwrites != after.overwrites: embed = cls.determine_changed_overrides(embed, before, after) return embed if len(embed.fields) > 0 else None
async def addcategory(self, ctx: commands.Context, category: discord.CategoryChannel, *, time: str): """Add a category cooldown. Time format can be the following: - `1 hour` - `1h` - `1 hour 5 minutes` - `2h30m10s` """ if not category.permissions_for(ctx.me).manage_messages: await ctx.send( "I require the 'Manage messages' permission to let you use this command." ) cooldown_categories = await self.config.guild(ctx.guild ).cooldown_categories() if str(category.id) in cooldown_categories: await ctx.send( "This category is already added to the cooldown. If you want to edit" " the cooldown time, use `{prefix}slow channel edit`.".format( prefix=ctx.clean_prefix)) return time = self._return_time(time) if time: await self._update_category_data(ctx, category, time) await ctx.send( "Done! Every {category}'s channels are now set at 1 message every " "{time} seconds. Channels from this category are moderated but new channel " "won't be moderated. If you add channels that you want to also add cooldown" ", use `{prefix}slow category update`".format( category=category.name, time=time, prefix=ctx.clean_prefix)) else: await ctx.send("Your time is not correct to me.")
async def category(self, ctx, category: discord.CategoryChannel): """Set the category to create ticket channels under.""" if not category.permissions_for(ctx.guild.me).manage_channels: await ctx.send( 'I require "Manage Channels" permissions in that category to execute that command.' ) return await self.config.guild(ctx.guild).category.set(category.id) await ctx.send(f"Ticket channels will now be created in the {category.name} category")
def bot_can_manage_category(category: discord.CategoryChannel) -> Result[bool]: """ Checks if the bot can manage channels in the specified category. :param category: Discord server category. :return: Result of the check, with error message if not successful. """ if category.permissions_for(member=category.guild.me).manage_channels: return Result(success=True, value=True, error=None) return Result( success=False, value=False, error="The bot does not have permissions to manage channels in that category." )
async def archive_category(self, ctx, category: discord.CategoryChannel): """Set the category to move closed ticket channels to.""" if not category.permissions_for(ctx.guild.me).manage_channels: await ctx.send( 'I require "Manage Channels" permissions in that category to execute that command.' ) return async with self.config.guild(ctx.guild).archive() as data: data["category"] = category.id await ctx.send( f"Closed ticket channels will now be moved to the {category.name} category, " "if Archive mode is enabled.")
async def gdc_setup( self, ctx: commands.Context, team: HockeyTeams, category: discord.CategoryChannel = None, delete_gdc: bool = True, ) -> None: """ Setup game day channels for a single team or all teams Required parameters: `<team>` must use quotes if a space is in the name will search for partial team name Optional Parameters: `[category]` You must use the category ID or use this command in a channel already in the desired category `[delete_gdc=True]` will tell the bot whether or not to delete game day channels automatically must be either `True` or `False`. Defaults to `True` if not provided. """ guild = ctx.message.guild if team is None: return await ctx.send(_("You must provide a valid current team.")) if category is None and ctx.channel.category is not None: category = guild.get_channel(ctx.channel.category_id) else: return await ctx.send( _("You must specify a channel category for game day channels to be created under." )) if not category.permissions_for(guild.me).manage_channels: await ctx.send(_("I don't have manage channels permission!")) return await self.config.guild(guild).category.set(category.id) await self.config.guild(guild).gdc_team.set(team) await self.config.guild(guild).delete_gdc.set(delete_gdc) await self.config.guild(guild).create_channels.set(True) if team.lower() != "all": await self.create_gdc(guild) else: game_list = await Game.get_games(session=self.session) for game in game_list: await self.create_gdc(guild, game) await ctx.send( _("Game Day Channels for ") + team + _(" setup in ") + category.name)
async def setup_auto_pickems(self, ctx: commands.Context, category: discord.CategoryChannel = None): """ Sets up automatically created pickems channels every week. `[category]` the channel category where pickems channels will be created. This must be the category ID. If not provided this will use the current channels category if it exists. """ if category is None and not ctx.channel.category: return await ctx.send(_("A channel category is required.")) elif category is None and ctx.channel.category is not None: category = ctx.channel.category if not category.permissions_for(ctx.me).manage_channels: await ctx.send(_("I don't have manage channels permission!")) return await self.config.guild(ctx.guild).pickems_category.set(category.id) existing_channels = await self.config.guild(ctx.guild ).pickems_channels() if existing_channels: cant_delete = [] for chan_id in existing_channels: channel = ctx.guild.get_channel(chan_id) if channel: try: await channel.delete() except discord.errors.Forbidden: cant_delete.append(chan_id) await self.config.guild(ctx.guild).pickems_channels.clear() if cant_delete: chans = humanize_list([f"<#{_id}>" for _id in cant_delete]) await ctx.send( _("I tried to delete the following channels without success:\n{chans}" ).format(chans=chans)) async with self.pickems_save_lock: log.debug("Locking save") await Pickems.create_weekly_pickems_pages(self.bot, [ctx.guild], Game) await ctx.send( _("I will now automatically create pickems pages every Sunday."))
def test_server_info_command(time_since_patch, cog, ctx, moderator_role): time_since_patch.return_value = '2 days ago' ctx.guild.created_at = datetime(2001, 1, 1) ctx.guild.features = ('lemons', 'apples') ctx.guild.region = 'The Moon' ctx.guild.roles = [moderator_role] ctx.guild.channels = [ TextChannel(state={}, guild=ctx.guild, data={ 'id': 42, 'name': 'lemons-offering', 'position': 22, 'type': 'text' }), CategoryChannel(state={}, guild=ctx.guild, data={ 'id': 5125, 'name': 'the-lemon-collection', 'position': 22, 'type': 'category' }), VoiceChannel(state={}, guild=ctx.guild, data={ 'id': 15290, 'name': 'listen-to-lemon', 'position': 22, 'type': 'voice' }) ] ctx.guild.members = [ member('online'), member('online'), member('idle'), member('dnd'), member('dnd'), member('dnd'), member('dnd'), member('offline'), member('offline'), member('offline') ] ctx.guild.member_count = 1_234 ctx.guild.icon_url = 'a-lemon.png' coroutine = cog.server_info.callback(cog, ctx) assert asyncio.run(coroutine) is None # no rval time_since_patch.assert_called_once_with(ctx.guild.created_at, precision='days') _, kwargs = ctx.send.call_args embed = kwargs.pop('embed') assert embed.colour == Colour.blurple() assert embed.description == textwrap.dedent(f""" **Server information** Created: {time_since_patch.return_value} Voice region: {ctx.guild.region} Features: {', '.join(ctx.guild.features)} **Counts** Members: {ctx.guild.member_count:,} Roles: {len(ctx.guild.roles)} Text: 1 Voice: 1 Channel categories: 1 **Members** {Emojis.status_online} 2 {Emojis.status_idle} 1 {Emojis.status_dnd} 4 {Emojis.status_offline} 3 """) assert embed.thumbnail.url == 'a-lemon.png'
async def check_perms_source_dest( self, autoroom_source: discord.VoiceChannel, category_dest: discord.CategoryChannel, detailed=False, ): """Check if the permissions in an AutoRoom Source and a destination category are sufficient.""" source = autoroom_source.permissions_for(autoroom_source.guild.me) dest = category_dest.permissions_for(category_dest.guild.me) # Check the basics result = (source.move_members and source.view_channel and source.connect and dest.view_channel and dest.manage_channels and dest.manage_messages and dest.connect and dest.move_members) if not detailed and not result: return False # Check the @everyone overwrites if they exist override_section = None if (autoroom_source.overwrites and autoroom_source.guild.default_role in autoroom_source.overwrites): overwrites = autoroom_source.overwrites[ autoroom_source.guild.default_role] # Skip permissions we can't give (manage_roles) or are required to be True (view_channel/connect) overwrites.update(view_channel=None, manage_roles=None, connect=None) for overwrite in overwrites: if overwrite[1] is not None: check_result = getattr(dest, overwrite[0]) if detailed: result = result and check_result if not override_section: override_section = SettingDisplay( f"Optional @everyone Permissions") override_section.add( overwrite[0].capitalize().replace("_", " "), check_result) elif not check_result: return False if not detailed: return True source_section = SettingDisplay( f"Required on Source: {autoroom_source.name}") source_section.add("Move members", source.move_members) source_section.add("View channels", source.view_channel) source_section.add("Connect", source.connect) dest_section = SettingDisplay( f"Required on Destination: {category_dest.name}") dest_section.add("View channels", dest.view_channel) dest_section.add("Manage channels", dest.manage_channels) dest_section.add("Manage messages", dest.manage_messages) dest_section.add("Connect", dest.connect) dest_section.add("Move members", dest.move_members) autoroom_sections = [dest_section] if override_section: autoroom_sections.append(override_section) return result, source_section.display(*autoroom_sections)
def _bot_can_manage_category(self, category: discord.CategoryChannel) -> bool: return category.permissions_for(category.guild.me).manage_channels
def check_perms_source_dest( self, autoroom_source: discord.VoiceChannel, category_dest: discord.CategoryChannel, with_manage_roles_guild=False, with_text_channel=False, with_optional_clone_perms=False, split_required_optional_check=False, detailed=False, ): """Check if the permissions in an AutoRoom Source and a destination category are sufficient.""" source = autoroom_source.permissions_for(autoroom_source.guild.me) dest = category_dest.permissions_for(category_dest.guild.me) result_required = True result_optional = True # Required for perm_name in self.perms_bot_source: result_required = result_required and getattr(source, perm_name) for perm_name in self.perms_bot_dest: result_required = result_required and getattr(dest, perm_name) if with_manage_roles_guild: result_required = ( result_required and category_dest.guild.me.guild_permissions.manage_roles ) # Optional if with_text_channel: for perm_name in self.perms_bot_dest_text: result_optional = result_optional and getattr(dest, perm_name) clone_section = None if with_optional_clone_perms: if detailed: clone_result, clone_section = self._check_perms_source_dest_optional( autoroom_source, dest, detailed=True ) else: clone_result = self._check_perms_source_dest_optional( autoroom_source, dest ) result_optional = result_optional and clone_result result = result_required and result_optional if not detailed: if split_required_optional_check: return result_required, result_optional else: return result source_section = SettingDisplay("Required on Source Voice Channel") for perm_name in self.perms_bot_source: source_section.add( perm_name.capitalize().replace("_", " "), getattr(source, perm_name) ) dest_section = SettingDisplay("Required on Destination Category") for perm_name in self.perms_bot_dest: dest_section.add( perm_name.capitalize().replace("_", " "), getattr(dest, perm_name) ) autoroom_sections = [dest_section] if with_manage_roles_guild: guild_section = SettingDisplay("Required in Guild") guild_section.add( "Manage roles", category_dest.guild.me.guild_permissions.manage_roles ) autoroom_sections.append(guild_section) if with_text_channel: text_section = SettingDisplay( "Optional on Destination Category (for text channel)" ) for perm_name in self.perms_bot_dest_text: text_section.add( perm_name.capitalize().replace("_", " "), getattr(dest, perm_name) ) autoroom_sections.append(text_section) if clone_section: autoroom_sections.append(clone_section) status_emoji = "\N{NO ENTRY SIGN}" if result: status_emoji = "\N{WHITE HEAVY CHECK MARK}" elif result_required: status_emoji = "\N{WARNING SIGN}\N{VARIATION SELECTOR-16}" result_str = ( f"\n{status_emoji} Source VC: {autoroom_source.mention} -> Dest Category: {category_dest.mention}" "\n" f"{source_section.display(*autoroom_sections)}" ) if split_required_optional_check: return result_required, result_optional, result_str else: return result, result_str