From 470d37a8db0edf793e02912a9000cf5bc600220a Mon Sep 17 00:00:00 2001 From: ajax146 <31014239+ajax146@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:32:49 -0700 Subject: [PATCH 1/2] Add file hash blocking capabilities into automod --- techsupport_bot/core/auxiliary.py | 14 +++++++++ techsupport_bot/functions/automod.py | 43 ++++++++++++++++++++++++++-- techsupport_bot/functions/logger.py | 18 ++++++++++-- techsupport_bot/functions/paste.py | 2 +- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/techsupport_bot/core/auxiliary.py b/techsupport_bot/core/auxiliary.py index e77d550f..4a840b75 100644 --- a/techsupport_bot/core/auxiliary.py +++ b/techsupport_bot/core/auxiliary.py @@ -5,6 +5,7 @@ from __future__ import annotations +import hashlib import json from functools import wraps from typing import TYPE_CHECKING, Any @@ -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() diff --git a/techsupport_bot/functions/automod.py b/techsupport_bot/functions/automod.py index 84c24370..bd7cd035 100644 --- a/techsupport_bot/functions/automod.py +++ b/techsupport_bot/functions/automod.py @@ -3,6 +3,7 @@ from __future__ import annotations import datetime +import hashlib import re from dataclasses import dataclass from typing import TYPE_CHECKING, Self @@ -11,7 +12,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: @@ -205,7 +206,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 @@ -455,7 +456,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 @@ -475,6 +476,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 @@ -528,6 +530,41 @@ 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: + try: + file_hash = await auxiliary.get_attachment_hash(attachment) + except Exception: + # Catch all, in case message is deleted during download + # Not ideal + continue + + 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]: diff --git a/techsupport_bot/functions/logger.py b/techsupport_bot/functions/logger.py index 97779baa..6cb4aefa 100644 --- a/techsupport_bot/functions/logger.py +++ b/techsupport_bot/functions/logger.py @@ -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: @@ -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, @@ -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)) diff --git a/techsupport_bot/functions/paste.py b/techsupport_bot/functions/paste.py index 8f9b4e94..ef8d0744 100644 --- a/techsupport_bot/functions/paste.py +++ b/techsupport_bot/functions/paste.py @@ -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 From d97b69de88d38e34b01effcdde8f8eb2ab6a5bb7 Mon Sep 17 00:00:00 2001 From: ajax146 <31014239+ajax146@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:34:22 -0700 Subject: [PATCH 2/2] Fix some issues --- techsupport_bot/functions/automod.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/techsupport_bot/functions/automod.py b/techsupport_bot/functions/automod.py index bd7cd035..be57fa4e 100644 --- a/techsupport_bot/functions/automod.py +++ b/techsupport_bot/functions/automod.py @@ -3,7 +3,6 @@ from __future__ import annotations import datetime -import hashlib import re from dataclasses import dataclass from typing import TYPE_CHECKING, Self @@ -545,13 +544,7 @@ async def handle_file_hashes( violations = [] for attachment in attachments: - try: - file_hash = await auxiliary.get_attachment_hash(attachment) - except Exception: - # Catch all, in case message is deleted during download - # Not ideal - continue - + file_hash = await auxiliary.get_attachment_hash(attachment) if file_hash in config.extensions.automod.banned_file_hashes.value: violations.append( AutoModPunishment(