mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Clean up Twitch badge appending code
This commit is contained in:
parent
c00f97ac53
commit
bcc53c9aa7
|
@ -173,6 +173,7 @@ SOURCES += \
|
||||||
src/providers/twitch/TwitchAccount.cpp \
|
src/providers/twitch/TwitchAccount.cpp \
|
||||||
src/providers/twitch/TwitchAccountManager.cpp \
|
src/providers/twitch/TwitchAccountManager.cpp \
|
||||||
src/providers/twitch/TwitchApi.cpp \
|
src/providers/twitch/TwitchApi.cpp \
|
||||||
|
src/providers/twitch/TwitchBadge.cpp \
|
||||||
src/providers/twitch/TwitchBadges.cpp \
|
src/providers/twitch/TwitchBadges.cpp \
|
||||||
src/providers/twitch/TwitchChannel.cpp \
|
src/providers/twitch/TwitchChannel.cpp \
|
||||||
src/providers/twitch/TwitchEmotes.cpp \
|
src/providers/twitch/TwitchEmotes.cpp \
|
||||||
|
@ -372,6 +373,7 @@ HEADERS += \
|
||||||
src/providers/twitch/TwitchAccount.hpp \
|
src/providers/twitch/TwitchAccount.hpp \
|
||||||
src/providers/twitch/TwitchAccountManager.hpp \
|
src/providers/twitch/TwitchAccountManager.hpp \
|
||||||
src/providers/twitch/TwitchApi.hpp \
|
src/providers/twitch/TwitchApi.hpp \
|
||||||
|
src/providers/twitch/TwitchBadge.hpp \
|
||||||
src/providers/twitch/TwitchBadges.hpp \
|
src/providers/twitch/TwitchBadges.hpp \
|
||||||
src/providers/twitch/TwitchChannel.hpp \
|
src/providers/twitch/TwitchChannel.hpp \
|
||||||
src/providers/twitch/TwitchCommon.hpp \
|
src/providers/twitch/TwitchCommon.hpp \
|
||||||
|
|
31
src/providers/twitch/TwitchBadge.cpp
Normal file
31
src/providers/twitch/TwitchBadge.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "providers/twitch/TwitchBadge.hpp"
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
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<QString> globalAuthority{"staff", "admin", "global_mod"};
|
||||||
|
const QSet<QString> channelAuthority{"moderator", "vip", "broadcaster"};
|
||||||
|
const QSet<QString> 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
|
21
src/providers/twitch/TwitchBadge.hpp
Normal file
21
src/providers/twitch/TwitchBadge.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "messages/MessageElement.hpp"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
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
|
|
@ -65,6 +65,59 @@ QColor getRandomColor(const QVariant &userId)
|
||||||
|
|
||||||
namespace chatterino {
|
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<QString, QString> parseBadgeInfos(const QVariantMap &tags)
|
||||||
|
{
|
||||||
|
std::map<QString, QString> 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<Badge> parseBadges(const QVariantMap &tags)
|
||||||
|
{
|
||||||
|
std::vector<Badge> 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(
|
TwitchMessageBuilder::TwitchMessageBuilder(
|
||||||
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
||||||
const MessageParseArgs &_args)
|
const MessageParseArgs &_args)
|
||||||
|
@ -1120,7 +1173,24 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fourtf: this is ugly
|
boost::optional<EmotePtr> 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()
|
void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
{
|
{
|
||||||
if (this->twitchChannel == nullptr)
|
if (this->twitchChannel == nullptr)
|
||||||
|
@ -1128,66 +1198,25 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iterator = this->tags.find("badges");
|
auto badgeInfos = parseBadgeInfos(this->tags);
|
||||||
if (iterator == this->tags.end())
|
auto badges = parseBadges(this->tags);
|
||||||
return;
|
|
||||||
|
|
||||||
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);
|
log("No channel/global variant found {}", badge.key_);
|
||||||
QString tooltip = QString("Twitch cheer ") + cheerAmount;
|
|
||||||
|
|
||||||
// Try to fetch channel-specific bit badge
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (twitchChannel)
|
|
||||||
if (const auto &_badge = this->twitchChannel->twitchBadge(
|
|
||||||
"bits", cheerAmount))
|
|
||||||
{
|
|
||||||
this->emplace<BadgeElement>(
|
|
||||||
_badge.get(), MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip(tooltip);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
auto tooltip = (*badgeEmote)->tooltip.string;
|
||||||
catch (const std::out_of_range &)
|
|
||||||
{
|
|
||||||
// Channel does not contain a special bit badge for this version
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use default bit badge
|
if (badge.key_ == "bits")
|
||||||
if (auto _badge = this->twitchChannel->globalTwitchBadges().badge(
|
|
||||||
"bits", cheerAmount))
|
|
||||||
{
|
{
|
||||||
this->emplace<BadgeElement>(_badge.get(),
|
const auto &cheerAmount = badge.value_;
|
||||||
MessageElementFlag::BadgeVanity)
|
tooltip = QString("Twitch cheer %0").arg(cheerAmount);
|
||||||
->setTooltip(tooltip);
|
|
||||||
}
|
}
|
||||||
}
|
else if (badge.key_ == "moderator")
|
||||||
else if (badge == "staff/1")
|
|
||||||
{
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.staff),
|
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Staff");
|
|
||||||
}
|
|
||||||
else if (badge == "admin/1")
|
|
||||||
{
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.admin),
|
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Admin");
|
|
||||||
}
|
|
||||||
else if (badge == "global_mod/1")
|
|
||||||
{
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.globalmod),
|
|
||||||
MessageElementFlag::BadgeGlobalAuthority)
|
|
||||||
->setTooltip("Global Moderator");
|
|
||||||
}
|
|
||||||
else if (badge == "moderator/1")
|
|
||||||
{
|
{
|
||||||
if (auto customModBadge = this->twitchChannel->ffzCustomModBadge())
|
if (auto customModBadge = this->twitchChannel->ffzCustomModBadge())
|
||||||
{
|
{
|
||||||
|
@ -1195,149 +1224,24 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
||||||
customModBadge.get(),
|
customModBadge.get(),
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
MessageElementFlag::BadgeChannelAuthority)
|
||||||
->setTooltip((*customModBadge)->tooltip.string);
|
->setTooltip((*customModBadge)->tooltip.string);
|
||||||
|
// early out, since we have to add a custom badge element here
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.moderator),
|
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
|
||||||
->setTooltip("Moderator");
|
|
||||||
}
|
}
|
||||||
else if (badge == "vip/1")
|
else if (badge.flag_ == MessageElementFlag::BadgeSubscription)
|
||||||
{
|
{
|
||||||
this->emplace<ImageElement>(
|
auto badgeInfoIt = badgeInfos.find(badge.key_);
|
||||||
Image::fromPixmap(getResources().twitch.vip),
|
if (badgeInfoIt != badgeInfos.end())
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
|
||||||
->setTooltip("VIP");
|
|
||||||
}
|
|
||||||
else if (badge == "broadcaster/1")
|
|
||||||
{
|
{
|
||||||
this->emplace<ImageElement>(
|
const auto &subMonths = badgeInfoIt->second;
|
||||||
Image::fromPixmap(getResources().twitch.broadcaster),
|
tooltip += QString(" (%0 months)").arg(subMonths);
|
||||||
MessageElementFlag::BadgeChannelAuthority)
|
|
||||||
->setTooltip("Broadcaster");
|
|
||||||
}
|
}
|
||||||
else if (badge == "turbo/1")
|
|
||||||
{
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.turbo),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip("Twitch Turbo");
|
|
||||||
}
|
|
||||||
else if (badge == "premium/1")
|
|
||||||
{
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.prime),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip("Twitch Prime");
|
|
||||||
}
|
|
||||||
else if (badge.startsWith("partner/"))
|
|
||||||
{
|
|
||||||
int index = badge.midRef(8).toInt();
|
|
||||||
switch (index)
|
|
||||||
{
|
|
||||||
case 1: {
|
|
||||||
this->emplace<ImageElement>(
|
|
||||||
Image::fromPixmap(getResources().twitch.verified,
|
|
||||||
0.25),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip("Verified");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
printf("[TwitchMessageBuilder] Unhandled partner badge "
|
|
||||||
"index: %d\n",
|
|
||||||
index);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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<BadgeElement>(
|
|
||||||
badgeEmote.get(),
|
|
||||||
MessageElementFlag::BadgeSubscription)
|
|
||||||
->setTooltip(QString((*badgeEmote)->tooltip.string) +
|
|
||||||
" (" + subMonths + " months)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->emplace<BadgeElement>(
|
|
||||||
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<BadgeElement>(
|
|
||||||
badgeEmote.get(),
|
|
||||||
MessageElementFlag::BadgeSubscription)
|
|
||||||
->setTooltip(QString((*badgeEmote)->tooltip.string) +
|
|
||||||
" (" + subMonths + " months)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->emplace<BadgeElement>(
|
|
||||||
badgeEmote.get(),
|
|
||||||
MessageElementFlag::BadgeSubscription)
|
|
||||||
->setTooltip((*badgeEmote)->tooltip.string);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default subscriber badge if custom one not found
|
this->emplace<BadgeElement>(badgeEmote.get(), badge.flag_)
|
||||||
this->emplace<ImageElement>(
|
->setTooltip(tooltip);
|
||||||
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<BadgeElement>(badgeEmote.get(),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip((*badgeEmote)->tooltip.string);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (auto _badge = this->twitchChannel->globalTwitchBadges().badge(
|
|
||||||
splits[0], splits[1]))
|
|
||||||
{
|
|
||||||
this->emplace<BadgeElement>(_badge.get(),
|
|
||||||
MessageElementFlag::BadgeVanity)
|
|
||||||
->setTooltip((*_badge)->tooltip.string);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} // namespace chatterino
|
|
||||||
|
|
||||||
void TwitchMessageBuilder::appendChatterinoBadges()
|
void TwitchMessageBuilder::appendChatterinoBadges()
|
||||||
{
|
{
|
||||||
|
@ -1373,4 +1277,5 @@ Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string)
|
||||||
}
|
}
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/Aliases.hpp"
|
#include "common/Aliases.hpp"
|
||||||
#include "common/Outcome.hpp"
|
#include "common/Outcome.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
|
#include "providers/twitch/TwitchBadge.hpp"
|
||||||
|
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
@ -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
|
// parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function
|
||||||
void parseHighlights();
|
void parseHighlights();
|
||||||
|
|
||||||
|
boost::optional<EmotePtr> getTwitchBadge(const Badge &badge);
|
||||||
void appendTwitchEmote(
|
void appendTwitchEmote(
|
||||||
const QString &emote,
|
const QString &emote,
|
||||||
std::vector<std::tuple<int, EmotePtr, EmoteName>> &vec,
|
std::vector<std::tuple<int, EmotePtr, EmoteName>> &vec,
|
||||||
|
|
|
@ -230,6 +230,7 @@ void Window::addDebugStuff()
|
||||||
|
|
||||||
// display name renders strangely
|
// 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"(@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
|
// clang-format on
|
||||||
|
|
||||||
createWindowShortcut(this, "F6", [=] {
|
createWindowShortcut(this, "F6", [=] {
|
||||||
|
|
Loading…
Reference in a new issue