From 6b9f64a83d555e86d29f7da6005e36a39ee98788 Mon Sep 17 00:00:00 2001 From: ebcq Date: Fri, 21 Aug 2026 20:13:14 +0200 Subject: [PATCH] feat: moderation: block executable attachments --- src/utils/moderation/blocked_extension.h | 34 ++++++++++++++++++++++++ src/utils/moderation/moderation.cpp | 22 +++++++++++++++ src/utils/moderation/moderation.h | 5 ++++ 3 files changed, 61 insertions(+) create mode 100644 src/utils/moderation/blocked_extension.h diff --git a/src/utils/moderation/blocked_extension.h b/src/utils/moderation/blocked_extension.h new file mode 100644 index 0000000..420610b --- /dev/null +++ b/src/utils/moderation/blocked_extension.h @@ -0,0 +1,34 @@ +#ifndef BLOCKED_EXTENSION_H +#define BLOCKED_EXTENSION_H + +#include +#include +#include +#include + +namespace moderation { + inline constexpr auto blockedExtensions = std::to_array({ + ".exe", ".dll", ".bat", ".cmd", ".scr", ".msi", ".ps1" + }); + + inline bool hasBlockedExtension(std::string_view filename) + { + const auto dot = filename.find_last_of('.'); + if (dot == std::string_view::npos) + return false; + + const std::string_view extension = filename.substr(dot); + + return std::ranges::any_of(blockedExtensions, + [extension](const std::string_view blocked) { + return extension.size() == blocked.size() + && std::ranges::equal(extension, blocked, + [](const char lhs, const char rhs) { + return std::tolower(static_cast(lhs)) + == std::tolower(static_cast(rhs)); + }); + }); + } +} + +#endif // BLOCKED_EXTENSION_H diff --git a/src/utils/moderation/moderation.cpp b/src/utils/moderation/moderation.cpp index f3e6d75..044bc51 100644 --- a/src/utils/moderation/moderation.cpp +++ b/src/utils/moderation/moderation.cpp @@ -1,6 +1,8 @@ #include "moderation.h" #include "../../globals/globals.h" +#include "blocked_extension.h" + ModerationService::ModerationService(dpp::cluster& bot) : bot(bot) {} void ModerationService::cleanupOldEntries(const std::chrono::steady_clock::time_point& now) @@ -38,6 +40,13 @@ std::string ModerationService::makeMessageSignature(const dpp::message& msg) return signature; } +bool ModerationService::hasBlockedAttachment(const dpp::message& msg) +{ + return std::any_of( + msg.attachments.begin(), msg.attachments.end(), + [](const dpp::attachment& att) { return moderation::hasBlockedExtension(att.filename); }); +} + bool ModerationService::handleMessage(const dpp::message_create_t& event) { if (event.msg.author.is_bot()) @@ -48,6 +57,19 @@ bool ModerationService::handleMessage(const dpp::message_create_t& event) event.msg.embeds.empty()) return false; + if (hasBlockedAttachment(event.msg)) + { + bot.message_delete(event.msg.id, event.msg.channel_id); + + dpp::message blockMessage( + event.msg.channel_id, + dpp::utility::user_mention(event.msg.author.id) + " " + std::string(executableBlockMsg)); + blockMessage.set_allowed_mentions(false, false, false, false, {event.msg.author.id}, {}); + bot.message_create(blockMessage); + + return true; + } + const auto now = std::chrono::steady_clock::now(); const auto userId = event.msg.author.id; const std::string signature = makeMessageSignature(event.msg); diff --git a/src/utils/moderation/moderation.h b/src/utils/moderation/moderation.h index 6eff698..28dddf2 100644 --- a/src/utils/moderation/moderation.h +++ b/src/utils/moderation/moderation.h @@ -35,6 +35,10 @@ class ModerationService "You have been jailed as you posted the same message on multiple channels. " "Please contact a staff member if you think this is a mistake."; + static constexpr std::string_view executableBlockMsg = + "Your message contained an executable attachment, " + "which is not allowed and has been removed."; + struct PostedMessage { dpp::snowflake channelId{}; @@ -49,6 +53,7 @@ class ModerationService }; static std::string makeMessageSignature(const dpp::message& msg); + static bool hasBlockedAttachment(const dpp::message& msg); void cleanupOldEntries(const std::chrono::steady_clock::time_point& now); dpp::cluster& bot;