Added custom FrankerFaceZ VIP badges (#2628)

Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
Paweł 2021-04-17 14:42:30 +02:00 committed by GitHub
parent 2f906c5504
commit ed7d1a88d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 21 deletions

View file

@ -2,6 +2,7 @@
## Unversioned
- Major: Added custom FrankerFaceZ VIP Badges. (#2628)
- Minor: Added `in:<channels>` 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)

View file

@ -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)

View file

@ -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:

View file

@ -86,34 +86,36 @@ namespace {
return {Success, std::move(emotes)};
}
boost::optional<EmotePtr> parseModBadge(const QJsonObject &jsonRoot)
boost::optional<EmotePtr> parseAuthorityBadge(const QJsonObject &badgeUrls,
const QString tooltip)
{
boost::optional<EmotePtr> modBadge;
boost::optional<EmotePtr> 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>(Emote{
authorityBadge = std::make_shared<Emote>(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> channel, const QString &channelId,
std::function<void(EmoteMap &&)> emoteCallback,
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback,
std::function<void(boost::optional<EmotePtr>)> 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)

View file

@ -27,6 +27,7 @@ public:
std::weak_ptr<Channel> channel, const QString &channelId,
std::function<void(EmoteMap &&)> emoteCallback,
std::function<void(boost::optional<EmotePtr>)> modBadgeCallback,
std::function<void(boost::optional<EmotePtr>)> vipBadgeCallback,
bool manualRefresh);
private:

View file

@ -269,6 +269,12 @@ void TwitchChannel::refreshFFZChannelEmotes(bool manualRefresh)
this->ffzCustomModBadge_.set(std::move(modBadge));
}
},
[this, weak = weakOf<Channel>(this)](auto &&vipBadge) {
if (auto shared = weak.lock())
{
this->ffzCustomVipBadge_.set(std::move(vipBadge));
}
},
manualRefresh);
}
@ -1067,6 +1073,11 @@ boost::optional<EmotePtr> TwitchChannel::ffzCustomModBadge() const
return this->ffzCustomModBadge_.get();
}
boost::optional<EmotePtr> TwitchChannel::ffzCustomVipBadge() const
{
return this->ffzCustomVipBadge_.get();
}
boost::optional<CheerEmote> TwitchChannel::cheerEmote(const QString &string)
{
auto sets = this->cheerEmoteSets_.access();

View file

@ -100,6 +100,7 @@ public:
// Badges
boost::optional<EmotePtr> ffzCustomModBadge() const;
boost::optional<EmotePtr> ffzCustomVipBadge() const;
boost::optional<EmotePtr> twitchBadge(const QString &set,
const QString &version) const;
@ -171,6 +172,7 @@ protected:
Atomic<std::shared_ptr<const EmoteMap>> bttvEmotes_;
Atomic<std::shared_ptr<const EmoteMap>> ffzEmotes_;
Atomic<boost::optional<EmotePtr>> ffzCustomModBadge_;
Atomic<boost::optional<EmotePtr>> ffzCustomVipBadge_;
private:
// Badges

View file

@ -1128,6 +1128,18 @@ void TwitchMessageBuilder::appendTwitchBadges()
continue;
}
}
else if (badge.key_ == "vip")
{
if (auto customVipBadge = this->twitchChannel->ffzCustomVipBadge())
{
this->emplace<VipBadgeElement>(
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_);