#include "TwitchParseCheerEmotes.hpp" #include #include #include namespace chatterino { namespace { template 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()) { return false; } out = value.Get(); return true; } template <> inline bool ReadValue(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>(const rapidjson::Value &object, const char *key, std::vector &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 ParseCheermoteSets(const rapidjson::Document &d) { std::vector 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