From 81babdf915ab7825e873e9a2c8ce123edc235969 Mon Sep 17 00:00:00 2001 From: Fela Ameghino Date: Thu, 2 Apr 2020 15:06:05 +0200 Subject: [PATCH] Implemented chat statistics (#981) --- td/generate/scheme/td_api.tl | 53 +++++++++++ td/generate/scheme/td_api.tlo | Bin 168604 -> 170828 bytes td/telegram/ContactsManager.cpp | 155 ++++++++++++++++++++++++++++++++ td/telegram/ContactsManager.h | 5 ++ td/telegram/Td.cpp | 14 +++ td/telegram/Td.h | 4 + 6 files changed, 231 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index c9826e1c..2ff508b2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2762,6 +2762,53 @@ inputStickerStatic sticker:InputFile emojis:string mask_position:maskPosition = inputStickerAnimated sticker:InputFile emojis:string = InputSticker; +//@description chatStatisticsDateRange +//@min_date min_date +//@max_date max_date +chatStatisticsDateRange min_date:int32 max_date:int32 = ChatStatisticsDateRange; + +//@description chatStatisticsAbsoluteValue +//@current current +//@previous previous +chatStatisticsAbsoluteValue current:double previous:double = ChatStatisticsAbsoluteValue; + +//@description chatStatisticsRelativeValue +//@part part +//@total total +chatStatisticsRelativeValue part:double total:double = ChatStatisticsRelativeValue; + + +//@class ChatStatisticsGraph @description ChatStatisticsGraph + +//@description chatStatisticsGraphAsync +//@token token +chatStatisticsGraphAsync token:string = ChatStatisticsGraph; + +//@description chatStatisticsGraphError +//@error_message error_message +chatStatisticsGraphError error_message:string = ChatStatisticsGraph; + +//@description chatStatisticsGraphData +//@json json +//@zoom_token zoom_token +chatStatisticsGraphData json:string zoom_token:string = ChatStatisticsGraph; + + +//@description chatStatisticsMessageInteractionCounters +//@message_id message_id +//@views views +//@forwards forwards +chatStatisticsMessageInteractionCounters message_id:int32 views:int32 forwards:int32 = ChatStatisticsMessageInteractionCounters; + + +//@description chatStatistics +//@period period @followers followers @views_per_post views_per_post @shares_per_post shares_per_post @enabled_notifications enabled_notifications +//@growth_graph growth_graph @followers_graph followers_graph @mute_graph mute_graph @top_hours_graph top_hours_graph @interactions_graph interactions_graph +//@iv_interactions_graph iv_interactions_graph @views_by_source_graph views_by_source_graph @new_followers_by_source_graph new_followers_by_source_graph +//@languages_graph languages_graph @recent_message_interactions recent_message_interactions +chatStatistics period:ChatStatisticsDateRange followers:ChatStatisticsAbsoluteValue views_per_post:ChatStatisticsAbsoluteValue shares_per_post:ChatStatisticsAbsoluteValue enabled_notifications:ChatStatisticsRelativeValue growth_graph:ChatStatisticsGraph followers_graph:ChatStatisticsGraph mute_graph:ChatStatisticsGraph top_hours_graph:ChatStatisticsGraph interactions_graph:ChatStatisticsGraph iv_interactions_graph:ChatStatisticsGraph views_by_source_graph:ChatStatisticsGraph new_followers_by_source_graph:ChatStatisticsGraph languages_graph:ChatStatisticsGraph recent_message_interactions:Vector = ChatStatistics; + + //@class Update @description Contains notifications about data changes //@description The user authorization state has changed @authorization_state New authorization state @@ -4140,6 +4187,12 @@ 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 Retrieve specified channel statistics @chat_id supergroup id @is_dark is dark +getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics; + +//@description Load async stats @token token @x zoom +getChatStatisticsGraph token:string x:int53 = ChatStatisticsGraph; + //@description Returns storage usage statistics. Can be called before authorization @chat_limit The maximum number of chats with the largest storage usage for which separate statistics should be returned. All other chats will be grouped in entries with chat_id == 0. If the chat info database is not used, the chat_limit is ignored and is always set to 0 getStorageStatistics chat_limit:int32 = StorageStatistics; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index ee23eb92f36c8b121ca6d0b84f848f421d076c9b..de83eff8c9215cae0668bdcf83959560a4dfeae5 100644 GIT binary patch delta 1619 zcma)6-%C?*6yLo~y_@3vH8T8a6&#b%DAF$uhx6UkB)7F6<#LH?<(2k05k`OIDUDTrEj;K z)c2X|F)9@j=o@QJu(PpXGY044qD*JUeO4|mjB0 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; + } +} + +static tl_object_ptr convert_stats_broadcast_stats(tl_object_ptr obj) { + CHECK(obj != nullptr); + + vector> recent_message_interactions; + recent_message_interactions.reserve(obj->recent_message_interactions_.size()); + for (auto& interaction : obj->recent_message_interactions_) { + recent_message_interactions.push_back( + make_tl_object(interaction->msg_id_, interaction->views_, interaction->forwards_)); + } + + return make_tl_object( + make_tl_object(obj->period_->min_date_, obj->period_->max_date_), + make_tl_object(obj->followers_->current_, obj->followers_->previous_), + make_tl_object(obj->views_per_post_->current_, obj->views_per_post_->previous_), + make_tl_object(obj->shares_per_post_->current_, obj->shares_per_post_->previous_), + make_tl_object(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->interactions_graph_)), + convert_stats_graph(std::move(obj->iv_interactions_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_)), + std::move(recent_message_interactions) + ); +} + +class GetBroadcastStatsQuery : 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(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(convert_stats_broadcast_stats(std::move(result))); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetBroadcastStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class LoadAyncGraphQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit LoadAyncGraphQuery(Promise> &&promise) : promise_(std::move(promise)) { + } + + void send(const string& token, int64 x) { + 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))); + } + + 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(convert_stats_graph(std::move(result))); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + bool ContactsManager::UserFull::is_expired() const { return expires_at < Time::now(); } @@ -5571,6 +5691,41 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow td_->create_handler(std::move(promise))->send(channel_id, slow_mode_delay); } +void ContactsManager::get_broadcast_stats(DialogId dialog_id, bool is_dark, + Promise> &&promise) { + if (!dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat specified")); + } + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (dialog_id.get_type() != DialogType::Channel) { + return promise.set_error(Status::Error(400, "Chat is not a channel")); + } + + auto channel_id = dialog_id.get_channel_id(); + const Channel *c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Chat info not found")); + } + if (c->is_megagroup) { + return promise.set_error(Status::Error(400, "Chat is not a channel")); + } + + const ChannelFull* c_full = get_channel_full(channel_id, "get_broadcast_stats"); + if (c_full == nullptr) { + return promise.set_error(Status::Error(400, "Chat info not found")); + } + + td_->create_handler(std::move(promise))->send(channel_id, is_dark, c_full->stats_dc_id); +} + +void ContactsManager::load_async_graph(const string& token, int64 x, Promise> &&promise) +{ + td_->create_handler(std::move(promise))->send(token, x); +} + void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_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 e9f4e07e..9cf91934 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -357,6 +357,11 @@ class ContactsManager : public Actor { void delete_channel(ChannelId channel_id, Promise &&promise); + void get_broadcast_stats(DialogId dialog_id, bool is_dark, + Promise> &&promise); + + void load_async_graph(const string& token, int64 x, Promise> &&promise); + void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise); void add_channel_participant(ChannelId channel_id, UserId user_id, Promise &&promise, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 4acd9b97..0b5e3db4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6494,6 +6494,20 @@ void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { std::move(promise)); } +void Td::on_request(uint64 id, td_api::getChatStatistics &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_broadcast_stats(DialogId(request.chat_id_), request.is_dark_, + std::move(promise)); +} + +void Td::on_request(uint64 id, td_api::getChatStatisticsGraph &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.token_); + contacts_manager_->load_async_graph(request.token_, request.x_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::setChatNotificationSettings &request) { CHECK_IS_USER(); answer_ok_query(id, messages_manager_->set_dialog_notification_settings(DialogId(request.chat_id_), diff --git a/td/telegram/Td.h b/td/telegram/Td.h index a3f641a7..43c59aa1 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -887,6 +887,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatStatisticsUrl &request); + void on_request(uint64 id, td_api::getChatStatistics &request); + + void on_request(uint64 id, td_api::getChatStatisticsGraph &request); + void on_request(uint64 id, const td_api::getMapThumbnailFile &request); void on_request(uint64 id, const td_api::getLocalizationTargetInfo &request);