api_polls.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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_polls.h"
  8. #include "api/api_common.h"
  9. #include "api/api_updates.h"
  10. #include "apiwrap.h"
  11. #include "base/random.h"
  12. #include "data/business/data_shortcut_messages.h"
  13. #include "data/data_changes.h"
  14. #include "data/data_histories.h"
  15. #include "data/data_poll.h"
  16. #include "data/data_session.h"
  17. #include "history/history.h"
  18. #include "history/history_item.h"
  19. #include "history/history_item_helpers.h" // ShouldSendSilent
  20. #include "main/main_session.h"
  21. namespace Api {
  22. namespace {
  23. [[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
  24. return TimeId(msgId >> 32);
  25. }
  26. } // namespace
  27. Polls::Polls(not_null<ApiWrap*> api)
  28. : _session(&api->session())
  29. , _api(&api->instance()) {
  30. }
  31. void Polls::create(
  32. const PollData &data,
  33. SendAction action,
  34. Fn<void()> done,
  35. Fn<void()> fail) {
  36. _session->api().sendAction(action);
  37. const auto history = action.history;
  38. const auto peer = history->peer;
  39. const auto topicRootId = action.replyTo.messageId
  40. ? action.replyTo.topicRootId
  41. : 0;
  42. auto sendFlags = MTPmessages_SendMedia::Flags(0);
  43. if (action.replyTo) {
  44. sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
  45. }
  46. const auto clearCloudDraft = action.clearDraft;
  47. if (clearCloudDraft) {
  48. sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
  49. history->clearLocalDraft(topicRootId);
  50. history->clearCloudDraft(topicRootId);
  51. history->startSavingCloudDraft(topicRootId);
  52. }
  53. const auto silentPost = ShouldSendSilent(peer, action.options);
  54. const auto starsPaid = std::min(
  55. peer->starsPerMessageChecked(),
  56. action.options.starsApproved);
  57. if (silentPost) {
  58. sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
  59. }
  60. if (action.options.scheduled) {
  61. sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
  62. }
  63. if (action.options.shortcutId) {
  64. sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
  65. }
  66. if (action.options.effectId) {
  67. sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
  68. }
  69. if (starsPaid) {
  70. action.options.starsApproved -= starsPaid;
  71. sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
  72. }
  73. const auto sendAs = action.options.sendAs;
  74. if (sendAs) {
  75. sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
  76. }
  77. auto &histories = history->owner().histories();
  78. const auto randomId = base::RandomValue<uint64>();
  79. histories.sendPreparedMessage(
  80. history,
  81. action.replyTo,
  82. randomId,
  83. Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
  84. MTP_flags(sendFlags),
  85. peer->input,
  86. Data::Histories::ReplyToPlaceholder(),
  87. PollDataToInputMedia(&data),
  88. MTP_string(),
  89. MTP_long(randomId),
  90. MTPReplyMarkup(),
  91. MTPVector<MTPMessageEntity>(),
  92. MTP_int(action.options.scheduled),
  93. (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
  94. Data::ShortcutIdToMTP(_session, action.options.shortcutId),
  95. MTP_long(action.options.effectId),
  96. MTP_long(starsPaid)
  97. ), [=](const MTPUpdates &result, const MTP::Response &response) {
  98. if (clearCloudDraft) {
  99. history->finishSavingCloudDraft(
  100. topicRootId,
  101. UnixtimeFromMsgId(response.outerMsgId));
  102. }
  103. _session->changes().historyUpdated(
  104. history,
  105. (action.options.scheduled
  106. ? Data::HistoryUpdate::Flag::ScheduledSent
  107. : Data::HistoryUpdate::Flag::MessageSent));
  108. done();
  109. }, [=](const MTP::Error &error, const MTP::Response &response) {
  110. if (clearCloudDraft) {
  111. history->finishSavingCloudDraft(
  112. topicRootId,
  113. UnixtimeFromMsgId(response.outerMsgId));
  114. }
  115. fail();
  116. });
  117. }
  118. void Polls::sendVotes(
  119. FullMsgId itemId,
  120. const std::vector<QByteArray> &options) {
  121. if (_pollVotesRequestIds.contains(itemId)) {
  122. return;
  123. }
  124. const auto item = _session->data().message(itemId);
  125. const auto media = item ? item->media() : nullptr;
  126. const auto poll = media ? media->poll() : nullptr;
  127. if (!item) {
  128. return;
  129. }
  130. const auto showSending = poll && !options.empty();
  131. const auto hideSending = [=] {
  132. if (showSending) {
  133. if (const auto item = _session->data().message(itemId)) {
  134. poll->sendingVotes.clear();
  135. _session->data().requestItemRepaint(item);
  136. }
  137. }
  138. };
  139. if (showSending) {
  140. poll->sendingVotes = options;
  141. _session->data().requestItemRepaint(item);
  142. }
  143. auto prepared = QVector<MTPbytes>();
  144. prepared.reserve(options.size());
  145. ranges::transform(
  146. options,
  147. ranges::back_inserter(prepared),
  148. [](const QByteArray &option) { return MTP_bytes(option); });
  149. const auto requestId = _api.request(MTPmessages_SendVote(
  150. item->history()->peer->input,
  151. MTP_int(item->id),
  152. MTP_vector<MTPbytes>(prepared)
  153. )).done([=](const MTPUpdates &result) {
  154. _pollVotesRequestIds.erase(itemId);
  155. hideSending();
  156. _session->updates().applyUpdates(result);
  157. }).fail([=] {
  158. _pollVotesRequestIds.erase(itemId);
  159. hideSending();
  160. }).send();
  161. _pollVotesRequestIds.emplace(itemId, requestId);
  162. }
  163. void Polls::close(not_null<HistoryItem*> item) {
  164. const auto itemId = item->fullId();
  165. if (_pollCloseRequestIds.contains(itemId)) {
  166. return;
  167. }
  168. const auto media = item ? item->media() : nullptr;
  169. const auto poll = media ? media->poll() : nullptr;
  170. if (!poll) {
  171. return;
  172. }
  173. const auto requestId = _api.request(MTPmessages_EditMessage(
  174. MTP_flags(MTPmessages_EditMessage::Flag::f_media),
  175. item->history()->peer->input,
  176. MTP_int(item->id),
  177. MTPstring(),
  178. PollDataToInputMedia(poll, true),
  179. MTPReplyMarkup(),
  180. MTPVector<MTPMessageEntity>(),
  181. MTP_int(0), // schedule_date
  182. MTPint() // quick_reply_shortcut_id
  183. )).done([=](const MTPUpdates &result) {
  184. _pollCloseRequestIds.erase(itemId);
  185. _session->updates().applyUpdates(result);
  186. }).fail([=] {
  187. _pollCloseRequestIds.erase(itemId);
  188. }).send();
  189. _pollCloseRequestIds.emplace(itemId, requestId);
  190. }
  191. void Polls::reloadResults(not_null<HistoryItem*> item) {
  192. const auto itemId = item->fullId();
  193. if (!item->isRegular() || _pollReloadRequestIds.contains(itemId)) {
  194. return;
  195. }
  196. const auto requestId = _api.request(MTPmessages_GetPollResults(
  197. item->history()->peer->input,
  198. MTP_int(item->id)
  199. )).done([=](const MTPUpdates &result) {
  200. _pollReloadRequestIds.erase(itemId);
  201. _session->updates().applyUpdates(result);
  202. }).fail([=] {
  203. _pollReloadRequestIds.erase(itemId);
  204. }).send();
  205. _pollReloadRequestIds.emplace(itemId, requestId);
  206. }
  207. } // namespace Api