mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
feat: show restricted chats and suspicious treatment updates (#5056)
Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
69a54d944d
commit
036a5f3f21
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- Major: Allow use of Twitch follower emotes in other channels if subscribed. (#4922)
|
- 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, #5026)
|
- Major: Add `/automod` split to track automod caught messages across all open channels the user moderates. (#4986, #5026)
|
||||||
|
- Major: Show restricted chat messages and suspicious treatment updates. (#5056)
|
||||||
- Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
|
- 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: 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)
|
- Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795)
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
- Bugfix: Fixes to section deletion in text input fields. (#5013)
|
- Bugfix: Fixes to section deletion in text input fields. (#5013)
|
||||||
- Bugfix: Show user text input within watch streak notices. (#5029)
|
- Bugfix: Show user text input within watch streak notices. (#5029)
|
||||||
- Bugfix: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052)
|
- Bugfix: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052)
|
||||||
|
- Bugfix: Fixed moderator-only topics being subscribed to for non-moderators. (#5056)
|
||||||
- Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978)
|
- Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978)
|
||||||
- Dev: Change clang-format from v14 to v16. (#4929)
|
- Dev: Change clang-format from v14 to v16. (#4929)
|
||||||
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "providers/twitch/PubSubActions.hpp"
|
#include "providers/twitch/PubSubActions.hpp"
|
||||||
#include "providers/twitch/PubSubManager.hpp"
|
#include "providers/twitch/PubSubManager.hpp"
|
||||||
#include "providers/twitch/PubSubMessages.hpp"
|
#include "providers/twitch/PubSubMessages.hpp"
|
||||||
|
#include "providers/twitch/pubsubmessages/LowTrustUsers.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||||
|
@ -473,6 +474,87 @@ void Application::initPubSub()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
std::ignore =
|
||||||
|
this->twitch->pubsub->signals_.moderation.suspiciousMessageReceived
|
||||||
|
.connect([&](const auto &action) {
|
||||||
|
if (action.treatment ==
|
||||||
|
PubSubLowTrustUsersMessage::Treatment::INVALID)
|
||||||
|
{
|
||||||
|
qCWarning(chatterinoTwitch)
|
||||||
|
<< "Received suspicious message with unknown "
|
||||||
|
"treatment:"
|
||||||
|
<< action.treatmentString;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// monitored chats are received over irc; in the future, we will use pubsub instead
|
||||||
|
if (action.treatment !=
|
||||||
|
PubSubLowTrustUsersMessage::Treatment::Restricted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSettings()->streamerModeHideModActions &&
|
||||||
|
isInStreamerMode())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chan =
|
||||||
|
this->twitch->getChannelOrEmptyByID(action.channelID);
|
||||||
|
|
||||||
|
if (chan->isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postToThread([chan, action] {
|
||||||
|
const auto p =
|
||||||
|
TwitchMessageBuilder::makeLowTrustUserMessage(
|
||||||
|
action, chan->getName());
|
||||||
|
chan->addMessage(p.first);
|
||||||
|
chan->addMessage(p.second);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
std::ignore =
|
||||||
|
this->twitch->pubsub->signals_.moderation.suspiciousTreatmentUpdated
|
||||||
|
.connect([&](const auto &action) {
|
||||||
|
if (action.treatment ==
|
||||||
|
PubSubLowTrustUsersMessage::Treatment::INVALID)
|
||||||
|
{
|
||||||
|
qCWarning(chatterinoTwitch)
|
||||||
|
<< "Received suspicious user update with unknown "
|
||||||
|
"treatment:"
|
||||||
|
<< action.treatmentString;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.updatedByUserLogin.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSettings()->streamerModeHideModActions &&
|
||||||
|
isInStreamerMode())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chan =
|
||||||
|
this->twitch->getChannelOrEmptyByID(action.channelID);
|
||||||
|
if (chan->isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postToThread([chan, action] {
|
||||||
|
auto msg =
|
||||||
|
TwitchMessageBuilder::makeLowTrustUpdateMessage(action);
|
||||||
|
chan->addMessage(msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
std::ignore =
|
std::ignore =
|
||||||
this->twitch->pubsub->signals_.moderation.autoModMessageCaught.connect(
|
this->twitch->pubsub->signals_.moderation.autoModMessageCaught.connect(
|
||||||
[&](const auto &msg, const QString &channelID) {
|
[&](const auto &msg, const QString &channelID) {
|
||||||
|
@ -672,6 +754,7 @@ void Application::initPubSub()
|
||||||
[this] {
|
[this] {
|
||||||
this->twitch->pubsub->unlistenAllModerationActions();
|
this->twitch->pubsub->unlistenAllModerationActions();
|
||||||
this->twitch->pubsub->unlistenAutomod();
|
this->twitch->pubsub->unlistenAutomod();
|
||||||
|
this->twitch->pubsub->unlistenLowTrustUsers();
|
||||||
this->twitch->pubsub->unlistenWhispers();
|
this->twitch->pubsub->unlistenWhispers();
|
||||||
},
|
},
|
||||||
boost::signals2::at_front);
|
boost::signals2::at_front);
|
||||||
|
|
|
@ -412,6 +412,8 @@ set(SOURCE_FILES
|
||||||
providers/twitch/pubsubmessages/ChatModeratorAction.hpp
|
providers/twitch/pubsubmessages/ChatModeratorAction.hpp
|
||||||
providers/twitch/pubsubmessages/Listen.cpp
|
providers/twitch/pubsubmessages/Listen.cpp
|
||||||
providers/twitch/pubsubmessages/Listen.hpp
|
providers/twitch/pubsubmessages/Listen.hpp
|
||||||
|
providers/twitch/pubsubmessages/LowTrustUsers.cpp
|
||||||
|
providers/twitch/pubsubmessages/LowTrustUsers.hpp
|
||||||
providers/twitch/pubsubmessages/Message.hpp
|
providers/twitch/pubsubmessages/Message.hpp
|
||||||
providers/twitch/pubsubmessages/Unlisten.cpp
|
providers/twitch/pubsubmessages/Unlisten.cpp
|
||||||
providers/twitch/pubsubmessages/Unlisten.hpp
|
providers/twitch/pubsubmessages/Unlisten.hpp
|
||||||
|
|
|
@ -315,7 +315,6 @@ bool Channel::isBroadcaster() const
|
||||||
|
|
||||||
bool Channel::hasModRights() const
|
bool Channel::hasModRights() const
|
||||||
{
|
{
|
||||||
// fourtf: check if staff
|
|
||||||
return this->isMod() || this->isBroadcaster();
|
return this->isMod() || this->isBroadcaster();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ enum class MessageFlag : int64_t {
|
||||||
LiveUpdatesUpdate = (1LL << 30),
|
LiveUpdatesUpdate = (1LL << 30),
|
||||||
/// The message caught by AutoMod containing the user who sent the message & its contents
|
/// The message caught by AutoMod containing the user who sent the message & its contents
|
||||||
AutoModOffendingMessage = (1LL << 31),
|
AutoModOffendingMessage = (1LL << 31),
|
||||||
|
LowTrustUsers = (1LL << 32),
|
||||||
};
|
};
|
||||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,8 @@ void MessageLayout::updateBuffer(QPixmap *buffer,
|
||||||
blendColors(backgroundColor,
|
blendColors(backgroundColor,
|
||||||
*ctx.colorProvider.color(ColorType::RedeemedHighlight));
|
*ctx.colorProvider.color(ColorType::RedeemedHighlight));
|
||||||
}
|
}
|
||||||
else if (this->message_->flags.has(MessageFlag::AutoMod))
|
else if (this->message_->flags.has(MessageFlag::AutoMod) ||
|
||||||
|
this->message_->flags.has(MessageFlag::LowTrustUsers))
|
||||||
{
|
{
|
||||||
backgroundColor = QColor("#404040");
|
backgroundColor = QColor("#404040");
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
#include "providers/twitch/PubSubMessages.hpp"
|
#include "providers/twitch/PubSubMessages.hpp"
|
||||||
#include "providers/twitch/TwitchAccount.hpp"
|
#include "providers/twitch/TwitchAccount.hpp"
|
||||||
|
#include "pubsubmessages/LowTrustUsers.hpp"
|
||||||
#include "util/DebugCount.hpp"
|
#include "util/DebugCount.hpp"
|
||||||
#include "util/Helpers.hpp"
|
#include "util/Helpers.hpp"
|
||||||
#include "util/RapidjsonHelpers.hpp"
|
#include "util/RapidjsonHelpers.hpp"
|
||||||
|
@ -585,6 +586,25 @@ void PubSub::unlistenAutomod()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PubSub::unlistenLowTrustUsers()
|
||||||
|
{
|
||||||
|
for (const auto &p : this->clients)
|
||||||
|
{
|
||||||
|
const auto &client = p.second;
|
||||||
|
if (const auto &[topics, nonce] =
|
||||||
|
client->unlistenPrefix("low-trust-users.");
|
||||||
|
!topics.empty())
|
||||||
|
{
|
||||||
|
this->registerNonce(nonce, {
|
||||||
|
client,
|
||||||
|
"UNLISTEN",
|
||||||
|
topics,
|
||||||
|
topics.size(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PubSub::unlistenWhispers()
|
void PubSub::unlistenWhispers()
|
||||||
{
|
{
|
||||||
for (const auto &p : this->clients)
|
for (const auto &p : this->clients)
|
||||||
|
@ -670,6 +690,30 @@ void PubSub::listenToAutomod(const QString &channelID)
|
||||||
this->listenToTopic(topic);
|
this->listenToTopic(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PubSub::listenToLowTrustUsers(const QString &channelID)
|
||||||
|
{
|
||||||
|
if (this->userID_.isEmpty())
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoPubSub)
|
||||||
|
<< "Unable to listen to low trust users topic, no user logged in";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QString topicFormat("low-trust-users.%1.%2");
|
||||||
|
assert(!channelID.isEmpty());
|
||||||
|
|
||||||
|
auto topic = topicFormat.arg(this->userID_, channelID);
|
||||||
|
|
||||||
|
if (this->isListeningToTopic(topic))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(chatterinoPubSub) << "Listen to topic" << topic;
|
||||||
|
|
||||||
|
this->listenToTopic(topic);
|
||||||
|
}
|
||||||
|
|
||||||
void PubSub::listenToChannelPointRewards(const QString &channelID)
|
void PubSub::listenToChannelPointRewards(const QString &channelID)
|
||||||
{
|
{
|
||||||
static const QString topicFormat("community-points-channel-v1.%1");
|
static const QString topicFormat("community-points-channel-v1.%1");
|
||||||
|
@ -1169,6 +1213,38 @@ void PubSub::handleMessageResponse(const PubSubMessageMessage &message)
|
||||||
this->signals_.moderation.autoModMessageCaught.invoke(innerMessage,
|
this->signals_.moderation.autoModMessageCaught.invoke(innerMessage,
|
||||||
channelID);
|
channelID);
|
||||||
}
|
}
|
||||||
|
else if (topic.startsWith("low-trust-users."))
|
||||||
|
{
|
||||||
|
auto oInnerMessage = message.toInner<PubSubLowTrustUsersMessage>();
|
||||||
|
if (!oInnerMessage)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto innerMessage = *oInnerMessage;
|
||||||
|
|
||||||
|
switch (innerMessage.type)
|
||||||
|
{
|
||||||
|
case PubSubLowTrustUsersMessage::Type::UserMessage: {
|
||||||
|
this->signals_.moderation.suspiciousMessageReceived.invoke(
|
||||||
|
innerMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PubSubLowTrustUsersMessage::Type::TreatmentUpdate: {
|
||||||
|
this->signals_.moderation.suspiciousTreatmentUpdated.invoke(
|
||||||
|
innerMessage);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PubSubLowTrustUsersMessage::Type::INVALID: {
|
||||||
|
qCWarning(chatterinoPubSub)
|
||||||
|
<< "Invalid low trust users event type:"
|
||||||
|
<< innerMessage.typeString;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoPubSub) << "Unknown topic:" << topic;
|
qCDebug(chatterinoPubSub) << "Unknown topic:" << topic;
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct PubSubAutoModQueueMessage;
|
||||||
struct AutomodAction;
|
struct AutomodAction;
|
||||||
struct AutomodUserAction;
|
struct AutomodUserAction;
|
||||||
struct AutomodInfoAction;
|
struct AutomodInfoAction;
|
||||||
|
struct PubSubLowTrustUsersMessage;
|
||||||
struct PubSubWhisperMessage;
|
struct PubSubWhisperMessage;
|
||||||
|
|
||||||
struct PubSubListenMessage;
|
struct PubSubListenMessage;
|
||||||
|
@ -67,9 +68,6 @@ class PubSub
|
||||||
QString userID_;
|
QString userID_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// The max amount of connections we may open
|
|
||||||
static constexpr int maxConnections = 10;
|
|
||||||
|
|
||||||
PubSub(const QString &host,
|
PubSub(const QString &host,
|
||||||
std::chrono::seconds pingInterval = std::chrono::seconds(15));
|
std::chrono::seconds pingInterval = std::chrono::seconds(15));
|
||||||
|
|
||||||
|
@ -100,6 +98,9 @@ public:
|
||||||
Signal<BanAction> userBanned;
|
Signal<BanAction> userBanned;
|
||||||
Signal<UnbanAction> userUnbanned;
|
Signal<UnbanAction> userUnbanned;
|
||||||
|
|
||||||
|
Signal<PubSubLowTrustUsersMessage> suspiciousMessageReceived;
|
||||||
|
Signal<PubSubLowTrustUsersMessage> suspiciousTreatmentUpdated;
|
||||||
|
|
||||||
// Message caught by automod
|
// Message caught by automod
|
||||||
// channelID
|
// channelID
|
||||||
pajlada::Signals::Signal<PubSubAutoModQueueMessage, QString>
|
pajlada::Signals::Signal<PubSubAutoModQueueMessage, QString>
|
||||||
|
@ -126,12 +127,56 @@ public:
|
||||||
|
|
||||||
void unlistenAllModerationActions();
|
void unlistenAllModerationActions();
|
||||||
void unlistenAutomod();
|
void unlistenAutomod();
|
||||||
|
void unlistenLowTrustUsers();
|
||||||
void unlistenWhispers();
|
void unlistenWhispers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to incoming whispers for the currently logged in user.
|
||||||
|
* This topic is relevant for everyone.
|
||||||
|
*
|
||||||
|
* PubSub topic: whispers.{currentUserID}
|
||||||
|
*/
|
||||||
bool listenToWhispers();
|
bool listenToWhispers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to moderation actions in the given channel.
|
||||||
|
* This topic is relevant for everyone.
|
||||||
|
* For moderators, this topic includes blocked/permitted terms updates,
|
||||||
|
* roomstate changes, general mod/vip updates, all bans/timeouts/deletions.
|
||||||
|
* For normal users, this topic includes moderation actions that are targetted at the local user:
|
||||||
|
* automod catching a user's sent message, a moderator approving or denying their caught messages,
|
||||||
|
* the user gaining/losing mod/vip, the user receiving a ban/timeout/deletion.
|
||||||
|
*
|
||||||
|
* PubSub topic: chat_moderator_actions.{currentUserID}.{channelID}
|
||||||
|
*/
|
||||||
void listenToChannelModerationActions(const QString &channelID);
|
void listenToChannelModerationActions(const QString &channelID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to Automod events in the given channel.
|
||||||
|
* This topic is only relevant for moderators.
|
||||||
|
* This will send events about incoming messages that
|
||||||
|
* are caught by Automod.
|
||||||
|
*
|
||||||
|
* PubSub topic: automod-queue.{currentUserID}.{channelID}
|
||||||
|
*/
|
||||||
void listenToAutomod(const QString &channelID);
|
void listenToAutomod(const QString &channelID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to Low Trust events in the given channel.
|
||||||
|
* This topic is only relevant for moderators.
|
||||||
|
* This will fire events about suspicious treatment updates
|
||||||
|
* and messages sent by restricted/monitored users.
|
||||||
|
*
|
||||||
|
* PubSub topic: low-trust-users.{currentUserID}.{channelID}
|
||||||
|
*/
|
||||||
|
void listenToLowTrustUsers(const QString &channelID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to incoming channel point redemptions in the given channel.
|
||||||
|
* This topic is relevant for everyone.
|
||||||
|
*
|
||||||
|
* PubSub topic: community-points-channel-v1.{channelID}
|
||||||
|
*/
|
||||||
void listenToChannelPointRewards(const QString &channelID);
|
void listenToChannelPointRewards(const QString &channelID);
|
||||||
|
|
||||||
std::vector<QString> requests;
|
std::vector<QString> requests;
|
||||||
|
|
|
@ -1253,7 +1253,11 @@ void TwitchChannel::refreshPubSub()
|
||||||
getApp()->twitch->pubsub->setAccount(currentAccount);
|
getApp()->twitch->pubsub->setAccount(currentAccount);
|
||||||
|
|
||||||
getApp()->twitch->pubsub->listenToChannelModerationActions(roomId);
|
getApp()->twitch->pubsub->listenToChannelModerationActions(roomId);
|
||||||
getApp()->twitch->pubsub->listenToAutomod(roomId);
|
if (this->hasModRights())
|
||||||
|
{
|
||||||
|
getApp()->twitch->pubsub->listenToAutomod(roomId);
|
||||||
|
getApp()->twitch->pubsub->listenToLowTrustUsers(roomId);
|
||||||
|
}
|
||||||
getApp()->twitch->pubsub->listenToChannelPointRewards(roomId);
|
getApp()->twitch->pubsub->listenToChannelPointRewards(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,11 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
// using namespace Communi;
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
#define TWITCH_PUBSUB_URL "wss://pubsub-edge.twitch.tv"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
const QString TWITCH_PUBSUB_URL = "wss://pubsub-edge.twitch.tv";
|
||||||
const QString BTTV_LIVE_UPDATES_URL = "wss://sockets.betterttv.net/ws";
|
const QString BTTV_LIVE_UPDATES_URL = "wss://sockets.betterttv.net/ws";
|
||||||
const QString SEVENTV_EVENTAPI_URL = "wss://events.7tv.io/v3";
|
const QString SEVENTV_EVENTAPI_URL = "wss://events.7tv.io/v3";
|
||||||
|
|
||||||
|
@ -45,11 +43,10 @@ TwitchIrcServer::TwitchIrcServer()
|
||||||
, liveChannel(new Channel("/live", Channel::Type::TwitchLive))
|
, liveChannel(new Channel("/live", Channel::Type::TwitchLive))
|
||||||
, automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod))
|
, automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod))
|
||||||
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
|
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
|
||||||
|
, pubsub(new PubSub(TWITCH_PUBSUB_URL))
|
||||||
{
|
{
|
||||||
this->initializeIrc();
|
this->initializeIrc();
|
||||||
|
|
||||||
this->pubsub = new PubSub(TWITCH_PUBSUB_URL);
|
|
||||||
|
|
||||||
if (getSettings()->enableBTTVLiveUpdates &&
|
if (getSettings()->enableBTTVLiveUpdates &&
|
||||||
getSettings()->enableBTTVChannelEmotes)
|
getSettings()->enableBTTVChannelEmotes)
|
||||||
{
|
{
|
||||||
|
|
|
@ -80,6 +80,7 @@ public:
|
||||||
const ChannelPtr automodChannel;
|
const ChannelPtr automodChannel;
|
||||||
IndirectChannel watchingChannel;
|
IndirectChannel watchingChannel;
|
||||||
|
|
||||||
|
// NOTE: We currently leak this
|
||||||
PubSub *pubsub;
|
PubSub *pubsub;
|
||||||
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
|
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
|
||||||
std::unique_ptr<SeventvEventAPI> seventvEventAPI;
|
std::unique_ptr<SeventvEventAPI> seventvEventAPI;
|
||||||
|
|
|
@ -1933,6 +1933,188 @@ std::pair<MessagePtr, MessagePtr> TwitchMessageBuilder::makeAutomodMessage(
|
||||||
return std::make_pair(message1, message2);
|
return std::make_pair(message1, message2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessagePtr TwitchMessageBuilder::makeLowTrustUpdateMessage(
|
||||||
|
const PubSubLowTrustUsersMessage &action)
|
||||||
|
{
|
||||||
|
MessageBuilder builder;
|
||||||
|
builder.emplace<TimestampElement>();
|
||||||
|
builder.message().flags.set(MessageFlag::System);
|
||||||
|
builder.message().flags.set(MessageFlag::PubSub);
|
||||||
|
builder.message().flags.set(MessageFlag::DoNotTriggerNotification);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(action.updatedByUserDisplayName,
|
||||||
|
MessageElementFlag::Username,
|
||||||
|
MessageColor::System, FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::UserInfo, action.updatedByUserLogin});
|
||||||
|
|
||||||
|
assert(action.treatment != PubSubLowTrustUsersMessage::Treatment::INVALID);
|
||||||
|
switch (action.treatment)
|
||||||
|
{
|
||||||
|
case PubSubLowTrustUsersMessage::Treatment::NoTreatment: {
|
||||||
|
builder.emplace<TextElement>("removed", MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(action.suspiciousUserDisplayName,
|
||||||
|
MessageElementFlag::Username,
|
||||||
|
MessageColor::System,
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::UserInfo, action.suspiciousUserLogin});
|
||||||
|
builder.emplace<TextElement>("from the suspicious user list.",
|
||||||
|
MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PubSubLowTrustUsersMessage::Treatment::ActiveMonitoring: {
|
||||||
|
builder.emplace<TextElement>("added", MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(action.suspiciousUserDisplayName,
|
||||||
|
MessageElementFlag::Username,
|
||||||
|
MessageColor::System,
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::UserInfo, action.suspiciousUserLogin});
|
||||||
|
builder.emplace<TextElement>("as a monitored suspicious chatter.",
|
||||||
|
MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PubSubLowTrustUsersMessage::Treatment::Restricted: {
|
||||||
|
builder.emplace<TextElement>("added", MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
builder
|
||||||
|
.emplace<TextElement>(action.suspiciousUserDisplayName,
|
||||||
|
MessageElementFlag::Username,
|
||||||
|
MessageColor::System,
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::UserInfo, action.suspiciousUserLogin});
|
||||||
|
builder.emplace<TextElement>("as a restricted suspicious chatter.",
|
||||||
|
MessageElementFlag::Text,
|
||||||
|
MessageColor::System);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCDebug(chatterinoTwitch) << "Unexpected suspicious treatment: "
|
||||||
|
<< action.treatmentString;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<MessagePtr, MessagePtr> TwitchMessageBuilder::makeLowTrustUserMessage(
|
||||||
|
const PubSubLowTrustUsersMessage &action, const QString &channelName)
|
||||||
|
{
|
||||||
|
MessageBuilder builder, builder2;
|
||||||
|
|
||||||
|
// Builder for low trust user message with explanation
|
||||||
|
builder.message().channelName = channelName;
|
||||||
|
builder.message().flags.set(MessageFlag::PubSub);
|
||||||
|
builder.message().flags.set(MessageFlag::LowTrustUsers);
|
||||||
|
|
||||||
|
// AutoMod shield badge
|
||||||
|
builder.emplace<BadgeElement>(makeAutoModBadge(),
|
||||||
|
MessageElementFlag::BadgeChannelAuthority);
|
||||||
|
|
||||||
|
// Suspicious user header message
|
||||||
|
QString prefix = "Suspicious User:";
|
||||||
|
builder.emplace<TextElement>(prefix, MessageElementFlag::Text,
|
||||||
|
MessageColor(QColor("blue")),
|
||||||
|
FontStyle::ChatMediumBold);
|
||||||
|
|
||||||
|
QString headerMessage;
|
||||||
|
if (action.treatment == PubSubLowTrustUsersMessage::Treatment::Restricted)
|
||||||
|
{
|
||||||
|
headerMessage = "Restricted";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
headerMessage = "Monitored";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.restrictionTypes.has(
|
||||||
|
PubSubLowTrustUsersMessage::RestrictionType::ManuallyAdded))
|
||||||
|
{
|
||||||
|
headerMessage += " by " + action.updatedByUserLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerMessage += " at " + action.updatedAt;
|
||||||
|
|
||||||
|
if (action.restrictionTypes.has(
|
||||||
|
PubSubLowTrustUsersMessage::RestrictionType::DetectedBanEvader))
|
||||||
|
{
|
||||||
|
QString evader;
|
||||||
|
if (action.evasionEvaluation ==
|
||||||
|
PubSubLowTrustUsersMessage::EvasionEvaluation::LikelyEvader)
|
||||||
|
{
|
||||||
|
evader = "likely";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
evader = "possible";
|
||||||
|
}
|
||||||
|
|
||||||
|
headerMessage += ". Detected as " + evader + " ban evader";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.restrictionTypes.has(
|
||||||
|
PubSubLowTrustUsersMessage::RestrictionType::BannedInSharedChannel))
|
||||||
|
{
|
||||||
|
headerMessage += ". Banned in " +
|
||||||
|
QString::number(action.sharedBanChannelIDs.size()) +
|
||||||
|
" shared channels";
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.emplace<TextElement>(headerMessage, MessageElementFlag::Text,
|
||||||
|
MessageColor::Text);
|
||||||
|
builder.message().messageText = prefix + " " + headerMessage;
|
||||||
|
builder.message().searchText = prefix + " " + headerMessage;
|
||||||
|
|
||||||
|
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.suspiciousUserLogin;
|
||||||
|
builder2.message().flags.set(MessageFlag::PubSub);
|
||||||
|
builder2.message().flags.set(MessageFlag::LowTrustUsers);
|
||||||
|
|
||||||
|
// sender username
|
||||||
|
builder2
|
||||||
|
.emplace<TextElement>(action.suspiciousUserDisplayName + ":",
|
||||||
|
MessageElementFlag::BoldUsername,
|
||||||
|
MessageColor(action.suspiciousUserColor),
|
||||||
|
FontStyle::ChatMediumBold)
|
||||||
|
->setLink({Link::UserInfo, action.suspiciousUserLogin});
|
||||||
|
builder2
|
||||||
|
.emplace<TextElement>(action.suspiciousUserDisplayName + ":",
|
||||||
|
MessageElementFlag::NonBoldUsername,
|
||||||
|
MessageColor(action.suspiciousUserColor))
|
||||||
|
->setLink({Link::UserInfo, action.suspiciousUserLogin});
|
||||||
|
|
||||||
|
// sender's message caught by AutoMod
|
||||||
|
builder2.emplace<TextElement>(action.text, MessageElementFlag::Text,
|
||||||
|
MessageColor::Text);
|
||||||
|
auto text =
|
||||||
|
QString("%1: %2").arg(action.suspiciousUserDisplayName, action.text);
|
||||||
|
builder2.message().messageText = text;
|
||||||
|
builder2.message().searchText = text;
|
||||||
|
|
||||||
|
auto message2 = builder2.release();
|
||||||
|
|
||||||
|
return std::make_pair(message1, message2);
|
||||||
|
}
|
||||||
|
|
||||||
void TwitchMessageBuilder::setThread(std::shared_ptr<MessageThread> thread)
|
void TwitchMessageBuilder::setThread(std::shared_ptr<MessageThread> thread)
|
||||||
{
|
{
|
||||||
this->thread_ = std::move(thread);
|
this->thread_ = std::move(thread);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/Aliases.hpp"
|
#include "common/Aliases.hpp"
|
||||||
#include "common/Outcome.hpp"
|
#include "common/Outcome.hpp"
|
||||||
#include "messages/SharedMessageBuilder.hpp"
|
#include "messages/SharedMessageBuilder.hpp"
|
||||||
|
#include "pubsubmessages/LowTrustUsers.hpp"
|
||||||
|
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
@ -93,6 +94,11 @@ public:
|
||||||
const AutomodAction &action, const QString &channelName);
|
const AutomodAction &action, const QString &channelName);
|
||||||
static MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action);
|
static MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action);
|
||||||
|
|
||||||
|
static std::pair<MessagePtr, MessagePtr> makeLowTrustUserMessage(
|
||||||
|
const PubSubLowTrustUsersMessage &action, const QString &channelName);
|
||||||
|
static MessagePtr makeLowTrustUpdateMessage(
|
||||||
|
const PubSubLowTrustUsersMessage &action);
|
||||||
|
|
||||||
// Shares some common logic from SharedMessageBuilder::parseBadgeTag
|
// Shares some common logic from SharedMessageBuilder::parseBadgeTag
|
||||||
static std::unordered_map<QString, QString> parseBadgeInfoTag(
|
static std::unordered_map<QString, QString> parseBadgeInfoTag(
|
||||||
const QVariantMap &tags);
|
const QVariantMap &tags);
|
||||||
|
|
104
src/providers/twitch/pubsubmessages/LowTrustUsers.cpp
Normal file
104
src/providers/twitch/pubsubmessages/LowTrustUsers.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include "providers/twitch/pubsubmessages/LowTrustUsers.hpp"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
PubSubLowTrustUsersMessage::PubSubLowTrustUsersMessage(const QJsonObject &root)
|
||||||
|
: typeString(root.value("type").toString())
|
||||||
|
{
|
||||||
|
if (const auto oType =
|
||||||
|
magic_enum::enum_cast<Type>(this->typeString.toStdString());
|
||||||
|
oType.has_value())
|
||||||
|
{
|
||||||
|
this->type = oType.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = root.value("data").toObject();
|
||||||
|
|
||||||
|
if (this->type == Type::UserMessage)
|
||||||
|
{
|
||||||
|
this->msgID = data.value("message_id").toString();
|
||||||
|
this->sentAt = data.value("sent_at").toString();
|
||||||
|
this->text =
|
||||||
|
data.value("message_content").toObject().value("text").toString();
|
||||||
|
|
||||||
|
// the rest of the data is within a nested object
|
||||||
|
data = data.value("low_trust_user").toObject();
|
||||||
|
|
||||||
|
const auto sender = data.value("sender").toObject();
|
||||||
|
this->suspiciousUserID = sender.value("user_id").toString();
|
||||||
|
this->suspiciousUserLogin = sender.value("login").toString();
|
||||||
|
this->suspiciousUserDisplayName =
|
||||||
|
sender.value("display_name").toString();
|
||||||
|
this->suspiciousUserColor =
|
||||||
|
QColor(sender.value("chat_color").toString());
|
||||||
|
|
||||||
|
std::vector<LowTrustUserChatBadge> badges;
|
||||||
|
for (const auto &badge : sender.value("badges").toArray())
|
||||||
|
{
|
||||||
|
badges.emplace_back(badge.toObject());
|
||||||
|
}
|
||||||
|
this->senderBadges = badges;
|
||||||
|
|
||||||
|
const auto sharedValue = data.value("shared_ban_channel_ids");
|
||||||
|
std::vector<QString> sharedIDs;
|
||||||
|
if (!sharedValue.isNull())
|
||||||
|
{
|
||||||
|
for (const auto &id : sharedValue.toArray())
|
||||||
|
{
|
||||||
|
sharedIDs.emplace_back(id.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->sharedBanChannelIDs = sharedIDs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->suspiciousUserID = data.value("target_user_id").toString();
|
||||||
|
this->suspiciousUserLogin = data.value("target_user").toString();
|
||||||
|
this->suspiciousUserDisplayName = this->suspiciousUserLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->channelID = data.value("channel_id").toString();
|
||||||
|
this->updatedAtString = data.value("updated_at").toString();
|
||||||
|
this->updatedAt = QDateTime::fromString(this->updatedAtString, Qt::ISODate)
|
||||||
|
.toLocalTime()
|
||||||
|
.toString("MMM d yyyy, h:mm ap");
|
||||||
|
|
||||||
|
const auto updatedBy = data.value("updated_by").toObject();
|
||||||
|
this->updatedByUserID = updatedBy.value("id").toString();
|
||||||
|
this->updatedByUserLogin = updatedBy.value("login").toString();
|
||||||
|
this->updatedByUserDisplayName = updatedBy.value("display_name").toString();
|
||||||
|
|
||||||
|
this->treatmentString = data.value("treatment").toString();
|
||||||
|
if (const auto oTreatment = magic_enum::enum_cast<Treatment>(
|
||||||
|
this->treatmentString.toStdString());
|
||||||
|
oTreatment.has_value())
|
||||||
|
{
|
||||||
|
this->treatment = oTreatment.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->evasionEvaluationString =
|
||||||
|
data.value("ban_evasion_evaluation").toString();
|
||||||
|
if (const auto oEvaluation = magic_enum::enum_cast<EvasionEvaluation>(
|
||||||
|
this->evasionEvaluationString.toStdString());
|
||||||
|
oEvaluation.has_value())
|
||||||
|
{
|
||||||
|
this->evasionEvaluation = oEvaluation.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
FlagsEnum<RestrictionType> restrictions;
|
||||||
|
for (const auto &rType : data.value("types").toArray())
|
||||||
|
{
|
||||||
|
if (const auto oRestriction = magic_enum::enum_cast<RestrictionType>(
|
||||||
|
rType.toString().toStdString());
|
||||||
|
oRestriction.has_value())
|
||||||
|
{
|
||||||
|
restrictions.set(oRestriction.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->restrictionTypes = restrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
255
src/providers/twitch/pubsubmessages/LowTrustUsers.hpp
Normal file
255
src/providers/twitch/pubsubmessages/LowTrustUsers.hpp
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/FlagsEnum.hpp>
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
struct LowTrustUserChatBadge {
|
||||||
|
QString id;
|
||||||
|
QString version;
|
||||||
|
|
||||||
|
explicit LowTrustUserChatBadge(const QJsonObject &obj)
|
||||||
|
: id(obj.value("id").toString())
|
||||||
|
, version(obj.value("version").toString())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PubSubLowTrustUsersMessage {
|
||||||
|
/**
|
||||||
|
* The type of low trust message update
|
||||||
|
*/
|
||||||
|
enum class Type {
|
||||||
|
/**
|
||||||
|
* An incoming message from someone marked as low trust
|
||||||
|
*/
|
||||||
|
UserMessage,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An incoming update about a user's low trust status
|
||||||
|
*/
|
||||||
|
TreatmentUpdate,
|
||||||
|
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The treatment set for the suspicious user
|
||||||
|
*/
|
||||||
|
enum class Treatment {
|
||||||
|
NoTreatment,
|
||||||
|
ActiveMonitoring,
|
||||||
|
Restricted,
|
||||||
|
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ban evasion likelihood value (if any) that has been applied to the user
|
||||||
|
* automatically by Twitch
|
||||||
|
*/
|
||||||
|
enum class EvasionEvaluation {
|
||||||
|
UnknownEvader,
|
||||||
|
UnlikelyEvader,
|
||||||
|
LikelyEvader,
|
||||||
|
PossibleEvader,
|
||||||
|
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restriction type (if any) that apply to the suspicious user
|
||||||
|
*/
|
||||||
|
enum class RestrictionType : uint8_t {
|
||||||
|
UnknownType = 1 << 0,
|
||||||
|
ManuallyAdded = 1 << 1,
|
||||||
|
DetectedBanEvader = 1 << 2,
|
||||||
|
BannedInSharedChannel = 1 << 3,
|
||||||
|
|
||||||
|
INVALID = 1 << 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type = Type::INVALID;
|
||||||
|
|
||||||
|
Treatment treatment = Treatment::INVALID;
|
||||||
|
|
||||||
|
EvasionEvaluation evasionEvaluation = EvasionEvaluation::INVALID;
|
||||||
|
|
||||||
|
FlagsEnum<RestrictionType> restrictionTypes;
|
||||||
|
|
||||||
|
QString channelID;
|
||||||
|
|
||||||
|
QString suspiciousUserID;
|
||||||
|
QString suspiciousUserLogin;
|
||||||
|
QString suspiciousUserDisplayName;
|
||||||
|
|
||||||
|
QString updatedByUserID;
|
||||||
|
QString updatedByUserLogin;
|
||||||
|
QString updatedByUserDisplayName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatted timestamp of when the treatment was last updated for the suspicious user
|
||||||
|
*/
|
||||||
|
QString updatedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plain text of the message sent.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
QString text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the message.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
QString msgID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RFC3339 timestamp of when the message was sent.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
QString sentAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color of the user who sent the message.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
QColor suspiciousUserColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of channel IDs where the suspicious user is also banned.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
std::vector<QString> sharedBanChannelIDs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of badges of the user who sent the message.
|
||||||
|
* Only used for the UserMessage type.
|
||||||
|
*/
|
||||||
|
std::vector<LowTrustUserChatBadge> senderBadges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the string value of `type`
|
||||||
|
* Useful in case type shows up as invalid after being parsed
|
||||||
|
*/
|
||||||
|
QString typeString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the string value of `treatment`
|
||||||
|
* Useful in case treatment shows up as invalid after being parsed
|
||||||
|
*/
|
||||||
|
QString treatmentString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the string value of `ban_evasion_evaluation`
|
||||||
|
* Useful in case evasionEvaluation shows up as invalid after being parsed
|
||||||
|
*/
|
||||||
|
QString evasionEvaluationString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the string value of `updated_at`
|
||||||
|
* Useful in case formattedUpdatedAt doesn't parse correctly
|
||||||
|
*/
|
||||||
|
QString updatedAtString;
|
||||||
|
|
||||||
|
PubSubLowTrustUsersMessage() = default;
|
||||||
|
explicit PubSubLowTrustUsersMessage(const QJsonObject &root);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::Type>(
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::Type value) noexcept
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case chatterino::PubSubLowTrustUsersMessage::Type::UserMessage:
|
||||||
|
return "low_trust_user_new_message";
|
||||||
|
|
||||||
|
case chatterino::PubSubLowTrustUsersMessage::Type::TreatmentUpdate:
|
||||||
|
return "low_trust_user_treatment_update";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return default_tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::Treatment>(
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::Treatment value) noexcept
|
||||||
|
{
|
||||||
|
using Treatment = chatterino::PubSubLowTrustUsersMessage::Treatment;
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case Treatment::NoTreatment:
|
||||||
|
return "NO_TREATMENT";
|
||||||
|
|
||||||
|
case Treatment::ActiveMonitoring:
|
||||||
|
return "ACTIVE_MONITORING";
|
||||||
|
|
||||||
|
case Treatment::Restricted:
|
||||||
|
return "RESTRICTED";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return default_tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::EvasionEvaluation>(
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::EvasionEvaluation value) noexcept
|
||||||
|
{
|
||||||
|
using EvasionEvaluation =
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::EvasionEvaluation;
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case EvasionEvaluation::UnknownEvader:
|
||||||
|
return "UNKNOWN_EVADER";
|
||||||
|
|
||||||
|
case EvasionEvaluation::UnlikelyEvader:
|
||||||
|
return "UNLIKELY_EVADER";
|
||||||
|
|
||||||
|
case EvasionEvaluation::LikelyEvader:
|
||||||
|
return "LIKELY_EVADER";
|
||||||
|
|
||||||
|
case EvasionEvaluation::PossibleEvader:
|
||||||
|
return "POSSIBLE_EVADER";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return default_tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::RestrictionType>(
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::RestrictionType value) noexcept
|
||||||
|
{
|
||||||
|
using RestrictionType =
|
||||||
|
chatterino::PubSubLowTrustUsersMessage::RestrictionType;
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case RestrictionType::UnknownType:
|
||||||
|
return "UNKNOWN_TYPE";
|
||||||
|
|
||||||
|
case RestrictionType::ManuallyAdded:
|
||||||
|
return "MANUALLY_ADDED";
|
||||||
|
|
||||||
|
case RestrictionType::DetectedBanEvader:
|
||||||
|
return "DETECTED_BAN_EVADER";
|
||||||
|
|
||||||
|
case RestrictionType::BannedInSharedChannel:
|
||||||
|
return "BANNED_IN_SHARED_CHANNEL";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return default_tag;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue