diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp index 4fce208cc..5e8ecf3f3 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetconvert.cpp @@ -61,6 +61,7 @@ QTweetStatus QTweetConvert::variantMapToStatus(const QVariantMap &var) status.setId(var["id"].toLongLong()); status.setInReplyToUserId(var["in_reply_to_user_id"].toLongLong()); status.setInReplyToScreenName(var["in_reply_to_screen_name"].toString()); + status.setFavorited(var["favorited"].toBool()); QVariantMap userMap = var["user"].toMap(); QTweetUser user = variantMapToUserInfo(userMap); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp index 1871ed0a2..2eab28264 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetfriendshipdestroy.cpp @@ -64,7 +64,7 @@ void QTweetFriendshipDestroy::unfollow(qint64 userid, bool includeEntities) QNetworkRequest req(url); - QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::GET); + QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::DELETE); req.setRawHeader(AUTH_HEADER, oauthHeader); QNetworkReply *reply = oauthTwitter()->networkAccessManager()->deleteResource(req); @@ -92,7 +92,7 @@ void QTweetFriendshipDestroy::unfollow(const QString &screenName, bool includeEn QNetworkRequest req(url); - QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::GET); + QByteArray oauthHeader = oauthTwitter()->generateAuthorizationHeader(url, OAuth::DELETE); req.setRawHeader(AUTH_HEADER, oauthHeader); QNetworkReply *reply = oauthTwitter()->networkAccessManager()->deleteResource(req); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp index 834af4e60..6e264e588 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetsearchresult.cpp @@ -69,7 +69,7 @@ void QTweetSearchResult::setCreatedAt(const QDateTime &dateTime) void QTweetSearchResult::setCreatedAt(const QString &twitterDate) { QString dateString = twitterDate.left(3) + ' ' + twitterDate.mid(8, 3) + ' ' + - twitterDate.mid(5, 2) + ' ' + twitterDate.mid(13, 4); + twitterDate.mid(5, 2) + ' ' + twitterDate.mid(12, 4); QString timeString = twitterDate.mid(17, 8); QDate date = QDate::fromString(dateString); diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp index 7e2526e46..a3d63b697 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.cpp @@ -33,14 +33,28 @@ #define TWITTER_USERSTREAM_URL "https://userstream.twitter.com/2/user.json" +// ### TODO User Agent or X-User-Agent + /** * Constructor */ QTweetUserStream::QTweetUserStream(QObject *parent) : - QObject(parent), m_oauthTwitter(0), m_reply(0), m_backofftimer(new QTimer(this)) + QObject(parent), m_oauthTwitter(0), m_reply(0), + m_backofftimer(new QTimer(this)), + m_timeoutTimer(new QTimer(this)), + m_streamTryingReconnect(false) { + m_backofftimer->setInterval(20000); m_backofftimer->setSingleShot(true); connect(m_backofftimer, SIGNAL(timeout()), this, SLOT(startFetching())); + + m_timeoutTimer->setInterval(90000); + connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(replyTimeout())); + +#ifdef STREAM_LOGGER + m_streamLog.setFileName("streamlog.txt"); + m_streamLog.open(QIODevice::WriteOnly | QIODevice::Text); +#endif } /** @@ -59,49 +73,6 @@ OAuthTwitter* QTweetUserStream::oauthTwitter() const return m_oauthTwitter; } -/** - * Called when there is network error - */ -void QTweetUserStream::replyError(QNetworkReply::NetworkError code) -{ - qDebug() << "Reply error: " << code; - - // ### TODO: determine network error codes, assumptions here - - if (code < 200) { - //linear backoff - if (m_backofftimer->interval() < 250) { - m_backofftimer->setInterval(250); - } else { - int nextLinInterval = m_backofftimer->interval() + 250; - - if (nextLinInterval > 16000) //cap - nextLinInterval = 16000; - - m_backofftimer->setInterval(nextLinInterval); - } - - m_backofftimer->start(); - return; - } - - if (code > 200) { - //exp. backoff - if (m_backofftimer->interval() < 10000) { - m_backofftimer->setInterval(10000); - } else { - int nextExpInterval = 2 * m_backofftimer->interval(); - - if (nextExpInterval > 240000) - nextExpInterval = 240000; - - m_backofftimer->setInterval(240000); - } - - m_backofftimer->start(); - } -} - /** * Starts fetching user stream */ @@ -122,7 +93,9 @@ void QTweetUserStream::startFetching() m_reply = m_oauthTwitter->networkAccessManager()->get(req); connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); connect(m_reply, SIGNAL(readyRead()), this, SLOT(replyReadyRead())); - connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); + + connect(m_reply, SIGNAL(readyRead()), m_timeoutTimer, SLOT(start())); + connect(m_reply, SIGNAL(finished()), m_timeoutTimer, SLOT(stop())); } /** @@ -130,16 +103,40 @@ void QTweetUserStream::startFetching() */ void QTweetUserStream::replyFinished() { - if (!m_reply->error()) { //no error, reconnect + qDebug() << "User stream closed "; - //sligh delay for reconnect - m_backofftimer->setInterval(250); + m_streamTryingReconnect = true; + + if (!m_reply->error()) { //no error, reconnect + qDebug() << "No error, reconnect"; + + m_reply->deleteLater(); + m_reply = 0; + + startFetching(); + } else { //error + qDebug() << "Error: " << m_reply->error() << ", " << m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + //if (m_backofftimer->interval() < 20001) { + // m_backofftimer->start(); + // return; + //} + + //increase back off interval + int nextInterval = 2 * m_backofftimer->interval(); + + if (nextInterval > 300000) { + m_backofftimer->setInterval(300000); + emit failureConnect(); + } + + m_backofftimer->setInterval(nextInterval); m_backofftimer->start(); - m_reply->deleteLater(); - m_reply = 0; - } else { //error, delete QNetworkReply - m_reply->deleteLater(); - m_reply = 0; + + qDebug() << "Exp backoff interval: " << nextInterval; } } @@ -147,30 +144,53 @@ void QTweetUserStream::replyReadyRead() { QByteArray response = m_reply->readAll(); - //reset timer - m_backofftimer->setInterval(0); +#ifdef STREAM_LOGGER + m_streamLog.write(response); + m_streamLog.write("\n"); +#endif - //split to response to delimited and not delimited part - int lastCarrReturn = response.lastIndexOf('\r'); - QByteArray rightNotDelimitedPart = response.mid(lastCarrReturn + 1); - QByteArray leftDelimitedPart = response.left(lastCarrReturn); - - //prepend to left previous not delimited response - leftDelimitedPart = leftDelimitedPart.prepend(m_cashedResponse); - - QList elements = leftDelimitedPart.split('\r'); - - for (int i = 0; i < elements.size(); ++i) { - if (elements.at(i) != QByteArray(1, '\n')) { - emit stream(elements.at(i)); - parseStream(elements.at(i)); - } + if (m_streamTryingReconnect) { + emit reconnected(); + m_streamTryingReconnect = false; } - if (rightNotDelimitedPart != QByteArray(1, '\n')) - m_cashedResponse = rightNotDelimitedPart; - else - m_cashedResponse.clear(); + //set backoff timer to initial interval + m_backofftimer->setInterval(20000); + + QByteArray responseWithPreviousCache = response.prepend(m_cachedResponse); + + int start = 0; + int end; + + while ((end = responseWithPreviousCache.indexOf('\r', start)) != -1) { + if (start != end) { + QByteArray element = responseWithPreviousCache.mid(start, end - start); + + if (!element.isEmpty()) { + emit stream(element); + parseStream(element); + } + } + + int skip = (response.at(end + 1) == QLatin1Char('\n')) ? 2 : 1; + start = end + skip; + } + + //undelimited part just cache it + m_cachedResponse.clear(); + + if (start != responseWithPreviousCache.size()) { + QByteArray element = responseWithPreviousCache.mid(start); + if (!element.isEmpty()) + m_cachedResponse = element; + } +} + +void QTweetUserStream::replyTimeout() +{ + qDebug() << "Timeout connection"; + + m_reply->abort(); } void QTweetUserStream::parseStream(const QByteArray& data) @@ -187,6 +207,9 @@ void QTweetUserStream::parsingFinished(const QVariant &json, bool ok, const QStr { if (!ok) { qDebug() << "JSON parsing error: " << errorMsg; +#ifdef STREAM_LOGGER + m_streamLog.write("***** JSON parsing error *****\n"); +#endif return; } diff --git a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h index c7f1e3bc6..114cd758f 100644 --- a/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h +++ b/thirdparty/qtweetlib/qtweetlib/src/qtweetuserstream.h @@ -25,6 +25,10 @@ #include #include "qtweetlib_global.h" +#ifdef STREAM_LOGGER + #include +#endif + class QNetworkAccessManager; class QNetworkReply; class OAuthTwitter; @@ -49,7 +53,6 @@ signals: * Emits stream elements */ void stream(const QByteArray& ); - /** * Emits tweets (parsed) elements from stream */ @@ -68,14 +71,24 @@ signals: * Emits deletion of status in the stream */ void deleteStatusStream(qint64 id, qint64 userid); + /** + * Emited when user stream is reconnected after failure + * Usefull when user stream connection fails to fetch missed tweets with REST API + */ + void reconnected(); + /** + * Emited when user stream doesn't connect and backoff timer reaches maximum value (300 seconds) + * Usefull when users stream fails to revert to REST API + */ + void failureConnect(); public slots: void startFetching(); private slots: - void replyError(QNetworkReply::NetworkError code); void replyFinished(); void replyReadyRead(); + void replyTimeout(); void parsingFinished(const QVariant& json, bool ok, const QString& errorMsg); private: @@ -84,10 +97,16 @@ private: void parseDirectMessage(const QVariantMap& streamObject); void parseDeleteStatus(const QVariantMap& streamObject); + QByteArray m_cachedResponse; OAuthTwitter *m_oauthTwitter; QNetworkReply *m_reply; QTimer *m_backofftimer; - QByteArray m_cashedResponse; + QTimer *m_timeoutTimer; + bool m_streamTryingReconnect; + +#ifdef STREAM_LOGGER + QFile m_streamLog; +#endif }; #endif // QTWEETUSERSTREAM_H