diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 2e128cf6f..31a2d1a36 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2908,10 +2908,49 @@ statisticsGraphError error_message:string = StatisticsGraph; //@message_id Message identifier //@view_count Number of times the message was viewed //@forward_count Number of times the message was forwarded -chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionCounters; +chatStatisticsMessageInteractionInfo message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionInfo; + +//@description Contains statistics about messages sent by a user +//@user_id User identifier +//@sent_message_count Number of sent messages +//@average_character_count Average number of characters in sent messages +chatStatisticsMessageSenderInfo user_id:int32 sent_message_count:int32 average_character_count:int32 = ChatStatisticsMessageSenderInfo; + +//@description Contains statistics about administrator actions done by a user +//@user_id Administrator user identifier +//@deleted_message_count Number of messages deleted by the administrator +//@banned_user_count Number of users banned by the administrator +//@restricted_user_count Number of users restricted by the administrator +chatStatisticsAdministratorActionsInfo user_id:int32 deleted_message_count:int32 banned_user_count:int32 restricted_user_count:int32 = ChatStatisticsAdministratorActionsInfo; + +//@description Contains statistics about number of new members invited by a user +//@user_id User identifier +//@added_member_count Number of new members invited by the user +chatStatisticsInviterInfo user_id:int32 added_member_count:int32 = ChatStatisticsInviterInfo; -//@description A detailed statistics about a chat +//@class ChatStatistics @description Contains a detailed statistics about a chat + +//@description A detailed statistics about a supergroup chat +//@period A period to which the statistics applies +//@member_count Number of members in the chat +//@message_count Number of messages sent to the chat +//@viewer_count Number of users who viewed messages in the chat +//@sender_count Number of users who sent messages to the chat +//@member_count_graph A graph containing number of members in the chat +//@join_graph A graph containing number of members joined and left the chat +//@join_by_source_graph A graph containing number of new member joins per source +//@language_graph A graph containing distribution of active users per language +//@message_content_graph A graph containing distribution of sent messages by content type +//@action_graph A graph containing number of different actions in the chat +//@day_graph A graph containing distribution of message views per hour +//@week_graph A graph containing distribution of message views per day of week +//@top_senders List of users sent most messages in the last week +//@top_administrators List of most active administrators in the last week +//@top_inviters List of most active inviters of new members in the last week +chatStatisticsSupergroup period:dateRange member_count:statisticsValue message_count:statisticsValue viewer_count:statisticsValue sender_count:statisticsValue member_count_graph:StatisticsGraph join_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_content_graph:StatisticsGraph action_graph:StatisticsGraph day_graph:StatisticsGraph week_graph:StatisticsGraph top_senders:vector top_administrators:vector top_inviters:vector = ChatStatistics; + +//@description A detailed statistics about a channel chat //@period A period to which the statistics applies //@member_count Number of members in the chat //@mean_view_count Mean number of times the recently sent messages was viewed @@ -2927,7 +2966,7 @@ chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forwa //@message_interaction_graph A graph containing number of chat message views and shares //@instant_view_interaction_graph A graph containing number of views of associated with the chat instant views //@recent_message_interactions Detailed statistics about number of views and shares of recently sent messages -chatStatistics period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector = ChatStatistics; +chatStatisticsChannel period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector = ChatStatistics; //@class Update @description Contains notifications about data changes @@ -4342,7 +4381,7 @@ reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; //@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics is disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl; -//@description Returns detailed statistics about a chat. Currently this method can be used only for channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app +//@description Returns detailed statistics about a chat. Currently this method can be used only for supergroups and channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics; //@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifier @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 5407ed5a5..34e305a25 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 2b445c86e..93cf64a30 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2692,6 +2692,11 @@ class GetSupportUserQuery : 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); @@ -2734,19 +2739,61 @@ tl_object_ptr ContactsManager::convert_stats_absolute_v get_percentage_value(obj->current_ - obj->previous_, obj->previous_)); } -tl_object_ptr ContactsManager::convert_broadcast_stats( +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](auto &&top_poster) { + return td_api::make_object( + this->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](auto &&top_admin) { + return td_api::make_object( + this->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](auto &&top_inviter) { + return td_api::make_object( + this->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( + return make_tl_object( MessageId(ServerMessageId(interaction->msg_id_)).get(), interaction->views_, interaction->forwards_); }); - return make_tl_object( - make_tl_object(obj->period_->min_date_, obj->period_->max_date_), - convert_stats_absolute_value(obj->followers_), convert_stats_absolute_value(obj->views_per_post_), - convert_stats_absolute_value(obj->shares_per_post_), + 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_)), @@ -2756,12 +2803,51 @@ tl_object_ptr ContactsManager::convert_broadcast_stats( convert_stats_graph(std::move(obj->iv_interactions_graph_)), std::move(recent_message_interactions)); } -class GetBroadcastStatsQuery : public Td::ResultHandler { - Promise> promise_; +class GetMegagroupStatsQuery : public Td::ResultHandler { + Promise> promise_; ChannelId channel_id_; public: - explicit GetBroadcastStatsQuery(Promise> &&promise) + 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(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + promise_.set_value(td->contacts_manager_->convert_megagroup_stats(std::move(result))); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMegagroupStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class GetBroadcastStatsQuery : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetBroadcastStatsQuery(Promise> &&promise) : promise_(std::move(promise)) { } @@ -5867,9 +5953,6 @@ void ContactsManager::get_channel_statistics_dc_id(DialogId dialog_id, Promiseis_megagroup) { - return promise.set_error(Status::Error(400, "Chat is not a channel")); - } auto channel_full = get_channel_full_force(channel_id, "get_channel_statistics_dc_id"); if (channel_full == nullptr || !channel_full->stats_dc_id.is_exact()) { @@ -5902,25 +5985,30 @@ void ContactsManager::get_channel_statistics_dc_id_impl(ChannelId channel_id, Pr } void ContactsManager::get_channel_statistics(DialogId dialog_id, bool is_dark, - Promise> &&promise) { + 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_broadcast_stats_query, r_dc_id.move_as_ok(), + 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, std::move(dc_id_promise)); } -void ContactsManager::send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, - Promise> &&promise) { +void ContactsManager::send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } - - td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); + 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); + } } void ContactsManager::load_statistics_graph(DialogId dialog_id, const string &token, int64 x, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index dec8dc23f..352c90445 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -361,7 +361,7 @@ class ContactsManager : public Actor { void delete_channel(ChannelId channel_id, Promise &&promise); void get_channel_statistics(DialogId dialog_id, bool is_dark, - Promise> &&promise); + Promise> &&promise); void load_statistics_graph(DialogId dialog_id, const string &token, int64 x, Promise> &&promise); @@ -543,6 +543,9 @@ class ContactsManager : 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 new_value, double old_value); @@ -550,7 +553,9 @@ class ContactsManager : public Actor { static tl_object_ptr convert_stats_absolute_value( const tl_object_ptr &obj); - static tl_object_ptr convert_broadcast_stats( + tl_object_ptr convert_megagroup_stats(tl_object_ptr obj); + + static tl_object_ptr convert_broadcast_stats( tl_object_ptr obj); private: @@ -1393,8 +1398,8 @@ class ContactsManager : public Actor { void get_channel_statistics_dc_id_impl(ChannelId channel_id, Promise &&promise); - void send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, - Promise> &&promise); + void send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise); void send_load_async_graph_query(DcId dc_id, string token, int64 x, Promise> &&promise);