diff --git a/CMakeLists.txt b/CMakeLists.txt index b5d4fbe33..bc4bcbacc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -485,6 +485,7 @@ set(TDLIB_SOURCE td/telegram/StoryContent.cpp td/telegram/StoryContentType.cpp td/telegram/StoryDb.cpp + td/telegram/StoryForwardInfo.cpp td/telegram/StoryInteractionInfo.cpp td/telegram/StoryManager.cpp td/telegram/StoryStealthMode.cpp @@ -804,6 +805,7 @@ set(TDLIB_SOURCE td/telegram/StoryDb.h td/telegram/StoryFullId.h td/telegram/StoryId.h + td/telegram/StoryForwardInfo.h td/telegram/StoryInteractionInfo.h td/telegram/StoryListId.h td/telegram/StoryManager.h @@ -893,6 +895,7 @@ set(TDLIB_SOURCE td/telegram/StickerMaskPosition.hpp td/telegram/StickerPhotoSize.hpp td/telegram/StickersManager.hpp + td/telegram/StoryForwardInfo.hpp td/telegram/StoryInteractionInfo.hpp td/telegram/StoryStealthMode.hpp td/telegram/TranscriptionInfo.hpp diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f65c9eff8..41f760c37 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -3497,6 +3497,20 @@ storyListMain = StoryList; storyListArchive = StoryList; +//@class StoryOrigin @description Contains information about the origin of a story that was reposted + +//@description The original story was a public story with known sender @chat_id Identifier of the chat that posted original story @story_id Story identifier of the original story +storyOriginPublicStory chat_id:int53 story_id:int32 = StoryOrigin; + +//@description The original story was sent by a user, which is hidden by their privacy settings @sender_name Name of the story sender +storyOriginHiddenUser sender_name:string = StoryOrigin; + + +//@description Contains information about original story that was reposted +//@origin Origin of the story that was reposted +//@is_content_modified True, if story content was modified during reposting; otherwise, story wasn't modified +storyRepostInfo origin:StoryOrigin is_content_modified:Bool = StoryRepostInfo; + //@description Contains information about interactions with a story //@view_count Number of times the story was viewed //@forward_count Number of times the story was forwarded; 0 if none or unknown @@ -3521,13 +3535,14 @@ storyInteractionInfo view_count:int32 forward_count:int32 reaction_count:int32 r //@can_get_statistics True, if the story statistics are available through getStoryStatistics //@can_get_viewers True, if users viewed the story can be received through getStoryViewers //@has_expired_viewers True, if users viewed the story can't be received, because the story has expired more than getOption("story_viewers_expiration_delay") seconds ago +//@repost_info Information about the original story; may be null if the story wasn't reposted //@interaction_info Information about interactions with the story; may be null if the story isn't owned or there were no interactions //@chosen_reaction_type Type of the chosen reaction; may be null if none //@privacy_settings Privacy rules affecting story visibility; may be approximate for non-owned stories //@content Content of the story //@areas Clickable areas to be shown on the story content //@caption Caption of the story -story id:int32 sender_chat_id:int53 date:int32 is_being_sent:Bool is_being_edited:Bool is_edited:Bool is_pinned:Bool is_visible_only_for_self:Bool can_be_deleted:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_replied:Bool can_toggle_is_pinned:Bool can_get_statistics:Bool can_get_viewers:Bool has_expired_viewers:Bool interaction_info:storyInteractionInfo chosen_reaction_type:ReactionType privacy_settings:StoryPrivacySettings content:StoryContent areas:vector caption:formattedText = Story; +story id:int32 sender_chat_id:int53 date:int32 is_being_sent:Bool is_being_edited:Bool is_edited:Bool is_pinned:Bool is_visible_only_for_self:Bool can_be_deleted:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_replied:Bool can_toggle_is_pinned:Bool can_get_statistics:Bool can_get_viewers:Bool has_expired_viewers:Bool repost_info:storyRepostInfo interaction_info:storyInteractionInfo chosen_reaction_type:ReactionType privacy_settings:StoryPrivacySettings content:StoryContent areas:vector caption:formattedText = Story; //@description Represents a list of stories @total_count Approximate total number of stories found @stories The list of stories stories total_count:int32 stories:vector = Stories; diff --git a/td/telegram/StoryForwardInfo.cpp b/td/telegram/StoryForwardInfo.cpp new file mode 100644 index 000000000..b34026acd --- /dev/null +++ b/td/telegram/StoryForwardInfo.cpp @@ -0,0 +1,79 @@ +// +// 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/StoryForwardInfo.h" + +#include "td/telegram/ContactsManager.h" +#include "td/telegram/Dependencies.h" +#include "td/telegram/MessagesManager.h" +#include "td/telegram/Td.h" + +#include "td/utils/logging.h" + +namespace td { + +StoryForwardInfo::StoryForwardInfo(Td *td, telegram_api::object_ptr &&fwd_header) { + CHECK(fwd_header != nullptr); + is_modified_ = fwd_header->modified_; + if (fwd_header->from_ != nullptr) { + dialog_id_ = DialogId(fwd_header->from_); + story_id_ = StoryId(fwd_header->story_id_); + if (!dialog_id_.is_valid() || !story_id_.is_server()) { + LOG(ERROR) << "Receive " << to_string(fwd_header); + dialog_id_ = {}; + story_id_ = {}; + } else { + td->messages_manager_->force_create_dialog(dialog_id_, "StoryForwardInfo", true); + } + } else if ((fwd_header->flags_ & telegram_api::storyFwdHeader::FROM_NAME_MASK) != 0) { + if (fwd_header->story_id_ != 0) { + LOG(ERROR) << "Receive " << to_string(fwd_header); + } + sender_name_ = std::move(fwd_header->from_name_); + } else { + LOG(ERROR) << "Receive " << to_string(fwd_header); + } +} + +void StoryForwardInfo::hide_sender_if_needed(Td *td) { + if (dialog_id_.get_type() == DialogType::User) { + auto private_forward_name = td->contacts_manager_->get_user_private_forward_name(dialog_id_.get_user_id()); + if (!private_forward_name.empty()) { + dialog_id_ = {}; + story_id_ = {}; + sender_name_ = std::move(private_forward_name); + } + } +} + +void StoryForwardInfo::add_dependencies(Dependencies &dependencies) const { + // don't try to load original story + dependencies.add_dialog_and_dependencies(dialog_id_); +} + +td_api::object_ptr StoryForwardInfo::get_story_repost_info_object(Td *td) const { + auto origin = [&]() -> td_api::object_ptr { + if (dialog_id_.is_valid() && story_id_.is_valid()) { + return td_api::make_object( + td->messages_manager_->get_chat_id_object(dialog_id_, "storyOriginPublicStory"), story_id_.get()); + } + return td_api::make_object(sender_name_); + }(); + return td_api::make_object(std::move(origin), is_modified_); +} + +bool operator==(const unique_ptr &lhs, const unique_ptr &rhs) { + if (lhs == nullptr) { + return rhs == nullptr; + } + if (rhs == nullptr) { + return false; + } + return lhs->dialog_id_ == rhs->dialog_id_ && lhs->story_id_ == rhs->story_id_ && + lhs->sender_name_ == rhs->sender_name_ && lhs->is_modified_ == rhs->is_modified_; +} + +} // namespace td diff --git a/td/telegram/StoryForwardInfo.h b/td/telegram/StoryForwardInfo.h new file mode 100644 index 000000000..6982258ad --- /dev/null +++ b/td/telegram/StoryForwardInfo.h @@ -0,0 +1,60 @@ +// +// 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/DialogId.h" +#include "td/telegram/StoryFullId.h" +#include "td/telegram/StoryId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" + +#include + +namespace td { + +class Dependencies; +class Td; + +class StoryForwardInfo { + DialogId dialog_id_; + StoryId story_id_; + string sender_name_; + bool is_modified_ = false; + + friend bool operator==(const unique_ptr &lhs, const unique_ptr &rhs); + + public: + StoryForwardInfo() = default; + + StoryForwardInfo(Td *td, telegram_api::object_ptr &&fwd_header); + + StoryForwardInfo(StoryFullId story_full_id, bool is_modified) + : dialog_id_(story_full_id.get_dialog_id()), story_id_(story_full_id.get_story_id()), is_modified_(is_modified) { + } + + void hide_sender_if_needed(Td *td); + + void add_dependencies(Dependencies &dependencies) const; + + td_api::object_ptr get_story_repost_info_object(Td *td) const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +bool operator==(const unique_ptr &lhs, const unique_ptr &rhs); + +inline bool operator!=(const unique_ptr &lhs, const unique_ptr &rhs) { + return !(lhs == rhs); +} + +} // namespace td diff --git a/td/telegram/StoryForwardInfo.hpp b/td/telegram/StoryForwardInfo.hpp new file mode 100644 index 000000000..628a55b00 --- /dev/null +++ b/td/telegram/StoryForwardInfo.hpp @@ -0,0 +1,62 @@ +// +// 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/StoryForwardInfo.h" + +#include "td/utils/common.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void StoryForwardInfo::store(StorerT &storer) const { + using td::store; + bool has_dialog_id = dialog_id_.is_valid(); + bool has_story_id = story_id_.is_valid(); + bool has_sender_name = !sender_name_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_dialog_id); + STORE_FLAG(has_story_id); + STORE_FLAG(has_sender_name); + STORE_FLAG(is_modified_); + END_STORE_FLAGS(); + if (has_dialog_id) { + store(dialog_id_, storer); + } + if (has_story_id) { + store(story_id_, storer); + } + if (has_sender_name) { + store(sender_name_, storer); + } +} + +template +void StoryForwardInfo::parse(ParserT &parser) { + using td::parse; + bool has_dialog_id; + bool has_story_id; + bool has_sender_name; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_dialog_id); + PARSE_FLAG(has_story_id); + PARSE_FLAG(has_sender_name); + PARSE_FLAG(is_modified_); + END_PARSE_FLAGS(); + if (has_dialog_id) { + parse(dialog_id_, parser); + } + if (has_story_id) { + parse(story_id_, parser); + } + if (has_sender_name) { + parse(sender_name_, parser); + } +} + +} // namespace td diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index ea7a21ddd..4d413b2e3 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -27,6 +27,8 @@ #include "td/telegram/ReportReason.h" #include "td/telegram/StoryContent.h" #include "td/telegram/StoryContentType.h" +#include "td/telegram/StoryForwardInfo.h" +#include "td/telegram/StoryForwardInfo.hpp" #include "td/telegram/StoryInteractionInfo.hpp" #include "td/telegram/StoryStealthMode.hpp" #include "td/telegram/StoryViewer.h" @@ -1057,6 +1059,7 @@ void StoryManager::Story::store(StorerT &storer) const { bool has_caption = !caption_.text.empty(); bool has_areas = !areas_.empty(); bool has_chosen_reaction_type = !chosen_reaction_type_.is_empty(); + bool has_forward_info = forward_info_ != nullptr; BEGIN_STORE_FLAGS(); STORE_FLAG(is_edited_); STORE_FLAG(is_pinned_); @@ -1073,6 +1076,7 @@ void StoryManager::Story::store(StorerT &storer) const { STORE_FLAG(has_areas); STORE_FLAG(has_chosen_reaction_type); STORE_FLAG(is_outgoing_); + STORE_FLAG(has_forward_info); END_STORE_FLAGS(); store(date_, storer); store(expire_date_, storer); @@ -1097,6 +1101,9 @@ void StoryManager::Story::store(StorerT &storer) const { if (has_chosen_reaction_type) { store(chosen_reaction_type_, storer); } + if (has_forward_info) { + store(forward_info_, storer); + } } template @@ -1109,6 +1116,7 @@ void StoryManager::Story::parse(ParserT &parser) { bool has_caption; bool has_areas; bool has_chosen_reaction_type; + bool has_forward_info; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_edited_); PARSE_FLAG(is_pinned_); @@ -1125,6 +1133,7 @@ void StoryManager::Story::parse(ParserT &parser) { PARSE_FLAG(has_areas); PARSE_FLAG(has_chosen_reaction_type); PARSE_FLAG(is_outgoing_); + PARSE_FLAG(has_forward_info); END_PARSE_FLAGS(); parse(date_, parser); parse(expire_date_, parser); @@ -1149,6 +1158,9 @@ void StoryManager::Story::parse(ParserT &parser) { if (has_chosen_reaction_type) { parse(chosen_reaction_type_, parser); } + if (has_forward_info) { + parse(forward_info_, parser); + } } template @@ -1804,6 +1816,9 @@ StoryManager::ActiveStories *StoryManager::on_get_active_stories_from_database(S } void StoryManager::add_story_dependencies(Dependencies &dependencies, const Story *story) { + if (story->forward_info_ != nullptr) { + story->forward_info_->add_dependencies(dependencies); + } story->interaction_info_.add_dependencies(dependencies); story->privacy_rules_.add_dependencies(dependencies); if (story->content_ != nullptr) { @@ -3001,6 +3016,8 @@ td_api::object_ptr StoryManager::get_story_object(StoryFullId sto auto unix_time = G()->unix_time(); auto can_get_statistics = can_get_story_statistics(story_full_id, story); auto can_get_viewers = can_get_story_viewers(story_full_id, story, unix_time).is_ok(); + auto repost_info = + story->forward_info_ != nullptr ? story->forward_info_->get_story_repost_info_object(td_) : nullptr; auto interaction_info = story->interaction_info_.get_story_interaction_info_object(td_); auto has_expired_viewers = is_my_story(owner_dialog_id) && story_id.is_server() && unix_time >= get_story_viewers_expire_date(story) && interaction_info != nullptr && @@ -3016,8 +3033,9 @@ td_api::object_ptr StoryManager::get_story_object(StoryFullId sto story_id.get(), td_->messages_manager_->get_chat_id_object(owner_dialog_id, "get_story_object"), story->date_, is_being_sent, is_being_edited, is_edited, story->is_pinned_, is_visible_only_for_self, can_be_deleted, can_be_edited, can_be_forwarded, can_be_replied, can_toggle_is_pinned, can_get_statistics, can_get_viewers, - has_expired_viewers, std::move(interaction_info), story->chosen_reaction_type_.get_reaction_type_object(), - std::move(privacy_settings), get_story_content_object(td_, content), std::move(story_areas), + has_expired_viewers, std::move(repost_info), std::move(interaction_info), + story->chosen_reaction_type_.get_reaction_type_object(), std::move(privacy_settings), + get_story_content_object(td_, content), std::move(story_areas), get_formatted_text_object(*caption, true, get_story_content_duration(td_, content))); } @@ -3241,6 +3259,12 @@ StoryId StoryManager::on_get_new_story(DialogId owner_dialog_id, if (owner_dialog_id.get_type() == DialogType::User && !is_my_story(owner_dialog_id)) { story_item->min_ = false; } + unique_ptr forward_info = + story_item->fwd_from_ != nullptr ? make_unique(td_, std::move(story_item->fwd_from_)) : nullptr; + if (story->forward_info_ != forward_info) { + story->forward_info_ = std::move(forward_info); + is_changed = true; + } if (!story_item->min_) { auto privacy_rules = UserPrivacySettingRules::get_user_privacy_setting_rules(td_, std::move(story_item->privacy_)); auto interaction_info = StoryInteractionInfo(td_, std::move(story_item->views_)); diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 4f1720a8f..703edfd88 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -49,6 +49,7 @@ struct BinlogEvent; class Dependencies; class ReportReason; class StoryContent; +class StoryForwardInfo; struct StoryDbStory; class Td; @@ -66,6 +67,7 @@ class StoryManager final : public Actor { bool is_outgoing_ = false; bool noforwards_ = false; mutable bool is_update_sent_ = false; // whether the story is known to the app + unique_ptr forward_info_; StoryInteractionInfo interaction_info_; ReactionType chosen_reaction_type_; UserPrivacySettingRules privacy_rules_;