diff --git a/CHANGELOG.md b/CHANGELOG.md index 84db9de6d16..b0137122b69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Minor: Links can now have prefixes and suffixes such as parentheses. (#5486, #5515) - Minor: Added support for scrolling in splits with touchscreen panning gestures. (#5524) - Minor: Removed experimental IRC support. (#5547) +- Minor: Moderators can now see which mods start and cancel raids. (#5563) - Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378) - Bugfix: Fixed restricted users usernames not being clickable. (#5405) diff --git a/src/controllers/commands/builtin/twitch/Raid.cpp b/src/controllers/commands/builtin/twitch/Raid.cpp index 57fb3c011d0..1a54f17f3f6 100644 --- a/src/controllers/commands/builtin/twitch/Raid.cpp +++ b/src/controllers/commands/builtin/twitch/Raid.cpp @@ -152,9 +152,8 @@ QString startRaid(const CommandContext &ctx) channel{ctx.channel}](const HelixUser &targetUser) { getHelix()->startRaid( twitchChannel->roomId(), targetUser.id, - [channel, targetUser] { - channel->addSystemMessage(QString("You started to raid %1.") - .arg(targetUser.displayName)); + [] { + // do nothing }, [channel, targetUser](auto error, auto message) { auto errorMessage = formatStartRaidError(error, message); @@ -202,8 +201,8 @@ QString cancelRaid(const CommandContext &ctx) getHelix()->cancelRaid( ctx.twitchChannel->roomId(), - [channel{ctx.channel}] { - channel->addSystemMessage("You cancelled the raid."); + [] { + // do nothing }, [channel{ctx.channel}](auto error, auto message) { auto errorMessage = formatCancelRaidError(error, message); diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index db3ecc13ba4..d3750ea6370 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -880,6 +880,40 @@ MessageBuilder::MessageBuilder(const WarnAction &action) this->message().searchText = text; } +MessageBuilder::MessageBuilder(const RaidAction &action) + : MessageBuilder() +{ + this->emplace(); + this->message().flags.set(MessageFlag::System); + + QString text; + + this->emplaceSystemTextAndUpdate(action.source.login, text) + ->setLink({Link::UserInfo, "id:" + action.source.id}); + this->emplaceSystemTextAndUpdate("initiated a raid to", text); + this->emplaceSystemTextAndUpdate(action.target + ".", text) + ->setLink({Link::UserInfo, action.target}); + + this->message().messageText = text; + this->message().searchText = text; +} + +MessageBuilder::MessageBuilder(const UnraidAction &action) + : MessageBuilder() +{ + this->emplace(); + this->message().flags.set(MessageFlag::System); + + QString text; + + this->emplaceSystemTextAndUpdate(action.source.login, text) + ->setLink({Link::UserInfo, "id:" + action.source.id}); + this->emplaceSystemTextAndUpdate("canceled the raid.", text); + + this->message().messageText = text; + this->message().searchText = text; +} + MessageBuilder::MessageBuilder(const AutomodUserAction &action) : MessageBuilder() { diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp index 325abafbd8c..996595a8e8f 100644 --- a/src/messages/MessageBuilder.hpp +++ b/src/messages/MessageBuilder.hpp @@ -23,6 +23,8 @@ namespace chatterino { struct BanAction; struct UnbanAction; struct WarnAction; +struct RaidAction; +struct UnraidAction; struct AutomodAction; struct AutomodUserAction; struct AutomodInfoAction; @@ -127,6 +129,8 @@ class MessageBuilder MessageBuilder(const BanAction &action, uint32_t count = 1); MessageBuilder(const UnbanAction &action); MessageBuilder(const WarnAction &action); + MessageBuilder(const RaidAction &action); + MessageBuilder(const UnraidAction &action); MessageBuilder(const AutomodUserAction &action); MessageBuilder(LiveUpdatesAddEmoteMessageTag, const QString &platform, diff --git a/src/providers/twitch/PubSubActions.hpp b/src/providers/twitch/PubSubActions.hpp index 82c94da3004..5f83b40515e 100644 --- a/src/providers/twitch/PubSubActions.hpp +++ b/src/providers/twitch/PubSubActions.hpp @@ -172,6 +172,16 @@ struct AutomodInfoAction : PubSubAction { } type; }; +struct RaidAction : PubSubAction { + using PubSubAction::PubSubAction; + + QString target; +}; + +struct UnraidAction : PubSubAction { + using PubSubAction::PubSubAction; +}; + struct WarnAction : PubSubAction { using PubSubAction::PubSubAction; diff --git a/src/providers/twitch/PubSubManager.cpp b/src/providers/twitch/PubSubManager.cpp index 010cac05224..89a08327b18 100644 --- a/src/providers/twitch/PubSubManager.cpp +++ b/src/providers/twitch/PubSubManager.cpp @@ -320,6 +320,35 @@ PubSub::PubSub(const QString &host, std::chrono::seconds pingInterval) this->moderation.userWarned.invoke(action); }; + this->moderationActionHandlers["raid"] = [this](const auto &data, + const auto &roomID) { + RaidAction action(data, roomID); + + action.source.id = data.value("created_by_user_id").toString(); + action.source.login = data.value("created_by").toString(); + + const auto args = data.value("args").toArray(); + + if (args.isEmpty()) + { + return; + } + + action.target = args[0].toString(); + + this->moderation.raidStarted.invoke(action); + }; + + this->moderationActionHandlers["unraid"] = [this](const auto &data, + const auto &roomID) { + UnraidAction action(data, roomID); + + action.source.id = data.value("created_by_user_id").toString(); + action.source.login = data.value("created_by").toString(); + + this->moderation.raidCanceled.invoke(action); + }; + /* // This handler is no longer required as we use the automod-queue topic now this->moderationActionHandlers["automod_rejected"] = diff --git a/src/providers/twitch/PubSubManager.hpp b/src/providers/twitch/PubSubManager.hpp index 186cd5c6ae3..f14eabf7741 100644 --- a/src/providers/twitch/PubSubManager.hpp +++ b/src/providers/twitch/PubSubManager.hpp @@ -44,6 +44,8 @@ struct PubSubAutoModQueueMessage; struct AutomodAction; struct AutomodUserAction; struct AutomodInfoAction; +struct RaidAction; +struct UnraidAction; struct WarnAction; struct PubSubLowTrustUsersMessage; struct PubSubWhisperMessage; @@ -104,6 +106,9 @@ class PubSub Signal modeChanged; Signal moderationStateChanged; + Signal raidStarted; + Signal raidCanceled; + Signal userBanned; Signal userUnbanned; Signal userWarned; diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index b0bfcf69dad..6fbbb63d7e4 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -648,6 +648,40 @@ void TwitchIrcServer::initialize() }); }); + this->connections_.managedConnect( + getApp()->getTwitchPubSub()->moderation.raidStarted, + [this](const auto &action) { + auto chan = this->getChannelOrEmptyByID(action.roomID); + + if (chan->isEmpty()) + { + return; + } + + auto msg = MessageBuilder(action).release(); + + postToThread([chan, msg] { + chan->addMessage(msg, MessageContext::Original); + }); + }); + + this->connections_.managedConnect( + getApp()->getTwitchPubSub()->moderation.raidCanceled, + [this](const auto &action) { + auto chan = this->getChannelOrEmptyByID(action.roomID); + + if (chan->isEmpty()) + { + return; + } + + auto msg = MessageBuilder(action).release(); + + postToThread([chan, msg] { + chan->addMessage(msg, MessageContext::Original); + }); + }); + this->connections_.managedConnect( getApp()->getTwitchPubSub()->pointReward.redeemed, [this](auto &data) { QString channelId = data.value("channel_id").toString();