From 17fc0c3a88a7b0673bb48e9f47c435156324e3b0 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 18 May 2023 17:20:22 +0300 Subject: [PATCH] Add separate class for StoryContent. --- CMakeLists.txt | 4 + td/telegram/StoryContent.cpp | 167 +++++++++++++++++++++++++++++++ td/telegram/StoryContent.h | 35 +++++++ td/telegram/StoryContentType.cpp | 25 +++++ td/telegram/StoryContentType.h | 26 +++++ td/telegram/StoryManager.cpp | 26 ++--- td/telegram/StoryManager.h | 11 +- 7 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 td/telegram/StoryContent.cpp create mode 100644 td/telegram/StoryContent.h create mode 100644 td/telegram/StoryContentType.cpp create mode 100644 td/telegram/StoryContentType.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7df922305..b8d730090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -468,6 +468,8 @@ set(TDLIB_SOURCE td/telegram/StickersManager.cpp td/telegram/StickerType.cpp td/telegram/StorageManager.cpp + td/telegram/StoryContent.cpp + td/telegram/StoryContentType.cpp td/telegram/StoryManager.cpp td/telegram/SuggestedAction.cpp td/telegram/Support.cpp @@ -758,6 +760,8 @@ set(TDLIB_SOURCE td/telegram/StickersManager.h td/telegram/StickerType.h td/telegram/StorageManager.h + td/telegram/StoryContent.h + td/telegram/StoryContentType.h td/telegram/StoryId.h td/telegram/StoryManager.h td/telegram/SuggestedAction.h diff --git a/td/telegram/StoryContent.cpp b/td/telegram/StoryContent.cpp new file mode 100644 index 000000000..41b7be4bc --- /dev/null +++ b/td/telegram/StoryContent.cpp @@ -0,0 +1,167 @@ +// +// 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/StoryContent.h" + +#include "td/telegram/DocumentsManager.h" +#include "td/telegram/files/FileId.h" +#include "td/telegram/Photo.h" +#include "td/telegram/Td.h" +#include "td/telegram/VideosManager.h" + +#include "td/utils/common.h" + +namespace td { + +class StoryContentPhoto final : public StoryContent { + public: + Photo photo_; + + StoryContentPhoto() = default; + explicit StoryContentPhoto(Photo &&photo) : photo_(std::move(photo)) { + } + + StoryContentType get_type() const final { + return StoryContentType::Photo; + } +}; + +class StoryContentVideo final : public StoryContent { + public: + FileId file_id_; + FileId alt_file_id_; + + StoryContentVideo() = default; + StoryContentVideo(FileId file_id, FileId alt_file_id) : file_id_(file_id), alt_file_id_(alt_file_id) { + } + + StoryContentType get_type() const final { + return StoryContentType::Video; + } +}; + +class StoryContentUnsupported final : public StoryContent { + public: + static constexpr int32 CURRENT_VERSION = 1; + int32 version_ = CURRENT_VERSION; + + StoryContentUnsupported() = default; + explicit StoryContentUnsupported(int32 version) : version_(version) { + } + + StoryContentType get_type() const final { + return StoryContentType::Unsupported; + } +}; + +unique_ptr get_story_content(Td *td, tl_object_ptr &&media_ptr, + DialogId owner_dialog_id) { + CHECK(media_ptr != nullptr); + int32 constructor_id = media_ptr->get_id(); + switch (constructor_id) { + case telegram_api::messageMediaPhoto::ID: { + auto media = move_tl_object_as(media_ptr); + if (media->photo_ == nullptr || (media->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) != 0 || + media->spoiler_) { + break; + } + + auto photo = get_photo(td, std::move(media->photo_), owner_dialog_id); + if (photo.is_empty()) { + break; + } + return make_unique(std::move(photo)); + } + case telegram_api::messageMediaDocument::ID: { + auto media = move_tl_object_as(media_ptr); + if (media->document_ == nullptr || (media->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) != 0 || + media->spoiler_) { + break; + } + + auto document_ptr = std::move(media->document_); + int32 document_id = document_ptr->get_id(); + if (document_id == telegram_api::documentEmpty::ID) { + break; + } + CHECK(document_id == telegram_api::document::ID); + auto parsed_document = td->documents_manager_->on_get_document( + move_tl_object_as(document_ptr), owner_dialog_id, nullptr); + if (parsed_document.empty() || parsed_document.type != Document::Type::Video) { + break; + } + CHECK(parsed_document.file_id.is_valid()); + + FileId alt_file_id; + if (media->alt_document_ != nullptr) { + auto alt_document_ptr = std::move(media->alt_document_); + int32 alt_document_id = alt_document_ptr->get_id(); + if (alt_document_id == telegram_api::documentEmpty::ID) { + LOG(ERROR) << "Receive alternative " << to_string(alt_document_ptr); + } else { + CHECK(alt_document_id == telegram_api::document::ID); + auto parsed_alt_document = td->documents_manager_->on_get_document( + move_tl_object_as(alt_document_ptr), owner_dialog_id, nullptr); + if (parsed_alt_document.empty() || parsed_alt_document.type != Document::Type::Video) { + LOG(ERROR) << "Receive alternative " << to_string(alt_document_ptr); + } else { + alt_file_id = parsed_alt_document.file_id; + } + } + } + + return make_unique(parsed_document.file_id, alt_file_id); + } + case telegram_api::messageMediaUnsupported::ID: + return make_unique(); + default: + break; + } + LOG(ERROR) << "Receive a story with content " << to_string(media_ptr); + return nullptr; +} + +void merge_story_contents(Td *td, const StoryContent *old_content, StoryContent *new_content, DialogId dialog_id, + bool need_merge_files, bool &is_content_changed, bool &need_update) { + StoryContentType content_type = new_content->get_type(); + CHECK(old_content->get_type() == content_type); + + switch (content_type) { + case StoryContentType::Photo: { + const auto *old_ = static_cast(old_content); + auto *new_ = static_cast(new_content); + merge_photos(td, &old_->photo_, &new_->photo_, dialog_id, need_merge_files, is_content_changed, need_update); + break; + } + case StoryContentType::Video: { + const auto *old_ = static_cast(old_content); + const auto *new_ = static_cast(new_content); + if (old_->file_id_ != new_->file_id_) { + if (need_merge_files) { + td->videos_manager_->merge_videos(new_->file_id_, old_->file_id_); + } + need_update = true; + } + if (old_->alt_file_id_ != new_->alt_file_id_) { + need_update = true; + } + break; + } + case StoryContentType::Unsupported: { + const auto *old_ = static_cast(old_content); + const auto *new_ = static_cast(new_content); + if (old_->version_ != new_->version_) { + is_content_changed = true; + } + break; + } + default: + UNREACHABLE(); + break; + } +} + +} // namespace td diff --git a/td/telegram/StoryContent.h b/td/telegram/StoryContent.h new file mode 100644 index 000000000..96c24d94b --- /dev/null +++ b/td/telegram/StoryContent.h @@ -0,0 +1,35 @@ +// +// 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/StoryContentType.h" +#include "td/telegram/telegram_api.h" + +namespace td { + +class Td; + +class StoryContent { + public: + StoryContent() = default; + StoryContent(const StoryContent &) = default; + StoryContent &operator=(const StoryContent &) = default; + StoryContent(StoryContent &&) = default; + StoryContent &operator=(StoryContent &&) = default; + + virtual StoryContentType get_type() const = 0; + virtual ~StoryContent() = default; +}; + +unique_ptr get_story_content(Td *td, telegram_api::object_ptr &&media_ptr, + DialogId owner_dialog_id); + +void merge_story_contents(Td *td, const StoryContent *old_content, StoryContent *new_content, DialogId dialog_id, + bool need_merge_files, bool &is_content_changed, bool &need_update); + +} // namespace td diff --git a/td/telegram/StoryContentType.cpp b/td/telegram/StoryContentType.cpp new file mode 100644 index 000000000..40bfa7737 --- /dev/null +++ b/td/telegram/StoryContentType.cpp @@ -0,0 +1,25 @@ +// +// 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/StoryContentType.h" + +namespace td { + +StringBuilder &operator<<(StringBuilder &string_builder, StoryContentType content_type) { + switch (content_type) { + case StoryContentType::Photo: + return string_builder << "Photo"; + case StoryContentType::Video: + return string_builder << "Video"; + case StoryContentType::Unsupported: + return string_builder << "Unsupported"; + default: + UNREACHABLE(); + return string_builder; + } +} + +} // namespace td diff --git a/td/telegram/StoryContentType.h b/td/telegram/StoryContentType.h new file mode 100644 index 000000000..ef34a32c2 --- /dev/null +++ b/td/telegram/StoryContentType.h @@ -0,0 +1,26 @@ +// +// 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/utils/common.h" +#include "td/utils/HashTableUtils.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +// increase StoryContentUnsupported::CURRENT_VERSION each time a new Story content type is added +enum class StoryContentType : int32 { Photo, Video, Unsupported }; + +StringBuilder &operator<<(StringBuilder &string_builder, StoryContentType content_type); + +struct StoryContentTypeHash { + uint32 operator()(StoryContentType content_type) const { + return Hash()(static_cast(content_type)); + } +}; + +} // namespace td diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index 4fc3f8866..24114cc83 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -8,9 +8,9 @@ #include "td/telegram/AuthManager.h" #include "td/telegram/ContactsManager.h" -#include "td/telegram/MessageContent.h" -#include "td/telegram/MessageContentType.h" #include "td/telegram/MessageEntity.h" +#include "td/telegram/StoryContent.h" +#include "td/telegram/StoryContentType.h" #include "td/telegram/Td.h" namespace td { @@ -18,6 +18,8 @@ namespace td { StoryManager::StoryManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { } +StoryManager::~StoryManager() = default; + void StoryManager::tear_down() { parent_.reset(); } @@ -53,18 +55,14 @@ StoryId StoryManager::on_get_story(DialogId owner_dialog_id, CHECK(story != nullptr); bool is_bot = td_->auth_manager_->is_bot(); - auto message_text = + auto caption = get_message_text(td_->contacts_manager_.get(), std::move(story_item->caption_), std::move(story_item->entities_), true, is_bot, story_item->date_, false, "on_get_story"); - int32 ttl = 0; - auto content = get_message_content(td_, std::move(message_text), std::move(story_item->media_), owner_dialog_id, - false, UserId(), &ttl, nullptr, "on_get_story"); - auto content_type = content->get_type(); - if (content_type != MessageContentType::Photo && content_type != MessageContentType::Video && - content_type != MessageContentType::Unsupported) { - LOG(ERROR) << "Receive " << story_id << " of type " << content_type; + auto content = get_story_content(td_, std::move(story_item->media_), owner_dialog_id); + if (content == nullptr) { return StoryId(); } + auto content_type = content->get_type(); auto privacy_rules = UserPrivacySettingRules::get_user_privacy_setting_rules(td_, std::move(story_item->privacy_)); @@ -85,7 +83,8 @@ StoryId StoryManager::on_get_story(DialogId owner_dialog_id, if (story->is_pinned_ != story_item->pinned_ || story->is_public_ != story_item->public_ || story->is_for_close_friends_ != story_item->close_friends_ || story->date_ != story_item->date_ || story->expire_date_ != story_item->expire_date_ || !(story->privacy_rules_ == privacy_rules) || - story->recent_viewer_user_ids_ != recent_viewer_user_ids || story->view_count_ != view_count) { + story->recent_viewer_user_ids_ != recent_viewer_user_ids || story->view_count_ != view_count || + story->caption_ != caption) { story->is_pinned_ = story_item->pinned_; story->is_public_ = story_item->public_; story->is_for_close_friends_ = story_item->close_friends_; @@ -94,14 +93,15 @@ StoryId StoryManager::on_get_story(DialogId owner_dialog_id, story->privacy_rules_ = std::move(privacy_rules); story->recent_viewer_user_ids_ = std::move(recent_viewer_user_ids); story->view_count_ = view_count; + story->caption_ = std::move(caption); is_changed = true; } if (story->content_ == nullptr || story->content_->get_type() != content_type) { story->content_ = std::move(content); is_changed = true; } else { - merge_message_contents(td_, story->content_.get(), content.get(), false, owner_dialog_id, false, - need_save_to_database, is_changed); + merge_story_contents(td_, story->content_.get(), content.get(), owner_dialog_id, false, need_save_to_database, + is_changed); } if (is_changed || need_save_to_database) { diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index ece5204c6..da5845dd6 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/MessageEntity.h" #include "td/telegram/StoryId.h" #include "td/telegram/UserId.h" #include "td/telegram/UserPrivacySettingRule.h" @@ -18,12 +19,17 @@ namespace td { -class MessageContent; +class StoryContent; class Td; class StoryManager final : public Actor { public: StoryManager(Td *td, ActorShared<> parent); + StoryManager(const StoryManager &) = delete; + StoryManager &operator=(const StoryManager &) = delete; + StoryManager(StoryManager &&) = delete; + StoryManager &operator=(StoryManager &&) = delete; + ~StoryManager() final; StoryId on_get_story(DialogId owner_dialog_id, telegram_api::object_ptr &&story_item); @@ -37,7 +43,8 @@ class StoryManager final : public Actor { vector recent_viewer_user_ids_; int32 view_count_ = 0; UserPrivacySettingRules privacy_rules_; - unique_ptr content_; + unique_ptr content_; + FormattedText caption_; }; void tear_down() final;