// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/telegram/FullMessageId.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/PollId.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" #include "td/actor/MultiTimeout.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/FlatHashMap.h" #include "td/utils/FlatHashSet.h" #include "td/utils/Promise.h" #include "td/utils/Status.h" #include "td/utils/WaitFreeHashMap.h" #include "td/utils/WaitFreeHashSet.h" #include namespace td { struct BinlogEvent; class Td; class PollManager final : public Actor { public: PollManager(Td *td, ActorShared<> parent); PollManager(const PollManager &) = delete; PollManager &operator=(const PollManager &) = delete; PollManager(PollManager &&) = delete; PollManager &operator=(PollManager &&) = delete; ~PollManager() final; static bool is_local_poll_id(PollId poll_id); PollId create_poll(string &&question, vector &&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); void register_poll(PollId poll_id, FullMessageId full_message_id, const char *source); void unregister_poll(PollId poll_id, FullMessageId full_message_id, const char *source); bool get_poll_is_closed(PollId poll_id) const; bool get_poll_is_anonymous(PollId poll_id) const; string get_poll_search_text(PollId poll_id) const; void set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector &&option_ids, Promise &&promise); void get_poll_voters(PollId poll_id, FullMessageId full_message_id, int32 option_id, int32 offset, int32 limit, Promise>> &&promise); void stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr &&reply_markup, Promise &&promise); void stop_local_poll(PollId poll_id); PollId dup_poll(PollId poll_id); bool has_input_media(PollId poll_id) const; tl_object_ptr get_input_media(PollId poll_id) const; PollId on_get_poll(PollId poll_id, tl_object_ptr &&poll_server, tl_object_ptr &&poll_results, const char *source); void on_get_poll_vote(PollId poll_id, UserId user_id, vector &&options); td_api::object_ptr get_poll_object(PollId poll_id) const; void on_binlog_events(vector &&events); static vector get_vote_percentage(const vector &voter_counts, int32 total_voter_count); template void store_poll(PollId poll_id, StorerT &storer) const; template PollId parse_poll(ParserT &parser); private: struct PollOption { string text_; string data_; int32 voter_count_ = 0; bool is_chosen_ = false; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; struct Poll { string question; vector options; vector recent_voter_user_ids; FormattedText explanation; int32 total_voter_count = 0; int32 correct_option_id = -1; int32 open_period = 0; int32 close_date = 0; bool is_anonymous = true; bool allow_multiple_answers = false; bool is_quiz = false; bool is_closed = false; bool is_updated_after_close = false; mutable bool was_saved = false; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; struct PollOptionVoters { vector voter_user_ids_; string next_offset_; vector>>> pending_queries_; bool was_invalidated_ = false; // the list needs to be invalidated when voters are changed }; static constexpr int32 MAX_GET_POLL_VOTERS = 50; // server side limit static constexpr int32 UNLOAD_POLL_DELAY = 600; // some reasonable value class SetPollAnswerLogEvent; class StopPollLogEvent; void start_up() final; void tear_down() final; static void on_update_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int); static void on_close_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int); static void on_unload_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int); static td_api::object_ptr get_poll_option_object(const PollOption &poll_option); static telegram_api::object_ptr get_input_poll_option(const PollOption &poll_option); static vector get_poll_options(vector> &&poll_options); bool have_poll(PollId poll_id) const; bool have_poll_force(PollId poll_id); const Poll *get_poll(PollId poll_id) const; const Poll *get_poll(PollId poll_id); Poll *get_poll_editable(PollId poll_id); bool can_unload_poll(PollId poll_id); void schedule_poll_unload(PollId poll_id); void notify_on_poll_update(PollId poll_id); static string get_poll_database_key(PollId poll_id); static void save_poll(const Poll *poll, PollId poll_id); void on_load_poll_from_database(PollId poll_id, string value); double get_polling_timeout() const; void on_update_poll_timeout(PollId poll_id); void on_close_poll_timeout(PollId poll_id); void on_unload_poll_timeout(PollId poll_id); void on_online(); Poll *get_poll_force(PollId poll_id); td_api::object_ptr get_poll_object(PollId poll_id, const Poll *poll) const; void on_get_poll_results(PollId poll_id, uint64 generation, Result> result); void do_set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector &&options, uint64 log_event_id, Promise &&promise); void on_set_poll_answer(PollId poll_id, uint64 generation, Result> &&result); void on_set_poll_answer_finished(PollId poll_id, Result &&result, vector> &&promises); void invalidate_poll_voters(const Poll *poll, PollId poll_id); void invalidate_poll_option_voters(const Poll *poll, PollId poll_id, size_t option_index); PollOptionVoters &get_poll_option_voters(const Poll *poll, PollId poll_id, int32 option_id); void on_get_poll_voters(PollId poll_id, int32 option_id, string offset, int32 limit, Result> &&result); void do_stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr &&reply_markup, uint64 log_event_id, Promise &&promise); void on_stop_poll_finished(PollId poll_id, FullMessageId full_message_id, uint64 log_event_id, Result &&result, Promise &&promise); void forget_local_poll(PollId poll_id); MultiTimeout update_poll_timeout_{"UpdatePollTimeout"}; MultiTimeout close_poll_timeout_{"ClosePollTimeout"}; MultiTimeout unload_poll_timeout_{"UnloadPollTimeout"}; Td *td_; ActorShared<> parent_; WaitFreeHashMap, PollIdHash> polls_; WaitFreeHashMap, PollIdHash> server_poll_messages_; WaitFreeHashMap, PollIdHash> other_poll_messages_; struct PendingPollAnswer { vector options_; vector> promises_; uint64 generation_ = 0; uint64 log_event_id_ = 0; NetQueryRef query_ref_; }; FlatHashMap pending_answers_; FlatHashMap, PollIdHash> poll_voters_; int64 current_local_poll_id_ = 0; uint64 current_generation_ = 0; FlatHashSet loaded_from_database_polls_; FlatHashSet being_closed_polls_; }; } // namespace td