Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added custom FrankerFaceZ VIP badges #2628

Merged
merged 6 commits into from
Apr 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions src/messages/MessageElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions src/messages/MessageElement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum class MessageElementFlag : int64_t {
BadgeGlobalAuthority = (1LL << 14),

// Slot 2: Twitch
// - VIP badge
// - Moderator badge
// - Broadcaster badge
BadgeChannelAuthority = (1LL << 15),
Expand Down Expand Up @@ -275,6 +276,16 @@ class ModBadgeElement : public BadgeElement
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:
Expand Down
54 changes: 32 additions & 22 deletions src/providers/ffz/FfzEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 authorityBadge1x = getEmoteLink(badgeUrls, "1");
auto authorityBadge2x = getEmoteLink(badgeUrls, "2");
auto authorityBadge3x = getEmoteLink(badgeUrls, "4");

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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/providers/ffz/FfzEmotes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class FfzEmotes final
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:
Expand Down
11 changes: 11 additions & 0 deletions src/providers/twitch/TwitchChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions src/providers/twitch/TwitchChannel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class TwitchChannel : public Channel,

// Badges
boost::optional<EmotePtr> ffzCustomModBadge() const;
boost::optional<EmotePtr> ffzCustomVipBadge() const;
boost::optional<EmotePtr> twitchBadge(const QString &set,
const QString &version) const;

Expand Down Expand Up @@ -171,6 +172,7 @@ class TwitchChannel : public Channel,
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
Expand Down
12 changes: 12 additions & 0 deletions src/providers/twitch/TwitchMessageBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand Down