From 820eb3d58c195142f9b35c9517e65dc1f3feb411 Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 16 Mar 2025 16:31:14 +0100 Subject: [PATCH] feat(eventsub): handle unban requests (#6086) --- CHANGELOG.md | 2 +- .../payloads/channel-moderate-v2.hpp | 8 +- .../payloads/channel-moderate-v2.cpp | 8 +- src/providers/twitch/eventsub/Connection.cpp | 11 ++ .../twitch/eventsub/MessageBuilder.cpp | 34 ++++ .../twitch/eventsub/MessageBuilder.hpp | 6 + .../unban-request-approve-message.json | 165 +++++++++++++++++ .../unban-request-approve.json | 161 +++++++++++++++++ .../unban-request-deny-message.json | 166 ++++++++++++++++++ .../channel-moderate/unban-request-deny.json | 161 +++++++++++++++++ 10 files changed, 713 insertions(+), 9 deletions(-) create mode 100644 tests/snapshots/EventSub/channel-moderate/unban-request-approve-message.json create mode 100644 tests/snapshots/EventSub/channel-moderate/unban-request-approve.json create mode 100644 tests/snapshots/EventSub/channel-moderate/unban-request-deny-message.json create mode 100644 tests/snapshots/EventSub/channel-moderate/unban-request-deny.json diff --git a/CHANGELOG.md b/CHANGELOG.md index cbeef191..5f708193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,7 @@ - Bugfix: Fixed some windows not immediately closing. (#6054) - Bugfix: The emote button no longer looks crunchy. (#6080) - Dev: Subscriptions to PubSub channel points redemption topics now use no auth token, making it continue to work during PubSub shutdown. (#5947) -- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974, #5980, #5981, #5985, #5990, #5992, #5993, #5996, #5995, #6000, #6001, #6002, #6003, #6005, #6007, #6010, #6008, #6012, #6013, #6015, #6017, #6027, #6028, #6035, #6036, #6040, #6041, #6048, #6058, #6059, #6078, #6079) +- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897, #5904, #5910, #5903, #5915, #5916, #5930, #5935, #5932, #5943, #5952, #5953, #5968, #5973, #5974, #5980, #5981, #5985, #5990, #5992, #5993, #5996, #5995, #6000, #6001, #6002, #6003, #6005, #6007, #6010, #6008, #6012, #6013, #6015, #6017, #6027, #6028, #6035, #6036, #6040, #6041, #6048, #6058, #6059, #6078, #6079, #6086) - Dev: Remove unneeded platform specifier for toasts. (#5914) - Dev: Cleanly shutdown on `SIGINT`/`SIGTERM` on Linux & macOS. (#6053) - Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784) diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp index ec59a5b6..97b83a84 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp @@ -315,11 +315,11 @@ struct UnbanRequest { bool isApproved; - std::string userID; - std::string userLogin; - std::string userName; + String userID; + String userLogin; + String userName; - std::string moderatorMessage; + String moderatorMessage; }; struct ApproveUnbanRequest : public UnbanRequest { diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp index 1b76cf63..f405dcf8 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp @@ -1013,7 +1013,7 @@ boost::json::result_for::type tag_invoke( EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto userID = boost::json::try_value_to(*jvuserID); + auto userID = boost::json::try_value_to(*jvuserID); if (userID.has_error()) { @@ -1026,7 +1026,7 @@ boost::json::result_for::type tag_invoke( EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto userLogin = boost::json::try_value_to(*jvuserLogin); + auto userLogin = boost::json::try_value_to(*jvuserLogin); if (userLogin.has_error()) { @@ -1039,7 +1039,7 @@ boost::json::result_for::type tag_invoke( EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto userName = boost::json::try_value_to(*jvuserName); + auto userName = boost::json::try_value_to(*jvuserName); if (userName.has_error()) { @@ -1053,7 +1053,7 @@ boost::json::result_for::type tag_invoke( } auto moderatorMessage = - boost::json::try_value_to(*jvmoderatorMessage); + boost::json::try_value_to(*jvmoderatorMessage); if (moderatorMessage.has_error()) { diff --git a/src/providers/twitch/eventsub/Connection.cpp b/src/providers/twitch/eventsub/Connection.cpp index e83b7659..bea0d281 100644 --- a/src/providers/twitch/eventsub/Connection.cpp +++ b/src/providers/twitch/eventsub/Connection.cpp @@ -170,6 +170,17 @@ void Connection::onChannelModerate( std::visit( [&](auto &&action) { using Action = std::remove_cvref_t; + static_assert(CanMakeModMessage || + CanHandleModMessage || + std::is_same_v, + "All actions must be handled"); + + if constexpr (std::is_same_v) + { + qCWarning(LOG) << "Unhandled moderation action:" + << QUtf8StringView(action); + } + if constexpr (CanMakeModMessage) { EventSubMessageBuilder builder(channel, now); diff --git a/src/providers/twitch/eventsub/MessageBuilder.cpp b/src/providers/twitch/eventsub/MessageBuilder.cpp index fa21b88c..5f900fb4 100644 --- a/src/providers/twitch/eventsub/MessageBuilder.cpp +++ b/src/providers/twitch/eventsub/MessageBuilder.cpp @@ -588,6 +588,40 @@ void makeModerateMessage( builder.setMessageAndSearchText(text); } +void makeModerateMessage( + EventSubMessageBuilder &builder, + const lib::payload::channel_moderate::v2::Event &event, + const lib::payload::channel_moderate::v2::UnbanRequest &action) +{ + builder->flags.set(MessageFlag::ModerationAction); + + QString text; + + builder.appendUser(event.moderatorUserName, event.moderatorUserLogin, text); + if (action.isApproved) + { + builder.emplaceSystemTextAndUpdate("approved", text); + } + else + { + builder.emplaceSystemTextAndUpdate("denied", text); + } + builder.appendOrEmplaceSystemTextAndUpdate("the unban request from", text); + builder.appendUser(action.userName, action.userLogin, text, false); + if (action.moderatorMessage.isEmpty()) + { + builder.emplaceSystemTextAndUpdate(".", text); + } + else + { + builder.emplaceSystemTextAndUpdate(":", text); + builder.appendOrEmplaceSystemTextAndUpdate(action.moderatorMessage.qt(), + text); + } + + builder.setMessageAndSearchText(text); +} + MessagePtr makeAutomodHoldMessageHeader( TwitchChannel *channel, const QDateTime &time, const lib::payload::automod_message_hold::v2::Event &event) diff --git a/src/providers/twitch/eventsub/MessageBuilder.hpp b/src/providers/twitch/eventsub/MessageBuilder.hpp index eb0d37d7..bc3cb36e 100644 --- a/src/providers/twitch/eventsub/MessageBuilder.hpp +++ b/src/providers/twitch/eventsub/MessageBuilder.hpp @@ -160,6 +160,12 @@ void makeModerateMessage( const lib::payload::channel_moderate::v2::Event &event, const lib::payload::channel_moderate::v2::Unraid &action); +/// {approved/denied} the unban request from [: ] +void makeModerateMessage( + EventSubMessageBuilder &builder, + const lib::payload::channel_moderate::v2::Event &event, + const lib::payload::channel_moderate::v2::UnbanRequest &action); + MessagePtr makeAutomodHoldMessageHeader( TwitchChannel *channel, const QDateTime &time, const lib::payload::automod_message_hold::v2::Event &event); diff --git a/tests/snapshots/EventSub/channel-moderate/unban-request-approve-message.json b/tests/snapshots/EventSub/channel-moderate/unban-request-approve-message.json new file mode 100644 index 00000000..aa0bdd18 --- /dev/null +++ b/tests/snapshots/EventSub/channel-moderate/unban-request-approve-message.json @@ -0,0 +1,165 @@ +{ + "input": { + "action": "approve_unban_request", + "automod_terms": null, + "ban": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "delete": null, + "followers": null, + "mod": null, + "moderator_user_id": "129546453", + "moderator_user_login": "nerixyz", + "moderator_user_name": "nerixyz", + "raid": null, + "shared_chat_ban": null, + "shared_chat_delete": null, + "shared_chat_timeout": null, + "shared_chat_unban": null, + "shared_chat_untimeout": null, + "slow": null, + "source_broadcaster_user_id": null, + "source_broadcaster_user_login": null, + "source_broadcaster_user_name": null, + "timeout": null, + "unban": null, + "unban_request": { + "is_approved": true, + "moderator_message": "Ok you are unbanned", + "user_id": "489584266", + "user_login": "uint128", + "user_name": "uint128" + }, + "unmod": null, + "unraid": null, + "untimeout": null, + "unvip": null, + "vip": null, + "warn": null + }, + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "", + "count": 1, + "displayName": "", + "elements": [ + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "nerixyz", + "words": [ + "nerixyz" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "approved", + "the", + "unban", + "request", + "from" + ] + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": false, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "uint128", + "words": [ + "uint128" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + ":", + "Ok", + "you", + "are", + "unbanned" + ] + } + ], + "flags": "System|EventSub|ModerationAction", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz approved the unban request from uint128: Ok you are unbanned ", + "searchText": "nerixyz approved the unban request from uint128: Ok you are unbanned ", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/channel-moderate/unban-request-approve.json b/tests/snapshots/EventSub/channel-moderate/unban-request-approve.json new file mode 100644 index 00000000..d66c07a5 --- /dev/null +++ b/tests/snapshots/EventSub/channel-moderate/unban-request-approve.json @@ -0,0 +1,161 @@ +{ + "input": { + "action": "approve_unban_request", + "automod_terms": null, + "ban": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "delete": null, + "followers": null, + "mod": null, + "moderator_user_id": "129546453", + "moderator_user_login": "nerixyz", + "moderator_user_name": "nerixyz", + "raid": null, + "shared_chat_ban": null, + "shared_chat_delete": null, + "shared_chat_timeout": null, + "shared_chat_unban": null, + "shared_chat_untimeout": null, + "slow": null, + "source_broadcaster_user_id": null, + "source_broadcaster_user_login": null, + "source_broadcaster_user_name": null, + "timeout": null, + "unban": null, + "unban_request": { + "is_approved": true, + "moderator_message": "", + "user_id": "489584266", + "user_login": "uint128", + "user_name": "uint128" + }, + "unmod": null, + "unraid": null, + "untimeout": null, + "unvip": null, + "vip": null, + "warn": null + }, + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "", + "count": 1, + "displayName": "", + "elements": [ + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "nerixyz", + "words": [ + "nerixyz" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "approved", + "the", + "unban", + "request", + "from" + ] + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": false, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "uint128", + "words": [ + "uint128" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "." + ] + } + ], + "flags": "System|EventSub|ModerationAction", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz approved the unban request from uint128. ", + "searchText": "nerixyz approved the unban request from uint128. ", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/channel-moderate/unban-request-deny-message.json b/tests/snapshots/EventSub/channel-moderate/unban-request-deny-message.json new file mode 100644 index 00000000..61f902bc --- /dev/null +++ b/tests/snapshots/EventSub/channel-moderate/unban-request-deny-message.json @@ -0,0 +1,166 @@ +{ + "input": { + "action": "deny_unban_request", + "automod_terms": null, + "ban": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "delete": null, + "followers": null, + "mod": null, + "moderator_user_id": "129546453", + "moderator_user_login": "nerixyz", + "moderator_user_name": "nerixyz", + "raid": null, + "shared_chat_ban": null, + "shared_chat_delete": null, + "shared_chat_timeout": null, + "shared_chat_unban": null, + "shared_chat_untimeout": null, + "slow": null, + "source_broadcaster_user_id": null, + "source_broadcaster_user_login": null, + "source_broadcaster_user_name": null, + "timeout": null, + "unban": null, + "unban_request": { + "is_approved": false, + "moderator_message": "Nah, no unban for you", + "user_id": "489584266", + "user_login": "uint128", + "user_name": "uint128" + }, + "unmod": null, + "unraid": null, + "untimeout": null, + "unvip": null, + "vip": null, + "warn": null + }, + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "", + "count": 1, + "displayName": "", + "elements": [ + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "nerixyz", + "words": [ + "nerixyz" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "denied", + "the", + "unban", + "request", + "from" + ] + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": false, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "uint128", + "words": [ + "uint128" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + ":", + "Nah,", + "no", + "unban", + "for", + "you" + ] + } + ], + "flags": "System|EventSub|ModerationAction", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz denied the unban request from uint128: Nah, no unban for you ", + "searchText": "nerixyz denied the unban request from uint128: Nah, no unban for you ", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/channel-moderate/unban-request-deny.json b/tests/snapshots/EventSub/channel-moderate/unban-request-deny.json new file mode 100644 index 00000000..e68211d6 --- /dev/null +++ b/tests/snapshots/EventSub/channel-moderate/unban-request-deny.json @@ -0,0 +1,161 @@ +{ + "input": { + "action": "deny_unban_request", + "automod_terms": null, + "ban": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "delete": null, + "followers": null, + "mod": null, + "moderator_user_id": "129546453", + "moderator_user_login": "nerixyz", + "moderator_user_name": "nerixyz", + "raid": null, + "shared_chat_ban": null, + "shared_chat_delete": null, + "shared_chat_timeout": null, + "shared_chat_unban": null, + "shared_chat_untimeout": null, + "slow": null, + "source_broadcaster_user_id": null, + "source_broadcaster_user_login": null, + "source_broadcaster_user_name": null, + "timeout": null, + "unban": null, + "unban_request": { + "is_approved": false, + "moderator_message": "", + "user_id": "489584266", + "user_login": "uint128", + "user_name": "uint128" + }, + "unmod": null, + "unraid": null, + "untimeout": null, + "unvip": null, + "vip": null, + "warn": null + }, + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "", + "count": 1, + "displayName": "", + "elements": [ + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "nerixyz", + "words": [ + "nerixyz" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "denied", + "the", + "unban", + "request", + "from" + ] + }, + { + "color": "Text", + "fallbackColor": "System", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": false, + "type": "MentionElement", + "userColor": "System", + "userLoginName": "uint128", + "words": [ + "uint128" + ] + }, + { + "color": "System", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "." + ] + } + ], + "flags": "System|EventSub|ModerationAction", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz denied the unban request from uint128. ", + "searchText": "nerixyz denied the unban request from uint128. ", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +}