From cda21980b0888fd8d12ac91c3202bb3d76b433d3 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 17 Nov 2023 14:26:51 +0300 Subject: [PATCH] Add td_api::getStoryStatistics. --- td/generate/scheme/td_api.tl | 8 ++++ td/telegram/StatisticsManager.cpp | 77 +++++++++++++++++++++++++++++++ td/telegram/StatisticsManager.h | 7 +++ td/telegram/StoryManager.cpp | 16 +++++++ td/telegram/StoryManager.h | 4 ++ td/telegram/Td.cpp | 7 +++ td/telegram/Td.h | 2 + td/telegram/cli.cpp | 6 +++ 8 files changed, 127 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 0b3acebd7..8682eb195 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -5962,6 +5962,11 @@ chatStatisticsChannel period:dateRange member_count:statisticalValue mean_view_c //@message_reaction_graph A graph containing number of message reactions messageStatistics message_interaction_graph:StatisticalGraph message_reaction_graph:StatisticalGraph = MessageStatistics; +//@description A detailed statistics about a story +//@story_interaction_graph A graph containing number of story views and shares +//@story_reaction_graph A graph containing number of story reactions +storyStatistics story_interaction_graph:StatisticalGraph story_reaction_graph:StatisticalGraph = StoryStatistics; + //@description A point on a Cartesian plane @x The point's first coordinate @y The point's second coordinate point x:double y:double = Point; @@ -9079,6 +9084,9 @@ getMessageStatistics chat_id:int53 message_id:int53 is_dark:Bool = MessageStatis //@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit getMessagePublicForwards chat_id:int53 message_id:int53 offset:string limit:int32 = FoundMessages; +//@description Returns detailed statistics about a story. Can be used only if story.can_get_statistics == true @chat_id Chat identifier @story_id Story identifier @is_dark Pass true if a dark theme is used by the application +getStoryStatistics chat_id:int53 story_id:int32 is_dark:Bool = StoryStatistics; + //@description Loads an asynchronous or a zoomed in statistical graph @chat_id Chat identifier @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise getStatisticalGraph chat_id:int53 token:string x:int53 = StatisticalGraph; diff --git a/td/telegram/StatisticsManager.cpp b/td/telegram/StatisticsManager.cpp index 8e215acae..bc694c55c 100644 --- a/td/telegram/StatisticsManager.cpp +++ b/td/telegram/StatisticsManager.cpp @@ -12,6 +12,7 @@ #include "td/telegram/MessagesInfo.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/ServerMessageId.h" +#include "td/telegram/StoryManager.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -282,6 +283,52 @@ class GetMessageStatsQuery final : public Td::ResultHandler { } }; +static td_api::object_ptr convert_story_stats( + telegram_api::object_ptr obj) { + return td_api::make_object(convert_stats_graph(std::move(obj->views_graph_)), + convert_stats_graph(std::move(obj->reactions_by_emotion_graph_))); +} + +class GetStoryStatsQuery final : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetStoryStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, StoryId story_id, bool is_dark, DcId dc_id) { + channel_id_ = channel_id; + + auto input_peer = td_->messages_manager_->get_input_peer(DialogId(channel_id), AccessRights::Read); + if (input_peer == nullptr) { + return promise_.set_error(Status::Error(400, "Chat not found")); + } + + int32 flags = 0; + if (is_dark) { + flags |= telegram_api::stats_getStoryStats::DARK_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::stats_getStoryStats(flags, false /*ignored*/, std::move(input_peer), story_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(convert_story_stats(result_ptr.move_as_ok())); + } + + void on_error(Status status) final { + td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetStoryStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + class LoadAsyncGraphQuery final : public Td::ResultHandler { Promise> promise_; @@ -427,6 +474,36 @@ void StatisticsManager::send_get_channel_message_stats_query( ->send(dialog_id.get_channel_id(), message_full_id.get_message_id(), is_dark, dc_id); } +void StatisticsManager::get_channel_story_statistics(StoryFullId story_full_id, bool is_dark, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), story_full_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_story_stats_query, r_dc_id.move_as_ok(), + story_full_id, is_dark, std::move(promise)); + }); + td_->contacts_manager_->get_channel_statistics_dc_id(story_full_id.get_dialog_id(), false, std::move(dc_id_promise)); +} + +void StatisticsManager::send_get_channel_story_stats_query( + DcId dc_id, StoryFullId story_full_id, bool is_dark, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto dialog_id = story_full_id.get_dialog_id(); + if (!td_->story_manager_->have_story_force(story_full_id)) { + return promise.set_error(Status::Error(400, "Story not found")); + } + if (!td_->story_manager_->can_get_story_statistics(story_full_id)) { + return promise.set_error(Status::Error(400, "Story statistics is inaccessible")); + } + CHECK(dialog_id.get_type() == DialogType::Channel); + td_->create_handler(std::move(promise)) + ->send(dialog_id.get_channel_id(), story_full_id.get_story_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, diff --git a/td/telegram/StatisticsManager.h b/td/telegram/StatisticsManager.h index dd676c572..af6ae5d12 100644 --- a/td/telegram/StatisticsManager.h +++ b/td/telegram/StatisticsManager.h @@ -10,6 +10,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/MessageFullId.h" #include "td/telegram/net/DcId.h" +#include "td/telegram/StoryFullId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -32,6 +33,9 @@ class StatisticsManager final : public Actor { void get_channel_message_statistics(MessageFullId message_full_id, bool is_dark, Promise> &&promise); + void get_channel_story_statistics(StoryFullId message_full_id, bool is_dark, + Promise> &&promise); + void load_statistics_graph(DialogId dialog_id, string token, int64 x, Promise> &&promise); @@ -51,6 +55,9 @@ class StatisticsManager final : public Actor { void send_get_channel_message_stats_query(DcId dc_id, MessageFullId message_full_id, bool is_dark, Promise> &&promise); + void send_get_channel_story_stats_query(DcId dc_id, StoryFullId story_full_id, bool is_dark, + Promise> &&promise); + void send_load_async_graph_query(DcId dc_id, string token, int64 x, Promise> &&promise); diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index 748fe3c17..a670f0144 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -1711,6 +1711,22 @@ StoryManager::Story *StoryManager::on_get_story_from_database(StoryFullId story_ return result; } +bool StoryManager::can_get_story_statistics(StoryFullId story_full_id) { + return can_get_story_statistics(story_full_id.get_dialog_id(), + get_story_force(story_full_id, "can_get_story_statistics")); +} + +bool StoryManager::can_get_story_statistics(DialogId owner_dialog_id, const Story *story) const { + if (td_->auth_manager_->is_bot()) { + return false; + } + if (owner_dialog_id.get_type() != DialogType::Channel || + !td_->contacts_manager_->is_broadcast_channel(owner_dialog_id.get_channel_id())) { + return false; + } + return true; +} + const StoryManager::ActiveStories *StoryManager::get_active_stories(DialogId owner_dialog_id) const { return active_stories_.get_pointer(owner_dialog_id); } diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 4ed8e8c57..d8d5c3309 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -302,6 +302,8 @@ class StoryManager final : public Actor { bool have_story_force(StoryFullId story_full_id); + bool can_get_story_statistics(StoryFullId story_full_id); + bool is_inaccessible_story(StoryFullId story_full_id) const; int32 get_story_duration(StoryFullId story_full_id) const; @@ -368,6 +370,8 @@ class StoryManager final : public Actor { bool can_access_expired_story(DialogId owner_dialog_id, const Story *story) const; + bool can_get_story_statistics(DialogId owner_dialog_id, const Story *story) const; + bool can_get_story_view_count(DialogId owner_dialog_id); bool can_post_stories(DialogId owner_dialog_id) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 2ec483eae..df8ec79dc 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -8138,6 +8138,13 @@ void Td::on_request(uint64 id, const td_api::getMessageStatistics &request) { request.is_dark_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getStoryStatistics &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + statistics_manager_->get_channel_story_statistics({DialogId(request.chat_id_), StoryId(request.story_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_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 813b73fd9..c5f156654 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -1450,6 +1450,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::getMessageStatistics &request); + void on_request(uint64 id, const td_api::getStoryStatistics &request); + void on_request(uint64 id, td_api::getStatisticalGraph &request); void on_request(uint64 id, const td_api::getMapThumbnailFile &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index afc6a8392..8d024f69b 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -5990,6 +5990,12 @@ class CliClient final : public Actor { bool is_dark; get_args(args, chat_id, message_id, is_dark); send_request(td_api::make_object(chat_id, message_id, is_dark)); + } else if (op == "gsst") { + ChatId chat_id; + StoryId story_id; + bool is_dark; + get_args(args, chat_id, story_id, is_dark); + send_request(td_api::make_object(chat_id, story_id, is_dark)); } else if (op == "gstg") { ChatId chat_id; string token;