From 6dc5e016f1f3c879ebaa6efd3886bd81a0c6c13f Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Fri, 29 Jan 2021 13:05:41 +0100 Subject: [PATCH] freshrss experimental working --- .../services/greader/definitions.h | 8 ++ .../services/greader/greadernetwork.cpp | 87 ++++++++++++++++++- .../services/greader/greadernetwork.h | 16 +++- .../services/greader/greaderserviceroot.cpp | 85 +++++++++++------- .../network/inoreadernetworkfactory.cpp | 1 - 5 files changed, 164 insertions(+), 33 deletions(-) diff --git a/src/librssguard/services/greader/definitions.h b/src/librssguard/services/greader/definitions.h index b95ba5349..e7e1d8f56 100755 --- a/src/librssguard/services/greader/definitions.h +++ b/src/librssguard/services/greader/definitions.h @@ -8,11 +8,19 @@ #define GREADER_API_STATE_READ "state/com.google/read" #define GREADER_API_STATE_IMPORTANT "state/com.google/starred" +#define GREADER_API_FULL_STATE_READING_LIST "user/-/state/com.google/reading-list" +#define GREADER_API_FULL_STATE_READ "user/-/state/com.google/read" +#define GREADER_API_FULL_STATE_IMPORTANT "user/-/state/com.google/starred" + // API. #define GREADER_API_CLIENT_LOGIN "accounts/ClientLogin?Email=%1&Passwd=%2" #define GREADER_API_TAG_LIST "reader/api/0/tag/list?output=json" #define GREADER_API_SUBSCRIPTION_LIST "reader/api/0/subscription/list?output=json" #define GREADER_API_STREAM_CONTENTS "reader/api/0/stream/contents/%1?output=json&n=%2" +#define GREADER_API_EDIT_TAG "reader/api/0/edit-tag" + +// Misc. +#define GREADER_API_EDIT_TAG_BATCH 200 // FreshRSS. #define FRESHRSS_BASE_URL_PATH "api/greader.php/" diff --git a/src/librssguard/services/greader/greadernetwork.cpp b/src/librssguard/services/greader/greadernetwork.cpp index a2bd38540..7bd1b4312 100755 --- a/src/librssguard/services/greader/greadernetwork.cpp +++ b/src/librssguard/services/greader/greadernetwork.cpp @@ -22,6 +22,84 @@ GreaderNetwork::GreaderNetwork(QObject* parent) clearCredentials(); } +QNetworkReply::NetworkError GreaderNetwork::editLabels(const QString& state, + bool assign, + const QStringList& msg_custom_ids, + const QNetworkProxy& proxy) { + QString full_url = generateFullUrl(Operations::EditTag); + int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + + QNetworkReply::NetworkError network_err; + + if (!ensureLogin(proxy, &network_err)) { + return network_err; + } + + QStringList trimmed_ids; + QRegularExpression regex_short_id(QSL("[0-9a-zA-Z]+$")); + + for (const QString& id : msg_custom_ids) { + trimmed_ids.append(QString("i=") + id); + } + + QStringList working_subset; working_subset.reserve(std::min(GREADER_API_EDIT_TAG_BATCH, trimmed_ids.size())); + + // Now, we perform messages update in batches (max X messages per batch). + while (!trimmed_ids.isEmpty()) { + // We take X IDs. + for (int i = 0; i < GREADER_API_EDIT_TAG_BATCH && !trimmed_ids.isEmpty(); i++) { + working_subset.append(trimmed_ids.takeFirst()); + } + + QString args; + + if (assign) { + args = QString("a=") + state + "&"; + } + else { + args = QString("r=") + state + "&"; + } + + args += working_subset.join(QL1C('&')); + + // We send this batch. + QByteArray output; + auto result_edit = NetworkFactory::performNetworkOperation(full_url, + timeout, + args.toUtf8(), + output, + QNetworkAccessManager::Operation::PostOperation, + { authHeader(), + { QSL("Content-Type").toLocal8Bit(), + QSL("application/x-www-form-urlencoded").toLocal8Bit() } }, + false, + {}, + {}, + proxy); + + if (result_edit.first != QNetworkReply::NetworkError::NoError) { + return result_edit.first; + } + + // Cleanup for next batch. + working_subset.clear(); + } + + return QNetworkReply::NetworkError::NoError; +} + +QNetworkReply::NetworkError GreaderNetwork::markMessagesRead(RootItem::ReadStatus status, + const QStringList& msg_custom_ids, + const QNetworkProxy& proxy) { + return editLabels(GREADER_API_FULL_STATE_READ, status == RootItem::ReadStatus::Read, msg_custom_ids, proxy); +} + +QNetworkReply::NetworkError GreaderNetwork::markMessagesStarred(RootItem::Importance importance, + const QStringList& msg_custom_ids, + const QNetworkProxy& proxy) { + return editLabels(GREADER_API_FULL_STATE_IMPORTANT, importance == RootItem::Importance::Important, msg_custom_ids, proxy); +} + QList GreaderNetwork::streamContents(ServiceRoot* root, const QString& stream_id, Feed::Status& error, const QNetworkProxy& proxy) { QString full_url = generateFullUrl(Operations::StreamContents).arg(stream_id, @@ -307,10 +385,14 @@ QPair GreaderNetwork::authHeader() const { return { QSL("Authorization").toLocal8Bit(), QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() }; } -bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy) { +bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output) { if (m_authSid.isEmpty()) { auto login = clientLogin(proxy); + if (output != nullptr) { + *output = login; + } + if (login != QNetworkReply::NetworkError::NoError) { qCriticalNN << LOGSEC_GREADER << "Login failed with error:" @@ -440,5 +522,8 @@ QString GreaderNetwork::generateFullUrl(GreaderNetwork::Operations operation) co case Operations::StreamContents: return sanitizedBaseUrl() + GREADER_API_STREAM_CONTENTS; + + case Operations::EditTag: + return sanitizedBaseUrl() + GREADER_API_EDIT_TAG; } } diff --git a/src/librssguard/services/greader/greadernetwork.h b/src/librssguard/services/greader/greadernetwork.h index 76b3a16ad..5574082ba 100755 --- a/src/librssguard/services/greader/greadernetwork.h +++ b/src/librssguard/services/greader/greadernetwork.h @@ -17,11 +17,23 @@ class GreaderNetwork : public QObject { ClientLogin, TagList, SubscriptionList, - StreamContents + StreamContents, + EditTag }; explicit GreaderNetwork(QObject* parent = nullptr); + QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, + const QStringList& msg_custom_ids, + const QNetworkProxy& proxy); + QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, + const QStringList& msg_custom_ids, + const QNetworkProxy& proxy); + + // Assign/deassign tags to/from message(s). + QNetworkReply::NetworkError editLabels(const QString& state, bool assign, + const QStringList& msg_custom_ids, const QNetworkProxy& proxy); + // Stream contents for a feed/label/etc. QList streamContents(ServiceRoot* root, const QString& stream_id, Feed::Status& error, const QNetworkProxy& proxy); @@ -56,7 +68,7 @@ class GreaderNetwork : public QObject { QPair authHeader() const; // Make sure we are logged in and if we are not, return error. - bool ensureLogin(const QNetworkProxy& proxy); + bool ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output = nullptr); QList decodeStreamContents(ServiceRoot* root, const QString& stream_json_data, const QString& stream_id); RootItem* decodeTagsSubscriptions(const QString& categories, const QString& feeds, bool obtain_icons); diff --git a/src/librssguard/services/greader/greaderserviceroot.cpp b/src/librssguard/services/greader/greaderserviceroot.cpp index 67a932cd3..0d147f0b1 100755 --- a/src/librssguard/services/greader/greaderserviceroot.cpp +++ b/src/librssguard/services/greader/greaderserviceroot.cpp @@ -61,48 +61,75 @@ QString GreaderServiceRoot::code() const { void GreaderServiceRoot::saveAllCachedData(bool ignore_errors) { auto msg_cache = takeMessageCache(); + QMapIterator i(msg_cache.m_cachedStatesRead); - /* - QMapIterator i(msg_cache.m_cachedStatesRead); + // Save the actual data read/unread. + while (i.hasNext()) { + i.next(); + auto key = i.key(); + QStringList ids = i.value(); - // Save the actual data read/unread. - while (i.hasNext()) { - i.next(); - auto key = i.key(); - QStringList ids = i.value(); - - if (!ids.isEmpty()) { - auto res = network()->markMessagesRead(key, ids, networkProxy()); - - if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) { + if (!ids.isEmpty()) { + if (network()->markMessagesRead(key, ids, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { addMessageStatesToCache(ids, key); } - } - } + } + } - QMapIterator> j(msg_cache.m_cachedStatesImportant); + QMapIterator> j(msg_cache.m_cachedStatesImportant); - // Save the actual data important/not important. - while (j.hasNext()) { - j.next(); - auto key = j.key(); - QList messages = j.value(); + // Save the actual data important/not important. + while (j.hasNext()) { + j.next(); + auto key = j.key(); + QList messages = j.value(); - if (!messages.isEmpty()) { - QStringList feed_ids, guid_hashes; + if (!messages.isEmpty()) { + QStringList custom_ids; for (const Message& msg : messages) { - feed_ids.append(msg.m_feedId); - guid_hashes.append(msg.m_customHash); + custom_ids.append(msg.m_customId); } - auto res = network()->markMessagesStarred(key, feed_ids, guid_hashes, networkProxy()); - - if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) { + if (network()->markMessagesStarred(key, custom_ids, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { addMessageStatesToCache(messages, key); } - } - }*/ + } + } + + QMapIterator k(msg_cache.m_cachedLabelAssignments); + + // Assign label for these messages. + while (k.hasNext()) { + k.next(); + auto label_custom_id = k.key(); + QStringList messages = k.value(); + + if (!messages.isEmpty()) { + if (network()->editLabels(label_custom_id, true, messages, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { + addLabelsAssignmentsToCache(messages, label_custom_id, true); + } + } + } + + QMapIterator l(msg_cache.m_cachedLabelDeassignments); + + // Remove label from these messages. + while (l.hasNext()) { + l.next(); + auto label_custom_id = l.key(); + QStringList messages = l.value(); + + if (!messages.isEmpty()) { + if (network()->editLabels(label_custom_id, false, messages, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { + addLabelsAssignmentsToCache(messages, label_custom_id, false); + } + } + } } void GreaderServiceRoot::updateTitle() { diff --git a/src/librssguard/services/inoreader/network/inoreadernetworkfactory.cpp b/src/librssguard/services/inoreader/network/inoreadernetworkfactory.cpp index fa27e4e60..1ae9d0c51 100644 --- a/src/librssguard/services/inoreader/network/inoreadernetworkfactory.cpp +++ b/src/librssguard/services/inoreader/network/inoreadernetworkfactory.cpp @@ -222,7 +222,6 @@ QNetworkReply::NetworkError InoreaderNetworkFactory::editLabels(const QString& s m_oauth2->bearer().toLocal8Bit())); QStringList trimmed_ids; - QRegularExpression regex_short_id(QSL("[0-9a-zA-Z]+$")); for (const QString& id : msg_custom_ids) { trimmed_ids.append(QString("i=") + id);