-
-
Notifications
You must be signed in to change notification settings - Fork 455
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use New 7TV Cosmetics System (#4512)
* feat(seventv): use new cosmetics system * chore: add changelog entry * fix: old `clang-format` * fix: small suggestions pt1 * refactor: add 7tv api wrapper * fix: small clang-tidy things * fix: remove unused constants * fix: old clangtidy * refactor: rename * fix: increase interval to 60s * fix: newline * fix: Twitch * docs: add comment * fix: remove v2 badges endpoint * fix: deadlock This is actually really sad. * fix: remove api entry * fix: old clang-format * Sort functions in SeventvBadges.hpp/cpp * Remove unused vector include * Add comments to SeventvBadges.hpp functions * Rename `addBadge` to `registerBadge` * fix: cleanup eventloop * ci(test): add timeout --------- Co-authored-by: Felanbird <41973452+Felanbird@users.noreply.github.com> Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
- Loading branch information
1 parent
8cfa5e8
commit 33fa3e0
Showing
25 changed files
with
836 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
#include "providers/seventv/SeventvAPI.hpp" | ||
|
||
#include "common/Literals.hpp" | ||
#include "common/NetworkRequest.hpp" | ||
#include "common/NetworkResult.hpp" | ||
#include "common/Outcome.hpp" | ||
|
||
namespace { | ||
|
||
using namespace chatterino::literals; | ||
|
||
const QString API_URL_USER = u"https://7tv.io/v3/users/twitch/%1"_s; | ||
const QString API_URL_EMOTE_SET = u"https://7tv.io/v3/emote-sets/%1"_s; | ||
const QString API_URL_PRESENCES = u"https://7tv.io/v3/users/%1/presences"_s; | ||
|
||
} // namespace | ||
|
||
// NOLINTBEGIN(readability-convert-member-functions-to-static) | ||
namespace chatterino { | ||
|
||
void SeventvAPI::getUserByTwitchID( | ||
const QString &twitchID, SuccessCallback<const QJsonObject &> &&onSuccess, | ||
ErrorCallback &&onError) | ||
{ | ||
NetworkRequest(API_URL_USER.arg(twitchID), NetworkRequestType::Get) | ||
.timeout(20000) | ||
.onSuccess([callback = std::move(onSuccess)]( | ||
const NetworkResult &result) -> Outcome { | ||
auto json = result.parseJson(); | ||
callback(json); | ||
return Success; | ||
}) | ||
.onError([callback = std::move(onError)](const NetworkResult &result) { | ||
callback(result); | ||
}) | ||
.execute(); | ||
} | ||
|
||
void SeventvAPI::getEmoteSet(const QString &emoteSet, | ||
SuccessCallback<const QJsonObject &> &&onSuccess, | ||
ErrorCallback &&onError) | ||
{ | ||
NetworkRequest(API_URL_EMOTE_SET.arg(emoteSet), NetworkRequestType::Get) | ||
.timeout(25000) | ||
.onSuccess([callback = std::move(onSuccess)]( | ||
const NetworkResult &result) -> Outcome { | ||
auto json = result.parseJson(); | ||
callback(json); | ||
return Success; | ||
}) | ||
.onError([callback = std::move(onError)](const NetworkResult &result) { | ||
callback(result); | ||
}) | ||
.execute(); | ||
} | ||
|
||
void SeventvAPI::updatePresence(const QString &twitchChannelID, | ||
const QString &seventvUserID, | ||
SuccessCallback<> &&onSuccess, | ||
ErrorCallback &&onError) | ||
{ | ||
QJsonObject payload{ | ||
{u"kind"_s, 1}, // UserPresenceKindChannel | ||
{u"data"_s, | ||
QJsonObject{ | ||
{u"id"_s, twitchChannelID}, | ||
{u"platform"_s, u"TWITCH"_s}, | ||
}}, | ||
}; | ||
|
||
NetworkRequest(API_URL_PRESENCES.arg(seventvUserID), | ||
NetworkRequestType::Post) | ||
.json(payload) | ||
.timeout(10000) | ||
.onSuccess([callback = std::move(onSuccess)](const auto &) -> Outcome { | ||
callback(); | ||
return Success; | ||
}) | ||
.onError([callback = std::move(onError)](const NetworkResult &result) { | ||
callback(result); | ||
}) | ||
.execute(); | ||
} | ||
|
||
SeventvAPI &getSeventvAPI() | ||
{ | ||
static SeventvAPI instance; | ||
return instance; | ||
} | ||
|
||
} // namespace chatterino | ||
// NOLINTEND(readability-convert-member-functions-to-static) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
|
||
class QString; | ||
class QJsonObject; | ||
|
||
namespace chatterino { | ||
|
||
class NetworkResult; | ||
|
||
class SeventvAPI | ||
{ | ||
using ErrorCallback = std::function<void(const NetworkResult &)>; | ||
template <typename... T> | ||
using SuccessCallback = std::function<void(T...)>; | ||
|
||
public: | ||
void getUserByTwitchID(const QString &twitchID, | ||
SuccessCallback<const QJsonObject &> &&onSuccess, | ||
ErrorCallback &&onError); | ||
void getEmoteSet(const QString &emoteSet, | ||
SuccessCallback<const QJsonObject &> &&onSuccess, | ||
ErrorCallback &&onError); | ||
|
||
void updatePresence(const QString &twitchChannelID, | ||
const QString &seventvUserID, | ||
SuccessCallback<> &&onSuccess, ErrorCallback &&onError); | ||
}; | ||
|
||
SeventvAPI &getSeventvAPI(); | ||
|
||
} // namespace chatterino |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,77 +1,80 @@ | ||
#include "providers/seventv/SeventvBadges.hpp" | ||
|
||
#include "common/NetworkRequest.hpp" | ||
#include "common/NetworkResult.hpp" | ||
#include "common/Outcome.hpp" | ||
#include "messages/Emote.hpp" | ||
#include "messages/Image.hpp" | ||
#include "providers/seventv/SeventvAPI.hpp" | ||
#include "providers/seventv/SeventvEmotes.hpp" | ||
|
||
#include <QJsonArray> | ||
#include <QUrl> | ||
#include <QUrlQuery> | ||
|
||
#include <map> | ||
|
||
namespace chatterino { | ||
|
||
void SeventvBadges::initialize(Settings & /*settings*/, Paths & /*paths*/) | ||
{ | ||
this->loadSeventvBadges(); | ||
} | ||
|
||
boost::optional<EmotePtr> SeventvBadges::getBadge(const UserId &id) | ||
boost::optional<EmotePtr> SeventvBadges::getBadge(const UserId &id) const | ||
{ | ||
std::shared_lock lock(this->mutex_); | ||
|
||
auto it = this->badgeMap_.find(id.string); | ||
if (it != this->badgeMap_.end()) | ||
{ | ||
return this->emotes_[it->second]; | ||
return it->second; | ||
} | ||
return boost::none; | ||
} | ||
|
||
void SeventvBadges::loadSeventvBadges() | ||
void SeventvBadges::assignBadgeToUser(const QString &badgeID, | ||
const UserId &userID) | ||
{ | ||
const std::unique_lock lock(this->mutex_); | ||
|
||
const auto badgeIt = this->knownBadges_.find(badgeID); | ||
if (badgeIt != this->knownBadges_.end()) | ||
{ | ||
this->badgeMap_[userID.string] = badgeIt->second; | ||
} | ||
} | ||
|
||
void SeventvBadges::clearBadgeFromUser(const QString &badgeID, | ||
const UserId &userID) | ||
{ | ||
const std::unique_lock lock(this->mutex_); | ||
|
||
const auto it = this->badgeMap_.find(userID.string); | ||
if (it != this->badgeMap_.end() && it->second->id.string == badgeID) | ||
{ | ||
this->badgeMap_.erase(userID.string); | ||
} | ||
} | ||
|
||
void SeventvBadges::registerBadge(const QJsonObject &badgeJson) | ||
{ | ||
// Cosmetics will work differently in v3, until this is ready | ||
// we'll use this endpoint. | ||
static QUrl url("https://7tv.io/v2/cosmetics"); | ||
|
||
static QUrlQuery urlQuery; | ||
// valid user_identifier values: "object_id", "twitch_id", "login" | ||
urlQuery.addQueryItem("user_identifier", "twitch_id"); | ||
|
||
url.setQuery(urlQuery); | ||
|
||
NetworkRequest(url) | ||
.onSuccess([this](const NetworkResult &result) -> Outcome { | ||
auto root = result.parseJson(); | ||
|
||
std::unique_lock lock(this->mutex_); | ||
|
||
int index = 0; | ||
for (const auto &jsonBadge : root.value("badges").toArray()) | ||
{ | ||
auto badge = jsonBadge.toObject(); | ||
auto urls = badge.value("urls").toArray(); | ||
auto emote = | ||
Emote{EmoteName{}, | ||
ImageSet{Url{urls.at(0).toArray().at(1).toString()}, | ||
Url{urls.at(1).toArray().at(1).toString()}, | ||
Url{urls.at(2).toArray().at(1).toString()}}, | ||
Tooltip{badge.value("tooltip").toString()}, Url{}}; | ||
|
||
this->emotes_.push_back( | ||
std::make_shared<const Emote>(std::move(emote))); | ||
|
||
for (const auto &user : badge.value("users").toArray()) | ||
{ | ||
this->badgeMap_[user.toString()] = index; | ||
} | ||
++index; | ||
} | ||
|
||
return Success; | ||
}) | ||
.execute(); | ||
const auto badgeID = badgeJson["id"].toString(); | ||
|
||
const std::unique_lock lock(this->mutex_); | ||
|
||
if (this->knownBadges_.find(badgeID) != this->knownBadges_.end()) | ||
{ | ||
return; | ||
} | ||
|
||
auto emote = Emote{ | ||
.name = EmoteName{}, | ||
.images = SeventvEmotes::createImageSet(badgeJson), | ||
.tooltip = Tooltip{badgeJson["tooltip"].toString()}, | ||
.homePage = Url{}, | ||
.id = EmoteId{badgeID}, | ||
}; | ||
|
||
if (emote.images.getImage1()->isEmpty()) | ||
{ | ||
return; // Bad images | ||
} | ||
|
||
this->knownBadges_[badgeID] = | ||
std::make_shared<const Emote>(std::move(emote)); | ||
} | ||
|
||
} // namespace chatterino |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#pragma once | ||
|
||
#include <magic_enum.hpp> | ||
|
||
namespace chatterino::seventv { | ||
|
||
enum class CosmeticKind { | ||
Badge, | ||
Paint, | ||
EmoteSet, | ||
|
||
INVALID, | ||
}; | ||
|
||
} // namespace chatterino::seventv | ||
|
||
template <> | ||
constexpr magic_enum::customize::customize_t | ||
magic_enum::customize::enum_name<chatterino::seventv::CosmeticKind>( | ||
chatterino::seventv::CosmeticKind value) noexcept | ||
{ | ||
using chatterino::seventv::CosmeticKind; | ||
switch (value) | ||
{ | ||
case CosmeticKind::Badge: | ||
return "BADGE"; | ||
case CosmeticKind::Paint: | ||
return "PAINT"; | ||
case CosmeticKind::EmoteSet: | ||
return "EMOTE_SET"; | ||
|
||
default: | ||
return default_tag; | ||
} | ||
} |
Oops, something went wrong.