776ce8bdbc
* Added subage and followage information to usercard We are using Leppunen's API here to determine user's subage to the current channel and since that API call also returns followage information I decided to utilize that and save ourselves an extra Helix API call. I also added new files specifying new class and methods for Ivr API, which can be very easily expanded with new methods in the future if we ever have to do that. When I was coding I also saw couple unnecessary nitpicks which I fixed :) * Added changelog entry * remove empty lambda * Update UserInfoPopup.cpp * xd Co-authored-by: fourtf <tf.four@gmail.com>
369 lines
9.4 KiB
C++
369 lines
9.4 KiB
C++
#include "providers/twitch/api/Helix.hpp"
|
|
|
|
#include "common/Outcome.hpp"
|
|
|
|
namespace chatterino {
|
|
|
|
static Helix *instance = nullptr;
|
|
|
|
void Helix::fetchUsers(QStringList userIds, QStringList userLogins,
|
|
ResultCallback<std::vector<HelixUser>> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QUrlQuery urlQuery;
|
|
|
|
for (const auto &id : userIds)
|
|
{
|
|
urlQuery.addQueryItem("id", id);
|
|
}
|
|
|
|
for (const auto &login : userLogins)
|
|
{
|
|
urlQuery.addQueryItem("login", login);
|
|
}
|
|
|
|
// TODO: set on success and on error
|
|
this->makeRequest("users", urlQuery)
|
|
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
|
auto root = result.parseJson();
|
|
auto data = root.value("data");
|
|
|
|
if (!data.isArray())
|
|
{
|
|
failureCallback();
|
|
return Failure;
|
|
}
|
|
|
|
std::vector<HelixUser> users;
|
|
|
|
for (const auto &jsonUser : data.toArray())
|
|
{
|
|
users.emplace_back(jsonUser.toObject());
|
|
}
|
|
|
|
successCallback(users);
|
|
|
|
return Success;
|
|
})
|
|
.onError([failureCallback](auto result) {
|
|
// TODO: make better xd
|
|
failureCallback();
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
void Helix::getUserByName(QString userName,
|
|
ResultCallback<HelixUser> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QStringList userIds;
|
|
QStringList userLogins{userName};
|
|
|
|
this->fetchUsers(
|
|
userIds, userLogins,
|
|
[successCallback,
|
|
failureCallback](const std::vector<HelixUser> &users) {
|
|
if (users.empty())
|
|
{
|
|
failureCallback();
|
|
return;
|
|
}
|
|
successCallback(users[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
void Helix::getUserById(QString userId,
|
|
ResultCallback<HelixUser> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QStringList userIds{userId};
|
|
QStringList userLogins;
|
|
|
|
this->fetchUsers(
|
|
userIds, userLogins,
|
|
[successCallback, failureCallback](const auto &users) {
|
|
if (users.empty())
|
|
{
|
|
failureCallback();
|
|
return;
|
|
}
|
|
successCallback(users[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
void Helix::fetchUsersFollows(
|
|
QString fromId, QString toId,
|
|
ResultCallback<HelixUsersFollowsResponse> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
assert(!fromId.isEmpty() || !toId.isEmpty());
|
|
|
|
QUrlQuery urlQuery;
|
|
|
|
if (!fromId.isEmpty())
|
|
{
|
|
urlQuery.addQueryItem("from_id", fromId);
|
|
}
|
|
|
|
if (!toId.isEmpty())
|
|
{
|
|
urlQuery.addQueryItem("to_id", toId);
|
|
}
|
|
|
|
// TODO: set on success and on error
|
|
this->makeRequest("users/follows", urlQuery)
|
|
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
|
auto root = result.parseJson();
|
|
if (root.empty())
|
|
{
|
|
failureCallback();
|
|
return Failure;
|
|
}
|
|
successCallback(HelixUsersFollowsResponse(root));
|
|
return Success;
|
|
})
|
|
.onError([failureCallback](auto result) {
|
|
// TODO: make better xd
|
|
failureCallback();
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
void Helix::getUserFollowers(
|
|
QString userId, ResultCallback<HelixUsersFollowsResponse> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
this->fetchUsersFollows("", userId, successCallback, failureCallback);
|
|
}
|
|
|
|
void Helix::getUserFollow(
|
|
QString userId, QString targetId,
|
|
ResultCallback<bool, HelixUsersFollowsRecord> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
this->fetchUsersFollows(
|
|
userId, targetId,
|
|
[successCallback](const auto &response) {
|
|
if (response.data.empty())
|
|
{
|
|
successCallback(false, HelixUsersFollowsRecord());
|
|
return;
|
|
}
|
|
|
|
successCallback(true, response.data[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
void Helix::fetchStreams(
|
|
QStringList userIds, QStringList userLogins,
|
|
ResultCallback<std::vector<HelixStream>> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QUrlQuery urlQuery;
|
|
|
|
for (const auto &id : userIds)
|
|
{
|
|
urlQuery.addQueryItem("user_id", id);
|
|
}
|
|
|
|
for (const auto &login : userLogins)
|
|
{
|
|
urlQuery.addQueryItem("user_login", login);
|
|
}
|
|
|
|
// TODO: set on success and on error
|
|
this->makeRequest("streams", urlQuery)
|
|
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
|
auto root = result.parseJson();
|
|
auto data = root.value("data");
|
|
|
|
if (!data.isArray())
|
|
{
|
|
failureCallback();
|
|
return Failure;
|
|
}
|
|
|
|
std::vector<HelixStream> streams;
|
|
|
|
for (const auto &jsonStream : data.toArray())
|
|
{
|
|
streams.emplace_back(jsonStream.toObject());
|
|
}
|
|
|
|
successCallback(streams);
|
|
|
|
return Success;
|
|
})
|
|
.onError([failureCallback](auto result) {
|
|
// TODO: make better xd
|
|
failureCallback();
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
void Helix::getStreamById(QString userId,
|
|
ResultCallback<bool, HelixStream> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QStringList userIds{userId};
|
|
QStringList userLogins;
|
|
|
|
this->fetchStreams(
|
|
userIds, userLogins,
|
|
[successCallback, failureCallback](const auto &streams) {
|
|
if (streams.empty())
|
|
{
|
|
successCallback(false, HelixStream());
|
|
return;
|
|
}
|
|
successCallback(true, streams[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
void Helix::getStreamByName(QString userName,
|
|
ResultCallback<bool, HelixStream> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QStringList userIds;
|
|
QStringList userLogins{userName};
|
|
|
|
this->fetchStreams(
|
|
userIds, userLogins,
|
|
[successCallback, failureCallback](const auto &streams) {
|
|
if (streams.empty())
|
|
{
|
|
successCallback(false, HelixStream());
|
|
return;
|
|
}
|
|
successCallback(true, streams[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
///
|
|
|
|
void Helix::fetchGames(QStringList gameIds, QStringList gameNames,
|
|
ResultCallback<std::vector<HelixGame>> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
assert((gameIds.length() + gameNames.length()) > 0);
|
|
|
|
QUrlQuery urlQuery;
|
|
|
|
for (const auto &id : gameIds)
|
|
{
|
|
urlQuery.addQueryItem("id", id);
|
|
}
|
|
|
|
for (const auto &login : gameNames)
|
|
{
|
|
urlQuery.addQueryItem("name", login);
|
|
}
|
|
|
|
// TODO: set on success and on error
|
|
this->makeRequest("games", urlQuery)
|
|
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
|
auto root = result.parseJson();
|
|
auto data = root.value("data");
|
|
|
|
if (!data.isArray())
|
|
{
|
|
failureCallback();
|
|
return Failure;
|
|
}
|
|
|
|
std::vector<HelixGame> games;
|
|
|
|
for (const auto &jsonStream : data.toArray())
|
|
{
|
|
games.emplace_back(jsonStream.toObject());
|
|
}
|
|
|
|
successCallback(games);
|
|
|
|
return Success;
|
|
})
|
|
.onError([failureCallback](auto result) {
|
|
// TODO: make better xd
|
|
failureCallback();
|
|
})
|
|
.execute();
|
|
}
|
|
|
|
void Helix::getGameById(QString gameId,
|
|
ResultCallback<HelixGame> successCallback,
|
|
HelixFailureCallback failureCallback)
|
|
{
|
|
QStringList gameIds{gameId};
|
|
QStringList gameNames;
|
|
|
|
this->fetchGames(
|
|
gameIds, gameNames,
|
|
[successCallback, failureCallback](const auto &games) {
|
|
if (games.empty())
|
|
{
|
|
failureCallback();
|
|
return;
|
|
}
|
|
successCallback(games[0]);
|
|
},
|
|
failureCallback);
|
|
}
|
|
|
|
NetworkRequest Helix::makeRequest(QString url, QUrlQuery urlQuery)
|
|
{
|
|
assert(!url.startsWith("/"));
|
|
|
|
if (this->clientId.isEmpty())
|
|
{
|
|
qDebug()
|
|
<< "Helix::makeRequest called without a client ID set BabyRage";
|
|
// return boost::none;
|
|
}
|
|
|
|
if (this->oauthToken.isEmpty())
|
|
{
|
|
qDebug()
|
|
<< "Helix::makeRequest called without an oauth token set BabyRage";
|
|
// return boost::none;
|
|
}
|
|
|
|
const QString baseUrl("https://api.twitch.tv/helix/");
|
|
|
|
QUrl fullUrl(baseUrl + url);
|
|
|
|
fullUrl.setQuery(urlQuery);
|
|
|
|
return NetworkRequest(fullUrl)
|
|
.timeout(5 * 1000)
|
|
.header("Accept", "application/json")
|
|
.header("Client-ID", this->clientId)
|
|
.header("Authorization", "Bearer " + this->oauthToken);
|
|
}
|
|
|
|
void Helix::update(QString clientId, QString oauthToken)
|
|
{
|
|
this->clientId = clientId;
|
|
this->oauthToken = oauthToken;
|
|
}
|
|
|
|
void Helix::initialize()
|
|
{
|
|
assert(instance == nullptr);
|
|
|
|
instance = new Helix();
|
|
}
|
|
|
|
Helix *getHelix()
|
|
{
|
|
assert(instance != nullptr);
|
|
|
|
return instance;
|
|
}
|
|
|
|
} // namespace chatterino
|