From 776ce8bdbc3f7d5e2aa3767af6b1699a600b8d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= <44851575+zneix@users.noreply.github.com> Date: Sun, 4 Oct 2020 18:32:52 +0200 Subject: [PATCH] Added subage and followage to usercard (#2023) * 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 --- CHANGELOG.md | 1 + chatterino.pro | 2 + src/main.cpp | 2 + src/providers/IvrApi.cpp | 59 +++++++++++++++++++++++++++ src/providers/IvrApi.hpp | 48 ++++++++++++++++++++++ src/providers/twitch/api/Helix.cpp | 4 +- src/providers/twitch/api/Kraken.hpp | 1 - src/widgets/dialogs/UserInfoPopup.cpp | 58 +++++++++++++++++++++++--- src/widgets/dialogs/UserInfoPopup.hpp | 1 + 9 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 src/providers/IvrApi.cpp create mode 100644 src/providers/IvrApi.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index ee104f69..325ba5ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Minor: Added followage and subage information to usercard. (#2023) - Minor: Added an option to only open channels specified in command line with `-c` parameter. You can also use `--help` to display short help message (#1940) - Minor: Added customizable timeout buttons to the user info popup - Minor: Deprecate loading of "v1" window layouts. If you haven't updated Chatterino in more than 2 years, there's a chance you will lose your window layout. diff --git a/chatterino.pro b/chatterino.pro index 4a1d7f10..cbd5c987 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -175,6 +175,7 @@ SOURCES += \ src/providers/irc/IrcConnection2.cpp \ src/providers/irc/IrcMessageBuilder.cpp \ src/providers/irc/IrcServer.cpp \ + src/providers/IvrApi.cpp \ src/providers/LinkResolver.cpp \ src/providers/twitch/ChannelPointReward.cpp \ src/providers/twitch/api/Helix.cpp \ @@ -389,6 +390,7 @@ HEADERS += \ src/providers/irc/IrcConnection2.hpp \ src/providers/irc/IrcMessageBuilder.hpp \ src/providers/irc/IrcServer.hpp \ + src/providers/IvrApi.hpp \ src/providers/LinkResolver.hpp \ src/providers/twitch/ChannelPointReward.hpp \ src/providers/twitch/api/Helix.hpp \ diff --git a/src/main.cpp b/src/main.cpp index 4202654d..72c577e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include "common/Args.hpp" #include "common/Modes.hpp" #include "common/Version.hpp" +#include "providers/IvrApi.hpp" #include "providers/twitch/api/Helix.hpp" #include "providers/twitch/api/Kraken.hpp" #include "singletons/Paths.hpp" @@ -45,6 +46,7 @@ int main(int argc, char **argv) } else { + IvrApi::initialize(); Helix::initialize(); Kraken::initialize(); diff --git a/src/providers/IvrApi.cpp b/src/providers/IvrApi.cpp new file mode 100644 index 00000000..e2163ecf --- /dev/null +++ b/src/providers/IvrApi.cpp @@ -0,0 +1,59 @@ +#include "IvrApi.hpp" + +#include "common/Outcome.hpp" + +#include + +namespace chatterino { + +static IvrApi *instance = nullptr; + +void IvrApi::getSubage(QString userName, QString channelName, + ResultCallback successCallback, + IvrFailureCallback failureCallback) +{ + assert(!userName.isEmpty() && !channelName.isEmpty()); + + this->makeRequest("twitch/subage/" + userName + "/" + channelName, {}) + .onSuccess([successCallback, failureCallback](auto result) -> Outcome { + auto root = result.parseJson(); + + successCallback(root); + + return Success; + }) + .onError([failureCallback](NetworkResult result) { + qDebug() << "Failed IVR API Call!" << result.status() + << QString(result.getData()); + failureCallback(); + }) + .execute(); +} + +NetworkRequest IvrApi::makeRequest(QString url, QUrlQuery urlQuery) +{ + assert(!url.startsWith("/")); + + const QString baseUrl("https://api.ivr.fi/"); + QUrl fullUrl(baseUrl + url); + fullUrl.setQuery(urlQuery); + + return NetworkRequest(fullUrl).timeout(5 * 1000).header("Accept", + "application/json"); +} + +void IvrApi::initialize() +{ + assert(instance == nullptr); + + instance = new IvrApi(); +} + +IvrApi *getIvr() +{ + assert(instance != nullptr); + + return instance; +} + +} // namespace chatterino diff --git a/src/providers/IvrApi.hpp b/src/providers/IvrApi.hpp new file mode 100644 index 00000000..7cf73e2c --- /dev/null +++ b/src/providers/IvrApi.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "common/NetworkRequest.hpp" +#include "messages/Link.hpp" + +#include + +namespace chatterino { + +using IvrFailureCallback = std::function; +template +using ResultCallback = std::function; + +struct IvrSubage { + const bool isSubHidden; + const bool isSubbed; + const QString subTier; + const int totalSubMonths; + const QString followingSince; + + IvrSubage(QJsonObject root) + : isSubHidden(root.value("hidden").toBool()) + , isSubbed(root.value("subscribed").toBool()) + , subTier(root.value("meta").toObject().value("tier").toString()) + , totalSubMonths( + root.value("cumulative").toObject().value("months").toInt()) + , followingSince(root.value("followedAt").toString()) + { + } +}; + +class IvrApi final : boost::noncopyable +{ +public: + // https://api.ivr.fi/docs#tag/Twitch/paths/~1twitch~1subage~1{username}~1{channel}/get + void getSubage(QString userName, QString channelName, + ResultCallback resultCallback, + IvrFailureCallback failureCallback); + + static void initialize(); + +private: + NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); +}; + +IvrApi *getIvr(); + +} // namespace chatterino diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index 974b6212..73c2923c 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -52,12 +52,12 @@ void Helix::fetchUsers(QStringList userIds, QStringList userLogins, .execute(); } -void Helix::getUserByName(QString userId, +void Helix::getUserByName(QString userName, ResultCallback successCallback, HelixFailureCallback failureCallback) { QStringList userIds; - QStringList userLogins{userId}; + QStringList userLogins{userName}; this->fetchUsers( userIds, userLogins, diff --git a/src/providers/twitch/api/Kraken.hpp b/src/providers/twitch/api/Kraken.hpp index 047173d6..1a5f4013 100644 --- a/src/providers/twitch/api/Kraken.hpp +++ b/src/providers/twitch/api/Kraken.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index e810f458..3b45891f 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -6,6 +6,7 @@ #include "controllers/accounts/AccountController.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "messages/Message.hpp" +#include "providers/IvrApi.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/api/Helix.hpp" #include "providers/twitch/api/Kraken.hpp" @@ -140,9 +141,9 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically) QUrl("https://twitch.tv/" + this->userName_.toLower())); }); - // items on the right auto vbox = head.emplace(); { + // items on the right { auto box = vbox.emplace() .withoutMargin() @@ -150,18 +151,23 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically) this->ui_.nameLabel = addCopyableLabel(box); this->ui_.nameLabel->setFontStyle(FontStyle::UiMediumBold); box->addStretch(1); - this->ui_.userIDLabel = addCopyableLabel(box); auto palette = QPalette(); palette.setColor(QPalette::WindowText, QColor("#aaa")); + this->ui_.userIDLabel = addCopyableLabel(box); this->ui_.userIDLabel->setPalette(palette); } + // items on the left vbox.emplace