Twitch API: v5 to Helix migration (#1560)
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.
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
#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 userId,
|
||||
ResultCallback<HelixUser> successCallback,
|
||||
HelixFailureCallback failureCallback)
|
||||
{
|
||||
QStringList userIds;
|
||||
QStringList userLogins{userId};
|
||||
|
||||
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
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/NetworkRequest.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
using HelixFailureCallback = std::function<void()>;
|
||||
template <typename... T>
|
||||
using ResultCallback = std::function<void(T...)>;
|
||||
|
||||
struct HelixUser {
|
||||
QString id;
|
||||
QString login;
|
||||
QString displayName;
|
||||
QString description;
|
||||
QString profileImageUrl;
|
||||
int viewCount;
|
||||
|
||||
explicit HelixUser(QJsonObject jsonObject)
|
||||
: id(jsonObject.value("id").toString())
|
||||
, login(jsonObject.value("login").toString())
|
||||
, displayName(jsonObject.value("display_name").toString())
|
||||
, description(jsonObject.value("description").toString())
|
||||
, profileImageUrl(jsonObject.value("profile_image_url").toString())
|
||||
, viewCount(jsonObject.value("view_count").toInt())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixUsersFollowsRecord {
|
||||
QString fromId;
|
||||
QString fromName;
|
||||
QString toId;
|
||||
QString toName;
|
||||
QString followedAt; // date time object
|
||||
|
||||
HelixUsersFollowsRecord()
|
||||
: fromId("")
|
||||
, fromName("")
|
||||
, toId("")
|
||||
, toName("")
|
||||
, followedAt("")
|
||||
{
|
||||
}
|
||||
|
||||
explicit HelixUsersFollowsRecord(QJsonObject jsonObject)
|
||||
: fromId(jsonObject.value("from_id").toString())
|
||||
, fromName(jsonObject.value("from_name").toString())
|
||||
, toId(jsonObject.value("to_id").toString())
|
||||
, toName(jsonObject.value("to_name").toString())
|
||||
, followedAt(jsonObject.value("followed_at").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixUsersFollowsResponse {
|
||||
int total;
|
||||
std::vector<HelixUsersFollowsRecord> data;
|
||||
explicit HelixUsersFollowsResponse(QJsonObject jsonObject)
|
||||
: total(jsonObject.value("total").toInt())
|
||||
{
|
||||
const auto &jsonData = jsonObject.value("data").toArray();
|
||||
std::transform(jsonData.begin(), jsonData.end(),
|
||||
std::back_inserter(this->data),
|
||||
[](const QJsonValue &record) {
|
||||
return HelixUsersFollowsRecord(record.toObject());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixStream {
|
||||
QString id; // stream id
|
||||
QString userId;
|
||||
QString userName;
|
||||
QString gameId;
|
||||
QString type;
|
||||
QString title;
|
||||
int viewerCount;
|
||||
QString startedAt;
|
||||
QString language;
|
||||
QString thumbnailUrl;
|
||||
|
||||
HelixStream()
|
||||
: id("")
|
||||
, userId("")
|
||||
, userName("")
|
||||
, gameId("")
|
||||
, type("")
|
||||
, title("")
|
||||
, viewerCount()
|
||||
, startedAt("")
|
||||
, language("")
|
||||
, thumbnailUrl("")
|
||||
{
|
||||
}
|
||||
|
||||
explicit HelixStream(QJsonObject jsonObject)
|
||||
: id(jsonObject.value("id").toString())
|
||||
, userId(jsonObject.value("user_id").toString())
|
||||
, userName(jsonObject.value("user_name").toString())
|
||||
, gameId(jsonObject.value("game_id").toString())
|
||||
, type(jsonObject.value("type").toString())
|
||||
, title(jsonObject.value("title").toString())
|
||||
, viewerCount(jsonObject.value("viewer_count").toInt())
|
||||
, startedAt(jsonObject.value("started_at").toString())
|
||||
, language(jsonObject.value("language").toString())
|
||||
, thumbnailUrl(jsonObject.value("thumbnail_url").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct HelixGame {
|
||||
QString id; // stream id
|
||||
QString name;
|
||||
QString boxArtUrl;
|
||||
|
||||
explicit HelixGame(QJsonObject jsonObject)
|
||||
: id(jsonObject.value("id").toString())
|
||||
, name(jsonObject.value("name").toString())
|
||||
, boxArtUrl(jsonObject.value("box_art_url").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Helix final : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
// https://dev.twitch.tv/docs/api/reference#get-users
|
||||
void fetchUsers(QStringList userIds, QStringList userLogins,
|
||||
ResultCallback<std::vector<HelixUser>> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
void getUserByName(QString userName,
|
||||
ResultCallback<HelixUser> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
void getUserById(QString userId, ResultCallback<HelixUser> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#get-users-follows
|
||||
void fetchUsersFollows(
|
||||
QString fromId, QString toId,
|
||||
ResultCallback<HelixUsersFollowsResponse> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void getUserFollowers(
|
||||
QString userId,
|
||||
ResultCallback<HelixUsersFollowsResponse> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void getUserFollow(
|
||||
QString userId, QString targetId,
|
||||
ResultCallback<bool, HelixUsersFollowsRecord> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#get-streams
|
||||
void fetchStreams(QStringList userIds, QStringList userLogins,
|
||||
ResultCallback<std::vector<HelixStream>> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void getStreamById(QString userId,
|
||||
ResultCallback<bool, HelixStream> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void getStreamByName(QString userName,
|
||||
ResultCallback<bool, HelixStream> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/api/reference#get-games
|
||||
void fetchGames(QStringList gameIds, QStringList gameNames,
|
||||
ResultCallback<std::vector<HelixGame>> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void getGameById(QString gameId, ResultCallback<HelixGame> successCallback,
|
||||
HelixFailureCallback failureCallback);
|
||||
|
||||
void update(QString clientId, QString oauthToken);
|
||||
|
||||
static void initialize();
|
||||
|
||||
private:
|
||||
NetworkRequest makeRequest(QString url, QUrlQuery urlQuery);
|
||||
|
||||
QString clientId;
|
||||
QString oauthToken;
|
||||
};
|
||||
|
||||
Helix *getHelix();
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "providers/twitch/api/Kraken.hpp"
|
||||
|
||||
#include "common/Outcome.hpp"
|
||||
#include "providers/twitch/TwitchCommon.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
static Kraken *instance = nullptr;
|
||||
|
||||
void Kraken::getChannel(QString userId,
|
||||
ResultCallback<KrakenChannel> successCallback,
|
||||
KrakenFailureCallback failureCallback)
|
||||
{
|
||||
assert(!userId.isEmpty());
|
||||
|
||||
this->makeRequest("channels/" + userId, {})
|
||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||
auto root = result.parseJson();
|
||||
|
||||
successCallback(root);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
void Kraken::getUser(QString userId, ResultCallback<KrakenUser> successCallback,
|
||||
KrakenFailureCallback failureCallback)
|
||||
{
|
||||
assert(!userId.isEmpty());
|
||||
|
||||
this->makeRequest("users/" + userId, {})
|
||||
.onSuccess([successCallback, failureCallback](auto result) -> Outcome {
|
||||
auto root = result.parseJson();
|
||||
|
||||
successCallback(root);
|
||||
|
||||
return Success;
|
||||
})
|
||||
.onError([failureCallback](auto result) {
|
||||
// TODO: make better xd
|
||||
failureCallback();
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
NetworkRequest Kraken::makeRequest(QString url, QUrlQuery urlQuery)
|
||||
{
|
||||
assert(!url.startsWith("/"));
|
||||
|
||||
if (this->clientId.isEmpty())
|
||||
{
|
||||
qDebug()
|
||||
<< "Kraken::makeRequest called without a client ID set BabyRage";
|
||||
}
|
||||
|
||||
const QString baseUrl("https://api.twitch.tv/kraken/");
|
||||
|
||||
QUrl fullUrl(baseUrl + url);
|
||||
|
||||
fullUrl.setQuery(urlQuery);
|
||||
|
||||
if (!this->oauthToken.isEmpty())
|
||||
{
|
||||
return NetworkRequest(fullUrl)
|
||||
.timeout(5 * 1000)
|
||||
.header("Accept", "application/vnd.twitchtv.v5+json")
|
||||
.header("Client-ID", this->clientId)
|
||||
.header("Authorization", "OAuth " + this->oauthToken);
|
||||
}
|
||||
|
||||
return NetworkRequest(fullUrl)
|
||||
.timeout(5 * 1000)
|
||||
.header("Accept", "application/vnd.twitchtv.v5+json")
|
||||
.header("Client-ID", this->clientId);
|
||||
}
|
||||
|
||||
void Kraken::update(QString clientId, QString oauthToken)
|
||||
{
|
||||
this->clientId = clientId;
|
||||
this->oauthToken = oauthToken;
|
||||
}
|
||||
|
||||
void Kraken::initialize()
|
||||
{
|
||||
assert(instance == nullptr);
|
||||
|
||||
instance = new Kraken();
|
||||
|
||||
getKraken()->update(getDefaultClientID(), "");
|
||||
}
|
||||
|
||||
Kraken *getKraken()
|
||||
{
|
||||
assert(instance != nullptr);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/NetworkRequest.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrlQuery>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
using KrakenFailureCallback = std::function<void()>;
|
||||
template <typename... T>
|
||||
using ResultCallback = std::function<void(T...)>;
|
||||
|
||||
struct KrakenChannel {
|
||||
const QString status;
|
||||
|
||||
KrakenChannel(QJsonObject jsonObject)
|
||||
: status(jsonObject.value("status").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct KrakenUser {
|
||||
const QString createdAt;
|
||||
|
||||
KrakenUser(QJsonObject jsonObject)
|
||||
: createdAt(jsonObject.value("created_at").toString())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Kraken final : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
// https://dev.twitch.tv/docs/v5/reference/users#follow-channel
|
||||
void getChannel(QString userId,
|
||||
ResultCallback<KrakenChannel> resultCallback,
|
||||
KrakenFailureCallback failureCallback);
|
||||
|
||||
// https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id
|
||||
void getUser(QString userId, ResultCallback<KrakenUser> resultCallback,
|
||||
KrakenFailureCallback failureCallback);
|
||||
|
||||
void update(QString clientId, QString oauthToken);
|
||||
|
||||
static void initialize();
|
||||
|
||||
private:
|
||||
NetworkRequest makeRequest(QString url, QUrlQuery urlQuery);
|
||||
|
||||
QString clientId;
|
||||
QString oauthToken;
|
||||
};
|
||||
|
||||
Kraken *getKraken();
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,125 @@
|
||||
# Twitch API
|
||||
this folder describes what sort of API requests we do, what permissions are required for the requests etc
|
||||
|
||||
## Kraken (V5)
|
||||
We use a bunch of Kraken (V5) in Chatterino2.
|
||||
|
||||
### Get User
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-by-id
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We implement this in `providers/twitch/api/Kraken.cpp getUser`
|
||||
Used in:
|
||||
* `UserInfoPopup` to get the "created at" date of a user
|
||||
|
||||
### Get Channel
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/channels#get-channel
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We implement this in `providers/twitch/api/Kraken.cpp getChannel`
|
||||
Used in:
|
||||
* `TwitchChannel::refreshTitle` to check the current stream title/game of offline channels
|
||||
|
||||
### Follow Channel
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#follow-channel
|
||||
Requires `user_follows_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We implement this API in `providers/twitch/TwitchAccount.cpp followUser`
|
||||
|
||||
### Unfollow Channel
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#unfollow-channel
|
||||
Requires `user_follows_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We implement this API in `providers/twitch/TwitchAccount.cpp unfollowUser`
|
||||
|
||||
|
||||
### Get Cheermotes
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/bits#get-cheermotes
|
||||
|
||||
Migration path: **Not checked**
|
||||
|
||||
* We implement this API in `providers/twitch/TwitchChannel.cpp` to resolve a chats available cheer emotes. This helps us parse incoming messages like `pajaCheer1000`
|
||||
|
||||
### Get User Block List
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-block-list
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp loadIgnores`
|
||||
|
||||
### Block User
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#block-user
|
||||
Requires `user_blocks_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp ignoreByID`
|
||||
|
||||
### Unblock User
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#unblock-user
|
||||
Requires `user_blocks_edit` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp unignoreByID`
|
||||
|
||||
### Get User Emotes
|
||||
URL: https://dev.twitch.tv/docs/v5/reference/users#get-user-emotes
|
||||
Requires `user_subscriptions` scope
|
||||
|
||||
Migration path: **Unknown**
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp loadEmotes` to figure out which emotes a user is allowed to use!
|
||||
|
||||
### AUTOMOD APPROVE
|
||||
**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp autoModAllow` to approve an automod deny/allow question
|
||||
|
||||
### AUTOMOD DENY
|
||||
**Unofficial** documentation: https://discuss.dev.twitch.tv/t/allowing-others-aka-bots-to-use-twitchbot-reject/8508/2
|
||||
|
||||
* We use this in `providers/twitch/TwitchAccount.cpp autoModDeny` to deny an automod deny/allow question
|
||||
|
||||
## Helix
|
||||
Full Helix API reference: https://dev.twitch.tv/docs/api/reference
|
||||
|
||||
### Get Users
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-users
|
||||
|
||||
* We implement this in `providers/twitch/api/Helix.cpp fetchUsers`.
|
||||
Used in:
|
||||
* `UserInfoPopup` to get ID and viewcount of username we clicked
|
||||
* `CommandController` to power any commands that need to get a user ID
|
||||
* `Toasts` to get the profile picture of a streamer who just went live
|
||||
* `TwitchAccount` ignore and unignore features to translate user name to user ID
|
||||
|
||||
### Get Users Follows
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-users-follows
|
||||
|
||||
* We implement this in `providers/twitch/api/Helix.cpp fetchUsersFollows`
|
||||
Used in:
|
||||
* `UserInfoPopup` to get number of followers a user has
|
||||
|
||||
### Get Streams
|
||||
URL: https://dev.twitch.tv/docs/api/reference#get-streams
|
||||
|
||||
* We implement this in `providers/twitch/api/Helix.cpp fetchStreams`
|
||||
Used in:
|
||||
* `TwitchChannel` to get live status, game, title, and viewer count of a channel
|
||||
* `NotificationController` to provide notifications for channels you might not have open in Chatterino, but are still interested in getting notifications for
|
||||
|
||||
## TMI
|
||||
The TMI api is undocumented.
|
||||
|
||||
### Get Chatters
|
||||
**Undocumented**
|
||||
|
||||
* We use this in `widgets/splits/Split.cpp showViewerList`
|
||||
* We use this in `providers/twitch/TwitchChannel.cpp refreshChatters`
|
||||
Reference in New Issue
Block a user