diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index e9bde941d..e93da322f 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7283,6 +7283,12 @@ openStory story_sender_user_id:int53 story_id:int32 = Ok; //@story_id The identifier of the story closeStory story_sender_user_id:int53 story_id:int32 = Ok; +//@description Returns viewers of a recent outgoing story. The method can be called if story.can_get_viewers == true +//@story_id Story identifier +//@offset_viewer A viewer from which to return next viewers; pass null to get results from the beginning +//@limit The maximum number of story viewers to return +getStoryViewers story_id:int32 offset_viewer:messageViewer limit:int32 = MessageViewers; + //@description Returns information about a bot that can be added to attachment menu @bot_user_id Bot's user identifier getAttachmentMenuBot bot_user_id:int53 = AttachmentMenuBot; diff --git a/td/telegram/MessageViewer.cpp b/td/telegram/MessageViewer.cpp index c6520e9ab..883c6f0d4 100644 --- a/td/telegram/MessageViewer.cpp +++ b/td/telegram/MessageViewer.cpp @@ -13,7 +13,7 @@ namespace td { MessageViewer::MessageViewer(telegram_api::object_ptr &&read_date) - : user_id_(read_date->user_id_), date_(read_date->date_) { + : MessageViewer(UserId(read_date->user_id_), read_date->date_) { } td_api::object_ptr MessageViewer::get_message_viewer_object( @@ -26,6 +26,17 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageViewer &vi return string_builder << '[' << viewer.user_id_ << " at " << viewer.date_ << ']'; } +MessageViewers::MessageViewers(vector> &&story_views) { + for (auto &story_view : story_views) { + UserId user_id(story_view->user_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive " << user_id << " as story viewer"; + continue; + } + message_viewers_.emplace_back(user_id, story_view->date_); + } +} + MessageViewers::MessageViewers(vector> &&read_dates) : message_viewers_( transform(std::move(read_dates), [](telegram_api::object_ptr &&read_date) { diff --git a/td/telegram/MessageViewer.h b/td/telegram/MessageViewer.h index fb071af1b..4eb2bce0f 100644 --- a/td/telegram/MessageViewer.h +++ b/td/telegram/MessageViewer.h @@ -26,6 +26,9 @@ class MessageViewer { public: explicit MessageViewer(telegram_api::object_ptr &&read_date); + MessageViewer(UserId user_id, int32 date) : user_id_(user_id), date_(td::max(date, static_cast(0))) { + } + UserId get_user_id() const { return user_id_; } @@ -38,6 +41,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageViewer &vi struct MessageViewers { vector message_viewers_; + explicit MessageViewers(vector> &&story_views); + explicit MessageViewers(vector> &&read_dates); td_api::object_ptr get_message_viewers_object(ContactsManager *contacts_manager) const; diff --git a/td/telegram/StoryInteractionInfo.h b/td/telegram/StoryInteractionInfo.h index db7258490..dc95c8f93 100644 --- a/td/telegram/StoryInteractionInfo.h +++ b/td/telegram/StoryInteractionInfo.h @@ -36,6 +36,14 @@ class StoryInteractionInfo { return view_count_ < 0; } + bool set_view_count(int32 view_count) { + if (view_count > view_count_) { + view_count = view_count_; + return true; + } + return false; + } + td_api::object_ptr get_story_interaction_info_object(Td *td) const; }; diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index 74ae42eb9..36877ce5f 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -16,6 +16,7 @@ #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/MessageViewer.h" #include "td/telegram/OptionManager.h" #include "td/telegram/StoryContent.h" #include "td/telegram/StoryContentType.h" @@ -162,6 +163,33 @@ class ReadStoriesQuery final : public Td::ResultHandler { } }; +class GetStoryViewsListQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetStoryViewsListQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(StoryId story_id, int32 offset_date, int64 offset_user_id, int32 limit) { + send_query(G()->net_query_creator().create( + telegram_api::stories_getStoryViewsList(story_id.get(), offset_date, offset_user_id, limit))); + } + + 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(result_ptr.move_as_ok()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class GetStoriesByIDQuery final : public Td::ResultHandler { Promise promise_; UserId user_id_; @@ -962,6 +990,66 @@ void StoryManager::read_stories_on_server(DialogId owner_dialog_id, StoryId stor td_->create_handler(get_erase_log_event_promise(log_event_id))->send(owner_dialog_id, story_id); } +void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageViewer *offset, int32 limit, + Promise> &&promise) { + DialogId owner_dialog_id(td_->contacts_manager_->get_my_id()); + StoryFullId story_full_id{owner_dialog_id, story_id}; + const Story *story = get_story(story_full_id); + if (story == nullptr) { + return promise.set_error(Status::Error(400, "Story not found")); + } + if (!story_id.is_server()) { + return promise.set_value(td_api::object_ptr()); + } + // TRY_STATUS_PROMISE(promise, can_get_story_viewers(story_full_id)); + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + int32 offset_date = 0; + int64 offset_user_id = 0; + if (offset != nullptr) { + offset_date = offset->view_date_; + offset_user_id = offset->user_id_; + } + + auto query_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), story_id, promise = std::move(promise)]( + Result> result) mutable { + send_closure(actor_id, &StoryManager::on_get_story_viewers, story_id, std::move(result), std::move(promise)); + }); + + td_->create_handler(std::move(query_promise)) + ->send(story_full_id.get_story_id(), offset_date, offset_user_id, limit); +} + +void StoryManager::on_get_story_viewers( + StoryId story_id, Result> r_view_list, + Promise> &&promise) { + G()->ignore_result_if_closing(r_view_list); + if (r_view_list.is_error()) { + return promise.set_error(r_view_list.move_as_error()); + } + auto view_list = r_view_list.move_as_ok(); + + DialogId owner_dialog_id(td_->contacts_manager_->get_my_id()); + CHECK(story_id.is_server()); + StoryFullId story_full_id{owner_dialog_id, story_id}; + Story *story = get_story_editable(story_full_id); + if (story == nullptr) { + return promise.set_value(td_api::object_ptr()); + } + + td_->contacts_manager_->on_get_users(std::move(view_list->users_), "on_get_story_viewers"); + + if (story->content_ != nullptr && story->interaction_info_.set_view_count(view_list->count_)) { + on_story_changed(story_full_id, story, true, true); + } + + MessageViewers story_viewers(std::move(view_list->views_)); + promise.set_value(story_viewers.get_message_viewers_object(td_->contacts_manager_.get())); +} + bool StoryManager::have_story(StoryFullId story_full_id) const { return get_story(story_full_id) != nullptr; } @@ -1542,12 +1630,11 @@ void StoryManager::on_get_story_views(const vector &story_ids, continue; } - bool is_changed = false; StoryInteractionInfo interaction_info(td_, std::move(story_views->views_[i])); CHECK(!interaction_info.is_empty()); if (story->interaction_info_ != interaction_info) { story->interaction_info_ = std::move(interaction_info); - on_story_changed(story_full_id, story, is_changed, false); + on_story_changed(story_full_id, story, true, true); } } } diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 08dad03dc..b5ed6351c 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -120,6 +120,9 @@ class StoryManager final : public Actor { void close_story(DialogId owner_dialog_id, StoryId story_id, Promise &&promise); + void get_story_viewers(StoryId story_id, const td_api::messageViewer *offset, int32 limit, + Promise> &&promise); + StoryId on_get_story(DialogId owner_dialog_id, telegram_api::object_ptr &&story_item_ptr); std::pair> on_get_stories(DialogId owner_dialog_id, vector &&expected_story_ids, @@ -262,6 +265,10 @@ class StoryManager final : public Actor { void on_synchronized_archive_all_stories(bool set_archive_all_stories, Result result); + void on_get_story_viewers(StoryId story_id, + Result> r_view_list, + Promise> &&promise); + std::shared_ptr upload_media_callback_; WaitFreeHashMap story_full_id_to_file_source_id_; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 762d254d8..f4e1b7ae8 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6457,6 +6457,13 @@ void Td::on_request(uint64 id, const td_api::closeStory &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getStoryViewers &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + story_manager_->get_story_viewers(StoryId(request.story_id_), request.offset_viewer_.get(), request.limit_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getAttachmentMenuBot &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index cd887f833..543d63563 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -1010,6 +1010,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::closeStory &request); + void on_request(uint64 id, const td_api::getStoryViewers &request); + void on_request(uint64 id, const td_api::getAttachmentMenuBot &request); void on_request(uint64 id, const td_api::toggleBotIsAddedToAttachmentMenu &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ee682699f..fd732b217 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4068,6 +4068,14 @@ class CliClient final : public Actor { StoryId story_id; get_args(args, story_sender_user_id, story_id); send_request(td_api::make_object(story_sender_user_id, story_id)); + } else if (op == "gsv") { + StoryId story_id; + string limit; + int32 offset_date; + UserId offset_user_id; + get_args(args, story_id, limit, offset_date, offset_user_id); + send_request(td_api::make_object( + story_id, td_api::make_object(offset_user_id, offset_date), as_limit(limit))); } else if (op == "gamb") { UserId user_id; get_args(args, user_id);