Refactor twitch-helpers

Added more http helpers, reordered parameters, and reduced code
duplication
This commit is contained in:
Przemek Pawlas 2023-11-26 14:15:06 +01:00 committed by WarmUpTill
parent 473e24cc42
commit 9397d50764
8 changed files with 259 additions and 143 deletions

View File

@ -190,7 +190,7 @@ void CategoryGrabber::Search(const std::string &)
std::string cursor;
httplib::Params params = {
{"first", "100"}, {"after", cursor}, {"query", _searchString}};
auto response = SendGetRequest(uri, path, *_token, params);
auto response = SendGetRequest(*_token, uri, path, params);
while (response.status == 200 && !_stop) {
cursor = ParseReply(response.data);
@ -200,7 +200,7 @@ void CategoryGrabber::Search(const std::string &)
params = {{"first", "100"},
{"after", cursor},
{"query", _searchString}};
response = SendGetRequest(uri, path, *_token, params);
response = SendGetRequest(*_token, uri, path, params);
emit CategoryCountUpdated(_categoryMap.size() - startCount);
}
}
@ -214,7 +214,7 @@ void CategoryGrabber::GetAll()
static std::string cursor;
httplib::Params params = {{"first", "100"}, {"after", cursor}};
auto response = SendGetRequest(uri, path, *_token, params);
auto response = SendGetRequest(*_token, uri, path, params);
while (response.status == 200 && !_stop) {
cursor = ParseReply(response.data);
@ -222,7 +222,7 @@ void CategoryGrabber::GetAll()
break; // End of category list
}
params = {{"first", "100"}, {"after", cursor}};
response = SendGetRequest(uri, path, *_token, params);
response = SendGetRequest(*_token, uri, path, params);
emit CategoryCountUpdated(_categoryMap.size());
}
}

View File

@ -35,8 +35,8 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
return it->second;
}
auto res = SendGetRequest("https://api.twitch.tv", "/helix/users",
token, {{"login", _name}});
auto res = SendGetRequest(token, "https://api.twitch.tv",
"/helix/users", {{"login", _name}});
if (res.status == 400) {
userIDCache[_name] = "invalid";
@ -112,8 +112,8 @@ TwitchChannel::GetLiveInfo(const TwitchToken &token)
httplib::Params params = {
{"first", "1"}, {"after", ""}, {"user_id", id}};
auto result = SendGetRequest("https://api.twitch.tv", "/helix/streams",
token, params, true);
auto result = SendGetRequest(token, "https://api.twitch.tv",
"/helix/streams", params, true);
if (result.status != 200) {
return {};
}
@ -153,8 +153,8 @@ std::optional<ChannelInfo> TwitchChannel::GetInfo(const TwitchToken &token)
httplib::Params params = {
{"first", "1"}, {"after", ""}, {"broadcaster_id", id}};
auto result = SendGetRequest("https://api.twitch.tv", "/helix/channels",
token, params, true);
auto result = SendGetRequest(token, "https://api.twitch.tv",
"/helix/channels", params, true);
if (result.status != 200) {
return {};
}

View File

@ -230,14 +230,16 @@ std::string EventSub::AddEventSubscribtion(std::shared_ptr<TwitchToken> token,
OBSDataAutoRelease postData = copyData(subscription.data);
setTransportData(postData.Get(), eventSub->_sessionID);
auto result = SendPostRequest(registerSubscriptionURL.data(),
registerSubscriptionPath.data(), *token,
auto result = SendPostRequest(*token, registerSubscriptionURL.data(),
registerSubscriptionPath.data(), {},
postData.Get());
if (result.status != 202) {
vblog(LOG_INFO, "failed to register Twitch EventSub (%d)",
result.status);
return "";
}
OBSDataArrayAutoRelease replyArray =
obs_data_get_array(result.data, "data");
OBSDataAutoRelease replyData = obs_data_array_item(replyArray, 0);

View File

@ -73,11 +73,10 @@ void MacroActionTwitch::SetStreamTitle(
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "title", _streamTitle.c_str());
auto result = SendPatchRequest(
"https://api.twitch.tv",
std::string("/helix/channels?broadcaster_id=") +
token->GetUserID(),
*token, data.Get());
auto result = SendPatchRequest(*token, "https://api.twitch.tv",
"/helix/channels",
{{"broadcaster_id", token->GetUserID()}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to set stream title! (%d)",
@ -95,11 +94,11 @@ void MacroActionTwitch::SetStreamCategory(
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "game_id",
std::to_string(_category.id).c_str());
auto result = SendPatchRequest(
"https://api.twitch.tv",
std::string("/helix/channels?broadcaster_id=") +
token->GetUserID(),
*token, data.Get());
auto result = SendPatchRequest(*token, "https://api.twitch.tv",
"/helix/channels",
{{"broadcaster_id", token->GetUserID()}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to set stream category! (%d)",
result.status);
@ -117,9 +116,8 @@ void MacroActionTwitch::CreateStreamMarker(
_markerDescription.c_str());
}
auto result = SendPostRequest("https://api.twitch.tv",
"/helix/streams/markers", *token,
data.Get());
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/streams/markers", {}, data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to create marker! (%d)", result.status);
@ -129,14 +127,12 @@ void MacroActionTwitch::CreateStreamMarker(
void MacroActionTwitch::CreateStreamClip(
const std::shared_ptr<TwitchToken> &token) const
{
OBSDataAutoRelease data = obs_data_create();
auto hasDelay = _clipHasDelay ? "true" : "false";
auto result = SendPostRequest(
"https://api.twitch.tv",
"/helix/clips?broadcaster_id=" + token->GetUserID() +
"&has_delay=" + hasDelay,
*token, data.Get());
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/clips",
{{"broadcaster_id", token->GetUserID()},
{"has_delay", hasDelay}});
if (result.status != 202) {
blog(LOG_INFO, "Failed to create clip! (%d)", result.status);
@ -149,9 +145,10 @@ void MacroActionTwitch::StartCommercial(
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "broadcaster_id", token->GetUserID().c_str());
obs_data_set_int(data, "length", _duration.Seconds());
auto result = SendPostRequest("https://api.twitch.tv",
"/helix/channels/commercial", *token,
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/channels/commercial", {},
data.Get());
if (result.status != 200) {
OBSDataArrayAutoRelease replyArray =
obs_data_get_array(result.data, "data");
@ -178,11 +175,10 @@ void MacroActionTwitch::SendChatAnnouncement(
announcementColorsTwitch.at(_announcementColor).c_str());
auto userId = token->GetUserID();
auto result =
SendPostRequest("https://api.twitch.tv",
"/helix/chat/announcements?broadcaster_id=" +
userId + "&moderator_id=" + userId,
*token, data.Get());
auto result = SendPostRequest(
*token, "https://api.twitch.tv", "/helix/chat/announcements",
{{"broadcaster_id", userId}, {"moderator_id", userId}},
data.Get());
if (result.status != 204) {
blog(LOG_INFO, "Failed to send chat announcement! (%d)",
@ -197,11 +193,10 @@ void MacroActionTwitch::SetChatEmoteOnlyMode(
obs_data_set_bool(data, "emote_mode", enable);
auto userId = token->GetUserID();
auto result =
SendPatchRequest("https://api.twitch.tv",
"/helix/chat/settings?broadcaster_id=" +
userId + "&moderator_id=" + userId,
*token, data.Get());
auto result = SendPatchRequest(
*token, "https://api.twitch.tv", "/helix/chat/settings",
{{"broadcaster_id", userId}, {"moderator_id", userId}},
data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to %s chat's emote-only mode! (%d)",
@ -216,8 +211,9 @@ void MacroActionTwitch::StartRaid(const std::shared_ptr<TwitchToken> &token)
token->GetUserID().c_str());
obs_data_set_string(data, "to_broadcaster_id",
_channel.GetUserID(*token).c_str());
auto result = SendPostRequest("https://api.twitch.tv", "/helix/raids",
*token, data.Get());
auto result = SendPostRequest(*token, "https://api.twitch.tv",
"/helix/raids", {}, data.Get());
if (result.status != 200) {
blog(LOG_INFO, "Failed to start raid! (%d)\n", result.status);
}

View File

@ -134,9 +134,9 @@ TwitchPointsRewardSelection::GetPointsRewards(
httplib::Params params = {
{"broadcaster_id", channel.GetUserID(*token)}};
auto response = SendGetRequest("https://api.twitch.tv",
auto response = SendGetRequest(*token, "https://api.twitch.tv",
"/helix/channel_points/custom_rewards",
*token, params);
params);
if (response.status != 200) {
blog(LOG_WARNING,

View File

@ -194,7 +194,7 @@ void TwitchToken::SetToken(const std::string &value)
{
_token = value;
auto res =
SendGetRequest("https://api.twitch.tv", "/helix/users", *this);
SendGetRequest(*this, "https://api.twitch.tv", "/helix/users");
if (res.status != 200) {
blog(LOG_WARNING, "failed to get Twitch user id from token!");
_userID = -1;

View File

@ -1,11 +1,18 @@
#include "twitch-helpers.hpp"
#include "token.hpp"
#include <log-helper.hpp>
namespace advss {
static constexpr std::string_view clientID = "ds5tt4ogliifsqc04mz3d3etnck3e5";
static const int cacheTimeoutSeconds = 10;
const char *GetClientID()
{
return clientID.data();
}
class Args {
public:
Args(const std::string &uri, const std::string &path,
@ -43,7 +50,7 @@ struct CacheEntry {
std::chrono::system_clock::now();
};
static bool chacheIsTooOld(const CacheEntry &cache)
static bool cacheIsTooOld(const CacheEntry &cache)
{
auto currentTime = std::chrono::system_clock::now();
auto diff = currentTime - cache.cacheTime;
@ -54,7 +61,7 @@ static bool cacheIsValid(const std::map<Args, CacheEntry> &cache,
const Args &args)
{
auto it = cache.find(args);
return it != cache.end() && !chacheIsTooOld(it->second);
return it != cache.end() && !cacheIsTooOld(it->second);
}
static httplib::Headers getTokenRequestHeaders(const std::string &token)
@ -65,166 +72,262 @@ static httplib::Headers getTokenRequestHeaders(const std::string &token)
};
}
RequestResult SendGetRequest(const std::string &uri, const std::string &path,
const TwitchToken &token,
static std::string getRequestBody(const OBSData &data)
{
auto json = obs_data_get_json(data);
return json ? json : "";
}
static RequestResult processResult(const httplib::Result &response,
const char *funcName)
{
if (!response) {
auto err = response.error();
blog(LOG_WARNING, "Twitch request failed in %s with error: %s",
funcName, httplib::to_string(err).c_str());
return {};
}
RequestResult result;
result.status = response->status;
if (response->body.empty()) {
return result;
}
OBSDataAutoRelease replyData =
obs_data_create_from_json(response->body.c_str());
result.data = replyData;
return result;
}
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params)
{
httplib::Client cli(uri);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto url = httplib::append_query_params(uri + path, params);
vblog(LOG_INFO, "Twitch GET request to %s began", url.c_str());
auto headers = getTokenRequestHeaders(*tokenStr);
auto response = cli.Get(path, params, headers);
if (!response) {
auto err = response.error();
blog(LOG_INFO, "%s failed - %s", __func__,
httplib::to_string(err).c_str());
return {};
}
RequestResult result;
result.status = response->status;
if (response->body.empty()) {
return result;
}
OBSDataAutoRelease replyData =
obs_data_create_from_json(response->body.c_str());
result.data = replyData;
return result;
return processResult(response, __func__);
}
RequestResult SendGetRequest(const std::string &uri, const std::string &path,
const TwitchToken &token,
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, bool useCache)
{
static std::map<Args, CacheEntry> cache;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto headers = getTokenRequestHeaders(*tokenStr);
Args args(uri, path, "", params, headers);
if (useCache && cacheIsValid(cache, args)) {
auto it = cache.find(args);
return it->second.result;
}
auto result = SendGetRequest(uri, path, token, params);
auto result = SendGetRequest(token, uri, path, params);
cache[args] = {result};
return result;
}
RequestResult SendPostRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data)
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data)
{
httplib::Client cli(uri);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto pathWithParams = httplib::append_query_params(path, params);
auto url = uri + pathWithParams;
vblog(LOG_INFO, "Twitch POST request to %s began", url.c_str());
auto headers = getTokenRequestHeaders(*tokenStr);
auto json = obs_data_get_json(data);
std::string body = json ? json : "";
auto response = cli.Post(path, headers, body, "application/json");
if (!response) {
auto err = response.error();
blog(LOG_INFO, "%s failed - %s", __func__,
httplib::to_string(err).c_str());
return {};
}
RequestResult result;
result.status = response->status;
if (response->body.empty()) {
return result;
}
OBSDataAutoRelease replyData =
obs_data_create_from_json(response->body.c_str());
result.data = replyData;
return result;
auto body = getRequestBody(data);
auto response =
cli.Post(pathWithParams, headers, body, "application/json");
return processResult(response, __func__);
}
RequestResult SendPostRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data,
bool useCache)
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache)
{
static std::map<Args, CacheEntry> cache;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto headers = getTokenRequestHeaders(*tokenStr);
auto jsonCstr = obs_data_get_json(data);
Args args(uri, path, jsonCstr ? jsonCstr : "", {}, headers);
auto body = getRequestBody(data);
Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) {
auto it = cache.find(args);
return it->second.result;
}
auto result = SendPostRequest(uri, path, token, data);
auto result = SendPostRequest(token, uri, path, params, data);
cache[args] = {result};
return result;
}
RequestResult SendPatchRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data)
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, const OBSData &data)
{
httplib::Client cli(uri);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto pathWithParams = httplib::append_query_params(path, params);
auto url = uri + pathWithParams;
vblog(LOG_INFO, "Twitch PUT request to %s began", url.c_str());
auto headers = getTokenRequestHeaders(*tokenStr);
auto json = obs_data_get_json(data);
std::string body = json ? json : "";
auto response = cli.Patch(path, headers, body, "application/json");
if (!response) {
auto err = response.error();
blog(LOG_INFO, "%s failed - %s", __func__,
httplib::to_string(err).c_str());
return {};
}
RequestResult result;
result.status = response->status;
if (response->body.empty()) {
return result;
}
OBSDataAutoRelease replyData =
obs_data_create_from_json(response->body.c_str());
result.data = replyData;
return result;
auto body = getRequestBody(data);
auto response =
cli.Put(pathWithParams, headers, body, "application/json");
return processResult(response, __func__);
}
RequestResult SendPatchRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data,
bool useCache)
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, const OBSData &data,
bool useCache)
{
static std::map<Args, CacheEntry> cache;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto headers = getTokenRequestHeaders(*tokenStr);
auto jsonCstr = obs_data_get_json(data);
Args args(uri, path, jsonCstr ? jsonCstr : "", {}, headers);
auto body = getRequestBody(data);
Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) {
auto it = cache.find(args);
return it->second.result;
}
auto result = SendPatchRequest(uri, path, token, data);
auto result = SendPutRequest(token, uri, path, params, data);
cache[args] = {result};
return result;
}
const char *GetClientID()
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data)
{
return clientID.data();
httplib::Client cli(uri);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto pathWithParams = httplib::append_query_params(path, params);
auto url = uri + pathWithParams;
vblog(LOG_INFO, "Twitch PATCH request to %s began", url.c_str());
auto headers = getTokenRequestHeaders(*tokenStr);
auto body = getRequestBody(data);
auto response =
cli.Patch(pathWithParams, headers, body, "application/json");
return processResult(response, __func__);
}
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache)
{
static std::map<Args, CacheEntry> cache;
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto headers = getTokenRequestHeaders(*tokenStr);
auto body = getRequestBody(data);
Args args(uri, path, body, params, headers);
if (useCache && cacheIsValid(cache, args)) {
auto it = cache.find(args);
return it->second.result;
}
auto result = SendPatchRequest(token, uri, path, params, data);
cache[args] = {result};
return result;
}
RequestResult SendDeleteRequest(const TwitchToken &token,
const std::string &uri, const std::string &path,
const httplib::Params &params)
{
httplib::Client cli(uri);
auto tokenStr = token.GetToken();
if (!tokenStr) {
return {};
}
auto pathWithParams = httplib::append_query_params(path, params);
auto url = uri + pathWithParams;
vblog(LOG_INFO, "Twitch DELETE request to %s began", url.c_str());
auto headers = getTokenRequestHeaders(*tokenStr);
auto response =
cli.Delete(pathWithParams, headers, "", "application/json");
return processResult(response, __func__);
}
} // namespace advss

View File

@ -13,29 +13,44 @@ struct RequestResult {
OBSData data = nullptr;
};
// These functions do *not* use or create RequestResult cache entries
const char *GetClientID();
RequestResult SendGetRequest(const std::string &uri, const std::string &path,
const TwitchToken &token,
const httplib::Params & = {});
RequestResult SendPostRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data);
RequestResult SendPatchRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data);
// These functions do *not* use or create RequestResult cache entries
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params = {});
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params = {},
const OBSData &data = nullptr);
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params = {},
const OBSData &data = nullptr);
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params = {},
const OBSData &data = nullptr);
RequestResult SendDeleteRequest(const TwitchToken &token,
const std::string &uri, const std::string &path,
const httplib::Params &params = {});
// These functions will cache the RequestResult for 10s
// Note that the cache will be reported as a "memory leak" on OBS shutdown
RequestResult SendGetRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const httplib::Params &,
RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, bool useCache);
RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache);
RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params, const OBSData &data,
bool useCache);
RequestResult SendPostRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data,
bool useCache);
RequestResult SendPatchRequest(const std::string &uri, const std::string &path,
const TwitchToken &token, const OBSData &data,
bool useCache);
const char *GetClientID();
RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
const std::string &path,
const httplib::Params &params,
const OBSData &data, bool useCache);
} // namespace advss