File indexing completed on 2024-05-05 04:57:28
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 "twittersearch.h" 0010 0011 #include <QJsonDocument> 0012 0013 #include <KIO/StoredTransferJob> 0014 #include <KLocalizedString> 0015 0016 #include "twitterapimicroblog.h" 0017 0018 #include "twitteraccount.h" 0019 #include "twitterdebug.h" 0020 0021 const QRegExp TwitterSearch::m_rId(QLatin1String("tag:search.twitter.com,[0-9]+:([0-9]+)")); 0022 0023 TwitterSearch::TwitterSearch(QObject *parent): TwitterApiSearch(parent) 0024 { 0025 qCDebug(CHOQOK); 0026 mSearchCode[CustomSearch].clear(); 0027 mSearchCode[ToUser] = QLatin1String("to:"); 0028 mSearchCode[FromUser] = QLatin1String("from:"); 0029 mSearchCode[ReferenceUser] = QLatin1Char('@'); 0030 mSearchCode[ReferenceHashtag] = QLatin1Char('#'); 0031 0032 mI18nSearchCode[CustomSearch].clear(); 0033 mI18nSearchCode[ReferenceUser] = QLatin1Char('@'); 0034 mI18nSearchCode[ReferenceHashtag] = QLatin1Char('#'); 0035 mI18nSearchCode[ToUser] = i18nc("Posts sent to user", "To:"); 0036 mI18nSearchCode[FromUser] = i18nc("Posts from user, Sent by user", "From:"); 0037 0038 mSearchTypes[CustomSearch].first = i18n("Custom Search"); 0039 mSearchTypes[CustomSearch].second = true; 0040 0041 mSearchTypes[ToUser].first = i18nc("Tweets are Twitter posts", "Tweets To This User"); 0042 mSearchTypes[ToUser].second = true; 0043 0044 mSearchTypes[FromUser].first = i18nc("Tweets are Twitter posts", "Tweets From This User"); 0045 mSearchTypes[FromUser].second = true; 0046 0047 mSearchTypes[ReferenceUser].first = i18nc("Tweets are Twitter posts", "Tweets Including This Username"); 0048 mSearchTypes[ReferenceUser].second = true; 0049 0050 mSearchTypes[ReferenceHashtag].first = i18nc("Tweets are Twitter posts", "Tweets Including This Hashtag"); 0051 mSearchTypes[ReferenceHashtag].second = true; 0052 } 0053 0054 void TwitterSearch::requestSearchResults(const SearchInfo &searchInfo, 0055 const QString &sinceStatusId, uint count, uint page) 0056 { 0057 Q_UNUSED(page) 0058 qCDebug(CHOQOK); 0059 0060 TwitterAccount *account = qobject_cast< TwitterAccount * >(searchInfo.account); 0061 QUrl url = account->apiUrl(); 0062 0063 QUrlQuery urlQuery; 0064 0065 const QString query = searchInfo.query; 0066 if (searchInfo.option == TwitterSearch::FromUser) { 0067 url.setPath(url.path() + QLatin1String("/statuses/user_timeline.json")); 0068 0069 urlQuery.addQueryItem(QLatin1String("screen_name"), query); 0070 } else { 0071 url.setPath(url.path() + QLatin1String("/search/tweets.json")); 0072 0073 const QByteArray formattedQuery(QUrl::toPercentEncoding(mSearchCode[searchInfo.option] + query)); 0074 urlQuery.addQueryItem(QLatin1String("q"), QString::fromLatin1(formattedQuery)); 0075 } 0076 0077 if (!sinceStatusId.isEmpty()) { 0078 urlQuery.addQueryItem(QLatin1String("since_id"), sinceStatusId); 0079 } 0080 0081 int cntStr; 0082 if (count && count <= 100) { // Twitter API specifies a max count of 100 0083 cntStr = count; 0084 } else { 0085 cntStr = 100; 0086 } 0087 urlQuery.addQueryItem(QLatin1String("tweet_mode"), QLatin1String("extended")); 0088 urlQuery.addQueryItem(QLatin1String("count"), QString::number(cntStr)); 0089 0090 url.setQuery(urlQuery); 0091 0092 qCDebug(CHOQOK) << url; 0093 KIO::StoredTransferJob *job = KIO::storedGet(url, KIO::Reload, KIO::HideProgressInfo); 0094 if (!job) { 0095 qCCritical(CHOQOK) << "Cannot create an http GET request!"; 0096 return; 0097 } 0098 0099 TwitterApiMicroBlog *microblog = qobject_cast<TwitterApiMicroBlog *>(account->microblog()); 0100 0101 job->addMetaData(QStringLiteral("customHTTPHeader"), 0102 QStringLiteral("Authorization: ") + 0103 QLatin1String(microblog->authorizationHeader(account, url, QNetworkAccessManager::GetOperation))); 0104 0105 mSearchJobs[job] = searchInfo; 0106 connect(job, &KIO::StoredTransferJob::result, this, &TwitterSearch::searchResultsReturned); 0107 job->start(); 0108 } 0109 0110 void TwitterSearch::searchResultsReturned(KJob *job) 0111 { 0112 qCDebug(CHOQOK); 0113 if (!job) { 0114 qCDebug(CHOQOK) << "job is a null pointer"; 0115 Q_EMIT error(i18n("Unable to fetch search results.")); 0116 return; 0117 } 0118 0119 const SearchInfo info = mSearchJobs.take(job); 0120 QList<Choqok::Post *> postsList; 0121 if (job->error()) { 0122 qCCritical(CHOQOK) << "Error:" << job->errorString(); 0123 Q_EMIT error(i18n("Unable to fetch search results: %1", job->errorString())); 0124 } else { 0125 KIO::StoredTransferJob *jj = qobject_cast<KIO::StoredTransferJob *>(job); 0126 const QJsonDocument json = QJsonDocument::fromJson(jj->data()); 0127 0128 if (!json.isNull()) { 0129 if (info.option == TwitterSearch::FromUser) { 0130 for (const QVariant &elem: json.toVariant().toList()) { 0131 postsList.prepend(readStatusesFromJsonMap(elem.toMap())); 0132 } 0133 } else { 0134 const QVariantMap map = json.toVariant().toMap(); 0135 0136 if (map.contains(QLatin1String("statuses"))) { 0137 for (const QVariant &elem: map[QLatin1String("statuses")].toList()) { 0138 postsList.prepend(readStatusesFromJsonMap(elem.toMap())); 0139 } 0140 } 0141 } 0142 } 0143 } 0144 0145 Q_EMIT searchResultsReceived(info, postsList); 0146 } 0147 0148 Choqok::Post *TwitterSearch::readStatusesFromJsonMap(const QVariantMap &var) 0149 { 0150 Choqok::Post *post = new Choqok::Post; 0151 0152 post->content = var[QLatin1String("text")].toString(); 0153 0154 // Support for extended tweet_mode 0155 if (var.contains(QLatin1String("full_text"))) { 0156 post->content = var[QLatin1String("full_text")].toString(); 0157 } 0158 0159 //%*s %s %d %d:%d:%d %d %d 0160 post->creationDateTime = dateFromString(var[QLatin1String("created_at")].toString()); 0161 post->postId = var[QLatin1String("id")].toString(); 0162 post->source = var[QLatin1String("source")].toString(); 0163 QVariantMap userMap = var[QLatin1String("user")].toMap(); 0164 post->author.realName = userMap[QLatin1String("name")].toString(); 0165 post->author.userName = userMap[QLatin1String("screen_name")].toString(); 0166 post->author.profileImageUrl = userMap[QLatin1String("profile_image_url")].toUrl(); 0167 post->isPrivate = false; 0168 post->isFavorited = false; 0169 post->replyToPostId = var[QLatin1String("in_reply_to_status_id_str")].toString(); 0170 post->replyToUser.userName = var[QLatin1String("in_reply_to_screen_name")].toString(); 0171 0172 post->link = QUrl::fromUserInput(QStringLiteral("https://twitter.com/%1/status/%2").arg(post->author.userName).arg(post->postId)); 0173 0174 return post; 0175 } 0176 0177 QString TwitterSearch::optionCode(int option) 0178 { 0179 return mI18nSearchCode[option]; 0180 } 0181 0182 TwitterSearch::~TwitterSearch() 0183 { 0184 } 0185 0186 #include "moc_twittersearch.cpp"