From be63c469926960ece4b67786aaddacea3f7b5288 Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 19 Sep 2023 21:40:29 +0300 Subject: [PATCH] Move statistics related functions to StatisticsManager. --- td/telegram/ContactsManager.cpp | 351 ----------------------------- td/telegram/ContactsManager.h | 37 --- td/telegram/StatisticsManager.cpp | 360 ++++++++++++++++++++++++++++++ td/telegram/StatisticsManager.h | 46 ++++ td/telegram/Td.cpp | 10 +- 5 files changed, 411 insertions(+), 393 deletions(-) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 1def9aafd..95fc77b9d 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3816,281 +3816,6 @@ class GetStoriesMaxIdsQuery final : public Td::ResultHandler { } }; -tl_object_ptr ContactsManager::convert_date_range( - const tl_object_ptr &obj) { - return make_tl_object(obj->min_date_, obj->max_date_); -} - -tl_object_ptr ContactsManager::convert_stats_graph( - tl_object_ptr obj) { - CHECK(obj != nullptr); - - switch (obj->get_id()) { - case telegram_api::statsGraphAsync::ID: { - auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->token_)); - } - case telegram_api::statsGraphError::ID: { - auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->error_)); - } - case telegram_api::statsGraph::ID: { - auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->json_->data_), - std::move(graph->zoom_token_)); - } - default: - UNREACHABLE(); - return nullptr; - } -} - -double ContactsManager::get_percentage_value(double part, double total) { - if (total < 1e-6 && total > -1e-6) { - if (part < 1e-6 && part > -1e-6) { - return 0.0; - } - return 100.0; - } - if (part > 1e20) { - return 100.0; - } - return clamp(0.0, part / total * 100, 100.0); -} - -tl_object_ptr ContactsManager::convert_stats_absolute_value( - const tl_object_ptr &obj) { - return make_tl_object(obj->current_, obj->previous_, - get_percentage_value(obj->current_ - obj->previous_, obj->previous_)); -} - -tl_object_ptr ContactsManager::convert_megagroup_stats( - tl_object_ptr obj) { - CHECK(obj != nullptr); - - on_get_users(std::move(obj->users_), "convert_megagroup_stats"); - - // just in case - td::remove_if(obj->top_posters_, [](auto &obj) { - return !UserId(obj->user_id_).is_valid() || obj->messages_ < 0 || obj->avg_chars_ < 0; - }); - td::remove_if(obj->top_admins_, [](auto &obj) { - return !UserId(obj->user_id_).is_valid() || obj->deleted_ < 0 || obj->kicked_ < 0 || obj->banned_ < 0; - }); - td::remove_if(obj->top_inviters_, - [](auto &obj) { return !UserId(obj->user_id_).is_valid() || obj->invitations_ < 0; }); - - auto top_senders = - transform(std::move(obj->top_posters_), [this](tl_object_ptr &&top_poster) { - return td_api::make_object( - get_user_id_object(UserId(top_poster->user_id_), "get_top_senders"), top_poster->messages_, - top_poster->avg_chars_); - }); - auto top_administrators = - transform(std::move(obj->top_admins_), [this](tl_object_ptr &&top_admin) { - return td_api::make_object( - get_user_id_object(UserId(top_admin->user_id_), "get_top_administrators"), top_admin->deleted_, - top_admin->kicked_, top_admin->banned_); - }); - auto top_inviters = - transform(std::move(obj->top_inviters_), [this](tl_object_ptr &&top_inviter) { - return td_api::make_object( - get_user_id_object(UserId(top_inviter->user_id_), "get_top_inviters"), top_inviter->invitations_); - }); - - return make_tl_object( - convert_date_range(obj->period_), convert_stats_absolute_value(obj->members_), - convert_stats_absolute_value(obj->messages_), convert_stats_absolute_value(obj->viewers_), - convert_stats_absolute_value(obj->posters_), convert_stats_graph(std::move(obj->growth_graph_)), - convert_stats_graph(std::move(obj->members_graph_)), - convert_stats_graph(std::move(obj->new_members_by_source_graph_)), - convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->messages_graph_)), - convert_stats_graph(std::move(obj->actions_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), - convert_stats_graph(std::move(obj->weekdays_graph_)), std::move(top_senders), std::move(top_administrators), - std::move(top_inviters)); -} - -tl_object_ptr ContactsManager::convert_broadcast_stats( - tl_object_ptr obj) { - CHECK(obj != nullptr); - - auto recent_message_interactions = transform(std::move(obj->recent_message_interactions_), [](auto &&interaction) { - return make_tl_object( - MessageId(ServerMessageId(interaction->msg_id_)).get(), interaction->views_, interaction->forwards_); - }); - - return make_tl_object( - convert_date_range(obj->period_), convert_stats_absolute_value(obj->followers_), - convert_stats_absolute_value(obj->views_per_post_), convert_stats_absolute_value(obj->shares_per_post_), - get_percentage_value(obj->enabled_notifications_->part_, obj->enabled_notifications_->total_), - convert_stats_graph(std::move(obj->growth_graph_)), convert_stats_graph(std::move(obj->followers_graph_)), - convert_stats_graph(std::move(obj->mute_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), - convert_stats_graph(std::move(obj->views_by_source_graph_)), - convert_stats_graph(std::move(obj->new_followers_by_source_graph_)), - convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->interactions_graph_)), - convert_stats_graph(std::move(obj->iv_interactions_graph_)), std::move(recent_message_interactions)); -} - -class GetMegagroupStatsQuery final : public Td::ResultHandler { - Promise> promise_; - ChannelId channel_id_; - - public: - explicit GetMegagroupStatsQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id, bool is_dark, DcId dc_id) { - channel_id_ = channel_id; - - auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); - CHECK(input_channel != nullptr); - - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getMegagroupStats::DARK_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::stats_getMegagroupStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); - } - - void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - promise_.set_value(td_->contacts_manager_->convert_megagroup_stats(result_ptr.move_as_ok())); - } - - void on_error(Status status) final { - td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMegagroupStatsQuery"); - promise_.set_error(std::move(status)); - } -}; - -class GetBroadcastStatsQuery final : public Td::ResultHandler { - Promise> promise_; - ChannelId channel_id_; - - public: - explicit GetBroadcastStatsQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id, bool is_dark, DcId dc_id) { - channel_id_ = channel_id; - - auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); - CHECK(input_channel != nullptr); - - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getBroadcastStats::DARK_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); - } - - void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - auto result = ContactsManager::convert_broadcast_stats(result_ptr.move_as_ok()); - for (auto &info : result->recent_message_interactions_) { - td_->messages_manager_->on_update_message_interaction_info({DialogId(channel_id_), MessageId(info->message_id_)}, - info->view_count_, info->forward_count_, false, - nullptr); - } - promise_.set_value(std::move(result)); - } - - void on_error(Status status) final { - td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetBroadcastStatsQuery"); - promise_.set_error(std::move(status)); - } -}; - -tl_object_ptr ContactsManager::convert_message_stats( - tl_object_ptr obj) { - return make_tl_object(convert_stats_graph(std::move(obj->views_graph_))); -} - -class GetMessageStatsQuery final : public Td::ResultHandler { - Promise> promise_; - ChannelId channel_id_; - - public: - explicit GetMessageStatsQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id, MessageId message_id, bool is_dark, DcId dc_id) { - channel_id_ = channel_id; - - auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); - if (input_channel == nullptr) { - return promise_.set_error(Status::Error(400, "Supergroup not found")); - } - - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getMessageStats::DARK_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::stats_getMessageStats(flags, false /*ignored*/, std::move(input_channel), - message_id.get_server_message_id().get()), - {}, dc_id)); - } - - void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - promise_.set_value(td_->contacts_manager_->convert_message_stats(result_ptr.move_as_ok())); - } - - void on_error(Status status) final { - td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMessageStatsQuery"); - promise_.set_error(std::move(status)); - } -}; - -class LoadAsyncGraphQuery final : public Td::ResultHandler { - Promise> promise_; - - public: - explicit LoadAsyncGraphQuery(Promise> &&promise) - : promise_(std::move(promise)) { - } - - void send(const string &token, int64 x, DcId dc_id) { - int32 flags = 0; - if (x != 0) { - flags |= telegram_api::stats_loadAsyncGraph::X_MASK; - } - send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), {}, dc_id)); - } - - void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); - if (result_ptr.is_error()) { - return on_error(result_ptr.move_as_error()); - } - - auto result = result_ptr.move_as_ok(); - promise_.set_value(ContactsManager::convert_stats_graph(std::move(result))); - } - - void on_error(Status status) final { - promise_.set_error(std::move(status)); - } -}; - class ContactsManager::UploadProfilePhotoCallback final : public FileManager::UploadCallback { public: void on_upload_ok(FileId file_id, tl_object_ptr input_file) final { @@ -8475,32 +8200,6 @@ void ContactsManager::get_channel_statistics_dc_id_impl(ChannelId channel_id, bo promise.set_value(DcId(channel_full->stats_dc_id)); } -void ContactsManager::get_channel_statistics(DialogId dialog_id, bool is_dark, - Promise> &&promise) { - auto dc_id_promise = PromiseCreator::lambda( - [actor_id = actor_id(this), dialog_id, is_dark, promise = std::move(promise)](Result r_dc_id) mutable { - if (r_dc_id.is_error()) { - return promise.set_error(r_dc_id.move_as_error()); - } - send_closure(actor_id, &ContactsManager::send_get_channel_stats_query, r_dc_id.move_as_ok(), - dialog_id.get_channel_id(), is_dark, std::move(promise)); - }); - get_channel_statistics_dc_id(dialog_id, true, std::move(dc_id_promise)); -} - -void ContactsManager::send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - - const Channel *c = get_channel(channel_id); - CHECK(c != nullptr); - if (c->is_megagroup) { - td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); - } else { - td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); - } -} - bool ContactsManager::can_get_channel_message_statistics(DialogId dialog_id) const { if (dialog_id.get_type() != DialogType::Channel) { return false; @@ -8524,56 +8223,6 @@ bool ContactsManager::can_get_channel_message_statistics(DialogId dialog_id) con return c->status.is_administrator(); } -void ContactsManager::get_channel_message_statistics(FullMessageId full_message_id, bool is_dark, - Promise> &&promise) { - auto dc_id_promise = PromiseCreator::lambda([actor_id = actor_id(this), full_message_id, is_dark, - promise = std::move(promise)](Result r_dc_id) mutable { - if (r_dc_id.is_error()) { - return promise.set_error(r_dc_id.move_as_error()); - } - send_closure(actor_id, &ContactsManager::send_get_channel_message_stats_query, r_dc_id.move_as_ok(), - full_message_id, is_dark, std::move(promise)); - }); - get_channel_statistics_dc_id(full_message_id.get_dialog_id(), false, std::move(dc_id_promise)); -} - -void ContactsManager::send_get_channel_message_stats_query( - DcId dc_id, FullMessageId full_message_id, bool is_dark, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - - auto dialog_id = full_message_id.get_dialog_id(); - if (!td_->messages_manager_->have_message_force(full_message_id, "send_get_channel_message_stats_query")) { - return promise.set_error(Status::Error(400, "Message not found")); - } - if (!td_->messages_manager_->can_get_message_statistics(full_message_id)) { - return promise.set_error(Status::Error(400, "Message statistics is inaccessible")); - } - CHECK(dialog_id.get_type() == DialogType::Channel); - td_->create_handler(std::move(promise)) - ->send(dialog_id.get_channel_id(), full_message_id.get_message_id(), is_dark, dc_id); -} - -void ContactsManager::load_statistics_graph(DialogId dialog_id, string token, int64 x, - Promise> &&promise) { - auto dc_id_promise = PromiseCreator::lambda([actor_id = actor_id(this), token = std::move(token), x, - promise = std::move(promise)](Result r_dc_id) mutable { - if (r_dc_id.is_error()) { - return promise.set_error(r_dc_id.move_as_error()); - } - send_closure(actor_id, &ContactsManager::send_load_async_graph_query, r_dc_id.move_as_ok(), std::move(token), x, - std::move(promise)); - }); - get_channel_statistics_dc_id(dialog_id, false, std::move(dc_id_promise)); -} - -void ContactsManager::send_load_async_graph_query(DcId dc_id, string token, int64 x, - Promise> &&promise) { - TRY_STATUS_PROMISE(promise, G()->close_status()); - - td_->create_handler(std::move(promise))->send(token, x, dc_id); -} - void ContactsManager::report_channel_spam(ChannelId channel_id, const vector &message_ids, Promise &&promise) { auto c = get_channel(channel_id); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 5c6ec05a0..ac60bae79 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -477,17 +477,8 @@ class ContactsManager final : public Actor { void get_channel_statistics_dc_id(DialogId dialog_id, bool for_full_statistics, Promise &&promise); - void get_channel_statistics(DialogId dialog_id, bool is_dark, - Promise> &&promise); - bool can_get_channel_message_statistics(DialogId dialog_id) const; - void get_channel_message_statistics(FullMessageId full_message_id, bool is_dark, - Promise> &&promise); - - void load_statistics_graph(DialogId dialog_id, string token, int64 x, - Promise> &&promise); - struct CanTransferOwnershipResult { enum class Type : uint8 { Ok, PasswordNeeded, PasswordTooFresh, SessionTooFresh }; Type type = Type::Ok; @@ -729,25 +720,6 @@ class ContactsManager final : public Actor { void get_current_state(vector> &updates) const; - static tl_object_ptr convert_date_range( - const tl_object_ptr &obj); - - static tl_object_ptr convert_stats_graph(tl_object_ptr obj); - - static double get_percentage_value(double part, double total); - - static tl_object_ptr convert_stats_absolute_value( - const tl_object_ptr &obj); - - tl_object_ptr convert_megagroup_stats( - tl_object_ptr obj); - - static tl_object_ptr convert_broadcast_stats( - tl_object_ptr obj); - - static tl_object_ptr convert_message_stats( - tl_object_ptr obj); - private: struct User { string first_name; @@ -1894,15 +1866,6 @@ class ContactsManager final : public Actor { void get_channel_statistics_dc_id_impl(ChannelId channel_id, bool for_full_statistics, Promise &&promise); - void send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, - Promise> &&promise); - - void send_get_channel_message_stats_query(DcId dc_id, FullMessageId full_message_id, bool is_dark, - Promise> &&promise); - - void send_load_async_graph_query(DcId dc_id, string token, int64 x, - Promise> &&promise); - void on_get_support_user(UserId user_id, Promise> &&promise); static void on_user_online_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); diff --git a/td/telegram/StatisticsManager.cpp b/td/telegram/StatisticsManager.cpp index 946826484..f18d99cc2 100644 --- a/td/telegram/StatisticsManager.cpp +++ b/td/telegram/StatisticsManager.cpp @@ -6,8 +6,293 @@ // #include "td/telegram/StatisticsManager.h" +#include "td/telegram/ContactsManager.h" +#include "td/telegram/MessagesManager.h" +#include "td/telegram/Td.h" + +#include "td/utils/algorithm.h" +#include "td/utils/buffer.h" +#include "td/utils/misc.h" + namespace td { +td_api::object_ptr StatisticsManager::convert_date_range( + const telegram_api::object_ptr &obj) { + return td_api::make_object(obj->min_date_, obj->max_date_); +} + +td_api::object_ptr StatisticsManager::convert_stats_graph( + telegram_api::object_ptr obj) { + CHECK(obj != nullptr); + + switch (obj->get_id()) { + case telegram_api::statsGraphAsync::ID: { + auto graph = move_tl_object_as(obj); + return td_api::make_object(std::move(graph->token_)); + } + case telegram_api::statsGraphError::ID: { + auto graph = move_tl_object_as(obj); + return td_api::make_object(std::move(graph->error_)); + } + case telegram_api::statsGraph::ID: { + auto graph = move_tl_object_as(obj); + return td_api::make_object(std::move(graph->json_->data_), + std::move(graph->zoom_token_)); + } + default: + UNREACHABLE(); + return nullptr; + } +} + +double StatisticsManager::get_percentage_value(double part, double total) { + if (total < 1e-6 && total > -1e-6) { + if (part < 1e-6 && part > -1e-6) { + return 0.0; + } + return 100.0; + } + if (part > 1e20) { + return 100.0; + } + return clamp(0.0, part / total * 100, 100.0); +} + +td_api::object_ptr StatisticsManager::convert_stats_absolute_value( + const telegram_api::object_ptr &obj) { + return td_api::make_object( + obj->current_, obj->previous_, get_percentage_value(obj->current_ - obj->previous_, obj->previous_)); +} + +td_api::object_ptr StatisticsManager::convert_megagroup_stats( + telegram_api::object_ptr obj) { + CHECK(obj != nullptr); + + td_->contacts_manager_->on_get_users(std::move(obj->users_), "convert_megagroup_stats"); + + // just in case + td::remove_if(obj->top_posters_, [](auto &obj) { + return !UserId(obj->user_id_).is_valid() || obj->messages_ < 0 || obj->avg_chars_ < 0; + }); + td::remove_if(obj->top_admins_, [](auto &obj) { + return !UserId(obj->user_id_).is_valid() || obj->deleted_ < 0 || obj->kicked_ < 0 || obj->banned_ < 0; + }); + td::remove_if(obj->top_inviters_, + [](auto &obj) { return !UserId(obj->user_id_).is_valid() || obj->invitations_ < 0; }); + + auto top_senders = transform( + std::move(obj->top_posters_), [this](telegram_api::object_ptr &&top_poster) { + return td_api::make_object( + td_->contacts_manager_->get_user_id_object(UserId(top_poster->user_id_), "get_top_senders"), + top_poster->messages_, top_poster->avg_chars_); + }); + auto top_administrators = transform( + std::move(obj->top_admins_), [this](telegram_api::object_ptr &&top_admin) { + return td_api::make_object( + td_->contacts_manager_->get_user_id_object(UserId(top_admin->user_id_), "get_top_administrators"), + top_admin->deleted_, top_admin->kicked_, top_admin->banned_); + }); + auto top_inviters = + transform(std::move(obj->top_inviters_), + [this](telegram_api::object_ptr &&top_inviter) { + return td_api::make_object( + td_->contacts_manager_->get_user_id_object(UserId(top_inviter->user_id_), "get_top_inviters"), + top_inviter->invitations_); + }); + + return td_api::make_object( + convert_date_range(obj->period_), convert_stats_absolute_value(obj->members_), + convert_stats_absolute_value(obj->messages_), convert_stats_absolute_value(obj->viewers_), + convert_stats_absolute_value(obj->posters_), convert_stats_graph(std::move(obj->growth_graph_)), + convert_stats_graph(std::move(obj->members_graph_)), + convert_stats_graph(std::move(obj->new_members_by_source_graph_)), + convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->messages_graph_)), + convert_stats_graph(std::move(obj->actions_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), + convert_stats_graph(std::move(obj->weekdays_graph_)), std::move(top_senders), std::move(top_administrators), + std::move(top_inviters)); +} + +td_api::object_ptr StatisticsManager::convert_broadcast_stats( + telegram_api::object_ptr obj) { + CHECK(obj != nullptr); + + auto recent_message_interactions = transform(std::move(obj->recent_message_interactions_), [](auto &&interaction) { + return td_api::make_object( + MessageId(ServerMessageId(interaction->msg_id_)).get(), interaction->views_, interaction->forwards_); + }); + + return td_api::make_object( + convert_date_range(obj->period_), convert_stats_absolute_value(obj->followers_), + convert_stats_absolute_value(obj->views_per_post_), convert_stats_absolute_value(obj->shares_per_post_), + get_percentage_value(obj->enabled_notifications_->part_, obj->enabled_notifications_->total_), + convert_stats_graph(std::move(obj->growth_graph_)), convert_stats_graph(std::move(obj->followers_graph_)), + convert_stats_graph(std::move(obj->mute_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), + convert_stats_graph(std::move(obj->views_by_source_graph_)), + convert_stats_graph(std::move(obj->new_followers_by_source_graph_)), + convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->interactions_graph_)), + convert_stats_graph(std::move(obj->iv_interactions_graph_)), std::move(recent_message_interactions)); +} + +class GetMegagroupStatsQuery final : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetMegagroupStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, bool is_dark, DcId dc_id) { + channel_id_ = channel_id; + + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + + int32 flags = 0; + if (is_dark) { + flags |= telegram_api::stats_getMegagroupStats::DARK_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::stats_getMegagroupStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + promise_.set_value(td_->statistics_manager_->convert_megagroup_stats(result_ptr.move_as_ok())); + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMegagroupStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetBroadcastStatsQuery final : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetBroadcastStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, bool is_dark, DcId dc_id) { + channel_id_ = channel_id; + + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + + int32 flags = 0; + if (is_dark) { + flags |= telegram_api::stats_getBroadcastStats::DARK_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto result = StatisticsManager::convert_broadcast_stats(result_ptr.move_as_ok()); + for (auto &info : result->recent_message_interactions_) { + td_->messages_manager_->on_update_message_interaction_info({DialogId(channel_id_), MessageId(info->message_id_)}, + info->view_count_, info->forward_count_, false, + nullptr); + } + promise_.set_value(std::move(result)); + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetBroadcastStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +td_api::object_ptr StatisticsManager::convert_message_stats( + telegram_api::object_ptr obj) { + return td_api::make_object(convert_stats_graph(std::move(obj->views_graph_))); +} + +class GetMessageStatsQuery final : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetMessageStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, MessageId message_id, bool is_dark, DcId dc_id) { + channel_id_ = channel_id; + + auto input_channel = td_->contacts_manager_->get_input_channel(channel_id); + if (input_channel == nullptr) { + return promise_.set_error(Status::Error(400, "Supergroup not found")); + } + + int32 flags = 0; + if (is_dark) { + flags |= telegram_api::stats_getMessageStats::DARK_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::stats_getMessageStats(flags, false /*ignored*/, std::move(input_channel), + message_id.get_server_message_id().get()), + {}, dc_id)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + promise_.set_value(td_->statistics_manager_->convert_message_stats(result_ptr.move_as_ok())); + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMessageStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class LoadAsyncGraphQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit LoadAsyncGraphQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &token, int64 x, DcId dc_id) { + int32 flags = 0; + if (x != 0) { + flags |= telegram_api::stats_loadAsyncGraph::X_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), {}, dc_id)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + promise_.set_value(StatisticsManager::convert_stats_graph(std::move(result))); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + StatisticsManager::StatisticsManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { } @@ -15,4 +300,79 @@ void StatisticsManager::tear_down() { parent_.reset(); } +void StatisticsManager::get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), dialog_id, is_dark, promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &StatisticsManager::send_get_channel_stats_query, r_dc_id.move_as_ok(), + dialog_id.get_channel_id(), is_dark, std::move(promise)); + }); + td_->contacts_manager_->get_channel_statistics_dc_id(dialog_id, true, std::move(dc_id_promise)); +} + +void StatisticsManager::send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + if (td_->contacts_manager_->is_megagroup_channel(channel_id)) { + td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); + } else { + td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); + } +} + +void StatisticsManager::get_channel_message_statistics( + FullMessageId full_message_id, bool is_dark, Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda([actor_id = actor_id(this), full_message_id, is_dark, + promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &StatisticsManager::send_get_channel_message_stats_query, r_dc_id.move_as_ok(), + full_message_id, is_dark, std::move(promise)); + }); + td_->contacts_manager_->get_channel_statistics_dc_id(full_message_id.get_dialog_id(), false, + std::move(dc_id_promise)); +} + +void StatisticsManager::send_get_channel_message_stats_query( + DcId dc_id, FullMessageId full_message_id, bool is_dark, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto dialog_id = full_message_id.get_dialog_id(); + if (!td_->messages_manager_->have_message_force(full_message_id, "send_get_channel_message_stats_query")) { + return promise.set_error(Status::Error(400, "Message not found")); + } + if (!td_->messages_manager_->can_get_message_statistics(full_message_id)) { + return promise.set_error(Status::Error(400, "Message statistics is inaccessible")); + } + CHECK(dialog_id.get_type() == DialogType::Channel); + td_->create_handler(std::move(promise)) + ->send(dialog_id.get_channel_id(), full_message_id.get_message_id(), is_dark, dc_id); +} + +void StatisticsManager::load_statistics_graph(DialogId dialog_id, string token, int64 x, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda([actor_id = actor_id(this), token = std::move(token), x, + promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &StatisticsManager::send_load_async_graph_query, r_dc_id.move_as_ok(), std::move(token), x, + std::move(promise)); + }); + td_->contacts_manager_->get_channel_statistics_dc_id(dialog_id, false, std::move(dc_id_promise)); +} + +void StatisticsManager::send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + td_->create_handler(std::move(promise))->send(token, x, dc_id); +} + } // namespace td diff --git a/td/telegram/StatisticsManager.h b/td/telegram/StatisticsManager.h index ad34d1cc9..e9283e804 100644 --- a/td/telegram/StatisticsManager.h +++ b/td/telegram/StatisticsManager.h @@ -6,9 +6,17 @@ // #pragma once +#include "td/telegram/ChannelId.h" +#include "td/telegram/DialogId.h" +#include "td/telegram/FullMessageId.h" +#include "td/telegram/net/DcId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + #include "td/actor/actor.h" #include "td/utils/common.h" +#include "td/utils/Promise.h" namespace td { @@ -18,9 +26,47 @@ class StatisticsManager final : public Actor { public: StatisticsManager(Td *td, ActorShared<> parent); + void get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise); + + void get_channel_message_statistics(FullMessageId full_message_id, bool is_dark, + Promise> &&promise); + + void load_statistics_graph(DialogId dialog_id, string token, int64 x, + Promise> &&promise); + + static td_api::object_ptr convert_date_range( + const telegram_api::object_ptr &obj); + + static td_api::object_ptr convert_stats_graph( + telegram_api::object_ptr obj); + + static double get_percentage_value(double part, double total); + + static td_api::object_ptr convert_stats_absolute_value( + const telegram_api::object_ptr &obj); + + td_api::object_ptr convert_megagroup_stats( + telegram_api::object_ptr obj); + + static td_api::object_ptr convert_broadcast_stats( + telegram_api::object_ptr obj); + + static td_api::object_ptr convert_message_stats( + telegram_api::object_ptr obj); + private: void tear_down() final; + void send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise); + + void send_get_channel_message_stats_query(DcId dc_id, FullMessageId full_message_id, bool is_dark, + Promise> &&promise); + + void send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise); + Td *td_; ActorShared<> parent_; }; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 2f250d452..1c034aba1 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -8078,22 +8078,22 @@ void Td::on_request(uint64 id, const td_api::reportMessageReactions &request) { void Td::on_request(uint64 id, const td_api::getChatStatistics &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_channel_statistics(DialogId(request.chat_id_), request.is_dark_, std::move(promise)); + statistics_manager_->get_channel_statistics(DialogId(request.chat_id_), request.is_dark_, std::move(promise)); } void Td::on_request(uint64 id, const td_api::getMessageStatistics &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_channel_message_statistics({DialogId(request.chat_id_), MessageId(request.message_id_)}, - request.is_dark_, std::move(promise)); + statistics_manager_->get_channel_message_statistics({DialogId(request.chat_id_), MessageId(request.message_id_)}, + request.is_dark_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getStatisticalGraph &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.token_); CREATE_REQUEST_PROMISE(); - contacts_manager_->load_statistics_graph(DialogId(request.chat_id_), std::move(request.token_), request.x_, - std::move(promise)); + statistics_manager_->load_statistics_graph(DialogId(request.chat_id_), std::move(request.token_), request.x_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::setChatNotificationSettings &request) {