From ebbc900755b09862be95d29d2a02b8abd1792a3a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 12 Jun 2019 15:10:41 -0400 Subject: added a few helper classes for user accounts and stats moved the api label to the status bar refactored a bunch of copy/pasted code in NexusInterface to use shouldThrottle() and throttledWarning() --- src/nexusinterface.cpp | 314 +++++++++++++++++++++++++++++++------------------ 1 file changed, 201 insertions(+), 113 deletions(-) (limited to 'src/nexusinterface.cpp') diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index c677add0..8362143a 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -38,6 +38,82 @@ using namespace MOBase; using namespace MOShared; +void throttledWarning(const APIUserAccount& user) +{ + qCritical() << + QString( + "You have fewer than %1 requests remaining (%2). Only downloads and " + "login validation are being allowed.") + .arg(APIUserAccount::ThrottleThreshold) + .arg(user.remainingRequests()); +} + + +APIUserAccount::APIUserAccount() + : m_type(APIUserAccountTypes::None) +{ +} + +const QString& APIUserAccount::id() const +{ + return m_id; +} + +const QString& APIUserAccount::name() const +{ + return m_name; +} + +APIUserAccountTypes APIUserAccount::type() const +{ + return m_type; +} + +const APILimits& APIUserAccount::limits() const +{ + return m_limits; +} + +APIUserAccount& APIUserAccount::id(const QString& id) +{ + m_id = id; + return *this; +} + +APIUserAccount& APIUserAccount::name(const QString& name) +{ + m_name = name; + return *this; +} + +APIUserAccount& APIUserAccount::type(APIUserAccountTypes type) +{ + m_type = type; + return *this; +} + +APIUserAccount& APIUserAccount::limits(const APILimits& limits) +{ + m_limits = limits; + return *this; +} + +int APIUserAccount::remainingRequests() const +{ + return m_limits.remainingDailyRequests + m_limits.remainingHourlyRequests; +} + +bool APIUserAccount::shouldThrottle() const +{ + return (remainingRequests() < ThrottleThreshold); +} + +bool APIUserAccount::exhausted() const +{ + return (remainingRequests() <= 0); +} + + NexusBridge::NexusBridge(PluginContainer *pluginContainer, const QString &subModule) : m_Interface(NexusInterface::instance(pluginContainer)) , m_SubModule(subModule) @@ -179,15 +255,39 @@ void NexusBridge::nxmRequestFailed(QString gameName, int modID, int fileID, QVar QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); +APILimits NexusInterface::defaultAPILimits() +{ + // https://app.swaggerhub.com/apis-docs/NexusMods/nexus-mods_public_api_params_in_form_data/1.0#/ + const int MaxDaily = 2500; + const int MaxHourly = 100; + + APILimits limits; + + limits.maxDailyRequests = MaxDaily; + limits.remainingDailyRequests = MaxDaily; + limits.maxHourlyRequests = MaxHourly; + limits.remainingHourlyRequests = MaxHourly; + + return limits; +} + +APILimits NexusInterface::parseLimits(const QNetworkReply* reply) +{ + APILimits limits; + + limits.maxDailyRequests = reply->rawHeader("x-rl-daily-limit").toInt(); + limits.remainingDailyRequests = reply->rawHeader("x-rl-daily-remaining").toInt(); + limits.maxHourlyRequests = reply->rawHeader("x-rl-hourly-limit").toInt(); + limits.remainingHourlyRequests = reply->rawHeader("x-rl-hourly-remaining").toInt(); + + return limits; +} + + NexusInterface::NexusInterface(PluginContainer *pluginContainer) : m_PluginContainer(pluginContainer) - , m_RemainingDailyRequests(2500) - , m_RemainingHourlyRequests(100) - , m_MaxDailyRequests(2500) - , m_MaxHourlyRequests(100) - , m_IsPremium(false) - , m_UserID(0) { + m_User.limits(defaultAPILimits()); m_MOVersion = createVersionInfo(); m_AccessManager = new NXMAccessManager(this, m_MOVersion.displayString(3)); @@ -222,15 +322,10 @@ void NexusInterface::loginCompleted() nextRequest(); } -void NexusInterface::setRateMax(const QString&, int userId, bool isPremium, std::tuple limits) +void NexusInterface::setUserAccount(const APIUserAccount& user) { - m_RemainingDailyRequests = std::get<0>(limits); - m_MaxDailyRequests = std::get<1>(limits); - m_RemainingHourlyRequests = std::get<2>(limits); - m_MaxHourlyRequests = std::get<3>(limits); - m_IsPremium = isPremium; - m_UserID = userId; - emit requestsChanged(m_RequestQueue.size(), limits); + m_User = user; + emit requestsChanged(stats(), m_User); } void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) @@ -369,70 +464,70 @@ int NexusInterface::requestDescription(QString gameName, int modID, QObject *rec int NexusInterface::requestModInfo(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { - NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_MODINFO, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); + if (m_User.shouldThrottle()) { + throttledWarning(m_User); + return -1; + } - connect(this, SIGNAL(nxmModInfoAvailable(QString, int, QVariant, QVariant, int)), - receiver, SLOT(nxmModInfoAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); + NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_MODINFO, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmModInfoAvailable(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmModInfoAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") - .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); - return -1; + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; } int NexusInterface::requestUpdateInfo(QString gameName, NexusInterface::UpdatePeriod period, QObject *receiver, QVariant userData, const QString &subModule, const MOBase::IPluginGame *game) { - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { - NXMRequestInfo requestInfo(period, NXMRequestInfo::TYPE_CHECKUPDATES, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); + if (m_User.shouldThrottle()) { + throttledWarning(m_User); + return -1; + } - connect(this, SIGNAL(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), - receiver, SLOT(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), Qt::UniqueConnection); + NXMRequestInfo requestInfo(period, NXMRequestInfo::TYPE_CHECKUPDATES, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), + receiver, SLOT(nxmUpdateInfoAvailable(QString, QVariant, QVariant, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") - .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); - return -1; + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; } int NexusInterface::requestUpdates(const int &modID, QObject *receiver, QVariant userData, QString gameName, const QString &subModule) { - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { - IPluginGame *game = getGame(gameName); - if (game == nullptr) { - qCritical("requestUpdates can't find plugin for %s", qUtf8Printable(gameName)); - return -1; - } + if (m_User.shouldThrottle()) { + throttledWarning(m_User); + return -1; + } + + IPluginGame *game = getGame(gameName); + if (game == nullptr) { + qCritical("requestUpdates can't find plugin for %s", qUtf8Printable(gameName)); + return -1; + } - NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); + NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmUpdatesAvailable(QString, int, QVariant, QVariant, int)), - receiver, SLOT(nxmUpdatesAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmUpdatesAvailable(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmUpdatesAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") - .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); - return -1; + nextRequest(); + return requestInfo.m_ID; } @@ -527,23 +622,23 @@ int NexusInterface::requestEndorsementInfo(QObject *receiver, QVariant userData, int NexusInterface::requestToggleEndorsement(QString gameName, int modID, QString modVersion, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { - NXMRequestInfo requestInfo(modID, modVersion, NXMRequestInfo::TYPE_TOGGLEENDORSEMENT, userData, subModule, game); - requestInfo.m_Endorse = endorse; - m_RequestQueue.enqueue(requestInfo); + if (m_User.shouldThrottle()) { + throttledWarning(m_User); + return -1; + } - connect(this, SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), - receiver, SLOT(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); + NXMRequestInfo requestInfo(modID, modVersion, NXMRequestInfo::TYPE_TOGGLEENDORSEMENT, userData, subModule, game); + requestInfo.m_Endorse = endorse; + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") - .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); - return -1; + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; } int NexusInterface::requestTrackingInfo(QObject *receiver, QVariant userData, const QString &subModule) @@ -564,23 +659,23 @@ int NexusInterface::requestTrackingInfo(QObject *receiver, QVariant userData, co int NexusInterface::requestToggleTracking(QString gameName, int modID, bool track, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) >= 200) { - NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_TOGGLETRACKING, userData, subModule, game); - requestInfo.m_Track = track; - m_RequestQueue.enqueue(requestInfo); + if (m_User.shouldThrottle()) { + throttledWarning(m_User); + return -1; + } - connect(this, SIGNAL(nxmTrackingToggled(QString, int, QVariant, bool, int)), - receiver, SLOT(nxmTrackingToggled(QString, int, QVariant, bool, int)), Qt::UniqueConnection); + NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_TOGGLETRACKING, userData, subModule, game); + requestInfo.m_Track = track; + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmTrackingToggled(QString, int, QVariant, bool, int)), + receiver, SLOT(nxmTrackingToggled(QString, int, QVariant, bool, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - qCritical() << QString("You have fewer than 200 requests remaining (%1). Only downloads and login validation are being allowed.") - .arg(std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests)); - return -1; + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QNetworkReply::NetworkError, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; } int NexusInterface::requestInfoFromMd5(QString gameName, QByteArray &hash, QObject *receiver, QVariant userData, @@ -645,7 +740,7 @@ void NexusInterface::nextRequest() } } - if (std::max(m_RemainingDailyRequests, m_RemainingHourlyRequests) <= 0) { + if (m_User.exhausted()) { m_RequestQueue.clear(); QTime time = QTime::currentTime(); QTime targetTime; @@ -696,9 +791,9 @@ void NexusInterface::nextRequest() } break; case NXMRequestInfo::TYPE_DOWNLOADURL: { ModRepositoryFileInfo *fileInfo = qobject_cast(qvariant_cast(info.m_UserData)); - if (m_IsPremium) { + if (m_User.type() == APIUserAccountTypes::Premium) { url = QString("%1/games/%2/mods/%3/files/%4/download_link").arg(info.m_URL).arg(info.m_GameName).arg(info.m_ModID).arg(info.m_FileID); - } else if (!fileInfo->nexusKey.isEmpty() && fileInfo->nexusExpires && fileInfo->nexusDownloadUser == m_UserID) { + } else if (!fileInfo->nexusKey.isEmpty() && fileInfo->nexusExpires && fileInfo->nexusDownloadUser == m_User.id().toInt()) { url = QString("%1/games/%2/mods/%3/files/%4/download_link?key=%5&expires=%6") .arg(info.m_URL).arg(info.m_GameName).arg(info.m_ModID).arg(info.m_FileID).arg(fileInfo->nexusKey).arg(fileInfo->nexusExpires); } else { @@ -780,23 +875,16 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (iter->m_AllowedErrors.contains(error) && iter->m_AllowedErrors[error].contains(statusCode)) { // These errors are allows to silently happen. They should be handled in nxmRequestFailed below. } else if (statusCode == 429) { - m_RemainingDailyRequests = reply->rawHeader("x-rl-daily-remaining").toInt(); - m_MaxDailyRequests = reply->rawHeader("x-rl-daily-limit").toInt(); - m_RemainingHourlyRequests = reply->rawHeader("x-rl-hourly-remaining").toInt(); - m_MaxHourlyRequests = reply->rawHeader("x-rl-hourly-limit").toInt(); + m_User.limits(parseLimits(reply)); - if (m_RemainingDailyRequests || m_RemainingHourlyRequests) + if (!m_User.exhausted()) { qWarning("You appear to be making requests to the Nexus API too quickly and are being throttled. Please inform the MO2 team."); - else + } + else { qWarning("All API requests have been consumed and are now being denied."); + } - emit requestsChanged(m_RequestQueue.size(), std::tuple(std::make_tuple( - m_RemainingDailyRequests, - m_MaxDailyRequests, - m_RemainingHourlyRequests, - m_MaxHourlyRequests - ))); - + emit requestsChanged(stats(), m_User); qWarning("Error: %s", reply->errorString().toUtf8().constData()); } else { qWarning("request failed: %s", reply->errorString().toUtf8().constData()); @@ -871,17 +959,8 @@ void NexusInterface::requestFinished(std::list::iterator iter) } break; } - m_RemainingDailyRequests = reply->rawHeader("x-rl-daily-remaining").toInt(); - m_MaxDailyRequests = reply->rawHeader("x-rl-daily-limit").toInt(); - m_RemainingHourlyRequests = reply->rawHeader("x-rl-hourly-remaining").toInt(); - m_MaxHourlyRequests = reply->rawHeader("x-rl-hourly-limit").toInt(); - - emit requestsChanged(m_RequestQueue.size(), std::tuple(std::make_tuple( - m_RemainingDailyRequests, - m_MaxDailyRequests, - m_RemainingHourlyRequests, - m_MaxHourlyRequests - ))); + m_User.limits(parseLimits(reply)); + emit requestsChanged(stats(), m_User); } else { emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->error(), tr("invalid response")); } @@ -938,6 +1017,15 @@ void NexusInterface::requestTimeout() } } +APIStats NexusInterface::stats() const +{ + APIStats stats; + stats.requestsQueued = m_RequestQueue.size(); + + return stats; +} + + namespace { QString get_management_url() { -- cgit v1.3.1 From 616925ebbb8e916119f8dbee0c1f0cb97b14a68b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 14 Jun 2019 01:38:07 -0400 Subject: moved api user account classes to their own files api label in status bar: - now shows when not logged in - changed some of the colour thresholds to correspond to real throttling numbers make sure the api key is also cleared from the access manager when clearing from the settings removed unused bool m_ValidateAttempted in NXMAccessManager update the window title and status bar api label with correct values on startup, which fixes incorrect values when "restarting" MO after changing nexus settings --- src/CMakeLists.txt | 3 ++ src/apiuseraccount.cpp | 67 ++++++++++++++++++++++++ src/apiuseraccount.h | 129 ++++++++++++++++++++++++++++++++++++++++++++++ src/mainwindow.cpp | 13 +++-- src/nexusinterface.cpp | 78 ++++------------------------ src/nexusinterface.h | 131 ++--------------------------------------------- src/nxmaccessmanager.cpp | 17 ++++-- src/nxmaccessmanager.h | 7 +-- src/settingsdialog.cpp | 6 ++- src/statusbar.cpp | 34 +++++++----- 10 files changed, 263 insertions(+), 222 deletions(-) create mode 100644 src/apiuseraccount.cpp create mode 100644 src/apiuseraccount.h (limited to 'src/nexusinterface.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71f87a8a..5623a851 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,7 @@ SET(organizer_SRCS forcedloaddialogwidget.cpp filterwidget.cpp statusbar.cpp + apiuseraccount.cpp shared/windows_error.cpp shared/error_report.cpp @@ -215,6 +216,7 @@ SET(organizer_HDRS forcedloaddialogwidget.h filterwidget.h statusbar.h + apiuseraccount.h shared/windows_error.h shared/error_report.h @@ -299,6 +301,7 @@ set(core nxmaccessmanager organizercore organizerproxy + apiuseraccount ) set(dialogs diff --git a/src/apiuseraccount.cpp b/src/apiuseraccount.cpp new file mode 100644 index 00000000..b901e41a --- /dev/null +++ b/src/apiuseraccount.cpp @@ -0,0 +1,67 @@ +#include "apiuseraccount.h" + +APIUserAccount::APIUserAccount() + : m_type(APIUserAccountTypes::None) +{ +} + +const QString& APIUserAccount::id() const +{ + return m_id; +} + +const QString& APIUserAccount::name() const +{ + return m_name; +} + +APIUserAccountTypes APIUserAccount::type() const +{ + return m_type; +} + +const APILimits& APIUserAccount::limits() const +{ + return m_limits; +} + +APIUserAccount& APIUserAccount::id(const QString& id) +{ + m_id = id; + return *this; +} + +APIUserAccount& APIUserAccount::name(const QString& name) +{ + m_name = name; + return *this; +} + +APIUserAccount& APIUserAccount::type(APIUserAccountTypes type) +{ + m_type = type; + return *this; +} + +APIUserAccount& APIUserAccount::limits(const APILimits& limits) +{ + m_limits = limits; + return *this; +} + +int APIUserAccount::remainingRequests() const +{ + return std::max( + m_limits.remainingDailyRequests, + m_limits.remainingHourlyRequests); +} + +bool APIUserAccount::shouldThrottle() const +{ + return (remainingRequests() < ThrottleThreshold); +} + +bool APIUserAccount::exhausted() const +{ + return (remainingRequests() <= 0); +} diff --git a/src/apiuseraccount.h b/src/apiuseraccount.h new file mode 100644 index 00000000..8a238d71 --- /dev/null +++ b/src/apiuseraccount.h @@ -0,0 +1,129 @@ +#ifndef APIUSERACCOUNT_H +#define APIUSERACCOUNT_H + +#include + +/** +* represents user account types on a mod provider website such as nexus +*/ +enum class APIUserAccountTypes +{ + // not logged in + None = 0, + + // regular account + Regular, + + // premium account + Premium +}; + + +/** +* current limits imposed on the user account +**/ +struct APILimits +{ + // maximum number of requests per day + int maxDailyRequests = 0; + + // remaining number of requests today + int remainingDailyRequests = 0; + + // maximum number of requests per hour + int maxHourlyRequests = 0; + + // remaining number of requests this hour + int remainingHourlyRequests = 0; +}; + + +/** +* API statistics +*/ +struct APIStats +{ + // number of API requests currently queued + int requestsQueued = 0; +}; + + +/** +* represents a user account on the mod provier website +*/ +class APIUserAccount +{ +public: + // when the number of remanining requests is under this number, further + // requests will be throttled by avoiding non-critical ones + static const int ThrottleThreshold = 200; + + APIUserAccount(); + + /** + * user id + */ + const QString& id() const; + + /** + * user name + */ + const QString& name() const; + + /** + * account type + */ + APIUserAccountTypes type() const; + + /** + * current API limits + */ + const APILimits& limits() const; + + + /** + * sets the user id + */ + APIUserAccount& id(const QString& id); + + /** + * sets the user name + **/ + APIUserAccount& name(const QString& name); + + /** + * sets the acount type + */ + APIUserAccount& type(APIUserAccountTypes type); + + /** + * sets the current limits + */ + APIUserAccount& limits(const APILimits& limits); + + + /** + * returns the number of remaining requests + */ + int remainingRequests() const; + + /** + * whether the number of remaining requests is low enough that further + * requests should be throttled + */ + bool shouldThrottle() const; + + /** + * true if all the remaining requests have been used and the API will refuse + * further requests + */ + bool exhausted() const; + +private: + QString m_id, m_name; + APIUserAccountTypes m_type; + APILimits m_limits; + APIStats m_stats; +}; + +#endif // APIUSERACCOUNT_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c255759b..5018479d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -225,8 +225,17 @@ MainWindow::MainWindow(QSettings &initSettings QWebEngineProfile::defaultProfile()->setHttpCacheMaximumSize(52428800); QWebEngineProfile::defaultProfile()->setCachePath(m_OrganizerCore.settings().getCacheDirectory()); QWebEngineProfile::defaultProfile()->setPersistentStoragePath(m_OrganizerCore.settings().getCacheDirectory()); + ui->setupUi(this); - updateWindowTitle({}); + + { + auto* ni = NexusInterface::instance(&m_PluginContainer); + + updateWindowTitle(ni->getAPIUserAccount()); + + m_statusBar.reset(new StatusBar(statusBar(), ui)); + m_statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); + } languageChange(m_OrganizerCore.settings().language()); @@ -244,8 +253,6 @@ MainWindow::MainWindow(QSettings &initSettings connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom())); - m_statusBar.reset(new StatusBar(statusBar(), ui)); - updateProblemsButton(); setupToolbar(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 8362143a..ee9acf2c 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -49,71 +49,6 @@ void throttledWarning(const APIUserAccount& user) } -APIUserAccount::APIUserAccount() - : m_type(APIUserAccountTypes::None) -{ -} - -const QString& APIUserAccount::id() const -{ - return m_id; -} - -const QString& APIUserAccount::name() const -{ - return m_name; -} - -APIUserAccountTypes APIUserAccount::type() const -{ - return m_type; -} - -const APILimits& APIUserAccount::limits() const -{ - return m_limits; -} - -APIUserAccount& APIUserAccount::id(const QString& id) -{ - m_id = id; - return *this; -} - -APIUserAccount& APIUserAccount::name(const QString& name) -{ - m_name = name; - return *this; -} - -APIUserAccount& APIUserAccount::type(APIUserAccountTypes type) -{ - m_type = type; - return *this; -} - -APIUserAccount& APIUserAccount::limits(const APILimits& limits) -{ - m_limits = limits; - return *this; -} - -int APIUserAccount::remainingRequests() const -{ - return m_limits.remainingDailyRequests + m_limits.remainingHourlyRequests; -} - -bool APIUserAccount::shouldThrottle() const -{ - return (remainingRequests() < ThrottleThreshold); -} - -bool APIUserAccount::exhausted() const -{ - return (remainingRequests() <= 0); -} - - NexusBridge::NexusBridge(PluginContainer *pluginContainer, const QString &subModule) : m_Interface(NexusInterface::instance(pluginContainer)) , m_SubModule(subModule) @@ -325,7 +260,7 @@ void NexusInterface::loginCompleted() void NexusInterface::setUserAccount(const APIUserAccount& user) { m_User = user; - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); } void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) @@ -884,7 +819,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) qWarning("All API requests have been consumed and are now being denied."); } - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); qWarning("Error: %s", reply->errorString().toUtf8().constData()); } else { qWarning("request failed: %s", reply->errorString().toUtf8().constData()); @@ -960,7 +895,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) } m_User.limits(parseLimits(reply)); - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); } else { emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->error(), tr("invalid response")); } @@ -1017,7 +952,12 @@ void NexusInterface::requestTimeout() } } -APIStats NexusInterface::stats() const +APIUserAccount NexusInterface::getAPIUserAccount() const +{ + return m_User; +} + +APIStats NexusInterface::getAPIStats() const { APIStats stats; stats.requestsQueued = m_RequestQueue.size(); diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 6c4e8d11..6e768149 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -20,6 +20,8 @@ along with Mod Organizer. If not, see . #ifndef NEXUSINTERFACE_H #define NEXUSINTERFACE_H +#include "apiuseraccount.h" + #include #include #include @@ -40,130 +42,6 @@ class NexusInterface; class NXMAccessManager; -/** - * represents user account types on a mod provider website such as nexus - */ -enum class APIUserAccountTypes -{ - // not logged in - None = 0, - - // regular account - Regular, - - // premium account - Premium -}; - - -/** - * current limits imposed on the user account - **/ -struct APILimits -{ - // maximum number of requests per day - int maxDailyRequests = 0; - - // remaining number of requests today - int remainingDailyRequests = 0; - - // maximum number of requests per hour - int maxHourlyRequests = 0; - - // remaining number of requests this hour - int remainingHourlyRequests = 0; -}; - - -/** - * API statistics - */ -struct APIStats -{ - // number of API requests currently queued - int requestsQueued = 0; -}; - - -/** - * represents a user account on the mod provier website - */ -class APIUserAccount -{ -public: - // when the number of remanining requests is under this number, further - // requests will be throttled by avoiding non-critical ones - static const int ThrottleThreshold = 300; - - APIUserAccount(); - - /** - * user id - */ - const QString& id() const; - - /** - * user name - */ - const QString& name() const; - - /** - * account type - */ - APIUserAccountTypes type() const; - - /** - * current API limits - */ - const APILimits& limits() const; - - - /** - * sets the user id - */ - APIUserAccount& id(const QString& id); - - /** - * sets the user name - **/ - APIUserAccount& name(const QString& name); - - /** - * sets the acount type - */ - APIUserAccount& type(APIUserAccountTypes type); - - /** - * sets the current limits - */ - APIUserAccount& limits(const APILimits& limits); - - - /** - * returns the number of remaining requests - */ - int remainingRequests() const; - - /** - * whether the number of remaining requests is low enough that further - * requests should be throttled - */ - bool shouldThrottle() const; - - /** - * true if all the remaining requests have been used and the API will refuse - * further requests - */ - bool exhausted() const; - -private: - QString m_id, m_name; - APIUserAccountTypes m_type; - APILimits m_limits; - APIStats m_stats; -}; - - /** * @brief convenience class to make nxm requests easier * usually, all objects that started a nxm request will be signaled if one finished. @@ -521,6 +399,9 @@ public: std::vector> getGameChoices(const MOBase::IPluginGame *game); + APIUserAccount getAPIUserAccount() const; + APIStats getAPIStats() const; + public: /** @@ -667,8 +548,6 @@ private: MOBase::VersionInfo m_MOVersion; PluginContainer *m_PluginContainer; APIUserAccount m_User; - - APIStats stats() const; }; #endif // NEXUSINTERFACE_H diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 5886d3ee..ee6c03f1 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -190,6 +190,7 @@ void NXMAccessManager::apiCheck(const QString &apiKey, bool force) emit validateSuccessful(false); return; } + m_ApiKey = apiKey; startValidationCheck(); } @@ -218,6 +219,13 @@ QString NXMAccessManager::apiKey() const return m_ApiKey; } +void NXMAccessManager::clearApiKey() +{ + m_ApiKey = ""; + m_ValidateState = VALIDATE_NOT_VALID; + + emit credentialsReceived(APIUserAccount()); +} void NXMAccessManager::validateTimeout() { @@ -233,7 +241,6 @@ void NXMAccessManager::validateTimeout() if (m_ValidateReply != nullptr) { m_ValidateReply->deleteLater(); m_ValidateReply = nullptr; - m_ValidateAttempted = false; // this usually means we might have success later } emit validateFailed(tr("There was a timeout during the request")); @@ -280,17 +287,21 @@ void NXMAccessManager::validateFinished() QString name = credentialsData.value("name").toString(); bool premium = credentialsData.value("is_premium").toBool(); - emit credentialsReceived(APIUserAccount() + const auto user = APIUserAccount() .id(QString("%1").arg(id)) .name(name) .type(premium ? APIUserAccountTypes::Premium : APIUserAccountTypes::Regular) - .limits(NexusInterface::parseLimits(m_ValidateReply))); + .limits(NexusInterface::parseLimits(m_ValidateReply)); + + + emit credentialsReceived(user); m_ValidateReply->deleteLater(); m_ValidateReply = nullptr; m_ValidateState = VALIDATE_VALID; emit validateSuccessful(true); + } else { m_ApiKey.clear(); m_ValidateState = VALIDATE_NOT_VALID; diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 1d23faf9..1bdeae40 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #ifndef NXMACCESSMANAGER_H #define NXMACCESSMANAGER_H +#include "apiuseraccount.h" #include #include #include @@ -28,8 +29,6 @@ along with Mod Organizer. If not, see . namespace MOBase { class IPluginGame; } -class APIUserAccount; - /** * @brief access manager extended to handle nxm links **/ @@ -56,6 +55,7 @@ public: QString userAgent(const QString &subModule = QString()) const; QString apiKey() const; + void clearApiKey(); void startValidationCheck(); @@ -94,7 +94,6 @@ protected: QIODevice *device); private: - QTimer m_ValidateTimeout; QNetworkReply *m_ValidateReply; QProgressDialog *m_ProgressDialog { nullptr }; @@ -103,7 +102,6 @@ private: QString m_ApiKey; - bool m_ValidateAttempted; enum { VALIDATE_NOT_CHECKED, VALIDATE_CHECKING, @@ -112,7 +110,6 @@ private: VALIDATE_REFUSED, VALIDATE_VALID } m_ValidateState = VALIDATE_NOT_CHECKED; - }; #endif // NXMACCESSMANAGER_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index b3e8c8a7..95e4ceb0 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -511,6 +511,9 @@ bool SettingsDialog::clearKey() m_KeyCleared = true; const auto ret = m_settings->clearNexusApiKey(); updateNexusButtons(); + + NexusInterface::instance(m_PluginContainer)->getAccessManager()->clearApiKey(); + return ret; } @@ -522,8 +525,7 @@ void SettingsDialog::testApiKey() return; } - auto* am = NexusInterface::instance(m_PluginContainer)->getAccessManager(); - am->apiCheck(key, true); + NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(key, true); } void SettingsDialog::updateNexusButtons() diff --git a/src/statusbar.cpp b/src/statusbar.cpp index 4effa6a3..712eb005 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -48,28 +48,34 @@ void StatusBar::setNotifications(bool hasNotifications) void StatusBar::setAPI(const APIStats& stats, const APIUserAccount& user) { - m_api->setText( - QString("API: Q: %1 | D: %2 | H: %3") - .arg(stats.requestsQueued) - .arg(user.limits().remainingDailyRequests) - .arg(user.limits().remainingHourlyRequests)); - + QString text; QString textColor; QString backgroundColor; if (user.type() == APIUserAccountTypes::None) { - backgroundColor = "transparent"; - } else if (user.remainingRequests() > 300) { - textColor = "white"; - backgroundColor = "darkgreen"; - } else if (user.remainingRequests() < 150) { + text = "API: not logged in"; textColor = "white"; - backgroundColor = "darkred"; + backgroundColor = "transparent"; } else { - textColor = "black"; - backgroundColor = "rgb(226, 192, 0)"; // yellow + text = QString("API: Queued: %1 | Daily: %2 | Hourly: %3") + .arg(stats.requestsQueued) + .arg(user.limits().remainingDailyRequests) + .arg(user.limits().remainingHourlyRequests); + + if (user.remainingRequests() > 500) { + textColor = "white"; + backgroundColor = "darkgreen"; + } else if (user.remainingRequests() > 200) { + textColor = "black"; + backgroundColor = "rgb(226, 192, 0)"; // yellow + } else { + textColor = "white"; + backgroundColor = "darkred"; + } } + m_api->setText(text); + m_api->setStyleSheet(QString(R"( QLabel { -- cgit v1.3.1