From f4036b269bc9dfa789d6bb97278e6a3e5ddc680e Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 03:40:06 -0700 Subject: [PATCH 1/8] feat: add shared chat badge --- lib/lrucache/lrucache/lrucache.hpp | 11 ++++++++ resources/twitch/sharedChat.png | Bin 0 -> 2006 bytes src/messages/MessageBuilder.cpp | 30 +++++++++++++++++++-- src/messages/MessageBuilder.hpp | 1 + src/messages/MessageElement.hpp | 6 ++++- src/providers/twitch/TwitchIrcServer.cpp | 33 +++++++++++++++++++++++ src/providers/twitch/TwitchIrcServer.hpp | 11 ++++++++ src/singletons/WindowManager.cpp | 1 + 8 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 resources/twitch/sharedChat.png diff --git a/lib/lrucache/lrucache/lrucache.hpp b/lib/lrucache/lrucache/lrucache.hpp index ef7d031db..183bc3b02 100644 --- a/lib/lrucache/lrucache/lrucache.hpp +++ b/lib/lrucache/lrucache/lrucache.hpp @@ -71,6 +71,17 @@ public: } } + void remove(const key_t &key) + { + auto it = _cache_items_map.find(key); + if (it == _cache_items_map.end()) + { + throw std::range_error("There is no such key in cache"); + } + _cache_items_list.erase(it->second); + _cache_items_map.erase(it); + } + const value_t &get(const key_t &key) { auto it = _cache_items_map.find(key); diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png new file mode 100644 index 0000000000000000000000000000000000000000..116108c77032a75551cea742eae343bc3423c151 GIT binary patch literal 2006 zcmV;{2Pyc8P)Ev-aA1uf5Jb za5U$itial5t^fM=f7b2$*4i~mbdZfh4z>a9j*dVD=m=DRjz9(I2vmTMKn3UsRDg~^ z1(@0b&MsgH@GIa{;CP@9*ahqW-U0pr{1e#Qg7zy#t-_sjFK`oZJ#ctH`LBUhz#YKu zs*I=_RSC2l_#4m@QTI#WT3~$@hE$Da=b##J8}KA?;(+zMwZO_cUt6KsIp|j4mW1n^WI>PWf4Qd zoiqLwIkepLqQkq%Q^q&v?3A=jQq3t_lXQ`!Ay1tRp?1Z*$abf(2P+cjA%}OH^ZQRS z#3`D52RtH8NIEUVu9!Kf&<K;P7F{$sg_HmwD=32Ygt-`vLG<=UFX-FNGL^TtQsH zV&Mwn3bxqE4+0wkxz_?)3;4W<5oo)UABf=aVO~Kyvz`3&K{1AV}m&a-ztWs-1vSP{N(@@|jyB1WK%PJSA&B7!H0>qtAWZaePKKBiNjFRC&8XKW>5obetA^pf&S~?h43U=0zmvYi zBaS4k3w_UKW6~?x9|?4^kNpwFFEM{>9=2L#MKW&z{*u$)$(S$J?fGphwv-QHE7UJZ z4=b=Z)9Jv+IqmF66FsQ$S6Z3TlgeO1(w{uNz?aUGv@f!qeUi=%Xrq!LmP||KcM3x= zr2Jv!OH1Sr+2&!lN{XWj1wztMvPhu21<1mfq}%16kVDdsWnOtv@12m%Idfv#sq)~k zftgsat8ub=0{i2{+p!(;M&Mb}OL+`V!l17#Uk|($OWq2Beo*LKUMN6E zpaOIRDnLh|0(1l_K!`wx0XG8w#_sg!0H!hE)q4AfC2Z;i{u9YLTIamM;q;?mqtq|y z?ZAy+tFc9sPj8o2z`0okC&GFApu_7*&o5N@z+nq;2JpXn{uSWoO>(|j=lsU^W8M(E zmi7_{Y}b5EA|HCHk%t8i=OpA^pt{V-3-~*x_`$^IiT|%9F!-Lp3D|X+BE(+u#e#!M(qpKO(DOEAC0R z0zU#S#qL?J#-f#Fa0nK-@5iQzPp~QD24JH8uOB%}oQqrYmctgNg_;~3dn|(E!9@Mx zI%ngznxs`(b=zH_g-YKf8<#Y-1V>Hj8*w8Zk-YWu<{)?Wo>H<~H;aYX=L+s+sX@vO zF_FHwaAci(VWNx|c>=l7zF8%VY8F}Ii~?6i@Lf&%eO!_J$myEzINkE4gE*`tX;jkL zP4F&~|154)(pg#c@(9;BMBhj{{(CA;v`BemmwSlTe2>M65-E>py8LHdH@85XpJs@2 zpfMt|B}gD7{Vs#(5yhWp`O+IzzGaixtb}bv$39bl7qBmMTzm%H13V1u30q7uF^vDb z{a|32#ErS4h~l@HL+A6u`Z0k%!@TONFqa_ao3BSB>a+^0lbO(i!(*`P|M`0c!(Qz4 zbQ`df$zSaphk4SouvkvxCdV}331F#*Zw&jIv8?}V+2!yC{}f4&XOvy1a_&V|nj-V%eApm+$KX{gE6tH~pQ>4Pc1SwKd4X1m zR9YNG3e-qll2%B1R@NJ0v0befDNmgB$`D5Zt5_%w#e(NQ!@d@ri`}&7#y)N8!senq z^)!rqLhwFzob(!#zoDN7JdXd8=H0;WOWOBW?C8ivdd_js&bK?VMA}LE&T}4Wn4{`O z4GYDA_C8r1$D)Yt_w?=!{33|-XAME(SncX|!>vkFCEX))dtQ<>H-cl4q>p5`-&W<# oIjx$5ez0igA=Vb?2vmUY0DcCkx0}UhmH+?%07*qoM6N<$g1u_(00000 literal 0 HcmV?d00001 diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index e470b01ce..9b6cfe6fa 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -384,6 +384,17 @@ EmotePtr makeAutoModBadge() Url{"https://dashboard.twitch.tv/settings/moderation/automod"}}); } +EmotePtr makeSharedChatBadge(const QString &sourceName) +{ + return std::make_shared( + Emote{"SharedChat_" + sourceName, + ImageSet{Image::fromResourcePixmap( + getResources().twitch.sharedChat, 0.25)}, + Tooltip{"Shared Message" + + (sourceName.isEmpty() ? "" : " from " + sourceName)}, + Url{"https://link.twitch.tv/SharedChatViewer"}}); +} + } // namespace namespace chatterino { @@ -1156,6 +1167,14 @@ MessagePtr MessageBuilder::build() this->emplace(); } + if (this->sourceName.has_value()) + { + this->emplace( + makeSharedChatBadge(this->sourceName.value()), + MessageElementFlag::BadgeSharedChannel) + ->setLink({Link::UserInfo, sourceName.value()}); + } + this->appendTwitchBadges(); this->appendChatterinoBadges(); @@ -2242,16 +2261,23 @@ void MessageBuilder::parseRoomID() { this->message().flags.set(MessageFlag::SharedMessage); - auto sourceChan = - getApp()->getTwitch()->getChannelOrEmptyByID(sourceRoom); + auto *twitch = getApp()->getTwitch(); + auto sourceChan = twitch->getChannelOrEmptyByID(sourceRoom); if (sourceChan && !sourceChan->isEmpty()) { + this->sourceName = sourceChan->getName(); this->sourceChannel = dynamic_cast(sourceChan.get()); + // avoid duplicate pings this->message().flags.set( MessageFlag::DoNotTriggerNotification); } + else + { + this->sourceName = + twitch->getOrPopulateChannelCache(sourceRoom); + } } } } diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp index aa2933b64..18e0bb286 100644 --- a/src/messages/MessageBuilder.hpp +++ b/src/messages/MessageBuilder.hpp @@ -157,6 +157,7 @@ public: TwitchChannel *twitchChannel = nullptr; /// The Twitch Channel the message was sent in, according to the Shared Chat feature TwitchChannel *sourceChannel = nullptr; + std::optional sourceName = std::nullopt; Message *operator->(); Message &message(); diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 0e6c07b93..c19ed5c0c 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t { BitsStatic = (1LL << 11), BitsAnimated = (1LL << 12), + // Slot 0: Twitch + // - Shared Channel indicator badge + BadgeSharedChannel = (1LL << 37), + // Slot 1: Twitch // - Staff badge // - Admin badge @@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t { Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority | BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV | - BadgeFfz, + BadgeFfz | BadgeSharedChannel, ChannelName = (1LL << 20), diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 9ca93f6fd..f738125da 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -153,6 +153,7 @@ TwitchIrcServer::TwitchIrcServer() , liveChannel(new Channel("/live", Channel::Type::TwitchLive)) , automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod)) , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching) + , channelNamesById_(512) { // Initialize the connections // XXX: don't create write connection if there is no separate write connection. @@ -1128,6 +1129,38 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( return Channel::getEmpty(); } +std::optional TwitchIrcServer::getOrPopulateChannelCache( + const QString &channelId) +{ + { + const auto cache = this->channelNamesById_.access(); + if (cache->exists(channelId)) + { + return cache->get(channelId); + } + + // prevent multiple helix requests for single user + cache->put(channelId, ""); + } + + getHelix()->getUserById( + channelId, + [this](const HelixUser &user) { + const auto cache = this->channelNamesById_.access(); + cache->put(user.id, user.login); + }, + [this, &channelId] { + const auto cache = this->channelNamesById_.access(); + if (cache->exists(channelId) && cache->get(channelId).isEmpty()) + { + // invalidate cache so another helix request can be attempted + cache->remove(channelId); + } + }); + + return {}; +} + QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName) { if (dirtyChannelName.startsWith('#')) diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index fc3b888ef..532e0afe2 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -3,10 +3,12 @@ #include "common/Atomic.hpp" #include "common/Channel.hpp" #include "common/Common.hpp" +#include "common/UniqueAccess.hpp" #include "providers/irc/IrcConnection2.hpp" #include "util/RatelimitBucket.hpp" #include +#include #include #include @@ -43,6 +45,9 @@ public: virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0; virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0; + virtual std::optional getOrPopulateChannelCache( + const QString &channelId) = 0; + virtual void addFakeMessage(const QString &data) = 0; virtual void addGlobalSystemMessage(const QString &messageText) = 0; @@ -95,6 +100,9 @@ public: std::shared_ptr getChannelOrEmptyByID( const QString &channelID) override; + std::optional getOrPopulateChannelCache( + const QString &channelId) override; + void reloadAllBTTVChannelEmotes(); void reloadAllFFZChannelEmotes(); void reloadAllSevenTVChannelEmotes(); @@ -190,6 +198,9 @@ private: // https://dev.twitch.tv/docs/irc/guide#rate-limits QObjectPtr joinBucket_; + // cached channel id => name for resolving Shared Chat members + UniqueAccess> channelNamesById_; + QTimer reconnectTimer_; int falloffCounter_ = 1; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index f64eb47e7..88d5ec565 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask() flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic); // badges + flags.set(MEF::BadgeSharedChannel); flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority : MEF::None); flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions From 7d0c4738286fef7ec7dfd7ea277454953aae8ad9 Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Sat, 19 Oct 2024 03:48:56 -0700 Subject: [PATCH 2/8] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa0e5a5a..137a7126c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Major: Improve high-DPI support on Windows. (#4868, #5391) - Major: Added transparent overlay window (default keybind: CTRL + ALT + N). (#4746, #5643, #5659) - Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530) -- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625) +- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625, #5661) - Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530) - Minor: Add option to customise Moderation buttons with images. (#5369) - Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300) From c4faca50e0c8ffd54f5a125aff653c572690bed9 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 04:06:20 -0700 Subject: [PATCH 3/8] chore: update mocks --- mocks/include/mocks/TwitchIrcServer.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mocks/include/mocks/TwitchIrcServer.hpp b/mocks/include/mocks/TwitchIrcServer.hpp index d218192b3..8f8de58a8 100644 --- a/mocks/include/mocks/TwitchIrcServer.hpp +++ b/mocks/include/mocks/TwitchIrcServer.hpp @@ -26,6 +26,7 @@ public: , mentionsChannel(std::shared_ptr(new MockChannel("forsen3"))) , liveChannel(std::shared_ptr(new MockChannel("forsen"))) , automodChannel(std::shared_ptr(new MockChannel("forsen2"))) + , channelNamesById_(1) { } @@ -49,6 +50,14 @@ public: return {}; } + std::optional getOrPopulateChannelCache( + const QString &channelId) override + { + assert(false && + "unimplemented getOrPopulateChannelCache in mock irc server"); + return {}; + } + void addFakeMessage(const QString &data) override { } @@ -148,6 +157,7 @@ public: ChannelPtr mentionsChannel; ChannelPtr liveChannel; ChannelPtr automodChannel; + UniqueAccess> channelNamesById_; QString lastUserThatWhisperedMe{"forsen"}; std::unordered_map> mockChannels; From c64523f23cdd50c6fce62a4ef8e5df41e3eb84b9 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 16:12:25 -0700 Subject: [PATCH 4/8] chore: fix snapshot tests --- mocks/include/mocks/TwitchIrcServer.hpp | 8 ++++++-- .../shared-chat-announcement.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-emotes.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-known.json | 18 ++++++++++++++++++ .../IrcMessageHandler/shared-chat-unknown.json | 18 ++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/mocks/include/mocks/TwitchIrcServer.hpp b/mocks/include/mocks/TwitchIrcServer.hpp index 8f8de58a8..6b305e5ba 100644 --- a/mocks/include/mocks/TwitchIrcServer.hpp +++ b/mocks/include/mocks/TwitchIrcServer.hpp @@ -53,8 +53,12 @@ public: std::optional getOrPopulateChannelCache( const QString &channelId) override { - assert(false && - "unimplemented getOrPopulateChannelCache in mock irc server"); + if (channelId == "11148817") + return "pajlada"; + if (channelId == "141981764") + return "twitchdev"; + if (channelId == "1025594235") + return "shared_chat_test_01"; return {}; } diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json index 9c6fa2f65..4aa99b775 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-announcement.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "SharedChat_shared_chat_test_01", + "tooltip": "Shared Message from shared_chat_test_01" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "shared_chat_test_01" + }, + "tooltip": "Shared Message from shared_chat_test_01", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json index b7f9256d4..ffac89ba1 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-emotes.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "SharedChat_twitchdev", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "twitchdev" + }, + "tooltip": "Shared Message from twitchdev", + "trailingSpace": true, + "type": "BadgeElement" + }, { "color": "#ffff0000", "flags": "Username", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-known.json b/tests/snapshots/IrcMessageHandler/shared-chat-known.json index 6a13adcb4..c01c4a8ae 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-known.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-known.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "SharedChat_twitchdev", + "tooltip": "Shared Message from twitchdev" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "twitchdev" + }, + "tooltip": "Shared Message from twitchdev", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", diff --git a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json index accbb76b0..94914f72e 100644 --- a/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json +++ b/tests/snapshots/IrcMessageHandler/shared-chat-unknown.json @@ -64,6 +64,24 @@ "trailingSpace": true, "type": "TwitchModerationElement" }, + { + "emote": { + "homePage": "https://link.twitch.tv/SharedChatViewer", + "images": { + "1x": "" + }, + "name": "SharedChat_shared_chat_test_01", + "tooltip": "Shared Message from shared_chat_test_01" + }, + "flags": "BadgeSharedChannel", + "link": { + "type": "UserInfo", + "value": "shared_chat_test_01" + }, + "tooltip": "Shared Message from shared_chat_test_01", + "trailingSpace": true, + "type": "BadgeElement" + }, { "emote": { "homePage": "https://www.twitch.tv/jobs?ref=chat_badge", From 5638346d7c9c879675437e15b8f98f62ed01a6fe Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 17:27:09 -0700 Subject: [PATCH 5/8] chore: update shared chat icon --- resources/twitch/sharedChat.png | Bin 2006 -> 2407 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png index 116108c77032a75551cea742eae343bc3423c151..ec9f3f51269cdfdc1939cd30315c11396a960fe4 100644 GIT binary patch delta 2397 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 bRDkaQeg>(xo5g3A00000NkvXXu0mjfe7o_D From 68361054083226f45b1a3a1a48b0a402d4f85ed5 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sat, 19 Oct 2024 19:14:40 -0700 Subject: [PATCH 6/8] chore: crush png file --- resources/twitch/sharedChat.png | Bin 2407 -> 1026 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/twitch/sharedChat.png b/resources/twitch/sharedChat.png index ec9f3f51269cdfdc1939cd30315c11396a960fe4..f9a66b17c8f1307a811337d4095cfaf1d5133515 100644 GIT binary patch literal 1026 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAk#;Q*fy*NU|NG(!LX|Nr^(e@*uP zd$<4p{rms?ng1U@{9iif|L@=buU-Cs`TYM?i~qlU^Z)U~|I@nvSEm0zxaa@IRsSb6 z|9}4M|DnDA`x^h>xcYzj{QnbM|9}7X|M;Q*i)Q|xH}(Il{{MA(|DQei-(LCu#q<=zrK9*{>1C^clU0Xn)vbhy!sVAg*UfUI2;J^GsxzPd&a=P zbkWnrF(l&f+Z#8_RvCz}J>ckI4mh6Gp1eq8rcA-QsO;bW{7YvAziCqwl*&8j`oBAl z;kEY6ZQZ^9?nM4g+q+Op)#OKEfM=le*R<1Bdz{PpV=u4BdMY%jF<|AI&}yKVL)iAq z*Rn-j#e%dR$gQXeipB1Fb4wiI2 z)>(dif#tKbx}Ez~3nu2QcM!HS*|~?m)L~YaVmrU($G6wNJY6cGRMTu|=CWKQEhf`u($Zu_j(ksaa11KN_h^Z&GV=o?ZM?@%@Y-&FVjXfAll)^oi9wdK|>goDW=gNNsxji~kMF<|(h3uuOo{TaS6teGKZnoa~OJoN)-YzWg&zh?9*|LBGHGu!>011cstd z7EdO$@Uf^g+_5=l@$$e`5tTVg`cFGl8s@Q{yFH<>LdZdwQRMIJ?w$$E4%%nV&s^u1 z=G3rk#(d$s`ZE<7I*)yGDlbU;!&35OowTjU;fv2t);@Yte7&cQq4VFy>Lo%p^SJJK zI2~3Euc}K_xyIP>?9VjsB-v$7^Y-YR^mnqe@L74c%_h^RTXkDcgRw__*SV&Nv%^kk zG&CBdvfN`gls~>aVLzvA((NOh=D#0&F;%tW>_5rgulTY3gV~p2*(FT#Ute&T;CII% zBV%e((49*^vNnknYTa7fH_LYKGA#v9%d6GQTYZ?NqBi;M%~e{R`fb5cHO8qcFTY&u zzFO4$?}U`}H{bT%eHpdo^>xE9O0o8L4SrvH93SN4boPM9;wQjdz~JfX=d#Wzp$Pys C^A0Qk literal 2407 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 Z{}0aneoo8>n!W%4002ovPDHLkV1mX Date: Sun, 20 Oct 2024 21:40:32 -0700 Subject: [PATCH 7/8] docs: explain getOrPopulateChannelCache --- src/messages/MessageBuilder.cpp | 2 +- src/providers/twitch/TwitchIrcServer.hpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index efc885a49..35b360b00 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2759,7 +2759,7 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (this->message().flags.has(MessageFlag::SharedMessage)) { - QString sourceId = tags["source-room-id"].toString(); + const QString sourceId = tags["source-room-id"].toString(); std::optional sourceName; if (twitchChannel->roomId() == sourceId) { diff --git a/src/providers/twitch/TwitchIrcServer.hpp b/src/providers/twitch/TwitchIrcServer.hpp index 532e0afe2..a590a41f8 100644 --- a/src/providers/twitch/TwitchIrcServer.hpp +++ b/src/providers/twitch/TwitchIrcServer.hpp @@ -100,6 +100,11 @@ public: std::shared_ptr getChannelOrEmptyByID( const QString &channelID) override; + /** + * Obtains the channel login name associated with the passed ID, + * so that Shared Chat messages can provide source channel context. + * Can yield an empty string if a helix request is already in-flight. + */ std::optional getOrPopulateChannelCache( const QString &channelId) override; From 021f1a0bfc7c77c18aaa7f65a62f7263b7db6893 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Sun, 20 Oct 2024 21:48:05 -0700 Subject: [PATCH 8/8] fix: skip UserInfo link if name is empty --- src/messages/MessageBuilder.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 35b360b00..68e304bf3 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -2773,9 +2773,14 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags, if (sourceName.has_value()) { - this->emplace(makeSharedChatBadge(sourceName.value()), - MessageElementFlag::BadgeSharedChannel) - ->setLink({Link::UserInfo, sourceName.value()}); + const auto &name = sourceName.value(); + auto *badge = this->emplace( + makeSharedChatBadge(name), + MessageElementFlag::BadgeSharedChannel); + if (!name.isEmpty()) + { + badge->setLink({Link::UserInfo, name}); + } } }