diff --git a/src/common/ChatterinoSetting.hpp b/src/common/ChatterinoSetting.hpp index f2006e6d7..f53c99ff7 100644 --- a/src/common/ChatterinoSetting.hpp +++ b/src/common/ChatterinoSetting.hpp @@ -148,6 +148,9 @@ struct IsChatterinoSettingT : std::false_type { template struct IsChatterinoSettingT> : std::true_type { }; +template +struct IsChatterinoSettingT> : std::true_type { +}; template concept IsChatterinoSetting = IsChatterinoSettingT::value; diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index f88a7c499..00b5eedbf 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -26,6 +26,7 @@ #include "providers/twitch/api/Helix.hpp" #include "providers/twitch/ChannelPointReward.hpp" #include "providers/twitch/PubSubActions.hpp" +#include "providers/twitch/pubsubmessages/AutoMod.hpp" #include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchBadge.hpp" #include "providers/twitch/TwitchBadges.hpp" @@ -1637,6 +1638,12 @@ std::pair MessageBuilder::makeAutomodMessage( { MessageBuilder builder, builder2; + if (action.reasonCode == PubSubAutoModQueueMessage::Reason::BlockedTerm) + { + builder.message().flags.set(MessageFlag::AutoModBlockedTerm); + builder2.message().flags.set(MessageFlag::AutoModBlockedTerm); + } + // // Builder for AutoMod message with explanation builder.message().id = "automod_" + action.msgID; diff --git a/src/messages/MessageFlag.hpp b/src/messages/MessageFlag.hpp index 60c213f03..306587a09 100644 --- a/src/messages/MessageFlag.hpp +++ b/src/messages/MessageFlag.hpp @@ -52,6 +52,8 @@ enum class MessageFlag : std::int64_t { Action = (1LL << 36), /// The message is sent in a different source channel as part of a Shared Chat session SharedMessage = (1LL << 37), + /// AutoMod message that showed up due to containing a blocked term in the channel + AutoModBlockedTerm = (1LL << 38), }; using MessageFlags = FlagsEnum; diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp index 81c75c6b4..303abc44a 100644 --- a/src/messages/layouts/MessageLayout.cpp +++ b/src/messages/layouts/MessageLayout.cpp @@ -141,6 +141,9 @@ void MessageLayout::actuallyLayout(const MessageLayoutContext &ctx) bool hideModerated = getSettings()->hideModerated; bool hideModerationActions = getSettings()->hideModerationActions; + bool hideBlockedTermAutomodMessages = + getSettings()->showBlockedTermAutomodMessages.getEnum() == + ShowModerationState::Never; bool hideSimilar = getSettings()->hideSimilar; bool hideReplies = !ctx.flags.has(MessageElementFlag::RepliedMessage); @@ -154,6 +157,12 @@ void MessageLayout::actuallyLayout(const MessageLayoutContext &ctx) continue; } + if (hideBlockedTermAutomodMessages && + this->message_->flags.has(MessageFlag::AutoModBlockedTerm)) + { + continue; + } + if (this->message_->flags.has(MessageFlag::Timeout) || this->message_->flags.has(MessageFlag::Untimeout)) { diff --git a/src/providers/twitch/PubSubActions.hpp b/src/providers/twitch/PubSubActions.hpp index 5f83b4051..58e1a35e3 100644 --- a/src/providers/twitch/PubSubActions.hpp +++ b/src/providers/twitch/PubSubActions.hpp @@ -1,5 +1,7 @@ #pragma once +#include "providers/twitch/pubsubmessages/AutoMod.hpp" + #include #include #include @@ -143,6 +145,7 @@ struct AutomodAction : PubSubAction { QString message; QString reason; + PubSubAutoModQueueMessage::Reason reasonCode; QString msgID; }; diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 9e2fe2fbf..901ac0525 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -494,6 +494,7 @@ void TwitchIrcServer::initialize() action.msgID = msg.messageID; action.message = msg.messageText; + action.reasonCode = msg.reason; // this message also contains per-word automod data, which could be implemented diff --git a/src/providers/twitch/pubsubmessages/AutoMod.cpp b/src/providers/twitch/pubsubmessages/AutoMod.cpp index 697db1e32..3dd3d77df 100644 --- a/src/providers/twitch/pubsubmessages/AutoMod.cpp +++ b/src/providers/twitch/pubsubmessages/AutoMod.cpp @@ -15,6 +15,10 @@ PubSubAutoModQueueMessage::PubSubAutoModQueueMessage(const QJsonObject &root) this->type = oType.value(); } + this->reason = + qmagicenum::enumCast(data.value("reason_code").toString()) + .value_or(Reason::INVALID); + auto contentClassification = data.value("content_classification").toObject(); diff --git a/src/providers/twitch/pubsubmessages/AutoMod.hpp b/src/providers/twitch/pubsubmessages/AutoMod.hpp index 9f40d39da..6de09ee79 100644 --- a/src/providers/twitch/pubsubmessages/AutoMod.hpp +++ b/src/providers/twitch/pubsubmessages/AutoMod.hpp @@ -13,8 +13,17 @@ struct PubSubAutoModQueueMessage { INVALID, }; + + enum class Reason { + AutoMod, + BlockedTerm, + + INVALID, + }; + QString typeString; Type type = Type::INVALID; + Reason reason = Reason::INVALID; QJsonObject data; @@ -51,3 +60,20 @@ constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< return default_tag; } } + +template <> +constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< + chatterino::PubSubAutoModQueueMessage::Reason>( + chatterino::PubSubAutoModQueueMessage::Reason value) noexcept +{ + switch (value) + { + case chatterino::PubSubAutoModQueueMessage::Reason::AutoMod: + return "AutoModCaughtMessageReason"; + case chatterino::PubSubAutoModQueueMessage::Reason::BlockedTerm: + return "BlockedTermCaughtMessageReason"; + + default: + return default_tag; + } +} diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 94366dabe..fa039184e 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -69,6 +69,13 @@ enum class ChatSendProtocol : int { Helix = 2, }; +enum class ShowModerationState : int { + // Always show this moderation-related item + Always = 0, + // Never show this moderation-related item + Never = 1, +}; + enum StreamerModeSetting { Disabled = 0, Enabled = 1, @@ -345,6 +352,10 @@ public: IntSetting timeoutStackStyle = { "/moderation/timeoutStackStyle", static_cast(TimeoutStackStyle::Default)}; + EnumStringSetting showBlockedTermAutomodMessages = { + "/moderation/showBlockedTermAutomodMessages", + ShowModerationState::Always, + }; /// Highlighting // BoolSetting enableHighlights = {"/highlighting/enabled", true}; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 88d5ec565..75d09bde7 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -140,6 +140,8 @@ WindowManager::WindowManager(const Paths &paths, Settings &settings, this->forceLayoutChannelViewsListener.add(settings.enableRedeemedHighlight); this->forceLayoutChannelViewsListener.add(settings.colorUsernames); this->forceLayoutChannelViewsListener.add(settings.boldUsernames); + this->forceLayoutChannelViewsListener.add( + settings.showBlockedTermAutomodMessages); this->layoutChannelViewsListener.add(settings.timestampFormat); this->layoutChannelViewsListener.add(fonts.fontChanged); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 7103fb527..f76442e02 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -1167,6 +1167,14 @@ void GeneralPage::initLayout(GeneralPageView &layout) layout.addIntInput("Usercard scrollback limit (requires restart)", s.scrollbackUsercardLimit, 100, 100000, 100); + layout.addDropdownEnumClass( + "Show blocked term automod messages", + qmagicenum::enumNames(), + s.showBlockedTermAutomodMessages, + "Show messages that are blocked by AutoMod for containing a public " + "blocked term in the current channel.", + {}); + layout.addDropdown( "Stack timeouts", {"Stack", "Stack until timeout", "Don't stack"}, s.timeoutStackStyle,