{"action":"created","comment":{"url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/comments/31790721","id":31790721,"diff_hunk":"@@ -0,0 +1,632 @@\n+/****************************************************************************\n+ **\n+ ** Copyright (C) 2013-2015 Jolla Ltd.\n+ ** Contact: Chris Adams <chris.adams@jollamobile.com>\n+ **\n+ ** This program/library is free software; you can redistribute it and/or\n+ ** modify it under the terms of the GNU Lesser General Public License\n+ ** version 2.1 as published by the Free Software Foundation.\n+ **\n+ ** This program/library is distributed in the hope that it will be useful,\n+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ ** Lesser General Public License for more details.\n+ **\n+ ** You should have received a copy of the GNU Lesser General Public\n+ ** License along with this program/library; if not, write to the Free\n+ ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n+ ** 02110-1301 USA\n+ **\n+ ****************************************************************************/\n+\n+#include \"twitternotificationsyncadaptor.h\"\n+#include \"trace.h\"\n+\n+#include <QtCore/QPair>\n+#include <QtCore/QUrlQuery>\n+#include <QtCore/QJsonValue>\n+\n+//nemo-qml-plugins/notifications\n+#include <notification.h>\n+\n+// currently, we integrate with the device notifications via nemo-qml-plugin-notification\n+\n+TwitterNotificationSyncAdaptor::TwitterNotificationSyncAdaptor(QObject *parent)\n+    : TwitterDataTypeSyncAdaptor(SocialNetworkSyncAdaptor::Notifications, parent)\n+{\n+    setInitialActive(m_db.isValid());\n+}\n+\n+TwitterNotificationSyncAdaptor::~TwitterNotificationSyncAdaptor()\n+{\n+}\n+\n+QString TwitterNotificationSyncAdaptor::syncServiceName() const\n+{\n+    return QStringLiteral(\"twitter-microblog\");\n+}\n+\n+void TwitterNotificationSyncAdaptor::purgeDataForOldAccount(int oldId, SocialNetworkSyncAdaptor::PurgeMode)\n+{\n+    Notification *notification = findNotification(oldId, Mention);\n+    if (notification) {\n+        notification->close();\n+        notification->deleteLater();\n+    }\n+\n+    notification = findNotification(oldId, Retweet);\n+    if (notification) {\n+        notification->close();\n+        notification->deleteLater();\n+    }\n+\n+    notification = findNotification(oldId, Follower);\n+    if (notification) {\n+        notification->close();\n+        notification->deleteLater();\n+    }\n+\n+    m_db.setFollowerIds(oldId, QSet<QString>());\n+    m_db.setRetweetedTweetCounts(oldId, QHash<QString, int>());\n+    m_db.sync();\n+    m_db.wait();\n+}\n+\n+void TwitterNotificationSyncAdaptor::beginSync(int accountId, const QString &oauthToken, const QString &oauthTokenSecret)\n+{\n+    m_lastSyncTimestamp = lastSyncTimestamp(QLatin1String(\"twitter\"),\n+                                            SocialNetworkSyncAdaptor::dataTypeName(SocialNetworkSyncAdaptor::Notifications),\n+                                            accountId);\n+    SOCIALD_LOG_DEBUG(\"last sync of Twitter notifications was at:\" << m_lastSyncTimestamp.toString(Qt::ISODate));\n+    requestNotifications(accountId, oauthToken, oauthTokenSecret);\n+}\n+\n+void TwitterNotificationSyncAdaptor::finalize(int accountId)\n+{\n+    if (syncAborted()) {\n+        SOCIALD_LOG_INFO(\"sync aborted, won't commit notification database changes for Twitter account:\" << accountId);\n+    } else {\n+        m_db.sync();\n+        m_db.wait();\n+    }\n+}\n+\n+void TwitterNotificationSyncAdaptor::requestNotifications(int accountId, const QString &oauthToken, const QString &oauthTokenSecret, const QString &sinceTweetId, const QString &followersCursor)\n+{\n+    if (followersCursor.isEmpty()) {\n+        // request mentions\n+        QList<QPair<QString, QString> > queryItems;\n+        queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"count\")), QString(QLatin1String(\"50\"))));\n+        if (!sinceTweetId.isEmpty()) {\n+            queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"since_id\")), sinceTweetId));\n+        }\n+        QString baseUrl(QLatin1String(\"https://api.twitter.com/1.1/statuses/mentions_timeline.json\"));\n+        QUrl url(baseUrl);\n+        QUrlQuery query(url);\n+        query.setQueryItems(queryItems);\n+        url.setQuery(query);\n+\n+        QNetworkRequest mentionsRequest(url);\n+        mentionsRequest.setRawHeader(\"Authorization\", authorizationHeader(\n+                                     accountId, oauthToken, oauthTokenSecret,\n+                                     QLatin1String(\"GET\"), baseUrl, queryItems).toLatin1());\n+\n+        QNetworkReply *mreply = m_networkAccessManager->get(mentionsRequest);\n+\n+        if (mreply) {\n+            mreply->setProperty(\"accountId\", accountId);\n+            mreply->setProperty(\"oauthToken\", oauthToken);\n+            mreply->setProperty(\"oauthTokenSecret\", oauthTokenSecret);\n+            connect(mreply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(errorHandler(QNetworkReply::NetworkError)));\n+            connect(mreply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsHandler(QList<QSslError>)));\n+            connect(mreply, SIGNAL(finished()), this, SLOT(finishedMentionsHandler()));\n+\n+            // we're requesting data.  Increment the semaphore so that we know we're still busy.\n+            incrementSemaphore(accountId);\n+            setupReplyTimeout(accountId, mreply);\n+        } else {\n+            SOCIALD_LOG_ERROR(\"unable to request mention timeline notifications from Twitter account with id\" << accountId);\n+        }\n+\n+        // request retweets\n+        queryItems.clear();\n+        queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"count\")), QString(QLatin1String(\"40\"))));\n+        queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"trim_user\")), QString(QLatin1String(\"false\"))));\n+        queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"include_entities\")), QString(QLatin1String(\"false\"))));\n+        if (!sinceTweetId.isEmpty()) {\n+            queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"since_id\")), sinceTweetId));\n+        }\n+        baseUrl = QString(QLatin1String(\"https://api.twitter.com/1.1/statuses/retweets_of_me.json\"));\n+        url = QUrl(baseUrl);\n+        QUrlQuery retweetsQuery(url);\n+        retweetsQuery.setQueryItems(queryItems);\n+        url.setQuery(retweetsQuery);\n+\n+        QNetworkRequest retweetsRequest(url);\n+        retweetsRequest.setRawHeader(\"Authorization\", authorizationHeader(\n+                                     accountId, oauthToken, oauthTokenSecret,\n+                                     QLatin1String(\"GET\"), baseUrl, queryItems).toLatin1());\n+\n+        QNetworkReply *rreply = m_networkAccessManager->get(retweetsRequest);\n+\n+        if (rreply) {\n+            rreply->setProperty(\"accountId\", accountId);\n+            rreply->setProperty(\"oauthToken\", oauthToken);\n+            rreply->setProperty(\"oauthTokenSecret\", oauthTokenSecret);\n+            connect(rreply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(errorHandler(QNetworkReply::NetworkError)));\n+            connect(rreply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsHandler(QList<QSslError>)));\n+            connect(rreply, SIGNAL(finished()), this, SLOT(finishedRetweetsHandler()));\n+\n+            // we're requesting data.  Increment the semaphore so that we know we're still busy.\n+            incrementSemaphore(accountId);\n+            setupReplyTimeout(accountId, rreply);\n+        } else {\n+            SOCIALD_LOG_ERROR(\"unable to request retweet notifications from Twitter account with id\" << accountId);\n+        }\n+    }\n+\n+    // request followers\n+    QList<QPair<QString, QString> > queryItems;\n+    queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"count\")), QString(QLatin1String(\"5000\"))));\n+    queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"stringify_ids\")), QString(QLatin1String(\"true\"))));\n+    if (!followersCursor.isEmpty()) {\n+        queryItems.append(QPair<QString, QString>(QString(QLatin1String(\"cursor\")), followersCursor));\n+    }\n+    QString baseUrl(QLatin1String(\"https://api.twitter.com/1.1/followers/ids.json\"));\n+    QUrl url(baseUrl);\n+    QUrlQuery followersQuery(url);\n+    followersQuery.setQueryItems(queryItems);\n+    url.setQuery(followersQuery);\n+\n+    QNetworkRequest followersRequest(url);\n+    followersRequest.setRawHeader(\"Authorization\", authorizationHeader(\n+                                  accountId, oauthToken, oauthTokenSecret,\n+                                  QLatin1String(\"GET\"), baseUrl, queryItems).toLatin1());\n+\n+    QNetworkReply *freply = m_networkAccessManager->get(followersRequest);\n+\n+    if (freply) {\n+        freply->setProperty(\"accountId\", accountId);\n+        freply->setProperty(\"oauthToken\", oauthToken);\n+        freply->setProperty(\"oauthTokenSecret\", oauthTokenSecret);\n+        connect(freply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(errorHandler(QNetworkReply::NetworkError)));\n+        connect(freply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsHandler(QList<QSslError>)));\n+        connect(freply, SIGNAL(finished()), this, SLOT(finishedFollowersHandler()));\n+\n+        // we're requesting data.  Increment the semaphore so that we know we're still busy.\n+        incrementSemaphore(accountId);\n+        setupReplyTimeout(accountId, freply);\n+    } else {\n+        SOCIALD_LOG_ERROR(\"unable to request followers from Twitter account with id\" << accountId);\n+    }\n+}\n+\n+void TwitterNotificationSyncAdaptor::finishedMentionsHandler()\n+{\n+    QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());\n+    int accountId = reply->property(\"accountId\").toInt();\n+\n+    QByteArray replyData = reply->readAll();\n+    disconnect(reply);\n+    reply->deleteLater();\n+    removeReplyTimeout(accountId, reply);\n+\n+    if (syncAborted()) {\n+        SOCIALD_LOG_INFO(\"sync aborted, ignoring request response\");\n+        decrementSemaphore(accountId);\n+        return;\n+    }\n+\n+    bool ok = false;\n+    QJsonArray tweets = parseJsonArrayReplyData(replyData, &ok);\n+    if (ok) {\n+        if (!tweets.size()) {\n+            SOCIALD_LOG_DEBUG(\"no mentions received for account\" << accountId);\n+            decrementSemaphore(accountId);\n+            return;\n+        }\n+\n+        int mentionsCount = 0;\n+        QString body;\n+        QString summary;\n+        QDateTime timestamp;\n+        QString link;\n+        foreach (const QJsonValue &tweetValue, tweets) {\n+            QJsonObject tweet = tweetValue.toObject();\n+            QDateTime createdTime = parseTwitterDateTime(tweet.value(QLatin1String(\"created_at\")).toString());\n+            QString mentionId = tweet.value(QLatin1String(\"id_str\")).toString();\n+            QString text = tweet.value(QLatin1String(\"text\")).toString();\n+            QJsonObject user = tweet.value(QLatin1String(\"user\")).toObject();\n+            QString userName = user.value(QLatin1String(\"name\")).toString();\n+            QString userScreenName = user.value(QLatin1String(\"screen_name\")).toString();\n+\n+            // check to see if we need to post it to the notifications feed\n+            int sinceSpan = m_accountSyncProfile\n+                          ? m_accountSyncProfile->key(Buteo::KEY_SYNC_SINCE_DAYS_PAST, QStringLiteral(\"7\")).toInt()\n+                          : 7;\n+            if (m_lastSyncTimestamp.isValid() && createdTime < m_lastSyncTimestamp) {\n+                SOCIALD_LOG_DEBUG(\"mention notification for account\" << accountId << \"came after last sync:\" << createdTime << \":\" << text);\n+                break; // all subsequent notifications will be even older.\n+            } else if (createdTime.daysTo(QDateTime::currentDateTimeUtc()) > sinceSpan) {\n+                SOCIALD_LOG_DEBUG(\"mention for account\" << accountId << \"is more than\" << sinceSpan << \"days old:\" << createdTime << \":\" << text);\n+            } else {\n+                summary = userName;\n+                //: Label telling the user that someone mentioned them.  e.g: \"John Smith\" + \"mentioned you in a Tweet\"\n+                //% \"Mentioned you in a Tweet\"\n+                body = qtTrId(\"qtn_social_notifications_twitter_mentioned_you\");\n+                timestamp = createdTime;\n+                link = QLatin1String(\"https://mobile.twitter.com/\") + userScreenName + QLatin1String(\"/status/\") + mentionId;\n+                mentionsCount ++;\n+            }\n+        }\n+\n+        if (mentionsCount > 0) {\n+            // Search if we already have a notification\n+            Notification *notification = createNotification(accountId, Mention);\n+\n+            // Set properties of the notification\n+            notification->setItemCount(notification->itemCount() + mentionsCount);\n+            QStringList openUrlArgs;\n+            if (notification->itemCount() == 1) {\n+                notification->setTimestamp(timestamp);\n+                notification->setSummary(summary);\n+                notification->setBody(body);\n+                openUrlArgs << link;\n+            } else {\n+                notification->setTimestamp(QDateTime::currentDateTimeUtc());\n+                //: This label refers to the user of the device, eg \"You\" + \"have 5 new followers on Twitter\"\n+                //% \"You\"\n+                notification->setSummary(qtTrId(\"qtn_social_notifications_twitter_you\"));\n+                //: The number of tweets (n) by other people which mention this user. Include n.\n+                //% \"Received %n mentions\"\n+                notification->setBody(qtTrId(\"qtn_social_notifications_twitter_n_mentions_include_n\", notification->itemCount()));\n+                openUrlArgs << QLatin1String(\"https://mobile.twitter.com/i/connect\");\n+            }\n+            notification->setRemoteAction(Notification::remoteAction(\n+                \"default\",\n+                \"\",\n+                \"org.sailfishos.browser\",\n+                \"/\",\n+                \"org.sailfishos.browser\",\n+                \"openUrl\",\n+                QVariantList() << openUrlArgs\n+            ));\n+            notification->publish();\n+\n+            qlonglong localId = (0 + notification->replacesId());","path":"src/twitter/twitter-notifications/twitternotificationsyncadaptor.cpp","position":296,"original_position":296,"commit_id":"68e964055f6b5e94f8daaf7a52d42e4b8deec002","original_commit_id":"68e964055f6b5e94f8daaf7a52d42e4b8deec002","user":{"login":"matthewvogt","id":2569784,"avatar_url":"https://avatars.githubusercontent.com/u/2569784?v=3","gravatar_id":"","url":"https://api.github.com/users/matthewvogt","html_url":"https://github.com/matthewvogt","followers_url":"https://api.github.com/users/matthewvogt/followers","following_url":"https://api.github.com/users/matthewvogt/following{/other_user}","gists_url":"https://api.github.com/users/matthewvogt/gists{/gist_id}","starred_url":"https://api.github.com/users/matthewvogt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matthewvogt/subscriptions","organizations_url":"https://api.github.com/users/matthewvogt/orgs","repos_url":"https://api.github.com/users/matthewvogt/repos","events_url":"https://api.github.com/users/matthewvogt/events{/privacy}","received_events_url":"https://api.github.com/users/matthewvogt/received_events","type":"User","site_admin":false},"body":"This line is weird...","created_at":"2015-06-05T06:45:34Z","updated_at":"2015-06-05T06:45:34Z","html_url":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30#discussion_r31790721","pull_request_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30","_links":{"self":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/comments/31790721"},"html":{"href":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30#discussion_r31790721"},"pull_request":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30"}}},"pull_request":{"url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30","id":36824610,"html_url":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30","diff_url":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30.diff","patch_url":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30.patch","issue_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/30","number":30,"state":"open","locked":false,"title":"[buteo-sync-plugins-social] Add more Twitter notification types. Contributes to MER#1064","user":{"login":"chriadam","id":2569776,"avatar_url":"https://avatars.githubusercontent.com/u/2569776?v=3","gravatar_id":"","url":"https://api.github.com/users/chriadam","html_url":"https://github.com/chriadam","followers_url":"https://api.github.com/users/chriadam/followers","following_url":"https://api.github.com/users/chriadam/following{/other_user}","gists_url":"https://api.github.com/users/chriadam/gists{/gist_id}","starred_url":"https://api.github.com/users/chriadam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chriadam/subscriptions","organizations_url":"https://api.github.com/users/chriadam/orgs","repos_url":"https://api.github.com/users/chriadam/repos","events_url":"https://api.github.com/users/chriadam/events{/privacy}","received_events_url":"https://api.github.com/users/chriadam/received_events","type":"User","site_admin":false},"body":"This commit improves the Twitter Notifications sync adapter so that\r\nit now syncs multiple different types of notifications from Twitter.\r\n\r\nContributes to MER#1064","created_at":"2015-06-03T11:56:52Z","updated_at":"2015-06-05T06:45:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"62de55e5c63219621f787bc1076ef8c29fcdb717","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30/commits","review_comments_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30/comments","review_comment_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/30/comments","statuses_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/statuses/68e964055f6b5e94f8daaf7a52d42e4b8deec002","head":{"label":"nemomobile:twitternotifications","ref":"twitternotifications","sha":"68e964055f6b5e94f8daaf7a52d42e4b8deec002","user":{"login":"nemomobile","id":1088947,"avatar_url":"https://avatars.githubusercontent.com/u/1088947?v=3","gravatar_id":"","url":"https://api.github.com/users/nemomobile","html_url":"https://github.com/nemomobile","followers_url":"https://api.github.com/users/nemomobile/followers","following_url":"https://api.github.com/users/nemomobile/following{/other_user}","gists_url":"https://api.github.com/users/nemomobile/gists{/gist_id}","starred_url":"https://api.github.com/users/nemomobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nemomobile/subscriptions","organizations_url":"https://api.github.com/users/nemomobile/orgs","repos_url":"https://api.github.com/users/nemomobile/repos","events_url":"https://api.github.com/users/nemomobile/events{/privacy}","received_events_url":"https://api.github.com/users/nemomobile/received_events","type":"Organization","site_admin":false},"repo":{"id":20760271,"name":"buteo-sync-plugins-social","full_name":"nemomobile/buteo-sync-plugins-social","owner":{"login":"nemomobile","id":1088947,"avatar_url":"https://avatars.githubusercontent.com/u/1088947?v=3","gravatar_id":"","url":"https://api.github.com/users/nemomobile","html_url":"https://github.com/nemomobile","followers_url":"https://api.github.com/users/nemomobile/followers","following_url":"https://api.github.com/users/nemomobile/following{/other_user}","gists_url":"https://api.github.com/users/nemomobile/gists{/gist_id}","starred_url":"https://api.github.com/users/nemomobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nemomobile/subscriptions","organizations_url":"https://api.github.com/users/nemomobile/orgs","repos_url":"https://api.github.com/users/nemomobile/repos","events_url":"https://api.github.com/users/nemomobile/events{/privacy}","received_events_url":"https://api.github.com/users/nemomobile/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nemomobile/buteo-sync-plugins-social","description":"","fork":false,"url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social","forks_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/forks","keys_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/teams","hooks_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/hooks","issue_events_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/events{/number}","events_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/events","assignees_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/assignees{/user}","branches_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/branches{/branch}","tags_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/tags","blobs_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/refs{/sha}","trees_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/statuses/{sha}","languages_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/languages","stargazers_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/stargazers","contributors_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/contributors","subscribers_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/subscribers","subscription_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/subscription","commits_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/commits{/sha}","git_commits_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/commits{/sha}","comments_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/comments{/number}","issue_comment_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/comments{/number}","contents_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/contents/{+path}","compare_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/merges","archive_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/downloads","issues_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues{/number}","pulls_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls{/number}","milestones_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/milestones{/number}","notifications_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/labels{/name}","releases_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/releases{/id}","created_at":"2014-06-12T08:32:31Z","updated_at":"2015-05-16T16:13:51Z","pushed_at":"2015-06-05T06:41:42Z","git_url":"git://github.com/nemomobile/buteo-sync-plugins-social.git","ssh_url":"git@github.com:nemomobile/buteo-sync-plugins-social.git","clone_url":"https://github.com/nemomobile/buteo-sync-plugins-social.git","svn_url":"https://github.com/nemomobile/buteo-sync-plugins-social","homepage":null,"size":2375,"stargazers_count":14,"watchers_count":14,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":6,"forks":5,"open_issues":6,"watchers":14,"default_branch":"master"}},"base":{"label":"nemomobile:master","ref":"master","sha":"555dbfcfbd7dc58221a8d5f22c2f692ce5caabc0","user":{"login":"nemomobile","id":1088947,"avatar_url":"https://avatars.githubusercontent.com/u/1088947?v=3","gravatar_id":"","url":"https://api.github.com/users/nemomobile","html_url":"https://github.com/nemomobile","followers_url":"https://api.github.com/users/nemomobile/followers","following_url":"https://api.github.com/users/nemomobile/following{/other_user}","gists_url":"https://api.github.com/users/nemomobile/gists{/gist_id}","starred_url":"https://api.github.com/users/nemomobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nemomobile/subscriptions","organizations_url":"https://api.github.com/users/nemomobile/orgs","repos_url":"https://api.github.com/users/nemomobile/repos","events_url":"https://api.github.com/users/nemomobile/events{/privacy}","received_events_url":"https://api.github.com/users/nemomobile/received_events","type":"Organization","site_admin":false},"repo":{"id":20760271,"name":"buteo-sync-plugins-social","full_name":"nemomobile/buteo-sync-plugins-social","owner":{"login":"nemomobile","id":1088947,"avatar_url":"https://avatars.githubusercontent.com/u/1088947?v=3","gravatar_id":"","url":"https://api.github.com/users/nemomobile","html_url":"https://github.com/nemomobile","followers_url":"https://api.github.com/users/nemomobile/followers","following_url":"https://api.github.com/users/nemomobile/following{/other_user}","gists_url":"https://api.github.com/users/nemomobile/gists{/gist_id}","starred_url":"https://api.github.com/users/nemomobile/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nemomobile/subscriptions","organizations_url":"https://api.github.com/users/nemomobile/orgs","repos_url":"https://api.github.com/users/nemomobile/repos","events_url":"https://api.github.com/users/nemomobile/events{/privacy}","received_events_url":"https://api.github.com/users/nemomobile/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nemomobile/buteo-sync-plugins-social","description":"","fork":false,"url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social","forks_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/forks","keys_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/teams","hooks_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/hooks","issue_events_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/events{/number}","events_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/events","assignees_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/assignees{/user}","branches_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/branches{/branch}","tags_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/tags","blobs_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/refs{/sha}","trees_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/statuses/{sha}","languages_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/languages","stargazers_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/stargazers","contributors_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/contributors","subscribers_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/subscribers","subscription_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/subscription","commits_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/commits{/sha}","git_commits_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/git/commits{/sha}","comments_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/comments{/number}","issue_comment_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/comments{/number}","contents_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/contents/{+path}","compare_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/merges","archive_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/downloads","issues_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues{/number}","pulls_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls{/number}","milestones_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/milestones{/number}","notifications_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/labels{/name}","releases_url":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/releases{/id}","created_at":"2014-06-12T08:32:31Z","updated_at":"2015-05-16T16:13:51Z","pushed_at":"2015-06-05T06:41:42Z","git_url":"git://github.com/nemomobile/buteo-sync-plugins-social.git","ssh_url":"git@github.com:nemomobile/buteo-sync-plugins-social.git","clone_url":"https://github.com/nemomobile/buteo-sync-plugins-social.git","svn_url":"https://github.com/nemomobile/buteo-sync-plugins-social","homepage":null,"size":2375,"stargazers_count":14,"watchers_count":14,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":6,"forks":5,"open_issues":6,"watchers":14,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30"},"html":{"href":"https://github.com/nemomobile/buteo-sync-plugins-social/pull/30"},"issue":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/30"},"comments":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/issues/30/comments"},"review_comments":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30/comments"},"review_comment":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/pulls/30/commits"},"statuses":{"href":"https://api.github.com/repos/nemomobile/buteo-sync-plugins-social/statuses/68e964055f6b5e94f8daaf7a52d42e4b8deec002"}}}}