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