refactor: load Twitch emotes from Helix (#5239)

This commit is contained in:
nerix
2024-09-01 11:22:54 +02:00
committed by GitHub
parent 03b0e4881f
commit 820aa12af6
40 changed files with 1251 additions and 528 deletions
+133
View File
@@ -2,6 +2,8 @@
#include "Application.hpp"
#include "common/Common.hpp"
#include "common/Env.hpp"
#include "common/Literals.hpp"
#include "common/network/NetworkRequest.hpp"
#include "common/network/NetworkResult.hpp"
#include "common/QLogging.hpp"
@@ -32,6 +34,7 @@
#include "providers/twitch/TwitchAccount.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchIrcServer.hpp"
#include "providers/twitch/TwitchUsers.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
#include "singletons/StreamerMode.hpp"
@@ -47,11 +50,15 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QStringBuilder>
#include <QThread>
#include <QTimer>
#include <rapidjson/document.h>
namespace chatterino {
using namespace literals;
namespace {
#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
const QString MAGIC_MESSAGE_SUFFIX = QString((const char *)u8" \U000E0000");
@@ -84,6 +91,7 @@ TwitchChannel::TwitchChannel(const QString &name)
, subscriptionUrl_("https://www.twitch.tv/subs/" + name)
, channelUrl_("https://twitch.tv/" + name)
, popoutPlayerUrl_(TWITCH_PLAYER_URL.arg(name))
, localTwitchEmotes_(std::make_shared<EmoteMap>())
, bttvEmotes_(std::make_shared<EmoteMap>())
, ffzEmotes_(std::make_shared<EmoteMap>())
, seventvEmotes_(std::make_shared<EmoteMap>())
@@ -94,6 +102,7 @@ TwitchChannel::TwitchChannel(const QString &name)
getApp()->getAccounts()->twitch.currentUserChanged.connect([this] {
this->setMod(false);
this->refreshPubSub();
this->refreshTwitchChannelEmotes(false);
}));
this->refreshPubSub();
@@ -133,6 +142,36 @@ TwitchChannel::TwitchChannel(const QString &name)
});
this->threadClearTimer_.start(5 * 60 * 1000);
this->signalHolder_.managedConnect(
getApp()->getAccounts()->twitch.emotesReloaded,
[this](auto *caller, const auto &result) {
if (result)
{
// emotes were reloaded - clear follower emotes if the user is
// now subscribed to the streamer
if (!this->localTwitchEmotes_.get()->empty() &&
getApp()->getAccounts()->twitch.getCurrent()->hasEmoteSet(
EmoteSetId{this->localTwitchEmoteSetID_.get()}))
{
this->localTwitchEmotes_.set(std::make_shared<EmoteMap>());
}
if (caller == this)
{
this->addSystemMessage(
"Twitch subscriber emotes reloaded.");
}
return;
}
if (caller == this || caller == nullptr)
{
this->addSystemMessage(
u"Failed to load Twitch subscriber emotes: " %
result.error());
}
});
// debugging
#if 0
for (int i = 0; i < 1000; i++) {
@@ -194,6 +233,82 @@ void TwitchChannel::setLocalizedName(const QString &name)
this->nameOptions.localizedName = name;
}
void TwitchChannel::refreshTwitchChannelEmotes(bool manualRefresh)
{
if (getApp()->isTest())
{
return;
}
// Twitch's 'Get User Emotes' doesn't assigns a different set-ID to follower
// emotes compared to subscriber emotes.
QString setID = TWITCH_SUB_EMOTE_SET_PREFIX % this->roomId();
this->localTwitchEmoteSetID_.set(setID);
if (getApp()->getAccounts()->twitch.getCurrent()->hasEmoteSet(
EmoteSetId{setID}))
{
this->localTwitchEmotes_.set(std::make_shared<EmoteMap>());
return;
}
auto makeEmotes = [](const auto &emotes) {
EmoteMap map;
for (const auto &emote : emotes)
{
if (emote.type != u"follower")
{
continue;
}
map.emplace(
EmoteName{emote.name},
getApp()->getEmotes()->getTwitchEmotes()->getOrCreateEmote(
EmoteId{emote.id}, EmoteName{emote.name}));
}
return map;
};
getHelix()->getFollowedChannel(
getApp()->getAccounts()->twitch.getCurrent()->getUserId(),
this->roomId(),
[weak{this->weak_from_this()}, makeEmotes](const auto &chan) {
auto self = std::dynamic_pointer_cast<TwitchChannel>(weak.lock());
if (!self || !chan)
{
return;
}
getHelix()->getChannelEmotes(
self->roomId(),
[weak, makeEmotes](const auto &emotes) {
auto self =
std::dynamic_pointer_cast<TwitchChannel>(weak.lock());
if (!self)
{
return;
}
self->localTwitchEmotes_.set(
std::make_shared<EmoteMap>(makeEmotes(emotes)));
},
[weak] {
auto self = weak.lock();
if (!self)
{
return;
}
self->addSystemMessage("Failed to load follower emotes.");
});
},
[](const auto &error) {
qCWarning(chatterinoTwitch)
<< "Failed to get following status:" << error;
});
if (manualRefresh)
{
getApp()->getAccounts()->twitch.getCurrent()->reloadEmotes(this);
}
}
void TwitchChannel::refreshBTTVChannelEmotes(bool manualRefresh)
{
if (!Settings::instance().enableBTTVChannelEmotes)
@@ -536,6 +651,7 @@ void TwitchChannel::roomIdChanged()
this->refreshPubSub();
this->refreshBadges();
this->refreshCheerEmotes();
this->refreshTwitchChannelEmotes(false);
this->refreshFFZChannelEmotes(false);
this->refreshBTTVChannelEmotes(false);
this->refreshSevenTVChannelEmotes(false);
@@ -789,6 +905,18 @@ SharedAccessGuard<const TwitchChannel::StreamStatus>
return this->streamStatus_.accessConst();
}
std::optional<EmotePtr> TwitchChannel::twitchEmote(const EmoteName &name) const
{
auto emotes = this->localTwitchEmotes();
auto it = emotes->find(name);
if (it == emotes->end())
{
return getApp()->getAccounts()->twitch.getCurrent()->twitchEmote(name);
}
return it->second;
}
std::optional<EmotePtr> TwitchChannel::bttvEmote(const EmoteName &name) const
{
auto emotes = this->bttvEmotes_.get();
@@ -825,6 +953,11 @@ std::optional<EmotePtr> TwitchChannel::seventvEmote(const EmoteName &name) const
return it->second;
}
std::shared_ptr<const EmoteMap> TwitchChannel::localTwitchEmotes() const
{
return this->localTwitchEmotes_.get();
}
std::shared_ptr<const EmoteMap> TwitchChannel::bttvEmotes() const
{
return this->bttvEmotes_.get();