mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
9a8b85e338
There's a document in src/providers/twitch/api which describes how we interact with the Twitch API. Keeping this up to date might be a healthy way for us to ensure we keep using the right APIs for the right job.
322 lines
8 KiB
C++
322 lines
8 KiB
C++
#include "TwitchParseCheerEmotes.hpp"
|
|
|
|
#include <rapidjson/document.h>
|
|
#include <QString>
|
|
#include <vector>
|
|
|
|
namespace chatterino {
|
|
|
|
namespace {
|
|
|
|
template <typename Type>
|
|
inline bool ReadValue(const rapidjson::Value &object, const char *key,
|
|
Type &out)
|
|
{
|
|
if (!object.HasMember(key))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &value = object[key];
|
|
|
|
if (!value.Is<Type>())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out = value.Get<Type>();
|
|
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
inline bool ReadValue<QString>(const rapidjson::Value &object,
|
|
const char *key, QString &out)
|
|
{
|
|
if (!object.HasMember(key))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &value = object[key];
|
|
|
|
if (!value.IsString())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out = value.GetString();
|
|
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
inline bool ReadValue<std::vector<QString>>(const rapidjson::Value &object,
|
|
const char *key,
|
|
std::vector<QString> &out)
|
|
{
|
|
if (!object.HasMember(key))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &value = object[key];
|
|
|
|
if (!value.IsArray())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const rapidjson::Value &innerValue : value.GetArray())
|
|
{
|
|
if (!innerValue.IsString())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out.emplace_back(innerValue.GetString());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Parse a single cheermote set (or "action") from the twitch api
|
|
inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set,
|
|
const rapidjson::Value &action)
|
|
{
|
|
if (!action.IsObject())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "prefix", set.prefix))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "scales", set.scales))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "backgrounds", set.backgrounds))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "states", set.states))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "type", set.type))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "updated_at", set.updatedAt))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(action, "priority", set.priority))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Tiers
|
|
if (!action.HasMember("tiers"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &tiersValue = action["tiers"];
|
|
|
|
if (!tiersValue.IsArray())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const rapidjson::Value &tierValue : tiersValue.GetArray())
|
|
{
|
|
JSONCheermoteSet::CheermoteTier tier;
|
|
|
|
if (!tierValue.IsObject())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(tierValue, "min_bits", tier.minBits))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(tierValue, "id", tier.id))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ReadValue(tierValue, "color", tier.color))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Images
|
|
if (!tierValue.HasMember("images"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &imagesValue = tierValue["images"];
|
|
|
|
if (!imagesValue.IsObject())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Read images object
|
|
for (const auto &imageBackgroundValue : imagesValue.GetObject())
|
|
{
|
|
QString background = imageBackgroundValue.name.GetString();
|
|
bool backgroundExists = false;
|
|
for (const auto &bg : set.backgrounds)
|
|
{
|
|
if (background == bg)
|
|
{
|
|
backgroundExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!backgroundExists)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const rapidjson::Value &imageBackgroundStates =
|
|
imageBackgroundValue.value;
|
|
if (!imageBackgroundStates.IsObject())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Read each key which represents a background
|
|
for (const auto &imageBackgroundState :
|
|
imageBackgroundStates.GetObject())
|
|
{
|
|
QString state = imageBackgroundState.name.GetString();
|
|
bool stateExists = false;
|
|
for (const auto &_state : set.states)
|
|
{
|
|
if (state == _state)
|
|
{
|
|
stateExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!stateExists)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const rapidjson::Value &imageScalesValue =
|
|
imageBackgroundState.value;
|
|
if (!imageScalesValue.IsObject())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Read each key which represents a scale
|
|
for (const auto &imageScaleValue :
|
|
imageScalesValue.GetObject())
|
|
{
|
|
QString scale = imageScaleValue.name.GetString();
|
|
bool scaleExists = false;
|
|
for (const auto &_scale : set.scales)
|
|
{
|
|
if (scale == _scale)
|
|
{
|
|
scaleExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!scaleExists)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const rapidjson::Value &imageScaleURLValue =
|
|
imageScaleValue.value;
|
|
if (!imageScaleURLValue.IsString())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QString url = imageScaleURLValue.GetString();
|
|
|
|
bool ok = false;
|
|
qreal scaleNumber = scale.toFloat(&ok);
|
|
if (!ok)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
qreal chatterinoScale = 1 / scaleNumber;
|
|
|
|
auto image = Image::fromUrl({url}, chatterinoScale);
|
|
|
|
// TODO(pajlada): Fill in name and tooltip
|
|
tier.images[background][state][scale] = image;
|
|
}
|
|
}
|
|
}
|
|
|
|
set.tiers.emplace_back(tier);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Look through the results of
|
|
// https://api.twitch.tv/kraken/bits/actions?channel_id=11148817 for cheermote
|
|
// sets or "Actions" as they are called in the API
|
|
std::vector<JSONCheermoteSet> ParseCheermoteSets(const rapidjson::Document &d)
|
|
{
|
|
std::vector<JSONCheermoteSet> sets;
|
|
|
|
if (!d.IsObject())
|
|
{
|
|
return sets;
|
|
}
|
|
|
|
if (!d.HasMember("actions"))
|
|
{
|
|
return sets;
|
|
}
|
|
|
|
const auto &actionsValue = d["actions"];
|
|
|
|
if (!actionsValue.IsArray())
|
|
{
|
|
return sets;
|
|
}
|
|
|
|
for (const auto &action : actionsValue.GetArray())
|
|
{
|
|
JSONCheermoteSet set;
|
|
bool res = ParseSingleCheermoteSet(set, action);
|
|
|
|
if (res)
|
|
{
|
|
sets.emplace_back(set);
|
|
}
|
|
}
|
|
|
|
return sets;
|
|
}
|
|
} // namespace chatterino
|