From 4e2e6f6e6148ad6821653676b1834d159daa0c5d Mon Sep 17 00:00:00 2001 From: levlam Date: Tue, 20 Jun 2023 15:54:47 +0300 Subject: [PATCH] Cache received story viewers. --- td/generate/scheme/td_api.tl | 4 ++- td/telegram/MessageViewer.cpp | 21 +++++++++++++- td/telegram/MessageViewer.h | 8 ++++++ td/telegram/StoryInteractionInfo.h | 4 +++ td/telegram/StoryManager.cpp | 44 +++++++++++++++++++++++++----- td/telegram/StoryManager.h | 10 ++++++- 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 7d6d9324a..424709eb2 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -7284,10 +7284,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 +//@description Returns viewers of a recent outgoing story. The method can be called if story.can_get_viewers == true. The views are returned in a reverse chronological order (i.e., in order of decreasing view_date) +//-For optimal performance, the number of returned stories is chosen by TDLib //@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 +//-For optimal performance, the number of returned stories is chosen by TDLib and can be smaller than the specified limit getStoryViewers story_id:int32 offset_viewer:messageViewer limit:int32 = MessageViewers; diff --git a/td/telegram/MessageViewer.cpp b/td/telegram/MessageViewer.cpp index c0e234a49..3d0e877c7 100644 --- a/td/telegram/MessageViewer.cpp +++ b/td/telegram/MessageViewer.cpp @@ -53,7 +53,7 @@ MessageViewers::MessageViewers(vector>()}; + MessageViewers result; bool found = offset.is_empty(); for (auto &message_viewer : message_viewers_) { if (found) { @@ -68,6 +68,25 @@ MessageViewers MessageViewers::get_sublist(const MessageViewer &offset, int32 li return result; } +void MessageViewers::add_sublist(const MessageViewer &offset, const MessageViewers &sublist) { + if (offset.is_empty()) { + if (message_viewers_.empty()) { + message_viewers_ = sublist.message_viewers_; + } else { + auto old_viewers = std::move(message_viewers_); + for (auto &viewer : sublist.message_viewers_) { + if (viewer == old_viewers[0]) { + append(message_viewers_, old_viewers); + return; + } + message_viewers_.push_back(viewer); + } + } + } else if (!message_viewers_.empty() && message_viewers_.back() == offset) { + append(message_viewers_, sublist.message_viewers_); + } +} + td_api::object_ptr MessageViewers::get_message_viewers_object( ContactsManager *contacts_manager) const { return td_api::make_object( diff --git a/td/telegram/MessageViewer.h b/td/telegram/MessageViewer.h index 9d4507c9d..a6046cd05 100644 --- a/td/telegram/MessageViewer.h +++ b/td/telegram/MessageViewer.h @@ -51,12 +51,20 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageViewer &vi struct MessageViewers { vector message_viewers_; + MessageViewers() = default; + explicit MessageViewers(vector> &&story_views); explicit MessageViewers(vector> &&read_dates); + bool is_empty() const { + return message_viewers_.empty(); + } + MessageViewers get_sublist(const MessageViewer &offset, int32 limit) const; + void add_sublist(const MessageViewer &offset, const MessageViewers &sublist); + 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 dc95c8f93..e7a73b7d9 100644 --- a/td/telegram/StoryInteractionInfo.h +++ b/td/telegram/StoryInteractionInfo.h @@ -44,6 +44,10 @@ class StoryInteractionInfo { return false; } + int32 get_view_count() const { + return view_count_; + } + 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 8a75f6f04..21d6fa4c6 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -16,7 +16,6 @@ #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" @@ -695,6 +694,7 @@ void StoryManager::on_story_can_get_viewers_timeout(int64 story_global_id) { send_closure(G()->td(), &Td::send_update, td_api::make_object(get_story_object(story_full_id, story))); } + cached_story_viewers_.erase(story_full_id); } bool StoryManager::is_story_owned(DialogId owner_dialog_id) const { @@ -1100,7 +1100,7 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView if (limit <= 0) { return promise.set_error(Status::Error(400, "Parameter limit must be positive")); } - if (can_get_story_viewers(story_full_id, story).is_error()) { + if (can_get_story_viewers(story_full_id, story).is_error() || story->interaction_info_.get_view_count() == 0) { return promise.set_value(td_api::object_ptr()); } @@ -1110,11 +1110,22 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView offset_date = offset->view_date_; offset_user_id = offset->user_id_; } + MessageViewer offset_viewer{UserId(offset_user_id), offset_date}; + + auto &cached_viewers = cached_story_viewers_[story_full_id]; + if (cached_viewers != nullptr && story->content_ != nullptr && + (cached_viewers->total_count_ == story->interaction_info_.get_view_count() || !offset_viewer.is_empty())) { + auto result = cached_viewers->viewers_.get_sublist(offset_viewer, limit); + if (!result.is_empty()) { + return promise.set_value(result.get_message_viewers_object(td_->contacts_manager_.get())); + } + } auto query_promise = PromiseCreator::lambda( - [actor_id = actor_id(this), story_id, promise = std::move(promise)]( + [actor_id = actor_id(this), story_id, offset_viewer, promise = std::move(promise)]( Result> result) mutable { - send_closure(actor_id, &StoryManager::on_get_story_viewers, story_id, std::move(result), std::move(promise)); + send_closure(actor_id, &StoryManager::on_get_story_viewers, story_id, offset_viewer, std::move(result), + std::move(promise)); }); td_->create_handler(std::move(query_promise)) @@ -1122,7 +1133,8 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView } void StoryManager::on_get_story_viewers( - StoryId story_id, Result> r_view_list, + StoryId story_id, MessageViewer offset, + Result> r_view_list, Promise> &&promise) { G()->ignore_result_if_closing(r_view_list); if (r_view_list.is_error()) { @@ -1140,11 +1152,29 @@ void StoryManager::on_get_story_viewers( 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); + auto total_count = view_list->count_; + if (total_count < 0 || static_cast(total_count) < view_list->views_.size()) { + LOG(ERROR) << "Receive total_count = " << total_count << " and " << view_list->views_.size() << " story viewers"; + total_count = static_cast(view_list->views_.size()); } MessageViewers story_viewers(std::move(view_list->views_)); + if (story->content_ != nullptr) { + if (story->interaction_info_.set_view_count(view_list->count_)) { + on_story_changed(story_full_id, story, true, true); + } + auto &cached_viewers = cached_story_viewers_[story_full_id]; + if (cached_viewers == nullptr) { + cached_viewers = make_unique(); + } + if (total_count < cached_viewers->total_count_) { + LOG(ERROR) << "Total viewer count decreased from " << cached_viewers->total_count_ << " to " << total_count; + } else { + cached_viewers->total_count_ = total_count; + } + cached_viewers->viewers_.add_sublist(offset, story_viewers); + } + promise.set_value(story_viewers.get_message_viewers_object(td_->contacts_manager_.get())); } diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 74ba6246e..5e0f3c3e3 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -11,6 +11,7 @@ #include "td/telegram/files/FileSourceId.h" #include "td/telegram/FullMessageId.h" #include "td/telegram/MessageEntity.h" +#include "td/telegram/MessageViewer.h" #include "td/telegram/StoryFullId.h" #include "td/telegram/StoryId.h" #include "td/telegram/StoryInteractionInfo.h" @@ -82,6 +83,11 @@ class StoryManager final : public Actor { vector story_ids_; }; + struct CachedStoryViewers { + int32 total_count_ = -1; + MessageViewers viewers_; + }; + public: StoryManager(Td *td, ActorShared<> parent); StoryManager(const StoryManager &) = delete; @@ -284,7 +290,7 @@ 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, + void on_get_story_viewers(StoryId story_id, MessageViewer offset, Result> r_view_list, Promise> &&promise); @@ -312,6 +318,8 @@ class StoryManager final : public Actor { FlatHashMap opened_owned_stories_; + FlatHashMap, StoryFullIdHash> cached_story_viewers_; + uint32 send_story_count_ = 0; int64 max_story_global_id_ = 0;