async def bulk_delete(self, messages: list[discord.Message]): guild = messages[0].guild if not guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) entry = None if LoggingEnum.MESSAGE not in options or log_channel is None: return if guild.me.guild_permissions.view_audit_log: entry = (await guild.audit_logs( limit=1, action=AuditLogAction.message_bulk_delete).flatten())[0] if (datetime.utcnow() - entry.created_at).total_seconds() > 5: entry = None embed = CustomEmbed(title=f"{len(messages)} Bulk Deleted", timestamp=datetime.utcnow()) if entry: embed.add_field( name="**Moderator**", value=(f"Moderator: {entry.user} [{entry.user.id}]"), inline=False, ) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def unload(self, ctx, extension): try: self.bot.unload_extension(extension) await ctx.reply(embed=CustomEmbed( description=f"Unloaded extension `{extension}`")) except Exception as e: await ctx.reply(embed=CustomEmbed(description=f"```py\n{e}```"))
async def on_member_remove(self, member): if not member.guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.bot.utils.get_enum( member.guild.id)) and (id := self.bot.cache[member.guild.id].get("logid")) is not None): if LoggingEnum.MODERATION not in options: return channel = self.bot.get_channel(id) if channel is None: return entry = await get_audit(member.guild, AuditLogAction.kick) if entry is None: return embed = CustomEmbed( title="**Member Kicked**", description=(f"User: {member} [{member.id}]\n" f"Moderator: {entry.user} [{entry.user.id}]" f"Reason: {getattr(entry, 'reason', 'None')}\n"), timestamp=datetime.utcnow(), ).set_thumbnail(url=member.avatar_url) embed.set_author(name=entry.user.name, url=entry.user.avatar_url) with suppress(discord.Forbidden): await channel.send(embed=embed)
async def on_member_unban(self, guild: discord.Guild, user: discord.User): if not guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.bot.utils.get_enum( guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): if LoggingEnum.MODERATION not in options: return channel = self.bot.get_channel(id) if channel is None: return action = await get_audit(guild, AuditLogAction.unban) embed = CustomEmbed( title="**Member Unbanned**", description=(f"User: {user} [{user.id}]\n" f"Reason: {getattr(action, 'reason', 'None')}\n"), timestamp=datetime.utcnow(), ).set_thumbnail(url=user.avatar_url) if action: embed.description += f"Moderator: {action.user}" embed.set_author(name=action.user.name, url=action.user.avatar_url) with suppress(discord.Forbidden): await channel.send(embed=embed)
async def reload(self, ctx, cog): try: self.bot.reload_extension(cog) await ctx.reply(embed=CustomEmbed( description=f"Reload of `{cog}` successful.")) except Exception as e: await ctx.reply(embed=CustomEmbed(description="```py\n" + str(e) + "```"))
async def send_command_help(self, command): command = await self.filter_commands([command]) command = safe_get(command, 0) if command is None: return embed = CustomEmbed(title="Help") embed.add_field(name="Help", value=command.help or "None", inline=False) embed.add_field( name="Aliases", value=" ".join("`" + alias + "`" for alias in command.aliases) or "None", inline=False, ) embed.add_field( name="Arguments", value=getattr( command, "usage", getattr(command, "old_signature", command.signature) ) or "None", inline=False, ) await self.get_destination().send(embed=embed)
async def on_channel_update(self, before, after): guild = after.guild if not after.guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) if LoggingEnum.CHANNELS not in options or log_channel is None: return if guild.me.guild_permissions.view_audit_log: entry = (await guild.audit_logs( limit=1, action=AuditLogAction.channel_update).flatten())[0] embed = CustomEmbed(title="Channel Edited", timestamp=datetime.utcnow()) embed.add_field( name="Basic Info", value=(f"Channel: {after.mention} [{after.id}] \n" f"Moderator: {entry.user} [{entry.user.id}]\n"), ) embed.add_field( name="Advanced Info", value="\n".join([ title_format(diff[0] + f"`{diff[1]}`" for diff in entry.after if diff[0] != "overwrites") ]), ) for diff in entry.after: if diff[0] == "overwrites": embed.add_field( name="Overwrites", value=await self.bot.utils.paste(format_overwrites(diff[1])), ) embed.set_author(name=entry.user.name, url=entry.user.avatar_url) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
def format_page(self, menu, entries): offset = menu.current_page * self.per_page embed = CustomEmbed( title=( f"{self.cog.qualified_name} " f"[{' | '.join([alias for alias in self.cog.aliases ])}]" if getattr(self.cog, "aliases", None) is not None else "" ) ) if (des := getattr(self.cog, "description")) is not None: embed.description = des
async def unmute(self, ctx, member: HierarchyMemberConverter): """ Unmutes a member. """ muted_role = ctx.guild.get_role(ctx.cache.get("muteid")) if muted_role is None: raise commands.BadArgument( "I cannot find the muted role in the config, this is probably because the role was deleted or you haven't set it up." ) if muted_role >= ctx.me.top_role: raise commands.BadArgument( "The muted role is greater than or equal to my top role in the hierarchy, please move my role above it." ) if muted_role in member.roles: await member.remove_roles( muted_role, reason=f"Unmute done by {ctx.author} [{ctx.author.id}]") await ctx.db.execute( "DELETE FROM mutes WHERE userid = $1 and guildid = $2", member.id, ctx.guild.id, ) await ctx.reply(embed=CustomEmbed(description=f"Unmuted {member}")) else: raise commands.BadArgument( "That user doesn't seem to have the `muted` role set in the config." )
async def kick( self, ctx, member: HierarchyMemberConverter, force: Optional[bool] = True, *, reason: Optional[str] = "No reason given", ): """Kicks a user. If ``force`` is True, then it kicks without notifying. """ notified = False if force is True: try: await member.kick(reason=reason) notified = False await ctx.reply(embed=CustomEmbed( title="Kicked User", description= f"Kicked member {member.name} for reason `{reason}`. User was {'notified.' if notified is True else 'not notified.'}", )) except: return await ctx.send(embed=CustomEmbed( description= "I was unable to kick the user for whatever reason.")) try: await member.send(embed=CustomEmbed( title="Kicked", description= f"You have been kicked from {ctx.guild.name} by moderator {ctx.author.name} for the reason `{reason}`.", )) notified = True except discord.Forbidden: notified = False finally: await member.kick(reason=reason) await ctx.reply(embed=CustomEmbed( title="Member Kicked", description= f"Kicked member {member.name} for reason `{reason}`. User was {'notified.' if notified is True else 'not notified.'}", ))
async def on_guild_role_create(self, role: discord.Role): guild = role.guild if not guild.me.guild_permissions.view_audit_log: return if ( LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None ): log_channel = guild.get_channel(id) entry = None if LoggingEnum.GUILD not in options or log_channel is None: return entry = await get_audit(guild, AuditLogAction.role_create) embed = CustomEmbed(title="Role Created").add_field( name="Basic Info", value=( f"Moderator: {entry.user} [{entry.user.id}]\n" f"Role: {role.name} [{role.id}]" ), ) embed.add_field( name="Advanced Info", value="\n".join( f"{title_format(diff[0])}: `{diff[1]}`" for diff in entry.after if diff[0] not in ("permissions", "permissions_new") ), inline=False, ) for diff in entry.after: if diff[0] in ("permissions"): embed.add_field( name=title_format(diff[0]), value=await self.bot.utils.paste( ( f"{role.name}\n" + "\n".join( f"{perm} : {value}" for perm, value in dict(diff[1]).items() ) ) ), ) embed.set_author(name=entry.user.name, icon_url=entry.user.avatar_url) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def ban( self, ctx, member: HierarchyMemberConverter, force: Optional[bool] = False, delete: Optional[int] = 1, *, reason: Optional[str] = "None given", ): notified = False if force is True: try: await member.ban(reason=reason, delete_message_days=delete) notified = False await ctx.reply(embed=CustomEmbed( title="Banned User", description= f"Banned member {member.name} for reason `{reason}`. User was {'notified.' if notified is True else 'not notified.'}", )) except: return await ctx.send(embed=CustomEmbed( description= "I was unable to ban the user for whatever reason.")) try: await member.send(embed=CustomEmbed( title="Banned", description= f"You have been banned from {ctx.guild.name} by moderator {ctx.author.name} for the reason `{reason}`.", )) notified = True except discord.Forbidden: notified = False finally: await member.ban(reason=reason, delete_message_days=delete) await ctx.reply(embed=CustomEmbed( title="Member Banned", description= f"Banned member {member.name} for reason `{reason}`. User was {'notified.' if notified is True else 'not notified.'}", ))
async def purge(self, ctx: commands.Context, num: int): """Purges my messages in the channel""" iterate = ctx.channel.permissions_for(ctx.me).manage_messages try: msgs = len(await ctx.channel.purge( limit=num, check=lambda m: m.author == self.bot.user, bulk=iterate)) except Exception as e: await ctx.reply(embed=CustomEmbed( title=f"An error occurred. \n ```\n{e}```")) await ctx.reply( embed=CustomEmbed( description= f"Deleted {msgs} / {num} possible message(s) \U0001f44d"), delete_after=30, )
async def on_channel_create(self, channel): guild = channel.guild if not channel.guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) if LoggingEnum.CHANNELS not in options or log_channel is None: return entry = await get_audit(channel.guild, AuditLogAction.channel_create) if entry is None: return embed = CustomEmbed(title="Channel Created", timestamp=datetime.utcnow()) embed.add_field( name="**Basic Info**", value= (f"Name: `{channel}` [{channel.id}]\n" f"Type: `{channel.type}`\n" f"Position: `{channel.position}`\n" f"Created At: `{channel.created_at.strftime(TIME_TEMPLATE)}` (UTC)\n" f"Category: `{channel.category}`" f"Moderator: {entry.user} [{entry.user.id}]\n"), ) embed.add_field( name="Advanced Info", value="\n".join([ title_format(diff[0] + f"`{diff[1]}`" for diff in entry.after if diff[0] != "overwrites") ]), ) for diff in entry.after: if diff[0] == "overwrites": embed.add_field( name="Overwrites", value=await self.bot.utils.paste(format_overwrites(diff[1])), ) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def command_error(self, ctx: commands.Context, error): error = getattr(error, "original", error) if isinstance(error, self.ignored_errors): return conditions = ( ctx.command is not None and ctx.command.has_error_handler(), ctx.command.parent is not None and ctx.command.parent.has_error_handler(), ) if any(conditions): return elif isinstance(error, self.str_errors): return await ctx.reply(embed=CustomEmbed(description=str(error))) else: traceback_text = "".join( traceback.format_exception(type(error), error, error.__traceback__, 4) ) await ctx.reply( embed=CustomEmbed( title="An error has occurred.", description="```\n" + str(error) + "```", ).set_footer(text="I have reported it to the developers.") ) paste = await self.bot.utils.paste(traceback_text, syntax="py") await self.bot.hook.send( embed=CustomEmbed( description=f"An error occurred! \n URL: {paste}" ).set_footer(text=f"Caused by {ctx.command}"), username=str(ctx.author), avatar_url=ctx.author.avatar, ) self.logger.exception(f"{ctx.command.name} \n{traceback_text}")
async def on_questionmark(self, payload): embed = CustomEmbed( title="Help Info", description=( "```diff\n" "- <arg> Required \n" "- [arg] Optional \n" "- [arg..] Multiple Args\n" "```\n" ), ) embed.add_field( name="What do the emojis do?", value=( f"{EMOJIS['arrow_left']} - Goes one page backward.\n" f"{EMOJIS['double_backward']} - Goes to the first page.\n" f"{EMOJIS['stop']} - Stops the menu.\n" f"{EMOJIS['double_forward']} - Goes to the last page.\n" f"{EMOJIS['arrow_right']} - Goes one page forward\n" f"{EMOJIS['information']} - Shows this page.\n" ), ) if self.source.get_max_pages() == 1: embed.set_footer(text="To get back to the page, use the double arrows.") await self.message.edit(embed=embed)
async def on_guild_emojis_update(self, guild, before, after): if not guild.me.guild_permissions.view_audit_log: return if ( LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None ): log_channel = guild.get_channel(id) entry = None if LoggingEnum.GUILD not in options or log_channel is None: return entry = await get_audit(guild, AuditLogAction.emoji_update) embed = CustomEmbed(title="Emojis Updated").add_field( name="Basic Info", value=(f"Moderator: {entry.user} [{entry.user.id}]\n"), ) embed.add_field( name="Advanced Info", value="\n".join( f"{title_format(diff[0])}: `{diff[1]}`" for diff in entry.after ), inline=False, ) embed.set_author(name=entry.user.name, icon_url=entry.user.avatar_url) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def softban( self, ctx, member: HierarchyMemberConverter, *, reason: Optional[str] = "None given", ): """Bans and unbans a user, thusly deleting their messages.""" await member.ban(reason=reason) await member.unban() await ctx.reply( embed=CustomEmbed(title="Member Softbanned", description=f"Softbanned user {member}."))
async def reply_with_prefix(self, message): if message.author.bot or message.author == self.bot.user: return if message.content in ( MENTION_TEMPLATE.format(f"!{self.bot.user.id}"), MENTION_TEMPLATE.format(self.bot.user.id), ): prefixes = await self.bot.get_prefix(message) await message.reply(embed=CustomEmbed( description= f"My prefixes are `@Harley ` and `{discord.utils.escape_mentions(prefixes[2])}`." ))
async def guild_leave(self, guild: discord.Guild): await self.bot.db.execute("DELETE FROM config WHERE id = $1") hook = self.bot.logger await hook.send( embed=CustomEmbed( title="Guild Left", description= ("```diff\n" f"- Owner: {guild.owner} [{guild.owner.id}] \n" f"- Members: {guild.member_count} [{(sum([member.bot for member in guild.members]) / guild.member_count) * 1000:.2f}% bots]\n" f"- Created: {guild.created_at.strftime(TIME_TEMPLATE)}\n" "```"), ), avatar_url=guild.icon_url, username=guild.name, )
async def guild_update(self, before: discord.Guild, after: discord.Guild): if not after.me.guild_permissions.view_audit_log: return if ( LoggingEnum.NONE not in (options := self.get_enum(after.id)) and (id := self.bot.cache[after.id].get("logid")) is not None ): log_channel = after.get_channel(id) if LoggingEnum.GUILD not in options or log_channel is None: return entry = await get_audit(after, AuditLogAction.guild_update) if entry is None: return embed = CustomEmbed(title="Guild Updated") embed.add_field( name="Basic Info", value=(f"Moderator: {entry.user} [{entry.user.id}]") ) embed.add_field( name="Advanced Info", value="\n".join( [f"{title_format(diff[0])}: `{diff[1]}`" for diff in entry.after] ), ) embed.set_author(name=entry.user.name, icon_url=entry.user.avatar_url) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def on_msg_delete(self, message: discord.Message): if message.author.bot: return guild = message.guild if not guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) entry = None if LoggingEnum.MESSAGE not in options or log_channel is None: return entry = await get_audit(message.guild, AuditLogAction.message_delete) embed = CustomEmbed( title="Message Deleted", description=( f"Author: {message.author} [{message.author.id}] \n" f"Channel: {message.channel} [{message.channel.id}] \n"), timestamp=datetime.utcnow(), ) embed.add_field( name="**Content**", value=discord.utils.escape_markdown(message.content) or "None" if not message.embeds else "Message had embeds.", inline=False, ) if entry: embed.add_field( name="**Moderator**", value=(f"Moderator: {entry.user} [{entry.user.id}]"), inline=False, ) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def guild_join(self, guild: discord.Guild): await self.bot.db.execute( "INSERT INTO config(id) VALUES($1) ON CONFLICT DO NOTHING", guild.id) await self.bot.refresh_cache_for(guild.id) hook = self.bot.hook await hook.send( embed=CustomEmbed( title="Guild Joined", description= ("```diff\n" f"- Owner: {guild.owner} [{guild.owner.id}] \n" f"- Members: {guild.member_count} [{(sum([member.bot for member in guild.members]) / guild.member_count) * 100:.2f}% bots]\n" f"- Created: {guild.created_at.strftime(TIME_TEMPLATE)}\n" "```"), ), avatar_url=guild.icon_url, username=guild.name, )
async def reload_all(self, ctx): successful = [] unsuccessful = [] extensions = list(self.bot.extensions.keys()) for cog in extensions: try: self.bot.reload_extension(cog) successful.append(cog) except: unsuccessful.append(cog) embed = CustomEmbed(title="Reloaded all extensions") embed.add_field(name="Successful", value="\t".join(successful) or "None") embed.add_field(name="Unsuccessful", value="\t".join(unsuccessful) or "None", inline=False) await ctx.send(embed=embed)
async def on_msg_edit(self, before: discord.Message, after: discord.Message): if before.author.bot: return guild = before.guild if not guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) if LoggingEnum.MESSAGE not in options or log_channel is None: return embed = CustomEmbed(title="Message Edited", timestamp=datetime.utcnow()) embed.add_field( name="Before Content", value=discord.utils.escape_markdown(before.content) or "Message only contained embeds.", inline=False, ) embed.add_field( name="After Content", value=discord.utils.escape_markdown(after.content) or "Message only contained embeds.", inline=False, ) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
def format_page(self, menu, entries): offset = menu.current_page * self.per_page embed = CustomEmbed( title="Help", description=( f"Use `{self.prefix}help[command]` for more info on a command.\n" f"You can also use `{self.prefix}help[category]` for more info on a category.\n" ), ) for index, category in enumerate(entries, start=offset): cate = list(category.keys())[0] category_name = getattr(cate, "qualified_name", "None") commands = category.get(list(category.keys())[0]) embed.add_field( name=f"**{category_name}** [{' | '.join(alias for alias in cate.aliases)}]" if getattr(cate, "aliases", None) is not None else f"**{category_name}**", value=f"{getattr(cate, 'description', '')}" + "\n" + " ".join(f"`{command.qualified_name}`" for command in commands) or "`None`", inline=False, ) embed.set_footer( text=f"Page {menu.current_page + 1} / {self.get_max_pages()}" if self.get_max_pages() > 0 else "Page 0/0" ) return embed
async def on_channel_delete(self, channel): guild = channel.guild if not channel.guild.me.guild_permissions.view_audit_log: return if (LoggingEnum.NONE not in (options := self.get_enum(guild.id)) and (id := self.bot.cache[guild.id].get("logid")) is not None): log_channel = guild.get_channel(id) entry = None if LoggingEnum.CHANNELS not in options or log_channel is None: return if guild.me.guild_permissions.view_audit_log: entry = (await guild.audit_logs( limit=1, action=AuditLogAction.channel_delete).flatten())[0] embed = CustomEmbed(title="Channel Deleted", timestamp=datetime.utcnow()) embed.add_field( name="**Channel**", value= (f"Name: `{channel}` [{channel.id}]\n" f"Type: `{channel.type}`\n" f"Position: `{channel.position}`\n" f"Created At: `{channel.created_at.strftime(TIME_TEMPLATE)}` (UTC)" ), ) if entry is not None: for diff in entry.after: if diff[0] == "overwrites": if diff[1] is None: continue url = await self.bot.utils.paste(format_overwrites( diff[1]), syntax=None) embed.add_field(name="Overwrites", value=url or "None") else: embed.add_field( name=title_format(diff[0]), value=f"`{diff[1]}`", inline=False, ) embed.add_field(name="**Moderator**", value=(f"{entry.user}"), inline=False) else: embed.add_field( embed.add_field( name="**Moderator**", value="Cannot access the audit log to get more info.", )) with suppress(discord.Forbidden): await log_channel.send(embed=embed)
async def test(self, ctx, arg: int): await ctx.reply(embed=CustomEmbed(title=arg))
async def mute( self, ctx, member: HierarchyMemberConverter, time: TimeConverter, *, reason="None Given", ): """ Mutes a member for the specified time format is `6d` Valid time specifiers are `d`, `m`, `s`, `h` """ muted_role = ctx.guild.get_role(ctx.cache.get("muteid")) if muted_role is None: raise commands.BadArgument( "I cannot find the muted role in the config, this is probably because the role was deleted." ) if muted_role >= ctx.me.top_role: raise commands.BadArgument( "The muted role is greater than or equal to my top role in the hierarchy, please move my role above it." ) if muted_role in member.roles: await ctx.reply(embed=CustomEmbed( description= "User was already muted, changing the mute to the new time.")) await member.remove_roles(muted_role, reason="Unmuting") await ctx.db.execute( "DELETE FROM mutes WHERE guildid = $1 and userid = $2", ctx.guild.id, member.id, ) sleep = (time - datetime.datetime.utcnow()).total_seconds() if sleep <= 1800: task = self.bot.loop.create_task( self.perform_unmute(member=member, role=muted_role, when=time, record=None)) task.add_done_callback(self.unmute_error) await ctx.db.execute( "INSERT INTO mutes(userid, guildid, starttime, endtime, reason) VALUES($1, $2, $3, $4, $5)", member.id, ctx.guild.id, datetime.datetime.utcnow(), time, reason, ) await member.add_roles( muted_role, reason=f"Mute done by {ctx.author} [{ctx.author.id}]") await ctx.reply(embed=CustomEmbed( description=(f"Muted {member}\n" f"For Reason: `{reason}`\n"), timestamp=time, ).set_footer(text="Ends at"))