Refactor NetworkRequest class

Add followUser and unfollowUser methods to TwitchAccount
This commit is contained in:
Rasmus Karlsson
2018-07-07 11:08:57 +00:00
parent cada32edfd
commit 6a418e6e59
27 changed files with 835 additions and 669 deletions
+21 -11
View File
@@ -21,11 +21,12 @@ void BTTVEmotes::loadGlobalEmotes()
{
QString url("https://api.betterttv.net/2/emotes");
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.setTimeout(30000);
req.setUseQuickLoadCache(true);
req.getJSON([this](QJsonObject &root) {
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.setUseQuickLoadCache(true);
request.onSuccess([this](auto result) {
auto root = result.parseJson();
auto emotes = root.value("emotes").toArray();
QString urlTemplate = "https:" + root.value("urlTemplate").toString();
@@ -49,7 +50,11 @@ void BTTVEmotes::loadGlobalEmotes()
}
this->globalEmoteCodes = codes;
return true;
});
request.execute();
}
void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<EmoteMap> _map)
@@ -60,15 +65,16 @@ void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<Emo
Log("Request bttv channel emotes for {}", channelName);
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.setTimeout(3000);
req.setUseQuickLoadCache(true);
req.getJSON([this, channelName, _map](QJsonObject &rootNode) {
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(3000);
request.setUseQuickLoadCache(true);
request.onSuccess([this, channelName, _map](auto result) {
auto rootNode = result.parseJson();
auto map = _map.lock();
if (_map.expired()) {
return;
return false;
}
map->clear();
@@ -110,7 +116,11 @@ void BTTVEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<Emo
}
this->channelEmoteCodes[channelName] = codes;
return true;
});
request.execute();
}
} // namespace chatterino
+21 -11
View File
@@ -46,11 +46,12 @@ void FFZEmotes::loadGlobalEmotes()
{
QString url("https://api.frankerfacez.com/v1/set/global");
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.setTimeout(30000);
req.setUseQuickLoadCache(true);
req.getJSON([this](QJsonObject &root) {
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.setUseQuickLoadCache(true);
request.onSuccess([this](auto result) {
auto root = result.parseJson();
auto sets = root.value("sets").toObject();
std::vector<QString> codes;
@@ -75,7 +76,11 @@ void FFZEmotes::loadGlobalEmotes()
this->globalEmoteCodes = codes;
}
return true;
});
request.execute();
}
void FFZEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<EmoteMap> _map)
@@ -84,15 +89,16 @@ void FFZEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<Emot
QString url("https://api.frankerfacez.com/v1/room/" + channelName);
NetworkRequest req(url);
req.setCaller(QThread::currentThread());
req.setTimeout(3000);
req.setUseQuickLoadCache(true);
req.getJSON([this, channelName, _map](QJsonObject &rootNode) {
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(3000);
request.setUseQuickLoadCache(true);
request.onSuccess([this, channelName, _map](auto result) {
auto rootNode = result.parseJson();
auto map = _map.lock();
if (_map.expired()) {
return;
return false;
}
map->clear();
@@ -128,7 +134,11 @@ void FFZEmotes::loadChannelEmotes(const QString &channelName, std::weak_ptr<Emot
this->channelEmoteCodes[channelName] = codes;
}
return true;
});
request.execute();
}
} // namespace chatterino
@@ -0,0 +1,70 @@
#include "providers/twitch/PartialTwitchUser.hpp"
#include "common/NetworkRequest.hpp"
#include "debug/Log.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include <cassert>
namespace chatterino {
PartialTwitchUser PartialTwitchUser::byName(const QString &username)
{
PartialTwitchUser user;
user.username_ = username;
return user;
}
PartialTwitchUser PartialTwitchUser::byId(const QString &id)
{
PartialTwitchUser user;
user.id_ = id;
return user;
}
void PartialTwitchUser::getId(std::function<void(QString)> successCallback, const QObject *caller)
{
assert(!this->username_.isEmpty());
if (caller == nullptr) {
caller = QThread::currentThread();
}
NetworkRequest request("https://api.twitch.tv/kraken/users?login=" + this->username_);
request.setCaller(caller);
request.makeAuthorizedV5(getDefaultClientID());
request.onSuccess([successCallback](auto result) {
auto root = result.parseJson();
if (!root.value("users").isArray()) {
Log("API Error while getting user id, users is not an array");
return false;
}
auto users = root.value("users").toArray();
if (users.size() != 1) {
Log("API Error while getting user id, users array size is not 1");
return false;
}
if (!users[0].isObject()) {
Log("API Error while getting user id, first user is not an object");
return false;
}
auto firstUser = users[0].toObject();
auto id = firstUser.value("_id");
if (!id.isString()) {
Log("API Error: while getting user id, first user object `_id` key is not a "
"string");
return false;
}
successCallback(id.toString());
return true;
});
request.execute();
}
} // namespace chatterino
@@ -0,0 +1,25 @@
#pragma once
#include <QObject>
#include <QString>
#include <functional>
namespace chatterino {
// Experimental class to test a method of calling APIs on twitch users
class PartialTwitchUser
{
PartialTwitchUser() = default;
QString username_;
QString id_;
public:
static PartialTwitchUser byName(const QString &username);
static PartialTwitchUser byId(const QString &id);
void getId(std::function<void(QString)> successCallback, const QObject *caller = nullptr);
};
} // namespace chatterino
+70 -19
View File
@@ -3,6 +3,7 @@
#include "common/NetworkRequest.hpp"
#include "common/UrlFetch.hpp"
#include "debug/Log.hpp"
#include "providers/twitch/PartialTwitchUser.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "util/RapidjsonHelpers.hpp"
@@ -76,10 +77,10 @@ void TwitchAccount::loadIgnores()
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks");
NetworkRequest req(url);
req.setRequestType(NetworkRequest::GetRequest);
req.setCaller(QThread::currentThread());
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
req.onSuccess([=](const rapidjson::Document &document) {
req.onSuccess([=](auto result) {
auto document = result.parseRapidJson();
if (!document.IsObject()) {
return false;
}
@@ -125,9 +126,11 @@ void TwitchAccount::loadIgnores()
void TwitchAccount::ignore(const QString &targetName,
std::function<void(IgnoreResult, const QString &)> onFinished)
{
twitchApiGetUserID(targetName, QThread::currentThread(), [=](QString targetUserID) {
this->ignoreByID(targetUserID, targetName, onFinished); //
});
const auto onIdFetched = [this, targetName, onFinished](QString targetUserId) {
this->ignoreByID(targetUserId, targetName, onFinished); //
};
PartialTwitchUser::byName(this->userName_).getId(onIdFetched);
}
void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targetName,
@@ -136,8 +139,7 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" +
targetUserID);
NetworkRequest req(url);
req.setRequestType(NetworkRequest::PutRequest);
NetworkRequest req(url, NetworkRequestType::Put);
req.setCaller(QThread::currentThread());
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
@@ -148,7 +150,8 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe
return true;
});
req.onSuccess([=](const rapidjson::Document &document) {
req.onSuccess([=](auto result) {
auto document = result.parseRapidJson();
if (!document.IsObject()) {
onFinished(IgnoreResult_Failed, "Bad JSON data while ignoring user " + targetName);
return false;
@@ -190,9 +193,11 @@ void TwitchAccount::ignoreByID(const QString &targetUserID, const QString &targe
void TwitchAccount::unignore(const QString &targetName,
std::function<void(UnignoreResult, const QString &message)> onFinished)
{
twitchApiGetUserID(targetName, QThread::currentThread(), [=](QString targetUserID) {
this->unignoreByID(targetUserID, targetName, onFinished); //
});
const auto onIdFetched = [this, targetName, onFinished](QString targetUserId) {
this->unignoreByID(targetUserId, targetName, onFinished); //
};
PartialTwitchUser::byName(this->userName_).getId(onIdFetched);
}
void TwitchAccount::unignoreByID(
@@ -202,8 +207,7 @@ void TwitchAccount::unignoreByID(
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/blocks/" +
targetUserID);
NetworkRequest req(url);
req.setRequestType(NetworkRequest::DeleteRequest);
NetworkRequest req(url, NetworkRequestType::Delete);
req.setCaller(QThread::currentThread());
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
@@ -215,7 +219,8 @@ void TwitchAccount::unignoreByID(
return true;
});
req.onSuccess([=](const rapidjson::Document &document) {
req.onSuccess([=](auto result) {
auto document = result.parseRapidJson();
TwitchUser ignoredUser;
ignoredUser.id = targetUserID;
{
@@ -238,7 +243,6 @@ void TwitchAccount::checkFollow(const QString targetUserID,
targetUserID);
NetworkRequest req(url);
req.setRequestType(NetworkRequest::GetRequest);
req.setCaller(QThread::currentThread());
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
@@ -252,7 +256,8 @@ void TwitchAccount::checkFollow(const QString targetUserID,
return true;
});
req.onSuccess([=](const rapidjson::Document &document) {
req.onSuccess([=](auto result) {
auto document = result.parseRapidJson();
onFinished(FollowResult_Following);
return true;
});
@@ -260,6 +265,53 @@ void TwitchAccount::checkFollow(const QString targetUserID,
req.execute();
}
void TwitchAccount::followUser(const QString userID, std::function<void()> successCallback)
{
QUrl requestUrl("https://api.twitch.tv/kraken/users/" + this->getUserId() +
"/follows/channels/" + userID);
NetworkRequest request(requestUrl, NetworkRequestType::Put);
request.setCaller(QThread::currentThread());
request.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
// TODO: Properly check result of follow request
request.onSuccess([successCallback](auto result) {
successCallback();
return true;
});
request.execute();
}
void TwitchAccount::unfollowUser(const QString userID, std::function<void()> successCallback)
{
QUrl requestUrl("https://api.twitch.tv/kraken/users/" + this->getUserId() +
"/follows/channels/" + userID);
NetworkRequest request(requestUrl, NetworkRequestType::Delete);
request.setCaller(QThread::currentThread());
request.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
request.onError([successCallback](int code) {
if (code >= 200 && code <= 299) {
successCallback();
}
return true;
});
request.onSuccess([successCallback](const auto &document) {
successCallback();
return true;
});
request.execute();
}
std::set<TwitchUser> TwitchAccount::getIgnores() const
{
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
@@ -282,7 +334,6 @@ void TwitchAccount::loadEmotes(std::function<void(const rapidjson::Document &)>
QString url("https://api.twitch.tv/kraken/users/" + this->getUserId() + "/emotes");
NetworkRequest req(url);
req.setRequestType(NetworkRequest::GetRequest);
req.setCaller(QThread::currentThread());
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
@@ -297,8 +348,8 @@ void TwitchAccount::loadEmotes(std::function<void(const rapidjson::Document &)>
return true;
});
req.onSuccess([=](const rapidjson::Document &document) {
cb(document);
req.onSuccess([=](auto result) {
cb(result.parseRapidJson());
return true;
});
+2
View File
@@ -65,6 +65,8 @@ public:
std::function<void(UnignoreResult, const QString &message)> onFinished);
void checkFollow(const QString targetUserID, std::function<void(FollowResult)> onFinished);
void followUser(const QString userID, std::function<void()> successCallback);
void unfollowUser(const QString userID, std::function<void()> successCallback);
std::set<TwitchUser> getIgnores() const;
+40 -13
View File
@@ -2,9 +2,11 @@
#include "common/Common.hpp"
#include "common/UrlFetch.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "debug/Log.hpp"
#include "messages/Message.hpp"
#include "providers/twitch/PubsubClient.hpp"
#include "providers/twitch/TwitchCommon.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp"
@@ -82,8 +84,16 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection
}
}
twitchApiGet("https://tmi.twitch.tv/group/user/" + this->name + "/chatters",
QThread::currentThread(), refreshChatters);
NetworkRequest request("https://tmi.twitch.tv/group/user/" + this->name + "/chatters");
request.setCaller(QThread::currentThread());
request.onSuccess([refreshChatters](auto result) {
refreshChatters(result.parseJson()); //
return true;
});
request.execute();
};
doRefreshChatters();
@@ -153,7 +163,7 @@ void TwitchChannel::sendMessage(const QString &message)
// Do last message processing
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
parsedMessage.trim();
parsedMessage = parsedMessage.trimmed();
if (parsedMessage.isEmpty()) {
return;
@@ -325,23 +335,26 @@ void TwitchChannel::refreshLiveStatus()
std::weak_ptr<Channel> weak = this->shared_from_this();
twitchApiGet2(url, QThread::currentThread(), false, [weak](const rapidjson::Document &d) {
auto request = makeGetStreamRequest(this->roomID, QThread::currentThread());
request.onSuccess([weak](auto result) {
auto d = result.parseRapidJson();
ChannelPtr shared = weak.lock();
if (!shared) {
return;
return false;
}
TwitchChannel *channel = dynamic_cast<TwitchChannel *>(shared.get());
if (!d.IsObject()) {
Log("[TwitchChannel:refreshLiveStatus] root is not an object");
return;
return false;
}
if (!d.HasMember("stream")) {
Log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
return;
return false;
}
const auto &stream = d["stream"];
@@ -349,21 +362,21 @@ void TwitchChannel::refreshLiveStatus()
if (!stream.IsObject()) {
// Stream is offline (stream is most likely null)
channel->setLive(false);
return;
return false;
}
if (!stream.HasMember("viewers") || !stream.HasMember("game") ||
!stream.HasMember("channel") || !stream.HasMember("created_at")) {
Log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
channel->setLive(false);
return;
return false;
}
const rapidjson::Value &streamChannel = stream["channel"];
if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) {
Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in channel");
return;
return false;
}
// Stream is live
@@ -400,7 +413,11 @@ void TwitchChannel::refreshLiveStatus()
// Signal all listeners that the stream status has been updated
channel->updateLiveInfo.invoke();
return true;
});
request.execute();
}
void TwitchChannel::startRefreshLiveStatusTimer(int intervalMS)
@@ -423,13 +440,18 @@ void TwitchChannel::fetchRecentMessages()
static QString genericURL =
"https://tmi.twitch.tv/api/rooms/%1/recent_messages?client_id=" + getDefaultClientID();
NetworkRequest request(genericURL.arg(this->roomID));
request.makeAuthorizedV5(getDefaultClientID());
request.setCaller(QThread::currentThread());
std::weak_ptr<Channel> weak = this->shared_from_this();
twitchApiGet(genericURL.arg(roomID), QThread::currentThread(), [weak](QJsonObject obj) {
request.onSuccess([weak](auto result) {
auto obj = result.parseJson();
ChannelPtr shared = weak.lock();
if (!shared) {
return;
return false;
}
auto channel = dynamic_cast<TwitchChannel *>(shared.get());
@@ -439,7 +461,7 @@ void TwitchChannel::fetchRecentMessages()
QJsonArray msgArray = obj.value("messages").toArray();
if (msgArray.empty()) {
return;
return false;
}
std::vector<MessagePtr> messages;
@@ -455,8 +477,13 @@ void TwitchChannel::fetchRecentMessages()
messages.push_back(builder.build());
}
}
channel->addMessagesAtStart(messages);
return true;
});
request.execute();
}
} // namespace chatterino
+5 -39
View File
@@ -45,39 +45,6 @@ QString cleanUpCode(const QString &dirtyEmoteCode)
return cleanCode;
}
void loadSetData(std::shared_ptr<TwitchEmotes::EmoteSet> emoteSet)
{
Log("Load twitch emote set data for {}", emoteSet->key);
NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" + emoteSet->key +
"/");
req.setRequestType(NetworkRequest::GetRequest);
req.onError([](int errorCode) -> bool {
Log("Emote sets on ERROR {}", errorCode);
return true;
});
req.onSuccess([emoteSet](const rapidjson::Document &root) -> bool {
Log("Emote sets on success");
if (!root.IsObject()) {
return false;
}
std::string emoteSetID;
QString channelName;
if (!rj::getSafe(root, "channel_name", channelName)) {
return false;
}
emoteSet->channelName = channelName;
return true;
});
req.execute();
}
} // namespace
TwitchEmotes::TwitchEmotes()
@@ -165,7 +132,7 @@ void TwitchEmotes::refresh(const std::shared_ptr<TwitchAccount> &user)
emoteSet->key = emoteSetJSON.name.GetString();
loadSetData(emoteSet);
this->loadSetData(emoteSet);
for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray()) {
if (!emoteJSON.IsObject()) {
@@ -221,18 +188,17 @@ void TwitchEmotes::loadSetData(std::shared_ptr<TwitchEmotes::EmoteSet> emoteSet)
return;
}
Log("Load twitch emote set data for {}..", emoteSet->key);
NetworkRequest req("https://braize.pajlada.com/chatterino/twitchemotes/set/" + emoteSet->key +
"/");
req.setRequestType(NetworkRequest::GetRequest);
req.setUseQuickLoadCache(true);
req.onError([](int errorCode) -> bool {
Log("Emote sets on ERROR {}", errorCode);
Log("Error code {} while loading emote set data", errorCode);
return true;
});
req.onSuccess([emoteSet](const rapidjson::Document &root) -> bool {
req.onSuccess([emoteSet](auto result) -> bool {
auto root = result.parseRapidJson();
if (!root.IsObject()) {
return false;
}