From ed7d1a88d0ce921ff12cbd3d64118f0778b81782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Sat, 17 Apr 2021 14:42:30 +0200 Subject: [PATCH] Added custom FrankerFaceZ VIP badges (#2628) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/messages/MessageElement.cpp | 16 ++++++ src/messages/MessageElement.hpp | 11 ++++ src/providers/ffz/FfzEmotes.cpp | 52 +++++++++++-------- src/providers/ffz/FfzEmotes.hpp | 1 + src/providers/twitch/TwitchChannel.cpp | 11 ++++ src/providers/twitch/TwitchChannel.hpp | 2 + src/providers/twitch/TwitchMessageBuilder.cpp | 12 +++++ 8 files changed, 85 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4d0a3057..cbf2d32af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Major: Added custom FrankerFaceZ VIP Badges. (#2628) - Minor: Added `in:` search filter to find messages sent in specific channels. (#2299, #2634) - Minor: Allow for built-in Chatterino commands to be used in custom commands. (#2632) - Bugfix: Fix crash that could occur when the user changed the "Custom stream player URI Scheme" setting if the user had closed down and splits in the application runtime. (#2592) diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 1f5fa0a3d..f371fe500 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -253,6 +253,22 @@ MessageLayoutElement *ModBadgeElement::makeImageLayoutElement( return element; } +// VIP BADGE +VipBadgeElement::VipBadgeElement(const EmotePtr &data, + MessageElementFlags flags_) + : BadgeElement(data, flags_) +{ +} + +MessageLayoutElement *VipBadgeElement::makeImageLayoutElement( + const ImagePtr &image, const QSize &size) +{ + auto element = + (new ImageLayoutElement(*this, image, size))->setLink(this->getLink()); + + return element; +} + // FFZ Badge FfzBadgeElement::FfzBadgeElement(const EmotePtr &data, MessageElementFlags flags_, QColor &color) diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 876c19473..48f6cdf4b 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -60,6 +60,7 @@ enum class MessageElementFlag : int64_t { BadgeGlobalAuthority = (1LL << 14), // Slot 2: Twitch + // - VIP badge // - Moderator badge // - Broadcaster badge BadgeChannelAuthority = (1LL << 15), @@ -275,6 +276,16 @@ protected: const QSize &size) override; }; +class VipBadgeElement : public BadgeElement +{ +public: + VipBadgeElement(const EmotePtr &data, MessageElementFlags flags_); + +protected: + MessageLayoutElement *makeImageLayoutElement(const ImagePtr &image, + const QSize &size) override; +}; + class FfzBadgeElement : public BadgeElement { public: diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index f032ab44f..467c1c584 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -86,34 +86,36 @@ namespace { return {Success, std::move(emotes)}; } - boost::optional parseModBadge(const QJsonObject &jsonRoot) + boost::optional parseAuthorityBadge(const QJsonObject &badgeUrls, + const QString tooltip) { - boost::optional modBadge; + boost::optional authorityBadge; - auto room = jsonRoot.value("room").toObject(); - auto modUrls = room.value("mod_urls").toObject(); - if (!modUrls.isEmpty()) + qDebug() << badgeUrls; + if (!badgeUrls.isEmpty()) { - auto modBadge1x = getEmoteLink(modUrls, "1"); - auto modBadge2x = getEmoteLink(modUrls, "2"); - auto modBadge3x = getEmoteLink(modUrls, "4"); + auto authorityBadge1x = getEmoteLink(badgeUrls, "1"); + auto authorityBadge2x = getEmoteLink(badgeUrls, "2"); + auto authorityBadge3x = getEmoteLink(badgeUrls, "4"); - auto modBadgeImageSet = ImageSet{ - Image::fromUrl(modBadge1x, 1), - modBadge2x.string.isEmpty() ? Image::getEmpty() - : Image::fromUrl(modBadge2x, 0.5), - modBadge3x.string.isEmpty() ? Image::getEmpty() - : Image::fromUrl(modBadge3x, 0.25), + auto authorityBadgeImageSet = ImageSet{ + Image::fromUrl(authorityBadge1x, 1), + authorityBadge2x.string.isEmpty() + ? Image::getEmpty() + : Image::fromUrl(authorityBadge2x, 0.5), + authorityBadge3x.string.isEmpty() + ? Image::getEmpty() + : Image::fromUrl(authorityBadge3x, 0.25), }; - modBadge = std::make_shared(Emote{ + authorityBadge = std::make_shared(Emote{ {""}, - modBadgeImageSet, - Tooltip{"Moderator"}, - modBadge1x, + authorityBadgeImageSet, + Tooltip{tooltip}, + authorityBadge1x, }); } - return modBadge; + return authorityBadge; } EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot) @@ -199,6 +201,7 @@ void FfzEmotes::loadChannel( std::weak_ptr channel, const QString &channelId, std::function emoteCallback, std::function)> modBadgeCallback, + std::function)> vipBadgeCallback, bool manualRefresh) { qCDebug(chatterinoFfzemotes) @@ -208,16 +211,23 @@ void FfzEmotes::loadChannel( .timeout(20000) .onSuccess([emoteCallback = std::move(emoteCallback), - modBadgeCallback = std::move(modBadgeCallback), channel, + modBadgeCallback = std::move(modBadgeCallback), + vipBadgeCallback = std::move(vipBadgeCallback), channel, manualRefresh](auto result) -> Outcome { auto json = result.parseJson(); auto emoteMap = parseChannelEmotes(json); - auto modBadge = parseModBadge(json); + auto modBadge = parseAuthorityBadge( + json.value("room").toObject().value("mod_urls").toObject(), + "Moderator"); + auto vipBadge = parseAuthorityBadge( + json.value("room").toObject().value("vip_badge").toObject(), + "VIP"); bool hasEmotes = !emoteMap.empty(); emoteCallback(std::move(emoteMap)); modBadgeCallback(std::move(modBadge)); + vipBadgeCallback(std::move(vipBadge)); if (auto shared = channel.lock(); manualRefresh) { if (hasEmotes) diff --git a/src/providers/ffz/FfzEmotes.hpp b/src/providers/ffz/FfzEmotes.hpp index 817341c83..123777670 100644 --- a/src/providers/ffz/FfzEmotes.hpp +++ b/src/providers/ffz/FfzEmotes.hpp @@ -27,6 +27,7 @@ public: std::weak_ptr channel, const QString &channelId, std::function emoteCallback, std::function)> modBadgeCallback, + std::function)> vipBadgeCallback, bool manualRefresh); private: diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 91670206e..2b55eb32d 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -269,6 +269,12 @@ void TwitchChannel::refreshFFZChannelEmotes(bool manualRefresh) this->ffzCustomModBadge_.set(std::move(modBadge)); } }, + [this, weak = weakOf(this)](auto &&vipBadge) { + if (auto shared = weak.lock()) + { + this->ffzCustomVipBadge_.set(std::move(vipBadge)); + } + }, manualRefresh); } @@ -1067,6 +1073,11 @@ boost::optional TwitchChannel::ffzCustomModBadge() const return this->ffzCustomModBadge_.get(); } +boost::optional TwitchChannel::ffzCustomVipBadge() const +{ + return this->ffzCustomVipBadge_.get(); +} + boost::optional TwitchChannel::cheerEmote(const QString &string) { auto sets = this->cheerEmoteSets_.access(); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index a6f74f47b..365a07467 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -100,6 +100,7 @@ public: // Badges boost::optional ffzCustomModBadge() const; + boost::optional ffzCustomVipBadge() const; boost::optional twitchBadge(const QString &set, const QString &version) const; @@ -171,6 +172,7 @@ protected: Atomic> bttvEmotes_; Atomic> ffzEmotes_; Atomic> ffzCustomModBadge_; + Atomic> ffzCustomVipBadge_; private: // Badges diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index b47e95034..d3108bd04 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -1128,6 +1128,18 @@ void TwitchMessageBuilder::appendTwitchBadges() continue; } } + else if (badge.key_ == "vip") + { + if (auto customVipBadge = this->twitchChannel->ffzCustomVipBadge()) + { + this->emplace( + customVipBadge.get(), + MessageElementFlag::BadgeChannelAuthority) + ->setTooltip((*customVipBadge)->tooltip.string); + // early out, since we have to add a custom badge element here + continue; + } + } else if (badge.flag_ == MessageElementFlag::BadgeSubscription) { auto badgeInfoIt = badgeInfos.find(badge.key_);