From 17a44e19bb1e55a107892da3d64939f6e2520b22 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 1 Mar 2025 15:46:41 +0100 Subject: [PATCH] feat(eventsub): implement automod message update (#6010) --- CHANGELOG.md | 2 +- .../payloads/automod-message-update-v2.hpp | 9 +- .../payloads/automod-message-update-v2.cpp | 8 +- src/providers/twitch/TwitchChannel.cpp | 18 ++ src/providers/twitch/TwitchChannel.hpp | 1 + src/providers/twitch/eventsub/Connection.cpp | 24 +- .../automod-message-update/approved.json | 297 ++++++++++++++++++ .../automod-message-update/denied.json | 297 ++++++++++++++++++ .../automod-message-update/expired.json | 297 ++++++++++++++++++ .../automod-message-update/unknown-msg.json | 41 +++ tests/src/EventSubMessages.cpp | 53 +++- 11 files changed, 1028 insertions(+), 19 deletions(-) create mode 100644 tests/snapshots/EventSub/automod-message-update/approved.json create mode 100644 tests/snapshots/EventSub/automod-message-update/denied.json create mode 100644 tests/snapshots/EventSub/automod-message-update/expired.json create mode 100644 tests/snapshots/EventSub/automod-message-update/unknown-msg.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 622d83ad..8cf5357f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ - Bugfix: Fixed the input font not immediately updating when zooming in/out. (#5960) - Bugfix: Fixed color input thinking blue is also red. (#5982) - 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) +- 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) - Dev: Remove unneeded platform specifier for toasts. (#5914) - Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784) - Dev: Removed unused PubSub whisper code. (#5898) diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-update-v2.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-update-v2.hpp index fdd63b6f..22996c0a 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-update-v2.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-update-v2.hpp @@ -3,6 +3,7 @@ #include "twitch-eventsub-ws/payloads/automod-message.hpp" #include "twitch-eventsub-ws/payloads/structured-message.hpp" #include "twitch-eventsub-ws/payloads/subscription.hpp" +#include "twitch-eventsub-ws/string.hpp" #include @@ -12,9 +13,9 @@ namespace chatterino::eventsub::lib::payload::automod_message_update::v2 { struct Event { // Broadcaster of the channel the message was sent in - std::string broadcasterUserID; - std::string broadcasterUserLogin; - std::string broadcasterUserName; + String broadcasterUserID; + String broadcasterUserLogin; + String broadcasterUserName; // User who sent the message std::string userID; @@ -26,7 +27,7 @@ struct Event { std::string moderatorUserLogin; std::string moderatorUserName; - std::string messageID; + String messageID; chat::Message message; // "Approved", "Denied", or "Expired" diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/automod-message-update-v2.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/automod-message-update-v2.cpp index 27b2def2..5b5ae709 100644 --- a/lib/twitch-eventsub-ws/src/generated/payloads/automod-message-update-v2.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/automod-message-update-v2.cpp @@ -25,7 +25,7 @@ boost::json::result_for::type tag_invoke( } auto broadcasterUserID = - boost::json::try_value_to(*jvbroadcasterUserID); + boost::json::try_value_to(*jvbroadcasterUserID); if (broadcasterUserID.has_error()) { @@ -40,7 +40,7 @@ boost::json::result_for::type tag_invoke( } auto broadcasterUserLogin = - boost::json::try_value_to(*jvbroadcasterUserLogin); + boost::json::try_value_to(*jvbroadcasterUserLogin); if (broadcasterUserLogin.has_error()) { @@ -55,7 +55,7 @@ boost::json::result_for::type tag_invoke( } auto broadcasterUserName = - boost::json::try_value_to(*jvbroadcasterUserName); + boost::json::try_value_to(*jvbroadcasterUserName); if (broadcasterUserName.has_error()) { @@ -149,7 +149,7 @@ boost::json::result_for::type tag_invoke( EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); } - auto messageID = boost::json::try_value_to(*jvmessageID); + auto messageID = boost::json::try_value_to(*jvmessageID); if (messageID.has_error()) { diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 88b14c15..d9628b75 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -1522,6 +1522,22 @@ void TwitchChannel::refreshPubSub() }, }, }); + this->eventSubAutomodMessageUpdateHandle = + getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{ + .subscriptionType = "automod.message.update", + .subscriptionVersion = "2", + .conditions = + { + { + "broadcaster_user_id", + roomId, + }, + { + "moderator_user_id", + currentAccount->getUserId(), + }, + }, + }); this->eventSubSuspiciousUserMessageHandle = getApp()->getEventSub()->subscribe(eventsub::SubscriptionRequest{ .subscriptionType = "channel.suspicious_user.message", @@ -1542,6 +1558,8 @@ void TwitchChannel::refreshPubSub() else { this->eventSubChannelModerateHandle.reset(); + this->eventSubAutomodMessageHoldHandle.reset(); + this->eventSubAutomodMessageUpdateHandle.reset(); this->eventSubSuspiciousUserMessageHandle.reset(); } getApp()->getTwitchPubSub()->listenToChannelPointRewards(roomId); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index ed897792..4603a0b2 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -510,6 +510,7 @@ private: eventsub::SubscriptionHandle eventSubChannelModerateHandle; eventsub::SubscriptionHandle eventSubAutomodMessageHoldHandle; + eventsub::SubscriptionHandle eventSubAutomodMessageUpdateHandle; eventsub::SubscriptionHandle eventSubSuspiciousUserMessageHandle; friend class TwitchIrcServer; diff --git a/src/providers/twitch/eventsub/Connection.cpp b/src/providers/twitch/eventsub/Connection.cpp index c273ecd0..04868a26 100644 --- a/src/providers/twitch/eventsub/Connection.cpp +++ b/src/providers/twitch/eventsub/Connection.cpp @@ -232,12 +232,28 @@ void Connection::onAutomodMessageHold( }); } void Connection::onAutomodMessageUpdate( - const lib::messages::Metadata &metadata, + const lib::messages::Metadata & /*metadata*/, const lib::payload::automod_message_update::v2::Payload &payload) { - (void)metadata; - qCDebug(LOG) << "On automod message update for" - << payload.event.broadcasterUserLogin.c_str(); + auto *channel = dynamic_cast( + getApp() + ->getTwitch() + ->getChannelOrEmpty(payload.event.broadcasterUserLogin.qt()) + .get()); + if (!channel || channel->isEmpty()) + { + qCDebug(LOG) + << "Automod message hold for broadcaster we're not interested in" + << payload.event.broadcasterUserLogin.qt(); + return; + } + + // Gray out approve/deny button upon "ALLOWED" and "DENIED" statuses + // They are versions of automod_message_(denied|approved) but for mods. + auto id = "automod_" + payload.event.messageID.qt(); + runInGuiThread([channel, id] { + channel->disableMessage(id); + }); } void Connection::onChannelSuspiciousUserMessage( diff --git a/tests/snapshots/EventSub/automod-message-update/approved.json b/tests/snapshots/EventSub/automod-message-update/approved.json new file mode 100644 index 00000000..a18d6088 --- /dev/null +++ b/tests/snapshots/EventSub/automod-message-update/approved.json @@ -0,0 +1,297 @@ +{ + "input": [ + { + "__subscription": "automod-message-hold", + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "reason": "automod", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + }, + { + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "moderator_user_id": "489584266", + "moderator_user_login": "uint128", + "moderator_user_name": "uint128", + "reason": "automod", + "status": "approved", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + } + ], + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "emote": { + "homePage": "https://dashboard.twitch.tv/settings/moderation/automod", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "AutoMod" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "AutoMod", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff0000ff", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "AutoMod:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Held", + "a", + "message", + "for", + "reason:", + "swearing", + "level", + "4.", + "Allow", + "will", + "post", + "it", + "in", + "chat.", + "" + ] + }, + { + "color": "#ff00ff00", + "flags": "Text", + "link": { + "type": "AutoModAllow", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Allow" + ] + }, + { + "color": "#ffff0000", + "flags": "Text", + "link": { + "type": "AutoModDeny", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "", + "Deny" + ] + } + ], + "flags": "Timeout|Disabled|PubSub|AutoMod|AutoModOffendingMessageHeader|EventSub", + "id": "automod_19d067ff-89b5-4790-a720-97599894eb6b", + "localizedName": "", + "loginName": "automod", + "messageText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "searchText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + }, + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "color": "System", + "flags": "ChannelName", + "link": { + "type": "JumpToChannel", + "value": "pajlada" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "#pajlada" + ] + }, + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "color": "Text", + "fallbackColor": "Text", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "Text", + "userLoginName": "nerixyz", + "words": [ + "nerixyz:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "ass" + ] + } + ], + "flags": "Timeout|PubSub|AutoMod|AutoModOffendingMessage|EventSub", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz: ass", + "searchText": "nerixyz: ass", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/automod-message-update/denied.json b/tests/snapshots/EventSub/automod-message-update/denied.json new file mode 100644 index 00000000..7883eea3 --- /dev/null +++ b/tests/snapshots/EventSub/automod-message-update/denied.json @@ -0,0 +1,297 @@ +{ + "input": [ + { + "__subscription": "automod-message-hold", + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "reason": "automod", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + }, + { + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "moderator_user_id": "489584266", + "moderator_user_login": "uint128", + "moderator_user_name": "uint128", + "reason": "automod", + "status": "denied", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + } + ], + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "emote": { + "homePage": "https://dashboard.twitch.tv/settings/moderation/automod", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "AutoMod" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "AutoMod", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff0000ff", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "AutoMod:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Held", + "a", + "message", + "for", + "reason:", + "swearing", + "level", + "4.", + "Allow", + "will", + "post", + "it", + "in", + "chat.", + "" + ] + }, + { + "color": "#ff00ff00", + "flags": "Text", + "link": { + "type": "AutoModAllow", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Allow" + ] + }, + { + "color": "#ffff0000", + "flags": "Text", + "link": { + "type": "AutoModDeny", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "", + "Deny" + ] + } + ], + "flags": "Timeout|Disabled|PubSub|AutoMod|AutoModOffendingMessageHeader|EventSub", + "id": "automod_19d067ff-89b5-4790-a720-97599894eb6b", + "localizedName": "", + "loginName": "automod", + "messageText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "searchText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + }, + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "color": "System", + "flags": "ChannelName", + "link": { + "type": "JumpToChannel", + "value": "pajlada" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "#pajlada" + ] + }, + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "color": "Text", + "fallbackColor": "Text", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "Text", + "userLoginName": "nerixyz", + "words": [ + "nerixyz:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "ass" + ] + } + ], + "flags": "Timeout|PubSub|AutoMod|AutoModOffendingMessage|EventSub", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz: ass", + "searchText": "nerixyz: ass", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/automod-message-update/expired.json b/tests/snapshots/EventSub/automod-message-update/expired.json new file mode 100644 index 00000000..e33b9fab --- /dev/null +++ b/tests/snapshots/EventSub/automod-message-update/expired.json @@ -0,0 +1,297 @@ +{ + "input": [ + { + "__subscription": "automod-message-hold", + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "reason": "automod", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + }, + { + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "moderator_user_id": "489584266", + "moderator_user_login": "uint128", + "moderator_user_name": "uint128", + "reason": "automod", + "status": "expired", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + } + ], + "output": [ + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "emote": { + "homePage": "https://dashboard.twitch.tv/settings/moderation/automod", + "images": { + "1x": "" + }, + "name": "", + "tooltip": "AutoMod" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "AutoMod", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff0000ff", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "AutoMod:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Held", + "a", + "message", + "for", + "reason:", + "swearing", + "level", + "4.", + "Allow", + "will", + "post", + "it", + "in", + "chat.", + "" + ] + }, + { + "color": "#ff00ff00", + "flags": "Text", + "link": { + "type": "AutoModAllow", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "Allow" + ] + }, + { + "color": "#ffff0000", + "flags": "Text", + "link": { + "type": "AutoModDeny", + "value": "19d067ff-89b5-4790-a720-97599894eb6b" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "", + "Deny" + ] + } + ], + "flags": "Timeout|Disabled|PubSub|AutoMod|AutoModOffendingMessageHeader|EventSub", + "id": "automod_19d067ff-89b5-4790-a720-97599894eb6b", + "localizedName": "", + "loginName": "automod", + "messageText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "searchText": "AutoMod: Held a message for reason: swearing level 4. Allow will post it in chat. Allow Deny", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + }, + { + "badgeInfos": { + }, + "badges": [ + ], + "channelName": "pajlada", + "count": 1, + "displayName": "", + "elements": [ + { + "color": "System", + "flags": "ChannelName", + "link": { + "type": "JumpToChannel", + "value": "pajlada" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "#pajlada" + ] + }, + { + "element": { + "color": "System", + "flags": "Timestamp", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "12:31" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "12:31:47", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "color": "Text", + "fallbackColor": "Text", + "flags": "Text|Mention", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "MentionElement", + "userColor": "Text", + "userLoginName": "nerixyz", + "words": [ + "nerixyz:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "ass" + ] + } + ], + "flags": "Timeout|PubSub|AutoMod|AutoModOffendingMessage|EventSub", + "id": "", + "localizedName": "", + "loginName": "nerixyz", + "messageText": "nerixyz: ass", + "searchText": "nerixyz: ass", + "serverReceivedTime": "2024-05-14T12:31:47Z", + "timeoutUser": "", + "userID": "", + "usernameColor": "#ff000000" + } + ] +} diff --git a/tests/snapshots/EventSub/automod-message-update/unknown-msg.json b/tests/snapshots/EventSub/automod-message-update/unknown-msg.json new file mode 100644 index 00000000..af210d3b --- /dev/null +++ b/tests/snapshots/EventSub/automod-message-update/unknown-msg.json @@ -0,0 +1,41 @@ +{ + "input": { + "automod": { + "boundaries": [ + { + "end_pos": 2, + "start_pos": 0 + } + ], + "category": "swearing", + "level": 4 + }, + "blocked_term": null, + "broadcaster_user_id": "11148817", + "broadcaster_user_login": "pajlada", + "broadcaster_user_name": "pajlada", + "held_at": "2025-02-28T16:15:15.008668809Z", + "message": { + "fragments": [ + { + "cheermote": null, + "emote": null, + "text": "ass", + "type": "text" + } + ], + "text": "ass" + }, + "message_id": "19d067ff-89b5-4790-a720-97599894eb6b", + "moderator_user_id": "489584266", + "moderator_user_login": "uint128", + "moderator_user_name": "uint128", + "reason": "automod", + "status": "approved", + "user_id": "129546453", + "user_login": "nerixyz", + "user_name": "nerixyz" + }, + "output": [ + ] +} diff --git a/tests/src/EventSubMessages.cpp b/tests/src/EventSubMessages.cpp index e5cd6d41..fb483cf5 100644 --- a/tests/src/EventSubMessages.cpp +++ b/tests/src/EventSubMessages.cpp @@ -82,6 +82,25 @@ const std::map "cost": 0 })", }, + { + "automod-message-update", + R"({ + "id": "a3122e32-6498-4847-8675-109b9b94f29c", + "status": "enabled", + "type": "automod.message.update", + "version": "2", + "condition": { + "broadcaster_user_id": "489584266", + "moderator_user_id": "489584266" + }, + "transport": { + "method":"websocket", + "session_id":"AgoQ59RRLw0mS6S000QtK8f54BIGY2VsbC1j" + }, + "created_at": "2025-02-28T15:55:37.85489173Z", + "cost": 0 + })", + }, { "channel-suspicious-user-message", R"({ @@ -222,13 +241,35 @@ TEST_P(TestEventSubMessagesP, Run) auto subscription = SUBSCRIPTIONS.find(subcategory); ASSERT_NE(subscription, SUBSCRIPTIONS.end()) << subcategory; - auto json = makePayload(subscription->second, snapshot->input().toObject()); + QJsonArray input{snapshot->input()}; + if (snapshot->input().isArray()) + { + input = snapshot->input().toArray(); + } - std::unique_ptr listener = - std::make_unique(); - auto ec = eventsub::lib::handleMessage(listener, json); - ASSERT_FALSE(ec.failed()) - << ec.what() << ec.message() << ec.location().to_string(); + for (const auto inputRef : input) + { + auto inputObj = inputRef.toObject(); + + // "__subscription" overrides the subscription type the message is built + // as. By default, the subcategory (directory) name is used. + auto eventSubscription = subscription; + if (inputObj.contains(u"__subscription")) + { + eventSubscription = + SUBSCRIPTIONS.find(inputObj[u"__subscription"].toString()); + ASSERT_NE(eventSubscription, SUBSCRIPTIONS.end()); + inputObj.remove("__subscription"); + } + + auto json = makePayload(eventSubscription->second, inputObj); + + std::unique_ptr listener = + std::make_unique(); + auto ec = eventsub::lib::handleMessage(listener, json); + ASSERT_FALSE(ec.failed()) + << ec.what() << ec.message() << ec.location().to_string(); + } auto messages = mainChannel->getMessageSnapshot(); QJsonArray output;