api_messages_search.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "api/api_messages_search.h"
  8. #include "apiwrap.h"
  9. #include "data/data_channel.h"
  10. #include "data/data_histories.h"
  11. #include "data/data_message_reaction_id.h"
  12. #include "data/data_peer.h"
  13. #include "data/data_session.h"
  14. #include "history/history.h"
  15. #include "history/history_item.h"
  16. #include "main/main_session.h"
  17. namespace Api {
  18. namespace {
  19. constexpr auto kSearchPerPage = 50;
  20. [[nodiscard]] MessageIdsList HistoryItemsFromTL(
  21. not_null<Data::Session*> data,
  22. const QVector<MTPMessage> &messages) {
  23. auto result = MessageIdsList();
  24. for (const auto &message : messages) {
  25. const auto peerId = PeerFromMessage(message);
  26. if (const auto peer = data->peerLoaded(peerId)) {
  27. if (const auto lastDate = DateFromMessage(message)) {
  28. const auto item = data->addNewMessage(
  29. message,
  30. MessageFlags(),
  31. NewMessageType::Existing);
  32. result.push_back(item->fullId());
  33. }
  34. } else {
  35. LOG(("API Error: a search results with not loaded peer %1"
  36. ).arg(peerId.value));
  37. }
  38. }
  39. return result;
  40. }
  41. [[nodiscard]] QString RequestToToken(
  42. const MessagesSearch::Request &request) {
  43. auto result = request.query;
  44. if (request.from) {
  45. result += '\n' + QString::number(request.from->id.value);
  46. }
  47. for (const auto &tag : request.tags) {
  48. result += '\n';
  49. if (const auto customId = tag.custom()) {
  50. result += u"custom"_q + QString::number(customId);
  51. } else {
  52. result += u"emoji"_q + tag.emoji();
  53. }
  54. }
  55. return result;
  56. }
  57. } // namespace
  58. MessagesSearch::MessagesSearch(not_null<History*> history)
  59. : _history(history) {
  60. }
  61. MessagesSearch::~MessagesSearch() {
  62. _history->owner().histories().cancelRequest(
  63. base::take(_searchInHistoryRequest));
  64. }
  65. void MessagesSearch::searchMessages(Request request) {
  66. _request = std::move(request);
  67. _offsetId = {};
  68. searchRequest();
  69. }
  70. void MessagesSearch::searchMore() {
  71. if (_searchInHistoryRequest || _requestId) {
  72. return;
  73. }
  74. searchRequest();
  75. }
  76. void MessagesSearch::searchRequest() {
  77. const auto nextToken = RequestToToken(_request);
  78. if (!_offsetId) {
  79. const auto it = _cacheOfStartByToken.find(nextToken);
  80. if (it != end(_cacheOfStartByToken)) {
  81. _requestId = 0;
  82. searchReceived(it->second, _requestId, nextToken);
  83. return;
  84. }
  85. }
  86. auto callback = [=](Fn<void()> finish) {
  87. using Flag = MTPmessages_Search::Flag;
  88. const auto from = _request.from;
  89. const auto fromPeer = _history->peer->isUser() ? nullptr : from;
  90. const auto savedPeer = _history->peer->isSelf() ? from : nullptr;
  91. _requestId = _history->session().api().request(MTPmessages_Search(
  92. MTP_flags((fromPeer ? Flag::f_from_id : Flag())
  93. | (savedPeer ? Flag::f_saved_peer_id : Flag())
  94. | (_request.topMsgId ? Flag::f_top_msg_id : Flag())
  95. | (_request.tags.empty() ? Flag() : Flag::f_saved_reaction)),
  96. _history->peer->input,
  97. MTP_string(_request.query),
  98. (fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
  99. (savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
  100. MTP_vector_from_range(_request.tags | ranges::views::transform(
  101. Data::ReactionToMTP
  102. )),
  103. MTP_int(_request.topMsgId), // top_msg_id
  104. MTP_inputMessagesFilterEmpty(),
  105. MTP_int(0), // min_date
  106. MTP_int(0), // max_date
  107. MTP_int(_offsetId), // offset_id
  108. MTP_int(0), // add_offset
  109. MTP_int(kSearchPerPage),
  110. MTP_int(0), // max_id
  111. MTP_int(0), // min_id
  112. MTP_long(0) // hash
  113. )).done([=](const TLMessages &result, mtpRequestId id) {
  114. _searchInHistoryRequest = 0;
  115. searchReceived(result, id, nextToken);
  116. finish();
  117. }).fail([=](const MTP::Error &error, mtpRequestId id) {
  118. _searchInHistoryRequest = 0;
  119. if (_requestId == id) {
  120. _requestId = 0;
  121. }
  122. if (error.type() == u"SEARCH_QUERY_EMPTY"_q) {
  123. _messagesFounds.fire({ 0, MessageIdsList(), nextToken });
  124. }
  125. finish();
  126. }).send();
  127. return _requestId;
  128. };
  129. _searchInHistoryRequest = _history->owner().histories().sendRequest(
  130. _history,
  131. Data::Histories::RequestType::History,
  132. std::move(callback));
  133. }
  134. void MessagesSearch::searchReceived(
  135. const TLMessages &result,
  136. mtpRequestId requestId,
  137. const QString &nextToken) {
  138. if (requestId != _requestId) {
  139. return;
  140. }
  141. auto &owner = _history->owner();
  142. auto found = result.match([&](const MTPDmessages_messages &data) {
  143. if (_requestId != 0) {
  144. // Don't apply cached data!
  145. owner.processUsers(data.vusers());
  146. owner.processChats(data.vchats());
  147. }
  148. auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
  149. const auto total = int(data.vmessages().v.size());
  150. return FoundMessages{ total, std::move(items), nextToken };
  151. }, [&](const MTPDmessages_messagesSlice &data) {
  152. if (_requestId != 0) {
  153. // Don't apply cached data!
  154. owner.processUsers(data.vusers());
  155. owner.processChats(data.vchats());
  156. }
  157. auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
  158. // data.vnext_rate() is used only in global search.
  159. const auto total = int(data.vcount().v);
  160. return FoundMessages{ total, std::move(items), nextToken };
  161. }, [&](const MTPDmessages_channelMessages &data) {
  162. if (_requestId != 0) {
  163. // Don't apply cached data!
  164. owner.processUsers(data.vusers());
  165. owner.processChats(data.vchats());
  166. }
  167. if (const auto channel = _history->peer->asChannel()) {
  168. channel->ptsReceived(data.vpts().v);
  169. if (_requestId != 0) {
  170. // Don't apply cached data!
  171. channel->processTopics(data.vtopics());
  172. }
  173. } else {
  174. LOG(("API Error: "
  175. "received messages.channelMessages when no channel "
  176. "was passed!"));
  177. }
  178. auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
  179. const auto total = int(data.vcount().v);
  180. return FoundMessages{ total, std::move(items), nextToken };
  181. }, [](const MTPDmessages_messagesNotModified &data) {
  182. return FoundMessages{};
  183. });
  184. if (!_offsetId) {
  185. _cacheOfStartByToken.emplace(nextToken, result);
  186. }
  187. _requestId = 0;
  188. _offsetId = found.messages.empty()
  189. ? MsgId()
  190. : found.messages.back().msg;
  191. _messagesFounds.fire(std::move(found));
  192. }
  193. rpl::producer<FoundMessages> MessagesSearch::messagesFounds() const {
  194. return _messagesFounds.events();
  195. }
  196. } // namespace Api