From bcc53c9aa7966baa9051feda0aaee6458d8ab73c Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sun, 1 Dec 2019 13:32:41 +0100 Subject: [PATCH] Clean up Twitch badge appending code --- chatterino.pro | 2 + src/providers/twitch/TwitchBadge.cpp | 31 ++ src/providers/twitch/TwitchBadge.hpp | 21 ++ src/providers/twitch/TwitchMessageBuilder.cpp | 283 ++++++------------ src/providers/twitch/TwitchMessageBuilder.hpp | 2 + src/widgets/Window.cpp | 1 + 6 files changed, 151 insertions(+), 189 deletions(-) create mode 100644 src/providers/twitch/TwitchBadge.cpp create mode 100644 src/providers/twitch/TwitchBadge.hpp diff --git a/chatterino.pro b/chatterino.pro index 555f3586e..cc692b864 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -173,6 +173,7 @@ SOURCES += \ src/providers/twitch/TwitchAccount.cpp \ src/providers/twitch/TwitchAccountManager.cpp \ src/providers/twitch/TwitchApi.cpp \ + src/providers/twitch/TwitchBadge.cpp \ src/providers/twitch/TwitchBadges.cpp \ src/providers/twitch/TwitchChannel.cpp \ src/providers/twitch/TwitchEmotes.cpp \ @@ -372,6 +373,7 @@ HEADERS += \ src/providers/twitch/TwitchAccount.hpp \ src/providers/twitch/TwitchAccountManager.hpp \ src/providers/twitch/TwitchApi.hpp \ + src/providers/twitch/TwitchBadge.hpp \ src/providers/twitch/TwitchBadges.hpp \ src/providers/twitch/TwitchChannel.hpp \ src/providers/twitch/TwitchCommon.hpp \ diff --git a/src/providers/twitch/TwitchBadge.cpp b/src/providers/twitch/TwitchBadge.cpp new file mode 100644 index 000000000..699e4b87d --- /dev/null +++ b/src/providers/twitch/TwitchBadge.cpp @@ -0,0 +1,31 @@ +#include "providers/twitch/TwitchBadge.hpp" + +#include + +namespace chatterino { + +// set of badge IDs that should be given specific flags. +// vanity flag is left out on purpose as it is our default flag +const QSet globalAuthority{"staff", "admin", "global_mod"}; +const QSet channelAuthority{"moderator", "vip", "broadcaster"}; +const QSet subBadges{"subscriber", "founder"}; + +Badge::Badge(QString key, QString value) + : key_(std::move(key)) + , value_(std::move(value)) +{ + if (globalAuthority.contains(this->key_)) + { + this->flag_ = MessageElementFlag::BadgeGlobalAuthority; + } + else if (channelAuthority.contains(this->key_)) + { + this->flag_ = MessageElementFlag::BadgeChannelAuthority; + } + else if (subBadges.contains(this->key_)) + { + this->flag_ = MessageElementFlag::BadgeSubscription; + } +} + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchBadge.hpp b/src/providers/twitch/TwitchBadge.hpp new file mode 100644 index 000000000..764be2e71 --- /dev/null +++ b/src/providers/twitch/TwitchBadge.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "messages/MessageElement.hpp" + +#include + +namespace chatterino { + +class Badge +{ +public: + Badge(QString key, QString value); + + QString key_; // e.g. bits + QString value_; // e.g. 100 + QString extraValue_{}; // e.g. 5 (the number of months subscribed) + MessageElementFlag flag_{ + MessageElementFlag::BadgeVanity}; // badge slot it takes up +}; + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index ff30522ee..ab65d84cb 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -65,6 +65,59 @@ QColor getRandomColor(const QVariant &userId) namespace chatterino { +namespace { + + QStringList parseTagList(const QVariantMap &tags, const QString &key) + { + auto iterator = tags.find(key); + if (iterator == tags.end()) + return QStringList{}; + + return iterator.value().toString().split( + ',', QString::SplitBehavior::SkipEmptyParts); + } + + std::map parseBadgeInfos(const QVariantMap &tags) + { + std::map badgeInfos; + + for (QString badgeInfo : parseTagList(tags, "badge-info")) + { + QStringList parts = badgeInfo.split('/'); + if (parts.size() != 2) + { + log("Skipping badge-info because it split weird: {}", + badgeInfo); + continue; + } + + badgeInfos.emplace(parts[0], parts[1]); + } + + return badgeInfos; + } + + std::vector parseBadges(const QVariantMap &tags) + { + std::vector badges; + + for (QString badge : parseTagList(tags, "badges")) + { + QStringList parts = badge.split('/'); + if (parts.size() != 2) + { + log("Skipping badge because it split weird: {}", badge); + continue; + } + + badges.emplace_back(parts[0], parts[1]); + } + + return badges; + } + +} // namespace + TwitchMessageBuilder::TwitchMessageBuilder( Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage, const MessageParseArgs &_args) @@ -1120,7 +1173,24 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name) return Failure; } -// fourtf: this is ugly +boost::optional TwitchMessageBuilder::getTwitchBadge( + const Badge &badge) +{ + if (auto channelBadge = + this->twitchChannel->twitchBadge(badge.key_, badge.value_)) + { + return channelBadge; + } + + if (auto globalBadge = this->twitchChannel->globalTwitchBadges().badge( + badge.key_, badge.value_)) + { + return globalBadge; + } + + return boost::none; +} + void TwitchMessageBuilder::appendTwitchBadges() { if (this->twitchChannel == nullptr) @@ -1128,66 +1198,25 @@ void TwitchMessageBuilder::appendTwitchBadges() return; } - auto iterator = this->tags.find("badges"); - if (iterator == this->tags.end()) - return; + auto badgeInfos = parseBadgeInfos(this->tags); + auto badges = parseBadges(this->tags); - for (QString badge : iterator.value().toString().split(',')) + for (const auto &badge : badges) { - if (badge.startsWith("bits/")) + auto badgeEmote = this->getTwitchBadge(badge); + if (!badgeEmote) { - QString cheerAmount = badge.mid(5); - QString tooltip = QString("Twitch cheer ") + cheerAmount; + log("No channel/global variant found {}", badge.key_); + continue; + } + auto tooltip = (*badgeEmote)->tooltip.string; - // Try to fetch channel-specific bit badge - try - { - if (twitchChannel) - if (const auto &_badge = this->twitchChannel->twitchBadge( - "bits", cheerAmount)) - { - this->emplace( - _badge.get(), MessageElementFlag::BadgeVanity) - ->setTooltip(tooltip); - continue; - } - } - catch (const std::out_of_range &) - { - // Channel does not contain a special bit badge for this version - } - - // Use default bit badge - if (auto _badge = this->twitchChannel->globalTwitchBadges().badge( - "bits", cheerAmount)) - { - this->emplace(_badge.get(), - MessageElementFlag::BadgeVanity) - ->setTooltip(tooltip); - } - } - else if (badge == "staff/1") + if (badge.key_ == "bits") { - this->emplace( - Image::fromPixmap(getResources().twitch.staff), - MessageElementFlag::BadgeGlobalAuthority) - ->setTooltip("Staff"); + const auto &cheerAmount = badge.value_; + tooltip = QString("Twitch cheer %0").arg(cheerAmount); } - else if (badge == "admin/1") - { - this->emplace( - Image::fromPixmap(getResources().twitch.admin), - MessageElementFlag::BadgeGlobalAuthority) - ->setTooltip("Admin"); - } - else if (badge == "global_mod/1") - { - this->emplace( - Image::fromPixmap(getResources().twitch.globalmod), - MessageElementFlag::BadgeGlobalAuthority) - ->setTooltip("Global Moderator"); - } - else if (badge == "moderator/1") + else if (badge.key_ == "moderator") { if (auto customModBadge = this->twitchChannel->ffzCustomModBadge()) { @@ -1195,149 +1224,24 @@ void TwitchMessageBuilder::appendTwitchBadges() customModBadge.get(), MessageElementFlag::BadgeChannelAuthority) ->setTooltip((*customModBadge)->tooltip.string); + // early out, since we have to add a custom badge element here continue; } - this->emplace( - Image::fromPixmap(getResources().twitch.moderator), - MessageElementFlag::BadgeChannelAuthority) - ->setTooltip("Moderator"); } - else if (badge == "vip/1") + else if (badge.flag_ == MessageElementFlag::BadgeSubscription) { - this->emplace( - Image::fromPixmap(getResources().twitch.vip), - MessageElementFlag::BadgeChannelAuthority) - ->setTooltip("VIP"); - } - else if (badge == "broadcaster/1") - { - this->emplace( - Image::fromPixmap(getResources().twitch.broadcaster), - MessageElementFlag::BadgeChannelAuthority) - ->setTooltip("Broadcaster"); - } - else if (badge == "turbo/1") - { - this->emplace( - Image::fromPixmap(getResources().twitch.turbo), - MessageElementFlag::BadgeVanity) - ->setTooltip("Twitch Turbo"); - } - else if (badge == "premium/1") - { - this->emplace( - Image::fromPixmap(getResources().twitch.prime), - MessageElementFlag::BadgeVanity) - ->setTooltip("Twitch Prime"); - } - else if (badge.startsWith("partner/")) - { - int index = badge.midRef(8).toInt(); - switch (index) + auto badgeInfoIt = badgeInfos.find(badge.key_); + if (badgeInfoIt != badgeInfos.end()) { - case 1: { - this->emplace( - Image::fromPixmap(getResources().twitch.verified, - 0.25), - MessageElementFlag::BadgeVanity) - ->setTooltip("Verified"); - } - break; - default: { - printf("[TwitchMessageBuilder] Unhandled partner badge " - "index: %d\n", - index); - } - break; + const auto &subMonths = badgeInfoIt->second; + tooltip += QString(" (%0 months)").arg(subMonths); } } - else if (badge.startsWith("founder/")) - { - if (auto badgeEmote = - this->twitchChannel->globalTwitchBadges().badge("founder", - "0")) - { - auto badgeInfo = this->tags.find("badge-info"); - if (badgeInfo != this->tags.end() && - badgeInfo.value().toString().split(',')[0].startsWith( - "founder/")) - { - auto subMonths = - badgeInfo.value().toString().split(',')[0].mid(8); - this->emplace( - badgeEmote.get(), - MessageElementFlag::BadgeSubscription) - ->setTooltip(QString((*badgeEmote)->tooltip.string) + - " (" + subMonths + " months)"); - } - else - { - this->emplace( - badgeEmote.get(), - MessageElementFlag::BadgeSubscription) - ->setTooltip((*badgeEmote)->tooltip.string); - } - } - } - else if (badge.startsWith("subscriber/")) - { - if (auto badgeEmote = this->twitchChannel->twitchBadge( - "subscriber", badge.mid(11))) - { - auto badgeInfo = this->tags.find("badge-info"); - if (badgeInfo != this->tags.end() && - badgeInfo.value().toString().split(',')[0].startsWith( - "subscriber/")) - { - auto subMonths = - badgeInfo.value().toString().split(',')[0].mid(11); - this->emplace( - badgeEmote.get(), - MessageElementFlag::BadgeSubscription) - ->setTooltip(QString((*badgeEmote)->tooltip.string) + - " (" + subMonths + " months)"); - } - else - { - this->emplace( - badgeEmote.get(), - MessageElementFlag::BadgeSubscription) - ->setTooltip((*badgeEmote)->tooltip.string); - } - continue; - } - // use default subscriber badge if custom one not found - this->emplace( - Image::fromPixmap(getResources().twitch.subscriber, 0.25), - MessageElementFlag::BadgeSubscription) - ->setTooltip("Twitch Subscriber"); - } - else - { - auto splits = badge.split('/'); - if (splits.size() != 2) - continue; - - if (auto badgeEmote = - this->twitchChannel->twitchBadge(splits[0], splits[1])) - { - this->emplace(badgeEmote.get(), - MessageElementFlag::BadgeVanity) - ->setTooltip((*badgeEmote)->tooltip.string); - continue; - } - if (auto _badge = this->twitchChannel->globalTwitchBadges().badge( - splits[0], splits[1])) - { - this->emplace(_badge.get(), - MessageElementFlag::BadgeVanity) - ->setTooltip((*_badge)->tooltip.string); - continue; - } - } + this->emplace(badgeEmote.get(), badge.flag_) + ->setTooltip(tooltip); } -} // namespace chatterino +} void TwitchMessageBuilder::appendChatterinoBadges() { @@ -1373,4 +1277,5 @@ Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string) } return Success; } + } // namespace chatterino diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index c43c14195..df06a2522 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -3,6 +3,7 @@ #include "common/Aliases.hpp" #include "common/Outcome.hpp" #include "messages/MessageBuilder.hpp" +#include "providers/twitch/TwitchBadge.hpp" #include #include @@ -60,6 +61,7 @@ private: // parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function void parseHighlights(); + boost::optional getTwitchBadge(const Badge &badge); void appendTwitchEmote( const QString &emote, std::vector> &vec, diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index 030ba8351..8be758d29 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -230,6 +230,7 @@ void Window::addDebugStuff() // display name renders strangely miscMessages.emplace_back(R"(@badges=;color=#00AD2B;display-name=Iamme420\s;emotes=;id=d47a1e4b-a3c6-4b9e-9bf1-51b8f3dbc76e;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1529670347537;turbo=0;user-id=56422869;user-type= :iamme420!iamme420@iamme420.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); + miscMessages.emplace_back(R"(@badge-info=founder/47;badges=moderator/1,founder/0,premium/1;color=#00FF80;display-name=gempir;emotes=;flags=;id=d4514490-202e-43cb-b429-ef01a9d9c2fe;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1575198233854;turbo=0;user-id=77829817;user-type=mod :gempir!gempir@gempir.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); // clang-format on createWindowShortcut(this, "F6", [=] {