From f4212028d6ef301f2c5c29ae3230f32960538a3d Mon Sep 17 00:00:00 2001 From: James Upjohn Date: Sat, 3 Jan 2026 02:36:22 +1300 Subject: [PATCH] feat: show mod/VIP badges from shared chats (#6653) Reviewed-by: Nerixyz Reviewed-by: Rasmus Karlsson --- CHANGELOG.md | 1 + mocks/include/mocks/TwitchUsers.hpp | 9 +- src/messages/MessageBuilder.cpp | 50 ++++- src/providers/twitch/TwitchIrc.cpp | 5 +- src/providers/twitch/TwitchIrc.hpp | 10 +- .../shared-chat-announcement.json | 29 ++- .../IrcMessageHandler/shared-chat-emotes.json | 6 +- .../IrcMessageHandler/shared-chat-known.json | 25 ++- .../shared-chat-moderator-both-channels.json | 180 +++++++++++++++++ ...hared-chat-moderator-external-channel.json | 188 +++++++++++++++++ .../shared-chat-unknown.json | 29 ++- .../shared-chat-vip-both-channels.json | 191 ++++++++++++++++++ .../shared-chat-vip-external-channel.json | 189 +++++++++++++++++ 13 files changed, 890 insertions(+), 22 deletions(-) create mode 100644 tests/snapshots/IrcMessageHandler/shared-chat-moderator-both-channels.json create mode 100644 tests/snapshots/IrcMessageHandler/shared-chat-moderator-external-channel.json create mode 100644 tests/snapshots/IrcMessageHandler/shared-chat-vip-both-channels.json create mode 100644 tests/snapshots/IrcMessageHandler/shared-chat-vip-external-channel.json diff --git a/CHANGELOG.md b/CHANGELOG.md index e0aca98d..477dd60c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Minor: Added `debug.traceback` for plugins. (#6652) - Minor: Added title and duration options for `/clip` command. (#6669) - Minor: Added Markdown support to user notes. (#6490) +- Minor: Moderators and VIPs in shared chats now show their channel badges. (#6653) - Bugfix: Moderation checks now include the lead moderator badge. (#6642) - Bugfix: Fixed lead moderator badges not being filtered by the `Channel` badge setting. (#6665) - Bugfix: Expose the "Extra extension IDs" setting on non-Windows systems too. (#6509) diff --git a/mocks/include/mocks/TwitchUsers.hpp b/mocks/include/mocks/TwitchUsers.hpp index 14f6bf7e..1aeaaf5a 100644 --- a/mocks/include/mocks/TwitchUsers.hpp +++ b/mocks/include/mocks/TwitchUsers.hpp @@ -10,12 +10,15 @@ class TwitchUsers : public ITwitchUsers public: TwitchUsers() = default; - std::shared_ptr resolveID(const UserId &id) + std::shared_ptr resolveID(const UserId &id) override { TwitchUser u = { .id = id.string, - .name = {}, - .displayName = {}, + .name = "forsen", + .displayName = "forsen", + .profilePictureUrl = + "https://static-cdn.jtvnw.net/jtv_user_pictures/" + "forsen-profile_image-48b43e1e4f54b5c8-300x300.png", }; return std::make_shared(u); } diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 6be351eb..7c1233e9 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -348,6 +348,38 @@ void appendBadges(MessageBuilder *builder, const std::vector &badges, builder->message().badgeInfos = badgeInfos; } +std::vector appendSharedChatBadges( + MessageBuilder *builder, const std::vector &sharedBadges, + const QString &sharedChannelName, const TwitchChannel *twitchChannel) +{ + auto appendedBadges = std::vector{}; + for (const auto &badge : sharedBadges) + { + if (badge.key_ != "moderator" && badge.key_ != "vip") + { + continue; + } + + auto badgeEmote = getTwitchBadge(badge, twitchChannel); + if (!badgeEmote) + { + continue; + } + + auto tooltip = (*badgeEmote)->tooltip.string; + if (sharedChannelName != "") + { + tooltip = QString("%1 (%2)").arg(tooltip, sharedChannelName); + } + + builder->emplace(*badgeEmote, badge.flag_) + ->setTooltip(tooltip); + appendedBadges.push_back(badge); + } + + return appendedBadges; +} + bool doesWordContainATwitchEmote( int cursor, const QString &word, const std::vector &twitchEmotes, @@ -2379,6 +2411,8 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, return; } + auto badges = parseBadgeTag(tags); + if (this->message().flags.has(MessageFlag::SharedMessage)) { const QString sourceId = tags["source-room-id"].toString(); @@ -2410,10 +2444,24 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, this->emplace( makeSharedChatBadge(sourceName, sourceProfilePicture, sourceLogin), MessageElementFlag::BadgeSharedChannel); + + const auto sourceBadges = parseBadgeTag(tags, "source-badges"); + const auto appendedBadges = appendSharedChatBadges( + this, sourceBadges, sourceName, twitchChannel); + + // Dedup mod/vip badges if user is mod/vip in both chats, + // preferring source channel's badges for the tooltips + for (const auto &appendedBadge : appendedBadges) + { + if (auto b = std::ranges::find(badges, appendedBadge); + b != badges.end()) + { + badges.erase(b); + } + } } auto badgeInfos = parseBadgeInfoTag(tags); - auto badges = parseBadgeTag(tags); appendBadges(this, badges, badgeInfos, twitchChannel); } diff --git a/src/providers/twitch/TwitchIrc.cpp b/src/providers/twitch/TwitchIrc.cpp index c36fa78b..c5ae7f9a 100644 --- a/src/providers/twitch/TwitchIrc.cpp +++ b/src/providers/twitch/TwitchIrc.cpp @@ -106,11 +106,12 @@ std::unordered_map parseBadgeInfoTag(const QVariantMap &tags) return infoMap; } -std::vector parseBadgeTag(const QVariantMap &tags) +std::vector parseBadgeTag(const QVariantMap &tags, + const QString &tagName) { std::vector b; - auto badgesIt = tags.constFind("badges"); + auto badgesIt = tags.constFind(tagName); if (badgesIt == tags.end()) { return b; diff --git a/src/providers/twitch/TwitchIrc.hpp b/src/providers/twitch/TwitchIrc.hpp index 60529fb7..049680d2 100644 --- a/src/providers/twitch/TwitchIrc.hpp +++ b/src/providers/twitch/TwitchIrc.hpp @@ -35,18 +35,20 @@ struct TwitchEmoteOccurrence { /// @returns A map of badge-names to their values std::unordered_map parseBadgeInfoTag(const QVariantMap &tags); -/// @brief Parses the `badges` tag of an IRC message +/// @brief Parses the badges from the specified tag of an IRC message /// -/// The `badges` tag contains a comma separated list of key-value elements which -/// make up the name and version of each badge. +/// All `badges`-type tags contain a comma separated list of key-value elements +/// which make up the name and version of each badge. /// /// **Example**: /// `badges=broadcaster/1,subscriber/18` would be parsed as /// `[(broadcaster, 1), (subscriber, 18)]` /// /// @param tags The tags of the IRC message +/// @param tagName The name of the tag to read badges from /// @returns A list of badges (name and version) -std::vector parseBadgeTag(const QVariantMap &tags); +std::vector parseBadgeTag(const QVariantMap &tags, + const QString &tagName = "badges"); /// @brief Parses Twitch emotes in an IRC message /// diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 1d521fbe..4033dddb 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -66,19 +66,40 @@ }, { "emote": { - "homePage": "https://link.twitch.tv/SharedChatViewer", + "homePage": "https://www.twitch.tv/forsen", "images": { - "1x": "" + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" }, "name": "", - "tooltip": "Shared Message" + "tooltip": "Shared Message from forsen" }, "flags": "BadgeSharedChannel", "link": { "type": "None", "value": "" }, - "tooltip": "Shared Message", + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/3" + }, + "name": "", + "tooltip": "Moderator" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Moderator (forsen)", "trailingSpace": true, "type": "BadgeElement" }, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json index 1adb6aea..52b5fe16 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json @@ -66,9 +66,11 @@ }, { "emote": { - "homePage": "https://link.twitch.tv/SharedChatViewer", + "homePage": "https://www.twitch.tv/forsen", "images": { - "1x": "" + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" }, "name": "", "tooltip": "Shared Message from twitchdev" diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-known.json b/tests/snapshots/IrcMessageHandler/shared-chat-known.json index a0fb3042..b6b5ec6d 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-known.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-known.json @@ -66,9 +66,11 @@ }, { "emote": { - "homePage": "https://link.twitch.tv/SharedChatViewer", + "homePage": "https://www.twitch.tv/forsen", "images": { - "1x": "" + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" }, "name": "", "tooltip": "Shared Message from twitchdev" @@ -82,6 +84,25 @@ "trailingSpace": true, "type": "BadgeElement" }, + { + "emote": { + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/3" + }, + "name": "", + "tooltip": "Moderator" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Moderator (twitchdev)", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-moderator-both-channels.json b/tests/snapshots/IrcMessageHandler/shared-chat-moderator-both-channels.json new file mode 100644 index 00000000..a54c485e --- /dev/null +++ b/tests/snapshots/IrcMessageHandler/shared-chat-moderator-both-channels.json @@ -0,0 +1,180 @@ +{ + "input": "@mod=1;badges=moderator/1;tmi-sent-ts=1729627237027;id=3e0750f7-541e-4f7c-8fec-4f943bfd84a3;room-id=11148817;user-id=100229878;display-name=nuuls;badges=bits/100;badge-info=;color=#00FF7F;flags=5-8:P.3;user-type=mod;emotes=;source-only=0;source-badge-info=subscriber/52;source-id=0dd9957e-6140-498f-a6e8-e62d1b43dfe7;source-room-id=22484632;source-badges=moderator/1,subscriber/48 :nuuls!nuuls@nuuls.tmi.twitch.tv PRIVMSG #pajlada :I'm a mod in the current & shared channel", + "output": [ + { + "badgeInfos": { + }, + "badges": [ + "bits" + ], + "channelName": "pajlada", + "count": 1, + "displayName": "nuuls", + "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": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "20:00" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "20:00:37", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "emote": { + "homePage": "https://www.twitch.tv/forsen", + "images": { + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" + }, + "name": "", + "tooltip": "Shared Message from forsen" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/3" + }, + "name": "", + "tooltip": "Moderator" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Moderator (forsen)", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://bits.twitch.tv", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/3" + }, + "name": "", + "tooltip": "cheer 100" + }, + "flags": "BadgeVanity", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Twitch cheer 100", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff00ff7f", + "flags": "Username", + "link": { + "type": "UserInfo", + "value": "nuuls" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "nuuls:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "I'm", + "a", + "mod", + "in", + "the", + "current", + "&", + "shared", + "channel" + ] + }, + { + "background": "#ffa0a0a4", + "flags": "ReplyButton", + "link": { + "type": "ReplyToMessage", + "value": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3" + }, + "padding": 2, + "tooltip": "", + "trailingSpace": true, + "type": "CircularImageElement", + "url": "" + } + ], + "flags": "Collapsed|SharedMessage", + "frozen": false, + "id": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3", + "localizedName": "", + "loginName": "nuuls", + "messageText": "I'm a mod in the current & shared channel", + "searchText": "nuuls nuuls: I'm a mod in the current & shared channel ", + "serverReceivedTime": "2024-10-22T20:00:37Z", + "timeoutUser": "", + "userID": "100229878", + "usernameColor": "#ff00ff7f" + } + ] +} diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-moderator-external-channel.json b/tests/snapshots/IrcMessageHandler/shared-chat-moderator-external-channel.json new file mode 100644 index 00000000..b9d21dda --- /dev/null +++ b/tests/snapshots/IrcMessageHandler/shared-chat-moderator-external-channel.json @@ -0,0 +1,188 @@ +{ + "input": "@tmi-sent-ts=1729627237027;id=3e0750f7-541e-4f7c-8fec-4f943bfd84a3;room-id=11148817;user-id=100229878;display-name=nuuls;badges=bits/100;badge-info=;color=#00FF7F;flags=5-8:P.3;user-type=;emotes=;source-only=0;source-badge-info=subscriber/52;source-id=0dd9957e-6140-498f-a6e8-e62d1b43dfe7;source-room-id=22484632;source-badges=moderator/1,subscriber/48 :nuuls!nuuls@nuuls.tmi.twitch.tv PRIVMSG #pajlada :I'm a mod in a shared channel", + "output": [ + { + "badgeInfos": { + }, + "badges": [ + "bits" + ], + "channelName": "pajlada", + "count": 1, + "displayName": "nuuls", + "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": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "20:00" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "20:00:37", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "emote": { + "homePage": "https://www.twitch.tv/forsen", + "images": { + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" + }, + "name": "", + "tooltip": "Shared Message from forsen" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/3" + }, + "name": "", + "tooltip": "Moderator" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Moderator (forsen)", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://bits.twitch.tv", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/3" + }, + "name": "", + "tooltip": "cheer 100" + }, + "flags": "BadgeVanity", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Twitch cheer 100", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff00ff7f", + "flags": "Username", + "link": { + "type": "UserInfo", + "value": "nuuls" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "nuuls:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "I'm", + "a", + "mod", + "in", + "a", + "shared", + "channel" + ] + }, + { + "background": "#ffa0a0a4", + "flags": "ReplyButton", + "link": { + "type": "ReplyToMessage", + "value": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3" + }, + "padding": 2, + "tooltip": "", + "trailingSpace": true, + "type": "CircularImageElement", + "url": "" + } + ], + "flags": "Collapsed|SharedMessage", + "frozen": false, + "id": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3", + "localizedName": "", + "loginName": "nuuls", + "messageText": "I'm a mod in a shared channel", + "searchText": "nuuls nuuls: I'm a mod in a shared channel ", + "serverReceivedTime": "2024-10-22T20:00:37Z", + "timeoutUser": "", + "userID": "100229878", + "usernameColor": "#ff00ff7f" + } + ] +} diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index a54019b8..3fbe0698 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -66,19 +66,40 @@ }, { "emote": { - "homePage": "https://link.twitch.tv/SharedChatViewer", + "homePage": "https://www.twitch.tv/forsen", "images": { - "1x": "" + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" }, "name": "", - "tooltip": "Shared Message" + "tooltip": "Shared Message from forsen" }, "flags": "BadgeSharedChannel", "link": { "type": "None", "value": "" }, - "tooltip": "Shared Message", + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/3" + }, + "name": "", + "tooltip": "Moderator" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Moderator (forsen)", "trailingSpace": true, "type": "BadgeElement" }, diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-vip-both-channels.json b/tests/snapshots/IrcMessageHandler/shared-chat-vip-both-channels.json new file mode 100644 index 00000000..bcd0569e --- /dev/null +++ b/tests/snapshots/IrcMessageHandler/shared-chat-vip-both-channels.json @@ -0,0 +1,191 @@ +{ + "input": "@vip=1;badges=vip/1;tmi-sent-ts=1729627237027;id=3e0750f7-541e-4f7c-8fec-4f943bfd84a3;room-id=11148817;user-id=100229878;display-name=nuuls;badges=bits/100;badge-info=;color=#00FF7F;flags=5-8:P.3;user-type=;emotes=;source-only=0;source-badge-info=subscriber/52;source-id=0dd9957e-6140-498f-a6e8-e62d1b43dfe7;source-room-id=22484632;source-badges=vip/1,subscriber/48 :nuuls!nuuls@nuuls.tmi.twitch.tv PRIVMSG #pajlada :I'm a vip in the current & shared channel", + "output": [ + { + "badgeInfos": { + }, + "badges": [ + "bits" + ], + "channelName": "pajlada", + "count": 1, + "displayName": "nuuls", + "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": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "20:00" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "20:00:37", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "emote": { + "homePage": "https://www.twitch.tv/forsen", + "images": { + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" + }, + "name": "", + "tooltip": "Shared Message from forsen" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://help.twitch.tv/customer/en/portal/articles/659115-twitch-chat-badges-guide", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/3" + }, + "name": "", + "tooltip": "VIP" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "VIP (forsen)", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://bits.twitch.tv", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/3" + }, + "name": "", + "tooltip": "cheer 100" + }, + "flags": "BadgeVanity", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Twitch cheer 100", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff00ff7f", + "flags": "Username", + "link": { + "type": "UserInfo", + "value": "nuuls" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "nuuls:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "I'm", + "a", + "vip", + "in", + "the", + "current", + "&", + "shared", + "channel" + ] + }, + { + "background": "#ffa0a0a4", + "flags": "ReplyButton", + "link": { + "type": "ReplyToMessage", + "value": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3" + }, + "padding": 2, + "tooltip": "", + "trailingSpace": true, + "type": "CircularImageElement", + "url": "" + } + ], + "flags": "Collapsed|SharedMessage", + "frozen": false, + "id": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3", + "localizedName": "", + "loginName": "nuuls", + "messageText": "I'm a vip in the current & shared channel", + "searchText": "nuuls nuuls: I'm a vip in the current & shared channel ", + "serverReceivedTime": "2024-10-22T20:00:37Z", + "timeoutUser": "", + "userID": "100229878", + "usernameColor": "#ff00ff7f" + } + ] +} diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-vip-external-channel.json b/tests/snapshots/IrcMessageHandler/shared-chat-vip-external-channel.json new file mode 100644 index 00000000..752a29fa --- /dev/null +++ b/tests/snapshots/IrcMessageHandler/shared-chat-vip-external-channel.json @@ -0,0 +1,189 @@ +{ + "input": "@tmi-sent-ts=1729627237027;id=3e0750f7-541e-4f7c-8fec-4f943bfd84a3;room-id=11148817;user-id=100229878;display-name=nuuls;badges=bits/100;badge-info=;color=#00FF7F;flags=5-8:P.3;user-type=;emotes=;source-only=0;source-badge-info=subscriber/52;source-id=0dd9957e-6140-498f-a6e8-e62d1b43dfe7;source-room-id=22484632;source-badges=vip/1,subscriber/48 :nuuls!nuuls@nuuls.tmi.twitch.tv PRIVMSG #pajlada :I'm a vip in a shared channel", + "output": [ + { + "badgeInfos": { + }, + "badges": [ + "bits" + ], + "channelName": "pajlada", + "count": 1, + "displayName": "nuuls", + "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": "TimestampMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "20:00" + ] + }, + "flags": "Timestamp", + "format": "", + "link": { + "type": "None", + "value": "" + }, + "time": "20:00:37", + "tooltip": "", + "trailingSpace": true, + "type": "TimestampElement" + }, + { + "flags": "ModeratorTools", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "", + "trailingSpace": true, + "type": "TwitchModerationElement" + }, + { + "emote": { + "homePage": "https://www.twitch.tv/forsen", + "images": { + "1x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-28x28.png", + "2x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-70x70.png", + "3x": "https://static-cdn.jtvnw.net/jtv_user_pictures/forsen-profile_image-48b43e1e4f54b5c8-150x150.png" + }, + "name": "", + "tooltip": "Shared Message from forsen" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Shared Message from forsen", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://help.twitch.tv/customer/en/portal/articles/659115-twitch-chat-badges-guide", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/3" + }, + "name": "", + "tooltip": "VIP" + }, + "flags": "BadgeChannelAuthority", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "VIP (forsen)", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "emote": { + "homePage": "https://bits.twitch.tv", + "images": { + "1x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/1", + "2x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/2", + "3x": "https://static-cdn.jtvnw.net/badges/v1/09d93036-e7ce-431c-9a9e-7044297133f2/3" + }, + "name": "", + "tooltip": "cheer 100" + }, + "flags": "BadgeVanity", + "link": { + "type": "None", + "value": "" + }, + "tooltip": "Twitch cheer 100", + "trailingSpace": true, + "type": "BadgeElement" + }, + { + "color": "#ff00ff7f", + "flags": "Username", + "link": { + "type": "UserInfo", + "value": "nuuls" + }, + "style": "ChatMediumBold", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "nuuls:" + ] + }, + { + "color": "Text", + "flags": "Text", + "link": { + "type": "None", + "value": "" + }, + "style": "ChatMedium", + "tooltip": "", + "trailingSpace": true, + "type": "TextElement", + "words": [ + "I'm", + "a", + "vip", + "in", + "a", + "shared", + "channel" + ] + }, + { + "background": "#ffa0a0a4", + "flags": "ReplyButton", + "link": { + "type": "ReplyToMessage", + "value": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3" + }, + "padding": 2, + "tooltip": "", + "trailingSpace": true, + "type": "CircularImageElement", + "url": "" + } + ], + "flags": "Collapsed|SharedMessage", + "frozen": false, + "id": "3e0750f7-541e-4f7c-8fec-4f943bfd84a3", + "localizedName": "", + "loginName": "nuuls", + "messageText": "I'm a vip in a shared channel", + "searchText": "nuuls nuuls: I'm a vip in a shared channel ", + "serverReceivedTime": "2024-10-22T20:00:37Z", + "timeoutUser": "", + "userID": "100229878", + "usernameColor": "#ff00ff7f" + } + ] +}