api_views.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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_views.h"
  8. #include "apiwrap.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_peer_id.h"
  11. #include "data/data_session.h"
  12. #include "history/history.h"
  13. #include "history/history_item.h"
  14. #include "main/main_session.h"
  15. namespace Api {
  16. namespace {
  17. // Send channel views each second.
  18. constexpr auto kSendViewsTimeout = crl::time(1000);
  19. constexpr auto kPollExtendedMediaPeriod = 30 * crl::time(1000);
  20. constexpr auto kMaxPollPerRequest = 100;
  21. } // namespace
  22. ViewsManager::ViewsManager(not_null<ApiWrap*> api)
  23. : _session(&api->session())
  24. , _api(&api->instance())
  25. , _incrementTimer([=] { viewsIncrement(); })
  26. , _pollTimer([=] { sendPollRequests(); }) {
  27. }
  28. void ViewsManager::scheduleIncrement(not_null<HistoryItem*> item) {
  29. auto peer = item->history()->peer;
  30. auto i = _incremented.find(peer);
  31. if (i != _incremented.cend()) {
  32. if (i->second.contains(item->id)) {
  33. return;
  34. }
  35. } else {
  36. i = _incremented.emplace(peer).first;
  37. }
  38. i->second.emplace(item->id);
  39. auto j = _toIncrement.find(peer);
  40. if (j == _toIncrement.cend()) {
  41. j = _toIncrement.emplace(peer).first;
  42. _incrementTimer.callOnce(kSendViewsTimeout);
  43. }
  44. j->second.emplace(item->id);
  45. }
  46. void ViewsManager::removeIncremented(not_null<PeerData*> peer) {
  47. _incremented.remove(peer);
  48. }
  49. void ViewsManager::pollExtendedMedia(
  50. not_null<HistoryItem*> item,
  51. bool force) {
  52. if (!item->isRegular()) {
  53. return;
  54. }
  55. const auto id = item->id;
  56. const auto peer = item->history()->peer;
  57. auto &request = _pollRequests[peer];
  58. if (request.ids.contains(id) || request.sent.contains(id)) {
  59. if (!force || request.forced) {
  60. return;
  61. }
  62. }
  63. request.ids.emplace(id);
  64. if (force) {
  65. request.forced = true;
  66. }
  67. const auto delay = force ? 1 : kPollExtendedMediaPeriod;
  68. if (!request.id && (!request.when || force)) {
  69. request.when = crl::now() + delay;
  70. }
  71. if (!_pollTimer.isActive() || force) {
  72. _pollTimer.callOnce(delay);
  73. }
  74. }
  75. void ViewsManager::viewsIncrement() {
  76. for (auto i = _toIncrement.begin(); i != _toIncrement.cend();) {
  77. if (_incrementRequests.contains(i->first)) {
  78. ++i;
  79. continue;
  80. }
  81. QVector<MTPint> ids;
  82. ids.reserve(i->second.size());
  83. for (const auto &msgId : i->second) {
  84. ids.push_back(MTP_int(msgId));
  85. }
  86. const auto requestId = _api.request(MTPmessages_GetMessagesViews(
  87. i->first->input,
  88. MTP_vector<MTPint>(ids),
  89. MTP_bool(true)
  90. )).done([=](
  91. const MTPmessages_MessageViews &result,
  92. mtpRequestId requestId) {
  93. done(ids, result, requestId);
  94. }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
  95. fail(error, requestId);
  96. }).afterDelay(5).send();
  97. _incrementRequests.emplace(i->first, requestId);
  98. i = _toIncrement.erase(i);
  99. }
  100. }
  101. void ViewsManager::sendPollRequests() {
  102. const auto now = crl::now();
  103. auto toRequest = base::flat_map<not_null<PeerData*>, QVector<MTPint>>();
  104. auto nearest = crl::time();
  105. for (auto &[peer, request] : _pollRequests) {
  106. if (request.id) {
  107. continue;
  108. } else if (request.when <= now) {
  109. Assert(request.sent.empty());
  110. auto &list = toRequest[peer];
  111. const auto count = int(request.ids.size());
  112. if (count < kMaxPollPerRequest) {
  113. request.sent = base::take(request.ids);
  114. } else {
  115. const auto from = begin(request.ids);
  116. const auto end = from + kMaxPollPerRequest;
  117. request.sent = { from, end };
  118. request.ids.erase(from, end);
  119. }
  120. list.reserve(request.sent.size());
  121. for (const auto &id : request.sent) {
  122. list.push_back(MTP_int(id.bare));
  123. }
  124. if (!request.ids.empty()) {
  125. nearest = now;
  126. }
  127. } else if (!nearest || nearest > request.when) {
  128. nearest = request.when;
  129. }
  130. }
  131. sendPollRequests(toRequest);
  132. if (nearest) {
  133. _pollTimer.callOnce(std::max(nearest - now, crl::time(1)));
  134. }
  135. }
  136. void ViewsManager::sendPollRequests(
  137. const base::flat_map<
  138. not_null<PeerData*>,
  139. QVector<MTPint>> &batched) {
  140. for (auto &[peer, list] : batched) {
  141. const auto finish = [=, list = list](mtpRequestId id) {
  142. const auto now = crl::now();
  143. const auto owner = &_session->data();
  144. for (auto i = begin(_pollRequests); i != end(_pollRequests);) {
  145. if (i->second.id == id) {
  146. const auto peer = i->first->id;
  147. for (const auto &itemId : i->second.sent) {
  148. if (const auto item = owner->message(peer, itemId)) {
  149. owner->requestItemRepaint(item);
  150. }
  151. }
  152. i->second.sent.clear();
  153. i->second.id = 0;
  154. if (i->second.ids.empty()) {
  155. i = _pollRequests.erase(i);
  156. } else {
  157. const auto delay = i->second.forced
  158. ? 1
  159. : kPollExtendedMediaPeriod;
  160. i->second.when = now + delay;
  161. if (!_pollTimer.isActive() || i->second.forced) {
  162. _pollTimer.callOnce(delay);
  163. }
  164. ++i;
  165. }
  166. } else {
  167. ++i;
  168. }
  169. }
  170. };
  171. const auto requestId = _api.request(MTPmessages_GetExtendedMedia(
  172. peer->input,
  173. MTP_vector<MTPint>(list)
  174. )).done([=](const MTPUpdates &result, mtpRequestId id) {
  175. _session->api().applyUpdates(result);
  176. finish(id);
  177. }).fail([=](const MTP::Error &error, mtpRequestId id) {
  178. finish(id);
  179. }).send();
  180. _pollRequests[peer].id = requestId;
  181. }
  182. }
  183. void ViewsManager::done(
  184. QVector<MTPint> ids,
  185. const MTPmessages_MessageViews &result,
  186. mtpRequestId requestId) {
  187. const auto &data = result.c_messages_messageViews();
  188. auto &owner = _session->data();
  189. owner.processUsers(data.vusers());
  190. owner.processChats(data.vchats());
  191. auto &v = data.vviews().v;
  192. if (ids.size() == v.size()) {
  193. for (const auto &[peer, id] : _incrementRequests) {
  194. if (id != requestId) {
  195. continue;
  196. }
  197. for (auto j = 0, l = int(ids.size()); j < l; ++j) {
  198. if (const auto item = owner.message(peer->id, ids[j].v)) {
  199. v[j].match([&](const MTPDmessageViews &data) {
  200. if (const auto views = data.vviews()) {
  201. if (item->changeViewsCount(views->v)) {
  202. _session->data().notifyItemDataChange(item);
  203. }
  204. }
  205. if (const auto forwards = data.vforwards()) {
  206. item->setForwardsCount(forwards->v);
  207. }
  208. if (const auto replies = data.vreplies()) {
  209. item->setReplies(
  210. HistoryMessageRepliesData(replies));
  211. }
  212. });
  213. }
  214. }
  215. _incrementRequests.erase(peer);
  216. break;
  217. }
  218. }
  219. if (!_toIncrement.empty() && !_incrementTimer.isActive()) {
  220. _incrementTimer.callOnce(kSendViewsTimeout);
  221. }
  222. }
  223. void ViewsManager::fail(const MTP::Error &error, mtpRequestId requestId) {
  224. for (const auto &[peer, id] : _incrementRequests) {
  225. if (id == requestId) {
  226. _incrementRequests.erase(peer);
  227. break;
  228. }
  229. }
  230. if (!_toIncrement.empty() && !_incrementTimer.isActive()) {
  231. _incrementTimer.callOnce(kSendViewsTimeout);
  232. }
  233. }
  234. } // namespace Api