feat: add sound and flash alert for automod caught messages (#5026)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
iProdigy 2023-12-25 17:17:44 -06:00 committed by GitHub
parent 1006bf955a
commit eb12cfa50b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 348 additions and 193 deletions

View file

@ -3,7 +3,7 @@
## Unversioned
- Major: Allow use of Twitch follower emotes in other channels if subscribed. (#4922)
- Major: Add `/automod` split to track automod caught messages across all open channels the user moderates. (#4986)
- Major: Add `/automod` split to track automod caught messages across all open channels the user moderates. (#4986, #5026)
- Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
- Minor: The account switcher is now styled to match your theme. (#4817)
- Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795)

View file

@ -552,7 +552,8 @@ void Application::initPubSub()
senderDisplayName, senderColor};
postToThread([chan, action] {
const auto p =
makeAutomodMessage(action, chan->getName());
TwitchMessageBuilder::makeAutomodMessage(
action, chan->getName());
chan->addMessage(p.first);
chan->addMessage(p.second);
@ -584,7 +585,8 @@ void Application::initPubSub()
}
postToThread([chan, action] {
const auto p = makeAutomodMessage(action, chan->getName());
const auto p = TwitchMessageBuilder::makeAutomodMessage(
action, chan->getName());
chan->addMessage(p.first);
chan->addMessage(p.second);
});
@ -626,7 +628,8 @@ void Application::initPubSub()
}
postToThread([chan, action] {
const auto p = makeAutomodInfoMessage(action);
const auto p =
TwitchMessageBuilder::makeAutomodInfoMessage(action);
chan->addMessage(p);
});
});

View file

@ -204,6 +204,41 @@ void rebuildMessageHighlights(Settings &settings,
{
checks.emplace_back(highlightPhraseCheck(highlight));
}
if (settings.enableAutomodHighlight)
{
const auto highlightSound =
settings.enableAutomodHighlightSound.getValue();
const auto highlightAlert =
settings.enableAutomodHighlightTaskbar.getValue();
const auto highlightSoundUrlValue =
settings.automodHighlightSoundUrl.getValue();
checks.emplace_back(HighlightCheck{
[=](const auto & /*args*/, const auto & /*badges*/,
const auto & /*senderName*/, const auto & /*originalMessage*/,
const auto &flags,
const auto /*self*/) -> std::optional<HighlightResult> {
if (!flags.has(MessageFlag::AutoModOffendingMessage))
{
return std::nullopt;
}
std::optional<QUrl> highlightSoundUrl;
if (!highlightSoundUrlValue.isEmpty())
{
highlightSoundUrl = highlightSoundUrlValue;
}
return HighlightResult{
highlightAlert, // alert
highlightSound, // playSound
highlightSoundUrl, // customSoundUrl
nullptr, // color
false, // showInMentions
};
}});
}
}
void rebuildUserHighlights(Settings &settings,
@ -434,6 +469,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/)
this->rebuildListener_.addSetting(settings.threadHighlightSoundUrl);
this->rebuildListener_.addSetting(settings.showThreadHighlightInMentions);
this->rebuildListener_.addSetting(settings.enableAutomodHighlight);
this->rebuildListener_.addSetting(settings.enableAutomodHighlightSound);
this->rebuildListener_.addSetting(settings.enableAutomodHighlightTaskbar);
this->rebuildListener_.addSetting(settings.automodHighlightSoundUrl);
this->rebuildListener_.setCB([this, &settings] {
qCDebug(chatterinoHighlights)
<< "Rebuild checks because a setting changed";

View file

@ -234,6 +234,30 @@ void HighlightModel::afterInit()
this->insertCustomRow(threadMessageRow,
HighlightRowIndexes::ThreadMessageRow);
// Highlight settings for automod caught messages
const std::vector<QStandardItem *> automodRow = this->createRow();
setBoolItem(automodRow[Column::Pattern],
getSettings()->enableAutomodHighlight.getValue(), true, false);
automodRow[Column::Pattern]->setData("AutoMod Caught Messages",
Qt::DisplayRole);
automodRow[Column::ShowInMentions]->setFlags({});
setBoolItem(automodRow[Column::FlashTaskbar],
getSettings()->enableAutomodHighlightTaskbar.getValue(), true,
false);
setBoolItem(automodRow[Column::PlaySound],
getSettings()->enableAutomodHighlightSound.getValue(), true,
false);
automodRow[Column::UseRegex]->setFlags({});
automodRow[Column::CaseSensitive]->setFlags({});
const auto automodSound =
QUrl(getSettings()->automodHighlightSoundUrl.getValue());
setFilePathItem(automodRow[Column::SoundPath], automodSound, false);
automodRow[Column::Color]->setFlags(Qt::ItemFlag::NoItemFlags);
this->insertCustomRow(automodRow, HighlightRowIndexes::AutomodRow);
}
void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
@ -278,6 +302,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
getSettings()->enableThreadHighlight.setValue(
value.toBool());
}
else if (rowIndex == HighlightRowIndexes::AutomodRow)
{
getSettings()->enableAutomodHighlight.setValue(
value.toBool());
}
}
}
break;
@ -336,6 +365,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
getSettings()->enableThreadHighlightTaskbar.setValue(
value.toBool());
}
else if (rowIndex == HighlightRowIndexes::AutomodRow)
{
getSettings()->enableAutomodHighlightTaskbar.setValue(
value.toBool());
}
}
}
break;
@ -377,6 +411,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
getSettings()->enableThreadHighlightSound.setValue(
value.toBool());
}
else if (rowIndex == HighlightRowIndexes::AutomodRow)
{
getSettings()->enableAutomodHighlightSound.setValue(
value.toBool());
}
}
}
break;
@ -412,6 +451,11 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
getSettings()->threadHighlightSoundUrl.setValue(
value.toString());
}
else if (rowIndex == HighlightRowIndexes::AutomodRow)
{
getSettings()->automodHighlightSoundUrl.setValue(
value.toString());
}
}
}
break;

View file

@ -34,6 +34,7 @@ public:
FirstMessageRow = 4,
ElevatedMessageRow = 5,
ThreadMessageRow = 6,
AutomodRow = 7,
};
enum UserHighlightRowIndexes {

View file

@ -50,6 +50,8 @@ enum class MessageFlag : int64_t {
LiveUpdatesAdd = (1LL << 28),
LiveUpdatesRemove = (1LL << 29),
LiveUpdatesUpdate = (1LL << 30),
/// The message caught by AutoMod containing the user who sent the message & its contents
AutoModOffendingMessage = (1LL << 31),
};
using MessageFlags = FlagsEnum<MessageFlag>;

View file

@ -78,159 +78,6 @@ MessagePtr makeSystemMessage(const QString &text, const QTime &time)
return MessageBuilder(systemMessage, text, time).release();
}
EmotePtr makeAutoModBadge()
{
return std::make_shared<Emote>(Emote{
EmoteName{},
ImageSet{Image::fromResourcePixmap(getResources().twitch.automod)},
Tooltip{"AutoMod"},
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
}
MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action)
{
auto builder = MessageBuilder();
QString text("AutoMod: ");
builder.emplace<TimestampElement>();
builder.message().flags.set(MessageFlag::PubSub);
// AutoMod shield badge
builder.emplace<BadgeElement>(makeAutoModBadge(),
MessageElementFlag::BadgeChannelAuthority);
// AutoMod "username"
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::BoldUsername,
MessageColor(QColor("blue")),
FontStyle::ChatMediumBold);
builder.emplace<TextElement>(
"AutoMod:", MessageElementFlag::NonBoldUsername,
MessageColor(QColor("blue")));
switch (action.type)
{
case AutomodInfoAction::OnHold: {
QString info("Hey! Your message is being checked "
"by mods and has not been sent.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
case AutomodInfoAction::Denied: {
QString info("Mods have removed your message.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
case AutomodInfoAction::Approved: {
QString info("Mods have accepted your message.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
}
builder.message().flags.set(MessageFlag::AutoMod);
builder.message().messageText = text;
builder.message().searchText = text;
auto message = builder.release();
return message;
}
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
const AutomodAction &action, const QString &channelName)
{
MessageBuilder builder, builder2;
//
// Builder for AutoMod message with explanation
builder.message().loginName = "automod";
builder.message().channelName = channelName;
builder.message().flags.set(MessageFlag::PubSub);
builder.message().flags.set(MessageFlag::Timeout);
builder.message().flags.set(MessageFlag::AutoMod);
// AutoMod shield badge
builder.emplace<BadgeElement>(makeAutoModBadge(),
MessageElementFlag::BadgeChannelAuthority);
// AutoMod "username"
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::BoldUsername,
MessageColor(QColor("blue")),
FontStyle::ChatMediumBold);
builder.emplace<TextElement>(
"AutoMod:", MessageElementFlag::NonBoldUsername,
MessageColor(QColor("blue")));
// AutoMod header message
builder.emplace<TextElement>(
("Held a message for reason: " + action.reason +
". Allow will post it in chat. "),
MessageElementFlag::Text, MessageColor::Text);
// Allow link button
builder
.emplace<TextElement>("Allow", MessageElementFlag::Text,
MessageColor(QColor("green")),
FontStyle::ChatMediumBold)
->setLink({Link::AutoModAllow, action.msgID});
// Deny link button
builder
.emplace<TextElement>(" Deny", MessageElementFlag::Text,
MessageColor(QColor("red")),
FontStyle::ChatMediumBold)
->setLink({Link::AutoModDeny, action.msgID});
// ID of message caught by AutoMod
// builder.emplace<TextElement>(action.msgID, MessageElementFlag::Text,
// MessageColor::Text);
auto text1 =
QString("AutoMod: Held a message for reason: %1. Allow will post "
"it in chat. Allow Deny")
.arg(action.reason);
builder.message().messageText = text1;
builder.message().searchText = text1;
auto message1 = builder.release();
//
// Builder for offender's message
builder2.message().channelName = channelName;
builder2
.emplace<TextElement>("#" + channelName,
MessageElementFlag::ChannelName,
MessageColor::System)
->setLink({Link::JumpToChannel, channelName});
builder2.emplace<TimestampElement>();
builder2.emplace<TwitchModerationElement>();
builder2.message().loginName = action.target.login;
builder2.message().flags.set(MessageFlag::PubSub);
builder2.message().flags.set(MessageFlag::Timeout);
builder2.message().flags.set(MessageFlag::AutoMod);
// sender username
builder2
.emplace<TextElement>(
action.target.displayName + ":", MessageElementFlag::BoldUsername,
MessageColor(action.target.color), FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, action.target.login});
builder2
.emplace<TextElement>(action.target.displayName + ":",
MessageElementFlag::NonBoldUsername,
MessageColor(action.target.color))
->setLink({Link::UserInfo, action.target.login});
// sender's message caught by AutoMod
builder2.emplace<TextElement>(action.message, MessageElementFlag::Text,
MessageColor::Text);
auto text2 =
QString("%1: %2").arg(action.target.displayName, action.message);
builder2.message().messageText = text2;
builder2.message().searchText = text2;
auto message2 = builder2.release();
return std::make_pair(message1, message2);
}
MessageBuilder::MessageBuilder()
: message_(std::make_shared<Message>())
{

View file

@ -53,9 +53,6 @@ const ImageUploaderResultTag imageUploaderResultMessage{};
MessagePtr makeSystemMessage(const QString &text);
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
const AutomodAction &action, const QString &channelName);
MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action);
struct MessageParseArgs {
bool disablePingSounds = false;

View file

@ -18,6 +18,8 @@
#include <QFileInfo>
#include <optional>
namespace {
using namespace chatterino;
@ -170,18 +172,10 @@ void SharedMessageBuilder::parseHighlights()
this->highlightAlert_ = highlightResult.alert;
this->highlightSound_ = highlightResult.playSound;
this->highlightSoundCustomUrl_ = highlightResult.customSoundUrl;
this->message().highlightColor = highlightResult.color;
if (highlightResult.customSoundUrl)
{
this->highlightSoundUrl_ = *highlightResult.customSoundUrl;
}
else
{
this->highlightSoundUrl_ = getFallbackHighlightSound();
}
if (highlightResult.showInMentions)
{
this->message().flags.set(MessageFlag::ShowInMentions);
@ -199,6 +193,15 @@ void SharedMessageBuilder::appendChannelName()
}
void SharedMessageBuilder::triggerHighlights()
{
SharedMessageBuilder::triggerHighlights(
this->channel->getName(), this->highlightSound_,
this->highlightSoundCustomUrl_, this->highlightAlert_);
}
void SharedMessageBuilder::triggerHighlights(
const QString &channelName, bool playSound,
const std::optional<QUrl> &customSoundUrl, bool windowAlert)
{
if (isInStreamerMode() && getSettings()->streamerModeMuteMentions)
{
@ -206,21 +209,32 @@ void SharedMessageBuilder::triggerHighlights()
return;
}
if (getSettings()->isMutedChannel(this->channel->getName()))
if (getSettings()->isMutedChannel(channelName))
{
// Do nothing. Pings are muted in this channel.
return;
}
bool hasFocus = (QApplication::focusWidget() != nullptr);
bool resolveFocus = !hasFocus || getSettings()->highlightAlwaysPlaySound;
const bool hasFocus = (QApplication::focusWidget() != nullptr);
const bool resolveFocus =
!hasFocus || getSettings()->highlightAlwaysPlaySound;
if (this->highlightSound_ && resolveFocus)
if (playSound && resolveFocus)
{
getIApp()->getSound()->play(this->highlightSoundUrl_);
// TODO(C++23): optional or_else
QUrl soundUrl;
if (customSoundUrl)
{
soundUrl = *customSoundUrl;
}
else
{
soundUrl = getFallbackHighlightSound();
}
getIApp()->getSound()->play(soundUrl);
}
if (this->highlightAlert_)
if (windowAlert)
{
getApp()->windows->sendAlert();
}

View file

@ -8,6 +8,8 @@
#include <QColor>
#include <QUrl>
#include <optional>
namespace chatterino {
class Badge;
@ -57,6 +59,9 @@ protected:
// parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function
virtual void parseHighlights();
static void triggerHighlights(const QString &channelName, bool playSound,
const std::optional<QUrl> &customSoundUrl,
bool windowAlert);
void appendChannelName();
@ -72,8 +77,7 @@ protected:
bool highlightAlert_ = false;
bool highlightSound_ = false;
QUrl highlightSoundUrl_;
std::optional<QUrl> highlightSoundCustomUrl_{};
};
} // namespace chatterino

View file

@ -342,9 +342,13 @@ void MessageLayout::updateBuffer(QPixmap *buffer,
this->message_->flags.has(MessageFlag::HighlightedWhisper)) &&
!this->flags.has(MessageLayoutFlag::IgnoreHighlights))
{
// Blend highlight color with usual background color
backgroundColor =
blendColors(backgroundColor, *this->message_->highlightColor);
assert(this->message_->highlightColor);
if (this->message_->highlightColor)
{
// Blend highlight color with usual background color
backgroundColor =
blendColors(backgroundColor, *this->message_->highlightColor);
}
}
else if (this->message_->flags.has(MessageFlag::Subscription) &&
ctx.preferences.enableSubHighlight)

View file

@ -47,6 +47,12 @@ void MessagePreferences::connectSettings(Settings *settings,
},
holder);
settings->enableAutomodHighlight.connect(
[this](const auto &newValue) {
this->enableAutomodHighlight = newValue;
},
holder);
settings->alternateMessages.connect(
[this](const auto &newValue) {
this->alternateMessages = newValue;

View file

@ -39,6 +39,7 @@ struct MessagePreferences {
bool enableElevatedMessageHighlight{};
bool enableFirstMessageHighlight{};
bool enableSubHighlight{};
bool enableAutomodHighlight{};
bool alternateMessages{};
bool separateMessages{};

View file

@ -5,6 +5,7 @@
#include "common/Literals.hpp"
#include "common/QLogging.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "controllers/ignores/IgnoreController.hpp"
#include "controllers/ignores/IgnorePhrase.hpp"
#include "controllers/userdata/UserDataController.hpp"
@ -1765,6 +1766,173 @@ MessagePtr TwitchMessageBuilder::buildHypeChatMessage(
return builder.release();
}
EmotePtr makeAutoModBadge()
{
return std::make_shared<Emote>(Emote{
EmoteName{},
ImageSet{Image::fromResourcePixmap(getResources().twitch.automod)},
Tooltip{"AutoMod"},
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
}
MessagePtr TwitchMessageBuilder::makeAutomodInfoMessage(
const AutomodInfoAction &action)
{
auto builder = MessageBuilder();
QString text("AutoMod: ");
builder.emplace<TimestampElement>();
builder.message().flags.set(MessageFlag::PubSub);
// AutoMod shield badge
builder.emplace<BadgeElement>(makeAutoModBadge(),
MessageElementFlag::BadgeChannelAuthority);
// AutoMod "username"
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::BoldUsername,
MessageColor(QColor("blue")),
FontStyle::ChatMediumBold);
builder.emplace<TextElement>(
"AutoMod:", MessageElementFlag::NonBoldUsername,
MessageColor(QColor("blue")));
switch (action.type)
{
case AutomodInfoAction::OnHold: {
QString info("Hey! Your message is being checked "
"by mods and has not been sent.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
case AutomodInfoAction::Denied: {
QString info("Mods have removed your message.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
case AutomodInfoAction::Approved: {
QString info("Mods have accepted your message.");
text += info;
builder.emplace<TextElement>(info, MessageElementFlag::Text,
MessageColor::Text);
}
break;
}
builder.message().flags.set(MessageFlag::AutoMod);
builder.message().messageText = text;
builder.message().searchText = text;
auto message = builder.release();
return message;
}
std::pair<MessagePtr, MessagePtr> TwitchMessageBuilder::makeAutomodMessage(
const AutomodAction &action, const QString &channelName)
{
MessageBuilder builder, builder2;
//
// Builder for AutoMod message with explanation
builder.message().loginName = "automod";
builder.message().channelName = channelName;
builder.message().flags.set(MessageFlag::PubSub);
builder.message().flags.set(MessageFlag::Timeout);
builder.message().flags.set(MessageFlag::AutoMod);
// AutoMod shield badge
builder.emplace<BadgeElement>(makeAutoModBadge(),
MessageElementFlag::BadgeChannelAuthority);
// AutoMod "username"
builder.emplace<TextElement>("AutoMod:", MessageElementFlag::BoldUsername,
MessageColor(QColor("blue")),
FontStyle::ChatMediumBold);
builder.emplace<TextElement>(
"AutoMod:", MessageElementFlag::NonBoldUsername,
MessageColor(QColor("blue")));
// AutoMod header message
builder.emplace<TextElement>(
("Held a message for reason: " + action.reason +
". Allow will post it in chat. "),
MessageElementFlag::Text, MessageColor::Text);
// Allow link button
builder
.emplace<TextElement>("Allow", MessageElementFlag::Text,
MessageColor(QColor("green")),
FontStyle::ChatMediumBold)
->setLink({Link::AutoModAllow, action.msgID});
// Deny link button
builder
.emplace<TextElement>(" Deny", MessageElementFlag::Text,
MessageColor(QColor("red")),
FontStyle::ChatMediumBold)
->setLink({Link::AutoModDeny, action.msgID});
// ID of message caught by AutoMod
// builder.emplace<TextElement>(action.msgID, MessageElementFlag::Text,
// MessageColor::Text);
auto text1 =
QString("AutoMod: Held a message for reason: %1. Allow will post "
"it in chat. Allow Deny")
.arg(action.reason);
builder.message().messageText = text1;
builder.message().searchText = text1;
auto message1 = builder.release();
//
// Builder for offender's message
builder2.message().channelName = channelName;
builder2
.emplace<TextElement>("#" + channelName,
MessageElementFlag::ChannelName,
MessageColor::System)
->setLink({Link::JumpToChannel, channelName});
builder2.emplace<TimestampElement>();
builder2.emplace<TwitchModerationElement>();
builder2.message().loginName = action.target.login;
builder2.message().flags.set(MessageFlag::PubSub);
builder2.message().flags.set(MessageFlag::Timeout);
builder2.message().flags.set(MessageFlag::AutoMod);
builder2.message().flags.set(MessageFlag::AutoModOffendingMessage);
// sender username
builder2
.emplace<TextElement>(
action.target.displayName + ":", MessageElementFlag::BoldUsername,
MessageColor(action.target.color), FontStyle::ChatMediumBold)
->setLink({Link::UserInfo, action.target.login});
builder2
.emplace<TextElement>(action.target.displayName + ":",
MessageElementFlag::NonBoldUsername,
MessageColor(action.target.color))
->setLink({Link::UserInfo, action.target.login});
// sender's message caught by AutoMod
builder2.emplace<TextElement>(action.message, MessageElementFlag::Text,
MessageColor::Text);
auto text2 =
QString("%1: %2").arg(action.target.displayName, action.message);
builder2.message().messageText = text2;
builder2.message().searchText = text2;
auto message2 = builder2.release();
// Normally highlights would be checked & triggered during the builder parse steps
// and when the message is added to the channel
// We do this a bit weird since the message comes in from PubSub and not the normal message route
auto [highlighted, highlightResult] = getIApp()->getHighlights()->check(
{}, {}, action.target.login, action.message, message2->flags);
if (highlighted)
{
SharedMessageBuilder::triggerHighlights(
channelName, highlightResult.playSound,
highlightResult.customSoundUrl, highlightResult.alert);
}
return std::make_pair(message1, message2);
}
void TwitchMessageBuilder::setThread(std::shared_ptr<MessageThread> thread)
{
this->thread_ = std::move(thread);

View file

@ -89,6 +89,10 @@ public:
static MessagePtr buildHypeChatMessage(Communi::IrcPrivateMessage *message);
static std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
const AutomodAction &action, const QString &channelName);
static MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action);
// Shares some common logic from SharedMessageBuilder::parseBadgeTag
static std::unordered_map<QString, QString> parseBadgeInfoTag(
const QVariantMap &tags);

View file

@ -375,6 +375,23 @@ public:
""};
QStringSetting subHighlightColor = {"/highlighting/subHighlightColor", ""};
BoolSetting enableAutomodHighlight = {
"/highlighting/automod/enabled",
true,
};
BoolSetting enableAutomodHighlightSound = {
"/highlighting/automod/enableSound",
false,
};
BoolSetting enableAutomodHighlightTaskbar = {
"/highlighting/automod/enableTaskbarFlashing",
false,
};
QStringSetting automodHighlightSoundUrl = {
"/highlighting/automod/soundUrl",
"",
};
BoolSetting enableThreadHighlight = {
"/highlighting/thread/nameIsHighlightKeyword", true};
BoolSetting showThreadHighlightInMentions = {

View file

@ -1093,12 +1093,13 @@ void ChannelView::messageAppended(MessagePtr &message,
if (!messageFlags->has(MessageFlag::DoNotTriggerNotification))
{
if (messageFlags->has(MessageFlag::Highlighted) &&
messageFlags->has(MessageFlag::ShowInMentions) &&
!messageFlags->has(MessageFlag::Subscription) &&
(getSettings()->highlightMentions ||
this->channel_->getType() != Channel::Type::TwitchMentions))
if ((messageFlags->has(MessageFlag::Highlighted) &&
messageFlags->has(MessageFlag::ShowInMentions) &&
!messageFlags->has(MessageFlag::Subscription) &&
(getSettings()->highlightMentions ||
this->channel_->getType() != Channel::Type::TwitchMentions)) ||
(this->channel_->getType() == Channel::Type::TwitchAutomod &&
getSettings()->enableAutomodHighlight))
{
this->tabHighlightRequested.invoke(HighlightState::Highlighted);
}

View file

@ -26,6 +26,7 @@ ScrollbarHighlight::ScrollbarHighlight(const std::shared_ptr<QColor> color,
QColor ScrollbarHighlight::getColor() const
{
assert(this->color_);
return *this->color_;
}

View file

@ -370,21 +370,22 @@ void HighlightingPage::tableCellClicked(const QModelIndex &clicked,
EditableModelView *view,
HighlightTab tab)
{
if (!clicked.flags().testFlag(Qt::ItemIsEnabled))
{
return;
}
switch (tab)
{
case HighlightTab::Messages:
case HighlightTab::Users: {
using Column = HighlightModel::Column;
bool restrictColorRow =
(tab == HighlightTab::Messages &&
clicked.row() ==
HighlightModel::HighlightRowIndexes::WhisperRow);
if (clicked.column() == Column::SoundPath &&
clicked.flags().testFlag(Qt::ItemIsEnabled))
if (clicked.column() == Column::SoundPath)
{
this->openSoundDialog(clicked, view, Column::SoundPath);
}
else if (clicked.column() == Column::Color && !restrictColorRow)
else if (clicked.column() == Column::Color)
{
this->openColorDialog(clicked, view, tab);
}