mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Refactored loading emotes and emotesets (#2539)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
e8a42a13f8
commit
af4e3f5062
4 changed files with 140 additions and 119 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "providers/twitch/TwitchCommon.hpp"
|
||||
#include "providers/twitch/TwitchUser.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/api/Kraken.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "util/RapidjsonHelpers.hpp"
|
||||
|
||||
|
@ -176,39 +177,70 @@ void TwitchAccount::loadEmotes()
|
|||
qCDebug(chatterinoTwitch)
|
||||
<< "Loading Twitch emotes for user" << this->getUserName();
|
||||
|
||||
const auto &clientID = this->getOAuthClient();
|
||||
const auto &oauthToken = this->getOAuthToken();
|
||||
|
||||
if (clientID.isEmpty() || oauthToken.isEmpty())
|
||||
if (this->getOAuthClient().isEmpty() || this->getOAuthToken().isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoTwitch) << "Missing Client ID or OAuth token";
|
||||
return;
|
||||
}
|
||||
|
||||
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() +
|
||||
"/emotes");
|
||||
getKraken()->getUserEmotes(
|
||||
this,
|
||||
[this](KrakenEmoteSets data) {
|
||||
// clear emote data
|
||||
auto emoteData = this->emotes_.access();
|
||||
emoteData->emoteSets.clear();
|
||||
emoteData->allEmoteNames.clear();
|
||||
|
||||
NetworkRequest(url)
|
||||
|
||||
.authorizeTwitchV5(this->getOAuthClient(), this->getOAuthToken())
|
||||
.onError([=](NetworkResult result) {
|
||||
// no emotes available
|
||||
if (data.emoteSets.isEmpty())
|
||||
{
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< "[TwitchAccount::loadEmotes] Error" << result.status();
|
||||
if (result.status() == 203)
|
||||
{
|
||||
// onFinished(FollowResult_NotFollowing);
|
||||
<< "\"emoticon_sets\" either empty or not present in "
|
||||
"Kraken::getUserEmotes response";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// onFinished(FollowResult_Failed);
|
||||
}
|
||||
})
|
||||
.onSuccess([=](auto result) -> Outcome {
|
||||
this->parseEmotes(result.parseRapidJson());
|
||||
|
||||
return Success;
|
||||
})
|
||||
.execute();
|
||||
for (auto emoteSetIt = data.emoteSets.begin();
|
||||
emoteSetIt != data.emoteSets.end(); ++emoteSetIt)
|
||||
{
|
||||
auto emoteSet = std::make_shared<EmoteSet>();
|
||||
|
||||
emoteSet->key = emoteSetIt.key();
|
||||
this->loadEmoteSetData(emoteSet);
|
||||
|
||||
for (const auto emoteArrObj : emoteSetIt.value().toArray())
|
||||
{
|
||||
if (!emoteArrObj.isObject())
|
||||
{
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< QString("Emote value from set %1 was invalid")
|
||||
.arg(emoteSet->key);
|
||||
}
|
||||
KrakenEmote krakenEmote(emoteArrObj.toObject());
|
||||
|
||||
auto id = EmoteId{krakenEmote.id};
|
||||
auto code = EmoteName{krakenEmote.code};
|
||||
|
||||
auto cleanCode =
|
||||
EmoteName{TwitchEmotes::cleanUpEmoteCode(code)};
|
||||
emoteSet->emotes.emplace_back(TwitchEmote{id, cleanCode});
|
||||
emoteData->allEmoteNames.push_back(cleanCode);
|
||||
|
||||
auto emote =
|
||||
getApp()->emotes->twitch.getOrCreateEmote(id, code);
|
||||
emoteData->emotes.emplace(code, emote);
|
||||
}
|
||||
|
||||
std::sort(emoteSet->emotes.begin(), emoteSet->emotes.end(),
|
||||
[](const TwitchEmote &l, const TwitchEmote &r) {
|
||||
return l.name.string < r.name.string;
|
||||
});
|
||||
emoteData->emoteSets.emplace_back(emoteSet);
|
||||
}
|
||||
},
|
||||
[] {
|
||||
// request failed
|
||||
});
|
||||
}
|
||||
|
||||
AccessGuard<const TwitchAccount::TwitchAccountEmoteData>
|
||||
|
@ -256,71 +288,6 @@ void TwitchAccount::autoModDeny(const QString msgID)
|
|||
.execute();
|
||||
}
|
||||
|
||||
void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
||||
{
|
||||
auto emoteData = this->emotes_.access();
|
||||
|
||||
emoteData->emoteSets.clear();
|
||||
emoteData->allEmoteNames.clear();
|
||||
|
||||
auto emoticonSets = root.FindMember("emoticon_sets");
|
||||
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject())
|
||||
{
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< "No emoticon_sets in load emotes response";
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &emoteSetJSON : emoticonSets->value.GetObject())
|
||||
{
|
||||
auto emoteSet = std::make_shared<EmoteSet>();
|
||||
|
||||
emoteSet->key = emoteSetJSON.name.GetString();
|
||||
|
||||
this->loadEmoteSetData(emoteSet);
|
||||
|
||||
for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray())
|
||||
{
|
||||
if (!emoteJSON.IsObject())
|
||||
{
|
||||
qCWarning(chatterinoTwitch) << "Emote value was invalid";
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t idNumber;
|
||||
if (!rj::getSafe(emoteJSON, "id", idNumber))
|
||||
{
|
||||
qCWarning(chatterinoTwitch) << "No ID key found in Emote value";
|
||||
return;
|
||||
}
|
||||
|
||||
QString _code;
|
||||
if (!rj::getSafe(emoteJSON, "code", _code))
|
||||
{
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< "No code key found in Emote value";
|
||||
return;
|
||||
}
|
||||
|
||||
auto code = EmoteName{_code};
|
||||
auto id = EmoteId{QString::number(idNumber)};
|
||||
|
||||
auto cleanCode = EmoteName{TwitchEmotes::cleanUpEmoteCode(code)};
|
||||
emoteSet->emotes.emplace_back(TwitchEmote{id, cleanCode});
|
||||
emoteData->allEmoteNames.push_back(cleanCode);
|
||||
|
||||
auto emote = getApp()->emotes->twitch.getOrCreateEmote(id, code);
|
||||
emoteData->emotes.emplace(code, emote);
|
||||
}
|
||||
|
||||
std::sort(emoteSet->emotes.begin(), emoteSet->emotes.end(),
|
||||
[](const TwitchEmote &l, const TwitchEmote &r) {
|
||||
return l.name.string < r.name.string;
|
||||
});
|
||||
emoteData->emoteSets.emplace_back(emoteSet);
|
||||
}
|
||||
};
|
||||
|
||||
void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
||||
{
|
||||
if (!emoteSet)
|
||||
|
@ -340,44 +307,35 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
|||
|
||||
NetworkRequest(Env::get().twitchEmoteSetResolverUrl.arg(emoteSet->key))
|
||||
.cache()
|
||||
.onError([](NetworkResult result) {
|
||||
qCWarning(chatterinoTwitch) << "Error code" << result.status()
|
||||
<< "while loading emote set data";
|
||||
})
|
||||
.onSuccess([emoteSet](auto result) -> Outcome {
|
||||
auto root = result.parseRapidJson();
|
||||
if (!root.IsObject())
|
||||
.onSuccess([emoteSet](NetworkResult result) -> Outcome {
|
||||
auto rootOld = result.parseRapidJson();
|
||||
auto root = result.parseJson();
|
||||
if (root.isEmpty())
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
std::string emoteSetID;
|
||||
QString channelName;
|
||||
QString type;
|
||||
if (!rj::getSafe(root, "channel_name", channelName))
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
TwitchEmoteSetResolverResponse response(root);
|
||||
|
||||
if (!rj::getSafe(root, "type", type))
|
||||
{
|
||||
return Failure;
|
||||
}
|
||||
|
||||
qCDebug(chatterinoTwitch)
|
||||
<< "Loaded twitch emote set data for" << emoteSet->key;
|
||||
|
||||
auto name = channelName;
|
||||
auto name = response.channelName;
|
||||
name.detach();
|
||||
name[0] = name[0].toUpper();
|
||||
|
||||
emoteSet->text = name;
|
||||
emoteSet->type = response.type;
|
||||
emoteSet->channelName = response.channelName;
|
||||
|
||||
emoteSet->type = type;
|
||||
emoteSet->channelName = channelName;
|
||||
qCDebug(chatterinoTwitch)
|
||||
<< QString("Loaded twitch emote set data for %1")
|
||||
.arg(emoteSet->key);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([](NetworkResult result) {
|
||||
qCWarning(chatterinoTwitch)
|
||||
<< QString("Error code %1 while loading emote set data")
|
||||
.arg(result.status());
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,31 @@ enum FollowResult {
|
|||
FollowResult_Failed,
|
||||
};
|
||||
|
||||
struct TwitchEmoteSetResolverResponse {
|
||||
const QString channelName;
|
||||
const QString channelId;
|
||||
const QString type;
|
||||
const int tier;
|
||||
const bool isCustom;
|
||||
// Example response:
|
||||
// {
|
||||
// "channel_name": "zneix",
|
||||
// "channel_id": "99631238",
|
||||
// "type": "",
|
||||
// "tier": 1,
|
||||
// "custom": false
|
||||
// }
|
||||
|
||||
TwitchEmoteSetResolverResponse(QJsonObject jsonObject)
|
||||
: channelName(jsonObject.value("channel_name").toString())
|
||||
, channelId(jsonObject.value("channel_id").toString())
|
||||
, type(jsonObject.value("type").toString())
|
||||
, tier(jsonObject.value("tier").toInt())
|
||||
, isCustom(jsonObject.value("custom").toBool())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class TwitchAccount : public Account
|
||||
{
|
||||
public:
|
||||
|
@ -91,7 +116,6 @@ public:
|
|||
void autoModDeny(const QString msgID);
|
||||
|
||||
private:
|
||||
void parseEmotes(const rapidjson::Document &document);
|
||||
void loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet);
|
||||
|
||||
QString oauthClient_;
|
||||
|
|
|
@ -8,6 +8,28 @@ namespace chatterino {
|
|||
|
||||
static Kraken *instance = nullptr;
|
||||
|
||||
void Kraken::getUserEmotes(TwitchAccount *account,
|
||||
ResultCallback<KrakenEmoteSets> successCallback,
|
||||
KrakenFailureCallback failureCallback)
|
||||
{
|
||||
this->makeRequest(QString("users/%1/emotes").arg(account->getUserId()), {})
|
||||
.authorizeTwitchV5(account->getOAuthClient(), account->getOAuthToken())
|
||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||
auto data = result.parseJson();
|
||||
|
||||
KrakenEmoteSets emoteSets(data);
|
||||
|
||||
successCallback(emoteSets);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](NetworkResult /*result*/) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery)
|
||||
{
|
||||
assert(!url.startsWith("/"));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
@ -14,11 +15,22 @@ using KrakenFailureCallback = std::function<void()>;
|
|||
template <typename... T>
|
||||
using ResultCallback = std::function<void(T...)>;
|
||||
|
||||
struct KrakenChannel {
|
||||
const QString status;
|
||||
struct KrakenEmoteSets {
|
||||
const QJsonObject emoteSets;
|
||||
|
||||
KrakenChannel(QJsonObject jsonObject)
|
||||
: status(jsonObject.value("status").toString())
|
||||
KrakenEmoteSets(QJsonObject jsonObject)
|
||||
: emoteSets(jsonObject.value("emoticon_sets").toObject())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct KrakenEmote {
|
||||
const QString code;
|
||||
const QString id;
|
||||
|
||||
KrakenEmote(QJsonObject jsonObject)
|
||||
: code(jsonObject.value("code").toString())
|
||||
, id(QString::number(jsonObject.value("id").toInt()))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -26,6 +38,11 @@ struct KrakenChannel {
|
|||
class Kraken final : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
// https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
||||
void getUserEmotes(TwitchAccount *account,
|
||||
ResultCallback<KrakenEmoteSets> successCallback,
|
||||
KrakenFailureCallback failureCallback);
|
||||
|
||||
void update(QString clientId, QString oauthToken);
|
||||
|
||||
static void initialize();
|
||||
|
|
Loading…
Reference in a new issue