Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions techsupport_bot/core/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import annotations

import hashlib
import json
from functools import wraps
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -529,3 +530,16 @@ async def bot_admin_check_interaction(interaction: discord.Interaction) -> bool:
if not is_admin:
raise app_commands.MissingPermissions(["bot_admin"])
return True


async def get_attachment_hash(attachment: discord.Attachment) -> str:
"""Computes a sha256 file hash for a given discord attachment

Args:
attachment (discord.Attachment): The attachment to generate the hash of

Returns:
str: The hash, as a string
"""
file_bytes = await attachment.read()
return hashlib.sha256(file_bytes).hexdigest()
36 changes: 33 additions & 3 deletions techsupport_bot/functions/automod.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import munch
from botlogging import LogContext, LogLevel
from commands import moderator, modlog
from core import cogs, extensionconfig, moderation
from core import auxiliary, cogs, extensionconfig, moderation
from discord.ext import commands

if TYPE_CHECKING:
Expand Down Expand Up @@ -205,7 +205,7 @@ async def response(
if ctx.message.author.top_role >= ctx.channel.guild.me.top_role:
return

all_punishments = run_all_checks(config, ctx.message)
all_punishments = await run_all_checks(config, ctx.message)

if len(all_punishments) == 0:
return
Expand Down Expand Up @@ -455,7 +455,7 @@ def generate_automod_alert_embed(
# All checks will return a list of AutoModPunishment, which may be nothing


def run_all_checks(
async def run_all_checks(
config: munch.Munch, message: discord.Message
) -> list[AutoModPunishment]:
"""This runs all 4 checks on a given discord.Message
Expand All @@ -475,6 +475,7 @@ def run_all_checks(
run_only_string_checks(config, message.clean_content)
+ handle_file_extensions(config, message.attachments)
+ handle_mentions(config, message)
+ await handle_file_hashes(config, message.attachments)
)
return all_violations

Expand Down Expand Up @@ -528,6 +529,35 @@ def handle_file_extensions(
return violations


async def handle_file_hashes(
config: munch.Munch, attachments: list[discord.Attachment]
) -> list[AutoModPunishment]:
"""This checks a list of attachments for attachments that match the configured list of hashes

Args:
config (munch.Munch): The guild config to check with
attachments (list[discord.Attachment]): The list of attachments to search

Returns:
list[AutoModPunishment]: The automod violations that the given message violated
"""
violations = []

for attachment in attachments:
file_hash = await auxiliary.get_attachment_hash(attachment)
if file_hash in config.extensions.automod.banned_file_hashes.value:
violations.append(
AutoModPunishment(
f"{attachment.filename} matches a banned file hash",
recommend_delete=True,
recommend_warn=False,
recommend_mute=3600,
)
)

return violations


def handle_mentions(
config: munch.Munch, message: discord.Message
) -> list[AutoModPunishment]:
Expand Down
18 changes: 15 additions & 3 deletions techsupport_bot/functions/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import discord
import munch
from botlogging import LogContext, LogLevel
from core import cogs, extensionconfig
from core import auxiliary, cogs, extensionconfig
from discord.ext import commands

if TYPE_CHECKING:
Expand Down Expand Up @@ -193,13 +193,13 @@ async def send_message(
attachments.insert(0, await author.display_avatar.to_file(filename="avatar.png"))

# Make and send the embed and files
embed = build_embed(
embed = await build_embed(
message, author, src_channel, content_override, special_flags=special_flags
)
await dest_channel.send(embed=embed, files=attachments[:11])


def build_embed(
async def build_embed(
message: discord.Message,
author: discord.Member,
src_channel: discord.abc.GuildChannel | discord.Thread,
Expand Down Expand Up @@ -266,6 +266,18 @@ def build_embed(
value=", ".join(generate_role_list(author)),
)

# Add file hashes, if relevant
file_hash_string = ""

if message.attachments:
parts = []
for attachment in message.attachments:
file_hash = await auxiliary.get_attachment_hash(attachment)
parts.append(f"{attachment.filename}: {file_hash}")

file_hash_string = ", ".join(parts)
embed.add_field(name="File Hashes", value=file_hash_string)

# Flags
if special_flags:
embed.add_field(name="Flags", value=", ".join(special_flags))
Expand Down
2 changes: 1 addition & 1 deletion techsupport_bot/functions/paste.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async def response(
"\n"
) > self.max_newlines(config.extensions.paste.length_limit.value):
if "automod" in config.get("enabled_extensions", []):
automod_actions = automod.run_all_checks(config, ctx.message)
automod_actions = await automod.run_all_checks(config, ctx.message)
automod_final = automod.process_automod_violations(automod_actions)
if automod_final and automod_final.delete_message:
return
Expand Down
Loading