1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/PollManager.h"
8 
9 #include "td/telegram/AccessRights.h"
10 #include "td/telegram/AuthManager.h"
11 #include "td/telegram/ContactsManager.h"
12 #include "td/telegram/Dependencies.h"
13 #include "td/telegram/DialogId.h"
14 #include "td/telegram/Global.h"
15 #include "td/telegram/logevent/LogEvent.h"
16 #include "td/telegram/logevent/LogEventHelper.h"
17 #include "td/telegram/MessagesManager.h"
18 #include "td/telegram/misc.h"
19 #include "td/telegram/net/NetActor.h"
20 #include "td/telegram/PollId.hpp"
21 #include "td/telegram/PollManager.hpp"
22 #include "td/telegram/SequenceDispatcher.h"
23 #include "td/telegram/StateManager.h"
24 #include "td/telegram/Td.h"
25 #include "td/telegram/TdDb.h"
26 #include "td/telegram/TdParameters.h"
27 #include "td/telegram/telegram_api.hpp"
28 #include "td/telegram/UpdatesManager.h"
29 
30 #include "td/db/binlog/BinlogEvent.h"
31 #include "td/db/binlog/BinlogHelper.h"
32 #include "td/db/SqliteKeyValue.h"
33 #include "td/db/SqliteKeyValueAsync.h"
34 
35 #include "td/utils/algorithm.h"
36 #include "td/utils/buffer.h"
37 #include "td/utils/format.h"
38 #include "td/utils/logging.h"
39 #include "td/utils/misc.h"
40 #include "td/utils/Random.h"
41 #include "td/utils/Slice.h"
42 #include "td/utils/SliceBuilder.h"
43 #include "td/utils/Status.h"
44 #include "td/utils/tl_helpers.h"
45 
46 #include <algorithm>
47 #include <limits>
48 #include <unordered_map>
49 
50 namespace td {
51 
52 class GetPollResultsQuery final : public Td::ResultHandler {
53   Promise<tl_object_ptr<telegram_api::Updates>> promise_;
54   PollId poll_id_;
55   DialogId dialog_id_;
56 
57  public:
GetPollResultsQuery(Promise<tl_object_ptr<telegram_api::Updates>> && promise)58   explicit GetPollResultsQuery(Promise<tl_object_ptr<telegram_api::Updates>> &&promise) : promise_(std::move(promise)) {
59   }
60 
send(PollId poll_id,FullMessageId full_message_id)61   void send(PollId poll_id, FullMessageId full_message_id) {
62     poll_id_ = poll_id;
63     dialog_id_ = full_message_id.get_dialog_id();
64     auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
65     if (input_peer == nullptr) {
66       LOG(INFO) << "Can't reget poll, because have no read access to " << dialog_id_;
67       return promise_.set_value(nullptr);
68     }
69 
70     auto message_id = full_message_id.get_message_id().get_server_message_id().get();
71     send_query(
72         G()->net_query_creator().create(telegram_api::messages_getPollResults(std::move(input_peer), message_id)));
73   }
74 
on_result(BufferSlice packet)75   void on_result(BufferSlice packet) final {
76     auto result_ptr = fetch_result<telegram_api::messages_getPollResults>(packet);
77     if (result_ptr.is_error()) {
78       return on_error(result_ptr.move_as_error());
79     }
80 
81     promise_.set_value(result_ptr.move_as_ok());
82   }
83 
on_error(Status status)84   void on_error(Status status) final {
85     if (!td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetPollResultsQuery") &&
86         status.message() != "MESSAGE_ID_INVALID") {
87       LOG(ERROR) << "Receive " << status << ", while trying to get results of " << poll_id_;
88     }
89     promise_.set_error(std::move(status));
90   }
91 };
92 
93 class GetPollVotersQuery final : public Td::ResultHandler {
94   Promise<tl_object_ptr<telegram_api::messages_votesList>> promise_;
95   PollId poll_id_;
96   DialogId dialog_id_;
97 
98  public:
GetPollVotersQuery(Promise<tl_object_ptr<telegram_api::messages_votesList>> && promise)99   explicit GetPollVotersQuery(Promise<tl_object_ptr<telegram_api::messages_votesList>> &&promise)
100       : promise_(std::move(promise)) {
101   }
102 
send(PollId poll_id,FullMessageId full_message_id,BufferSlice && option,const string & offset,int32 limit)103   void send(PollId poll_id, FullMessageId full_message_id, BufferSlice &&option, const string &offset, int32 limit) {
104     poll_id_ = poll_id;
105     dialog_id_ = full_message_id.get_dialog_id();
106     auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
107     if (input_peer == nullptr) {
108       LOG(INFO) << "Can't get poll, because have no read access to " << dialog_id_;
109       return promise_.set_error(Status::Error(400, "Chat is not accessible"));
110     }
111 
112     CHECK(!option.empty());
113     int32 flags = telegram_api::messages_getPollVotes::OPTION_MASK;
114     if (!offset.empty()) {
115       flags |= telegram_api::messages_getPollVotes::OFFSET_MASK;
116     }
117 
118     auto message_id = full_message_id.get_message_id().get_server_message_id().get();
119     send_query(G()->net_query_creator().create(telegram_api::messages_getPollVotes(
120         flags, std::move(input_peer), message_id, std::move(option), offset, limit)));
121   }
122 
on_result(BufferSlice packet)123   void on_result(BufferSlice packet) final {
124     auto result_ptr = fetch_result<telegram_api::messages_getPollVotes>(packet);
125     if (result_ptr.is_error()) {
126       return on_error(result_ptr.move_as_error());
127     }
128 
129     promise_.set_value(result_ptr.move_as_ok());
130   }
131 
on_error(Status status)132   void on_error(Status status) final {
133     if (!td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetPollVotersQuery") &&
134         status.message() != "MESSAGE_ID_INVALID") {
135       LOG(ERROR) << "Receive " << status << ", while trying to get voters of " << poll_id_;
136     }
137     promise_.set_error(std::move(status));
138   }
139 };
140 
141 class SetPollAnswerActor final : public NetActorOnce {
142   Promise<tl_object_ptr<telegram_api::Updates>> promise_;
143   DialogId dialog_id_;
144 
145  public:
SetPollAnswerActor(Promise<tl_object_ptr<telegram_api::Updates>> && promise)146   explicit SetPollAnswerActor(Promise<tl_object_ptr<telegram_api::Updates>> &&promise) : promise_(std::move(promise)) {
147   }
148 
send(FullMessageId full_message_id,vector<BufferSlice> && options,uint64 generation,NetQueryRef * query_ref)149   void send(FullMessageId full_message_id, vector<BufferSlice> &&options, uint64 generation, NetQueryRef *query_ref) {
150     dialog_id_ = full_message_id.get_dialog_id();
151     auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
152     if (input_peer == nullptr) {
153       LOG(INFO) << "Can't set poll answer, because have no read access to " << dialog_id_;
154       return on_error(Status::Error(400, "Can't access the chat"));
155     }
156 
157     auto message_id = full_message_id.get_message_id().get_server_message_id().get();
158     auto query = G()->net_query_creator().create(
159         telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options)));
160     *query_ref = query.get_weak();
161     auto sequence_id = -1;
162     send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
163                  std::move(query), actor_shared(this), sequence_id);
164   }
165 
on_result(BufferSlice packet)166   void on_result(BufferSlice packet) final {
167     auto result_ptr = fetch_result<telegram_api::messages_sendVote>(packet);
168     if (result_ptr.is_error()) {
169       return on_error(result_ptr.move_as_error());
170     }
171 
172     auto result = result_ptr.move_as_ok();
173     LOG(INFO) << "Receive sendVote result: " << to_string(result);
174     promise_.set_value(std::move(result));
175   }
176 
on_error(Status status)177   void on_error(Status status) final {
178     td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetPollAnswerActor");
179     promise_.set_error(std::move(status));
180   }
181 };
182 
183 class StopPollActor final : public NetActorOnce {
184   Promise<Unit> promise_;
185   DialogId dialog_id_;
186 
187  public:
StopPollActor(Promise<Unit> && promise)188   explicit StopPollActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
189   }
190 
send(FullMessageId full_message_id,unique_ptr<ReplyMarkup> && reply_markup)191   void send(FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup) {
192     dialog_id_ = full_message_id.get_dialog_id();
193     auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Edit);
194     if (input_peer == nullptr) {
195       LOG(INFO) << "Can't close poll, because have no edit access to " << dialog_id_;
196       return on_error(Status::Error(400, "Can't access the chat"));
197     }
198 
199     int32 flags = telegram_api::messages_editMessage::MEDIA_MASK;
200     auto input_reply_markup = get_input_reply_markup(reply_markup);
201     if (input_reply_markup != nullptr) {
202       flags |= telegram_api::messages_editMessage::REPLY_MARKUP_MASK;
203     }
204 
205     auto message_id = full_message_id.get_message_id().get_server_message_id().get();
206     auto poll = telegram_api::make_object<telegram_api::poll>();
207     poll->flags_ |= telegram_api::poll::CLOSED_MASK;
208     auto input_media = telegram_api::make_object<telegram_api::inputMediaPoll>(0, std::move(poll),
209                                                                                vector<BufferSlice>(), string(), Auto());
210     auto query = G()->net_query_creator().create(telegram_api::messages_editMessage(
211         flags, false /*ignored*/, std::move(input_peer), message_id, string(), std::move(input_media),
212         std::move(input_reply_markup), vector<tl_object_ptr<telegram_api::MessageEntity>>(), 0));
213     if (td_->auth_manager_->is_bot()) {
214       send_query(std::move(query));
215     } else {
216       auto sequence_id = -1;
217       send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
218                    std::move(query), actor_shared(this), sequence_id);
219     }
220   }
221 
on_result(BufferSlice packet)222   void on_result(BufferSlice packet) final {
223     auto result_ptr = fetch_result<telegram_api::messages_editMessage>(packet);
224     if (result_ptr.is_error()) {
225       return on_error(result_ptr.move_as_error());
226     }
227 
228     auto result = result_ptr.move_as_ok();
229     LOG(INFO) << "Receive result for StopPoll: " << to_string(result);
230     td_->updates_manager_->on_get_updates(std::move(result), std::move(promise_));
231   }
232 
on_error(Status status)233   void on_error(Status status) final {
234     if (!td_->auth_manager_->is_bot() && status.message() == "MESSAGE_NOT_MODIFIED") {
235       return promise_.set_value(Unit());
236     }
237     td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "StopPollActor");
238     promise_.set_error(std::move(status));
239   }
240 };
241 
PollManager(Td * td,ActorShared<> parent)242 PollManager::PollManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
243   update_poll_timeout_.set_callback(on_update_poll_timeout_callback);
244   update_poll_timeout_.set_callback_data(static_cast<void *>(this));
245 
246   close_poll_timeout_.set_callback(on_close_poll_timeout_callback);
247   close_poll_timeout_.set_callback_data(static_cast<void *>(this));
248 }
249 
start_up()250 void PollManager::start_up() {
251   class StateCallback final : public StateManager::Callback {
252    public:
253     explicit StateCallback(ActorId<PollManager> parent) : parent_(std::move(parent)) {
254     }
255     bool on_online(bool is_online) final {
256       if (is_online) {
257         send_closure(parent_, &PollManager::on_online);
258       }
259       return parent_.is_alive();
260     }
261 
262    private:
263     ActorId<PollManager> parent_;
264   };
265   send_closure(G()->state_manager(), &StateManager::add_callback, make_unique<StateCallback>(actor_id(this)));
266 }
267 
tear_down()268 void PollManager::tear_down() {
269   parent_.reset();
270 }
271 
272 PollManager::~PollManager() = default;
273 
on_update_poll_timeout_callback(void * poll_manager_ptr,int64 poll_id_int)274 void PollManager::on_update_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) {
275   if (G()->close_flag()) {
276     return;
277   }
278 
279   auto poll_manager = static_cast<PollManager *>(poll_manager_ptr);
280   send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_update_poll_timeout, PollId(poll_id_int));
281 }
282 
on_close_poll_timeout_callback(void * poll_manager_ptr,int64 poll_id_int)283 void PollManager::on_close_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) {
284   if (G()->close_flag()) {
285     return;
286   }
287 
288   auto poll_manager = static_cast<PollManager *>(poll_manager_ptr);
289   send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_close_poll_timeout, PollId(poll_id_int));
290 }
291 
is_local_poll_id(PollId poll_id)292 bool PollManager::is_local_poll_id(PollId poll_id) {
293   return poll_id.get() < 0 && poll_id.get() > std::numeric_limits<int32>::min();
294 }
295 
get_poll(PollId poll_id) const296 const PollManager::Poll *PollManager::get_poll(PollId poll_id) const {
297   auto p = polls_.find(poll_id);
298   if (p == polls_.end()) {
299     return nullptr;
300   } else {
301     return p->second.get();
302   }
303 }
304 
get_poll_editable(PollId poll_id)305 PollManager::Poll *PollManager::get_poll_editable(PollId poll_id) {
306   auto p = polls_.find(poll_id);
307   if (p == polls_.end()) {
308     return nullptr;
309   } else {
310     return p->second.get();
311   }
312 }
313 
have_poll(PollId poll_id) const314 bool PollManager::have_poll(PollId poll_id) const {
315   return get_poll(poll_id) != nullptr;
316 }
317 
notify_on_poll_update(PollId poll_id)318 void PollManager::notify_on_poll_update(PollId poll_id) {
319   auto it = poll_messages_.find(poll_id);
320   if (it == poll_messages_.end()) {
321     return;
322   }
323 
324   for (const auto &full_message_id : it->second) {
325     td_->messages_manager_->on_external_update_message_content(full_message_id);
326   }
327 }
328 
get_poll_database_key(PollId poll_id)329 string PollManager::get_poll_database_key(PollId poll_id) {
330   return PSTRING() << "poll" << poll_id.get();
331 }
332 
save_poll(const Poll * poll,PollId poll_id)333 void PollManager::save_poll(const Poll *poll, PollId poll_id) {
334   CHECK(!is_local_poll_id(poll_id));
335   poll->was_saved = true;
336 
337   if (!G()->parameters().use_message_db) {
338     return;
339   }
340 
341   LOG(INFO) << "Save " << poll_id << " to database";
342   CHECK(poll != nullptr);
343   G()->td_db()->get_sqlite_pmc()->set(get_poll_database_key(poll_id), log_event_store(*poll).as_slice().str(), Auto());
344 }
345 
on_load_poll_from_database(PollId poll_id,string value)346 void PollManager::on_load_poll_from_database(PollId poll_id, string value) {
347   loaded_from_database_polls_.insert(poll_id);
348 
349   LOG(INFO) << "Successfully loaded " << poll_id << " of size " << value.size() << " from database";
350   //  G()->td_db()->get_sqlite_pmc()->erase(get_poll_database_key(poll_id), Auto());
351   //  return;
352 
353   CHECK(!have_poll(poll_id));
354   if (!value.empty()) {
355     auto poll = make_unique<Poll>();
356     auto status = log_event_parse(*poll, value);
357     if (status.is_error()) {
358       LOG(FATAL) << status << ": " << format::as_hex_dump<4>(Slice(value));
359     }
360     for (auto &user_id : poll->recent_voter_user_ids) {
361       td_->contacts_manager_->have_user_force(user_id);
362     }
363     if (!poll->is_closed && poll->close_date != 0) {
364       if (poll->close_date <= G()->server_time()) {
365         poll->is_closed = true;
366       } else {
367         CHECK(!is_local_poll_id(poll_id));
368         close_poll_timeout_.set_timeout_in(poll_id.get(), poll->close_date - G()->server_time() + 1e-3);
369       }
370     }
371     polls_[poll_id] = std::move(poll);
372   }
373 }
374 
have_poll_force(PollId poll_id)375 bool PollManager::have_poll_force(PollId poll_id) {
376   return get_poll_force(poll_id) != nullptr;
377 }
378 
get_poll_force(PollId poll_id)379 PollManager::Poll *PollManager::get_poll_force(PollId poll_id) {
380   auto poll = get_poll_editable(poll_id);
381   if (poll != nullptr) {
382     return poll;
383   }
384   if (!G()->parameters().use_message_db) {
385     return nullptr;
386   }
387   if (loaded_from_database_polls_.count(poll_id)) {
388     return nullptr;
389   }
390 
391   LOG(INFO) << "Trying to load " << poll_id << " from database";
392   on_load_poll_from_database(poll_id, G()->td_db()->get_sqlite_sync_pmc()->get(get_poll_database_key(poll_id)));
393   return get_poll_editable(poll_id);
394 }
395 
get_poll_option_object(const PollOption & poll_option)396 td_api::object_ptr<td_api::pollOption> PollManager::get_poll_option_object(const PollOption &poll_option) {
397   return td_api::make_object<td_api::pollOption>(poll_option.text, poll_option.voter_count, 0, poll_option.is_chosen,
398                                                  false);
399 }
400 
get_vote_percentage(const vector<int32> & voter_counts,int32 total_voter_count)401 vector<int32> PollManager::get_vote_percentage(const vector<int32> &voter_counts, int32 total_voter_count) {
402   int32 sum = 0;
403   for (auto voter_count : voter_counts) {
404     CHECK(0 <= voter_count);
405     CHECK(voter_count <= std::numeric_limits<int32>::max() - sum);
406     sum += voter_count;
407   }
408   if (total_voter_count > sum) {
409     if (sum != 0) {
410       LOG(ERROR) << "Have total_voter_count = " << total_voter_count << ", but votes sum = " << sum << ": "
411                  << voter_counts;
412     }
413     total_voter_count = sum;
414   }
415 
416   vector<int32> result(voter_counts.size(), 0);
417   if (total_voter_count == 0) {
418     return result;
419   }
420   if (total_voter_count != sum) {
421     // just round to the nearest
422     for (size_t i = 0; i < result.size(); i++) {
423       result[i] =
424           static_cast<int32>((static_cast<int64>(voter_counts[i]) * 200 + total_voter_count) / total_voter_count / 2);
425     }
426     return result;
427   }
428 
429   // make sure that options with equal votes have equal percent and total sum is less than 100%
430   int32 percent_sum = 0;
431   vector<int32> gap(voter_counts.size(), 0);
432   for (size_t i = 0; i < result.size(); i++) {
433     auto multiplied_voter_count = static_cast<int64>(voter_counts[i]) * 100;
434     result[i] = static_cast<int32>(multiplied_voter_count / total_voter_count);
435     CHECK(0 <= result[i] && result[i] <= 100);
436     gap[i] = static_cast<int32>(static_cast<int64>(result[i] + 1) * total_voter_count - multiplied_voter_count);
437     CHECK(0 <= gap[i] && gap[i] <= total_voter_count);
438     percent_sum += result[i];
439   }
440   CHECK(0 <= percent_sum && percent_sum <= 100);
441   if (percent_sum == 100) {
442     return result;
443   }
444 
445   // now we need to choose up to (100 - percent_sum) options with a minimum total gap, such that
446   // any two options with the same voter_count are chosen or not chosen simultaneously
447   struct Option {
448     int32 pos = -1;
449     int32 count = 0;
450   };
451   std::unordered_map<int32, Option> options;
452   for (size_t i = 0; i < result.size(); i++) {
453     auto &option = options[voter_counts[i]];
454     option.pos = narrow_cast<int32>(i);
455     option.count++;
456   }
457   vector<Option> sorted_options;
458   for (const auto &it : options) {
459     const auto &option = it.second;
460     auto pos = option.pos;
461     if (gap[pos] > total_voter_count / 2) {
462       // do not round to wrong direction
463       continue;
464     }
465     if (total_voter_count % 2 == 0 && gap[pos] == total_voter_count / 2 && result[pos] >= 50) {
466       // round halves to the 50%
467       continue;
468     }
469     sorted_options.push_back(option);
470   }
471   std::sort(sorted_options.begin(), sorted_options.end(), [&](const Option &lhs, const Option &rhs) {
472     if (gap[lhs.pos] != gap[rhs.pos]) {
473       // prefer options with smallest gap
474       return gap[lhs.pos] < gap[rhs.pos];
475     }
476     return lhs.count > rhs.count;  // prefer more popular options
477   });
478 
479   // dynamic programming or brute force can give perfect result, but for now we use simple gready approach
480   int32 left_percent = 100 - percent_sum;
481   for (auto option : sorted_options) {
482     if (option.count <= left_percent) {
483       left_percent -= option.count;
484 
485       auto pos = option.pos;
486       for (size_t i = 0; i < result.size(); i++) {
487         if (voter_counts[i] == voter_counts[pos]) {
488           result[i]++;
489         }
490       }
491       if (left_percent == 0) {
492         break;
493       }
494     }
495   }
496   return result;
497 }
498 
get_poll_object(PollId poll_id) const499 td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id) const {
500   auto poll = get_poll(poll_id);
501   CHECK(poll != nullptr);
502   return get_poll_object(poll_id, poll);
503 }
504 
get_poll_object(PollId poll_id,const Poll * poll) const505 td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id, const Poll *poll) const {
506   vector<td_api::object_ptr<td_api::pollOption>> poll_options;
507   auto it = pending_answers_.find(poll_id);
508   int32 voter_count_diff = 0;
509   if (it == pending_answers_.end()) {
510     poll_options = transform(poll->options, get_poll_option_object);
511   } else {
512     auto &chosen_options = it->second.options_;
513     for (auto &poll_option : poll->options) {
514       auto is_being_chosen = td::contains(chosen_options, poll_option.data);
515       if (poll_option.is_chosen) {
516         voter_count_diff = -1;
517       }
518       poll_options.push_back(td_api::make_object<td_api::pollOption>(
519           poll_option.text, poll_option.voter_count - static_cast<int32>(poll_option.is_chosen), 0, false,
520           is_being_chosen));
521     }
522   }
523 
524   auto total_voter_count = poll->total_voter_count + voter_count_diff;
525   bool is_voted = false;
526   for (auto &poll_option : poll_options) {
527     is_voted |= poll_option->is_chosen_;
528   }
529   if (!is_voted && !poll->is_closed && !td_->auth_manager_->is_bot()) {
530     // hide the voter counts
531     for (auto &poll_option : poll_options) {
532       poll_option->voter_count_ = 0;
533     }
534   } else {
535     // calculate vote percentage and fix total_voter_count
536     auto voter_counts = transform(poll_options, [](auto &poll_option) { return poll_option->voter_count_; });
537     auto voter_count_sum = 0;
538     for (auto voter_count : voter_counts) {
539       if (total_voter_count < voter_count) {
540         LOG(ERROR) << "Fix total voter count from " << poll->total_voter_count << " + " << voter_count_diff << " to "
541                    << voter_count << " in " << poll_id;
542         total_voter_count = voter_count;
543       }
544       voter_count_sum += voter_count;
545     }
546     if (voter_count_sum < total_voter_count && voter_count_sum != 0) {
547       LOG(ERROR) << "Fix total voter count from " << poll->total_voter_count << " + " << voter_count_diff << " to "
548                  << voter_count_sum << " in " << poll_id;
549       total_voter_count = voter_count_sum;
550     }
551 
552     auto vote_percentage = get_vote_percentage(voter_counts, total_voter_count);
553     CHECK(poll_options.size() == vote_percentage.size());
554     for (size_t i = 0; i < poll_options.size(); i++) {
555       poll_options[i]->vote_percentage_ = vote_percentage[i];
556     }
557   }
558   td_api::object_ptr<td_api::PollType> poll_type;
559   if (poll->is_quiz) {
560     auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id;
561     poll_type = td_api::make_object<td_api::pollTypeQuiz>(
562         correct_option_id,
563         get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation, true, -1));
564   } else {
565     poll_type = td_api::make_object<td_api::pollTypeRegular>(poll->allow_multiple_answers);
566   }
567 
568   auto open_period = poll->open_period;
569   auto close_date = poll->close_date;
570   if (open_period != 0 && close_date == 0) {
571     close_date = G()->unix_time() + open_period;
572   }
573   if (open_period == 0 && close_date != 0) {
574     auto now = G()->unix_time();
575     if (close_date < now + 5) {
576       close_date = 0;
577     } else {
578       open_period = close_date - now;
579     }
580   }
581   if (poll->is_closed) {
582     open_period = 0;
583     close_date = 0;
584   }
585   return td_api::make_object<td_api::poll>(
586       poll_id.get(), poll->question, std::move(poll_options), total_voter_count,
587       td_->contacts_manager_->get_user_ids_object(poll->recent_voter_user_ids, "get_poll_object"), poll->is_anonymous,
588       std::move(poll_type), open_period, close_date, poll->is_closed);
589 }
590 
get_input_poll_option(const PollOption & poll_option)591 telegram_api::object_ptr<telegram_api::pollAnswer> PollManager::get_input_poll_option(const PollOption &poll_option) {
592   return telegram_api::make_object<telegram_api::pollAnswer>(poll_option.text, BufferSlice(poll_option.data));
593 }
594 
create_poll(string && question,vector<string> && options,bool is_anonymous,bool allow_multiple_answers,bool is_quiz,int32 correct_option_id,FormattedText && explanation,int32 open_period,int32 close_date,bool is_closed)595 PollId PollManager::create_poll(string &&question, vector<string> &&options, bool is_anonymous,
596                                 bool allow_multiple_answers, bool is_quiz, int32 correct_option_id,
597                                 FormattedText &&explanation, int32 open_period, int32 close_date, bool is_closed) {
598   auto poll = make_unique<Poll>();
599   poll->question = std::move(question);
600   int pos = '0';
601   for (auto &option_text : options) {
602     PollOption option;
603     option.text = std::move(option_text);
604     option.data = string(1, narrow_cast<char>(pos++));
605     poll->options.push_back(std::move(option));
606   }
607   poll->is_anonymous = is_anonymous;
608   poll->allow_multiple_answers = allow_multiple_answers;
609   poll->is_quiz = is_quiz;
610   poll->correct_option_id = correct_option_id;
611   poll->explanation = std::move(explanation);
612   poll->open_period = open_period;
613   poll->close_date = close_date;
614   poll->is_closed = is_closed;
615 
616   PollId poll_id(--current_local_poll_id_);
617   CHECK(is_local_poll_id(poll_id));
618   bool is_inserted = polls_.emplace(poll_id, std::move(poll)).second;
619   CHECK(is_inserted);
620   return poll_id;
621 }
622 
register_poll(PollId poll_id,FullMessageId full_message_id,const char * source)623 void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id, const char *source) {
624   CHECK(have_poll(poll_id));
625   if (full_message_id.get_message_id().is_scheduled()) {
626     return;
627   }
628   if (!full_message_id.get_message_id().is_server()) {
629     return;
630   }
631   LOG(INFO) << "Register " << poll_id << " from " << full_message_id << " from " << source;
632   bool is_inserted = poll_messages_[poll_id].insert(full_message_id).second;
633   LOG_CHECK(is_inserted) << source << " " << poll_id << " " << full_message_id;
634   auto poll = get_poll(poll_id);
635   CHECK(poll != nullptr);
636   if (!td_->auth_manager_->is_bot() && !is_local_poll_id(poll_id) &&
637       !(poll->is_closed && poll->is_updated_after_close)) {
638     update_poll_timeout_.add_timeout_in(poll_id.get(), 0);
639   }
640 }
641 
unregister_poll(PollId poll_id,FullMessageId full_message_id,const char * source)642 void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id, const char *source) {
643   CHECK(have_poll(poll_id));
644   if (full_message_id.get_message_id().is_scheduled()) {
645     return;
646   }
647   if (!full_message_id.get_message_id().is_server()) {
648     return;
649   }
650   LOG(INFO) << "Unregister " << poll_id << " from " << full_message_id << " from " << source;
651   auto &message_ids = poll_messages_[poll_id];
652   auto is_deleted = message_ids.erase(full_message_id) > 0;
653   LOG_CHECK(is_deleted) << source << " " << poll_id << " " << full_message_id;
654   if (message_ids.empty()) {
655     poll_messages_.erase(poll_id);
656     update_poll_timeout_.cancel_timeout(poll_id.get());
657   }
658 }
659 
get_poll_is_closed(PollId poll_id) const660 bool PollManager::get_poll_is_closed(PollId poll_id) const {
661   auto poll = get_poll(poll_id);
662   CHECK(poll != nullptr);
663   return poll->is_closed;
664 }
665 
get_poll_is_anonymous(PollId poll_id) const666 bool PollManager::get_poll_is_anonymous(PollId poll_id) const {
667   auto poll = get_poll(poll_id);
668   CHECK(poll != nullptr);
669   return poll->is_anonymous;
670 }
671 
get_poll_search_text(PollId poll_id) const672 string PollManager::get_poll_search_text(PollId poll_id) const {
673   auto poll = get_poll(poll_id);
674   CHECK(poll != nullptr);
675 
676   string result = poll->question;
677   for (auto &option : poll->options) {
678     result += ' ';
679     result += option.text;
680   }
681   return result;
682 }
683 
set_poll_answer(PollId poll_id,FullMessageId full_message_id,vector<int32> && option_ids,Promise<Unit> && promise)684 void PollManager::set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<int32> &&option_ids,
685                                   Promise<Unit> &&promise) {
686   td::unique(option_ids);
687 
688   if (is_local_poll_id(poll_id)) {
689     return promise.set_error(Status::Error(400, "Poll can't be answered"));
690   }
691 
692   auto poll = get_poll(poll_id);
693   CHECK(poll != nullptr);
694   if (poll->is_closed) {
695     return promise.set_error(Status::Error(400, "Can't answer closed poll"));
696   }
697   if (!poll->allow_multiple_answers && option_ids.size() > 1) {
698     return promise.set_error(Status::Error(400, "Can't choose more than 1 option in the poll"));
699   }
700   if (poll->is_quiz && option_ids.empty()) {
701     return promise.set_error(Status::Error(400, "Can't retract vote in a quiz"));
702   }
703   if (poll->is_quiz && pending_answers_.count(poll_id) != 0) {
704     return promise.set_error(Status::Error(400, "Can't revote in a quiz"));
705   }
706 
707   std::unordered_map<size_t, int> affected_option_ids;
708   vector<string> options;
709   for (auto &option_id : option_ids) {
710     auto index = static_cast<size_t>(option_id);
711     if (index >= poll->options.size()) {
712       return promise.set_error(Status::Error(400, "Invalid option ID specified"));
713     }
714     options.push_back(poll->options[index].data);
715 
716     affected_option_ids[index]++;
717   }
718   for (size_t option_index = 0; option_index < poll->options.size(); option_index++) {
719     if (poll->options[option_index].is_chosen) {
720       if (poll->is_quiz) {
721         return promise.set_error(Status::Error(400, "Can't revote in a quiz"));
722       }
723       affected_option_ids[option_index]++;
724     }
725   }
726   for (auto it : affected_option_ids) {
727     if (it.second == 1) {
728       invalidate_poll_option_voters(poll, poll_id, it.first);
729     }
730   }
731 
732   do_set_poll_answer(poll_id, full_message_id, std::move(options), 0, std::move(promise));
733 }
734 
735 class PollManager::SetPollAnswerLogEvent {
736  public:
737   PollId poll_id_;
738   FullMessageId full_message_id_;
739   vector<string> options_;
740 
741   template <class StorerT>
store(StorerT & storer) const742   void store(StorerT &storer) const {
743     td::store(poll_id_, storer);
744     td::store(full_message_id_, storer);
745     td::store(options_, storer);
746   }
747 
748   template <class ParserT>
parse(ParserT & parser)749   void parse(ParserT &parser) {
750     td::parse(poll_id_, parser);
751     td::parse(full_message_id_, parser);
752     td::parse(options_, parser);
753   }
754 };
755 
do_set_poll_answer(PollId poll_id,FullMessageId full_message_id,vector<string> && options,uint64 log_event_id,Promise<Unit> && promise)756 void PollManager::do_set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<string> &&options,
757                                      uint64 log_event_id, Promise<Unit> &&promise) {
758   LOG(INFO) << "Set answer in " << poll_id << " from " << full_message_id;
759   auto &pending_answer = pending_answers_[poll_id];
760   if (!pending_answer.promises_.empty() && pending_answer.options_ == options) {
761     pending_answer.promises_.push_back(std::move(promise));
762     return;
763   }
764 
765   if (pending_answer.log_event_id_ != 0 && log_event_id != 0) {
766     LOG(ERROR) << "Duplicate SetPollAnswer log event: " << pending_answer.log_event_id_ << " and " << log_event_id;
767     binlog_erase(G()->td_db()->get_binlog(), log_event_id);
768     return;
769   }
770   if (log_event_id == 0 && G()->parameters().use_message_db) {
771     SetPollAnswerLogEvent log_event;
772     log_event.poll_id_ = poll_id;
773     log_event.full_message_id_ = full_message_id;
774     log_event.options_ = options;
775     auto storer = get_log_event_storer(log_event);
776     if (pending_answer.generation_ == 0) {
777       CHECK(pending_answer.log_event_id_ == 0);
778       log_event_id = binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::SetPollAnswer, storer);
779       LOG(INFO) << "Add set poll answer log event " << log_event_id;
780     } else {
781       CHECK(pending_answer.log_event_id_ != 0);
782       log_event_id = pending_answer.log_event_id_;
783       auto new_log_event_id = binlog_rewrite(G()->td_db()->get_binlog(), pending_answer.log_event_id_,
784                                              LogEvent::HandlerType::SetPollAnswer, storer);
785       LOG(INFO) << "Rewrite set poll answer log event " << log_event_id << " with " << new_log_event_id;
786     }
787   }
788 
789   if (!pending_answer.promises_.empty()) {
790     CHECK(!pending_answer.query_ref_.empty());
791     cancel_query(pending_answer.query_ref_);
792     pending_answer.query_ref_ = NetQueryRef();
793 
794     auto promises = std::move(pending_answer.promises_);
795     pending_answer.promises_.clear();
796     for (auto &old_promise : promises) {
797       old_promise.set_value(Unit());
798     }
799   }
800 
801   vector<BufferSlice> sent_options;
802   for (auto &option : options) {
803     sent_options.emplace_back(option);
804   }
805 
806   auto generation = ++current_generation_;
807 
808   pending_answer.options_ = std::move(options);
809   pending_answer.promises_.push_back(std::move(promise));
810   pending_answer.generation_ = generation;
811   pending_answer.log_event_id_ = log_event_id;
812 
813   notify_on_poll_update(poll_id);
814 
815   auto query_promise = PromiseCreator::lambda(
816       [poll_id, generation, actor_id = actor_id(this)](Result<tl_object_ptr<telegram_api::Updates>> &&result) {
817         send_closure(actor_id, &PollManager::on_set_poll_answer, poll_id, generation, std::move(result));
818       });
819   send_closure(td_->create_net_actor<SetPollAnswerActor>(std::move(query_promise)), &SetPollAnswerActor::send,
820                full_message_id, std::move(sent_options), generation, &pending_answer.query_ref_);
821 }
822 
on_set_poll_answer(PollId poll_id,uint64 generation,Result<tl_object_ptr<telegram_api::Updates>> && result)823 void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation,
824                                      Result<tl_object_ptr<telegram_api::Updates>> &&result) {
825   if (G()->close_flag() && result.is_error()) {
826     // request will be re-sent after restart
827     return;
828   }
829   auto it = pending_answers_.find(poll_id);
830   if (it == pending_answers_.end()) {
831     // can happen if this is an answer with mismatched generation and server has ignored invoke-after
832     return;
833   }
834 
835   auto &pending_answer = it->second;
836   CHECK(!pending_answer.promises_.empty());
837   if (pending_answer.generation_ != generation) {
838     return;
839   }
840 
841   if (pending_answer.log_event_id_ != 0) {
842     LOG(INFO) << "Delete set poll answer log event " << pending_answer.log_event_id_;
843     binlog_erase(G()->td_db()->get_binlog(), pending_answer.log_event_id_);
844   }
845 
846   auto promises = std::move(pending_answer.promises_);
847   pending_answers_.erase(it);
848 
849   auto poll = get_poll(poll_id);
850   if (poll != nullptr) {
851     poll->was_saved = false;
852   }
853   if (result.is_ok()) {
854     td_->updates_manager_->on_get_updates(
855         result.move_as_ok(), PromiseCreator::lambda([actor_id = actor_id(this), poll_id,
856                                                      promises = std::move(promises)](Result<Unit> &&result) mutable {
857           send_closure(actor_id, &PollManager::on_set_poll_answer_finished, poll_id, Unit(), std::move(promises));
858         }));
859   } else {
860     on_set_poll_answer_finished(poll_id, result.move_as_error(), std::move(promises));
861   }
862 }
863 
on_set_poll_answer_finished(PollId poll_id,Result<Unit> && result,vector<Promise<Unit>> && promises)864 void PollManager::on_set_poll_answer_finished(PollId poll_id, Result<Unit> &&result, vector<Promise<Unit>> &&promises) {
865   if (!G()->close_flag()) {
866     auto poll = get_poll(poll_id);
867     if (poll != nullptr && !poll->was_saved) {
868       // no updates was sent during updates processing, so send them
869       // poll wasn't changed, so there is no reason to actually save it
870       if (!(poll->is_closed && poll->is_updated_after_close)) {
871         LOG(INFO) << "Schedule updating of " << poll_id << " soon";
872         update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0);
873       }
874 
875       notify_on_poll_update(poll_id);
876       poll->was_saved = true;
877     }
878   }
879 
880   for (auto &promise : promises) {
881     promise.set_result(result.clone());
882   }
883 }
884 
invalidate_poll_voters(const Poll * poll,PollId poll_id)885 void PollManager::invalidate_poll_voters(const Poll *poll, PollId poll_id) {
886   if (poll->is_anonymous) {
887     return;
888   }
889 
890   auto it = poll_voters_.find(poll_id);
891   if (it == poll_voters_.end()) {
892     return;
893   }
894 
895   for (auto &voters : it->second) {
896     voters.was_invalidated = true;
897   }
898 }
899 
invalidate_poll_option_voters(const Poll * poll,PollId poll_id,size_t option_index)900 void PollManager::invalidate_poll_option_voters(const Poll *poll, PollId poll_id, size_t option_index) {
901   if (poll->is_anonymous) {
902     return;
903   }
904 
905   auto it = poll_voters_.find(poll_id);
906   if (it == poll_voters_.end()) {
907     return;
908   }
909 
910   auto &poll_voters = it->second;
911   CHECK(poll_voters.size() == poll->options.size());
912   CHECK(option_index < poll_voters.size());
913   poll_voters[option_index].was_invalidated = true;
914 }
915 
get_poll_option_voters(const Poll * poll,PollId poll_id,int32 option_id)916 PollManager::PollOptionVoters &PollManager::get_poll_option_voters(const Poll *poll, PollId poll_id, int32 option_id) {
917   auto &poll_voters = poll_voters_[poll_id];
918   if (poll_voters.empty()) {
919     poll_voters.resize(poll->options.size());
920   }
921   auto index = narrow_cast<size_t>(option_id);
922   CHECK(index < poll_voters.size());
923   return poll_voters[index];
924 }
925 
get_poll_voters(PollId poll_id,FullMessageId full_message_id,int32 option_id,int32 offset,int32 limit,Promise<std::pair<int32,vector<UserId>>> && promise)926 void PollManager::get_poll_voters(PollId poll_id, FullMessageId full_message_id, int32 option_id, int32 offset,
927                                   int32 limit, Promise<std::pair<int32, vector<UserId>>> &&promise) {
928   if (is_local_poll_id(poll_id)) {
929     return promise.set_error(Status::Error(400, "Poll results can't be received"));
930   }
931   if (offset < 0) {
932     return promise.set_error(Status::Error(400, "Invalid offset specified"));
933   }
934   if (limit <= 0) {
935     return promise.set_error(Status::Error(400, "Parameter limit must be positive"));
936   }
937   if (limit > MAX_GET_POLL_VOTERS) {
938     limit = MAX_GET_POLL_VOTERS;
939   }
940 
941   auto poll = get_poll(poll_id);
942   CHECK(poll != nullptr);
943   if (option_id < 0 || static_cast<size_t>(option_id) >= poll->options.size()) {
944     return promise.set_error(Status::Error(400, "Invalid option ID specified"));
945   }
946   if (poll->is_anonymous) {
947     return promise.set_error(Status::Error(400, "Poll is anonymous"));
948   }
949 
950   auto &voters = get_poll_option_voters(poll, poll_id, option_id);
951   if (voters.pending_queries.empty() && voters.was_invalidated && offset == 0) {
952     voters.voter_user_ids.clear();
953     voters.next_offset.clear();
954     voters.was_invalidated = false;
955   }
956 
957   auto cur_offset = narrow_cast<int32>(voters.voter_user_ids.size());
958 
959   if (offset > cur_offset) {
960     return promise.set_error(Status::Error(400, "Too big offset specified; voters can be received only consequently"));
961   }
962   if (offset < cur_offset) {
963     vector<UserId> result;
964     for (int32 i = offset; i != cur_offset && i - offset < limit; i++) {
965       result.push_back(voters.voter_user_ids[i]);
966     }
967     return promise.set_value({poll->options[option_id].voter_count, std::move(result)});
968   }
969 
970   if (poll->options[option_id].voter_count == 0 || (voters.next_offset.empty() && cur_offset > 0)) {
971     return promise.set_value({0, vector<UserId>()});
972   }
973 
974   voters.pending_queries.push_back(std::move(promise));
975   if (voters.pending_queries.size() > 1) {
976     return;
977   }
978 
979   auto query_promise =
980       PromiseCreator::lambda([actor_id = actor_id(this), poll_id, option_id, offset = voters.next_offset,
981                               limit](Result<tl_object_ptr<telegram_api::messages_votesList>> &&result) mutable {
982         send_closure(actor_id, &PollManager::on_get_poll_voters, poll_id, option_id, std::move(offset), limit,
983                      std::move(result));
984       });
985   td_->create_handler<GetPollVotersQuery>(std::move(query_promise))
986       ->send(poll_id, full_message_id, BufferSlice(poll->options[option_id].data), voters.next_offset, max(limit, 10));
987 }
988 
on_get_poll_voters(PollId poll_id,int32 option_id,string offset,int32 limit,Result<tl_object_ptr<telegram_api::messages_votesList>> && result)989 void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, string offset, int32 limit,
990                                      Result<tl_object_ptr<telegram_api::messages_votesList>> &&result) {
991   auto poll = get_poll(poll_id);
992   CHECK(poll != nullptr);
993   if (option_id < 0 || static_cast<size_t>(option_id) >= poll->options.size()) {
994     LOG(ERROR) << "Can't process voters for option " << option_id << " in " << poll_id << ", because it has only "
995                << poll->options.size() << " options";
996     return;
997   }
998   if (poll->is_anonymous) {
999     // just in case
1000     result = Status::Error(400, "Poll is anonymous");
1001   }
1002 
1003   auto &voters = get_poll_option_voters(poll, poll_id, option_id);
1004   if (voters.next_offset != offset) {
1005     LOG(ERROR) << "Expected results for option " << option_id << " in " << poll_id << " with offset "
1006                << voters.next_offset << ", but received with " << offset;
1007     return;
1008   }
1009   auto promises = std::move(voters.pending_queries);
1010   if (promises.empty()) {
1011     LOG(ERROR) << "Have no waiting promises for option " << option_id << " in " << poll_id;
1012     return;
1013   }
1014   if (result.is_error()) {
1015     for (auto &promise : promises) {
1016       promise.set_error(result.error().clone());
1017     }
1018     return;
1019   }
1020 
1021   auto vote_list = result.move_as_ok();
1022   td_->contacts_manager_->on_get_users(std::move(vote_list->users_), "on_get_poll_voters");
1023 
1024   voters.next_offset = std::move(vote_list->next_offset_);
1025   if (poll->options[option_id].voter_count != vote_list->count_) {
1026     ++current_generation_;
1027     update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0);
1028   }
1029 
1030   vector<UserId> user_ids;
1031   for (auto &user_vote : vote_list->votes_) {
1032     UserId user_id;
1033     downcast_call(*user_vote, [&user_id](auto &voter) { user_id = UserId(voter.user_id_); });
1034     if (!user_id.is_valid()) {
1035       LOG(ERROR) << "Receive " << user_id << " as voter in " << poll_id;
1036       continue;
1037     }
1038 
1039     switch (user_vote->get_id()) {
1040       case telegram_api::messageUserVote::ID: {
1041         auto voter = telegram_api::move_object_as<telegram_api::messageUserVote>(user_vote);
1042         if (voter->option_ != poll->options[option_id].data) {
1043           continue;
1044         }
1045 
1046         user_ids.push_back(user_id);
1047         break;
1048       }
1049       case telegram_api::messageUserVoteInputOption::ID:
1050         user_ids.push_back(user_id);
1051         break;
1052       case telegram_api::messageUserVoteMultiple::ID: {
1053         auto voter = telegram_api::move_object_as<telegram_api::messageUserVoteMultiple>(user_vote);
1054         if (!td::contains(voter->options_, poll->options[option_id].data)) {
1055           continue;
1056         }
1057 
1058         user_ids.push_back(user_id);
1059         break;
1060       }
1061     }
1062   }
1063   voters.voter_user_ids.insert(voters.voter_user_ids.end(), user_ids.begin(), user_ids.end());
1064   if (static_cast<int32>(user_ids.size()) > limit) {
1065     user_ids.resize(limit);
1066   }
1067   if (voters.next_offset.empty() && narrow_cast<int32>(voters.voter_user_ids.size()) != vote_list->count_) {
1068     // invalidate_poll_option_voters(poll, poll_id, option_id);
1069     voters.was_invalidated = true;
1070   }
1071 
1072   for (auto &promise : promises) {
1073     promise.set_value({vote_list->count_, vector<UserId>(user_ids)});
1074   }
1075 }
1076 
stop_poll(PollId poll_id,FullMessageId full_message_id,unique_ptr<ReplyMarkup> && reply_markup,Promise<Unit> && promise)1077 void PollManager::stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup,
1078                             Promise<Unit> &&promise) {
1079   if (is_local_poll_id(poll_id)) {
1080     LOG(ERROR) << "Receive local " << poll_id << " from " << full_message_id << " in stop_poll";
1081     stop_local_poll(poll_id);
1082     promise.set_value(Unit());
1083     return;
1084   }
1085   auto poll = get_poll_editable(poll_id);
1086   CHECK(poll != nullptr);
1087   if (poll->is_closed) {
1088     promise.set_value(Unit());
1089     return;
1090   }
1091 
1092   ++current_generation_;
1093 
1094   poll->is_closed = true;
1095   notify_on_poll_update(poll_id);
1096   save_poll(poll, poll_id);
1097 
1098   do_stop_poll(poll_id, full_message_id, std::move(reply_markup), 0, std::move(promise));
1099 }
1100 
1101 class PollManager::StopPollLogEvent {
1102  public:
1103   PollId poll_id_;
1104   FullMessageId full_message_id_;
1105 
1106   template <class StorerT>
store(StorerT & storer) const1107   void store(StorerT &storer) const {
1108     td::store(poll_id_, storer);
1109     td::store(full_message_id_, storer);
1110   }
1111 
1112   template <class ParserT>
parse(ParserT & parser)1113   void parse(ParserT &parser) {
1114     td::parse(poll_id_, parser);
1115     td::parse(full_message_id_, parser);
1116   }
1117 };
1118 
do_stop_poll(PollId poll_id,FullMessageId full_message_id,unique_ptr<ReplyMarkup> && reply_markup,uint64 log_event_id,Promise<Unit> && promise)1119 void PollManager::do_stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup,
1120                                uint64 log_event_id, Promise<Unit> &&promise) {
1121   LOG(INFO) << "Stop " << poll_id << " from " << full_message_id;
1122   if (log_event_id == 0 && G()->parameters().use_message_db && reply_markup == nullptr) {
1123     StopPollLogEvent log_event{poll_id, full_message_id};
1124     log_event_id =
1125         binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::StopPoll, get_log_event_storer(log_event));
1126   }
1127 
1128   bool is_inserted = being_closed_polls_.insert(poll_id).second;
1129   CHECK(is_inserted);
1130   auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise));
1131 
1132   send_closure(td_->create_net_actor<StopPollActor>(std::move(new_promise)), &StopPollActor::send, full_message_id,
1133                std::move(reply_markup));
1134 }
1135 
stop_local_poll(PollId poll_id)1136 void PollManager::stop_local_poll(PollId poll_id) {
1137   CHECK(is_local_poll_id(poll_id));
1138   auto poll = get_poll_editable(poll_id);
1139   CHECK(poll != nullptr);
1140   if (poll->is_closed) {
1141     return;
1142   }
1143 
1144   poll->is_closed = true;
1145   notify_on_poll_update(poll_id);
1146 }
1147 
get_polling_timeout() const1148 double PollManager::get_polling_timeout() const {
1149   double result = td_->is_online() ? 60 : 30 * 60;
1150   return result * Random::fast(70, 100) * 0.01;
1151 }
1152 
on_update_poll_timeout(PollId poll_id)1153 void PollManager::on_update_poll_timeout(PollId poll_id) {
1154   if (G()->close_flag()) {
1155     return;
1156   }
1157   CHECK(!td_->auth_manager_->is_bot());
1158   CHECK(!is_local_poll_id(poll_id));
1159 
1160   auto poll = get_poll(poll_id);
1161   CHECK(poll != nullptr);
1162   if (poll->is_closed && poll->is_updated_after_close) {
1163     return;
1164   }
1165   if (pending_answers_.find(poll_id) != pending_answers_.end()) {
1166     LOG(INFO) << "Skip fetching results of " << poll_id << ", because it is being voted now";
1167     return;
1168   }
1169 
1170   auto it = poll_messages_.find(poll_id);
1171   if (it == poll_messages_.end()) {
1172     return;
1173   }
1174 
1175   auto full_message_id = *it->second.begin();
1176   LOG(INFO) << "Fetching results of " << poll_id << " from " << full_message_id;
1177   auto query_promise = PromiseCreator::lambda([poll_id, generation = current_generation_, actor_id = actor_id(this)](
1178                                                   Result<tl_object_ptr<telegram_api::Updates>> &&result) {
1179     send_closure(actor_id, &PollManager::on_get_poll_results, poll_id, generation, std::move(result));
1180   });
1181   td_->create_handler<GetPollResultsQuery>(std::move(query_promise))->send(poll_id, full_message_id);
1182 }
1183 
on_close_poll_timeout(PollId poll_id)1184 void PollManager::on_close_poll_timeout(PollId poll_id) {
1185   if (G()->close_flag()) {
1186     return;
1187   }
1188   CHECK(!is_local_poll_id(poll_id));
1189 
1190   auto poll = get_poll_editable(poll_id);
1191   CHECK(poll != nullptr);
1192   if (poll->is_closed || poll->close_date == 0) {
1193     return;
1194   }
1195 
1196   LOG(INFO) << "Trying to close " << poll_id << " by timer";
1197   if (poll->close_date <= G()->server_time()) {
1198     poll->is_closed = true;
1199     notify_on_poll_update(poll_id);
1200     save_poll(poll, poll_id);
1201 
1202     // don't send updatePoll for bots, because there is no way to guarantee it
1203 
1204     if (!td_->auth_manager_->is_bot()) {
1205       update_poll_timeout_.set_timeout_in(poll_id.get(), 1.0);
1206     }
1207   } else {
1208     close_poll_timeout_.set_timeout_in(poll_id.get(), poll->close_date - G()->server_time() + 1e-3);
1209   }
1210 }
1211 
on_get_poll_results(PollId poll_id,uint64 generation,Result<tl_object_ptr<telegram_api::Updates>> result)1212 void PollManager::on_get_poll_results(PollId poll_id, uint64 generation,
1213                                       Result<tl_object_ptr<telegram_api::Updates>> result) {
1214   auto poll = get_poll(poll_id);
1215   CHECK(poll != nullptr);
1216   if (result.is_error()) {
1217     if (!(poll->is_closed && poll->is_updated_after_close) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
1218       auto timeout = get_polling_timeout();
1219       LOG(INFO) << "Schedule updating of " << poll_id << " in " << timeout;
1220       update_poll_timeout_.add_timeout_in(poll_id.get(), timeout);
1221     }
1222     return;
1223   }
1224   if (result.ok() == nullptr) {
1225     return;
1226   }
1227   if (generation != current_generation_) {
1228     LOG(INFO) << "Receive possibly outdated result of " << poll_id << ", reget it";
1229     if (!(poll->is_closed && poll->is_updated_after_close) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
1230       update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0);
1231     }
1232     return;
1233   }
1234 
1235   td_->updates_manager_->on_get_updates(result.move_as_ok(), Promise<Unit>());
1236 }
1237 
on_online()1238 void PollManager::on_online() {
1239   if (td_->auth_manager_->is_bot()) {
1240     return;
1241   }
1242 
1243   for (auto &it : poll_messages_) {
1244     auto poll_id = it.first;
1245     if (update_poll_timeout_.has_timeout(poll_id.get())) {
1246       auto timeout = Random::fast(3, 30);
1247       LOG(INFO) << "Schedule updating of " << poll_id << " in " << timeout;
1248       update_poll_timeout_.set_timeout_in(poll_id.get(), timeout);
1249     }
1250   }
1251 }
1252 
dup_poll(PollId poll_id)1253 PollId PollManager::dup_poll(PollId poll_id) {
1254   auto poll = get_poll(poll_id);
1255   CHECK(poll != nullptr);
1256 
1257   auto question = poll->question;
1258   auto options = transform(poll->options, [](auto &option) { return option.text; });
1259   auto explanation = poll->explanation;
1260   return create_poll(std::move(question), std::move(options), poll->is_anonymous, poll->allow_multiple_answers,
1261                      poll->is_quiz, poll->correct_option_id, std::move(explanation), poll->open_period,
1262                      poll->open_period == 0 ? 0 : G()->unix_time(), false);
1263 }
1264 
has_input_media(PollId poll_id) const1265 bool PollManager::has_input_media(PollId poll_id) const {
1266   auto poll = get_poll(poll_id);
1267   CHECK(poll != nullptr);
1268   return !poll->is_quiz || poll->correct_option_id >= 0;
1269 }
1270 
get_input_media(PollId poll_id) const1271 tl_object_ptr<telegram_api::InputMedia> PollManager::get_input_media(PollId poll_id) const {
1272   auto poll = get_poll(poll_id);
1273   CHECK(poll != nullptr);
1274 
1275   int32 poll_flags = 0;
1276   if (!poll->is_anonymous) {
1277     poll_flags |= telegram_api::poll::PUBLIC_VOTERS_MASK;
1278   }
1279   if (poll->allow_multiple_answers) {
1280     poll_flags |= telegram_api::poll::MULTIPLE_CHOICE_MASK;
1281   }
1282   if (poll->is_quiz) {
1283     poll_flags |= telegram_api::poll::QUIZ_MASK;
1284   }
1285   if (poll->open_period != 0) {
1286     poll_flags |= telegram_api::poll::CLOSE_PERIOD_MASK;
1287   }
1288   if (poll->close_date != 0) {
1289     poll_flags |= telegram_api::poll::CLOSE_DATE_MASK;
1290   }
1291   if (poll->is_closed) {
1292     poll_flags |= telegram_api::poll::CLOSED_MASK;
1293   }
1294 
1295   int32 flags = 0;
1296   vector<BufferSlice> correct_answers;
1297   if (poll->is_quiz) {
1298     flags |= telegram_api::inputMediaPoll::CORRECT_ANSWERS_MASK;
1299     CHECK(poll->correct_option_id >= 0);
1300     CHECK(static_cast<size_t>(poll->correct_option_id) < poll->options.size());
1301     correct_answers.push_back(BufferSlice(poll->options[poll->correct_option_id].data));
1302 
1303     if (!poll->explanation.text.empty()) {
1304       flags |= telegram_api::inputMediaPoll::SOLUTION_MASK;
1305     }
1306   }
1307   return telegram_api::make_object<telegram_api::inputMediaPoll>(
1308       flags,
1309       telegram_api::make_object<telegram_api::poll>(
1310           0, poll_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, poll->question,
1311           transform(poll->options, get_input_poll_option), poll->open_period, poll->close_date),
1312       std::move(correct_answers), poll->explanation.text,
1313       get_input_message_entities(td_->contacts_manager_.get(), poll->explanation.entities, "get_input_media_poll"));
1314 }
1315 
get_poll_options(vector<tl_object_ptr<telegram_api::pollAnswer>> && poll_options)1316 vector<PollManager::PollOption> PollManager::get_poll_options(
1317     vector<tl_object_ptr<telegram_api::pollAnswer>> &&poll_options) {
1318   return transform(std::move(poll_options), [](tl_object_ptr<telegram_api::pollAnswer> &&poll_option) {
1319     PollOption option;
1320     option.text = std::move(poll_option->text_);
1321     option.data = poll_option->option_.as_slice().str();
1322     return option;
1323   });
1324 }
1325 
on_get_poll(PollId poll_id,tl_object_ptr<telegram_api::poll> && poll_server,tl_object_ptr<telegram_api::pollResults> && poll_results)1326 PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll> &&poll_server,
1327                                 tl_object_ptr<telegram_api::pollResults> &&poll_results) {
1328   bool is_bot = td_->auth_manager_->is_bot();
1329   bool need_update_poll = poll_id.is_valid() && is_bot;
1330   if (!poll_id.is_valid() && poll_server != nullptr) {
1331     poll_id = PollId(poll_server->id_);
1332   }
1333   if (!poll_id.is_valid() || is_local_poll_id(poll_id)) {
1334     LOG(ERROR) << "Receive " << poll_id << " from server: " << oneline(to_string(poll_server)) << " "
1335                << oneline(to_string(poll_results));
1336     return PollId();
1337   }
1338   if (poll_server != nullptr && poll_server->id_ != poll_id.get()) {
1339     LOG(ERROR) << "Receive poll " << poll_server->id_ << " instead of " << poll_id;
1340     return PollId();
1341   }
1342   constexpr size_t MAX_POLL_OPTIONS = 10;  // server-side limit
1343   if (poll_server != nullptr &&
1344       (poll_server->answers_.size() <= 1 || poll_server->answers_.size() > 10 * MAX_POLL_OPTIONS)) {
1345     LOG(ERROR) << "Receive " << poll_id << " with wrong number of answers: " << to_string(poll_server);
1346     return PollId();
1347   }
1348   if (poll_server != nullptr) {
1349     std::unordered_set<Slice, SliceHash> option_data;
1350     for (auto &answer : poll_server->answers_) {
1351       if (answer->option_.empty()) {
1352         LOG(ERROR) << "Receive " << poll_id << " with an empty option data: " << to_string(poll_server);
1353         return PollId();
1354       }
1355       option_data.insert(answer->option_.as_slice());
1356     }
1357     if (option_data.size() != poll_server->answers_.size()) {
1358       LOG(ERROR) << "Receive " << poll_id << " with duplicate options: " << to_string(poll_server);
1359       return PollId();
1360     }
1361   }
1362 
1363   auto poll = get_poll_force(poll_id);
1364   bool is_changed = false;
1365   bool need_save_to_database = false;
1366   if (poll == nullptr) {
1367     if (poll_server == nullptr) {
1368       LOG(INFO) << "Ignore " << poll_id << ", because have no data about it";
1369       return PollId();
1370     }
1371 
1372     auto p = make_unique<Poll>();
1373     poll = p.get();
1374     bool is_inserted = polls_.emplace(poll_id, std::move(p)).second;
1375     CHECK(is_inserted);
1376   }
1377   CHECK(poll != nullptr);
1378 
1379   bool poll_server_is_closed = false;
1380   if (poll_server != nullptr) {
1381     string correct_option_data;
1382     if (poll->correct_option_id != -1) {
1383       CHECK(0 <= poll->correct_option_id && poll->correct_option_id < static_cast<int32>(poll->options.size()));
1384       correct_option_data = poll->options[poll->correct_option_id].data;
1385     }
1386     bool are_options_changed = false;
1387     if (poll->options.size() != poll_server->answers_.size()) {
1388       poll->options = get_poll_options(std::move(poll_server->answers_));
1389       are_options_changed = true;
1390     } else {
1391       for (size_t i = 0; i < poll->options.size(); i++) {
1392         if (poll->options[i].text != poll_server->answers_[i]->text_) {
1393           poll->options[i].text = std::move(poll_server->answers_[i]->text_);
1394           is_changed = true;
1395         }
1396         if (poll->options[i].data != poll_server->answers_[i]->option_.as_slice()) {
1397           poll->options[i].data = poll_server->answers_[i]->option_.as_slice().str();
1398           poll->options[i].voter_count = 0;
1399           poll->options[i].is_chosen = false;
1400           are_options_changed = true;
1401         }
1402       }
1403     }
1404     if (are_options_changed) {
1405       if (!correct_option_data.empty()) {
1406         poll->correct_option_id = -1;
1407         for (size_t i = 0; i < poll->options.size(); i++) {
1408           if (poll->options[i].data == correct_option_data) {
1409             poll->correct_option_id = static_cast<int32>(i);
1410             break;
1411           }
1412         }
1413       }
1414       auto it = poll_voters_.find(poll_id);
1415       if (it != poll_voters_.end()) {
1416         for (auto &voters : it->second) {
1417           auto promises = std::move(voters.pending_queries);
1418           for (auto &promise : promises) {
1419             promise.set_error(Status::Error(500, "The poll was changed"));
1420           }
1421         }
1422         poll_voters_.erase(it);
1423       }
1424       is_changed = true;
1425     }
1426     if (poll->question != poll_server->question_) {
1427       poll->question = std::move(poll_server->question_);
1428       is_changed = true;
1429     }
1430     poll_server_is_closed = (poll_server->flags_ & telegram_api::poll::CLOSED_MASK) != 0;
1431     if (poll_server_is_closed && !poll->is_closed) {
1432       poll->is_closed = poll_server_is_closed;
1433       is_changed = true;
1434     }
1435     if (poll_server_is_closed && !poll->is_updated_after_close) {
1436       poll->is_updated_after_close = true;
1437       is_changed = true;
1438     }
1439     int32 open_period =
1440         (poll_server->flags_ & telegram_api::poll::CLOSE_PERIOD_MASK) != 0 ? poll_server->close_period_ : 0;
1441     int32 close_date = (poll_server->flags_ & telegram_api::poll::CLOSE_DATE_MASK) != 0 ? poll_server->close_date_ : 0;
1442     if (close_date == 0 || open_period == 0) {
1443       close_date = 0;
1444       open_period = 0;
1445     }
1446     if (open_period != poll->open_period) {
1447       poll->open_period = open_period;
1448       if (!poll->is_closed) {
1449         is_changed = true;
1450       } else {
1451         need_save_to_database = true;
1452       }
1453     }
1454     if (close_date != poll->close_date) {
1455       poll->close_date = close_date;
1456       if (!poll->is_closed) {
1457         is_changed = true;
1458         if (close_date != 0) {
1459           if (close_date <= G()->server_time()) {
1460             poll->is_closed = true;
1461           } else {
1462             close_poll_timeout_.set_timeout_in(poll_id.get(), close_date - G()->server_time() + 1e-3);
1463           }
1464         } else {
1465           close_poll_timeout_.cancel_timeout(poll_id.get());
1466         }
1467       } else {
1468         need_save_to_database = true;
1469       }
1470     }
1471     bool is_anonymous = (poll_server->flags_ & telegram_api::poll::PUBLIC_VOTERS_MASK) == 0;
1472     if (is_anonymous != poll->is_anonymous) {
1473       poll->is_anonymous = is_anonymous;
1474       is_changed = true;
1475     }
1476     bool allow_multiple_answers = (poll_server->flags_ & telegram_api::poll::MULTIPLE_CHOICE_MASK) != 0;
1477     bool is_quiz = (poll_server->flags_ & telegram_api::poll::QUIZ_MASK) != 0;
1478     if (is_quiz && allow_multiple_answers) {
1479       LOG(ERROR) << "Receive quiz " << poll_id << " allowing multiple answers";
1480       allow_multiple_answers = false;
1481     }
1482     if (allow_multiple_answers != poll->allow_multiple_answers) {
1483       poll->allow_multiple_answers = allow_multiple_answers;
1484       is_changed = true;
1485     }
1486     if (is_quiz != poll->is_quiz) {
1487       poll->is_quiz = is_quiz;
1488       is_changed = true;
1489     }
1490   }
1491 
1492   CHECK(poll_results != nullptr);
1493   bool is_min = poll_results->min_;
1494   bool has_total_voters = (poll_results->flags_ & telegram_api::pollResults::TOTAL_VOTERS_MASK) != 0;
1495   if (has_total_voters && poll_results->total_voters_ != poll->total_voter_count) {
1496     poll->total_voter_count = poll_results->total_voters_;
1497     if (poll->total_voter_count < 0) {
1498       LOG(ERROR) << "Receive " << poll->total_voter_count << " voters in " << poll_id;
1499       poll->total_voter_count = 0;
1500     }
1501     is_changed = true;
1502   }
1503   int32 correct_option_id = -1;
1504   for (auto &poll_result : poll_results->results_) {
1505     Slice data = poll_result->option_.as_slice();
1506     for (size_t option_index = 0; option_index < poll->options.size(); option_index++) {
1507       auto &option = poll->options[option_index];
1508       if (option.data != data) {
1509         continue;
1510       }
1511       if (!is_min) {
1512         bool is_chosen = poll_result->chosen_;
1513         if (is_chosen != option.is_chosen) {
1514           option.is_chosen = is_chosen;
1515           is_changed = true;
1516         }
1517       }
1518       if (!is_min || poll_server_is_closed) {
1519         bool is_correct = poll_result->correct_;
1520         if (is_correct) {
1521           if (correct_option_id != -1) {
1522             LOG(ERROR) << "Receive more than 1 correct answers " << correct_option_id << " and " << option_index;
1523           }
1524           correct_option_id = static_cast<int32>(option_index);
1525         }
1526       } else {
1527         correct_option_id = poll->correct_option_id;
1528       }
1529 
1530       if (poll_result->voters_ < 0) {
1531         LOG(ERROR) << "Receive " << poll_result->voters_ << " voters for an option in " << poll_id;
1532         poll_result->voters_ = 0;
1533       }
1534       if (option.is_chosen && poll_result->voters_ == 0) {
1535         LOG(ERROR) << "Receive 0 voters for the chosen option in " << poll_id;
1536         poll_result->voters_ = 1;
1537       }
1538       if (poll_result->voters_ > poll->total_voter_count) {
1539         LOG(ERROR) << "Have only " << poll->total_voter_count << " poll voters, but there are " << poll_result->voters_
1540                    << " voters for an option in " << poll_id;
1541         poll->total_voter_count = poll_result->voters_;
1542       }
1543       auto max_voter_count = std::numeric_limits<int32>::max() / narrow_cast<int32>(poll->options.size()) - 2;
1544       if (poll_result->voters_ > max_voter_count) {
1545         LOG(ERROR) << "Have too much " << poll_result->voters_ << " poll voters for an option in " << poll_id;
1546         poll_result->voters_ = max_voter_count;
1547       }
1548       if (poll_result->voters_ != option.voter_count) {
1549         invalidate_poll_option_voters(poll, poll_id, option_index);
1550         option.voter_count = poll_result->voters_;
1551         is_changed = true;
1552       }
1553     }
1554   }
1555   if (!poll_results->results_.empty() && has_total_voters) {
1556     int32 max_total_voter_count = 0;
1557     for (auto &option : poll->options) {
1558       max_total_voter_count += option.voter_count;
1559     }
1560     if (poll->total_voter_count > max_total_voter_count && max_total_voter_count != 0) {
1561       LOG(ERROR) << "Have only " << max_total_voter_count << " total poll voters, but there are "
1562                  << poll->total_voter_count << " voters in " << poll_id;
1563       poll->total_voter_count = max_total_voter_count;
1564     }
1565   }
1566 
1567   auto entities =
1568       get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll");
1569   auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, true, false);
1570   if (status.is_error()) {
1571     if (!clean_input_string(poll_results->solution_)) {
1572       poll_results->solution_.clear();
1573     }
1574     entities = find_entities(poll_results->solution_, true, true);
1575   }
1576   FormattedText explanation{std::move(poll_results->solution_), std::move(entities)};
1577 
1578   if (poll->is_quiz) {
1579     if (poll->correct_option_id != correct_option_id) {
1580       if (correct_option_id == -1 && poll->correct_option_id != -1) {
1581         LOG(ERROR) << "Can't change correct option of " << poll_id << " from " << poll->correct_option_id << " to "
1582                    << correct_option_id;
1583       } else {
1584         poll->correct_option_id = correct_option_id;
1585         is_changed = true;
1586       }
1587     }
1588     if (poll->explanation != explanation && (!is_min || poll_server_is_closed)) {
1589       if (explanation.text.empty() && !poll->explanation.text.empty()) {
1590         LOG(ERROR) << "Can't change known " << poll_id << " explanation to empty";
1591       } else {
1592         poll->explanation = std::move(explanation);
1593         is_changed = true;
1594       }
1595     }
1596   } else {
1597     if (correct_option_id != -1) {
1598       LOG(ERROR) << "Receive correct option " << correct_option_id << " in non-quiz " << poll_id;
1599     }
1600     if (!explanation.text.empty()) {
1601       LOG(ERROR) << "Receive explanation " << explanation << " in non-quiz " << poll_id;
1602     }
1603   }
1604 
1605   vector<UserId> recent_voter_user_ids;
1606   if (!is_bot) {
1607     for (auto &user_id_int : poll_results->recent_voters_) {
1608       UserId user_id(user_id_int);
1609       if (user_id.is_valid()) {
1610         recent_voter_user_ids.push_back(user_id);
1611       } else {
1612         LOG(ERROR) << "Receive " << user_id << " as recent voter in " << poll_id;
1613       }
1614     }
1615   }
1616   if (poll->is_anonymous && !recent_voter_user_ids.empty()) {
1617     LOG(ERROR) << "Receive anonymous " << poll_id << " with recent voters " << recent_voter_user_ids;
1618     recent_voter_user_ids.clear();
1619   }
1620   if (recent_voter_user_ids != poll->recent_voter_user_ids) {
1621     poll->recent_voter_user_ids = std::move(recent_voter_user_ids);
1622     invalidate_poll_voters(poll, poll_id);
1623     is_changed = true;
1624   }
1625 
1626   if (!is_bot && !poll->is_closed) {
1627     auto timeout = get_polling_timeout();
1628     LOG(INFO) << "Schedule updating of " << poll_id << " in " << timeout;
1629     update_poll_timeout_.set_timeout_in(poll_id.get(), timeout);
1630   }
1631   if (is_changed) {
1632     notify_on_poll_update(poll_id);
1633   }
1634   if (is_changed || need_save_to_database) {
1635     save_poll(poll, poll_id);
1636   }
1637   if (need_update_poll && (is_changed || (poll->is_closed && being_closed_polls_.erase(poll_id) != 0))) {
1638     send_closure(G()->td(), &Td::send_update, td_api::make_object<td_api::updatePoll>(get_poll_object(poll_id, poll)));
1639   }
1640   return poll_id;
1641 }
1642 
on_get_poll_vote(PollId poll_id,UserId user_id,vector<BufferSlice> && options)1643 void PollManager::on_get_poll_vote(PollId poll_id, UserId user_id, vector<BufferSlice> &&options) {
1644   if (!poll_id.is_valid()) {
1645     LOG(ERROR) << "Receive updateMessagePollVote about invalid " << poll_id;
1646     return;
1647   }
1648   if (!user_id.is_valid()) {
1649     LOG(ERROR) << "Receive updateMessagePollVote from invalid " << user_id;
1650     return;
1651   }
1652   if (!td_->auth_manager_->is_bot()) {
1653     return;
1654   }
1655 
1656   vector<int32> option_ids;
1657   for (auto &option : options) {
1658     auto slice = option.as_slice();
1659     if (slice.size() != 1 || slice[0] < '0' || slice[0] > '9') {
1660       LOG(ERROR) << "Receive updateMessagePollVote with unexpected option \"" << format::escaped(slice) << '"';
1661       return;
1662     }
1663     option_ids.push_back(static_cast<int32>(slice[0] - '0'));
1664   }
1665 
1666   send_closure(G()->td(), &Td::send_update,
1667                td_api::make_object<td_api::updatePollAnswer>(
1668                    poll_id.get(), td_->contacts_manager_->get_user_id_object(user_id, "on_get_poll_vote"),
1669                    std::move(option_ids)));
1670 }
1671 
on_binlog_events(vector<BinlogEvent> && events)1672 void PollManager::on_binlog_events(vector<BinlogEvent> &&events) {
1673   for (auto &event : events) {
1674     switch (event.type_) {
1675       case LogEvent::HandlerType::SetPollAnswer: {
1676         if (!G()->parameters().use_message_db) {
1677           binlog_erase(G()->td_db()->get_binlog(), event.id_);
1678           break;
1679         }
1680 
1681         SetPollAnswerLogEvent log_event;
1682         log_event_parse(log_event, event.data_).ensure();
1683 
1684         auto dialog_id = log_event.full_message_id_.get_dialog_id();
1685 
1686         Dependencies dependencies;
1687         add_dialog_dependencies(dependencies, dialog_id);  // do not load the dialog itself
1688         resolve_dependencies_force(td_, dependencies, "SetPollAnswerLogEvent");
1689 
1690         do_set_poll_answer(log_event.poll_id_, log_event.full_message_id_, std::move(log_event.options_), event.id_,
1691                            Auto());
1692         break;
1693       }
1694       case LogEvent::HandlerType::StopPoll: {
1695         if (!G()->parameters().use_message_db) {
1696           binlog_erase(G()->td_db()->get_binlog(), event.id_);
1697           break;
1698         }
1699 
1700         StopPollLogEvent log_event;
1701         log_event_parse(log_event, event.data_).ensure();
1702 
1703         auto dialog_id = log_event.full_message_id_.get_dialog_id();
1704 
1705         Dependencies dependencies;
1706         add_dialog_dependencies(dependencies, dialog_id);  // do not load the dialog itself
1707         resolve_dependencies_force(td_, dependencies, "StopPollLogEvent");
1708 
1709         do_stop_poll(log_event.poll_id_, log_event.full_message_id_, nullptr, event.id_, Auto());
1710         break;
1711       }
1712       default:
1713         LOG(FATAL) << "Unsupported log event type " << event.type_;
1714     }
1715   }
1716 }
1717 
1718 }  // namespace td
1719