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
19 changed files with 348 additions and 193 deletions
+2
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>;
-153
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>())
{
-3
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;
+29 -15
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();
}
+6 -2
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
+7 -3
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)
@@ -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;
@@ -39,6 +39,7 @@ struct MessagePreferences {
bool enableElevatedMessageHighlight{};
bool enableFirstMessageHighlight{};
bool enableSubHighlight{};
bool enableAutomodHighlight{};
bool alternateMessages{};
bool separateMessages{};