Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit Logging #10

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
4 changes: 4 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from emoji import UNICODE_EMOJI
from pkg_resources import parse_version

from core.audit_logger import AuditLogger

try:
# noinspection PyUnresolvedReferences
from colorama import init
Expand Down Expand Up @@ -87,6 +89,8 @@ def __init__(self):
self._configure_logging()

self.plugin_db = PluginDatabaseClient(self) # Deprecated

self.audit_logger = AuditLogger(bot=self)
self.startup()

def get_guild_icon(
Expand Down
74 changes: 74 additions & 0 deletions cogs/modmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from discord.ext.commands.view import StringView

from core import checks
from core.audit_logger import construct_from_ctx
from core.models import DMDisabled, PermissionLevel, SimilarCategoryConverter, getLogger
from core.paginator import EmbedPaginatorSession
from core.thread import Thread
Expand Down Expand Up @@ -245,6 +246,7 @@ async def snippet_add(self, ctx, name: str.lower, *, value: commands.clean_conte
color=self.bot.main_color,
description="Successfully created snippet.",
)
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.snippet.add", description=f"Added snippet {name}"))
return await ctx.send(embed=embed)

def _fix_aliases(self, snippet_being_deleted: str) -> Tuple[List[str]]:
Expand Down Expand Up @@ -347,6 +349,7 @@ async def snippet_remove(self, ctx, *, name: str.lower):
)
self.bot.snippets.pop(name)
await self.bot.config.update()
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.snippet.remove", description=f"removed snippet {name}"))
else:
embed = create_not_found_embed(name, self.bot.snippets.keys(), "Snippet")
await ctx.send(embed=embed)
Expand All @@ -370,6 +373,7 @@ async def snippet_edit(self, ctx, name: str.lower, *, value):
color=self.bot.main_color,
description=f'`{name}` will now send "{value}".',
)
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.snippet.edit", description=f"edited snippet {name}"))
else:
embed = create_not_found_embed(name, self.bot.snippets.keys(), "Snippet")
await ctx.send(embed=embed)
Expand Down Expand Up @@ -420,6 +424,12 @@ async def move(self, ctx, *, arguments):
category=category, end=True, sync_permissions=True, reason=f"{ctx.author} moved this thread."
)

self.bot.audit_logger.push(
await construct_from_ctx(
ctx,
action="modmail.thread.move",
description=f"{'silently ' if silent else ''}moved thread {thread.id} to {category.name}"))

if self.bot.config["thread_move_notify"] and not silent:
embed = discord.Embed(
title=self.bot.config["thread_move_title"],
Expand Down Expand Up @@ -512,6 +522,14 @@ async def close(
if after and after.dt > after.now:
await self.send_scheduled_close_message(ctx, after, silent)

# Generate audit event
description = None
if close_after == 0:
description = f"{'silently ' if silent else ''}closed thread {thread.id}"
else:
description = f"{'silently ' if silent else ''}scheduled to close thread {thread.id} in {human_timedelta(after.dt)}"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.close", description=description))

await thread.close(closer=ctx.author, after=close_after, message=message, silent=silent)

@staticmethod
Expand Down Expand Up @@ -677,7 +695,9 @@ async def unsubscribe(self, ctx, *, user_or_role: Union[discord.Role, User, str.
@checks.thread_only()
async def nsfw(self, ctx):
"""Flags a Modmail thread as NSFW (not safe for work)."""
# TODO set the thread as NSFW in the database
await ctx.channel.edit(nsfw=True)
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.set.nsfw", description=f"marked thread as nsfw"))
sent_emoji, _ = await self.bot.retrieve_emoji()
await self.bot.add_reaction(ctx.message, sent_emoji)

Expand All @@ -686,7 +706,9 @@ async def nsfw(self, ctx):
@checks.thread_only()
async def sfw(self, ctx):
"""Flags a Modmail thread as SFW (safe for work)."""
# TODO unset the thread as NSFW in the database
await ctx.channel.edit(nsfw=False)
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.set.sfw", description=f"marked thread as sfw"))
sent_emoji, _ = await self.bot.retrieve_emoji()
await self.bot.add_reaction(ctx.message, sent_emoji)

Expand Down Expand Up @@ -769,9 +791,14 @@ def format_log_embeds(self, logs, avatar_url):
@commands.cooldown(1, 600, BucketType.channel)
async def title(self, ctx, *, name: str):
"""Sets title for a thread"""
# todo update database with new title
await ctx.thread.set_title(name)
sent_emoji, _ = await self.bot.retrieve_emoji()
await ctx.message.pin()

# Push audit event
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.set.title", description=f"set thread title to {name}"))

await self.bot.add_reaction(ctx.message, sent_emoji)

@commands.command(usage="<users_or_roles...> [options]", cooldown_after_parsing=True)
Expand Down Expand Up @@ -865,6 +892,12 @@ async def adduser(self, ctx, *users_arg: Union[discord.Member, discord.Role, str
if i not in users:
to_exec.append(i.send(embed=em))

# Push audit event
users_string: str = ""
for user in users:
users_string += f"{user.name}({user.id}),"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.user.add", description=f"added user(s) {users_string} to thread"))

await ctx.thread.add_users(users)
if to_exec:
await asyncio.gather(*to_exec)
Expand Down Expand Up @@ -962,6 +995,12 @@ async def removeuser(self, ctx, *users_arg: Union[discord.Member, discord.Role,
if to_exec:
await asyncio.gather(*to_exec)

# Push audit event
users_string: str = ""
for user in users:
users_string += f"{user.name}({user.id}),"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.user.remove", description=f"removed user(s) {users_string} to thread"))

sent_emoji, _ = await self.bot.retrieve_emoji()
await self.bot.add_reaction(ctx.message, sent_emoji)

Expand Down Expand Up @@ -1052,6 +1091,12 @@ async def anonadduser(self, ctx, *users_arg: Union[discord.Member, discord.Role,
if i not in users:
to_exec.append(i.send(embed=em))

users_string: str = ""
for user in users:
users_string += f"{user.name}({user.id}),"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.user.add.anon", description=f"anonymously added user(s) {users_string} to thread"))


await ctx.thread.add_users(users)
if to_exec:
await asyncio.gather(*to_exec)
Expand Down Expand Up @@ -1145,6 +1190,13 @@ async def anonremoveuser(self, ctx, *users_arg: Union[discord.Member, discord.Ro
if to_exec:
await asyncio.gather(*to_exec)

# Push audit event
users_string: str = ""
for user in users:
users_string += f"{user.name}({user.id}),"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.thread.user.remove.anon",
description=f"anonymously removed user(s) {users_string} to thread"))

sent_emoji, _ = await self.bot.retrieve_emoji()
await self.bot.add_reaction(ctx.message, sent_emoji)

Expand Down Expand Up @@ -1772,6 +1824,10 @@ async def blocked_whitelist(self, ctx, *, user: User = None):
color=self.bot.main_color,
)
self.bot.blocked_whitelisted_users.remove(str(user.id))

self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.whitelist.remove",
description=f"removed user {user.name}({user.id}) from whitelist"))

return await ctx.send(embed=embed)

self.bot.blocked_whitelisted_users.append(str(user.id))
Expand All @@ -1782,6 +1838,9 @@ async def blocked_whitelist(self, ctx, *, user: User = None):

await self.bot.config.update()

self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.whitelist.add", description=f"added user {user.name}({user.id}) to whitelist"))


if msg.startswith("System Message: "):
# If the user is blocked internally (for example: below minimum account age)
# Show an extended message stating the original internal message
Expand Down Expand Up @@ -1869,6 +1928,9 @@ async def send_embed(title: str, message: str):
f"{__name__}: cannot block user, user is neither an instance of Discord Role or User"
)

desc = f"added {'role' if isinstance(user_or_role, discord.Role) else 'user'} {user_or_role.id} to blocklist"
self.bot.audit_logger.push(await construct_from_ctx(ctx, action="modmail.user.block", description=desc))

await self.bot.config.update()

return await send_embed("Success", desc)
Expand Down Expand Up @@ -1916,6 +1978,9 @@ async def send_embed(title: str, message: str):

await self.bot.config.update()

desc: str = f"removed {'role' if isinstance(user_or_role, discord.Role) else 'user'} {user_or_role.id} from blocklist"
self.bot.audit_logger.push(construct_from_ctx(ctx, action="modmail.user.unblock", description=desc))

return await send_embed("Success", f"{mention} has been unblocked.")

@commands.command()
Expand Down Expand Up @@ -1943,6 +2008,9 @@ async def delete(self, ctx, message_id: int = None):
color=self.bot.error_color,
)
)
# Push audit event
user = await self.bot.get_or_fetch_user(thread.recipient_id)
self.bot.audit_logger.push(construct_from_ctx(ctx, action="modmail.thread.delete_message", description=f"deleted message {message_id} sent by {user.name}({user.id}) from thread {thread.id}"))

sent_emoji, _ = await self.bot.retrieve_emoji()
await self.bot.add_reaction(ctx.message, sent_emoji)
Expand Down Expand Up @@ -2078,6 +2146,8 @@ async def enable(self, ctx):
self.bot.config["dm_disabled"] = DMDisabled.NONE
await self.bot.config.update()

self.bot.audit_logger.push(construct_from_ctx(ctx, action="modmail.dm.enable", description=f"enabled inbound direct messages"))

return await ctx.send(embed=embed)

@commands.group(invoke_without_command=True)
Expand Down Expand Up @@ -2109,6 +2179,8 @@ async def disable_new(self, ctx):
self.bot.config["dm_disabled"] = DMDisabled.NEW_THREADS
await self.bot.config.update()

self.bot.audit_logger.push(construct_from_ctx(ctx, action="modmail.dm.disable.new", description="disabled new thread creation"))

return await ctx.send(embed=embed)

@disable.command(name="all")
Expand All @@ -2129,6 +2201,8 @@ async def disable_all(self, ctx):
self.bot.config["dm_disabled"] = DMDisabled.ALL_THREADS
await self.bot.config.update()

self.bot.audit_logger.push(construct_from_ctx(ctx, action="modmail.dm.disable.all", description="disabled inbound direct message handling"))

return await ctx.send(embed=embed)

@commands.command()
Expand Down
Loading