diff --git a/CMakeLists.txt b/CMakeLists.txt index 8275ea98c..bd44d982b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,6 +480,7 @@ set(TDLIB_SOURCE td/telegram/StoryInteractionInfo.cpp td/telegram/StoryManager.cpp td/telegram/StoryStealthMode.cpp + td/telegram/StoryViewer.cpp td/telegram/SuggestedAction.cpp td/telegram/Support.cpp td/telegram/Td.cpp @@ -785,6 +786,7 @@ set(TDLIB_SOURCE td/telegram/StoryListId.h td/telegram/StoryManager.h td/telegram/StoryStealthMode.h + td/telegram/StoryViewer.h td/telegram/SuggestedAction.h td/telegram/Support.h td/telegram/Td.h diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 1d9cd02d4..c8e69c9df 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4931,6 +4931,13 @@ messageLink link:string is_public:Bool = MessageLink; messageLinkInfo is_public:Bool chat_id:int53 message_thread_id:int53 message:message media_timestamp:int32 for_album:Bool = MessageLinkInfo; +//@description Represents a viewer of a story @user_id User identifier of the viewer @view_date Approximate point in time (Unix timestamp) when the story was viewed +storyViewer user_id:int53 view_date:int32 = StoryViewer; + +//@description Represents a list of story viewers @viewers List of story viewers +storyViewers viewers:vector = StoryViewers; + + //@description Describes position of a clickable rectangle area on a story media //@x_percentage The abscissa of the rectangle's center, as a percentage of the media width //@y_percentage The ordinate of the rectangle's center, as a percentage of the media height @@ -7473,7 +7480,7 @@ setStoryReaction story_sender_chat_id:int53 story_id:int32 reaction_type:Reactio //@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; +getStoryViewers story_id:int32 offset_viewer:storyViewer limit:int32 = StoryViewers; //@description Reports a story to the Telegram moderators //@story_sender_chat_id The identifier of the sender of the story to report diff --git a/td/telegram/MessageViewer.cpp b/td/telegram/MessageViewer.cpp index 4f70d04fd..35d725862 100644 --- a/td/telegram/MessageViewer.cpp +++ b/td/telegram/MessageViewer.cpp @@ -35,17 +35,6 @@ 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) { for (auto &read_date : read_dates) { message_viewers_.emplace_back(std::move(read_date)); @@ -57,41 +46,6 @@ MessageViewers::MessageViewers(vector MessageViewers::get_user_ids() const { return transform(message_viewers_, [](auto &viewer) { return viewer.get_user_id(); }); } diff --git a/td/telegram/MessageViewer.h b/td/telegram/MessageViewer.h index 6ea77c121..8b4cfb9f6 100644 --- a/td/telegram/MessageViewer.h +++ b/td/telegram/MessageViewer.h @@ -56,18 +56,8 @@ class MessageViewers { public: 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); - vector get_user_ids() const; td_api::object_ptr get_message_viewers_object(ContactsManager *contacts_manager) const; diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index 8853b67ea..46ce901c5 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -2321,8 +2321,8 @@ Status StoryManager::can_get_story_viewers(StoryFullId story_full_id, const Stor return Status::OK(); } -void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageViewer *offset, int32 limit, - Promise> &&promise) { +void StoryManager::get_story_viewers(StoryId story_id, const td_api::storyViewer *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); @@ -2333,7 +2333,7 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView return promise.set_error(Status::Error(400, "Parameter limit must be positive")); } if (can_get_story_viewers(story_full_id, story).is_error() || story->interaction_info_.get_view_count() == 0) { - return promise.set_value(td_api::make_object()); + return promise.set_value(td_api::make_object()); } int32 offset_date = 0; @@ -2342,7 +2342,7 @@ 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}; + StoryViewer offset_viewer{UserId(offset_user_id), offset_date}; auto &cached_viewers = cached_story_viewers_[story_full_id]; if (cached_viewers != nullptr && story->content_ != nullptr && @@ -2352,7 +2352,7 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView // can return the viewers // don't need to reget the viewers, because story->interaction_info_.get_view_count() is updated every 10 seconds td_->contacts_manager_->on_view_user_active_stories(result.get_user_ids()); - return promise.set_value(result.get_message_viewers_object(td_->contacts_manager_.get())); + return promise.set_value(result.get_story_viewers_object(td_->contacts_manager_.get())); } } @@ -2368,9 +2368,9 @@ void StoryManager::get_story_viewers(StoryId story_id, const td_api::messageView } void StoryManager::on_get_story_viewers( - StoryId story_id, MessageViewer offset, + StoryId story_id, StoryViewer offset, Result> r_view_list, - Promise> &&promise) { + 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()); @@ -2382,7 +2382,7 @@ void StoryManager::on_get_story_viewers( 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::make_object()); + return promise.set_value(td_api::make_object()); } td_->contacts_manager_->on_get_users(std::move(view_list->users_), "on_get_story_viewers"); @@ -2393,7 +2393,7 @@ void StoryManager::on_get_story_viewers( total_count = static_cast(view_list->views_.size()); } - MessageViewers story_viewers(std::move(view_list->views_)); + StoryViewers story_viewers(std::move(view_list->views_)); if (story->content_ != nullptr) { if (story->interaction_info_.set_view_count(view_list->count_)) { if (offset.is_empty()) { @@ -2414,7 +2414,7 @@ void StoryManager::on_get_story_viewers( } td_->contacts_manager_->on_view_user_active_stories(story_viewers.get_user_ids()); - promise.set_value(story_viewers.get_message_viewers_object(td_->contacts_manager_.get())); + promise.set_value(story_viewers.get_story_viewers_object(td_->contacts_manager_.get())); } void StoryManager::report_story(StoryFullId story_full_id, ReportReason &&reason, Promise &&promise) { diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 6cc53fbde..bd3ac3bda 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -13,7 +13,6 @@ #include "td/telegram/FullMessageId.h" #include "td/telegram/MediaArea.h" #include "td/telegram/MessageEntity.h" -#include "td/telegram/MessageViewer.h" #include "td/telegram/ReactionType.h" #include "td/telegram/StoryDb.h" #include "td/telegram/StoryFullId.h" @@ -21,6 +20,7 @@ #include "td/telegram/StoryInteractionInfo.h" #include "td/telegram/StoryListId.h" #include "td/telegram/StoryStealthMode.h" +#include "td/telegram/StoryViewer.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -159,7 +159,7 @@ class StoryManager final : public Actor { struct CachedStoryViewers { int32 total_count_ = -1; - MessageViewers viewers_; + StoryViewers viewers_; }; struct StoryList { @@ -249,8 +249,8 @@ class StoryManager final : public Actor { void set_story_reaction(StoryFullId story_full_id, ReactionType reaction_type, bool add_to_recent, Promise &&promise); - void get_story_viewers(StoryId story_id, const td_api::messageViewer *offset, int32 limit, - Promise> &&promise); + void get_story_viewers(StoryId story_id, const td_api::storyViewer *offset, int32 limit, + Promise> &&promise); void report_story(StoryFullId story_full_id, ReportReason &&reason, Promise &&promise); @@ -527,9 +527,9 @@ class StoryManager final : public Actor { void set_story_stealth_mode(StoryStealthMode stealth_mode); - void on_get_story_viewers(StoryId story_id, MessageViewer offset, + void on_get_story_viewers(StoryId story_id, StoryViewer offset, Result> r_view_list, - Promise> &&promise); + Promise> &&promise); void on_set_story_reaction(StoryFullId story_full_id, Result &&result, Promise &&promise); diff --git a/td/telegram/StoryViewer.cpp b/td/telegram/StoryViewer.cpp new file mode 100644 index 000000000..5473f2859 --- /dev/null +++ b/td/telegram/StoryViewer.cpp @@ -0,0 +1,95 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/StoryViewer.h" + +#include "td/telegram/ContactsManager.h" + +#include "td/utils/algorithm.h" +#include "td/utils/logging.h" + +namespace td { + +td_api::object_ptr StoryViewer::get_story_viewer_object(ContactsManager *contacts_manager) const { + return td_api::make_object( + contacts_manager->get_user_id_object(user_id_, "get_story_viewer_object"), date_); +} + +bool operator==(const StoryViewer &lhs, const StoryViewer &rhs) { + return lhs.user_id_ == rhs.user_id_ && lhs.date_ == rhs.date_; +} + +bool operator!=(const StoryViewer &lhs, const StoryViewer &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewer &viewer) { + return string_builder << '[' << viewer.user_id_ << " at " << viewer.date_ << ']'; +} + +StoryViewers::StoryViewers(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; + } + story_viewers_.emplace_back(user_id, story_view->date_); + } +} + +StoryViewers StoryViewers::get_sublist(const StoryViewer &offset, int32 limit) const { + StoryViewers result; + bool found = offset.is_empty(); + for (auto &story_viewer : story_viewers_) { + if (found) { + if (limit-- <= 0) { + break; + } + result.story_viewers_.push_back(story_viewer); + } else if (story_viewer == offset) { + found = true; + } + } + return result; +} + +void StoryViewers::add_sublist(const StoryViewer &offset, const StoryViewers &sublist) { + if (offset.is_empty()) { + if (story_viewers_.empty()) { + story_viewers_ = sublist.story_viewers_; + } else { + auto old_viewers = std::move(story_viewers_); + for (auto &viewer : sublist.story_viewers_) { + if (viewer == old_viewers[0]) { + append(story_viewers_, old_viewers); + return; + } + story_viewers_.push_back(viewer); + } + } + } else if (!story_viewers_.empty() && story_viewers_.back() == offset) { + append(story_viewers_, sublist.story_viewers_); + } +} + +vector StoryViewers::get_user_ids() const { + return transform(story_viewers_, [](auto &viewer) { return viewer.get_user_id(); }); +} + +td_api::object_ptr StoryViewers::get_story_viewers_object( + ContactsManager *contacts_manager) const { + return td_api::make_object( + transform(story_viewers_, [contacts_manager](const StoryViewer &story_viewer) { + return story_viewer.get_story_viewer_object(contacts_manager); + })); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewers &viewers) { + return string_builder << viewers.story_viewers_; +} + +} // namespace td diff --git a/td/telegram/StoryViewer.h b/td/telegram/StoryViewer.h new file mode 100644 index 000000000..b7144f853 --- /dev/null +++ b/td/telegram/StoryViewer.h @@ -0,0 +1,74 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class ContactsManager; + +class StoryViewer { + UserId user_id_; + int32 date_ = 0; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewer &viewer); + + friend bool operator==(const StoryViewer &lhs, const StoryViewer &rhs); + + public: + StoryViewer(UserId user_id, int32 date) : user_id_(user_id), date_(td::max(date, static_cast(0))) { + } + + UserId get_user_id() const { + return user_id_; + } + + bool is_empty() const { + return user_id_ == UserId() && date_ == 0; + } + + td_api::object_ptr get_story_viewer_object(ContactsManager *contacts_manager) const; +}; + +bool operator==(const StoryViewer &lhs, const StoryViewer &rhs); + +bool operator!=(const StoryViewer &lhs, const StoryViewer &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewer &viewer); + +class StoryViewers { + vector story_viewers_; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewers &viewers); + + public: + StoryViewers() = default; + + explicit StoryViewers(vector> &&story_views); + + bool is_empty() const { + return story_viewers_.empty(); + } + + StoryViewers get_sublist(const StoryViewer &offset, int32 limit) const; + + void add_sublist(const StoryViewer &offset, const StoryViewers &sublist); + + vector get_user_ids() const; + + td_api::object_ptr get_story_viewers_object(ContactsManager *contacts_manager) const; +}; + +StringBuilder &operator<<(StringBuilder &string_builder, const StoryViewers &viewers); + +} // namespace td diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index aeb5e8656..11ae55e02 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4155,7 +4155,7 @@ class CliClient final : public Actor { 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))); + story_id, td_api::make_object(offset_user_id, offset_date), as_limit(limit))); } else if (op == "rst") { ChatId story_sender_chat_id; StoryId story_id;