async def message_count(self, ctx, user: UserConv, *exclude: TextChannelConv): """ Gets the total number of messages this user has sent in the guild. Text channels to exclude from the search can be added as optional arguments. """ message_count, edited_count, deleted_count = self.sql.message_count( ctx.guild, user, exclude ) descr = StringBuilder() descr.writeln( f"Found `{message_count}` message{plural(message_count)} from {user.mention}." ) if message_count: descr.writeln( f"Of those, `{edited_count}` (or `{edited_count / message_count * 100:.2f}%`) are edited,\n" f"and `{deleted_count}` (or `{deleted_count / message_count * 100:.2f}%`) are deleted." ) if hasattr(user, "joined_at"): descr.writeln() descr.writeln( f"They have been a member for {fancy_timedelta(user.joined_at)}." ) embed = discord.Embed(colour=discord.Colour.teal()) embed.description = str(descr) await ctx.send(embed=embed)
async def role_show(self, ctx): """ Shows all self-assignable roles. """ await self.check_channel(ctx) assignable_roles = sorted(self.bot.sql.roles.get_assignable_roles( ctx.guild), key=lambda r: r.name) if not assignable_roles: prefix = self.bot.prefix(ctx.guild) embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No self-assignable roles") embed.description = ( f"Moderators can use the `{prefix}role joinable/unjoinable` " "commands to change this list!") await ctx.send(embed=embed) return embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Self-assignable roles") descr = StringBuilder(sep=", ") for role in assignable_roles: descr.write(role.mention) embed.description = str(descr) await ctx.send(embed=embed)
async def log_dm_show(self, ctx, user: UserConv = None): """ Displays current journal mounts for a user. """ if user is None: user = self.bot.get_user(ctx.author.id) else: user = self.bot.get_user(user.id) outputs = self.bot.sql.journal.get_journals_on_user(user) outputs = sorted(outputs, key=lambda out: out.path) attributes = [] descr = StringBuilder(f"{user.mention}:\n\n") for output in outputs: if not output.settings.recursive: attributes.append("exact path") attr_str = f'({", ".join(attributes)})' if attributes else "" descr.writeln(f"- `{output.path}` {attr_str}") attributes.clear() if outputs: embed = discord.Embed(colour=discord.Colour.teal(), description=str(descr)) embed.set_author(name="Current journal outputs") else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No journal outputs!") await ctx.send(embed=embed)
async def ufind(self, ctx, *, name: str): """ Perform a fuzzy search to find users who match the given name. They are listed with the closest matches first. Users not in this guild are marked with a \N{GLOBE WITH MERIDIANS}. """ logger.info("Running ufind on '%s'", name) users = await similar_users(self.bot, name) users_not_in_guild = (set( member.id for member in ctx.guild.members) if ctx.guild else set()) descr = StringBuilder() for user in users: extra = "\N{GLOBE WITH MERIDIANS}" if user in users_not_in_guild else "" descr.writeln( f"- {user.mention} {user.name}#{user.discriminator} {extra}") if users: embed = discord.Embed(description=str(descr), colour=discord.Colour.teal()) else: embed = discord.Embed(description="**No users found!**", colour=discord.Colour.red()) await ctx.send(embed=embed)
async def pingable_show(self, ctx): """ Shows all channels where a role is pingable. """ logger.info( "Displaying pingable channels and roles in guild '%s' (%d)", ctx.guild.name, ctx.guild.id, ) # r[0] == current channel. channel_role = sorted( self.bot.sql.roles.get_pingable_role_channels(ctx.guild), key=lambda r: r[0].name, ) if not channel_role: prefix = self.bot.prefix(ctx.guild) embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No pingable roles in this guild") embed.description = ( f"Moderators can use the `{prefix}role pingable/unpingable` " "commands to change this list!") await ctx.send(embed=embed) return embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Pingable roles (channel, role)") descr = StringBuilder(sep="\n") for channel, role in channel_role: descr.write(f"{channel.mention}: {role.mention}") embed.description = str(descr) await ctx.send(embed=embed)
async def channel_set(self, ctx, *channels: TextChannelConv): """ Overwrites the channel(s) in the restricted role channel list to exactly this. """ logger.info( "Setting channels to be used for role commands in guild '%s' (%d): [%s]", ctx.guild.name, ctx.guild.id, ", ".join(channel.name for channel in channels), ) if not channels: raise CommandFailed() # Write new channel list to database with self.bot.sql.transaction(): self.bot.sql.roles.remove_all_role_command_channels(ctx.guild) for channel in channels: self.bot.sql.roles.add_role_command_channel(ctx.guild, channel) # Send response embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Set channels to be used for adding roles") descr = StringBuilder(sep=", ") for channel in channels: descr.write(channel.mention) embed.description = str(descr) await ctx.send(embed=embed) # Send journal event self.channel_journal(ctx.guild)
async def role_unjoinable(self, ctx, *roles: RoleConv): """ Allows a moderator to remove roles from the self-assignable group. """ logger.info( "Removing joinable roles for guild '%s' (%d): [%s]", ctx.guild.name, ctx.guild.id, ", ".join(role.name for role in roles), ) if not roles: raise CommandFailed() # Remove roles from database with self.bot.sql.transaction(): for role in roles: self.bot.sql.roles.remove_assignable_role(ctx.guild, role) # Send response embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Made roles not joinable") descr = StringBuilder(sep=", ") for role in roles: descr.write(role.mention) embed.description = str(descr) await ctx.send(embed=embed) # Send journal event content = f"Roles were set as not joinable: {self.str_roles(roles)}" self.journal.send("joinable/remove", ctx.guild, content, icon="role", roles=roles)
async def log_show(self, ctx, *channels: TextChannelConv): """ Displays current settings for this guild. If channels are provided, then only outputs for those channels are fetched. """ if not channels: channels = ctx.guild.channels outputs = self.bot.sql.journal.get_journals_on_channels(*channels) outputs = sorted(outputs, key=lambda out: out.path) attributes = [] descr = StringBuilder() for output in outputs: if not output.settings.recursive: attributes.append("exact path") attr_str = f'({", ".join(attributes)})' if attributes else "" descr.writeln( f"- `{output.path}` mounted at {output.sink.mention} {attr_str}" ) attributes.clear() if outputs: embed = discord.Embed(colour=discord.Colour.teal(), description=str(descr)) embed.set_author(name="Current journal outputs") else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No journal outputs!") await ctx.send(embed=embed)
async def log_dump(self, ctx, *, condition: str = None): """ Dump previous journal events that match the given conditions to JSON. The condition is a Python expression with no variables but the following attributes: path: str ppath: PurePath guild: discord.Guild content: str attributes: dict """ events, error_embeds = self.log_filter(ctx.guild, condition, 50) if error_embeds: await ctx.send(embed=error_embeds[0]) buffer = StringBuilder() obj = [event.to_dict() for event in reversed(events)] json.dump(obj, buffer, ensure_ascii=True) file = discord.File(buffer.bytes_io(), filename="journal-events.json") embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.description = ( "Uploaded journal events as JSON, see attached file. " "\N{WHITE UP POINTING BACKHAND INDEX}" ) await ctx.send(embed=embed, file=file)
async def role_joinable(self, ctx, *roles: RoleConv): """ Allows a moderator to add roles to the self-assignable group. """ logger.info( "Adding joinable roles for guild '%s' (%d): [%s]", ctx.guild.name, ctx.guild.id, ", ".join(role.name for role in roles), ) if not roles: raise CommandFailed() # Get special roles special_roles = self.bot.sql.settings.get_special_roles(ctx.guild) # Ensure none of the roles grant any permissions for role in roles: embed = permissions.elevated_role_embed(ctx.guild, role, "error") if embed is not None: raise ManualCheckFailure(embed=embed) for attr in ("member", "guest", "mute", "jail"): if role == getattr(special_roles, attr): embed = discord.Embed(colour=discord.Colour.red()) embed.set_author(name="Cannot add role as assignable") embed.description = ( f"{role.mention} cannot be self-assignable, " f"it is already used as the **{attr}** role!") raise ManualCheckFailure(embed=embed) # Get roles that are already assignable assignable_roles = self.bot.sql.roles.get_assignable_roles(ctx.guild) # Add roles to database with self.bot.sql.transaction(): for role in roles: if role not in assignable_roles: self.bot.sql.roles.add_assignable_role(ctx.guild, role) # Send response embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Made roles joinable") descr = StringBuilder(sep=", ") for role in roles: descr.write(role.mention) embed.description = str(descr) await ctx.send(embed=embed) # Send journal event content = f"Roles were set as joinable: {self.str_roles(roles)}" self.journal.send("joinable/add", ctx.guild, content, icon="role", roles=roles)
async def reapply_show(self, ctx): """ Lists all roles that are reappliable. Reappliable roles, in addition to all punishment and self-assignable roles, are automatically reapplied when the member rejoins the guild. """ reapply_roles = self.bot.sql.settings.get_reapply_roles(ctx.guild) special_roles = self.bot.sql.settings.get_special_roles(ctx.guild) embed = discord.Embed(colour=discord.Colour.dark_teal()) descr = StringBuilder(sep=", ") has_roles = False # Manually set for role in sorted(reapply_roles, key=lambda r: r.name): descr.write(role.mention) if descr: embed.add_field(name="Manually designated", value=str(descr)) has_roles = True else: embed.add_field(name="Manually designated", value="(none)") # Punishment roles descr.clear() if special_roles.mute_role is not None: descr.write(special_roles.mute_role.mention) if special_roles.jail_role is not None: descr.write(special_roles.jail_role.mention) if descr: embed.add_field(name="Punishment roles", value=str(descr)) has_roles = True # Self-assignable roles if "SelfAssignableRoles" in self.bot.cogs: assignable_roles = self.bot.sql.roles.get_assignable_roles( ctx.guild) if assignable_roles: embed.add_field( name="Self-assignable roles", value=", ".join(role.mention for role in sorted(assignable_roles, key=lambda r: r.name)), ) has_roles = True # Send final embed if has_roles: embed.title = "\N{MILITARY MEDAL} Roles which are automatically reapplied" else: embed.colour = discord.Colour.dark_purple() await ctx.send(embed=embed)
def elevated_role_embed(guild, role, level): """ Takes the result of elevated_role_perms() and produces an embed listing the permissions. The parameter level must be 'warning' or 'error'. """ elevated = elevated_role_perms(guild, role) if not elevated: return None if level == "warning": colour = discord.Colour.gold() icon = "\N{WARNING SIGN}" elif level == "error": colour = discord.Colour.red() icon = "\N{NO ENTRY}" else: raise ValueError(f"Unknown severity level: '{level}'") embed = discord.Embed() embed.colour = colour embed.title = f"{icon} Role gives elevated permissions" descr = StringBuilder() for location, perm in elevated: perm = perm.replace("_", " ").title() if isinstance(location, discord.Guild): descr.writeln(f"- {perm}") elif isinstance(location, discord.TextChannel): descr.writeln(f"- {perm} in {location.mention}") else: descr.writeln(f"- {perm} in {location.name}") embed.description = str(descr) return embed
async def role_unpingable(self, ctx, role: RoleConv, *channels: TextChannelConv): logger.info( "Making role '%s' not pingable in guild '%s' (%d), channel(s) [%s]", role.name, ctx.guild.name, ctx.guild.id, self.str_channels(channels), ) if not channels: raise CommandFailed() # See role_pingable for an explanation channel_role = zip( *self.bot.sql.roles.get_pingable_role_channels(ctx.guild)) pingable_channels = next(channel_role, set()) exempt_channels = [] with self.bot.sql.transaction(): for channel in channels: if channel in pingable_channels: self.bot.sql.roles.remove_pingable_role_channel( ctx.guild, channel, role) else: exempt_channels.append(channel) if exempt_channels: embed = discord.Embed(colour=discord.Colour.dark_grey()) embed.set_author( name="Failed to make role unpingable in channels: ") descr = StringBuilder(sep=", ") for channel in exempt_channels: descr.write(channel.mention) embed.description = str(descr) await ctx.send(embed=embed) if set(exempt_channels) == set(channels): raise CommandFailed() # Send journal event content = f"Role was set as not pingable in channels: {self.str_channels(channels)}, except {self.str_channels(exempt_channels)}" self.journal.send( "pingable/remove", ctx.guild, content, icon="role", role=role, channels=channels, )
async def sha1sum(self, ctx, *urls: str): """ Gives the SHA1 hashes of any files attached to the message. """ # Check all URLs links = [] for url in urls: match = URL_REGEX.match(url) if match is None: raise CommandFailed(content=f"Not a valid url: {url}") links.append(match[1]) links.extend(attach.url for attach in ctx.message.attachments) # Get list of "names" names = list(urls) names.extend(attach.filename for attach in ctx.message.attachments) # Send error if no URLS if not links: raise CommandFailed(content="No URLs listed or files attached.") # Download and check files contents = [] content = StringBuilder("Hashes:\n```") buffers = await download_links(links) for i, binio in enumerate(buffers): if binio is None: hashsum = SHA1_ERROR_MESSAGE else: hashsum = sha1(binio.getbuffer()).hexdigest() content.writeln(f"{hashsum} {names[i]}") if len(content) > 1920: contents.append(content) if i < len(buffers) - 1: content.clear() content.writeln("```") if len(content) > 4: content.writeln("```") contents.append(content) for content in contents: await ctx.send(content=str(content))
async def channel_show(self, ctx): """ Lists all channels that are allowed to be used for role commands. """ all_channels = self.bot.sql.roles.get_role_command_channels(ctx.guild) prefix = self.bot.prefix(ctx.guild) if all_channels: embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Permitted channels") descr = StringBuilder(sep=", ") for channel in all_channels: descr.write(channel.mention) embed.description = str(descr) else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="All channels are permitted") embed.description = ( f"There are no restricted role channels set, so `{prefix}role add/remove` commands " "can be used anywhere.") await ctx.send(embed=embed)
async def uinfo(self, ctx, *, name: str = None): """ Fetch information about a user, whether they are in the guild or not. If no argument is passed, the caller is checked instead. """ user = await self.get_user(ctx, name) usernames, nicknames = self.bot.sql.alias.get_alias_names( ctx.guild, user) logger.info("Running uinfo on '%s' (%d)", user.name, user.id) # Status content = StringBuilder() if getattr(user, "status", None): status = ("do not disturb" if user.status == discord.Status.dnd else user.status) content.writeln(f"{user.mention}, {status}") else: content.writeln(user.mention) embed = discord.Embed() embed.timestamp = user.created_at embed.set_author(name=user_discrim(user)) embed.set_thumbnail(url=user.avatar_url) # User colour if hasattr(user, "colour"): embed.colour = user.colour embed.add_field(name="ID", value=f"`{user.id}`") self.uinfo_add_roles(embed, user) self.uinfo_add_activity(embed, user, content) embed.description = str(content) content.clear() self.uinfo_add_voice(embed, user) self.uinfo_add_aliases(embed, content, usernames, nicknames) # Guild join date if hasattr(user, "joined_at"): embed.add_field(name="Member for", value=fancy_timedelta(user.joined_at)) # Discord join date embed.add_field(name="Account age", value=fancy_timedelta(user.created_at)) # Send them await ctx.send(embed=embed)
async def remind_list(self, ctx): """ Lists all reminders for the current user. """ reminders = self.bot.sql.navi.get_reminders(ctx.author) if reminders: descr = StringBuilder() for reminder in reminders: until = fancy_timedelta(reminder.timestamp) message = escape_backticks(reminder.message) descr.writeln( f"ID: #`{reminder.id:05}`: in `{until}` with message: `{message}`" ) embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name=f"Reminders for {ctx.author.display_name}") embed.description = str(descr) else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.description = f"No reminders for {ctx.author.mention}" await ctx.send(embed=embed)
async def listcogs(self, ctx): """ Lists all currently loaded cogs. """ content = StringBuilder("```\n") extensions = defaultdict(list) for cog_name, cog in self.bot.cogs.items(): ext_name = cog.__module__.rsplit(".", 1)[0] ext_name = ext_name.rsplit(".", 1)[1] extensions[ext_name].append(cog_name) # I hate this but tree-format uses lists and tuples for some reason # So this takes the nice dictionary and converts it to that extensions = [(ext, [(cog_name, []) for cog_name in cog]) for ext, cog in extensions.items()] tree = ("futaba", [("cogs", extensions)]) content.writeln( format_tree(tree, format_node=itemgetter(0), get_children=itemgetter(1))) content.writeln("```") await ctx.send(content=str(content))
async def roles(self, ctx): """ Lists all roles in the guild. """ contents = [] content = StringBuilder() logger.info("Listing roles within the guild") for role in ctx.guild.roles: content.writeln( f"- {role.mention} id: `{role.id}`, members: `{len(role.members)}`" ) if len(content) > 1900: # Too long, break into new embed contents.append(str(content)) # Start content over content.clear() if content: contents.append(str(content)) for i, content in enumerate(contents): embed = discord.Embed(description=content, colour=discord.Colour.dark_teal()) embed.set_footer(text=f"Page {i + 1}/{len(contents)}") await ctx.send(embed=embed)
async def check_text_filter(cog, message): # Also check embed content content = StringBuilder(message.content) for embed in message.embeds: embed_dict = embed.to_dict() content.writeln(embed_dict.get("description", "")) content.writeln(embed_dict.get("title", "")) for field in embed_dict.get("fields", []): content.writeln(field.get("name", "")) content.writeln(field.get("value", "")) # This is the string we will validate against to_check = str(content) logger.debug("Content to check: %r", to_check) # Iterate through all guild filters triggered = None filter_groups = ( (LocationType.GUILD, cog.filters[message.guild]), (LocationType.CHANNEL, cog.filters[message.channel]), ) for location_type, all_filters in filter_groups: for filter_text, (filter, filter_type) in all_filters.items(): if filter.matches(to_check): if triggered is None or filter_type.value > triggered.filter_type.value: triggered = FoundTextViolation( bot=cog.bot, journal=cog.journal, message=message, content=to_check, location_type=location_type, filter_type=filter_type, filter_text=filter_text, ) if triggered is not None: roles = cog.bot.sql.settings.get_special_roles(message.guild) await found_text_violation(triggered, roles)
async def rinfo(self, ctx, *, name: str = None): """ Fetches and prints information about a particular role in the current guild. If no role is specified, it displays information about the default role. """ if name is None: role = ctx.guild.default_role else: conv = RoleConv() try: role = await conv.convert(ctx, name) except commands.BadArgument: embed = discord.Embed(colour=discord.Colour.red()) embed.description = ( f"No role found in this guild for `{escape_backticks(name)}`." ) raise CommandFailed(embed=embed) logger.info("Running rinfo on '%s' (%d)", role.name, role.id) embed = discord.Embed(colour=role.colour) embed.timestamp = role.created_at embed.add_field(name="ID", value=str(role.id)) embed.add_field(name="Position", value=str(role.position)) descr = StringBuilder(f"{role.mention}\n") if role.mentionable: descr.writeln("Mentionable") if role.hoist: descr.writeln("Hoisted") if role.managed: descr.writeln("Managed") embed.description = str(descr) if role.members: max_members = 10 members = ", ".join( map(lambda m: m.mention, islice(role.members, 0, max_members))) if len(role.members) > max_members: diff = len(role.members) - max_members members += f" (and {diff} other{plural(diff)})" else: members = "(none)" embed.add_field(name="Members", value=members) await ctx.send(embed=embed)
def build_reason(ctx, action, minutes, reason, past=False): full_reason = StringBuilder(f"{action} by {user_discrim(ctx.author)}") if minutes: full_reason.write( f" {'for' if past else 'in'} {minutes} minute{plural(minutes)}" ) if reason: full_reason.write(f" with reason: {reason}") return str(full_reason)
async def log_find(self, ctx, *, condition: str = None): """ List previous journal events that match the given conditions. The condition is a Python expression with no variables but the following attributes: path: str ppath: PurePath guild: discord.Guild content: str attributes: dict """ events, error_embeds = self.log_filter(ctx.guild, condition, 10) if error_embeds: await ctx.send(embed=error_embeds[0]) if events: embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Matching journal events") descr = StringBuilder() embeds = [] for event in events: descr.writeln(f"Path: `{event.path}`, Content: {event.content}") descr.writeln(f"Attributes: ```py\n{pformat(event.attributes)}\n```\n") if len(descr) > 1400: embed.description = str(descr) embeds.append(embed) descr.clear() embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Matched journal events") embed.description = str(descr) else: embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No matching journal entries") embeds = (embed,) for i, embed in enumerate(embeds): embed.set_footer(text=f"Page {i + 1}/{len(embeds)}") await ctx.send(embed=embed)
def uinfo_add_voice(embed, user): if getattr(user, "voice", None): mute = user.voice.mute or user.voice.self_mute deaf = user.voice.deaf or user.voice.self_deaf states = StringBuilder(sep=" ") if mute: states.write("muted") if deaf: states.write("deafened") state = str(states) if states else "active" embed.add_field(name="Voice", value=state)
async def alert_add(self, ctx, attribute: str, relationship: str, *, amount: str): """ Adds a join alert with the given condition. Possible attributes: id, created, name, discrim, avatar, status Possible relationships: > >= = != < <= ~ """ logging.info( "Got request to add new join alert for '%s' (%d)", ctx.guild.name, ctx.guild.id, ) try: key = JoinAlertKey.parse(attribute) except ValueError: raise ManualCheckFailure(content=f"Invalid attribute: {attribute}") try: op = ValueRelationship(relationship) except ValueError: raise ManualCheckFailure( content=f"Invalid relationship: {relationship}") try: value = key.parse_value(amount) except ValueError as error: raise ManualCheckFailure(content=str(error)) alert = JoinAlert(ctx.guild, None, key, op, value) logging.info("Adding join alert: %s", alert) with self.bot.sql.transaction(): self.bot.sql.welcome.add_alert(ctx.guild, alert) self.alerts[alert.id] = alert # Notify the user embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Successfully added join alert") descr = StringBuilder() descr.writeln(f"ID: #`{alert.id:05}`, Condition: `{alert}`") descr.writeln( "To get these notifications in a channel, add a logger for path `/welcome/alert`" ) embed.description = str(descr) await ctx.send(embed=embed)
async def channel_delete(self, ctx, *channels: TextChannelConv): """ Removes the channel(s) from the restricted role channel list. """ logger.info( "Removing channels to be used for role commands in guild '%s' (%d): [%s]", ctx.guild.name, ctx.guild.id, ", ".join(channel.mention for channel in channels), ) if not channels: raise CommandFailed() # Remove channels from database with self.bot.sql.transaction(): for channel in channels: self.bot.sql.roles.remove_role_command_channel( ctx.guild, channel) # Send response embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Removed channels to be used for adding roles") all_channels = self.bot.sql.roles.get_role_command_channels(ctx.guild) descr = StringBuilder(sep=", ") for channel in channels: descr.write(channel.mention) embed.add_field(name="Removed", value=str(descr)) descr.clear() for channel in all_channels: descr.write(channel.mention) embed.add_field(name="Remaining", value=str(descr) or "(none)") await ctx.send(embed=embed) # Send journal event self.channel_journal(ctx.guild)
async def tracker_blacklist_show(self, ctx): """ Shows all blacklist entries for this guild. """ blacklist = self.bot.sql.settings.get_tracking_blacklist(ctx.guild) if not blacklist.blacklisted_users and not blacklist.blacklisted_channels: prefix = self.bot.prefix(ctx.guild) embed = discord.Embed(colour=discord.Colour.dark_purple()) embed.set_author(name="No blacklist entries") embed.description = ( f"Moderators can use the `{prefix}trackerblacklist add/remove` " "commands to change this list!") await ctx.send(embed=embed) return embed = discord.Embed(colour=discord.Colour.dark_teal()) embed.set_author(name="Blacklist entries") if blacklist.blacklisted_channels: channel_msg = StringBuilder(sep=", ") for channel_id in blacklist.blacklisted_channels: channel = discord.utils.get(ctx.guild.channels, id=channel_id) channel_msg.write(channel.mention) embed.add_field(name="Blacklisted channels", value=channel_msg) if blacklist.blacklisted_users: user_msg = StringBuilder(sep=", ") for user_id in blacklist.blacklisted_users: user = discord.utils.get(chain(ctx.guild.members, ctx.bot.users), id=user_id) user_msg.write(user.mention) embed.add_field(name="Blacklisted channels", value=user_msg) await ctx.send(embed=embed)
async def message_violator(jailed): response = StringBuilder( f"The {name_type.value} you just set violates a {filter_type.value} text filter " f"disallowing `{escaped_filter_text}`.\n" ) if jailed: if roles.jail is not None: response.writeln( f"In the mean time, you have been assigned the `{roles.jail.name}` role, " "revoking your posting privileges until a moderator clears you." ) else: response.writeln( "Your name has been manually cleared. Please do not set your name to " "anything offensive in the future." ) await member.send(content=str(response))
async def list_emojis(self, ctx, all_guilds=False): contents = [] content = StringBuilder() if all_guilds: if not mod_perm(ctx): raise ManualCheckFailure( content="Only moderators can do this.") guild_emojis = (guild.emojis for guild in self.bot.guilds) emojis = chain(*guild_emojis) else: emojis = ctx.guild.emojis logger.info("Listing all emojis within the guild") for emoji in emojis: managed = "M" if emoji.managed else "" content.writeln( f"- [{emoji}]({emoji.url}) id: `{emoji.id}`, name: `{emoji.name}` {managed}" ) if len(content) > 1900: # Too long, break into new embed contents.append(str(content)) # Start content over content.clear() if content: contents.append(str(content)) for i, content in enumerate(contents): embed = discord.Embed(description=content, colour=discord.Colour.dark_teal()) embed.set_footer(text=f"Page {i + 1}/{len(contents)}") if i == 0: if all_guilds: embed.set_author(name="Emojis in all guilds") else: embed.set_author(name=f"Emojis within {ctx.guild.name}") await ctx.send(embed=embed)
async def alert_show(self, ctx): """ Lists all join alerts for this guild. """ logging.info("Showing all join alerts for '%s' (%d)", ctx.guild.name, ctx.guild.id) embed = discord.Embed() embed.set_author(name="Join alerts") descr = StringBuilder() descr.writeln("__Alert ID__ | __Condition__") for alert in self.alerts.values(): assert alert.id is not None, "Alert was not given an ID" descr.writeln(f"#**`{alert.id:05}`** | `{alert}`") if self.alerts: embed.colour = discord.Colour.dark_teal() embed.description = str(descr) else: embed.colour = discord.Colour.dark_purple() embed.description = "No alerts for this guild" await ctx.send(embed=embed)