feat(eventsub): implement suspicious user message (#6007)

This commit is contained in:
nerix
2025-03-01 13:48:01 +01:00
committed by GitHub
parent c5d4a3a69b
commit bced3c658d
22 changed files with 872 additions and 62 deletions
+17
View File
@@ -1522,10 +1522,27 @@ void TwitchChannel::refreshPubSub()
},
},
});
this->eventSubSuspiciousUserMessageHandle =
getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{
.subscriptionType = "channel.suspicious_user.message",
.subscriptionVersion = "1",
.conditions =
{
{
"broadcaster_user_id",
roomId,
},
{
"moderator_user_id",
currentAccount->getUserId(),
},
},
});
}
else
{
this->eventSubChannelModerateHandle.reset();
this->eventSubSuspiciousUserMessageHandle.reset();
}
getApp()->getTwitchPubSub()->listenToChannelPointRewards(roomId);
}
+1
View File
@@ -510,6 +510,7 @@ private:
eventsub::SubscriptionHandle eventSubChannelModerateHandle;
eventsub::SubscriptionHandle eventSubAutomodMessageHoldHandle;
eventsub::SubscriptionHandle eventSubSuspiciousUserMessageHandle;
friend class TwitchIrcServer;
friend class MessageBuilder;
+36 -4
View File
@@ -12,6 +12,7 @@
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "singletons/Settings.hpp"
#include "singletons/StreamerMode.hpp"
#include "singletons/WindowManager.hpp"
#include "util/Helpers.hpp"
#include "util/PostToThread.hpp"
@@ -243,9 +244,40 @@ void Connection::onChannelSuspiciousUserMessage(
const lib::messages::Metadata &metadata,
const lib::payload::channel_suspicious_user_message::v1::Payload &payload)
{
(void)metadata;
qCDebug(LOG) << "On channel suspicious user message for"
<< payload.event.broadcasterUserLogin.c_str();
// monitored chats are received over irc; in the future, we will use eventsub instead
if (payload.event.lowTrustStatus !=
lib::suspicious_users::Status::Restricted)
{
return;
}
if (getSettings()->streamerModeHideModActions &&
getApp()->getStreamerMode()->isEnabled())
{
return;
}
auto *channel = dynamic_cast<TwitchChannel *>(
getApp()
->getTwitch()
->getChannelOrEmpty(payload.event.broadcasterUserLogin.qt())
.get());
if (!channel || channel->isEmpty())
{
qCDebug(LOG)
<< "Suspicious message for broadcaster we're not interested in"
<< payload.event.broadcasterUserLogin.qt();
return;
}
auto time = chronoToQDateTime(metadata.messageTimestamp);
auto header = makeSuspiciousUserMessageHeader(channel, time, payload.event);
auto body = makeSuspiciousUserMessageBody(channel, time, payload.event);
runInGuiThread([channel, header, body] {
channel->addMessage(header, MessageContext::Original);
channel->addMessage(body, MessageContext::Original);
});
}
void Connection::onChannelSuspiciousUserUpdate(
@@ -254,7 +286,7 @@ void Connection::onChannelSuspiciousUserUpdate(
{
(void)metadata;
qCDebug(LOG) << "On channel suspicious user update for"
<< payload.event.broadcasterUserLogin.c_str();
<< payload.event.broadcasterUserLogin.qt();
}
QString Connection::getSessionID() const
@@ -637,6 +637,7 @@ MessagePtr makeAutomodHoldMessageBody(
displayName + ':', event.userLogin.qt(), MessageColor::Text,
channel->getUserColor(event.userLogin.qt()));
// sender's message caught by AutoMod
// XXX: add the structured message here
builder.emplace<TextElement>(event.message.text.qt(),
MessageElementFlag::Text, MessageColor::Text);
auto text = displayName % u": " % event.message.text.qt();
@@ -646,4 +647,113 @@ MessagePtr makeAutomodHoldMessageBody(
return builder.release();
}
MessagePtr makeSuspiciousUserMessageHeader(
TwitchChannel *channel, const QDateTime &time,
const lib::payload::channel_suspicious_user_message::v1::Event &event)
{
EventSubMessageBuilder builder(channel);
// Builder for low trust user message with explanation
builder->channelName = event.broadcasterUserLogin.qt();
builder->serverReceivedTime = time;
builder->flags.set(MessageFlag::LowTrustUsers);
// AutoMod shield badge
builder.emplace<BadgeElement>(makeAutoModBadge(),
MessageElementFlag::BadgeChannelAuthority);
// Suspicious user header message
QString prefix = u"Suspicious User:"_s;
builder.emplace<TextElement>(prefix, MessageElementFlag::Text,
MessageColor(QColor(0, 0, 255)),
FontStyle::ChatMediumBold);
QString headerMessage;
if (event.lowTrustStatus == lib::suspicious_users::Status::Restricted)
{
headerMessage = u"Restricted"_s;
}
else
{
headerMessage = u"Monitored"_s;
}
auto hasType = [&](lib::suspicious_users::Type type) {
return std::ranges::find(event.types, type) != event.types.end();
};
if (hasType(lib::suspicious_users::Type::BanEvaderDetector))
{
QString evader;
if (event.banEvasionEvaluation ==
lib::suspicious_users::BanEvasionEvaluation::Likely)
{
evader = u"likely"_s;
}
else
{
evader = u"possible"_s;
}
headerMessage += u". Detected as " % evader % " ban evader";
}
if (hasType(lib::suspicious_users::Type::SharedChannelBan))
{
headerMessage += u". Banned in " %
QString::number(event.sharedBanChannelIds.size()) %
u" shared channels";
}
builder.emplace<TextElement>(headerMessage, MessageElementFlag::Text,
MessageColor::Text);
builder->messageText = prefix % u" " % headerMessage;
builder->searchText = prefix % u" " % headerMessage;
return builder.release();
}
MessagePtr makeSuspiciousUserMessageBody(
TwitchChannel *channel, const QDateTime &time,
const lib::payload::channel_suspicious_user_message::v1::Event &event)
{
EventSubMessageBuilder builder(channel);
builder->channelName = event.broadcasterUserLogin.qt();
builder->serverReceivedTime = time;
if (event.lowTrustStatus == lib::suspicious_users::Status::Restricted)
{
builder->flags.set(MessageFlag::RestrictedMessage);
}
else
{
builder->flags.set(MessageFlag::MonitoredMessage);
}
builder
.emplace<TextElement>(u'#' + event.broadcasterUserLogin.qt(),
MessageElementFlag::ChannelName,
MessageColor::System)
->setLink({Link::JumpToChannel, event.broadcasterUserLogin.qt()});
builder.emplace<TimestampElement>(time.time());
builder.emplace<TwitchModerationElement>();
builder->loginName = event.userLogin.qt();
builder->flags.set(MessageFlag::PubSub, MessageFlag::LowTrustUsers);
// sender username
builder.emplace<MentionElement>(
event.userName.qt() + ":", event.userLogin.qt(), MessageColor::Text,
channel->getUserColor(event.userLogin.qt()));
// sender's message caught by AutoMod
// XXX: add the structured message here
builder.emplace<TextElement>(event.message.text.qt(),
MessageElementFlag::Text, MessageColor::Text);
auto text = event.userName.qt() % u": " % event.message.text.qt();
builder.message().messageText = text;
builder.message().searchText = text;
return builder.release();
}
} // namespace chatterino::eventsub
@@ -4,6 +4,7 @@
#include "providers/twitch/TwitchChannel.hpp"
#include "twitch-eventsub-ws/payloads/automod-message-hold-v2.hpp"
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
#include "twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.hpp"
#include <QDateTime>
@@ -156,4 +157,12 @@ MessagePtr makeAutomodHoldMessageBody(
TwitchChannel *channel, const QDateTime &time,
const lib::payload::automod_message_hold::v2::Event &event);
MessagePtr makeSuspiciousUserMessageHeader(
TwitchChannel *channel, const QDateTime &time,
const lib::payload::channel_suspicious_user_message::v1::Event &event);
MessagePtr makeSuspiciousUserMessageBody(
TwitchChannel *channel, const QDateTime &time,
const lib::payload::channel_suspicious_user_message::v1::Event &event);
} // namespace chatterino::eventsub