File indexing completed on 2024-04-28 04:55:37
0001 /* 0002 This file is part of Choqok, the KDE micro-blogging client 0003 0004 SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "twitterapimicroblog.h" 0010 0011 #include <QAction> 0012 #include <QJsonDocument> 0013 #include <QMenu> 0014 #include <QStandardPaths> 0015 0016 #include <KIO/Job> 0017 #include <KLocalizedString> 0018 #include <KSharedConfig> 0019 0020 #include "account.h" 0021 #include "accountmanager.h" 0022 #include "application.h" 0023 #include "choqokappearancesettings.h" 0024 #include "choqokbehaviorsettings.h" 0025 #include "choqokuiglobal.h" 0026 #include "editaccountwidget.h" 0027 #include "microblogwidget.h" 0028 #include "notifymanager.h" 0029 #include "postwidget.h" 0030 #include "timelinewidget.h" 0031 0032 #include "twitterapiaccount.h" 0033 #include "twitterapicomposerwidget.h" 0034 #include "twitterapidebug.h" 0035 #include "twitterapidmessagedialog.h" 0036 #include "twitterapipostwidget.h" 0037 #include "twitterapisearch.h" 0038 #include "twitterapisearchdialog.h" 0039 #include "twitterapisearchtimelinewidget.h" 0040 0041 class TwitterApiMicroBlog::Private 0042 { 0043 public: 0044 Private(): countOfTimelinesToSave(0), friendsCursor(QLatin1String("-1")) 0045 { 0046 monthes[QLatin1String("Jan")] = 1; 0047 monthes[QLatin1String("Feb")] = 2; 0048 monthes[QLatin1String("Mar")] = 3; 0049 monthes[QLatin1String("Apr")] = 4; 0050 monthes[QLatin1String("May")] = 5; 0051 monthes[QLatin1String("Jun")] = 6; 0052 monthes[QLatin1String("Jul")] = 7; 0053 monthes[QLatin1String("Aug")] = 8; 0054 monthes[QLatin1String("Sep")] = 9; 0055 monthes[QLatin1String("Oct")] = 10; 0056 monthes[QLatin1String("Nov")] = 11; 0057 monthes[QLatin1String("Dec")] = 12; 0058 } 0059 int countOfTimelinesToSave; 0060 QString friendsCursor; 0061 QString followersCursor; 0062 QMap<QString, int> monthes; 0063 }; 0064 0065 TwitterApiMicroBlog::TwitterApiMicroBlog(const QString &componentName, QObject *parent) 0066 : MicroBlog(componentName, parent), d(new Private) 0067 { 0068 qCDebug(CHOQOK); 0069 0070 QStringList timelineTypes; 0071 timelineTypes << QLatin1String("Home") << QLatin1String("Reply") << QLatin1String("Inbox") << QLatin1String("Outbox") << QLatin1String("Favorite") << QLatin1String("ReTweets") << QLatin1String("Public"); 0072 setTimelineNames(timelineTypes); 0073 timelineApiPath[QLatin1String("Home")] = QLatin1String("/statuses/home_timeline.json"); 0074 timelineApiPath[QLatin1String("Reply")] = QLatin1String("/statuses/replies.json"); 0075 timelineApiPath[QLatin1String("Inbox")] = QLatin1String("/direct_messages.json"); 0076 timelineApiPath[QLatin1String("Outbox")] = QLatin1String("/direct_messages/sent.json"); 0077 timelineApiPath[QLatin1String("Favorite")] = QLatin1String("/favorites/list.json"); 0078 timelineApiPath[QLatin1String("ReTweets")] = QLatin1String("/statuses/retweets_of_me.json"); 0079 timelineApiPath[QLatin1String("Public")] = QLatin1String("/statuses/public_timeline.json"); 0080 setTimelineInfos(); 0081 } 0082 0083 void TwitterApiMicroBlog::setTimelineInfos() 0084 { 0085 Choqok::TimelineInfo *t = new Choqok::TimelineInfo; 0086 t->name = i18nc("Timeline Name", "Home"); 0087 t->description = i18nc("Timeline description", "You and your friends"); 0088 t->icon = QLatin1String("user-home"); 0089 mTimelineInfos[QLatin1String("Home")] = std::move(t); 0090 0091 t = new Choqok::TimelineInfo; 0092 t->name = i18nc("Timeline Name", "Reply"); 0093 t->description = i18nc("Timeline description", "Replies to you"); 0094 t->icon = QLatin1String("edit-undo"); 0095 mTimelineInfos[QLatin1String("Reply")] = std::move(t); 0096 0097 t = new Choqok::TimelineInfo; 0098 t->name = i18nc("Timeline Name", "Inbox"); 0099 t->description = i18nc("Timeline description", "Your incoming private messages"); 0100 t->icon = QLatin1String("mail-folder-inbox"); 0101 mTimelineInfos[QLatin1String("Inbox")] = std::move(t); 0102 0103 t = new Choqok::TimelineInfo; 0104 t->name = i18nc("Timeline Name", "Outbox"); 0105 t->description = i18nc("Timeline description", "Private messages you have sent"); 0106 t->icon = QLatin1String("mail-folder-outbox"); 0107 mTimelineInfos[QLatin1String("Outbox")] = std::move(t); 0108 0109 t = new Choqok::TimelineInfo; 0110 t->name = i18nc("Timeline Name", "Favorite"); 0111 t->description = i18nc("Timeline description", "Your favorites"); 0112 t->icon = QLatin1String("favorites"); 0113 mTimelineInfos[QLatin1String("Favorite")] = std::move(t); 0114 0115 t = new Choqok::TimelineInfo; 0116 t->name = i18nc("Timeline Name", "Public"); 0117 t->description = i18nc("Timeline description", "Public timeline"); 0118 t->icon = QLatin1String("folder-green"); 0119 mTimelineInfos[QLatin1String("Public")] = std::move(t); 0120 0121 t = new Choqok::TimelineInfo; 0122 t->name = i18nc("Timeline Name", "ReTweets"); 0123 t->description = i18nc("Timeline description", "Your posts that were ReTweeted by others"); 0124 t->icon = QLatin1String("folder-red"); 0125 mTimelineInfos[QLatin1String("ReTweets")] = std::move(t); 0126 } 0127 0128 TwitterApiMicroBlog::~TwitterApiMicroBlog() 0129 { 0130 qDeleteAll(mTimelineInfos); 0131 delete d; 0132 } 0133 0134 QMenu *TwitterApiMicroBlog::createActionsMenu(Choqok::Account *theAccount, QWidget *parent) 0135 { 0136 QMenu *menu = MicroBlog::createActionsMenu(theAccount, parent); 0137 0138 QAction *directMessge = new QAction(QIcon::fromTheme(QLatin1String("mail-message-new")), i18n("Send Private Message..."), menu); 0139 directMessge->setData(theAccount->alias()); 0140 connect(directMessge, SIGNAL(triggered(bool)), SLOT(showDirectMessageDialog())); 0141 menu->addAction(directMessge); 0142 0143 QAction *search = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), menu); 0144 search->setData(theAccount->alias()); 0145 connect(search, SIGNAL(triggered(bool)), SLOT(showSearchDialog())); 0146 menu->addAction(search); 0147 0148 QAction *updateFriendsList = new QAction(QIcon::fromTheme(QLatin1String("arrow-down")), i18n("Update Friends List"), menu); 0149 updateFriendsList->setData(theAccount->alias()); 0150 connect(updateFriendsList, &QAction::triggered, this, &TwitterApiMicroBlog::slotUpdateFriendsList); 0151 menu->addAction(updateFriendsList); 0152 0153 return menu; 0154 } 0155 0156 QList< Choqok::Post * > TwitterApiMicroBlog::loadTimeline(Choqok::Account *account, 0157 const QString &timelineName) 0158 { 0159 QList< Choqok::Post * > list; 0160 if (timelineName.compare(QLatin1String("Favorite")) == 0) { 0161 return list; //NOTE Won't cache favorites, and this is for compatibility with older versions! 0162 } 0163 qCDebug(CHOQOK) << timelineName; 0164 QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); 0165 KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); 0166 QStringList tmpList = postsBackup.groupList(); 0167 0168 /// to don't load old archives 0169 if (tmpList.isEmpty() || !(QDateTime::fromString(tmpList.first()).isValid())) { 0170 return list; 0171 } 0172 ///-------------- 0173 0174 QList<QDateTime> groupList; 0175 for (const QString &str: tmpList) { 0176 groupList.append(QDateTime::fromString(str)); 0177 } 0178 std::sort(groupList.begin(), groupList.end()); 0179 int count = groupList.count(); 0180 if (count) { 0181 Choqok::Post *st = nullptr; 0182 for (int i = 0; i < count; ++i) { 0183 st = new Choqok::Post; 0184 KConfigGroup grp(&postsBackup, groupList[i].toString()); 0185 st->creationDateTime = grp.readEntry("creationDateTime", QDateTime::currentDateTime()); 0186 st->postId = grp.readEntry("postId", QString()); 0187 st->content = grp.readEntry("text", QString()); 0188 st->source = grp.readEntry("source", QString()); 0189 st->replyToPostId = grp.readEntry("inReplyToPostId", QString()); 0190 st->replyToUser.userId = grp.readEntry("inReplyToUserId", QString()); 0191 st->isFavorited = grp.readEntry("favorited", false); 0192 st->replyToUser.userName = grp.readEntry("inReplyToUserName", QString()); 0193 st->author.userId = grp.readEntry("authorId", QString()); 0194 st->author.userName = grp.readEntry("authorUserName", QString()); 0195 st->author.realName = grp.readEntry("authorRealName", QString()); 0196 st->author.homePageUrl = grp.readEntry("authorHomePageUrl", QUrl()); 0197 st->author.profileImageUrl = grp.readEntry("authorProfileImageUrl", QUrl()); 0198 st->author.description = grp.readEntry("authorDescription" , QString()); 0199 st->author.isProtected = grp.readEntry("isProtected", false); 0200 st->isPrivate = grp.readEntry("isPrivate" , false); 0201 st->author.location = grp.readEntry("authorLocation", QString()); 0202 st->link = postUrl(account, st->author.userName, st->postId); 0203 st->isRead = grp.readEntry("isRead", true); 0204 st->repeatedFromUser.userName = grp.readEntry("repeatedFrom", QString()); 0205 st->repeatedFromUser.homePageUrl = grp.readEntry("repeatedFromUserHomePage", QUrl()); 0206 st->repeatedPostId = grp.readEntry("repeatedPostId", QString()); 0207 st->repeatedDateTime = grp.readEntry("repeatedDateTime", QDateTime()); 0208 st->conversationId = grp.readEntry("conversationId", QString()); 0209 st->media = grp.readEntry("mediaUrl", QUrl()); 0210 st->quotedPost.postId = grp.readEntry("quotedPostId", QString()); 0211 st->quotedPost.user.profileImageUrl = grp.readEntry("quotedProfileUrl", QUrl()); 0212 st->quotedPost.content = grp.readEntry("quotedContent", QString()); 0213 st->quotedPost.user.userName = grp.readEntry("quotedUsername", QString()); 0214 0215 list.append(st); 0216 } 0217 mTimelineLatestId[account][timelineName] = st->postId; 0218 } 0219 return list; 0220 } 0221 0222 void TwitterApiMicroBlog::saveTimeline(Choqok::Account *account, 0223 const QString &timelineName, 0224 const QList< Choqok::UI::PostWidget * > &timeline) 0225 { 0226 if (timelineName.compare(QLatin1String("Favorite")) != 0) { 0227 qCDebug(CHOQOK); 0228 QString fileName = Choqok::AccountManager::generatePostBackupFileName(account->alias(), timelineName); 0229 KConfig postsBackup(fileName, KConfig::NoGlobals, QStandardPaths::DataLocation); 0230 0231 ///Clear previous data: 0232 for (const QString &group: postsBackup.groupList()) { 0233 postsBackup.deleteGroup(group); 0234 } 0235 0236 for (Choqok::UI::PostWidget *wd: timeline) { 0237 const Choqok::Post *post = (wd->currentPost()); 0238 KConfigGroup grp(&postsBackup, post->creationDateTime.toString()); 0239 grp.writeEntry("creationDateTime", post->creationDateTime); 0240 grp.writeEntry("postId", post->postId); 0241 grp.writeEntry("text", post->content); 0242 grp.writeEntry("source", post->source); 0243 grp.writeEntry("inReplyToPostId", post->replyToPostId); 0244 grp.writeEntry("inReplyToUserId", post->replyToUser.userId); 0245 grp.writeEntry("favorited", post->isFavorited); 0246 grp.writeEntry("inReplyToUserName", post->replyToUser.userName); 0247 grp.writeEntry("authorId", post->author.userId); 0248 grp.writeEntry("authorUserName", post->author.userName); 0249 grp.writeEntry("authorRealName", post->author.realName); 0250 grp.writeEntry("authorHomePageUrl", post->author.homePageUrl); 0251 grp.writeEntry("authorProfileImageUrl", post->author.profileImageUrl); 0252 grp.writeEntry("authorDescription" , post->author.description); 0253 grp.writeEntry("isPrivate" , post->isPrivate); 0254 grp.writeEntry("authorLocation" , post->author.location); 0255 grp.writeEntry("isProtected" , post->author.isProtected); 0256 grp.writeEntry("isRead" , post->isRead); 0257 grp.writeEntry("repeatedFrom", post->repeatedFromUser.userName); 0258 grp.writeEntry("repeatedFromUserHomePage", post->repeatedFromUser.homePageUrl); 0259 grp.writeEntry("repeatedPostId", post->repeatedPostId); 0260 grp.writeEntry("repeatedDateTime", post->repeatedDateTime); 0261 grp.writeEntry("conversationId", post->conversationId); 0262 grp.writeEntry("mediaUrl", post->media); 0263 grp.writeEntry("quotedPostId", post->quotedPost.postId); 0264 grp.writeEntry("quotedProfileUrl", post->quotedPost.user.profileImageUrl); 0265 grp.writeEntry("quotedContent", post->quotedPost.content); 0266 grp.writeEntry("quotedUsername", post->quotedPost.user.userName); 0267 } 0268 postsBackup.sync(); 0269 } 0270 if (Choqok::Application::isShuttingDown()) { 0271 --d->countOfTimelinesToSave; 0272 if (d->countOfTimelinesToSave < 1) { 0273 Q_EMIT readyForUnload(); 0274 } 0275 } 0276 } 0277 0278 Choqok::UI::ComposerWidget *TwitterApiMicroBlog::createComposerWidget(Choqok::Account *account, QWidget *parent) 0279 { 0280 return new TwitterApiComposerWidget(account, parent); 0281 } 0282 0283 TwitterApiSearchTimelineWidget *TwitterApiMicroBlog::createSearchTimelineWidget(Choqok::Account *theAccount, 0284 QString name, 0285 const SearchInfo &info, 0286 QWidget *parent) 0287 { 0288 return new TwitterApiSearchTimelineWidget(theAccount, name, info, parent); 0289 } 0290 0291 void TwitterApiMicroBlog::createPost(Choqok::Account *theAccount, Choqok::Post *post) 0292 { 0293 qCDebug(CHOQOK); 0294 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0295 QByteArray data; 0296 QVariantMap params; 0297 if (!post || post->content.isEmpty()) { 0298 qCDebug(CHOQOK) << "ERROR: Status text is empty!"; 0299 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::OtherError, 0300 i18n("Creating the new post failed. Text is empty."), MicroBlog::Critical); 0301 return; 0302 } 0303 if (!post->isPrivate) { ///Status Update 0304 QUrl url = account->apiUrl(); 0305 url.setPath(url.path() + QLatin1String("/statuses/update.json")); 0306 params.insert(QLatin1String("status"), post->content); 0307 if (!post->replyToPostId.isEmpty()) { 0308 params.insert(QLatin1String("in_reply_to_status_id"), post->replyToPostId.toLatin1()); 0309 } 0310 data = "status="; 0311 data += QUrl::toPercentEncoding(post->content); 0312 if (!post->replyToPostId.isEmpty()) { 0313 data += "&in_reply_to_status_id="; 0314 data += post->replyToPostId.toLatin1(); 0315 } 0316 if (!account->usingOAuth()) { 0317 data += "&source=Choqok"; 0318 } 0319 KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; 0320 if (!job) { 0321 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0322 return; 0323 } 0324 job->addMetaData(QStringLiteral("content-type"), 0325 QStringLiteral("Content-Type: application/x-www-form-urlencoded")); 0326 job->addMetaData(QStringLiteral("customHTTPHeader"), 0327 QStringLiteral("Authorization: ") + 0328 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params))); 0329 mCreatePostMap[ job ] = post; 0330 mJobsAccount[job] = theAccount; 0331 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost); 0332 job->start(); 0333 } else {///Direct message 0334 QString recipientScreenName = post->replyToUser.userName; 0335 QUrl url = account->apiUrl(); 0336 url.setPath(url.path() + QLatin1String("/direct_messages/new.json")); 0337 params.insert(QLatin1String("user"), recipientScreenName.toLatin1()); 0338 params.insert(QLatin1String("text"), post->content); 0339 data = "user="; 0340 data += recipientScreenName.toLatin1(); 0341 data += "&text="; 0342 data += QUrl::toPercentEncoding(post->content); 0343 if (!account->usingOAuth()) { 0344 data += "&source=Choqok"; 0345 } 0346 KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; 0347 if (!job) { 0348 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0349 // QString errMsg = i18n ( "Creating the new post failed. Cannot create an http POST request. Please check your KDE installation." ); 0350 // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); 0351 return; 0352 } 0353 job->addMetaData(QStringLiteral("content-type"), 0354 QStringLiteral("Content-Type: application/x-www-form-urlencoded")); 0355 job->addMetaData(QStringLiteral("customHTTPHeader"), 0356 QStringLiteral("Authorization: ") 0357 + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation, params))); 0358 mCreatePostMap[ job ] = post; 0359 mJobsAccount[job] = theAccount; 0360 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost); 0361 job->start(); 0362 } 0363 } 0364 0365 void TwitterApiMicroBlog::repeatPost(Choqok::Account *theAccount, const QString &postId) 0366 { 0367 qCDebug(CHOQOK); 0368 if (postId.isEmpty()) { 0369 qCCritical(CHOQOK) << "ERROR: PostId is empty!"; 0370 return; 0371 } 0372 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0373 QUrl url = account->apiUrl(); 0374 url.setPath(url.path() + QStringLiteral("/statuses/retweet/%1.json").arg(postId)); 0375 QByteArray data; 0376 KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo) ; 0377 if (!job) { 0378 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0379 return; 0380 } 0381 job->addMetaData(QStringLiteral("content-type"), 0382 QStringLiteral("Content-Type: application/x-www-form-urlencoded")); 0383 job->addMetaData(QStringLiteral("customHTTPHeader"), 0384 QStringLiteral("Authorization: ") + 0385 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 0386 Choqok::Post *post = new Choqok::Post; 0387 post->postId = postId; 0388 mCreatePostMap[ job ] = post; 0389 mJobsAccount[job] = theAccount; 0390 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreatePost); 0391 job->start(); 0392 } 0393 0394 void TwitterApiMicroBlog::slotCreatePost(KJob *job) 0395 { 0396 qCDebug(CHOQOK); 0397 if (!job) { 0398 qCDebug(CHOQOK) << "Job is null pointer"; 0399 return; 0400 } 0401 Choqok::Post *post = mCreatePostMap.take(job); 0402 Choqok::Account *theAccount = mJobsAccount.take(job); 0403 if (!post || !theAccount) { 0404 qCDebug(CHOQOK) << "Account or Post is NULL pointer"; 0405 return; 0406 } 0407 if (job->error()) { 0408 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0409 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::CommunicationError, 0410 i18n("Creating the new post failed: %1", job->errorString()), MicroBlog::Critical); 0411 } else { 0412 KIO::StoredTransferJob *stj = qobject_cast< KIO::StoredTransferJob * > (job); 0413 if (!post->isPrivate) { 0414 readPost(theAccount, stj->data(), post); 0415 if (post->isError) { 0416 QString errorMsg; 0417 errorMsg = checkForError(stj->data()); 0418 if (errorMsg.isEmpty()) { // We get the error message by parsing the JSON output, if there was a parsing error, then we don't have an error message, while there were still an error because of the error flag 0419 qCCritical(CHOQOK) << "Creating post: JSON parsing error:" << stj->data() ; 0420 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, 0421 i18n("Creating the new post failed. The result data could not be parsed."), MicroBlog::Critical); 0422 } else { 0423 qCCritical(CHOQOK) << "Server Error:" << errorMsg ; 0424 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, 0425 i18n("Creating the new post failed, with error: %1", errorMsg), 0426 MicroBlog::Critical); 0427 } 0428 } else { 0429 Choqok::NotifyManager::success(i18n("New post for account %1 submitted successfully", theAccount->alias())); 0430 Q_EMIT postCreated(theAccount, post); 0431 } 0432 } else { 0433 Choqok::NotifyManager::success(i18n("Private message sent successfully")); 0434 Q_EMIT postCreated(theAccount, post); 0435 } 0436 } 0437 } 0438 0439 void TwitterApiMicroBlog::abortAllJobs(Choqok::Account *theAccount) 0440 { 0441 for (KJob *job: mJobsAccount.keys(theAccount)) { 0442 job->kill(KJob::EmitResult); 0443 } 0444 } 0445 0446 void TwitterApiMicroBlog::abortCreatePost(Choqok::Account *theAccount, Choqok::Post *post) 0447 { 0448 if (mCreatePostMap.isEmpty()) { 0449 return; 0450 } 0451 if (post) { 0452 mCreatePostMap.key(post)->kill(KJob::EmitResult); 0453 } else { 0454 for (KJob *job: mCreatePostMap.keys()) { 0455 if (mJobsAccount[job] == theAccount) { 0456 job->kill(KJob::EmitResult); 0457 } 0458 } 0459 } 0460 } 0461 0462 void TwitterApiMicroBlog::fetchPost(Choqok::Account *theAccount, Choqok::Post *post) 0463 { 0464 qCDebug(CHOQOK); 0465 if (!post || post->postId.isEmpty()) { 0466 return; 0467 } 0468 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0469 QUrl url = account->apiUrl(); 0470 url.setPath(url.path() + QStringLiteral("/statuses/show/%1.json").arg(post->postId)); 0471 0472 KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; 0473 if (!job) { 0474 qCDebug(CHOQOK) << "Cannot create an http GET request!"; 0475 // QString errMsg = i18n ( "Fetching the new post failed. Cannot create an HTTP GET request." 0476 // "Please check your KDE installation." ); 0477 // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, Low ); 0478 return; 0479 } 0480 job->addMetaData(QStringLiteral("customHTTPHeader"), 0481 QStringLiteral("Authorization: ") + 0482 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); 0483 mFetchPostMap[ job ] = post; 0484 mJobsAccount[ job ] = theAccount; 0485 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotFetchPost); 0486 job->start(); 0487 } 0488 0489 void TwitterApiMicroBlog::slotFetchPost(KJob *job) 0490 { 0491 qCDebug(CHOQOK); 0492 if (!job) { 0493 qCWarning(CHOQOK) << "NULL Job returned"; 0494 return; 0495 } 0496 Choqok::Post *post = mFetchPostMap.take(job); 0497 Choqok::Account *theAccount = mJobsAccount.take(job); 0498 if (job->error()) { 0499 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0500 Q_EMIT error(theAccount, Choqok::MicroBlog::CommunicationError, 0501 i18n("Fetching the new post failed. %1", job->errorString()), Low); 0502 } else { 0503 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *> (job); 0504 readPost(theAccount, stj->data(), post); 0505 if (post->isError) { 0506 QString errorMsg; 0507 errorMsg = checkForError(stj->data()); 0508 if (errorMsg.isEmpty()) { 0509 qCDebug(CHOQOK) << "Parsing Error"; 0510 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ParsingError, 0511 i18n("Fetching new post failed. The result data could not be parsed."), 0512 Low); 0513 } else { 0514 qCCritical(CHOQOK) << "Fetching post: Server Error:" << errorMsg; 0515 Q_EMIT errorPost(theAccount, post, Choqok::MicroBlog::ServerError, 0516 i18n("Fetching new post failed, with error:%1", errorMsg), 0517 Low); 0518 } 0519 } else { 0520 post->isError = true; 0521 Q_EMIT postFetched(theAccount, post); 0522 } 0523 } 0524 } 0525 0526 void TwitterApiMicroBlog::removePost(Choqok::Account *theAccount, Choqok::Post *post) 0527 { 0528 qCDebug(CHOQOK); 0529 if (!post->postId.isEmpty()) { 0530 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0531 QUrl url = account->apiUrl(); 0532 if (!post->isPrivate) { 0533 url.setPath(url.path() + QStringLiteral("/statuses/destroy/%1.json").arg(post->postId)); 0534 } else { 0535 url.setPath(url.path() + QStringLiteral("/direct_messages/destroy/%1.json").arg(post->postId)); 0536 } 0537 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 0538 if (!job) { 0539 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0540 // QString errMsg = i18n ( "Removing the post failed. Cannot create an HTTP POST request. Please check your KDE installation." ); 0541 // emit errorPost ( theAccount, post, Choqok::MicroBlog::OtherError, errMsg, MicroBlog::Critical ); 0542 return; 0543 } 0544 job->addMetaData(QStringLiteral("customHTTPHeader"), 0545 QStringLiteral("Authorization: ") + 0546 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 0547 mRemovePostMap[job] = post; 0548 mJobsAccount[job] = theAccount; 0549 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRemovePost); 0550 job->start(); 0551 } 0552 } 0553 0554 void TwitterApiMicroBlog::slotRemovePost(KJob *job) 0555 { 0556 qCDebug(CHOQOK); 0557 if (!job) { 0558 qCDebug(CHOQOK) << "Job is null pointer."; 0559 return; 0560 } 0561 Choqok::Post *post = mRemovePostMap.take(job); 0562 Choqok::Account *theAccount = mJobsAccount.take(job); 0563 if (job->error()) { 0564 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0565 Q_EMIT errorPost(theAccount, post, CommunicationError, 0566 i18n("Removing the post failed. %1", job->errorString()), MicroBlog::Critical); 0567 } else { 0568 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job); 0569 QString errMsg = checkForError(stj->data()); 0570 if (errMsg.isEmpty()) { 0571 Q_EMIT postRemoved(theAccount, post); 0572 } else { 0573 qCCritical(CHOQOK) << "Server error on removing post:" << errMsg; 0574 Q_EMIT errorPost(theAccount, post, ServerError, 0575 i18n("Removing the post failed. %1", errMsg), MicroBlog::Critical); 0576 } 0577 } 0578 } 0579 0580 void TwitterApiMicroBlog::createFavorite(Choqok::Account *theAccount, const QString &postId) 0581 { 0582 qCDebug(CHOQOK); 0583 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0584 QUrl url = account->apiUrl(); 0585 url.setPath(url.path() + QLatin1String("/favorites/create.json")); 0586 0587 QUrlQuery urlQuery; 0588 urlQuery.addQueryItem(QLatin1String("id"), postId); 0589 url.setQuery(urlQuery); 0590 0591 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 0592 if (!job) { 0593 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0594 // QString errMsg = i18n ( "The Favorite creation failed. Cannot create an http POST request. " 0595 // "Please check your KDE installation." ); 0596 // emit error ( theAccount, OtherError, errMsg ); 0597 return; 0598 } 0599 job->addMetaData(QStringLiteral("customHTTPHeader"), 0600 QStringLiteral("Authorization: ") + 0601 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 0602 mFavoriteMap[job] = postId; 0603 mJobsAccount[job] = theAccount; 0604 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreateFavorite); 0605 job->start(); 0606 } 0607 0608 void TwitterApiMicroBlog::slotCreateFavorite(KJob *job) 0609 { 0610 qCDebug(CHOQOK); 0611 if (!job) { 0612 qCDebug(CHOQOK) << "Job is null pointer."; 0613 return; 0614 } 0615 Choqok::Account *theAccount = mJobsAccount.take(job); 0616 QString postId = mFavoriteMap.take(job); 0617 if (job->error()) { 0618 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0619 Q_EMIT error(theAccount, CommunicationError, i18n("Favorite creation failed. %1", job->errorString())); 0620 } else { 0621 KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job); 0622 QString err = checkForError(stJob->data()); 0623 if (!err.isEmpty()) { 0624 Q_EMIT error(theAccount, ServerError, err, Critical); 0625 return; 0626 } else { 0627 Q_EMIT favoriteCreated(theAccount, postId); 0628 } 0629 } 0630 } 0631 0632 void TwitterApiMicroBlog::removeFavorite(Choqok::Account *theAccount, const QString &postId) 0633 { 0634 qCDebug(CHOQOK); 0635 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0636 QUrl url = account->apiUrl(); 0637 url.setPath(url.path() + QLatin1String("/favorites/destroy.json")); 0638 0639 QUrlQuery urlQuery; 0640 urlQuery.addQueryItem(QLatin1String("id"), postId); 0641 url.setQuery(urlQuery); 0642 0643 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 0644 if (!job) { 0645 qCDebug(CHOQOK) << "Cannot create an http POST request!"; 0646 // QString errMsg = i18n ( "Removing the favorite failed. Cannot create an http POST request. " 0647 // "Please check your KDE installation." ); 0648 // emit error ( theAccount, OtherError, errMsg ); 0649 return; 0650 } 0651 job->addMetaData(QStringLiteral("customHTTPHeader"), 0652 QStringLiteral("Authorization: ") + 0653 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 0654 mFavoriteMap[job] = postId; 0655 mJobsAccount[job] = theAccount; 0656 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRemoveFavorite); 0657 job->start(); 0658 } 0659 0660 void TwitterApiMicroBlog::slotRemoveFavorite(KJob *job) 0661 { 0662 qCDebug(CHOQOK); 0663 if (!job) { 0664 qCDebug(CHOQOK) << "Job is null pointer."; 0665 return; 0666 } 0667 QString id = mFavoriteMap.take(job); 0668 Choqok::Account *theAccount = mJobsAccount.take(job); 0669 if (job->error()) { 0670 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0671 Q_EMIT error(theAccount, CommunicationError, i18n("Removing the favorite failed. %1", job->errorString())); 0672 } else { 0673 KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job); 0674 QString err = checkForError(stJob->data()); 0675 if (!err.isEmpty()) { 0676 Q_EMIT error(theAccount, ServerError, err, Critical); 0677 return; 0678 } else { 0679 Q_EMIT favoriteRemoved(theAccount, id); 0680 } 0681 } 0682 } 0683 0684 void TwitterApiMicroBlog::listFriendsUsername(TwitterApiAccount *theAccount, bool active) 0685 { 0686 friendsList.clear(); 0687 d->friendsCursor = QLatin1String("-1"); 0688 if (theAccount) { 0689 requestFriendsScreenName(theAccount, active); 0690 } 0691 } 0692 0693 void TwitterApiMicroBlog::requestFriendsScreenName(TwitterApiAccount *theAccount, bool active) 0694 { 0695 qCDebug(CHOQOK); 0696 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0697 QUrl url = account->apiUrl(); 0698 url = url.adjusted(QUrl::StripTrailingSlash); 0699 url.setPath(url.path() + QLatin1String("/friends/list.json")); 0700 0701 QUrlQuery urlQuery; 0702 urlQuery.addQueryItem(QLatin1String("cursor"), d->friendsCursor); 0703 urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); 0704 url.setQuery(urlQuery); 0705 0706 KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; 0707 if (!job) { 0708 qCDebug(CHOQOK) << "Cannot create an http GET request!"; 0709 return; 0710 } 0711 job->addMetaData(QStringLiteral("customHTTPHeader"), 0712 QStringLiteral("Authorization: ") + 0713 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); 0714 mJobsAccount[job] = theAccount; 0715 if (active) { 0716 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFriendsScreenNameActive); 0717 } else { 0718 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive); 0719 } 0720 job->start(); 0721 Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating friends list for account %1...", 0722 theAccount->alias())); 0723 } 0724 0725 void TwitterApiMicroBlog::slotRequestFriendsScreenNameActive(KJob *job) 0726 { 0727 finishRequestFriendsScreenName(job, true); 0728 } 0729 0730 void TwitterApiMicroBlog::slotRequestFriendsScreenNamePassive(KJob *job) 0731 { 0732 finishRequestFriendsScreenName(job, false); 0733 } 0734 0735 void TwitterApiMicroBlog::finishRequestFriendsScreenName(KJob *job, bool active) 0736 { 0737 qCDebug(CHOQOK); 0738 TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job)); 0739 KIO::StoredTransferJob *stJob = qobject_cast<KIO::StoredTransferJob *>(job); 0740 Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; 0741 if (stJob->error()) { 0742 Q_EMIT error(theAccount, ServerError, i18n("Friends list for account %1 could not be updated:\n%2", 0743 theAccount->username(), stJob->errorString()), level); 0744 return; 0745 } 0746 QStringList newList = readFriendsScreenName(theAccount, stJob->data()); 0747 newList.removeDuplicates(); 0748 if (! checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. 0749 theAccount->setFriendsList(friendsList); 0750 Q_EMIT friendsUsernameListed(theAccount, friendsList); 0751 } else if (QString::compare(d->friendsCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had 0752 friendsList << newList; 0753 requestFriendsScreenName(theAccount, active); 0754 } else { 0755 friendsList << newList; 0756 theAccount->setFriendsList(friendsList); 0757 Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Friends list for account %1 has been updated.", 0758 theAccount->alias())); 0759 Q_EMIT friendsUsernameListed(theAccount, friendsList); 0760 } 0761 } 0762 0763 void TwitterApiMicroBlog::listFollowersUsername(TwitterApiAccount* theAccount, bool active) 0764 { 0765 followersList.clear(); 0766 d->followersCursor = QLatin1String("-1"); 0767 if ( theAccount ) { 0768 requestFollowersScreenName(theAccount, active); 0769 } 0770 } 0771 0772 void TwitterApiMicroBlog::requestFollowersScreenName(TwitterApiAccount* theAccount, bool active) 0773 { 0774 qCDebug(CHOQOK); 0775 TwitterApiAccount* account = qobject_cast<TwitterApiAccount*>(theAccount); 0776 QUrl url = account->apiUrl(); 0777 url = url.adjusted(QUrl::StripTrailingSlash); 0778 url.setPath(url.path() + QLatin1String("/followers/list.json")); 0779 0780 QUrlQuery urlQuery; 0781 urlQuery.addQueryItem(QLatin1String("cursor"), d->followersCursor); 0782 urlQuery.addQueryItem(QLatin1String("count"), QLatin1String("200")); 0783 url.setQuery(urlQuery); 0784 0785 KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); 0786 if (!job) { 0787 qCDebug(CHOQOK) << "Cannot create an http GET request!"; 0788 return; 0789 } 0790 job->addMetaData(QStringLiteral("customHTTPHeader"), 0791 QStringLiteral("Authorization: ") + 0792 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); 0793 mJobsAccount[job] = theAccount; 0794 if (active) { 0795 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFollowersScreenNameActive); 0796 } else { 0797 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive); 0798 } 0799 job->start(); 0800 Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Updating followers list for account %1...", 0801 theAccount->alias())); 0802 } 0803 0804 void TwitterApiMicroBlog::slotRequestFollowersScreenNameActive(KJob* job) 0805 { 0806 finishRequestFollowersScreenName(job, true); 0807 } 0808 0809 void TwitterApiMicroBlog::slotRequestFollowersScreenNamePassive(KJob* job) 0810 { 0811 finishRequestFollowersScreenName(job, false); 0812 } 0813 0814 void TwitterApiMicroBlog::finishRequestFollowersScreenName(KJob* job, bool active) 0815 { 0816 qCDebug(CHOQOK); 0817 TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>( mJobsAccount.take(job) ); 0818 KIO::StoredTransferJob* stJob = qobject_cast<KIO::StoredTransferJob*>( job ); 0819 Choqok::MicroBlog::ErrorLevel level = active ? Critical : Low; 0820 if (stJob->error()) { 0821 Q_EMIT error(theAccount, ServerError, i18n("Followers list for account %1 could not be updated:\n%2", 0822 theAccount->username(), stJob->errorString()), level); 0823 return; 0824 } 0825 QStringList newList = readFollowersScreenName(theAccount, stJob->data()); 0826 newList.removeDuplicates(); 0827 if (!checkForError(stJob->data()).isEmpty()) { // if an error occurred, do not replace the friends list. 0828 theAccount->setFollowersList(followersList); 0829 Q_EMIT followersUsernameListed(theAccount, followersList); 0830 } else if (QString::compare(d->followersCursor, QLatin1String("0"))) { // if the cursor is not "0", there is more friends data to be had 0831 followersList << newList; 0832 requestFollowersScreenName(theAccount, active); 0833 } else { 0834 followersList << newList; 0835 theAccount->setFollowersList(followersList); 0836 Choqok::UI::Global::mainWindow()->showStatusMessage(i18n("Followers list for account %1 has been updated.", 0837 theAccount->alias())); 0838 Q_EMIT followersUsernameListed(theAccount, followersList); 0839 } 0840 } 0841 0842 void TwitterApiMicroBlog::updateTimelines(Choqok::Account *theAccount) 0843 { 0844 qCDebug(CHOQOK); 0845 for (const QString &tm: theAccount->timelineNames()) { 0846 requestTimeLine(theAccount, tm, mTimelineLatestId[theAccount][tm]); 0847 } 0848 } 0849 0850 void TwitterApiMicroBlog::requestTimeLine(Choqok::Account *theAccount, QString type, 0851 QString latestStatusId, int page, QString maxId) 0852 { 0853 qCDebug(CHOQOK); 0854 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 0855 QUrl url = account->apiUrl(); 0856 url.setPath(url.path() + timelineApiPath[type]); 0857 0858 QUrlQuery urlQuery; 0859 // needed because lists have different parameter names but 0860 // returned timelines have the same JSON format 0861 if (timelineApiPath[type].contains(QLatin1String("lists/statuses"))) { 0862 0863 // type contains @username/timelinename 0864 const QString slug = type.mid(type.indexOf(QLatin1String("/")) + 1); 0865 urlQuery.addQueryItem(QLatin1String("slug"), slug); 0866 0867 const QString owner = type.mid(1, type.indexOf(QLatin1String("/")) - 1); 0868 urlQuery.addQueryItem(QLatin1String("owner_screen_name"), owner); 0869 } else { 0870 int countOfPost = Choqok::BehaviorSettings::countOfPosts(); 0871 if (!latestStatusId.isEmpty()) { 0872 urlQuery.addQueryItem(QLatin1String("since_id"), latestStatusId); 0873 countOfPost = 200; 0874 } 0875 0876 urlQuery.addQueryItem(QLatin1String("count"), QString::number(countOfPost)); 0877 0878 if (!maxId.isEmpty()) { 0879 urlQuery.addQueryItem(QLatin1String("max_id"), maxId); 0880 } 0881 0882 if (page) { 0883 urlQuery.addQueryItem(QLatin1String("page"), QString::number(page)); 0884 } 0885 } 0886 0887 url.setQuery(urlQuery); 0888 0889 qCDebug(CHOQOK) << "Latest" << type << "Id:" << latestStatusId;// << "apiReq:" << url; 0890 0891 KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo) ; 0892 if (!job) { 0893 qCDebug(CHOQOK) << "Cannot create an http GET request!"; 0894 // QString errMsg = i18n ( "Cannot create an http GET request. Please check your KDE installation." ); 0895 // emit error ( theAccount, OtherError, errMsg, Low ); 0896 return; 0897 } 0898 job->addMetaData(QStringLiteral("customHTTPHeader"), 0899 QStringLiteral("Authorization: ") 0900 + QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); 0901 mRequestTimelineMap[job] = type; 0902 mJobsAccount[job] = theAccount; 0903 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotRequestTimeline); 0904 job->start(); 0905 } 0906 0907 void TwitterApiMicroBlog::slotRequestTimeline(KJob *job) 0908 { 0909 qCDebug(CHOQOK);//TODO Add error detection 0910 if (!job) { 0911 qCDebug(CHOQOK) << "Job is null pointer"; 0912 return; 0913 } 0914 Choqok::Account *theAccount = mJobsAccount.take(job); 0915 if (job->error()) { 0916 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 0917 Q_EMIT error(theAccount, CommunicationError, 0918 i18n("Timeline update failed: %1", job->errorString()), Low); 0919 return; 0920 } 0921 QString type = mRequestTimelineMap.take(job); 0922 if (isValidTimeline(type)) { 0923 KIO::StoredTransferJob *j = qobject_cast<KIO::StoredTransferJob *>(job); 0924 QList<Choqok::Post *> list; 0925 if (type == QLatin1String("Inbox") || type == QLatin1String("Outbox")) { 0926 list = readDirectMessages(theAccount, j->data()); 0927 } else { 0928 list = readTimeline(theAccount, j->data()); 0929 0930 } 0931 if (!list.isEmpty()) { 0932 mTimelineLatestId[theAccount][type] = list.last()->postId; 0933 Q_EMIT timelineDataReceived(theAccount, type, list); 0934 } 0935 } 0936 } 0937 0938 QByteArray TwitterApiMicroBlog::authorizationHeader(TwitterApiAccount *theAccount, const QUrl &requestUrl, 0939 QNetworkAccessManager::Operation method, const QVariantMap ¶ms) 0940 { 0941 QByteArray auth; 0942 if (theAccount->usingOAuth()) { 0943 auth = theAccount->oauthInterface()->authorizationHeader(requestUrl, method, params); 0944 } else { 0945 auth = theAccount->username().toUtf8() + ':' + theAccount->password().toUtf8(); 0946 auth = auth.toBase64().prepend("Basic "); 0947 } 0948 return auth; 0949 } 0950 0951 void TwitterApiMicroBlog::setRepeatedOfInfo(Choqok::Post *post, Choqok::Post *repeatedPost) 0952 { 0953 post->content = repeatedPost->content; 0954 post->replyToPostId = repeatedPost->replyToPostId; 0955 post->replyToUser.userId = repeatedPost->replyToUser.userId; 0956 post->replyToUser.userName = repeatedPost->replyToUser.userName; 0957 post->repeatedPostId = repeatedPost->postId; 0958 post->repeatedDateTime = repeatedPost->creationDateTime; 0959 0960 if (Choqok::AppearanceSettings::showRetweetsInChoqokWay()) { 0961 post->repeatedFromUser.userName = repeatedPost->author.userName; 0962 post->repeatedFromUser.homePageUrl = repeatedPost->author.homePageUrl; 0963 } else { 0964 post->repeatedFromUser.userName = post->author.userName; 0965 post->repeatedFromUser.homePageUrl = post->author.homePageUrl; 0966 post->author = repeatedPost->author; 0967 } 0968 0969 if (!repeatedPost->quotedPost.content.isEmpty()) { 0970 post->quotedPost = repeatedPost->quotedPost; 0971 } 0972 } 0973 void TwitterApiMicroBlog::setQuotedPost(Choqok::Post* post, Choqok::Post* quotedPost) 0974 { 0975 post->quotedPost.user.profileImageUrl = quotedPost->author.profileImageUrl; 0976 post->quotedPost.user.userName = quotedPost->author.userName; 0977 post->quotedPost.postId = quotedPost->postId; 0978 post->quotedPost.content = quotedPost->content; 0979 } 0980 0981 QDateTime TwitterApiMicroBlog::dateFromString(const QString &date) 0982 { 0983 char s[10]; 0984 int year, day, hours, minutes, seconds, tz; 0985 sscanf(qPrintable(date), "%*s %s %d %d:%d:%d %d %d", s, &day, &hours, &minutes, &seconds, &tz, &year); 0986 int month = d->monthes[QLatin1String(s)]; 0987 QDateTime recognized(QDate(year, month, day), QTime(hours, minutes, seconds)); 0988 if (tz == 0) { //tz is the timezone, in Twitter it's always UTC(0) in Identica it's local +/-NUMBER 0989 recognized.setTimeSpec(Qt::UTC); 0990 } 0991 return recognized.toLocalTime(); 0992 } 0993 0994 void TwitterApiMicroBlog::aboutToUnload() 0995 { 0996 d->countOfTimelinesToSave = 0; 0997 for (Choqok::Account *acc: Choqok::AccountManager::self()->accounts()) { 0998 if (acc->microblog() == this) { 0999 d->countOfTimelinesToSave += acc->timelineNames().count(); 1000 } 1001 } 1002 Q_EMIT saveTimelines(); 1003 } 1004 1005 void TwitterApiMicroBlog::showDirectMessageDialog(TwitterApiAccount *theAccount, const QString &toUsername) 1006 { 1007 qCDebug(CHOQOK); 1008 if (!theAccount) { 1009 QAction *act = qobject_cast<QAction *>(sender()); 1010 theAccount = qobject_cast<TwitterApiAccount *>( 1011 Choqok::AccountManager::self()->findAccount(act->data().toString())); 1012 } 1013 TwitterApiDMessageDialog *dmsg = new TwitterApiDMessageDialog(theAccount, Choqok::UI::Global::mainWindow()); 1014 if (!toUsername.isEmpty()) { 1015 dmsg->setTo(toUsername); 1016 } 1017 dmsg->show(); 1018 } 1019 1020 Choqok::TimelineInfo *TwitterApiMicroBlog::timelineInfo(const QString &timelineName) 1021 { 1022 if (isValidTimeline(timelineName)) { 1023 return mTimelineInfos.value(timelineName); 1024 } else { 1025 return nullptr; 1026 } 1027 } 1028 1029 void TwitterApiMicroBlog::showSearchDialog(TwitterApiAccount *theAccount) 1030 { 1031 if (!theAccount) { 1032 QAction *act = qobject_cast<QAction *>(sender()); 1033 theAccount = qobject_cast<TwitterApiAccount *>( 1034 Choqok::AccountManager::self()->findAccount(act->data().toString())); 1035 } 1036 QPointer<TwitterApiSearchDialog> searchDlg = new TwitterApiSearchDialog(theAccount, 1037 Choqok::UI::Global::mainWindow()); 1038 searchDlg->show(); 1039 } 1040 1041 void TwitterApiMicroBlog::slotUpdateFriendsList() 1042 { 1043 QAction *act = qobject_cast<QAction *>(sender()); 1044 TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>( 1045 Choqok::AccountManager::self()->findAccount(act->data().toString())); 1046 listFriendsUsername(theAccount, true); 1047 } 1048 1049 void TwitterApiMicroBlog::createFriendship(Choqok::Account *theAccount, const QString &username) 1050 { 1051 qCDebug(CHOQOK); 1052 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 1053 QUrl url = account->apiUrl(); 1054 url.setPath(url.path() + QLatin1String("/friendships/create.json")); 1055 1056 QUrlQuery urlQuery; 1057 urlQuery.addQueryItem(QLatin1String("screen_name"), username); 1058 url.setQuery(urlQuery); 1059 1060 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 1061 qCDebug(CHOQOK) << url; 1062 if (!job) { 1063 qCCritical(CHOQOK) << "Cannot create an http POST request!"; 1064 return; 1065 } 1066 job->addMetaData(QStringLiteral("customHTTPHeader"), 1067 QStringLiteral("Authorization: ") + 1068 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 1069 mJobsAccount[job] = theAccount; 1070 mFriendshipMap[ job ] = username; 1071 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotCreateFriendship); 1072 job->start(); 1073 } 1074 1075 void TwitterApiMicroBlog::slotCreateFriendship(KJob *job) 1076 { 1077 qCDebug(CHOQOK); 1078 if (!job) { 1079 qCCritical(CHOQOK) << "Job is a null Pointer!"; 1080 return; 1081 } 1082 TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job)); 1083 QString username = mFriendshipMap.take(job); 1084 if (job->error()) { 1085 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 1086 Q_EMIT error(theAccount, CommunicationError, 1087 i18n("Creating friendship with %1 failed. %2", username, job->errorString())); 1088 return; 1089 } 1090 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job); 1091 Choqok::User *user = readUserInfo(stj->data()); 1092 if (user /*&& user->userName.compare(username, Qt::CaseInsensitive)*/) { 1093 Q_EMIT friendshipCreated(theAccount, username); 1094 Choqok::NotifyManager::success(i18n("You are now listening to %1's posts.", username)); 1095 theAccount->setFriendsList(QStringList()); 1096 listFriendsUsername(theAccount); 1097 } else { 1098 QString errorMsg = checkForError(stj->data()); 1099 if (errorMsg.isEmpty()) { 1100 qCDebug(CHOQOK) << "Parse Error:" << stj->data(); 1101 Q_EMIT error(theAccount, ParsingError, 1102 i18n("Creating friendship with %1 failed: the server returned invalid data.", 1103 username)); 1104 } else { 1105 qCDebug(CHOQOK) << "Server error:" << errorMsg; 1106 Q_EMIT error(theAccount, ServerError, 1107 i18n("Creating friendship with %1 failed: %2", 1108 username, errorMsg)); 1109 } 1110 } 1111 } 1112 1113 void TwitterApiMicroBlog::destroyFriendship(Choqok::Account *theAccount, const QString &username) 1114 { 1115 qCDebug(CHOQOK); 1116 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 1117 QUrl url = account->apiUrl(); 1118 url.setPath(url.path() + QLatin1String("/friendships/destroy.json")); 1119 1120 QUrlQuery urlQuery; 1121 urlQuery.addQueryItem(QLatin1String("screen_name"), username); 1122 url.setQuery(urlQuery); 1123 1124 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 1125 if (!job) { 1126 qCCritical(CHOQOK) << "Cannot create an http POST request!"; 1127 return; 1128 } 1129 job->addMetaData(QStringLiteral("customHTTPHeader"), 1130 QStringLiteral("Authorization: ") + 1131 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 1132 mJobsAccount[job] = theAccount; 1133 mFriendshipMap[ job ] = username; 1134 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotDestroyFriendship); 1135 job->start(); 1136 } 1137 1138 void TwitterApiMicroBlog::slotDestroyFriendship(KJob *job) 1139 { 1140 qCDebug(CHOQOK); 1141 if (!job) { 1142 qCCritical(CHOQOK) << "Job is a null Pointer!"; 1143 return; 1144 } 1145 TwitterApiAccount *theAccount = qobject_cast<TwitterApiAccount *>(mJobsAccount.take(job)); 1146 QString username = mFriendshipMap.take(job); 1147 if (job->error()) { 1148 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 1149 Q_EMIT error(theAccount, CommunicationError, 1150 i18n("Destroying friendship with %1 failed. %2", username, job->errorString())); 1151 return; 1152 } 1153 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job); 1154 Choqok::User *user = readUserInfo(stj->data()); 1155 if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { 1156 Q_EMIT friendshipDestroyed(theAccount, username); 1157 Choqok::NotifyManager::success(i18n("You will not receive %1's updates.", username)); 1158 theAccount->setFriendsList(QStringList()); 1159 listFriendsUsername(theAccount); 1160 } else { 1161 QString errorMsg = checkForError(stj->data()); 1162 if (errorMsg.isEmpty()) { 1163 qCDebug(CHOQOK) << "Parse Error:" << stj->data(); 1164 Q_EMIT error(theAccount, ParsingError, 1165 i18n("Destroying friendship with %1 failed: the server returned invalid data.", 1166 username)); 1167 } else { 1168 qCDebug(CHOQOK) << "Server error:" << errorMsg; 1169 Q_EMIT error(theAccount, ServerError, 1170 i18n("Destroying friendship with %1 failed: %2", 1171 username, errorMsg)); 1172 } 1173 } 1174 } 1175 1176 void TwitterApiMicroBlog::blockUser(Choqok::Account *theAccount, const QString &username) 1177 { 1178 qCDebug(CHOQOK); 1179 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 1180 QUrl url = account->apiUrl(); 1181 url.setPath(url.path() + QLatin1String("/blocks/create.json")); 1182 1183 QUrlQuery urlQuery; 1184 urlQuery.addQueryItem(QLatin1String("screen_name"), username); 1185 url.setQuery(urlQuery); 1186 1187 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 1188 if (!job) { 1189 qCCritical(CHOQOK) << "Cannot create an http POST request!"; 1190 return; 1191 } 1192 job->addMetaData(QStringLiteral("customHTTPHeader"), 1193 QStringLiteral("Authorization: ") + 1194 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 1195 mJobsAccount[job] = theAccount; 1196 mFriendshipMap[ job ] = username; 1197 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotBlockUser); 1198 job->start(); 1199 } 1200 1201 void TwitterApiMicroBlog::reportUserAsSpam(Choqok::Account *theAccount, const QString &username) 1202 { 1203 qCDebug(CHOQOK); 1204 TwitterApiAccount *account = qobject_cast<TwitterApiAccount *>(theAccount); 1205 QUrl url = account->apiUrl(); 1206 url = url.adjusted(QUrl::StripTrailingSlash); 1207 url.setPath(url.path() + QLatin1String("/users/report_spam.json")); 1208 1209 QUrlQuery urlQuery; 1210 urlQuery.addQueryItem(QLatin1String("screen_name"), username); 1211 url.setQuery(urlQuery); 1212 1213 KIO::StoredTransferJob *job = KIO::storedHttpPost(QByteArray(), url, KIO::HideProgressInfo) ; 1214 if (!job) { 1215 qCCritical(CHOQOK) << "Cannot create an http POST request!"; 1216 return; 1217 } 1218 job->addMetaData(QStringLiteral("customHTTPHeader"), 1219 QStringLiteral("Authorization: ") + 1220 QLatin1String(authorizationHeader(account, url, QNetworkAccessManager::PostOperation))); 1221 mJobsAccount[job] = theAccount; 1222 mFriendshipMap[ job ] = username; 1223 connect(job, &KIO::StoredTransferJob::result, this, &TwitterApiMicroBlog::slotReportUser); 1224 job->start(); 1225 1226 } 1227 1228 void TwitterApiMicroBlog::slotBlockUser(KJob *job) 1229 { 1230 qCDebug(CHOQOK); 1231 if (!job) { 1232 qCCritical(CHOQOK) << "Job is a null Pointer!"; 1233 return; 1234 } 1235 Choqok::Account *theAccount = mJobsAccount.take(job); 1236 QString username = mFriendshipMap.take(job); 1237 if (job->error()) { 1238 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 1239 Q_EMIT error(theAccount, CommunicationError, 1240 i18n("Blocking %1 failed. %2", username, job->errorString())); 1241 return; 1242 } 1243 Choqok::User *user = readUserInfo(qobject_cast<KIO::StoredTransferJob *>(job)->data()); 1244 if (user /*&& user->userName.compare( username, Qt::CaseInsensitive )*/) { 1245 Q_EMIT userBlocked(theAccount, username); 1246 Choqok::NotifyManager::success(i18n("You will no longer be disturbed by %1.", username)); 1247 } else { 1248 qCDebug(CHOQOK) << "Parse Error:" << qobject_cast<KIO::StoredTransferJob *>(job)->data(); 1249 Q_EMIT error(theAccount, ParsingError, 1250 i18n("Blocking %1 failed: the server returned invalid data.", 1251 username)); 1252 } 1253 //TODO Check for failor! 1254 } 1255 1256 void TwitterApiMicroBlog::slotReportUser(KJob *job) 1257 { 1258 qCDebug(CHOQOK); 1259 if (!job) { 1260 qCCritical(CHOQOK) << "Job is a null Pointer!"; 1261 return; 1262 } 1263 1264 Choqok::Account *theAccount = mJobsAccount.take(job); 1265 QString username = mFriendshipMap.take(job); 1266 if (job->error()) { 1267 qCDebug(CHOQOK) << "Job Error:" << job->errorString(); 1268 Q_EMIT error(theAccount, CommunicationError, 1269 i18n("Reporting %1 failed. %2", username, job->errorString())); 1270 return; 1271 } 1272 Choqok::User *user = readUserInfo(qobject_cast<KIO::StoredTransferJob *>(job)->data()); 1273 if (user) { 1274 Choqok::NotifyManager::success(i18n("Report sent successfully.")); 1275 } else { 1276 qCDebug(CHOQOK) << "Parse Error:" << qobject_cast<KIO::StoredTransferJob *>(job)->data(); 1277 Q_EMIT error(theAccount, ParsingError, 1278 i18n("Reporting %1 failed: the server returned invalid data.", 1279 username)); 1280 } 1281 } 1282 1283 ///=================================================================== 1284 1285 QString TwitterApiMicroBlog::checkForError(const QByteArray &buffer) 1286 { 1287 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1288 if (!json.isNull()) { 1289 const QVariantMap map = json.toVariant().toMap(); 1290 if (map.contains(QLatin1String("errors"))) { 1291 QStringList errors; 1292 for (const QVariant &msg: map[QLatin1String("errors")].toList()) { 1293 errors.append(msg.toMap()[QLatin1String("message")].toString()); 1294 qCCritical(CHOQOK) << "Error:" << errors.last(); 1295 } 1296 1297 return errors.join(QLatin1Char(';')); 1298 } 1299 } 1300 return QString(); 1301 } 1302 1303 QList< Choqok::Post * > TwitterApiMicroBlog::readTimeline(Choqok::Account *theAccount, 1304 const QByteArray &buffer) 1305 { 1306 QList<Choqok::Post *> postList; 1307 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1308 if (!json.isNull()) { 1309 for (const QVariant &list: json.toVariant().toList()) { 1310 Choqok::Post *post = readPost(theAccount, list.toMap(), new Choqok::Post); 1311 1312 if (post) { 1313 postList.prepend(post); 1314 } 1315 } 1316 } else { 1317 const QString err = checkForError(buffer); 1318 if (err.isEmpty()) { 1319 qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; 1320 Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); 1321 } else { 1322 Q_EMIT error(theAccount, ServerError, err); 1323 } 1324 } 1325 return postList; 1326 } 1327 1328 Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, 1329 const QByteArray &buffer, Choqok::Post *post) 1330 { 1331 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1332 if (!json.isNull()) { 1333 return readPost(theAccount, json.toVariant().toMap(), post); 1334 } else { 1335 if (!post) { 1336 qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; 1337 post = new Choqok::Post; 1338 } 1339 Q_EMIT errorPost(theAccount, post, ParsingError, i18n("Could not parse the data that has been received from the server.")); 1340 qCCritical(CHOQOK) << "JSon parsing failed. Buffer was:" << buffer; 1341 post->isError = true; 1342 return post; 1343 } 1344 } 1345 1346 Choqok::Post *TwitterApiMicroBlog::readPost(Choqok::Account *theAccount, 1347 const QVariantMap &var, Choqok::Post *post) 1348 { 1349 if (!post) { 1350 qCCritical(CHOQOK) << "TwitterApiMicroBlog::readPost: post is NULL!"; 1351 return nullptr; 1352 } 1353 post->content = var[QLatin1String("text")].toString(); 1354 post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); 1355 post->isFavorited = var[QLatin1String("favorited")].toBool(); 1356 post->postId = var[QLatin1String("id")].toString(); 1357 post->replyToPostId = var[QLatin1String("in_reply_to_status_id")].toString(); 1358 post->replyToUser.userId = var[QLatin1String("in_reply_to_user_id")].toString(); 1359 post->replyToUser.userName = var[QLatin1String("in_reply_to_screen_name")].toString(); 1360 post->source = var[QLatin1String("source")].toString(); 1361 QVariantMap userMap = var[QLatin1String("user")].toMap(); 1362 post->author.description = userMap[QLatin1String("description")].toString(); 1363 post->author.location = userMap[QLatin1String("location")].toString(); 1364 post->author.realName = userMap[QLatin1String("name")].toString(); 1365 post->author.userId = userMap[QLatin1String("id")].toString(); 1366 post->author.userName = userMap[QLatin1String("screen_name")].toString(); 1367 post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toUrl(); 1368 QVariantMap entities = var[QLatin1String("entities")].toMap(); 1369 QVariantMap mediaMap; 1370 QVariantList media = entities[QLatin1String("media")].toList(); 1371 if (media.size() > 0) { 1372 mediaMap = media.at(0).toMap(); 1373 post->media = QUrl::fromUserInput(mediaMap[QLatin1String("media_url")].toString() + QLatin1String(":small")); 1374 QVariantMap sizes = mediaMap[QLatin1String("sizes")].toMap(); 1375 QVariantMap w = sizes[QLatin1String("small")].toMap(); 1376 } else { 1377 post->media = QUrl(); 1378 } 1379 1380 QVariantMap retweetedMap = var[QLatin1String("retweeted_status")].toMap(); 1381 if (!retweetedMap.isEmpty()) { 1382 Choqok::Post *retweetedPost = readPost(theAccount, retweetedMap, new Choqok::Post); 1383 setRepeatedOfInfo(post, retweetedPost); 1384 delete retweetedPost; 1385 } 1386 QVariantMap quotedMap = var[QLatin1String("quoted_status")].toMap(); 1387 if (!quotedMap.isEmpty()) { 1388 Choqok::Post *quotedPost = readPost(theAccount, quotedMap, new Choqok::Post); 1389 setQuotedPost(post, quotedPost); 1390 delete quotedPost; 1391 } 1392 post->link = postUrl(theAccount, post->author.userName, post->postId); 1393 post->isRead = post->isFavorited || (post->repeatedFromUser.userName.compare(theAccount->username(), Qt::CaseInsensitive) == 0); 1394 1395 if(post->postId.isEmpty() || post->author.userName.isEmpty()) 1396 post->isError = true; 1397 1398 return post; 1399 } 1400 1401 QList< Choqok::Post * > TwitterApiMicroBlog::readDirectMessages(Choqok::Account *theAccount, 1402 const QByteArray &buffer) 1403 { 1404 QList<Choqok::Post *> postList; 1405 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1406 if (!json.isNull()) { 1407 for (const QVariant &list: json.toVariant().toList()) { 1408 postList.prepend(readDirectMessage(theAccount, list.toMap())); 1409 } 1410 } else { 1411 const QString err = checkForError(buffer); 1412 if (err.isEmpty()) { 1413 qCCritical(CHOQOK) << "JSON parsing failed.\nBuffer was: \n" << buffer; 1414 Q_EMIT error(theAccount, ParsingError, i18n("Could not parse the data that has been received from the server.")); 1415 } else { 1416 Q_EMIT error(theAccount, ServerError, err); 1417 } 1418 } 1419 return postList; 1420 } 1421 1422 Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, 1423 const QByteArray &buffer) 1424 { 1425 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1426 if (!json.isNull()) { 1427 return readDirectMessage(theAccount, json.toVariant().toMap()); 1428 } else { 1429 Choqok::Post *post = new Choqok::Post; 1430 post->isError = true; 1431 return post; 1432 } 1433 } 1434 1435 Choqok::Post *TwitterApiMicroBlog::readDirectMessage(Choqok::Account *theAccount, 1436 const QVariantMap &var) 1437 { 1438 Choqok::Post *msg = new Choqok::Post; 1439 1440 msg->isPrivate = true; 1441 QString senderId, recipientId, senderScreenName, recipientScreenName, senderName, 1442 senderDescription, recipientName, recipientDescription; 1443 QUrl senderProfileImageUrl, recipientProfileImageUrl; 1444 1445 msg->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); 1446 msg->content = var[QLatin1String("text")].toString(); 1447 msg->postId = var[QLatin1String("id")].toString();; 1448 senderId = var[QLatin1String("sender_id")].toString(); 1449 recipientId = var[QLatin1String("recipient_id")].toString(); 1450 senderScreenName = var[QLatin1String("sender_screen_name")].toString(); 1451 recipientScreenName = var[QLatin1String("recipient_screen_name")].toString(); 1452 QVariantMap sender = var[QLatin1String("sender")].toMap(); 1453 senderProfileImageUrl = sender[QLatin1String("profile_image_url")].toUrl(); 1454 senderName = sender[QLatin1String("name")].toString(); 1455 senderDescription = sender[QLatin1String("description")].toString(); 1456 QVariantMap recipient = var[QLatin1String("recipient")].toMap(); 1457 recipientProfileImageUrl = recipient[QLatin1String("profile_image_url")].toUrl(); 1458 recipientName = recipient[QLatin1String("name")].toString(); 1459 recipientDescription = recipient[QLatin1String("description")].toString(); 1460 if (senderScreenName.compare(theAccount->username(), Qt::CaseInsensitive) == 0) { 1461 msg->author.description = recipientDescription; 1462 msg->author.userName = recipientScreenName; 1463 msg->author.profileImageUrl = recipientProfileImageUrl; 1464 msg->author.realName = recipientName; 1465 msg->author.userId = recipientId; 1466 msg->replyToUser.userId = recipientId; 1467 msg->replyToUser.userName = recipientScreenName; 1468 msg->isRead = true; 1469 } else { 1470 msg->author.description = senderDescription; 1471 msg->author.userName = senderScreenName; 1472 msg->author.profileImageUrl = senderProfileImageUrl; 1473 msg->author.realName = senderName; 1474 msg->author.userId = senderId; 1475 msg->replyToUser.userId = recipientId; 1476 msg->replyToUser.userName = recipientScreenName; 1477 } 1478 return msg; 1479 } 1480 1481 Choqok::User *TwitterApiMicroBlog::readUserInfo(const QByteArray &buffer) 1482 { 1483 Choqok::User *user = nullptr; 1484 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1485 if (!json.isNull()) { 1486 user = new Choqok::User(readUser(nullptr, json.toVariant().toMap())); 1487 } else { 1488 QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); 1489 qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; 1490 Q_EMIT error(nullptr, ParsingError, err, Critical); 1491 } 1492 return user; 1493 } 1494 1495 QStringList TwitterApiMicroBlog::readFriendsScreenName(Choqok::Account *theAccount, 1496 const QByteArray &buffer) 1497 { 1498 QStringList list; 1499 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1500 if (!json.isNull()) { 1501 const QVariantMap map = json.toVariant().toMap(); 1502 QVariantList jsonList = map[QLatin1String("users")].toList(); 1503 QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); 1504 1505 if (nextCursor.isEmpty()) { 1506 nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already 1507 } 1508 1509 for (const QVariant &user: jsonList) { 1510 list << user.toMap()[QLatin1String("screen_name")].toString(); 1511 } 1512 d->friendsCursor = nextCursor; 1513 } else { 1514 QString err = i18n("Retrieving the friends list failed. The data returned from the server is corrupted."); 1515 qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; 1516 Q_EMIT error(theAccount, ParsingError, err, Critical); 1517 } 1518 1519 return list; 1520 } 1521 1522 QStringList TwitterApiMicroBlog::readFollowersScreenName(Choqok::Account *theAccount, 1523 const QByteArray &buffer) 1524 { 1525 QStringList list; 1526 const QJsonDocument json = QJsonDocument::fromJson(buffer); 1527 if (!json.isNull()) { 1528 const QVariantMap map = json.toVariant().toMap(); 1529 QVariantList jsonList = map[QLatin1String("users")].toList(); 1530 QString nextCursor = map[QLatin1String("next_cursor_str")].toString(); 1531 1532 if (nextCursor.isEmpty()) { 1533 nextCursor = QLatin1String("0"); // we probably ran the rate limit; stop bugging the server already 1534 } 1535 1536 for (const QVariant &user: jsonList) { 1537 list << user.toMap()[QLatin1String("screen_name")].toString(); 1538 } 1539 1540 d->followersCursor = nextCursor; 1541 } else { 1542 QString err = i18n("Retrieving the followers list failed. The data returned from the server is corrupted."); 1543 qCDebug(CHOQOK) << "JSON parse error:the buffer is: \n" << buffer; 1544 Q_EMIT error(theAccount, ParsingError, err, Critical); 1545 } 1546 1547 return list; 1548 } 1549 1550 Choqok::User TwitterApiMicroBlog::readUser(Choqok::Account *theAccount, const QVariantMap &map) 1551 { 1552 Q_UNUSED(theAccount); 1553 Choqok::User u; 1554 u.description = map[QLatin1String("description")].toString(); 1555 u.homePageUrl = map[QLatin1String("url")].toUrl(); 1556 u.isProtected = map[QLatin1String("protected")].toBool(); 1557 u.location = map[QLatin1String("location")].toString(); 1558 u.profileImageUrl = map[QLatin1String("profile_image_url")].toUrl(); 1559 u.realName = map[QLatin1String("name")].toString(); 1560 u.userId = map[QLatin1String("id_str")].toString(); 1561 u.userName = map[QLatin1String("screen_name")].toString(); 1562 return u; 1563 } 1564 1565 #include "moc_twitterapimicroblog.cpp"