From 4cf6aba717e18cc0667e190027827de485cf3ce7 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 28 Sep 2018 23:57:34 +0300 Subject: [PATCH] Move MessageContent and InputMessageText implementations to corresponding files. GitOrigin-RevId: d308007a3c850f1969b64a08865787ee7b340b34 --- CMakeLists.txt | 5 + td/telegram/Contact.cpp | 22 + td/telegram/Contact.h | 4 + td/telegram/Game.cpp | 24 + td/telegram/Game.h | 6 + td/telegram/InlineQueriesManager.cpp | 115 +- td/telegram/InlineQueriesManager.h | 13 +- td/telegram/InputMessageText.cpp | 50 + td/telegram/InputMessageText.h | 39 + td/telegram/InputMessageText.hpp | 35 + td/telegram/Location.cpp | 56 + td/telegram/Location.h | 7 + td/telegram/MessageContent.cpp | 4523 ++++++++++++++++++++++++++ td/telegram/MessageContent.h | 227 ++ td/telegram/MessageEntity.cpp | 40 + td/telegram/MessageEntity.h | 6 + td/telegram/MessagesDb.h | 23 + td/telegram/MessagesManager.cpp | 4035 +---------------------- td/telegram/MessagesManager.h | 810 +---- td/telegram/Td.cpp | 1 + 20 files changed, 5106 insertions(+), 4935 deletions(-) create mode 100644 td/telegram/InputMessageText.cpp create mode 100644 td/telegram/InputMessageText.h create mode 100644 td/telegram/InputMessageText.hpp create mode 100644 td/telegram/MessageContent.cpp create mode 100644 td/telegram/MessageContent.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4afbde281..252fb92ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,8 +373,10 @@ set(TDLIB_SOURCE td/telegram/Global.cpp td/telegram/HashtagHints.cpp td/telegram/InlineQueriesManager.cpp + td/telegram/InputMessageText.cpp td/telegram/LanguagePackManager.cpp td/telegram/Location.cpp + td/telegram/MessageContent.cpp td/telegram/MessageEntity.cpp td/telegram/MessagesDb.cpp td/telegram/MessagesManager.cpp @@ -488,11 +490,13 @@ set(TDLIB_SOURCE td/telegram/Global.h td/telegram/HashtagHints.h td/telegram/InlineQueriesManager.h + td/telegram/InputMessageText.h td/telegram/LanguagePackManager.h td/telegram/Location.h td/telegram/logevent/LogEvent.h td/telegram/logevent/LogEventHelper.h td/telegram/logevent/SecretChatEvent.h + td/telegram/MessageContent.h td/telegram/MessageEntity.h td/telegram/MessageId.h td/telegram/MessagesDb.h @@ -560,6 +564,7 @@ set(TDLIB_SOURCE td/telegram/files/FileId.hpp td/telegram/files/FileManager.hpp td/telegram/Game.hpp + td/telegram/InputMessageText.hpp td/telegram/MessageEntity.hpp td/telegram/Payments.hpp td/telegram/Photo.hpp diff --git a/td/telegram/Contact.cpp b/td/telegram/Contact.cpp index d377936a9..ddab5a792 100644 --- a/td/telegram/Contact.cpp +++ b/td/telegram/Contact.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/Contact.h" +#include "td/telegram/misc.h" #include "td/telegram/secret_api.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -77,4 +78,25 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Contact &contact) << ", vCard size = " << contact.vcard_.size() << contact.user_id_ << "]"; } +Result process_input_message_contact(tl_object_ptr &&input_message_content) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageContact::ID); + auto contact = std::move(static_cast(input_message_content.get())->contact_); + + if (!clean_input_string(contact->phone_number_)) { + return Status::Error(400, "Phone number must be encoded in UTF-8"); + } + if (!clean_input_string(contact->first_name_)) { + return Status::Error(400, "First name must be encoded in UTF-8"); + } + if (!clean_input_string(contact->last_name_)) { + return Status::Error(400, "Last name must be encoded in UTF-8"); + } + if (!clean_input_string(contact->vcard_)) { + return Status::Error(400, "vCard must be encoded in UTF-8"); + } + + return Contact(contact->phone_number_, contact->first_name_, contact->last_name_, contact->vcard_, contact->user_id_); +} + } // namespace td diff --git a/td/telegram/Contact.h b/td/telegram/Contact.h index 95d0500ea..d7a35331d 100644 --- a/td/telegram/Contact.h +++ b/td/telegram/Contact.h @@ -11,6 +11,7 @@ #include "td/telegram/Version.h" #include "td/utils/common.h" +#include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" @@ -139,4 +140,7 @@ struct ContactHash { } }; +Result process_input_message_contact(tl_object_ptr &&input_message_content) + TD_WARN_UNUSED_RESULT; + } // namespace td diff --git a/td/telegram/Game.cpp b/td/telegram/Game.cpp index 6f65e7e2b..dc97453c5 100644 --- a/td/telegram/Game.cpp +++ b/td/telegram/Game.cpp @@ -12,6 +12,7 @@ #include "td/telegram/AnimationsManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DocumentsManager.h" +#include "td/telegram/misc.h" #include "td/telegram/Photo.h" #include "td/telegram/Td.h" @@ -122,4 +123,27 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Game &game) { << ", photo = " << game.photo_ << ", animation_file_id = " << game.animation_file_id_ << "]"; } +Result process_input_message_game(const ContactsManager *contacts_manager, + tl_object_ptr &&input_message_content) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageGame::ID); + auto input_message_game = move_tl_object_as(input_message_content); + + UserId bot_user_id(input_message_game->bot_user_id_); + if (!contacts_manager->have_input_user(bot_user_id)) { + return Status::Error(400, "Game owner bot is not accessible"); + } + + if (!clean_input_string(input_message_game->game_short_name_)) { + return Status::Error(400, "Game short name must be encoded in UTF-8"); + } + + // TODO validate game_short_name + if (input_message_game->game_short_name_.empty()) { + return Status::Error(400, "Game short name must be non-empty"); + } + + return Game(bot_user_id, std::move(input_message_game->game_short_name_)); +} + } // namespace td diff --git a/td/telegram/Game.h b/td/telegram/Game.h index 26683e431..ccb856434 100644 --- a/td/telegram/Game.h +++ b/td/telegram/Game.h @@ -13,6 +13,7 @@ #include "td/telegram/UserId.h" #include "td/utils/common.h" +#include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/telegram/td_api.h" @@ -20,6 +21,7 @@ namespace td { +class ContactsManager; class Td; class Game { @@ -75,4 +77,8 @@ bool operator!=(const Game &lhs, const Game &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const Game &game); +Result process_input_message_game(const ContactsManager *contacts_manager, + tl_object_ptr &&input_message_content) + TD_WARN_UNUSED_RESULT; + } // namespace td diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index e3e57a97d..a4b9ca6a4 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -21,6 +21,9 @@ #include "td/telegram/files/FileManager.h" #include "td/telegram/Game.h" #include "td/telegram/Global.h" +#include "td/telegram/InputMessageText.h" +#include "td/telegram/Location.h" +#include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" @@ -249,8 +252,8 @@ Result> InlineQueriesManager: auto constructor_id = input_message_content->get_id(); if (constructor_id == td_api::inputMessageText::ID) { - TRY_RESULT(input_message_text, MessagesManager::process_input_message_text(td_->contacts_manager_.get(), DialogId(), - std::move(input_message_content), true)); + TRY_RESULT(input_message_text, process_input_message_text(td_->contacts_manager_.get(), DialogId(), + std::move(input_message_content), true)); if (input_message_text.disable_web_page_preview) { flags |= telegram_api::inputBotInlineMessageText::NO_WEBPAGE_MASK; @@ -265,16 +268,16 @@ Result> InlineQueriesManager: std::move(input_reply_markup)); } if (constructor_id == td_api::inputMessageContact::ID) { - TRY_RESULT(contact, MessagesManager::process_input_message_contact(std::move(input_message_content))); + TRY_RESULT(contact, process_input_message_contact(std::move(input_message_content))); return contact.get_input_bot_inline_message_media_contact(flags, std::move(input_reply_markup)); } if (constructor_id == td_api::inputMessageLocation::ID) { - TRY_RESULT(location, MessagesManager::process_input_message_location(std::move(input_message_content))); + TRY_RESULT(location, process_input_message_location(std::move(input_message_content))); return make_tl_object(flags, location.first.get_input_geo_point(), location.second, std::move(input_reply_markup)); } if (constructor_id == td_api::inputMessageVenue::ID) { - TRY_RESULT(venue, MessagesManager::process_input_message_venue(std::move(input_message_content))); + TRY_RESULT(venue, process_input_message_venue(std::move(input_message_content))); return venue.get_input_bot_inline_message_media_venue(flags, std::move(input_reply_markup)); } if (constructor_id == allowed_media_content_id) { @@ -317,108 +320,6 @@ Result> InlineQueriesManager: return Status::Error(400, "Unallowed inline message content type"); } -InlineMessageContent InlineQueriesManager::create_inline_message_content( - Td *td, FileId file_id, tl_object_ptr &&inline_message, - int32 allowed_media_content_id, Photo *photo, Game *game) { - CHECK(inline_message != nullptr); - CHECK((allowed_media_content_id == td_api::inputMessagePhoto::ID) == (photo != nullptr)); - CHECK((allowed_media_content_id == td_api::inputMessageGame::ID) == (game != nullptr)); - CHECK((allowed_media_content_id != td_api::inputMessagePhoto::ID && - allowed_media_content_id != td_api::inputMessageGame::ID && allowed_media_content_id != -1) == - file_id.is_valid()); - - InlineMessageContent result; - tl_object_ptr reply_markup; - result.disable_web_page_preview = false; - switch (inline_message->get_id()) { - case telegram_api::botInlineMessageText::ID: { - auto inline_message_text = move_tl_object_as(inline_message); - auto entities = get_message_entities(td->contacts_manager_.get(), std::move(inline_message_text->entities_), - "botInlineMessageText"); - auto status = fix_formatted_text(inline_message_text->message_, entities, false, true, true, false); - if (status.is_error()) { - LOG(ERROR) << "Receive error " << status << " while parsing botInlineMessageText " - << inline_message_text->message_; - break; - } - - result.disable_web_page_preview = - (inline_message_text->flags_ & telegram_api::botInlineMessageText::NO_WEBPAGE_MASK) != 0; - WebPageId web_page_id; - if (!result.disable_web_page_preview) { - web_page_id = - td->web_pages_manager_->get_web_page_by_url(get_first_url(inline_message_text->message_, entities)); - } - result.message_content = make_unique( - FormattedText{std::move(inline_message_text->message_), std::move(entities)}, web_page_id); - reply_markup = std::move(inline_message_text->reply_markup_); - break; - } - case telegram_api::botInlineMessageMediaGeo::ID: { - auto inline_message_geo = move_tl_object_as(inline_message); - if (inline_message_geo->period_ > 0) { - result.message_content = - make_unique(Location(inline_message_geo->geo_), inline_message_geo->period_); - } else { - result.message_content = make_unique(Location(inline_message_geo->geo_)); - } - reply_markup = std::move(inline_message_geo->reply_markup_); - break; - } - case telegram_api::botInlineMessageMediaVenue::ID: { - auto inline_message_venue = move_tl_object_as(inline_message); - result.message_content = make_unique( - Venue(inline_message_venue->geo_, std::move(inline_message_venue->title_), - std::move(inline_message_venue->address_), std::move(inline_message_venue->provider_), - std::move(inline_message_venue->venue_id_), std::move(inline_message_venue->venue_type_))); - reply_markup = std::move(inline_message_venue->reply_markup_); - break; - } - case telegram_api::botInlineMessageMediaContact::ID: { - auto inline_message_contact = move_tl_object_as(inline_message); - result.message_content = make_unique( - Contact(std::move(inline_message_contact->phone_number_), std::move(inline_message_contact->first_name_), - std::move(inline_message_contact->last_name_), std::move(inline_message_contact->vcard_), 0)); - reply_markup = std::move(inline_message_contact->reply_markup_); - break; - } - case telegram_api::botInlineMessageMediaAuto::ID: { - auto input_message_media_auto = move_tl_object_as(inline_message); - auto caption = MessagesManager::get_message_text(td->contacts_manager_.get(), input_message_media_auto->message_, - std::move(input_message_media_auto->entities_), 0, - "register_inline_message_content"); - if (allowed_media_content_id == td_api::inputMessageAnimation::ID) { - result.message_content = make_unique(file_id, std::move(caption)); - } else if (allowed_media_content_id == td_api::inputMessageAudio::ID) { - result.message_content = make_unique(file_id, std::move(caption)); - } else if (allowed_media_content_id == td_api::inputMessageDocument::ID) { - result.message_content = make_unique(file_id, std::move(caption)); - } else if (allowed_media_content_id == td_api::inputMessageGame::ID) { - CHECK(game != nullptr); - // TODO game->set_short_name(std::move(caption)); - result.message_content = make_unique(std::move(*game)); - } else if (allowed_media_content_id == td_api::inputMessagePhoto::ID) { - result.message_content = make_unique(std::move(*photo), std::move(caption)); - } else if (allowed_media_content_id == td_api::inputMessageSticker::ID) { - result.message_content = make_unique(file_id); - } else if (allowed_media_content_id == td_api::inputMessageVideo::ID) { - result.message_content = make_unique(file_id, std::move(caption)); - } else if (allowed_media_content_id == td_api::inputMessageVoiceNote::ID) { - result.message_content = make_unique(file_id, std::move(caption), true); - } else { - LOG(WARNING) << "Unallowed bot inline message " << to_string(input_message_media_auto); - } - - reply_markup = std::move(input_message_media_auto->reply_markup_); - break; - } - default: - UNREACHABLE(); - } - result.message_reply_markup = get_reply_markup(std::move(reply_markup), td->auth_manager_->is_bot(), true, false); - return result; -} - bool InlineQueriesManager::register_inline_message_content( int64 query_id, const string &result_id, FileId file_id, tl_object_ptr &&inline_message, int32 allowed_media_content_id, Photo *photo, diff --git a/td/telegram/InlineQueriesManager.h b/td/telegram/InlineQueriesManager.h index 72f7e1f14..b76cd89a6 100644 --- a/td/telegram/InlineQueriesManager.h +++ b/td/telegram/InlineQueriesManager.h @@ -17,6 +17,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/Location.h" +#include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/Photo.h" @@ -33,16 +34,8 @@ namespace td { class Td; -class MessageContent; - class Game; -struct InlineMessageContent { - unique_ptr message_content; - unique_ptr message_reply_markup; - bool disable_web_page_preview; -}; - class InlineQueriesManager : public Actor { public: InlineQueriesManager(Td *td, ActorShared<> parent); @@ -101,10 +94,6 @@ class InlineQueriesManager : public Actor { tl_object_ptr &&reply_markup_ptr, int32 allowed_media_content_id) const TD_WARN_UNUSED_RESULT; - static InlineMessageContent create_inline_message_content( - Td *td, FileId file_id, tl_object_ptr &&inline_message, - int32 allowed_media_content_id, Photo *photo, Game *game); - bool register_inline_message_content(int64 query_id, const string &result_id, FileId file_id, tl_object_ptr &&inline_message, int32 allowed_media_content_id, Photo *photo = nullptr, Game *game = nullptr); diff --git a/td/telegram/InputMessageText.cpp b/td/telegram/InputMessageText.cpp new file mode 100644 index 000000000..b08edbef4 --- /dev/null +++ b/td/telegram/InputMessageText.cpp @@ -0,0 +1,50 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/InputMessageText.h" + +#include "td/telegram/MessageEntity.h" + +namespace td { + +bool operator==(const InputMessageText &lhs, const InputMessageText &rhs) { + return lhs.text == rhs.text && lhs.disable_web_page_preview == rhs.disable_web_page_preview && + lhs.clear_draft == rhs.clear_draft; +} + +bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs) { + return !(lhs == rhs); +} + +Result process_input_message_text(const ContactsManager *contacts_manager, DialogId dialog_id, + tl_object_ptr &&input_message_content, + bool is_bot, bool for_draft) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageText::ID); + auto input_message_text = static_cast(input_message_content.get()); + if (input_message_text->text_ == nullptr) { + if (for_draft) { + return InputMessageText{FormattedText(), input_message_text->disable_web_page_preview_, + input_message_text->clear_draft_}; + } + + return Status::Error(400, "Message text can't be empty"); + } + + TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_))); + TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, false, + need_skip_bot_commands(contacts_manager, dialog_id, is_bot), for_draft)); + return InputMessageText{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)}, + input_message_text->disable_web_page_preview_, input_message_text->clear_draft_}; +} + +td_api::object_ptr get_input_message_text_object(const InputMessageText &input_message_text) { + return td_api::make_object(get_formatted_text_object(input_message_text.text), + input_message_text.disable_web_page_preview, + input_message_text.clear_draft); +} + +} // namespace td diff --git a/td/telegram/InputMessageText.h b/td/telegram/InputMessageText.h new file mode 100644 index 000000000..999290377 --- /dev/null +++ b/td/telegram/InputMessageText.h @@ -0,0 +1,39 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/MessageEntity.h" +#include "td/telegram/td_api.h" + +#include "td/utils/Status.h" + +namespace td { + +class ContactsManager; + +class InputMessageText { + public: + FormattedText text; + bool disable_web_page_preview = false; + bool clear_draft = false; + InputMessageText() = default; + InputMessageText(FormattedText text, bool disable_web_page_preview, bool clear_draft) + : text(std::move(text)), disable_web_page_preview(disable_web_page_preview), clear_draft(clear_draft) { + } +}; + +bool operator==(const InputMessageText &lhs, const InputMessageText &rhs); +bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs); + +Result process_input_message_text(const ContactsManager *contacts_manager, DialogId dialog_id, + tl_object_ptr &&input_message_content, + bool is_bot, bool for_draft = false) TD_WARN_UNUSED_RESULT; + +td_api::object_ptr get_input_message_text_object(const InputMessageText &input_message_text); + +} // namespace td diff --git a/td/telegram/InputMessageText.hpp b/td/telegram/InputMessageText.hpp new file mode 100644 index 000000000..2e555a1a6 --- /dev/null +++ b/td/telegram/InputMessageText.hpp @@ -0,0 +1,35 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/InputMessageText.h" + +#include "td/telegram/MessageEntity.hpp" + +#include "td/utils/tl_helpers.h" + +namespace td { + +template +static void store(const InputMessageText &input_message_text, StorerT &storer) { + BEGIN_STORE_FLAGS(); + STORE_FLAG(input_message_text.disable_web_page_preview); + STORE_FLAG(input_message_text.clear_draft); + END_STORE_FLAGS(); + store(input_message_text.text, storer); +} + +template +static void parse(InputMessageText &input_message_text, ParserT &parser) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(input_message_text.disable_web_page_preview); + PARSE_FLAG(input_message_text.clear_draft); + END_PARSE_FLAGS(); + parse(input_message_text.text, parser); +} + +} // namespace td diff --git a/td/telegram/Location.cpp b/td/telegram/Location.cpp index 0544ff084..1fd4e8d4d 100644 --- a/td/telegram/Location.cpp +++ b/td/telegram/Location.cpp @@ -7,6 +7,7 @@ #include "td/telegram/Location.h" #include "td/telegram/Global.h" +#include "td/telegram/misc.h" #include "td/telegram/secret_api.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -187,4 +188,59 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue) { << ", id = " << venue.id_ << ", type = " << venue.type_ << "]"; } +Result> process_input_message_location( + tl_object_ptr &&input_message_content) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageLocation::ID); + auto input_location = static_cast(input_message_content.get()); + + Location location(input_location->location_); + if (location.empty()) { + return Status::Error(400, "Wrong location specified"); + } + + constexpr int32 MIN_LIVE_LOCATION_PERIOD = 60; // seconds, server side limit + constexpr int32 MAX_LIVE_LOCATION_PERIOD = 86400; // seconds, server side limit + + auto period = input_location->live_period_; + if (period != 0 && (period < MIN_LIVE_LOCATION_PERIOD || period > MAX_LIVE_LOCATION_PERIOD)) { + return Status::Error(400, "Wrong live location period specified"); + } + + return std::make_pair(std::move(location), period); +} + +Result process_input_message_venue(tl_object_ptr &&input_message_content) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageVenue::ID); + auto venue = std::move(static_cast(input_message_content.get())->venue_); + + if (venue == nullptr) { + return Status::Error(400, "Venue can't be empty"); + } + + if (!clean_input_string(venue->title_)) { + return Status::Error(400, "Venue title must be encoded in UTF-8"); + } + if (!clean_input_string(venue->address_)) { + return Status::Error(400, "Venue address must be encoded in UTF-8"); + } + if (!clean_input_string(venue->provider_)) { + return Status::Error(400, "Venue provider must be encoded in UTF-8"); + } + if (!clean_input_string(venue->id_)) { + return Status::Error(400, "Venue identifier must be encoded in UTF-8"); + } + if (!clean_input_string(venue->type_)) { + return Status::Error(400, "Venue type must be encoded in UTF-8"); + } + + Venue result(venue); + if (result.empty()) { + return Status::Error(400, "Wrong venue location specified"); + } + + return result; +} + } // namespace td diff --git a/td/telegram/Location.h b/td/telegram/Location.h index 3cea14830..0f3777533 100644 --- a/td/telegram/Location.h +++ b/td/telegram/Location.h @@ -15,6 +15,7 @@ #include "td/telegram/telegram_api.h" #include "td/utils/common.h" +#include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" @@ -176,4 +177,10 @@ bool operator!=(const Venue &lhs, const Venue &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue); +Result> process_input_message_location( + td_api::object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; + +Result process_input_message_venue(td_api::object_ptr &&input_message_content) + TD_WARN_UNUSED_RESULT; + } // namespace td diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp new file mode 100644 index 000000000..bb5e7c6b6 --- /dev/null +++ b/td/telegram/MessageContent.cpp @@ -0,0 +1,4523 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/MessageContent.h" + +#include "td/telegram/AnimationsManager.h" +#include "td/telegram/AnimationsManager.hpp" +#include "td/telegram/AudiosManager.h" +#include "td/telegram/AudiosManager.hpp" +#include "td/telegram/AuthManager.h" +#include "td/telegram/CallDiscardReason.h" +#include "td/telegram/ChannelId.h" +#include "td/telegram/ChatId.h" +#include "td/telegram/Contact.h" +#include "td/telegram/ContactsManager.h" +#include "td/telegram/DocumentsManager.h" +#include "td/telegram/DocumentsManager.hpp" +#include "td/telegram/files/FileId.h" +#include "td/telegram/files/FileId.hpp" +#include "td/telegram/files/FileManager.h" +#include "td/telegram/Game.h" +#include "td/telegram/Game.hpp" +#include "td/telegram/Global.h" +#include "td/telegram/InputMessageText.h" +#include "td/telegram/Location.h" +#include "td/telegram/MessageEntity.h" +#include "td/telegram/MessageEntity.hpp" +#include "td/telegram/MessageId.h" +#include "td/telegram/MessagesDb.h" +#include "td/telegram/misc.h" +#include "td/telegram/Payments.h" +#include "td/telegram/Payments.hpp" +#include "td/telegram/Photo.h" +#include "td/telegram/Photo.hpp" +#include "td/telegram/secret_api.hpp" +#include "td/telegram/SecureValue.h" +#include "td/telegram/SecureValue.hpp" +#include "td/telegram/StickersManager.h" +#include "td/telegram/StickersManager.hpp" +#include "td/telegram/Td.h" +#include "td/telegram/UserId.h" +#include "td/telegram/VideosManager.h" +#include "td/telegram/VideosManager.hpp" +#include "td/telegram/VideoNotesManager.h" +#include "td/telegram/VideoNotesManager.hpp" +#include "td/telegram/VoiceNotesManager.h" +#include "td/telegram/VoiceNotesManager.hpp" +#include "td/telegram/WebPageId.h" +#include "td/telegram/WebPagesManager.h" + +#include "td/actor/MultiPromise.h" + +#include "td/utils/format.h" +#include "td/utils/HttpUrl.h" +#include "td/utils/MimeType.h" +#include "td/utils/misc.h" +#include "td/utils/PathView.h" +#include "td/utils/tl_helpers.h" +#include "td/utils/tl_parsers.h" +#include "td/utils/tl_storers.h" + +namespace td { + +class MessageText : public MessageContent { + public: + FormattedText text; + WebPageId web_page_id; + + MessageText() = default; + MessageText(FormattedText text, WebPageId web_page_id) : text(std::move(text)), web_page_id(web_page_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::Text; + } +}; + +class MessageAnimation : public MessageContent { + public: + FileId file_id; + + FormattedText caption; + + MessageAnimation() = default; + MessageAnimation(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Animation; + } +}; + +class MessageAudio : public MessageContent { + public: + FileId file_id; + + FormattedText caption; + + MessageAudio() = default; + MessageAudio(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Audio; + } +}; + +class MessageDocument : public MessageContent { + public: + FileId file_id; + + FormattedText caption; + + MessageDocument() = default; + MessageDocument(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Document; + } +}; + +class MessagePhoto : public MessageContent { + public: + Photo photo; + + FormattedText caption; + + MessagePhoto() = default; + MessagePhoto(Photo &&photo, FormattedText &&caption) : photo(std::move(photo)), caption(std::move(caption)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Photo; + } +}; + +class MessageSticker : public MessageContent { + public: + FileId file_id; + + MessageSticker() = default; + explicit MessageSticker(FileId file_id) : file_id(file_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::Sticker; + } +}; + +class MessageVideo : public MessageContent { + public: + FileId file_id; + + FormattedText caption; + + MessageVideo() = default; + MessageVideo(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Video; + } +}; + +class MessageVoiceNote : public MessageContent { + public: + FileId file_id; + + FormattedText caption; + bool is_listened; + + MessageVoiceNote() = default; + MessageVoiceNote(FileId file_id, FormattedText &&caption, bool is_listened) + : file_id(file_id), caption(std::move(caption)), is_listened(is_listened) { + } + + MessageContentType get_type() const override { + return MessageContentType::VoiceNote; + } +}; + +class MessageContact : public MessageContent { + public: + Contact contact; + + MessageContact() = default; + explicit MessageContact(Contact &&contact) : contact(std::move(contact)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Contact; + } +}; + +class MessageLocation : public MessageContent { + public: + Location location; + + MessageLocation() = default; + explicit MessageLocation(Location &&location) : location(std::move(location)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Location; + } +}; + +class MessageVenue : public MessageContent { + public: + Venue venue; + + MessageVenue() = default; + explicit MessageVenue(Venue &&venue) : venue(std::move(venue)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Venue; + } +}; + +class MessageChatCreate : public MessageContent { + public: + string title; + vector participant_user_ids; + + MessageChatCreate() = default; + MessageChatCreate(string &&title, vector &&participant_user_ids) + : title(std::move(title)), participant_user_ids(std::move(participant_user_ids)) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatCreate; + } +}; + +class MessageChatChangeTitle : public MessageContent { + public: + string title; + + MessageChatChangeTitle() = default; + explicit MessageChatChangeTitle(string &&title) : title(std::move(title)) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatChangeTitle; + } +}; + +class MessageChatChangePhoto : public MessageContent { + public: + Photo photo; + + MessageChatChangePhoto() = default; + explicit MessageChatChangePhoto(Photo &&photo) : photo(std::move(photo)) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatChangePhoto; + } +}; + +class MessageChatDeletePhoto : public MessageContent { + public: + MessageContentType get_type() const override { + return MessageContentType::ChatDeletePhoto; + } +}; + +class MessageChatDeleteHistory : public MessageContent { + public: + MessageContentType get_type() const override { + return MessageContentType::ChatDeleteHistory; + } +}; + +class MessageChatAddUsers : public MessageContent { + public: + vector user_ids; + + MessageChatAddUsers() = default; + explicit MessageChatAddUsers(vector &&user_ids) : user_ids(std::move(user_ids)) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatAddUsers; + } +}; + +class MessageChatJoinedByLink : public MessageContent { + public: + MessageContentType get_type() const override { + return MessageContentType::ChatJoinedByLink; + } +}; + +class MessageChatDeleteUser : public MessageContent { + public: + UserId user_id; + + MessageChatDeleteUser() = default; + explicit MessageChatDeleteUser(UserId user_id) : user_id(user_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatDeleteUser; + } +}; + +class MessageChatMigrateTo : public MessageContent { + public: + ChannelId migrated_to_channel_id; + + MessageChatMigrateTo() = default; + explicit MessageChatMigrateTo(ChannelId migrated_to_channel_id) : migrated_to_channel_id(migrated_to_channel_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatMigrateTo; + } +}; + +class MessageChannelCreate : public MessageContent { + public: + string title; + + MessageChannelCreate() = default; + explicit MessageChannelCreate(string &&title) : title(std::move(title)) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChannelCreate; + } +}; + +class MessageChannelMigrateFrom : public MessageContent { + public: + string title; + ChatId migrated_from_chat_id; + + MessageChannelMigrateFrom() = default; + MessageChannelMigrateFrom(string &&title, ChatId migrated_from_chat_id) + : title(std::move(title)), migrated_from_chat_id(migrated_from_chat_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChannelMigrateFrom; + } +}; + +class MessagePinMessage : public MessageContent { + public: + MessageId message_id; + + MessagePinMessage() = default; + explicit MessagePinMessage(MessageId message_id) : message_id(message_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::PinMessage; + } +}; + +class MessageGame : public MessageContent { + public: + Game game; + + MessageGame() = default; + explicit MessageGame(Game &&game) : game(std::move(game)) { + } + + MessageContentType get_type() const override { + return MessageContentType::Game; + } +}; + +class MessageGameScore : public MessageContent { + public: + MessageId game_message_id; + int64 game_id; + int32 score; + + MessageGameScore() = default; + MessageGameScore(MessageId game_message_id, int64 game_id, int32 score) + : game_message_id(game_message_id), game_id(game_id), score(score) { + } + + MessageContentType get_type() const override { + return MessageContentType::GameScore; + } +}; + +class MessageScreenshotTaken : public MessageContent { + public: + MessageContentType get_type() const override { + return MessageContentType::ScreenshotTaken; + } +}; + +class MessageChatSetTtl : public MessageContent { + public: + int32 ttl; + + MessageChatSetTtl() = default; + explicit MessageChatSetTtl(int32 ttl) : ttl(ttl) { + } + + MessageContentType get_type() const override { + return MessageContentType::ChatSetTtl; + } +}; + +class MessageUnsupported + : public MessageContent { // TODO save a layer in which the message was received to + // automatically reget it if the layer changes + public: + MessageContentType get_type() const override { + return MessageContentType::Unsupported; + } +}; + +class MessageCall : public MessageContent { + public: + int64 call_id; + int32 duration; + CallDiscardReason discard_reason; + + MessageCall() = default; + MessageCall(int64 call_id, int32 duration, CallDiscardReason discard_reason) + : call_id(call_id), duration(duration), discard_reason(discard_reason) { + } + + MessageContentType get_type() const override { + return MessageContentType::Call; + } +}; + +class MessageInvoice : public MessageContent { + public: + string title; + string description; + Photo photo; + string start_parameter; + + // InputMessageInvoice + Invoice invoice; + string payload; + string provider_token; + string provider_data; + + // MessageInvoice + int64 total_amount = 0; + MessageId receipt_message_id; + + MessageInvoice() = default; + MessageInvoice(string &&title, string &&description, Photo &&photo, string &&start_parameter, int64 total_amount, + string &¤cy, bool is_test, bool need_shipping_address, MessageId receipt_message_id) + : title(std::move(title)) + , description(std::move(description)) + , photo(std::move(photo)) + , start_parameter(std::move(start_parameter)) + , invoice(std::move(currency), is_test, need_shipping_address) + , payload() + , provider_token() + , provider_data() + , total_amount(total_amount) + , receipt_message_id(receipt_message_id) { + } + + MessageContentType get_type() const override { + return MessageContentType::Invoice; + } +}; + +class MessagePaymentSuccessful : public MessageContent { + public: + MessageId invoice_message_id; + string currency; + int64 total_amount = 0; + + // bots only part + string invoice_payload; + string shipping_option_id; + unique_ptr order_info; + string telegram_payment_charge_id; + string provider_payment_charge_id; + + MessagePaymentSuccessful() = default; + MessagePaymentSuccessful(MessageId invoice_message_id, string &¤cy, int64 total_amount) + : invoice_message_id(invoice_message_id), currency(std::move(currency)), total_amount(total_amount) { + } + + MessageContentType get_type() const override { + return MessageContentType::PaymentSuccessful; + } +}; + +class MessageVideoNote : public MessageContent { + public: + FileId file_id; + + bool is_viewed = false; + + MessageVideoNote() = default; + MessageVideoNote(FileId file_id, bool is_viewed) : file_id(file_id), is_viewed(is_viewed) { + } + + MessageContentType get_type() const override { + return MessageContentType::VideoNote; + } +}; + +class MessageContactRegistered : public MessageContent { + public: + MessageContentType get_type() const override { + return MessageContentType::ContactRegistered; + } +}; + +class MessageExpiredPhoto : public MessageContent { + public: + MessageExpiredPhoto() = default; + + MessageContentType get_type() const override { + return MessageContentType::ExpiredPhoto; + } +}; + +class MessageExpiredVideo : public MessageContent { + public: + MessageExpiredVideo() = default; + + MessageContentType get_type() const override { + return MessageContentType::ExpiredVideo; + } +}; + +class MessageLiveLocation : public MessageContent { + public: + Location location; + int32 period; + + MessageLiveLocation() = default; + MessageLiveLocation(Location &&location, int32 period) : location(std::move(location)), period(period) { + } + + MessageContentType get_type() const override { + return MessageContentType::LiveLocation; + } +}; + +class MessageCustomServiceAction : public MessageContent { + public: + string message; + + MessageCustomServiceAction() = default; + explicit MessageCustomServiceAction(string &&message) : message(std::move(message)) { + } + + MessageContentType get_type() const override { + return MessageContentType::CustomServiceAction; + } +}; + +class MessageWebsiteConnected : public MessageContent { + public: + string domain_name; + + MessageWebsiteConnected() = default; + explicit MessageWebsiteConnected(string &&domain_name) : domain_name(std::move(domain_name)) { + } + + MessageContentType get_type() const override { + return MessageContentType::WebsiteConnected; + } +}; + +class MessagePassportDataSent : public MessageContent { + public: + vector types; + + MessagePassportDataSent() = default; + explicit MessagePassportDataSent(vector &&types) : types(std::move(types)) { + } + + MessageContentType get_type() const override { + return MessageContentType::PassportDataSent; + } +}; + +class MessagePassportDataReceived : public MessageContent { + public: + vector values; + EncryptedSecureCredentials credentials; + + MessagePassportDataReceived() = default; + MessagePassportDataReceived(vector &&values, EncryptedSecureCredentials &&credentials) + : values(std::move(values)), credentials(std::move(credentials)) { + } + + MessageContentType get_type() const override { + return MessageContentType::PassportDataReceived; + } +}; + +StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type) { + switch (content_type) { + case MessageContentType::None: + return string_builder << "None"; + case MessageContentType::Animation: + return string_builder << "Animation"; + case MessageContentType::Audio: + return string_builder << "Audio"; + case MessageContentType::Document: + return string_builder << "Document"; + case MessageContentType::ExpiredPhoto: + return string_builder << "ExpiredPhoto"; + case MessageContentType::Photo: + return string_builder << "Photo"; + case MessageContentType::ExpiredVideo: + return string_builder << "ExpiredVideo"; + case MessageContentType::Video: + return string_builder << "Video"; + case MessageContentType::VideoNote: + return string_builder << "VideoNote"; + case MessageContentType::VoiceNote: + return string_builder << "VoiceNote"; + case MessageContentType::Contact: + return string_builder << "Contact"; + case MessageContentType::LiveLocation: + return string_builder << "LiveLocation"; + case MessageContentType::Location: + return string_builder << "Location"; + case MessageContentType::Venue: + return string_builder << "Venue"; + case MessageContentType::Game: + return string_builder << "Game"; + case MessageContentType::Invoice: + return string_builder << "Invoice"; + case MessageContentType::Sticker: + return string_builder << "Sticker"; + case MessageContentType::Text: + return string_builder << "Text"; + case MessageContentType::Unsupported: + return string_builder << "Unsupported"; + case MessageContentType::ChatCreate: + return string_builder << "ChatCreate"; + case MessageContentType::ChatChangeTitle: + return string_builder << "ChatChangeTitle"; + case MessageContentType::ChatChangePhoto: + return string_builder << "ChatChangePhoto"; + case MessageContentType::ChatDeletePhoto: + return string_builder << "ChatDeletePhoto"; + case MessageContentType::ChatDeleteHistory: + return string_builder << "ChatDeleteHistory"; + case MessageContentType::ChatAddUsers: + return string_builder << "ChatAddUsers"; + case MessageContentType::ChatJoinedByLink: + return string_builder << "ChatJoinedByLink"; + case MessageContentType::ChatDeleteUser: + return string_builder << "ChatDeleteUser"; + case MessageContentType::ChatMigrateTo: + return string_builder << "ChatMigrateTo"; + case MessageContentType::ChannelCreate: + return string_builder << "ChannelCreate"; + case MessageContentType::ChannelMigrateFrom: + return string_builder << "ChannelMigrateFrom"; + case MessageContentType::PinMessage: + return string_builder << "PinMessage"; + case MessageContentType::GameScore: + return string_builder << "GameScore"; + case MessageContentType::ScreenshotTaken: + return string_builder << "ScreenshotTaken"; + case MessageContentType::ChatSetTtl: + return string_builder << "ChatSetTtl"; + case MessageContentType::Call: + return string_builder << "Call"; + case MessageContentType::PaymentSuccessful: + return string_builder << "PaymentSuccessful"; + case MessageContentType::ContactRegistered: + return string_builder << "ContactRegistered"; + case MessageContentType::CustomServiceAction: + return string_builder << "CustomServiceAction"; + case MessageContentType::WebsiteConnected: + return string_builder << "WebsiteConnected"; + case MessageContentType::PassportDataSent: + return string_builder << "PassportDataSent"; + case MessageContentType::PassportDataReceived: + return string_builder << "PassportDataReceived"; + default: + UNREACHABLE(); + return string_builder; + } +} + +template +static void store(const MessageContent *content, StorerT &storer) { + CHECK(content != nullptr); + + Td *td = storer.context()->td().get_actor_unsafe(); + CHECK(td != nullptr); + + auto content_type = content->get_type(); + store(content_type, storer); + + switch (content_type) { + case MessageContentType::Animation: { + auto m = static_cast(content); + td->animations_manager_->store_animation(m->file_id, storer); + store(m->caption, storer); + break; + } + case MessageContentType::Audio: { + auto m = static_cast(content); + td->audios_manager_->store_audio(m->file_id, storer); + store(m->caption, storer); + store(true, storer); + break; + } + case MessageContentType::Contact: { + auto m = static_cast(content); + store(m->contact, storer); + break; + } + case MessageContentType::Document: { + auto m = static_cast(content); + td->documents_manager_->store_document(m->file_id, storer); + store(m->caption, storer); + break; + } + case MessageContentType::Game: { + auto m = static_cast(content); + store(m->game, storer); + break; + } + case MessageContentType::Invoice: { + auto m = static_cast(content); + store(m->title, storer); + store(m->description, storer); + store(m->photo, storer); + store(m->start_parameter, storer); + store(m->invoice, storer); + store(m->payload, storer); + store(m->provider_token, storer); + store(m->provider_data, storer); + store(m->total_amount, storer); + store(m->receipt_message_id, storer); + break; + } + case MessageContentType::LiveLocation: { + auto m = static_cast(content); + store(m->location, storer); + store(m->period, storer); + break; + } + case MessageContentType::Location: { + auto m = static_cast(content); + store(m->location, storer); + break; + } + case MessageContentType::Photo: { + auto m = static_cast(content); + store(m->photo, storer); + store(m->caption, storer); + break; + } + case MessageContentType::Sticker: { + auto m = static_cast(content); + td->stickers_manager_->store_sticker(m->file_id, false, storer); + break; + } + case MessageContentType::Text: { + auto m = static_cast(content); + store(m->text, storer); + store(m->web_page_id, storer); + break; + } + case MessageContentType::Unsupported: + break; + case MessageContentType::Venue: { + auto m = static_cast(content); + store(m->venue, storer); + break; + } + case MessageContentType::Video: { + auto m = static_cast(content); + td->videos_manager_->store_video(m->file_id, storer); + store(m->caption, storer); + break; + } + case MessageContentType::VideoNote: { + auto m = static_cast(content); + td->video_notes_manager_->store_video_note(m->file_id, storer); + store(m->is_viewed, storer); + break; + } + case MessageContentType::VoiceNote: { + auto m = static_cast(content); + td->voice_notes_manager_->store_voice_note(m->file_id, storer); + store(m->caption, storer); + store(m->is_listened, storer); + break; + } + case MessageContentType::ChatCreate: { + auto m = static_cast(content); + store(m->title, storer); + store(m->participant_user_ids, storer); + break; + } + case MessageContentType::ChatChangeTitle: { + auto m = static_cast(content); + store(m->title, storer); + break; + } + case MessageContentType::ChatChangePhoto: { + auto m = static_cast(content); + store(m->photo, storer); + break; + } + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + break; + case MessageContentType::ChatAddUsers: { + auto m = static_cast(content); + store(m->user_ids, storer); + break; + } + case MessageContentType::ChatJoinedByLink: + break; + case MessageContentType::ChatDeleteUser: { + auto m = static_cast(content); + store(m->user_id, storer); + break; + } + case MessageContentType::ChatMigrateTo: { + auto m = static_cast(content); + store(m->migrated_to_channel_id, storer); + break; + } + case MessageContentType::ChannelCreate: { + auto m = static_cast(content); + store(m->title, storer); + break; + } + case MessageContentType::ChannelMigrateFrom: { + auto m = static_cast(content); + store(m->title, storer); + store(m->migrated_from_chat_id, storer); + break; + } + case MessageContentType::PinMessage: { + auto m = static_cast(content); + store(m->message_id, storer); + break; + } + case MessageContentType::GameScore: { + auto m = static_cast(content); + store(m->game_message_id, storer); + store(m->game_id, storer); + store(m->score, storer); + break; + } + case MessageContentType::ScreenshotTaken: + break; + case MessageContentType::ChatSetTtl: { + auto m = static_cast(content); + store(m->ttl, storer); + break; + } + case MessageContentType::Call: { + auto m = static_cast(content); + store(m->call_id, storer); + store(m->duration, storer); + store(m->discard_reason, storer); + break; + } + case MessageContentType::PaymentSuccessful: { + auto m = static_cast(content); + bool has_payload = !m->invoice_payload.empty(); + bool has_shipping_option_id = !m->shipping_option_id.empty(); + bool has_order_info = m->order_info != nullptr; + bool has_telegram_payment_charge_id = !m->telegram_payment_charge_id.empty(); + bool has_provider_payment_charge_id = !m->provider_payment_charge_id.empty(); + bool has_invoice_message_id = m->invoice_message_id.is_valid(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_payload); + STORE_FLAG(has_shipping_option_id); + STORE_FLAG(has_order_info); + STORE_FLAG(has_telegram_payment_charge_id); + STORE_FLAG(has_provider_payment_charge_id); + STORE_FLAG(has_invoice_message_id); + END_STORE_FLAGS(); + store(m->currency, storer); + store(m->total_amount, storer); + if (has_payload) { + store(m->total_amount, storer); + } + if (has_shipping_option_id) { + store(m->invoice_payload, storer); + } + if (has_order_info) { + store(*m->order_info, storer); + } + if (has_telegram_payment_charge_id) { + store(m->telegram_payment_charge_id, storer); + } + if (has_provider_payment_charge_id) { + store(m->provider_payment_charge_id, storer); + } + if (has_invoice_message_id) { + store(m->invoice_message_id, storer); + } + break; + } + case MessageContentType::ContactRegistered: + break; + case MessageContentType::ExpiredPhoto: + break; + case MessageContentType::ExpiredVideo: + break; + case MessageContentType::CustomServiceAction: { + auto m = static_cast(content); + store(m->message, storer); + break; + } + case MessageContentType::WebsiteConnected: { + auto m = static_cast(content); + store(m->domain_name, storer); + break; + } + case MessageContentType::PassportDataSent: { + auto m = static_cast(content); + store(m->types, storer); + break; + } + case MessageContentType::PassportDataReceived: { + auto m = static_cast(content); + store(m->values, storer); + store(m->credentials, storer); + break; + } + default: + UNREACHABLE(); + } +} + +template +static void parse_caption(FormattedText &caption, ParserT &parser) { + parse(caption.text, parser); + if (parser.version() >= static_cast(Version::AddCaptionEntities)) { + parse(caption.entities, parser); + } else { + caption.entities = find_entities(caption.text, false); + } +} + +template +static void parse(unique_ptr &content, ParserT &parser) { + Td *td = parser.context()->td().get_actor_unsafe(); + CHECK(td != nullptr); + + MessageContentType content_type; + parse(content_type, parser); + + bool is_bad = false; + switch (content_type) { + case MessageContentType::Animation: { + auto m = make_unique(); + m->file_id = td->animations_manager_->parse_animation(parser); + parse_caption(m->caption, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::Audio: { + auto m = make_unique(); + m->file_id = td->audios_manager_->parse_audio(parser); + parse_caption(m->caption, parser); + bool legacy_is_listened; + parse(legacy_is_listened, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::Contact: { + auto m = make_unique(); + parse(m->contact, parser); + content = std::move(m); + break; + } + case MessageContentType::Document: { + auto m = make_unique(); + m->file_id = td->documents_manager_->parse_document(parser); + parse_caption(m->caption, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::Game: { + auto m = make_unique(); + parse(m->game, parser); + content = std::move(m); + break; + } + case MessageContentType::Invoice: { + auto m = make_unique(); + parse(m->title, parser); + parse(m->description, parser); + parse(m->photo, parser); + parse(m->start_parameter, parser); + parse(m->invoice, parser); + parse(m->payload, parser); + parse(m->provider_token, parser); + if (parser.version() >= static_cast(Version::AddMessageInvoiceProviderData)) { + parse(m->provider_data, parser); + } else { + m->provider_data.clear(); + } + parse(m->total_amount, parser); + parse(m->receipt_message_id, parser); + content = std::move(m); + break; + } + case MessageContentType::LiveLocation: { + auto m = make_unique(); + parse(m->location, parser); + parse(m->period, parser); + content = std::move(m); + break; + } + case MessageContentType::Location: { + auto m = make_unique(); + parse(m->location, parser); + content = std::move(m); + break; + } + case MessageContentType::Photo: { + auto m = make_unique(); + parse(m->photo, parser); + for (auto &photo_size : m->photo.photos) { + if (!photo_size.file_id.is_valid()) { + is_bad = true; + } + } + parse_caption(m->caption, parser); + content = std::move(m); + break; + } + case MessageContentType::Sticker: { + auto m = make_unique(); + m->file_id = td->stickers_manager_->parse_sticker(false, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::Text: { + auto m = make_unique(); + parse(m->text, parser); + parse(m->web_page_id, parser); + content = std::move(m); + break; + } + case MessageContentType::Unsupported: + content = make_unique(); + break; + case MessageContentType::Venue: { + auto m = make_unique(); + parse(m->venue, parser); + content = std::move(m); + break; + } + case MessageContentType::Video: { + auto m = make_unique(); + m->file_id = td->videos_manager_->parse_video(parser); + parse_caption(m->caption, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::VideoNote: { + auto m = make_unique(); + m->file_id = td->video_notes_manager_->parse_video_note(parser); + parse(m->is_viewed, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::VoiceNote: { + auto m = make_unique(); + m->file_id = td->voice_notes_manager_->parse_voice_note(parser); + parse_caption(m->caption, parser); + parse(m->is_listened, parser); + is_bad = !m->file_id.is_valid(); + content = std::move(m); + break; + } + case MessageContentType::ChatCreate: { + auto m = make_unique(); + parse(m->title, parser); + parse(m->participant_user_ids, parser); + content = std::move(m); + break; + } + case MessageContentType::ChatChangeTitle: { + auto m = make_unique(); + parse(m->title, parser); + content = std::move(m); + break; + } + case MessageContentType::ChatChangePhoto: { + auto m = make_unique(); + parse(m->photo, parser); + content = std::move(m); + break; + } + case MessageContentType::ChatDeletePhoto: + content = make_unique(); + break; + case MessageContentType::ChatDeleteHistory: + content = make_unique(); + break; + case MessageContentType::ChatAddUsers: { + auto m = make_unique(); + parse(m->user_ids, parser); + content = std::move(m); + break; + } + case MessageContentType::ChatJoinedByLink: + content = make_unique(); + break; + case MessageContentType::ChatDeleteUser: { + auto m = make_unique(); + parse(m->user_id, parser); + content = std::move(m); + break; + } + case MessageContentType::ChatMigrateTo: { + auto m = make_unique(); + parse(m->migrated_to_channel_id, parser); + content = std::move(m); + break; + } + case MessageContentType::ChannelCreate: { + auto m = make_unique(); + parse(m->title, parser); + content = std::move(m); + break; + } + case MessageContentType::ChannelMigrateFrom: { + auto m = make_unique(); + parse(m->title, parser); + parse(m->migrated_from_chat_id, parser); + content = std::move(m); + break; + } + case MessageContentType::PinMessage: { + auto m = make_unique(); + parse(m->message_id, parser); + content = std::move(m); + break; + } + case MessageContentType::GameScore: { + auto m = make_unique(); + parse(m->game_message_id, parser); + parse(m->game_id, parser); + parse(m->score, parser); + content = std::move(m); + break; + } + case MessageContentType::ScreenshotTaken: + content = make_unique(); + break; + case MessageContentType::ChatSetTtl: { + auto m = make_unique(); + parse(m->ttl, parser); + content = std::move(m); + break; + } + case MessageContentType::Call: { + auto m = make_unique(); + parse(m->call_id, parser); + parse(m->duration, parser); + parse(m->discard_reason, parser); + content = std::move(m); + break; + } + case MessageContentType::PaymentSuccessful: { + auto m = make_unique(); + bool has_payload; + bool has_shipping_option_id; + bool has_order_info; + bool has_telegram_payment_charge_id; + bool has_provider_payment_charge_id; + bool has_invoice_message_id; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_payload); + PARSE_FLAG(has_shipping_option_id); + PARSE_FLAG(has_order_info); + PARSE_FLAG(has_telegram_payment_charge_id); + PARSE_FLAG(has_provider_payment_charge_id); + PARSE_FLAG(has_invoice_message_id); + END_PARSE_FLAGS(); + parse(m->currency, parser); + parse(m->total_amount, parser); + if (has_payload) { + parse(m->total_amount, parser); + } + if (has_shipping_option_id) { + parse(m->invoice_payload, parser); + } + if (has_order_info) { + m->order_info = make_unique(); + parse(*m->order_info, parser); + } + if (has_telegram_payment_charge_id) { + parse(m->telegram_payment_charge_id, parser); + } + if (has_provider_payment_charge_id) { + parse(m->provider_payment_charge_id, parser); + } + if (has_invoice_message_id) { + parse(m->invoice_message_id, parser); + } + content = std::move(m); + break; + } + case MessageContentType::ContactRegistered: + content = make_unique(); + break; + case MessageContentType::ExpiredPhoto: + content = make_unique(); + break; + case MessageContentType::ExpiredVideo: + content = make_unique(); + break; + case MessageContentType::CustomServiceAction: { + auto m = make_unique(); + parse(m->message, parser); + content = std::move(m); + break; + } + case MessageContentType::WebsiteConnected: { + auto m = make_unique(); + parse(m->domain_name, parser); + content = std::move(m); + break; + } + case MessageContentType::PassportDataSent: { + auto m = make_unique(); + parse(m->types, parser); + content = std::move(m); + break; + } + case MessageContentType::PassportDataReceived: { + auto m = make_unique(); + parse(m->values, parser); + parse(m->credentials, parser); + content = std::move(m); + break; + } + default: + UNREACHABLE(); + } + if (is_bad) { + LOG(ERROR) << "Load a message with an invalid content of type " << content_type; + content = make_unique(); + } +} + +void store_message_content(const MessageContent *content, LogEventStorerCalcLength &storer) { + store(content, storer); +} + +void store_message_content(const MessageContent *content, LogEventStorerUnsafe &storer) { + store(content, storer); +} + +void parse_message_content(unique_ptr &content, LogEventParser &parser) { + parse(content, parser); +} + +InlineMessageContent create_inline_message_content(Td *td, FileId file_id, + tl_object_ptr &&inline_message, + int32 allowed_media_content_id, Photo *photo, Game *game) { + CHECK(inline_message != nullptr); + CHECK((allowed_media_content_id == td_api::inputMessagePhoto::ID) == (photo != nullptr)); + CHECK((allowed_media_content_id == td_api::inputMessageGame::ID) == (game != nullptr)); + CHECK((allowed_media_content_id != td_api::inputMessagePhoto::ID && + allowed_media_content_id != td_api::inputMessageGame::ID && allowed_media_content_id != -1) == + file_id.is_valid()); + + InlineMessageContent result; + tl_object_ptr reply_markup; + result.disable_web_page_preview = false; + switch (inline_message->get_id()) { + case telegram_api::botInlineMessageText::ID: { + auto inline_message_text = move_tl_object_as(inline_message); + auto entities = get_message_entities(td->contacts_manager_.get(), std::move(inline_message_text->entities_), + "botInlineMessageText"); + auto status = fix_formatted_text(inline_message_text->message_, entities, false, true, true, false); + if (status.is_error()) { + LOG(ERROR) << "Receive error " << status << " while parsing botInlineMessageText " + << inline_message_text->message_; + break; + } + + result.disable_web_page_preview = + (inline_message_text->flags_ & telegram_api::botInlineMessageText::NO_WEBPAGE_MASK) != 0; + WebPageId web_page_id; + if (!result.disable_web_page_preview) { + web_page_id = + td->web_pages_manager_->get_web_page_by_url(get_first_url(inline_message_text->message_, entities)); + } + result.message_content = make_unique( + FormattedText{std::move(inline_message_text->message_), std::move(entities)}, web_page_id); + reply_markup = std::move(inline_message_text->reply_markup_); + break; + } + case telegram_api::botInlineMessageMediaGeo::ID: { + auto inline_message_geo = move_tl_object_as(inline_message); + if (inline_message_geo->period_ > 0) { + result.message_content = + make_unique(Location(inline_message_geo->geo_), inline_message_geo->period_); + } else { + result.message_content = make_unique(Location(inline_message_geo->geo_)); + } + reply_markup = std::move(inline_message_geo->reply_markup_); + break; + } + case telegram_api::botInlineMessageMediaVenue::ID: { + auto inline_message_venue = move_tl_object_as(inline_message); + result.message_content = make_unique( + Venue(inline_message_venue->geo_, std::move(inline_message_venue->title_), + std::move(inline_message_venue->address_), std::move(inline_message_venue->provider_), + std::move(inline_message_venue->venue_id_), std::move(inline_message_venue->venue_type_))); + reply_markup = std::move(inline_message_venue->reply_markup_); + break; + } + case telegram_api::botInlineMessageMediaContact::ID: { + auto inline_message_contact = move_tl_object_as(inline_message); + result.message_content = make_unique( + Contact(std::move(inline_message_contact->phone_number_), std::move(inline_message_contact->first_name_), + std::move(inline_message_contact->last_name_), std::move(inline_message_contact->vcard_), 0)); + reply_markup = std::move(inline_message_contact->reply_markup_); + break; + } + case telegram_api::botInlineMessageMediaAuto::ID: { + auto input_message_media_auto = move_tl_object_as(inline_message); + auto caption = + get_message_text(td->contacts_manager_.get(), input_message_media_auto->message_, + std::move(input_message_media_auto->entities_), 0, "register_inline_message_content"); + if (allowed_media_content_id == td_api::inputMessageAnimation::ID) { + result.message_content = make_unique(file_id, std::move(caption)); + } else if (allowed_media_content_id == td_api::inputMessageAudio::ID) { + result.message_content = make_unique(file_id, std::move(caption)); + } else if (allowed_media_content_id == td_api::inputMessageDocument::ID) { + result.message_content = make_unique(file_id, std::move(caption)); + } else if (allowed_media_content_id == td_api::inputMessageGame::ID) { + CHECK(game != nullptr); + // TODO game->set_short_name(std::move(caption)); + result.message_content = make_unique(std::move(*game)); + } else if (allowed_media_content_id == td_api::inputMessagePhoto::ID) { + result.message_content = make_unique(std::move(*photo), std::move(caption)); + } else if (allowed_media_content_id == td_api::inputMessageSticker::ID) { + result.message_content = make_unique(file_id); + } else if (allowed_media_content_id == td_api::inputMessageVideo::ID) { + result.message_content = make_unique(file_id, std::move(caption)); + } else if (allowed_media_content_id == td_api::inputMessageVoiceNote::ID) { + result.message_content = make_unique(file_id, std::move(caption), true); + } else { + LOG(WARNING) << "Unallowed bot inline message " << to_string(input_message_media_auto); + } + + reply_markup = std::move(input_message_media_auto->reply_markup_); + break; + } + default: + UNREACHABLE(); + } + result.message_reply_markup = get_reply_markup(std::move(reply_markup), td->auth_manager_->is_bot(), true, false); + return result; +} + +unique_ptr create_text_message_content(string text, vector entities, + WebPageId web_page_id) { + return make_unique(FormattedText{std::move(text), std::move(entities)}, web_page_id); +} + +unique_ptr create_contact_registered_message_content() { + return make_unique(); +} + +unique_ptr create_screenshot_taken_message_content() { + return make_unique(); +} + +unique_ptr create_chat_set_ttl_message_content(int32 ttl) { + return make_unique(ttl); +} + +Result create_input_message_content( + DialogId dialog_id, tl_object_ptr &&input_message_content, Td *td, + FormattedText caption, FileId file_id, PhotoSize thumbnail, vector sticker_file_ids) { + CHECK(input_message_content != nullptr); + LOG(INFO) << "Create InputMessageContent with file " << file_id << " and thumbnail " << thumbnail.file_id; + + FileView file_view; + string file_name; + string mime_type; + if (file_id.is_valid()) { + file_view = td->file_manager_->get_file_view(file_id); + auto suggested_name = file_view.suggested_name(); + const PathView path_view(suggested_name); + file_name = path_view.file_name().str(); + mime_type = MimeType::from_extension(path_view.extension()); + } + + bool disable_web_page_preview = false; + bool clear_draft = false; + unique_ptr content; + UserId via_bot_user_id; + int32 ttl = 0; + bool is_bot = td->auth_manager_->is_bot(); + switch (input_message_content->get_id()) { + case td_api::inputMessageText::ID: { + TRY_RESULT(input_message_text, process_input_message_text(td->contacts_manager_.get(), dialog_id, + std::move(input_message_content), is_bot)); + disable_web_page_preview = input_message_text.disable_web_page_preview; + clear_draft = input_message_text.clear_draft; + + WebPageId web_page_id; + bool can_add_web_page_previews = + dialog_id.get_type() != DialogType::Channel || + td->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_add_web_page_previews(); + if (!is_bot && !disable_web_page_preview && can_add_web_page_previews) { + web_page_id = td->web_pages_manager_->get_web_page_by_url( + get_first_url(input_message_text.text.text, input_message_text.text.entities)); + } + content = make_unique(std::move(input_message_text.text), web_page_id); + break; + } + case td_api::inputMessageAnimation::ID: { + auto input_animation = static_cast(input_message_content.get()); + + td->animations_manager_->create_animation( + file_id, thumbnail, std::move(file_name), std::move(mime_type), input_animation->duration_, + get_dimensions(input_animation->width_, input_animation->height_), false); + + content = make_unique(file_id, std::move(caption)); + break; + } + case td_api::inputMessageAudio::ID: { + auto input_audio = static_cast(input_message_content.get()); + + if (!clean_input_string(input_audio->title_)) { + return Status::Error(400, "Audio title must be encoded in UTF-8"); + } + if (!clean_input_string(input_audio->performer_)) { + return Status::Error(400, "Audio performer must be encoded in UTF-8"); + } + + td->audios_manager_->create_audio(file_id, thumbnail, std::move(file_name), std::move(mime_type), + input_audio->duration_, std::move(input_audio->title_), + std::move(input_audio->performer_), false); + + content = make_unique(file_id, std::move(caption)); + break; + } + case td_api::inputMessageDocument::ID: + td->documents_manager_->create_document(file_id, thumbnail, std::move(file_name), std::move(mime_type), false); + + content = make_unique(file_id, std::move(caption)); + break; + case td_api::inputMessagePhoto::ID: { + auto input_photo = static_cast(input_message_content.get()); + + if (input_photo->width_ < 0 || input_photo->width_ > 10000) { + return Status::Error(400, "Wrong photo width"); + } + if (input_photo->height_ < 0 || input_photo->height_ > 10000) { + return Status::Error(400, "Wrong photo height"); + } + ttl = input_photo->ttl_; + + auto message_photo = make_unique(); + + if (file_view.has_remote_location() && !file_view.remote_location().is_web()) { + message_photo->photo.id = file_view.remote_location().get_id(); + } + message_photo->photo.date = G()->unix_time(); + + PhotoSize s; + s.type = 'i'; + s.dimensions = get_dimensions(input_photo->width_, input_photo->height_); + s.size = static_cast(file_view.size()); + s.file_id = file_id; + + if (thumbnail.file_id.is_valid()) { + message_photo->photo.photos.push_back(std::move(thumbnail)); + } + + message_photo->photo.photos.push_back(s); + + message_photo->photo.has_stickers = !sticker_file_ids.empty(); + message_photo->photo.sticker_file_ids = std::move(sticker_file_ids); + + message_photo->caption = std::move(caption); + + content = std::move(message_photo); + break; + } + case td_api::inputMessageSticker::ID: { + auto input_sticker = static_cast(input_message_content.get()); + td->stickers_manager_->create_sticker( + file_id, thumbnail, get_dimensions(input_sticker->width_, input_sticker->height_), true, nullptr, nullptr); + + content = make_unique(file_id); + break; + } + case td_api::inputMessageVideo::ID: { + auto input_video = static_cast(input_message_content.get()); + + ttl = input_video->ttl_; + + bool has_stickers = !sticker_file_ids.empty(); + td->videos_manager_->create_video(file_id, thumbnail, has_stickers, std::move(sticker_file_ids), + std::move(file_name), std::move(mime_type), input_video->duration_, + get_dimensions(input_video->width_, input_video->height_), + input_video->supports_streaming_, false); + + content = make_unique(file_id, std::move(caption)); + break; + } + case td_api::inputMessageVideoNote::ID: { + auto input_video_note = static_cast(input_message_content.get()); + + auto length = input_video_note->length_; + if (length < 0 || length >= 640) { + return Status::Error(400, "Wrong video note length"); + } + + td->video_notes_manager_->create_video_note(file_id, thumbnail, input_video_note->duration_, + get_dimensions(length, length), false); + + content = make_unique(file_id, false); + break; + } + case td_api::inputMessageVoiceNote::ID: { + auto input_voice_note = static_cast(input_message_content.get()); + + td->voice_notes_manager_->create_voice_note(file_id, std::move(mime_type), input_voice_note->duration_, + std::move(input_voice_note->waveform_), false); + + content = make_unique(file_id, std::move(caption), false); + break; + } + case td_api::inputMessageLocation::ID: { + TRY_RESULT(location, process_input_message_location(std::move(input_message_content))); + if (location.second == 0) { + content = make_unique(std::move(location.first)); + } else { + content = make_unique(std::move(location.first), location.second); + } + break; + } + case td_api::inputMessageVenue::ID: { + TRY_RESULT(venue, process_input_message_venue(std::move(input_message_content))); + content = make_unique(std::move(venue)); + break; + } + case td_api::inputMessageContact::ID: { + TRY_RESULT(contact, process_input_message_contact(std::move(input_message_content))); + content = make_unique(std::move(contact)); + break; + } + case td_api::inputMessageGame::ID: { + TRY_RESULT(game, process_input_message_game(td->contacts_manager_.get(), std::move(input_message_content))); + via_bot_user_id = game.get_bot_user_id(); + if (via_bot_user_id == td->contacts_manager_->get_my_id("send_message")) { + via_bot_user_id = UserId(); + } + + content = make_unique(std::move(game)); + break; + } + case td_api::inputMessageInvoice::ID: { + if (!is_bot) { + return Status::Error(400, "Invoices can be sent only by bots"); + } + + auto input_message_invoice = move_tl_object_as(input_message_content); + if (!clean_input_string(input_message_invoice->title_)) { + return Status::Error(400, "Invoice title must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->description_)) { + return Status::Error(400, "Invoice description must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->photo_url_)) { + return Status::Error(400, "Invoice photo URL must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->start_parameter_)) { + return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->provider_token_)) { + return Status::Error(400, "Invoice provider token must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->provider_data_)) { + return Status::Error(400, "Invoice provider data must be encoded in UTF-8"); + } + if (!clean_input_string(input_message_invoice->invoice_->currency_)) { + return Status::Error(400, "Invoice currency must be encoded in UTF-8"); + } + + auto message_invoice = make_unique(); + message_invoice->title = std::move(input_message_invoice->title_); + message_invoice->description = std::move(input_message_invoice->description_); + + auto r_http_url = parse_url(input_message_invoice->photo_url_); + if (r_http_url.is_error()) { + if (!input_message_invoice->photo_url_.empty()) { + LOG(INFO) << "Can't register url " << input_message_invoice->photo_url_; + } + message_invoice->photo.id = -2; + } else { + auto url = r_http_url.ok().get_url(); + auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp); + if (r_invoice_file_id.is_error()) { + LOG(INFO) << "Can't register url " << url; + message_invoice->photo.id = -2; + } else { + auto invoice_file_id = r_invoice_file_id.move_as_ok(); + + PhotoSize s; + s.type = 'u'; + s.dimensions = get_dimensions(input_message_invoice->photo_width_, input_message_invoice->photo_height_); + s.size = input_message_invoice->photo_size_; // TODO use invoice_file_id size + s.file_id = invoice_file_id; + + message_invoice->photo.photos.push_back(s); + } + } + message_invoice->start_parameter = std::move(input_message_invoice->start_parameter_); + + message_invoice->invoice.currency = std::move(input_message_invoice->invoice_->currency_); + message_invoice->invoice.price_parts.reserve(input_message_invoice->invoice_->price_parts_.size()); + int64 total_amount = 0; + const int64 MAX_AMOUNT = 9999'9999'9999; + for (auto &price : input_message_invoice->invoice_->price_parts_) { + if (!clean_input_string(price->label_)) { + return Status::Error(400, "Invoice price label must be encoded in UTF-8"); + } + message_invoice->invoice.price_parts.emplace_back(std::move(price->label_), price->amount_); + if (price->amount_ < -MAX_AMOUNT || price->amount_ > MAX_AMOUNT) { + return Status::Error(400, "Too big amount of currency specified"); + } + total_amount += price->amount_; + } + if (total_amount <= 0) { + return Status::Error(400, "Total price must be positive"); + } + if (total_amount > MAX_AMOUNT) { + return Status::Error(400, "Total price is too big"); + } + message_invoice->total_amount = total_amount; + + message_invoice->invoice.is_test = input_message_invoice->invoice_->is_test_; + message_invoice->invoice.need_name = input_message_invoice->invoice_->need_name_; + message_invoice->invoice.need_phone_number = input_message_invoice->invoice_->need_phone_number_; + message_invoice->invoice.need_email_address = input_message_invoice->invoice_->need_email_address_; + message_invoice->invoice.need_shipping_address = input_message_invoice->invoice_->need_shipping_address_; + message_invoice->invoice.send_phone_number_to_provider = + input_message_invoice->invoice_->send_phone_number_to_provider_; + message_invoice->invoice.send_email_address_to_provider = + input_message_invoice->invoice_->send_email_address_to_provider_; + message_invoice->invoice.is_flexible = input_message_invoice->invoice_->is_flexible_; + if (message_invoice->invoice.send_phone_number_to_provider) { + message_invoice->invoice.need_phone_number = true; + } + if (message_invoice->invoice.send_email_address_to_provider) { + message_invoice->invoice.need_email_address = true; + } + if (message_invoice->invoice.is_flexible) { + message_invoice->invoice.need_shipping_address = true; + } + + message_invoice->payload = std::move(input_message_invoice->payload_); + message_invoice->provider_token = std::move(input_message_invoice->provider_token_); + message_invoice->provider_data = std::move(input_message_invoice->provider_data_); + + content = std::move(message_invoice); + break; + } + default: + UNREACHABLE(); + } + return InputMessageContent{std::move(content), disable_web_page_preview, clear_draft, ttl, via_bot_user_id}; +} + +SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td, + tl_object_ptr input_file, + BufferSlice thumbnail, int32 layer) { + switch (content->get_type()) { + case MessageContentType::Animation: { + auto m = static_cast(content); + return td->animations_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, + std::move(thumbnail)); + } + case MessageContentType::Audio: { + auto m = static_cast(content); + return td->audios_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, + std::move(thumbnail)); + } + case MessageContentType::Contact: { + auto m = static_cast(content); + return m->contact.get_secret_input_media_contact(); + } + case MessageContentType::Document: { + auto m = static_cast(content); + return td->documents_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, + std::move(thumbnail)); + } + case MessageContentType::Location: { + auto m = static_cast(content); + return m->location.get_secret_input_media_geo_point(); + } + case MessageContentType::Photo: { + auto m = static_cast(content); + return photo_get_secret_input_media(td->file_manager_.get(), m->photo, std::move(input_file), m->caption.text, + std::move(thumbnail)); + } + case MessageContentType::Sticker: { + auto m = static_cast(content); + return td->stickers_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail)); + } + case MessageContentType::Text: { + CHECK(input_file == nullptr); + CHECK(thumbnail.empty()); + auto m = static_cast(content); + return td->web_pages_manager_->get_secret_input_media(m->web_page_id); + } + case MessageContentType::Venue: { + auto m = static_cast(content); + return m->venue.get_secret_input_media_venue(); + } + case MessageContentType::Video: { + auto m = static_cast(content); + return td->videos_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, + std::move(thumbnail)); + } + case MessageContentType::VideoNote: { + auto m = static_cast(content); + return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail), + layer); + } + case MessageContentType::VoiceNote: { + auto m = static_cast(content); + return td->voice_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text); + } + case MessageContentType::LiveLocation: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::Unsupported: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + break; + default: + UNREACHABLE(); + } + return SecretInputMedia{}; +} + +static tl_object_ptr get_input_invoice(const Invoice &invoice) { + int32 flags = 0; + if (invoice.is_test) { + flags |= telegram_api::invoice::TEST_MASK; + } + if (invoice.need_name) { + flags |= telegram_api::invoice::NAME_REQUESTED_MASK; + } + if (invoice.need_phone_number) { + flags |= telegram_api::invoice::PHONE_REQUESTED_MASK; + } + if (invoice.need_email_address) { + flags |= telegram_api::invoice::EMAIL_REQUESTED_MASK; + } + if (invoice.need_shipping_address) { + flags |= telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK; + } + if (invoice.send_phone_number_to_provider) { + flags |= telegram_api::invoice::PHONE_TO_PROVIDER_MASK; + } + if (invoice.send_email_address_to_provider) { + flags |= telegram_api::invoice::EMAIL_TO_PROVIDER_MASK; + } + if (invoice.is_flexible) { + flags |= telegram_api::invoice::FLEXIBLE_MASK; + } + + auto prices = transform(invoice.price_parts, [](const LabeledPricePart &price) { + return telegram_api::make_object(price.label, price.amount); + }); + return make_tl_object( + flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, false /*ignored*/, invoice.currency, std::move(prices)); +} + +static tl_object_ptr get_input_web_document(const FileManager *file_manager, + const Photo &photo) { + if (photo.id == -2) { + return nullptr; + } + + CHECK(photo.photos.size() == 1); + const PhotoSize &size = photo.photos[0]; + CHECK(size.file_id.is_valid()); + + vector> attributes; + if (size.dimensions.width != 0 && size.dimensions.height != 0) { + attributes.push_back( + make_tl_object(size.dimensions.width, size.dimensions.height)); + } + + auto file_view = file_manager->get_file_view(size.file_id); + CHECK(file_view.has_url()); + + auto file_name = get_url_file_name(file_view.url()); + return make_tl_object( + file_view.url(), size.size, MimeType::from_extension(PathView(file_name).extension(), "image/jpeg"), + std::move(attributes)); +} + +static tl_object_ptr get_input_media_invoice(const FileManager *file_manager, + const MessageInvoice *message_invoice) { + CHECK(message_invoice != nullptr); + int32 flags = 0; + auto input_web_document = get_input_web_document(file_manager, message_invoice->photo); + if (input_web_document != nullptr) { + flags |= telegram_api::inputMediaInvoice::PHOTO_MASK; + } + + return make_tl_object( + flags, message_invoice->title, message_invoice->description, std::move(input_web_document), + get_input_invoice(message_invoice->invoice), BufferSlice(message_invoice->payload), + message_invoice->provider_token, + telegram_api::make_object( + message_invoice->provider_data.empty() ? "null" : message_invoice->provider_data), + message_invoice->start_parameter); +} + +tl_object_ptr get_input_media(const MessageContent *content, Td *td, + tl_object_ptr input_file, + tl_object_ptr input_thumbnail, + int32 ttl) { + switch (content->get_type()) { + case MessageContentType::Animation: { + auto m = static_cast(content); + return td->animations_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); + } + case MessageContentType::Audio: { + auto m = static_cast(content); + return td->audios_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); + } + case MessageContentType::Contact: { + auto m = static_cast(content); + return m->contact.get_input_media_contact(); + } + case MessageContentType::Document: { + auto m = static_cast(content); + return td->documents_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); + } + case MessageContentType::Game: { + auto m = static_cast(content); + return m->game.get_input_media_game(td); + } + case MessageContentType::Invoice: { + auto m = static_cast(content); + return get_input_media_invoice(td->file_manager_.get(), m); + } + case MessageContentType::LiveLocation: { + auto m = static_cast(content); + return make_tl_object(m->location.get_input_geo_point(), m->period); + } + case MessageContentType::Location: { + auto m = static_cast(content); + return m->location.get_input_media_geo_point(); + } + case MessageContentType::Photo: { + auto m = static_cast(content); + return photo_get_input_media(td->file_manager_.get(), m->photo, std::move(input_file), ttl); + } + case MessageContentType::Sticker: { + auto m = static_cast(content); + return td->stickers_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); + } + case MessageContentType::Venue: { + auto m = static_cast(content); + return m->venue.get_input_media_venue(); + } + case MessageContentType::Video: { + auto m = static_cast(content); + return td->videos_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail), ttl); + } + case MessageContentType::VideoNote: { + auto m = static_cast(content); + return td->video_notes_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); + } + case MessageContentType::VoiceNote: { + auto m = static_cast(content); + return td->voice_notes_manager_->get_input_media(m->file_id, std::move(input_file)); + } + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + break; + default: + UNREACHABLE(); + } + return nullptr; +} + +void delete_message_content_thumbnail(MessageContent *content, Td *td) { + switch (content->get_type()) { + case MessageContentType::Animation: { + auto m = static_cast(content); + return td->animations_manager_->delete_animation_thumbnail(m->file_id); + } + case MessageContentType::Audio: { + auto m = static_cast(content); + return td->audios_manager_->delete_audio_thumbnail(m->file_id); + } + case MessageContentType::Document: { + auto m = static_cast(content); + return td->documents_manager_->delete_document_thumbnail(m->file_id); + } + case MessageContentType::Photo: { + auto m = static_cast(content); + return photo_delete_thumbnail(m->photo); + } + case MessageContentType::Sticker: { + auto m = static_cast(content); + return td->stickers_manager_->delete_sticker_thumbnail(m->file_id); + } + case MessageContentType::Video: { + auto m = static_cast(content); + return td->videos_manager_->delete_video_thumbnail(m->file_id); + } + case MessageContentType::VideoNote: { + auto m = static_cast(content); + return td->video_notes_manager_->delete_video_note_thumbnail(m->file_id); + } + case MessageContentType::Contact: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Venue: + case MessageContentType::VoiceNote: + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + break; + default: + UNREACHABLE(); + } +} + +bool is_allowed_media_group_content(MessageContentType content_type) { + switch (content_type) { + case MessageContentType::Photo: + case MessageContentType::Video: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + return true; + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Contact: + case MessageContentType::Document: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Sticker: + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return false; + default: + UNREACHABLE(); + return false; + } +} + +bool can_forward_message_content(const MessageContent *content) { + auto content_type = content->get_type(); + if (content_type == MessageContentType::Text) { + auto *text = static_cast(content); + return !is_empty_string(text->text.text); // text can't be empty in the new message + } + + return !is_service_message_content(content_type) && content_type != MessageContentType::Unsupported && + content_type != MessageContentType::ExpiredPhoto && content_type != MessageContentType::ExpiredVideo; +} + +bool is_secret_message_content(int32 ttl, MessageContentType content_type) { + if (ttl <= 0 || ttl > 60) { + return false; + } + switch (content_type) { + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Photo: + case MessageContentType::Video: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: + return true; + case MessageContentType::Contact: + case MessageContentType::Document: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Sticker: + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return false; + default: + UNREACHABLE(); + return false; + } +} + +bool is_service_message_content(MessageContentType content_type) { + switch (content_type) { + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Contact: + case MessageContentType::Document: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Photo: + case MessageContentType::Sticker: + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::Video: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + return false; + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return true; + default: + UNREACHABLE(); + return false; + } +} + +bool can_have_message_content_caption(MessageContentType content_type) { + switch (content_type) { + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Document: + case MessageContentType::Photo: + case MessageContentType::Video: + case MessageContentType::VoiceNote: + return true; + case MessageContentType::Contact: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Sticker: + case MessageContentType::Text: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::VideoNote: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return false; + default: + UNREACHABLE(); + return false; + } +} + +bool update_opened_message_content(MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::VideoNote: { + auto video_note_content = static_cast(content); + if (video_note_content->is_viewed) { + return false; + } + video_note_content->is_viewed = true; + return true; + } + case MessageContentType::VoiceNote: { + auto voice_note_content = static_cast(content); + if (voice_note_content->is_listened) { + return false; + } + voice_note_content->is_listened = true; + return true; + } + default: + return false; + } +} + +int32 get_message_content_index_mask(const MessageContent *content, const Td *td, bool is_secret, bool is_outgoing) { + switch (content->get_type()) { + case MessageContentType::Animation: + return search_messages_filter_index_mask(SearchMessagesFilter::Animation); + case MessageContentType::Audio: { + auto message_audio = static_cast(content); + auto duration = td->audios_manager_->get_audio_duration(message_audio->file_id); + return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::Audio) + : search_messages_filter_index_mask(SearchMessagesFilter::Document); + } + case MessageContentType::Document: + return search_messages_filter_index_mask(SearchMessagesFilter::Document); + case MessageContentType::Photo: + return search_messages_filter_index_mask(SearchMessagesFilter::Photo) | + search_messages_filter_index_mask(SearchMessagesFilter::PhotoAndVideo); + case MessageContentType::Text: + for (auto &entity : static_cast(content)->text.entities) { + if (entity.type == MessageEntity::Type::Url || entity.type == MessageEntity::Type::EmailAddress || + entity.type == MessageEntity::Type::TextUrl) { + return search_messages_filter_index_mask(SearchMessagesFilter::Url); + } + } + return 0; + case MessageContentType::Video: { + auto message_video = static_cast(content); + auto duration = td->videos_manager_->get_video_duration(message_video->file_id); + return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::Video) | + search_messages_filter_index_mask(SearchMessagesFilter::PhotoAndVideo) + : search_messages_filter_index_mask(SearchMessagesFilter::Document); + } + case MessageContentType::VideoNote: { + auto message_video_note = static_cast(content); + auto duration = td->video_notes_manager_->get_video_note_duration(message_video_note->file_id); + return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::VideoNote) | + search_messages_filter_index_mask(SearchMessagesFilter::VoiceAndVideoNote) + : search_messages_filter_index_mask(SearchMessagesFilter::Document); + } + case MessageContentType::VoiceNote: + return search_messages_filter_index_mask(SearchMessagesFilter::VoiceNote) | + search_messages_filter_index_mask(SearchMessagesFilter::VoiceAndVideoNote); + case MessageContentType::ChatChangePhoto: + return search_messages_filter_index_mask(SearchMessagesFilter::ChatPhoto); + case MessageContentType::Call: { + int32 index_mask = search_messages_filter_index_mask(SearchMessagesFilter::Call); + auto message_call = static_cast(content); + if (!is_outgoing && (message_call->discard_reason == CallDiscardReason::Declined || + message_call->discard_reason == CallDiscardReason::Missed)) { + index_mask |= search_messages_filter_index_mask(SearchMessagesFilter::MissedCall); + } + return index_mask; + } + case MessageContentType::Contact: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Sticker: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return 0; + default: + UNREACHABLE(); + return 0; + } + return 0; +} + +int32 get_message_content_new_participant_count(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::ChatAddUsers: + return narrow_cast(static_cast(content)->user_ids.size()); + case MessageContentType::ChatJoinedByLink: + return 1; + case MessageContentType::ChatDeleteUser: + return -1; + default: + return 0; + } +} + +MessageId get_message_content_pinned_message_id(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::PinMessage: + return static_cast(content)->message_id; + default: + return MessageId(); + } +} + +MessageId get_message_content_replied_message_id(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::PinMessage: + return static_cast(content)->message_id; + case MessageContentType::GameScore: + return static_cast(content)->game_message_id; + case MessageContentType::PaymentSuccessful: + return static_cast(content)->invoice_message_id; + default: + return MessageId(); + } +} + +UserId get_message_content_deleted_user_id(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::ChatDeleteUser: + return static_cast(content)->user_id; + default: + return UserId(); + } +} + +int32 get_message_content_live_location_period(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::LiveLocation: + return static_cast(content)->period; + default: + return 0; + } +} + +WebPageId get_message_content_web_page_id(const MessageContent *content) { + if (content->get_type() == MessageContentType::Text) { + return static_cast(content)->web_page_id; + } + return WebPageId(); +} + +void set_message_content_web_page_id(MessageContent *content, WebPageId web_page_id) { + CHECK(content->get_type() == MessageContentType::Text); + static_cast(content)->web_page_id = web_page_id; +} + +static void merge_location_access_hash(Location &first, Location &second) { + if (second.get_access_hash() != 0) { + first.set_access_hash(second.get_access_hash()); + } else { + second.set_access_hash(first.get_access_hash()); + } +} + +static bool need_message_text_changed_warning(const MessageText *old_content, const MessageText *new_content) { + if (new_content->text.text == "Unsupported characters" || + new_content->text.text == "This channel is blocked because it was used to spread pornographic content.") { + // message contained unsupported characters, text is replaced + return false; + } + if (/* old_message->message_id.is_yet_unsent() && */ !old_content->text.entities.empty() && + old_content->text.entities[0].offset == 0 && + (new_content->text.entities.empty() || new_content->text.entities[0].offset != 0) && + old_content->text.text != new_content->text.text && ends_with(old_content->text.text, new_content->text.text)) { + // server has deleted first entity and ltrim the message + return false; + } + for (auto &entity : new_content->text.entities) { + if (entity.type == MessageEntity::Type::PhoneNumber) { + // TODO remove after find_phone_numbers is implemented + return false; + } + } + return true; +} + +void merge_message_contents(Td *td, MessageContent *old_content, MessageContent *new_content, + bool need_message_changed_warning, DialogId dialog_id, bool need_merge_files, + bool &is_content_changed, bool &need_update) { + MessageContentType content_type = new_content->get_type(); + CHECK(old_content->get_type() == content_type); + + switch (content_type) { + case MessageContentType::Text: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->text.text != new_->text.text) { + if (need_message_changed_warning && need_message_text_changed_warning(old_, new_)) { + LOG(ERROR) << "Message text has changed from " + << to_string(get_message_content_object(old_content, td, -1, false)) << ". New content is " + << to_string(get_message_content_object(new_content, td, -1, false)); + } + need_update = true; + } + if (old_->text.entities != new_->text.entities) { + const int32 MAX_CUSTOM_ENTITIES_COUNT = 100; // server-side limit + if (need_message_changed_warning && need_message_text_changed_warning(old_, new_) && + old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT) { + LOG(WARNING) << "Entities has changed from " + << to_string(get_message_content_object(old_content, td, -1, false)) << ". New content is " + << to_string(get_message_content_object(new_content, td, -1, false)); + } + need_update = true; + } + if (old_->web_page_id != new_->web_page_id) { + LOG(INFO) << "Old: " << old_->web_page_id << ", new: " << new_->web_page_id; + is_content_changed = true; + need_update |= td->web_pages_manager_->have_web_page(old_->web_page_id) || + td->web_pages_manager_->have_web_page(new_->web_page_id); + } + break; + } + case MessageContentType::Animation: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->animations_manager_->merge_animations(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->caption != new_->caption) { + need_update = true; + } + break; + } + case MessageContentType::Audio: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->audios_manager_->merge_audios(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->caption != new_->caption) { + need_update = true; + } + break; + } + case MessageContentType::Contact: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->contact != new_->contact) { + need_update = true; + } + break; + } + case MessageContentType::Document: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->documents_manager_->merge_documents(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->caption != new_->caption) { + need_update = true; + } + break; + } + case MessageContentType::Game: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->game != new_->game) { + need_update = true; + } + break; + } + case MessageContentType::Invoice: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->title != new_->title || old_->description != new_->description || old_->photo != new_->photo || + old_->start_parameter != new_->start_parameter || old_->invoice != new_->invoice || + old_->total_amount != new_->total_amount || old_->receipt_message_id != new_->receipt_message_id) { + need_update = true; + } + if (old_->payload != new_->payload || old_->provider_token != new_->provider_token || + old_->provider_data != new_->provider_data) { + is_content_changed = true; + } + break; + } + case MessageContentType::LiveLocation: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->location != new_->location) { + need_update = true; + } + if (old_->period != new_->period) { + need_update = true; + } + if (old_->location.get_access_hash() != new_->location.get_access_hash()) { + is_content_changed = true; + merge_location_access_hash(old_->location, new_->location); + } + break; + } + case MessageContentType::Location: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->location != new_->location) { + need_update = true; + } + if (old_->location.get_access_hash() != new_->location.get_access_hash()) { + is_content_changed = true; + merge_location_access_hash(old_->location, new_->location); + } + break; + } + case MessageContentType::Photo: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + const Photo *old_photo = &old_->photo; + Photo *new_photo = &new_->photo; + if (old_photo->date != new_photo->date) { + is_content_changed = true; + } + if (old_photo->id != new_photo->id || old_->caption != new_->caption) { + need_update = true; + } + if (old_photo->photos != new_photo->photos) { + if (need_merge_files && + (old_photo->photos.size() == 1 || (old_photo->photos.size() == 2 && old_photo->photos[0].type == 't')) && + old_photo->photos.back().type == 'i' && !new_photo->photos.empty()) { + // first time get info about sent photo + if (old_photo->photos.size() == 2) { + new_photo->photos.push_back(old_photo->photos[0]); + } + new_photo->photos.push_back(old_photo->photos.back()); + + FileId old_file_id = get_message_content_file_id(old_content); + FileView old_file_view = td->file_manager_->get_file_view(old_file_id); + FileId new_file_id = new_photo->photos[0].file_id; + FileView new_file_view = td->file_manager_->get_file_view(new_file_id); + if (!old_file_view.has_remote_location()) { + CHECK(new_file_view.has_remote_location()); + CHECK(!new_file_view.remote_location().is_web()); + FileId file_id = td->file_manager_->register_remote( + FullRemoteFileLocation(FileType::Photo, new_file_view.remote_location().get_id(), + new_file_view.remote_location().get_access_hash(), 0, 0, 0, DcId::invalid()), + FileLocationSource::FromServer, dialog_id, old_photo->photos.back().size, 0, ""); + LOG_STATUS(td->file_manager_->merge(file_id, old_file_id)); + } + } + + // get sent photo again + auto new_photos_size = new_photo->photos.size(); + auto old_photos_size = old_photo->photos.size(); + if (old_photos_size == 2 + new_photos_size && old_photo->photos[new_photos_size].type == 't') { + new_photo->photos.push_back(old_photo->photos[new_photos_size]); + } + if (old_photos_size == 1 + new_photo->photos.size() && old_photo->photos.back().type == 'i') { + new_photo->photos.push_back(old_photo->photos.back()); + } + if (old_photo->photos != new_photo->photos) { + new_photo->photos.resize( + new_photos_size); // return previous size, because we shouldn't add local photo sizes + need_update = true; + } + } + break; + } + case MessageContentType::Sticker: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->stickers_manager_->merge_stickers(new_->file_id, old_->file_id, false))) { + need_update = true; + } + break; + } + case MessageContentType::Venue: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->venue != new_->venue) { + need_update = true; + } + if (old_->venue.location().get_access_hash() != new_->venue.location().get_access_hash()) { + is_content_changed = true; + merge_location_access_hash(old_->venue.location(), new_->venue.location()); + } + break; + } + case MessageContentType::Video: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->videos_manager_->merge_videos(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->caption != new_->caption) { + need_update = true; + } + break; + } + case MessageContentType::VideoNote: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->video_notes_manager_->merge_video_notes(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->is_viewed != new_->is_viewed) { + need_update = true; + } + break; + } + case MessageContentType::VoiceNote: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (new_->file_id != old_->file_id && + (!need_merge_files || td->voice_notes_manager_->merge_voice_notes(new_->file_id, old_->file_id, false))) { + need_update = true; + } + if (old_->caption != new_->caption) { + need_update = true; + } + if (old_->is_listened != new_->is_listened) { + need_update = true; + } + break; + } + case MessageContentType::ChatCreate: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->title != new_->title || old_->participant_user_ids != new_->participant_user_ids) { + need_update = true; + } + break; + } + case MessageContentType::ChatChangeTitle: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->title != new_->title) { + need_update = true; + } + break; + } + case MessageContentType::ChatChangePhoto: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->photo != new_->photo) { + need_update = true; + } + break; + } + case MessageContentType::ChatDeletePhoto: + break; + case MessageContentType::ChatDeleteHistory: + break; + case MessageContentType::ChatAddUsers: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->user_ids != new_->user_ids) { + need_update = true; + } + break; + } + case MessageContentType::ChatJoinedByLink: + break; + case MessageContentType::ChatDeleteUser: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->user_id != new_->user_id) { + need_update = true; + } + break; + } + case MessageContentType::ChatMigrateTo: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->migrated_to_channel_id != new_->migrated_to_channel_id) { + need_update = true; + } + break; + } + case MessageContentType::ChannelCreate: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->title != new_->title) { + need_update = true; + } + break; + } + case MessageContentType::ChannelMigrateFrom: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->title != new_->title || old_->migrated_from_chat_id != new_->migrated_from_chat_id) { + need_update = true; + } + break; + } + case MessageContentType::PinMessage: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->message_id != new_->message_id) { + need_update = true; + } + break; + } + case MessageContentType::GameScore: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->game_message_id != new_->game_message_id || old_->game_id != new_->game_id || + old_->score != new_->score) { + need_update = true; + } + break; + } + case MessageContentType::ScreenshotTaken: + break; + case MessageContentType::ChatSetTtl: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->ttl != new_->ttl) { + LOG(ERROR) << "Ttl has changed from " << old_->ttl << " to " << new_->ttl; + need_update = true; + } + break; + } + case MessageContentType::Call: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->call_id != new_->call_id) { + is_content_changed = true; + } + if (old_->duration != new_->duration || old_->discard_reason != new_->discard_reason) { + need_update = true; + } + break; + } + case MessageContentType::PaymentSuccessful: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->invoice_message_id != new_->invoice_message_id || old_->currency != new_->currency || + old_->total_amount != new_->total_amount || old_->invoice_payload != new_->invoice_payload || + old_->shipping_option_id != new_->shipping_option_id || + old_->telegram_payment_charge_id != new_->telegram_payment_charge_id || + old_->provider_payment_charge_id != new_->provider_payment_charge_id || + ((old_->order_info != nullptr || new_->order_info != nullptr) && + (old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info))) { + need_update = true; + } + break; + } + case MessageContentType::ContactRegistered: + break; + case MessageContentType::ExpiredPhoto: + break; + case MessageContentType::ExpiredVideo: + break; + case MessageContentType::CustomServiceAction: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->message != new_->message) { + need_update = true; + } + break; + } + case MessageContentType::WebsiteConnected: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->domain_name != new_->domain_name) { + need_update = true; + } + break; + } + case MessageContentType::PassportDataSent: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->types != new_->types) { + need_update = true; + } + break; + } + case MessageContentType::PassportDataReceived: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->values != new_->values) { + need_update = true; + } + if (old_->credentials != new_->credentials) { + need_update = true; + } + break; + } + case MessageContentType::Unsupported: + break; + default: + UNREACHABLE(); + break; + } +} + +bool merge_message_content_file_id(Td *td, MessageContent *message_content, FileId new_file_id) { + if (!new_file_id.is_valid()) { + return false; + } + + LOG(INFO) << "Merge message content of a message with file " << new_file_id; + MessageContentType content_type = message_content->get_type(); + switch (content_type) { + case MessageContentType::Animation: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->animations_manager_->merge_animations(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::Audio: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->audios_manager_->merge_audios(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::Document: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->documents_manager_->merge_documents(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::Photo: { + auto content = static_cast(message_content); + Photo *photo = &content->photo; + if (!photo->photos.empty() && photo->photos.back().type == 'i') { + FileId &old_file_id = photo->photos.back().file_id; + if (old_file_id != new_file_id) { + LOG_STATUS(td->file_manager_->merge(new_file_id, old_file_id)); + old_file_id = new_file_id; + return true; + } + } + break; + } + case MessageContentType::Sticker: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->stickers_manager_->merge_stickers(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::Video: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->videos_manager_->merge_videos(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::VideoNote: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->video_notes_manager_->merge_video_notes(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::VoiceNote: { + auto content = static_cast(message_content); + if (new_file_id != content->file_id) { + td->voice_notes_manager_->merge_voice_notes(new_file_id, content->file_id, false); + content->file_id = new_file_id; + return true; + } + break; + } + case MessageContentType::Contact: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Text: + case MessageContentType::Venue: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Unsupported: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type; + break; + default: + UNREACHABLE(); + break; + } + return false; +} + +static FormattedText get_secret_media_caption(string &&message_text, string &&message_caption) { + FormattedText caption; + if (message_text.empty()) { + caption.text = std::move(message_caption); + } else if (message_caption.empty()) { + caption.text = std::move(message_text); + } else { + caption.text = message_text + "\n\n" + message_caption; + } + + caption.entities = find_entities(caption.text, false); + return caption; +} + +template +static tl_object_ptr secret_to_telegram(FromT &from); + +// fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation; +static auto secret_to_telegram(secret_api::fileLocationUnavailable &file_location) { + return make_tl_object(file_location.volume_id_, file_location.local_id_, + file_location.secret_); +} + +// fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; +static auto secret_to_telegram(secret_api::fileLocation &file_location) { + return make_tl_object(file_location.dc_id_, file_location.volume_id_, + file_location.local_id_, file_location.secret_); +} + +// photoSizeEmpty#e17e23c type:string = PhotoSize; +static auto secret_to_telegram(secret_api::photoSizeEmpty &empty) { + if (!clean_input_string(empty.type_)) { + empty.type_.clear(); + } + return make_tl_object(empty.type_); +} + +// photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; +static auto secret_to_telegram(secret_api::photoSize &photo_size) { + if (!clean_input_string(photo_size.type_)) { + photo_size.type_.clear(); + } + return make_tl_object(photo_size.type_, + secret_to_telegram(*photo_size.location_), + photo_size.w_, photo_size.h_, photo_size.size_); +} + +// photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; +static auto secret_to_telegram(secret_api::photoCachedSize &photo_size) { + if (!clean_input_string(photo_size.type_)) { + photo_size.type_.clear(); + } + return make_tl_object( + photo_size.type_, secret_to_telegram(*photo_size.location_), photo_size.w_, + photo_size.h_, photo_size.bytes_.clone()); +} + +// documentAttributeImageSize #6c37c15c w:int h:int = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeImageSize &image_size) { + return make_tl_object(image_size.w_, image_size.h_); +} + +// documentAttributeAnimated #11b58939 = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeAnimated &animated) { + return make_tl_object(); +} + +// documentAttributeSticker23 #fb0a5727 = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeSticker23 &sticker) { + return make_tl_object( + 0, false /*ignored*/, "", make_tl_object(), nullptr); +} +static auto secret_to_telegram(secret_api::inputStickerSetEmpty &sticker_set) { + return make_tl_object(); +} +static auto secret_to_telegram(secret_api::inputStickerSetShortName &sticker_set) { + if (!clean_input_string(sticker_set.short_name_)) { + sticker_set.short_name_.clear(); + } + return make_tl_object(sticker_set.short_name_); +} + +// documentAttributeSticker #3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeSticker &sticker) { + if (!clean_input_string(sticker.alt_)) { + sticker.alt_.clear(); + } + return make_tl_object( + 0, false /*ignored*/, sticker.alt_, secret_to_telegram(*sticker.stickerset_), + nullptr); +} + +// documentAttributeVideo #5910cccb duration:int w:int h:int = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeVideo &video) { + return make_tl_object(0, false /*ignored*/, false /*ignored*/, video.duration_, + video.w_, video.h_); +} + +// documentAttributeFilename #15590068 file_name:string = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeFilename &filename) { + if (!clean_input_string(filename.file_name_)) { + filename.file_name_.clear(); + } + return make_tl_object(filename.file_name_); +} + +// documentAttributeVideo66#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeVideo66 &video) { + return make_tl_object( + (video.flags_ & secret_api::documentAttributeVideo66::ROUND_MESSAGE_MASK) != 0 + ? telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK + : 0, + video.round_message_, false, video.duration_, video.w_, video.h_); +} + +static auto telegram_documentAttributeAudio(bool is_voice_note, int duration, string title, string performer, + BufferSlice waveform) { + if (!clean_input_string(title)) { + title.clear(); + } + if (!clean_input_string(performer)) { + performer.clear(); + } + + int32 flags = 0; + if (is_voice_note) { + flags |= telegram_api::documentAttributeAudio::VOICE_MASK; + } + if (!title.empty()) { + flags |= telegram_api::documentAttributeAudio::TITLE_MASK; + } + if (!performer.empty()) { + flags |= telegram_api::documentAttributeAudio::PERFORMER_MASK; + } + if (waveform.size()) { + flags |= telegram_api::documentAttributeAudio::WAVEFORM_MASK; + } + return make_tl_object(flags, is_voice_note, duration, std::move(title), + std::move(performer), std::move(waveform)); +} + +// documentAttributeAudio23 #51448e5 duration:int = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeAudio23 &audio) { + return telegram_documentAttributeAudio(false, audio.duration_, "", "", Auto()); +} +// documentAttributeAudio45 #ded218e0 duration:int title:string performer:string = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeAudio45 &audio) { + return telegram_documentAttributeAudio(false, audio.duration_, audio.title_, audio.performer_, Auto()); +} + +// documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string +// performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute; +static auto secret_to_telegram(secret_api::documentAttributeAudio &audio) { + return telegram_documentAttributeAudio((audio.flags_ & secret_api::documentAttributeAudio::VOICE_MASK) != 0, + audio.duration_, audio.title_, audio.performer_, audio.waveform_.clone()); +} + +static auto secret_to_telegram(std::vector> &attributes) { + std::vector> res; + for (auto &attribute : attributes) { + auto telegram_attribute = secret_to_telegram(*attribute); + if (telegram_attribute) { + res.push_back(std::move(telegram_attribute)); + } + } + return res; +} + +// decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int +// thumb:PhotoSize dc_id:int attributes:Vector = DecryptedMessageMedia; +static auto secret_to_telegram_document(secret_api::decryptedMessageMediaExternalDocument &from) { + if (!clean_input_string(from.mime_type_)) { + from.mime_type_.clear(); + } + return make_tl_object(from.id_, from.access_hash_, from.date_, from.mime_type_, from.size_, + secret_to_telegram(*from.thumb_), from.dc_id_, + 0, secret_to_telegram(from.attributes_)); +} + +template +static tl_object_ptr secret_to_telegram(FromT &from) { + tl_object_ptr res; + downcast_call(from, [&](auto &p) { res = secret_to_telegram(p); }); + return res; +} + +static unique_ptr get_document_message_content( + std::pair &&parsed_document, FormattedText &&caption, bool is_opened) { + auto document_type = parsed_document.first; + auto file_id = parsed_document.second; + if (document_type != DocumentsManager::DocumentType::Unknown) { + CHECK(file_id.is_valid()); + } + switch (document_type) { + case DocumentsManager::DocumentType::Animation: + return make_unique(file_id, std::move(caption)); + case DocumentsManager::DocumentType::Audio: + return make_unique(file_id, std::move(caption)); + case DocumentsManager::DocumentType::General: + return make_unique(file_id, std::move(caption)); + case DocumentsManager::DocumentType::Sticker: + return make_unique(file_id); + case DocumentsManager::DocumentType::Unknown: + return make_unique(); + case DocumentsManager::DocumentType::Video: + return make_unique(file_id, std::move(caption)); + case DocumentsManager::DocumentType::VideoNote: + return make_unique(file_id, is_opened); + case DocumentsManager::DocumentType::VoiceNote: + return make_unique(file_id, std::move(caption), is_opened); + default: + UNREACHABLE(); + return nullptr; + } +} + +static unique_ptr get_secret_document_message_content( + Td *td, tl_object_ptr file, + tl_object_ptr &&document, + vector> &&attributes, DialogId owner_dialog_id, + FormattedText &&caption, bool is_opened) { + return get_document_message_content( + td->documents_manager_->on_get_document({std::move(file), std::move(document), std::move(attributes)}, + owner_dialog_id), + std::move(caption), is_opened); +} + +static unique_ptr get_document_message_content(Td *td, tl_object_ptr &&document, + DialogId owner_dialog_id, FormattedText &&caption, + bool is_opened, + MultiPromiseActor *load_data_multipromise_ptr) { + return get_document_message_content( + td->documents_manager_->on_get_document(std::move(document), owner_dialog_id, load_data_multipromise_ptr), + std::move(caption), is_opened); +} + +unique_ptr get_secret_message_content( + Td *td, string message_text, tl_object_ptr file, + tl_object_ptr &&media, + vector> &&secret_entities, DialogId owner_dialog_id, + MultiPromiseActor &load_data_multipromise) { + auto entities = get_message_entities(std::move(secret_entities)); + auto status = fix_formatted_text(message_text, entities, true, false, true, false); + if (status.is_error()) { + LOG(WARNING) << "Receive error " << status << " while parsing secret message \"" << message_text + << "\" with entities " << format::as_array(entities); + if (!clean_input_string(message_text)) { + message_text.clear(); + } + entities.clear(); + } + + if (media == nullptr) { + return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); + } + + int32 constructor_id = media->get_id(); + if (message_text.size()) { + if (constructor_id != secret_api::decryptedMessageMediaEmpty::ID) { + LOG(INFO) << "Receive non-empty message text and media"; + } else { + return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); + } + } + + // support of old layer and old constructions + switch (constructor_id) { + case secret_api::decryptedMessageMediaVideo::ID: { + auto video = move_tl_object_as(media); + std::vector> attributes; + attributes.emplace_back( + make_tl_object(video->duration_, video->w_, video->h_)); + media = make_tl_object( + std::move(video->thumb_), video->thumb_w_, video->thumb_h_, video->mime_type_, video->size_, + std::move(video->key_), std::move(video->iv_), std::move(attributes), std::move(video->caption_)); + + constructor_id = secret_api::decryptedMessageMediaDocument::ID; + break; + } + } + + bool is_media_empty = false; + switch (constructor_id) { + case secret_api::decryptedMessageMediaEmpty::ID: + LOG(ERROR) << "Receive empty message text and media"; + is_media_empty = true; + break; + case secret_api::decryptedMessageMediaGeoPoint::ID: { + auto message_geo_point = move_tl_object_as(media); + + auto m = make_unique(Location(std::move(message_geo_point))); + if (m->location.empty()) { + is_media_empty = true; + break; + } + + return std::move(m); + } + case secret_api::decryptedMessageMediaVenue::ID: { + auto message_venue = move_tl_object_as(media); + + if (!clean_input_string(message_venue->title_)) { + message_venue->title_.clear(); + } + if (!clean_input_string(message_venue->address_)) { + message_venue->address_.clear(); + } + if (!clean_input_string(message_venue->provider_)) { + message_venue->provider_.clear(); + } + if (!clean_input_string(message_venue->venue_id_)) { + message_venue->venue_id_.clear(); + } + + auto m = make_unique(Venue(Location(message_venue->lat_, message_venue->long_, 0), + std::move(message_venue->title_), std::move(message_venue->address_), + std::move(message_venue->provider_), std::move(message_venue->venue_id_), + string())); + if (m->venue.empty()) { + is_media_empty = true; + break; + } + + return std::move(m); + } + case secret_api::decryptedMessageMediaContact::ID: { + auto message_contact = move_tl_object_as(media); + if (!clean_input_string(message_contact->phone_number_)) { + message_contact->phone_number_.clear(); + } + if (!clean_input_string(message_contact->first_name_)) { + message_contact->first_name_.clear(); + } + if (!clean_input_string(message_contact->last_name_)) { + message_contact->last_name_.clear(); + } + return make_unique( + Contact(std::move(message_contact->phone_number_), std::move(message_contact->first_name_), + std::move(message_contact->last_name_), string(), message_contact->user_id_)); + } + case secret_api::decryptedMessageMediaWebPage::ID: { + auto media_web_page = move_tl_object_as(media); + if (!clean_input_string(media_web_page->url_)) { + media_web_page->url_.clear(); + } + auto r_http_url = parse_url(media_web_page->url_); + if (r_http_url.is_error()) { + is_media_empty = true; + break; + } + auto url = r_http_url.ok().get_url(); + + auto web_page_id = td->web_pages_manager_->get_web_page_by_url(url, load_data_multipromise.get_promise()); + auto result = make_unique(FormattedText{std::move(message_text), std::move(entities)}, web_page_id); + if (!result->web_page_id.is_valid()) { + load_data_multipromise.add_promise( + PromiseCreator::lambda([td, url, &web_page_id = result->web_page_id](Result result) { + if (result.is_ok()) { + web_page_id = td->web_pages_manager_->get_web_page_by_url(url); + } + })); + } + return std::move(result); + } + case secret_api::decryptedMessageMediaExternalDocument::ID: { + auto external_document = move_tl_object_as(media); + auto document = secret_to_telegram_document(*external_document); + return get_document_message_content(td, std::move(document), owner_dialog_id, + FormattedText{std::move(message_text), std::move(entities)}, false, + &load_data_multipromise); + } + } + if (file == nullptr && !is_media_empty) { + LOG(ERROR) << "Received secret message with media, but without a file"; + is_media_empty = true; + } + if (is_media_empty) { + return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); + } + switch (constructor_id) { + case secret_api::decryptedMessageMediaPhoto::ID: { + auto message_photo = move_tl_object_as(media); + if (!clean_input_string(message_photo->caption_)) { + message_photo->caption_.clear(); + } + return make_unique( + get_photo(td->file_manager_.get(), std::move(file), std::move(message_photo), owner_dialog_id), + get_secret_media_caption(std::move(message_text), std::move(message_photo->caption_))); + } + case secret_api::decryptedMessageMediaDocument::ID: { + auto message_document = move_tl_object_as(media); + if (!clean_input_string(message_document->caption_)) { + message_document->caption_.clear(); + } + if (!clean_input_string(message_document->mime_type_)) { + message_document->mime_type_.clear(); + } + auto attributes = secret_to_telegram(message_document->attributes_); + message_document->attributes_.clear(); + return get_secret_document_message_content( + td, std::move(file), std::move(message_document), std::move(attributes), owner_dialog_id, + get_secret_media_caption(std::move(message_text), std::move(message_document->caption_)), false); + } + default: + LOG(ERROR) << "Unsupported: " << to_string(media); + return make_unique(); + } +} + +unique_ptr get_message_content(Td *td, FormattedText message, + tl_object_ptr &&media, + DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id, + int32 *ttl) { + if (media == nullptr) { + return make_unique(std::move(message), WebPageId()); + } + + int32 constructor_id = media->get_id(); + if (message.text.size()) { + if (constructor_id != telegram_api::messageMediaEmpty::ID) { + LOG(INFO) << "Receive non-empty message text and media for message from " << owner_dialog_id; + } else { + return make_unique(std::move(message), WebPageId()); + } + } + switch (constructor_id) { + case telegram_api::messageMediaEmpty::ID: + LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id; + return make_unique(std::move(message), WebPageId()); + case telegram_api::messageMediaPhoto::ID: { + auto message_photo = move_tl_object_as(media); + if ((message_photo->flags_ & telegram_api::messageMediaPhoto::PHOTO_MASK) == 0) { + if ((message_photo->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) == 0) { + LOG(ERROR) << "Receive messageMediaPhoto without photo and TTL: " << oneline(to_string(message_photo)); + break; + } + + return make_unique(); + } + + auto photo_ptr = std::move(message_photo->photo_); + int32 photo_id = photo_ptr->get_id(); + if (photo_id == telegram_api::photoEmpty::ID) { + return make_unique(); + } + CHECK(photo_id == telegram_api::photo::ID); + + if (ttl != nullptr && (message_photo->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) != 0) { + *ttl = message_photo->ttl_seconds_; + } + return make_unique( + get_photo(td->file_manager_.get(), move_tl_object_as(photo_ptr), owner_dialog_id), + std::move(message)); + } + case telegram_api::messageMediaGeo::ID: { + auto message_geo_point = move_tl_object_as(media); + + auto m = make_unique(Location(std::move(message_geo_point->geo_))); + if (m->location.empty()) { + break; + } + + return std::move(m); + } + case telegram_api::messageMediaGeoLive::ID: { + auto message_geo_point_live = move_tl_object_as(media); + int32 period = message_geo_point_live->period_; + auto location = Location(std::move(message_geo_point_live->geo_)); + if (location.empty()) { + break; + } + + if (period <= 0) { + LOG(ERROR) << "Receive wrong live location period = " << period; + return make_unique(std::move(location)); + } + return make_unique(std::move(location), period); + } + case telegram_api::messageMediaVenue::ID: { + auto message_venue = move_tl_object_as(media); + + auto m = + make_unique(Venue(message_venue->geo_, std::move(message_venue->title_), + std::move(message_venue->address_), std::move(message_venue->provider_), + std::move(message_venue->venue_id_), std::move(message_venue->venue_type_))); + if (m->venue.empty()) { + break; + } + + return std::move(m); + } + case telegram_api::messageMediaContact::ID: { + auto message_contact = move_tl_object_as(media); + if (message_contact->user_id_ != 0) { + td->contacts_manager_->get_user_id_object(UserId(message_contact->user_id_), + "messageMediaContact"); // to ensure updateUser + } + return make_unique(Contact( + std::move(message_contact->phone_number_), std::move(message_contact->first_name_), + std::move(message_contact->last_name_), std::move(message_contact->vcard_), message_contact->user_id_)); + } + case telegram_api::messageMediaDocument::ID: { + auto message_document = move_tl_object_as(media); + if ((message_document->flags_ & telegram_api::messageMediaDocument::DOCUMENT_MASK) == 0) { + if ((message_document->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) == 0) { + LOG(ERROR) << "Receive messageMediaDocument without document and TTL: " + << oneline(to_string(message_document)); + break; + } + + return make_unique(); + } + + auto document_ptr = std::move(message_document->document_); + int32 document_id = document_ptr->get_id(); + if (document_id == telegram_api::documentEmpty::ID) { + break; + } + CHECK(document_id == telegram_api::document::ID); + + if (ttl != nullptr && (message_document->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) != 0) { + *ttl = message_document->ttl_seconds_; + } + return get_document_message_content(td, move_tl_object_as(document_ptr), owner_dialog_id, + std::move(message), is_content_read, nullptr); + } + case telegram_api::messageMediaGame::ID: { + auto message_game = move_tl_object_as(media); + + auto m = make_unique(Game(td, std::move(message_game->game_), owner_dialog_id)); + if (m->game.empty()) { + break; + } + + m->game.set_bot_user_id(via_bot_user_id); + m->game.set_text(std::move(message)); + + return std::move(m); + } + case telegram_api::messageMediaInvoice::ID: { + auto message_invoice = move_tl_object_as(media); + + MessageId receipt_message_id; + if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) { + receipt_message_id = MessageId(ServerMessageId(message_invoice->receipt_msg_id_)); + if (!receipt_message_id.is_valid()) { + LOG(ERROR) << "Receive as receipt message " << receipt_message_id << " in " << owner_dialog_id; + receipt_message_id = MessageId(); + } + } + bool need_shipping_address = + (message_invoice->flags_ & telegram_api::messageMediaInvoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0; + bool is_test = (message_invoice->flags_ & telegram_api::messageMediaInvoice::TEST_MASK) != 0; + return td::make_unique( + std::move(message_invoice->title_), std::move(message_invoice->description_), + get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id), + std::move(message_invoice->start_param_), message_invoice->total_amount_, + std::move(message_invoice->currency_), is_test, need_shipping_address, receipt_message_id); + } + case telegram_api::messageMediaWebPage::ID: { + auto media_web_page = move_tl_object_as(media); + auto web_page_id = td->web_pages_manager_->on_get_web_page(std::move(media_web_page->webpage_), owner_dialog_id); + return make_unique(std::move(message), web_page_id); + } + + case telegram_api::messageMediaUnsupported::ID: { + return make_unique(); + } + default: + UNREACHABLE(); + } + + // explicit empty media message + return make_unique(std::move(message), WebPageId()); +} + +unique_ptr dup_message_content(Td *td, DialogId dialog_id, const MessageContent *content, + bool for_forward) { + CHECK(content != nullptr); + + bool to_secret = dialog_id.get_type() == DialogType::SecretChat; + auto fix_file_id = [dialog_id, to_secret, file_manager = td->file_manager_.get()](FileId file_id) { + auto file_view = file_manager->get_file_view(file_id); + if (to_secret && !file_view.is_encrypted_secret()) { + auto download_file_id = file_manager->dup_file_id(file_id); + file_id = file_manager + ->register_generate(FileType::Encrypted, FileLocationSource::FromServer, file_view.suggested_name(), + PSTRING() << "#file_id#" << download_file_id.get(), dialog_id, file_view.size()) + .ok(); + } + return file_manager->dup_file_id(file_id); + }; + + FileId thumbnail_file_id; + if (to_secret) { + thumbnail_file_id = get_message_content_thumbnail_file_id(content, td); + } + switch (content->get_type()) { + case MessageContentType::Animation: { + auto result = make_unique(*static_cast(content)); + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->animations_manager_->dup_animation(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::Audio: { + auto result = make_unique(*static_cast(content)); + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->audios_manager_->dup_audio(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::Contact: + return make_unique(*static_cast(content)); + case MessageContentType::Document: { + auto result = make_unique(*static_cast(content)); + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->documents_manager_->dup_document(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::Game: + return make_unique(*static_cast(content)); + case MessageContentType::Invoice: + return make_unique(*static_cast(content)); + case MessageContentType::LiveLocation: + if (to_secret || for_forward) { + return make_unique(Location(static_cast(content)->location)); + } else { + return make_unique(*static_cast(content)); + } + case MessageContentType::Location: + return make_unique(*static_cast(content)); + case MessageContentType::Photo: { + auto result = make_unique(*static_cast(content)); + + if (result->photo.photos.size() > 2 && !to_secret) { + // already sent photo + return std::move(result); + } + + // Find 'i' or largest + CHECK(!result->photo.photos.empty()); + PhotoSize photo; + for (const auto &size : result->photo.photos) { + if (size.type == 'i') { + photo = size; + } + } + if (photo.type == 0) { + for (const auto &size : result->photo.photos) { + if (photo.type == 0 || photo < size) { + photo = size; + } + } + } + + // Find 't' or smallest + PhotoSize thumbnail; + for (const auto &size : result->photo.photos) { + if (size.type == 't') { + thumbnail = size; + } + } + if (thumbnail.type == 0) { + for (const auto &size : result->photo.photos) { + if (size.type != photo.type && (thumbnail.type == 0 || size < thumbnail)) { + thumbnail = size; + } + } + } + + result->photo.photos.clear(); + if (thumbnail.type != 0) { + thumbnail.type = 't'; + result->photo.photos.push_back(std::move(thumbnail)); + } + photo.type = 'i'; + result->photo.photos.push_back(std::move(photo)); + + if (photo_has_input_media(td->file_manager_.get(), result->photo, to_secret)) { + return std::move(result); + } + + result->photo.photos.back().file_id = fix_file_id(result->photo.photos.back().file_id); + if (thumbnail.type != 0) { + result->photo.photos[0].file_id = td->file_manager_->dup_file_id(result->photo.photos[0].file_id); + } + return std::move(result); + } + case MessageContentType::Sticker: { + auto result = make_unique(*static_cast(content)); + if (td->stickers_manager_->has_input_media(result->file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->stickers_manager_->dup_sticker(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::Text: + return make_unique(*static_cast(content)); + case MessageContentType::Venue: + return make_unique(*static_cast(content)); + case MessageContentType::Video: { + auto result = make_unique(*static_cast(content)); + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->videos_manager_->dup_video(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::VideoNote: { + auto result = make_unique(*static_cast(content)); + result->is_viewed = false; + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->video_notes_manager_->dup_video_note(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::VoiceNote: { + auto result = make_unique(*static_cast(content)); + result->is_listened = false; + if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { + return std::move(result); + } + result->file_id = td->voice_notes_manager_->dup_voice_note(fix_file_id(result->file_id), result->file_id); + CHECK(result->file_id.is_valid()); + return std::move(result); + } + case MessageContentType::Unsupported: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ContactRegistered: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return nullptr; + default: + UNREACHABLE(); + } + UNREACHABLE(); + return nullptr; +} + +unique_ptr get_action_message_content(Td *td, tl_object_ptr &&action, + DialogId owner_dialog_id, MessageId reply_to_message_id) { + CHECK(action != nullptr); + + switch (action->get_id()) { + case telegram_api::messageActionEmpty::ID: + LOG(ERROR) << "Receive empty message action in " << owner_dialog_id; + break; + case telegram_api::messageActionChatCreate::ID: { + auto chat_create = move_tl_object_as(action); + + vector participant_user_ids; + participant_user_ids.reserve(chat_create->users_.size()); + for (auto &user : chat_create->users_) { + UserId user_id(user); + if (user_id.is_valid()) { + participant_user_ids.push_back(user_id); + } else { + LOG(ERROR) << "Receive messageActionChatCreate with invalid " << user_id << " in " << owner_dialog_id; + } + } + + return td::make_unique(std::move(chat_create->title_), std::move(participant_user_ids)); + } + case telegram_api::messageActionChatEditTitle::ID: { + auto chat_edit_title = move_tl_object_as(action); + return td::make_unique(std::move(chat_edit_title->title_)); + } + case telegram_api::messageActionChatEditPhoto::ID: { + auto chat_edit_photo = move_tl_object_as(action); + + auto photo_ptr = std::move(chat_edit_photo->photo_); + int32 photo_id = photo_ptr->get_id(); + if (photo_id == telegram_api::photoEmpty::ID) { + break; + } + CHECK(photo_id == telegram_api::photo::ID); + + return make_unique( + get_photo(td->file_manager_.get(), move_tl_object_as(photo_ptr), owner_dialog_id)); + } + case telegram_api::messageActionChatDeletePhoto::ID: { + return make_unique(); + } + case telegram_api::messageActionHistoryClear::ID: { + return make_unique(); + } + case telegram_api::messageActionChatAddUser::ID: { + auto chat_add_user = move_tl_object_as(action); + + vector user_ids; + user_ids.reserve(chat_add_user->users_.size()); + for (auto &user : chat_add_user->users_) { + UserId user_id(user); + if (user_id.is_valid()) { + user_ids.push_back(user_id); + } else { + LOG(ERROR) << "Receive messageActionChatAddUser with invalid " << user_id << " in " << owner_dialog_id; + } + } + + return td::make_unique(std::move(user_ids)); + } + case telegram_api::messageActionChatJoinedByLink::ID: + return make_unique(); + case telegram_api::messageActionChatDeleteUser::ID: { + auto chat_delete_user = move_tl_object_as(action); + + UserId user_id(chat_delete_user->user_id_); + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive messageActionChatDeleteUser with invalid " << user_id << " in " << owner_dialog_id; + break; + } + + return make_unique(user_id); + } + case telegram_api::messageActionChatMigrateTo::ID: { + auto chat_migrate_to = move_tl_object_as(action); + + ChannelId migrated_to_channel_id(chat_migrate_to->channel_id_); + if (!migrated_to_channel_id.is_valid()) { + LOG(ERROR) << "Receive messageActionChatMigrateTo with invalid " << migrated_to_channel_id << " in " + << owner_dialog_id; + break; + } + + return make_unique(migrated_to_channel_id); + } + case telegram_api::messageActionChannelCreate::ID: { + auto channel_create = move_tl_object_as(action); + return td::make_unique(std::move(channel_create->title_)); + } + case telegram_api::messageActionChannelMigrateFrom::ID: { + auto channel_migrate_from = move_tl_object_as(action); + + ChatId chat_id(channel_migrate_from->chat_id_); + LOG_IF(ERROR, !chat_id.is_valid()) << "Receive messageActionChannelMigrateFrom with invalid " << chat_id << " in " + << owner_dialog_id; + + return td::make_unique(std::move(channel_migrate_from->title_), chat_id); + } + case telegram_api::messageActionPinMessage::ID: { + if (!reply_to_message_id.is_valid()) { + LOG(ERROR) << "Receive pinned message with " << reply_to_message_id << " in " << owner_dialog_id; + reply_to_message_id = MessageId(); + } + return make_unique(reply_to_message_id); + } + case telegram_api::messageActionGameScore::ID: { + if (!reply_to_message_id.is_valid()) { + LOG_IF(ERROR, !td->auth_manager_->is_bot()) + << "Receive game score with " << reply_to_message_id << " in " << owner_dialog_id; + reply_to_message_id = MessageId(); + } + auto game_score = move_tl_object_as(action); + return make_unique(reply_to_message_id, game_score->game_id_, game_score->score_); + } + case telegram_api::messageActionPhoneCall::ID: { + auto phone_call = move_tl_object_as(action); + auto duration = + (phone_call->flags_ & telegram_api::messageActionPhoneCall::DURATION_MASK) != 0 ? phone_call->duration_ : 0; + return make_unique(phone_call->call_id_, duration, get_call_discard_reason(phone_call->reason_)); + } + case telegram_api::messageActionPaymentSent::ID: { + LOG_IF(ERROR, td->auth_manager_->is_bot()) << "Receive MessageActionPaymentSent in " << owner_dialog_id; + if (!reply_to_message_id.is_valid()) { + LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id; + reply_to_message_id = MessageId(); + } + auto payment_sent = move_tl_object_as(action); + return td::make_unique(reply_to_message_id, std::move(payment_sent->currency_), + payment_sent->total_amount_); + } + case telegram_api::messageActionPaymentSentMe::ID: { + LOG_IF(ERROR, !td->auth_manager_->is_bot()) << "Receive MessageActionPaymentSentMe in " << owner_dialog_id; + if (!reply_to_message_id.is_valid()) { + LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id; + reply_to_message_id = MessageId(); + } + auto payment_sent = move_tl_object_as(action); + auto result = td::make_unique(reply_to_message_id, std::move(payment_sent->currency_), + payment_sent->total_amount_); + result->invoice_payload = payment_sent->payload_.as_slice().str(); + result->shipping_option_id = std::move(payment_sent->shipping_option_id_); + result->order_info = get_order_info(std::move(payment_sent->info_)); + result->telegram_payment_charge_id = std::move(payment_sent->charge_->id_); + result->provider_payment_charge_id = std::move(payment_sent->charge_->provider_charge_id_); + return std::move(result); + } + case telegram_api::messageActionScreenshotTaken::ID: { + return make_unique(); + } + case telegram_api::messageActionCustomAction::ID: { + auto custom_action = move_tl_object_as(action); + return td::make_unique(std::move(custom_action->message_)); + } + case telegram_api::messageActionBotAllowed::ID: { + auto bot_allowed = move_tl_object_as(action); + return td::make_unique(std::move(bot_allowed->domain_)); + } + case telegram_api::messageActionSecureValuesSent::ID: { + LOG_IF(ERROR, td->auth_manager_->is_bot()) << "Receive MessageActionSecureValuesSent in " << owner_dialog_id; + auto secure_values = move_tl_object_as(action); + return td::make_unique(get_secure_value_types(secure_values->types_)); + } + case telegram_api::messageActionSecureValuesSentMe::ID: { + LOG_IF(ERROR, !td->auth_manager_->is_bot()) << "Receive MessageActionSecureValuesSentMe in " << owner_dialog_id; + auto secure_values = move_tl_object_as(action); + return td::make_unique( + get_encrypted_secure_values(td->file_manager_.get(), std::move(secure_values->values_)), + get_encrypted_secure_credentials(std::move(secure_values->credentials_))); + } + default: + UNREACHABLE(); + } + // explicit empty or wrong action + return make_unique(FormattedText(), WebPageId()); +} + +tl_object_ptr get_message_content_object(const MessageContent *content, Td *td, + int32 message_date, bool is_content_secret) { + CHECK(content != nullptr); + switch (content->get_type()) { + case MessageContentType::Animation: { + const MessageAnimation *m = static_cast(content); + return make_tl_object( + td->animations_manager_->get_animation_object(m->file_id, "get_message_content_object"), + get_formatted_text_object(m->caption), is_content_secret); + } + case MessageContentType::Audio: { + const MessageAudio *m = static_cast(content); + return make_tl_object(td->audios_manager_->get_audio_object(m->file_id), + get_formatted_text_object(m->caption)); + } + case MessageContentType::Contact: { + const MessageContact *m = static_cast(content); + return make_tl_object(m->contact.get_contact_object()); + } + case MessageContentType::Document: { + const MessageDocument *m = static_cast(content); + return make_tl_object(td->documents_manager_->get_document_object(m->file_id), + get_formatted_text_object(m->caption)); + } + case MessageContentType::Game: { + const MessageGame *m = static_cast(content); + return make_tl_object(m->game.get_game_object(td)); + } + case MessageContentType::Invoice: { + const MessageInvoice *m = static_cast(content); + return make_tl_object( + m->title, m->description, get_photo_object(td->file_manager_.get(), &m->photo), m->invoice.currency, + m->total_amount, m->start_parameter, m->invoice.is_test, m->invoice.need_shipping_address, + m->receipt_message_id.get()); + } + case MessageContentType::LiveLocation: { + const MessageLiveLocation *m = static_cast(content); + auto passed = max(G()->unix_time_cached() - message_date, 0); + return make_tl_object(m->location.get_location_object(), m->period, + max(0, m->period - passed)); + } + case MessageContentType::Location: { + const MessageLocation *m = static_cast(content); + return make_tl_object(m->location.get_location_object(), 0, 0); + } + case MessageContentType::Photo: { + const MessagePhoto *m = static_cast(content); + return make_tl_object(get_photo_object(td->file_manager_.get(), &m->photo), + get_formatted_text_object(m->caption), is_content_secret); + } + case MessageContentType::Sticker: { + const MessageSticker *m = static_cast(content); + return make_tl_object(td->stickers_manager_->get_sticker_object(m->file_id)); + } + case MessageContentType::Text: { + const MessageText *m = static_cast(content); + return make_tl_object(get_formatted_text_object(m->text), + td->web_pages_manager_->get_web_page_object(m->web_page_id)); + } + case MessageContentType::Unsupported: { + return make_tl_object(); + } + case MessageContentType::Venue: { + const MessageVenue *m = static_cast(content); + return make_tl_object(m->venue.get_venue_object()); + } + case MessageContentType::Video: { + const MessageVideo *m = static_cast(content); + return make_tl_object(td->videos_manager_->get_video_object(m->file_id), + get_formatted_text_object(m->caption), is_content_secret); + } + case MessageContentType::VideoNote: { + const MessageVideoNote *m = static_cast(content); + return make_tl_object(td->video_notes_manager_->get_video_note_object(m->file_id), + m->is_viewed, is_content_secret); + } + case MessageContentType::VoiceNote: { + const MessageVoiceNote *m = static_cast(content); + return make_tl_object(td->voice_notes_manager_->get_voice_note_object(m->file_id), + get_formatted_text_object(m->caption), m->is_listened); + } + case MessageContentType::ChatCreate: { + const MessageChatCreate *m = static_cast(content); + return make_tl_object( + m->title, td->contacts_manager_->get_user_ids_object(m->participant_user_ids)); + } + case MessageContentType::ChatChangeTitle: { + const MessageChatChangeTitle *m = static_cast(content); + return make_tl_object(m->title); + } + case MessageContentType::ChatChangePhoto: { + const MessageChatChangePhoto *m = static_cast(content); + return make_tl_object(get_photo_object(td->file_manager_.get(), &m->photo)); + } + case MessageContentType::ChatDeletePhoto: + return make_tl_object(); + case MessageContentType::ChatDeleteHistory: + return make_tl_object(); + case MessageContentType::ChatAddUsers: { + const MessageChatAddUsers *m = static_cast(content); + return make_tl_object(td->contacts_manager_->get_user_ids_object(m->user_ids)); + } + case MessageContentType::ChatJoinedByLink: + return make_tl_object(); + case MessageContentType::ChatDeleteUser: { + const MessageChatDeleteUser *m = static_cast(content); + return make_tl_object( + td->contacts_manager_->get_user_id_object(m->user_id, "messageChatDeleteMember")); + } + case MessageContentType::ChatMigrateTo: { + const MessageChatMigrateTo *m = static_cast(content); + return make_tl_object( + td->contacts_manager_->get_supergroup_id_object(m->migrated_to_channel_id, "messageChatUpgradeTo")); + } + case MessageContentType::ChannelCreate: { + const MessageChannelCreate *m = static_cast(content); + return make_tl_object(m->title); + } + case MessageContentType::ChannelMigrateFrom: { + const MessageChannelMigrateFrom *m = static_cast(content); + return make_tl_object( + m->title, + td->contacts_manager_->get_basic_group_id_object(m->migrated_from_chat_id, "messageChatUpgradeFrom")); + } + case MessageContentType::PinMessage: { + const MessagePinMessage *m = static_cast(content); + return make_tl_object(m->message_id.get()); + } + case MessageContentType::GameScore: { + const MessageGameScore *m = static_cast(content); + return make_tl_object(m->game_message_id.get(), m->game_id, m->score); + } + case MessageContentType::ScreenshotTaken: + return make_tl_object(); + case MessageContentType::ChatSetTtl: { + const MessageChatSetTtl *m = static_cast(content); + return make_tl_object(m->ttl); + } + case MessageContentType::Call: { + const MessageCall *m = static_cast(content); + return make_tl_object(get_call_discard_reason_object(m->discard_reason), m->duration); + } + case MessageContentType::PaymentSuccessful: { + const MessagePaymentSuccessful *m = static_cast(content); + if (td->auth_manager_->is_bot()) { + return make_tl_object( + m->invoice_message_id.get(), m->currency, m->total_amount, m->invoice_payload, m->shipping_option_id, + get_order_info_object(m->order_info), m->telegram_payment_charge_id, m->provider_payment_charge_id); + } else { + return make_tl_object(m->invoice_message_id.get(), m->currency, + m->total_amount); + } + } + case MessageContentType::ContactRegistered: + return make_tl_object(); + case MessageContentType::ExpiredPhoto: + return make_tl_object(); + case MessageContentType::ExpiredVideo: + return make_tl_object(); + case MessageContentType::CustomServiceAction: { + const MessageCustomServiceAction *m = static_cast(content); + return make_tl_object(m->message); + } + case MessageContentType::WebsiteConnected: { + const MessageWebsiteConnected *m = static_cast(content); + return make_tl_object(m->domain_name); + } + case MessageContentType::PassportDataSent: { + const MessagePassportDataSent *m = static_cast(content); + return make_tl_object(get_passport_element_types_object(m->types)); + } + case MessageContentType::PassportDataReceived: { + const MessagePassportDataReceived *m = static_cast(content); + return make_tl_object( + get_encrypted_passport_element_object(td->file_manager_.get(), m->values), + get_encrypted_credentials_object(m->credentials)); + } + default: + UNREACHABLE(); + return nullptr; + } + UNREACHABLE(); + return nullptr; +} + +const FormattedText *get_message_content_text(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::Text: + return &static_cast(content)->text; + case MessageContentType::Game: + return &static_cast(content)->game.get_text(); + default: + return get_message_content_caption(content); + } +} + +const FormattedText *get_message_content_caption(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::Animation: + return &static_cast(content)->caption; + case MessageContentType::Audio: + return &static_cast(content)->caption; + case MessageContentType::Document: + return &static_cast(content)->caption; + case MessageContentType::Photo: + return &static_cast(content)->caption; + case MessageContentType::Video: + return &static_cast(content)->caption; + case MessageContentType::VoiceNote: + return &static_cast(content)->caption; + default: + return nullptr; + } +} + +int32 get_message_content_duration(const MessageContent *content, const Td *td) { + CHECK(content != nullptr); + switch (content->get_type()) { + case MessageContentType::Animation: { + auto animation_file_id = static_cast(content)->file_id; + return td->animations_manager_->get_animation_duration(animation_file_id); + } + case MessageContentType::Audio: { + auto audio_file_id = static_cast(content)->file_id; + return td->audios_manager_->get_audio_duration(audio_file_id); + } + case MessageContentType::Video: { + auto video_file_id = static_cast(content)->file_id; + return td->videos_manager_->get_video_duration(video_file_id); + } + case MessageContentType::VideoNote: { + auto video_note_file_id = static_cast(content)->file_id; + return td->video_notes_manager_->get_video_note_duration(video_note_file_id); + } + case MessageContentType::VoiceNote: { + auto voice_file_id = static_cast(content)->file_id; + return td->voice_notes_manager_->get_voice_note_duration(voice_file_id); + } + default: + return 0; + } +} + +FileId get_message_content_file_id(const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::Animation: + return static_cast(content)->file_id; + case MessageContentType::Audio: + return static_cast(content)->file_id; + case MessageContentType::Document: + return static_cast(content)->file_id; + case MessageContentType::Photo: + for (auto &size : static_cast(content)->photo.photos) { + if (size.type == 'i') { + return size.file_id; + } + } + break; + case MessageContentType::Sticker: + return static_cast(content)->file_id; + case MessageContentType::Video: + return static_cast(content)->file_id; + case MessageContentType::VideoNote: + return static_cast(content)->file_id; + case MessageContentType::VoiceNote: + return static_cast(content)->file_id; + default: + break; + } + return FileId(); +} + +void update_message_content_file_id_remote(MessageContent *content, FileId file_id) { + if (file_id.get_remote() == 0) { + return; + } + FileId *old_file_id = [&]() { + switch (content->get_type()) { + case MessageContentType::Animation: + return &static_cast(content)->file_id; + case MessageContentType::Audio: + return &static_cast(content)->file_id; + case MessageContentType::Document: + return &static_cast(content)->file_id; + case MessageContentType::Sticker: + return &static_cast(content)->file_id; + case MessageContentType::Video: + return &static_cast(content)->file_id; + case MessageContentType::VideoNote: + return &static_cast(content)->file_id; + case MessageContentType::VoiceNote: + return &static_cast(content)->file_id; + default: + return static_cast(nullptr); + } + }(); + if (old_file_id != nullptr && *old_file_id == file_id && old_file_id->get_remote() == 0) { + *old_file_id = file_id; + } +} + +FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td) { + switch (content->get_type()) { + case MessageContentType::Animation: + return td->animations_manager_->get_animation_thumbnail_file_id( + static_cast(content)->file_id); + case MessageContentType::Audio: + return td->audios_manager_->get_audio_thumbnail_file_id(static_cast(content)->file_id); + case MessageContentType::Document: + return td->documents_manager_->get_document_thumbnail_file_id( + static_cast(content)->file_id); + case MessageContentType::Photo: + for (auto &size : static_cast(content)->photo.photos) { + if (size.type == 't') { + return size.file_id; + } + } + break; + case MessageContentType::Sticker: + return td->stickers_manager_->get_sticker_thumbnail_file_id( + static_cast(content)->file_id); + case MessageContentType::Video: + return td->videos_manager_->get_video_thumbnail_file_id(static_cast(content)->file_id); + case MessageContentType::VideoNote: + return td->video_notes_manager_->get_video_note_thumbnail_file_id( + static_cast(content)->file_id); + case MessageContentType::VoiceNote: + return FileId(); + default: + break; + } + return FileId(); +} + +vector get_message_content_file_ids(const MessageContent *content, const Td *td) { + switch (content->get_type()) { + case MessageContentType::Photo: + return transform(static_cast(content)->photo.photos, + [](auto &size) { return size.file_id; }); + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Document: + case MessageContentType::Sticker: + case MessageContentType::Video: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: { + vector result; + result.reserve(2); + FileId file_id = get_message_content_file_id(content); + if (file_id.is_valid()) { + result.push_back(file_id); + } + FileId thumbnail_file_id = get_message_content_thumbnail_file_id(content, td); + if (thumbnail_file_id.is_valid()) { + result.push_back(thumbnail_file_id); + } + return result; + } + default: + return {}; + } +} + +string get_message_content_search_text(const Td *td, const MessageContent *content) { + switch (content->get_type()) { + case MessageContentType::Text: { + auto *text = static_cast(content); + if (!text->web_page_id.is_valid()) { + return text->text.text; + } + return PSTRING() << text->text.text << " " << td->web_pages_manager_->get_web_page_search_text(text->web_page_id); + } + case MessageContentType::Animation: { + auto animation = static_cast(content); + return PSTRING() << td->animations_manager_->get_animation_search_text(animation->file_id) << " " + << animation->caption.text; + } + case MessageContentType::Audio: { + auto audio = static_cast(content); + return PSTRING() << td->audios_manager_->get_audio_search_text(audio->file_id) << " " << audio->caption.text; + } + case MessageContentType::Document: { + auto document = static_cast(content); + return PSTRING() << td->documents_manager_->get_document_search_text(document->file_id) << " " + << document->caption.text; + } + case MessageContentType::Photo: { + auto photo = static_cast(content); + return photo->caption.text; + } + case MessageContentType::Video: { + auto video = static_cast(content); + return PSTRING() << td->videos_manager_->get_video_search_text(video->file_id) << " " << video->caption.text; + } + case MessageContentType::Contact: + case MessageContentType::Game: + case MessageContentType::Invoice: + case MessageContentType::LiveLocation: + case MessageContentType::Location: + case MessageContentType::Sticker: + case MessageContentType::Unsupported: + case MessageContentType::Venue: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: + case MessageContentType::ChatCreate: + case MessageContentType::ChatChangeTitle: + case MessageContentType::ChatChangePhoto: + case MessageContentType::ChatDeletePhoto: + case MessageContentType::ChatDeleteHistory: + case MessageContentType::ChatAddUsers: + case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatDeleteUser: + case MessageContentType::ChatMigrateTo: + case MessageContentType::ChannelCreate: + case MessageContentType::ChannelMigrateFrom: + case MessageContentType::PinMessage: + case MessageContentType::GameScore: + case MessageContentType::ScreenshotTaken: + case MessageContentType::ChatSetTtl: + case MessageContentType::Call: + case MessageContentType::PaymentSuccessful: + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + case MessageContentType::CustomServiceAction: + case MessageContentType::WebsiteConnected: + case MessageContentType::PassportDataSent: + case MessageContentType::PassportDataReceived: + return string(); + default: + UNREACHABLE(); + return string(); + } +} + +void update_expired_message_content(unique_ptr &content) { + switch (content->get_type()) { + case MessageContentType::Photo: + content = make_unique(); + break; + case MessageContentType::Video: + content = make_unique(); + break; + case MessageContentType::Unsupported: + // can happen if message content file id is broken + break; + case MessageContentType::ExpiredPhoto: + case MessageContentType::ExpiredVideo: + // can happen if message content has been reget from somewhere + break; + case MessageContentType::Animation: + case MessageContentType::Audio: + case MessageContentType::Document: + case MessageContentType::Sticker: + case MessageContentType::VideoNote: + case MessageContentType::VoiceNote: + // can happen if server will send a document with a wrong content + content = make_unique(); + break; + default: + UNREACHABLE(); + } +} + +void add_message_content_dependencies(Dependencies &dependencies, const MessageContent *message_content) { + switch (message_content->get_type()) { + case MessageContentType::Text: { + auto content = static_cast(message_content); + dependencies.web_page_ids.insert(content->web_page_id); + break; + } + case MessageContentType::Animation: + break; + case MessageContentType::Audio: + break; + case MessageContentType::Contact: { + auto content = static_cast(message_content); + dependencies.user_ids.insert(content->contact.get_user_id()); + break; + } + case MessageContentType::Document: + break; + case MessageContentType::Game: { + auto content = static_cast(message_content); + dependencies.user_ids.insert(content->game.get_bot_user_id()); + break; + } + case MessageContentType::Invoice: + break; + case MessageContentType::LiveLocation: + break; + case MessageContentType::Location: + break; + case MessageContentType::Photo: + break; + case MessageContentType::Sticker: + break; + case MessageContentType::Venue: + break; + case MessageContentType::Video: + break; + case MessageContentType::VideoNote: + break; + case MessageContentType::VoiceNote: + break; + case MessageContentType::ChatCreate: { + auto content = static_cast(message_content); + dependencies.user_ids.insert(content->participant_user_ids.begin(), content->participant_user_ids.end()); + break; + } + case MessageContentType::ChatChangeTitle: + break; + case MessageContentType::ChatChangePhoto: + break; + case MessageContentType::ChatDeletePhoto: + break; + case MessageContentType::ChatDeleteHistory: + break; + case MessageContentType::ChatAddUsers: { + auto content = static_cast(message_content); + dependencies.user_ids.insert(content->user_ids.begin(), content->user_ids.end()); + break; + } + case MessageContentType::ChatJoinedByLink: + break; + case MessageContentType::ChatDeleteUser: { + auto content = static_cast(message_content); + dependencies.user_ids.insert(content->user_id); + break; + } + case MessageContentType::ChatMigrateTo: { + auto content = static_cast(message_content); + dependencies.channel_ids.insert(content->migrated_to_channel_id); + break; + } + case MessageContentType::ChannelCreate: + break; + case MessageContentType::ChannelMigrateFrom: { + auto content = static_cast(message_content); + dependencies.chat_ids.insert(content->migrated_from_chat_id); + break; + } + case MessageContentType::PinMessage: + break; + case MessageContentType::GameScore: + break; + case MessageContentType::ScreenshotTaken: + break; + case MessageContentType::ChatSetTtl: + break; + case MessageContentType::Unsupported: + break; + case MessageContentType::Call: + break; + case MessageContentType::PaymentSuccessful: + break; + case MessageContentType::ContactRegistered: + break; + case MessageContentType::ExpiredPhoto: + break; + case MessageContentType::ExpiredVideo: + break; + case MessageContentType::CustomServiceAction: + break; + case MessageContentType::WebsiteConnected: + break; + case MessageContentType::PassportDataSent: + break; + case MessageContentType::PassportDataReceived: + break; + default: + UNREACHABLE(); + break; + } + add_formatted_text_dependencies(dependencies, get_message_content_text(message_content)); +} + +} // namespace td diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h new file mode 100644 index 000000000..b613c4ede --- /dev/null +++ b/td/telegram/MessageContent.h @@ -0,0 +1,227 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/Dependencies.h" +#include "td/telegram/DialogId.h" +#include "td/telegram/files/FileId.h" +#include "td/telegram/logevent/LogEvent.h" +#include "td/telegram/MessageEntity.h" +#include "td/telegram/MessageId.h" +#include "td/telegram/Photo.h" +#include "td/telegram/secret_api.h" +#include "td/telegram/ReplyMarkup.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" +#include "td/telegram/Version.h" +#include "td/telegram/WebPageId.h" + +#include "td/utils/buffer.h" +#include "td/utils/common.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class Game; +struct Photo; +class Td; + +class MultiPromiseActor; + +enum class MessageContentType : int32 { + None = -1, + Text, + Animation, + Audio, + Document, + Photo, + Sticker, + Video, + VoiceNote, + Contact, + Location, + Venue, + ChatCreate, + ChatChangeTitle, + ChatChangePhoto, + ChatDeletePhoto, + ChatDeleteHistory, + ChatAddUsers, + ChatJoinedByLink, + ChatDeleteUser, + ChatMigrateTo, + ChannelCreate, + ChannelMigrateFrom, + PinMessage, + Game, + GameScore, + ScreenshotTaken, + ChatSetTtl, + Unsupported, + Call, + Invoice, + PaymentSuccessful, + VideoNote, + ContactRegistered, + ExpiredPhoto, + ExpiredVideo, + LiveLocation, + CustomServiceAction, + WebsiteConnected, + PassportDataSent, + PassportDataReceived +}; + +StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type); + +// Do not forget to update merge_message_contents when one of the inheritors of this class changes +class MessageContent { + public: + MessageContent() = default; + MessageContent(const MessageContent &) = default; + MessageContent &operator=(const MessageContent &) = default; + MessageContent(MessageContent &&) = default; + MessageContent &operator=(MessageContent &&) = default; + + virtual MessageContentType get_type() const = 0; + virtual ~MessageContent() = default; +}; + +struct InputMessageContent { + unique_ptr content; + bool disable_web_page_preview = false; + bool clear_draft = false; + int32 ttl = 0; + UserId via_bot_user_id; + + InputMessageContent(unique_ptr &&content, bool disable_web_page_preview, bool clear_draft, int32 ttl, + UserId via_bot_user_id) + : content(std::move(content)) + , disable_web_page_preview(disable_web_page_preview) + , clear_draft(clear_draft) + , ttl(ttl) + , via_bot_user_id(via_bot_user_id) { + } +}; + +struct InlineMessageContent { + unique_ptr message_content; + unique_ptr message_reply_markup; + bool disable_web_page_preview; +}; + +void store_message_content(const MessageContent *content, LogEventStorerCalcLength &storer); + +void store_message_content(const MessageContent *content, LogEventStorerUnsafe &storer); + +void parse_message_content(unique_ptr &content, LogEventParser &parser); + +InlineMessageContent create_inline_message_content(Td *td, FileId file_id, + tl_object_ptr &&inline_message, + int32 allowed_media_content_id, Photo *photo, Game *game); + +unique_ptr create_text_message_content(string text, vector entities, + WebPageId web_page_id); + +unique_ptr create_contact_registered_message_content(); + +unique_ptr create_screenshot_taken_message_content(); + +unique_ptr create_chat_set_ttl_message_content(int32 ttl); + +Result create_input_message_content( + DialogId dialog_id, tl_object_ptr &&input_message_content, Td *td, + FormattedText caption, FileId file_id, PhotoSize thumbnail, vector sticker_file_ids); + +SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td, + tl_object_ptr input_file, + BufferSlice thumbnail, int32 layer); + +tl_object_ptr get_input_media(const MessageContent *content, Td *td, + tl_object_ptr input_file, + tl_object_ptr input_thumbnail, + int32 ttl); + +void delete_message_content_thumbnail(MessageContent *content, Td *td); + +bool is_allowed_media_group_content(MessageContentType content_type); + +bool can_forward_message_content(const MessageContent *content); + +bool is_secret_message_content(int32 ttl, MessageContentType content_type); + +bool is_service_message_content(MessageContentType content_type); + +bool can_have_message_content_caption(MessageContentType content_type); + +bool update_opened_message_content(MessageContent *content); + +int32 get_message_content_index_mask(const MessageContent *content, const Td *td, bool is_secret, bool is_outgoing); + +int32 get_message_content_new_participant_count(const MessageContent *content); + +MessageId get_message_content_pinned_message_id(const MessageContent *content); + +MessageId get_message_content_replied_message_id(const MessageContent *content); + +UserId get_message_content_deleted_user_id(const MessageContent *content); + +int32 get_message_content_live_location_period(const MessageContent *content); + +WebPageId get_message_content_web_page_id(const MessageContent *content); + +void set_message_content_web_page_id(MessageContent *content, WebPageId web_page_id); + +void merge_message_contents(Td *td, MessageContent *old_content, MessageContent *new_content, + bool need_message_changed_warning, DialogId dialog_id, bool need_merge_files, + bool &is_content_changed, bool &need_update); + +bool merge_message_content_file_id(Td *td, MessageContent *message_content, FileId new_file_id); + +unique_ptr get_secret_message_content( + Td *td, string message_text, tl_object_ptr file, + tl_object_ptr &&media, + vector> &&secret_entities, DialogId owner_dialog_id, + MultiPromiseActor &load_data_multipromise); + +unique_ptr get_message_content(Td *td, FormattedText message_text, + tl_object_ptr &&media, + DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id, + int32 *ttl); + +unique_ptr dup_message_content(Td *td, DialogId dialog_id, const MessageContent *content, + bool for_forward); + +unique_ptr get_action_message_content(Td *td, tl_object_ptr &&action, + DialogId owner_dialog_id, MessageId reply_to_message_id); + +tl_object_ptr get_message_content_object(const MessageContent *content, Td *td, + int32 message_date, bool is_content_secret); + +const FormattedText *get_message_content_text(const MessageContent *content); + +const FormattedText *get_message_content_caption(const MessageContent *content); + +int32 get_message_content_duration(const MessageContent *content, const Td *td); + +FileId get_message_content_file_id(const MessageContent *content); + +void update_message_content_file_id_remote(MessageContent *content, FileId file_id); + +FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td); + +vector get_message_content_file_ids(const MessageContent *content, const Td *td); + +string get_message_content_search_text(const Td *td, const MessageContent *content); + +void update_expired_message_content(unique_ptr &content); + +void add_message_content_dependencies(Dependencies &dependencies, const MessageContent *message_content); + +} // namespace td diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index bfcff0b74..4dcdc7e4a 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -2205,6 +2205,24 @@ Status fix_formatted_text(string &text, vector &entities, bool al return Status::OK(); } +FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text, + vector> &&server_entities, int32 send_date, + const char *source) { + auto entities = get_message_entities(contacts_manager, std::move(server_entities), source); + auto status = fix_formatted_text(message_text, entities, true, true, true, false); + if (status.is_error()) { + if (send_date == 0 || send_date > 1497000000) { // approximate fix date + LOG(ERROR) << "Receive error " << status << " while parsing message from " << source << " with content \"" + << message_text << "\" sent at " << send_date << " with entities " << format::as_array(entities); + } + if (!clean_input_string(message_text)) { + message_text.clear(); + } + entities.clear(); + } + return FormattedText{std::move(message_text), std::move(entities)}; +} + void add_formatted_text_dependencies(Dependencies &dependencies, const FormattedText *text) { if (text == nullptr) { return; @@ -2216,4 +2234,26 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted } } +bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot) { + if (is_bot) { + return false; + } + + switch (dialog_id.get_type()) { + case DialogType::User: + return !contacts_manager->is_user_bot(dialog_id.get_user_id()); + case DialogType::SecretChat: { + auto user_id = contacts_manager->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + return !contacts_manager->is_user_bot(user_id); + } + case DialogType::Chat: + case DialogType::Channel: + case DialogType::None: + return false; + default: + UNREACHABLE(); + return false; + } +} + } // namespace td diff --git a/td/telegram/MessageEntity.h b/td/telegram/MessageEntity.h index fe28bd251..ff38c7dc9 100644 --- a/td/telegram/MessageEntity.h +++ b/td/telegram/MessageEntity.h @@ -149,6 +149,12 @@ vector get_message_entities(vector &entities, bool allow_empty, bool skip_new_entities, bool skip_bot_commands, bool for_draft) TD_WARN_UNUSED_RESULT; +FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text, + vector> &&server_entities, int32 send_date, + const char *source); + void add_formatted_text_dependencies(Dependencies &dependencies, const FormattedText *text); +bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot); + } // namespace td diff --git a/td/telegram/MessagesDb.h b/td/telegram/MessagesDb.h index 71676ead2..65335d48c 100644 --- a/td/telegram/MessagesDb.h +++ b/td/telegram/MessagesDb.h @@ -21,6 +21,7 @@ #include namespace td { + // append only before Size enum class SearchMessagesFilter : int32 { Empty, @@ -166,4 +167,26 @@ std::shared_ptr create_messages_db_sync( std::shared_ptr create_messages_db_async(std::shared_ptr sync_db, int32 scheduler_id); + +inline constexpr size_t search_messages_filter_size() { + return static_cast(SearchMessagesFilter::Size) - 1; +} + +inline int32 search_messages_filter_index(SearchMessagesFilter filter) { + CHECK(filter != SearchMessagesFilter::Empty); + return static_cast(filter) - 1; +} + +inline int32 search_messages_filter_index_mask(SearchMessagesFilter filter) { + if (filter == SearchMessagesFilter::Empty) { + return 0; + } + return 1 << search_messages_filter_index(filter); +} + +inline int32 search_calls_filter_index(SearchMessagesFilter filter) { + CHECK(filter == SearchMessagesFilter::Call || filter == SearchMessagesFilter::MissedCall); + return static_cast(filter) - static_cast(SearchMessagesFilter::Call); +} + } // namespace td diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index fd1b94f6b..0c9b2ecaf 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -7,21 +7,16 @@ #include "td/telegram/MessagesManager.h" #include "td/telegram/AnimationsManager.h" -#include "td/telegram/AnimationsManager.hpp" -#include "td/telegram/AudiosManager.h" -#include "td/telegram/AudiosManager.hpp" #include "td/telegram/AuthManager.h" #include "td/telegram/ConfigShared.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogDb.h" -#include "td/telegram/DocumentsManager.h" -#include "td/telegram/DocumentsManager.hpp" #include "td/telegram/files/FileManager.h" -#include "td/telegram/Game.h" -#include "td/telegram/Game.hpp" +#include "td/telegram/files/FileId.hpp" #include "td/telegram/Global.h" #include "td/telegram/HashtagHints.h" #include "td/telegram/InlineQueriesManager.h" +#include "td/telegram/InputMessageText.hpp" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessageEntity.hpp" @@ -31,29 +26,19 @@ #include "td/telegram/net/NetActor.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/Payments.h" -#include "td/telegram/Payments.hpp" +#include "td/telegram/ReplyMarkup.h" #include "td/telegram/ReplyMarkup.hpp" #include "td/telegram/SecretChatActor.h" #include "td/telegram/SecretChatsManager.h" -#include "td/telegram/SecureValue.hpp" #include "td/telegram/SequenceDispatcher.h" #include "td/telegram/StickersManager.h" -#include "td/telegram/StickersManager.hpp" #include "td/telegram/Td.h" #include "td/telegram/TopDialogManager.h" #include "td/telegram/UpdatesManager.h" #include "td/telegram/Version.h" -#include "td/telegram/VideoNotesManager.h" -#include "td/telegram/VideoNotesManager.hpp" -#include "td/telegram/VideosManager.h" -#include "td/telegram/VideosManager.hpp" -#include "td/telegram/VoiceNotesManager.h" -#include "td/telegram/VoiceNotesManager.hpp" #include "td/telegram/WebPageId.h" #include "td/telegram/WebPagesManager.h" -#include "td/telegram/secret_api.hpp" -#include "td/telegram/td_api.hpp" #include "td/telegram/telegram_api.h" #include "td/actor/PromiseFuture.h" @@ -63,10 +48,7 @@ #include "td/db/SqliteKeyValueAsync.h" #include "td/utils/format.h" -#include "td/utils/HttpUrl.h" -#include "td/utils/MimeType.h" #include "td/utils/misc.h" -#include "td/utils/PathView.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/Time.h" @@ -3539,15 +3521,6 @@ class GetChannelAdminLogQuery : public Td::ResultHandler { } }; -bool operator==(const InputMessageText &lhs, const InputMessageText &rhs) { - return lhs.text == rhs.text && lhs.disable_web_page_preview == rhs.disable_web_page_preview && - lhs.clear_draft == rhs.clear_draft; -} - -bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs) { - return !(lhs == rhs); -} - class MessagesManager::UploadMediaCallback : public FileManager::UploadCallback { public: void on_progress(FileId file_id) override { @@ -3602,670 +3575,6 @@ class MessagesManager::UploadDialogPhotoCallback : public FileManager::UploadCal } }; -StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type) { - switch (content_type) { - case MessageContentType::None: - return string_builder << "None"; - case MessageContentType::Animation: - return string_builder << "Animation"; - case MessageContentType::Audio: - return string_builder << "Audio"; - case MessageContentType::Document: - return string_builder << "Document"; - case MessageContentType::ExpiredPhoto: - return string_builder << "ExpiredPhoto"; - case MessageContentType::Photo: - return string_builder << "Photo"; - case MessageContentType::ExpiredVideo: - return string_builder << "ExpiredVideo"; - case MessageContentType::Video: - return string_builder << "Video"; - case MessageContentType::VideoNote: - return string_builder << "VideoNote"; - case MessageContentType::VoiceNote: - return string_builder << "VoiceNote"; - case MessageContentType::Contact: - return string_builder << "Contact"; - case MessageContentType::LiveLocation: - return string_builder << "LiveLocation"; - case MessageContentType::Location: - return string_builder << "Location"; - case MessageContentType::Venue: - return string_builder << "Venue"; - case MessageContentType::Game: - return string_builder << "Game"; - case MessageContentType::Invoice: - return string_builder << "Invoice"; - case MessageContentType::Sticker: - return string_builder << "Sticker"; - case MessageContentType::Text: - return string_builder << "Text"; - case MessageContentType::Unsupported: - return string_builder << "Unsupported"; - case MessageContentType::ChatCreate: - return string_builder << "ChatCreate"; - case MessageContentType::ChatChangeTitle: - return string_builder << "ChatChangeTitle"; - case MessageContentType::ChatChangePhoto: - return string_builder << "ChatChangePhoto"; - case MessageContentType::ChatDeletePhoto: - return string_builder << "ChatDeletePhoto"; - case MessageContentType::ChatDeleteHistory: - return string_builder << "ChatDeleteHistory"; - case MessageContentType::ChatAddUsers: - return string_builder << "ChatAddUsers"; - case MessageContentType::ChatJoinedByLink: - return string_builder << "ChatJoinedByLink"; - case MessageContentType::ChatDeleteUser: - return string_builder << "ChatDeleteUser"; - case MessageContentType::ChatMigrateTo: - return string_builder << "ChatMigrateTo"; - case MessageContentType::ChannelCreate: - return string_builder << "ChannelCreate"; - case MessageContentType::ChannelMigrateFrom: - return string_builder << "ChannelMigrateFrom"; - case MessageContentType::PinMessage: - return string_builder << "PinMessage"; - case MessageContentType::GameScore: - return string_builder << "GameScore"; - case MessageContentType::ScreenshotTaken: - return string_builder << "ScreenshotTaken"; - case MessageContentType::ChatSetTtl: - return string_builder << "ChatSetTtl"; - case MessageContentType::Call: - return string_builder << "Call"; - case MessageContentType::PaymentSuccessful: - return string_builder << "PaymentSuccessful"; - case MessageContentType::ContactRegistered: - return string_builder << "ContactRegistered"; - case MessageContentType::CustomServiceAction: - return string_builder << "CustomServiceAction"; - case MessageContentType::WebsiteConnected: - return string_builder << "WebsiteConnected"; - case MessageContentType::PassportDataSent: - return string_builder << "PassportDataSent"; - case MessageContentType::PassportDataReceived: - return string_builder << "PassportDataReceived"; - default: - UNREACHABLE(); - return string_builder; - } -} - -template -static void store(const MessageContent *content, StorerT &storer) { - CHECK(content != nullptr); - - Td *td = storer.context()->td().get_actor_unsafe(); - CHECK(td != nullptr); - - auto content_type = content->get_type(); - store(content_type, storer); - - switch (content_type) { - case MessageContentType::Animation: { - auto m = static_cast(content); - td->animations_manager_->store_animation(m->file_id, storer); - store(m->caption, storer); - break; - } - case MessageContentType::Audio: { - auto m = static_cast(content); - td->audios_manager_->store_audio(m->file_id, storer); - store(m->caption, storer); - store(true, storer); - break; - } - case MessageContentType::Contact: { - auto m = static_cast(content); - store(m->contact, storer); - break; - } - case MessageContentType::Document: { - auto m = static_cast(content); - td->documents_manager_->store_document(m->file_id, storer); - store(m->caption, storer); - break; - } - case MessageContentType::Game: { - auto m = static_cast(content); - store(m->game, storer); - break; - } - case MessageContentType::Invoice: { - auto m = static_cast(content); - store(m->title, storer); - store(m->description, storer); - store(m->photo, storer); - store(m->start_parameter, storer); - store(m->invoice, storer); - store(m->payload, storer); - store(m->provider_token, storer); - store(m->provider_data, storer); - store(m->total_amount, storer); - store(m->receipt_message_id, storer); - break; - } - case MessageContentType::LiveLocation: { - auto m = static_cast(content); - store(m->location, storer); - store(m->period, storer); - break; - } - case MessageContentType::Location: { - auto m = static_cast(content); - store(m->location, storer); - break; - } - case MessageContentType::Photo: { - auto m = static_cast(content); - store(m->photo, storer); - store(m->caption, storer); - break; - } - case MessageContentType::Sticker: { - auto m = static_cast(content); - td->stickers_manager_->store_sticker(m->file_id, false, storer); - break; - } - case MessageContentType::Text: { - auto m = static_cast(content); - store(m->text, storer); - store(m->web_page_id, storer); - break; - } - case MessageContentType::Unsupported: - break; - case MessageContentType::Venue: { - auto m = static_cast(content); - store(m->venue, storer); - break; - } - case MessageContentType::Video: { - auto m = static_cast(content); - td->videos_manager_->store_video(m->file_id, storer); - store(m->caption, storer); - break; - } - case MessageContentType::VideoNote: { - auto m = static_cast(content); - td->video_notes_manager_->store_video_note(m->file_id, storer); - store(m->is_viewed, storer); - break; - } - case MessageContentType::VoiceNote: { - auto m = static_cast(content); - td->voice_notes_manager_->store_voice_note(m->file_id, storer); - store(m->caption, storer); - store(m->is_listened, storer); - break; - } - case MessageContentType::ChatCreate: { - auto m = static_cast(content); - store(m->title, storer); - store(m->participant_user_ids, storer); - break; - } - case MessageContentType::ChatChangeTitle: { - auto m = static_cast(content); - store(m->title, storer); - break; - } - case MessageContentType::ChatChangePhoto: { - auto m = static_cast(content); - store(m->photo, storer); - break; - } - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - break; - case MessageContentType::ChatAddUsers: { - auto m = static_cast(content); - store(m->user_ids, storer); - break; - } - case MessageContentType::ChatJoinedByLink: - break; - case MessageContentType::ChatDeleteUser: { - auto m = static_cast(content); - store(m->user_id, storer); - break; - } - case MessageContentType::ChatMigrateTo: { - auto m = static_cast(content); - store(m->migrated_to_channel_id, storer); - break; - } - case MessageContentType::ChannelCreate: { - auto m = static_cast(content); - store(m->title, storer); - break; - } - case MessageContentType::ChannelMigrateFrom: { - auto m = static_cast(content); - store(m->title, storer); - store(m->migrated_from_chat_id, storer); - break; - } - case MessageContentType::PinMessage: { - auto m = static_cast(content); - store(m->message_id, storer); - break; - } - case MessageContentType::GameScore: { - auto m = static_cast(content); - store(m->game_message_id, storer); - store(m->game_id, storer); - store(m->score, storer); - break; - } - case MessageContentType::ScreenshotTaken: - break; - case MessageContentType::ChatSetTtl: { - auto m = static_cast(content); - store(m->ttl, storer); - break; - } - case MessageContentType::Call: { - auto m = static_cast(content); - store(m->call_id, storer); - store(m->duration, storer); - store(m->discard_reason, storer); - break; - } - case MessageContentType::PaymentSuccessful: { - auto m = static_cast(content); - bool has_payload = !m->invoice_payload.empty(); - bool has_shipping_option_id = !m->shipping_option_id.empty(); - bool has_order_info = m->order_info != nullptr; - bool has_telegram_payment_charge_id = !m->telegram_payment_charge_id.empty(); - bool has_provider_payment_charge_id = !m->provider_payment_charge_id.empty(); - bool has_invoice_message_id = m->invoice_message_id.is_valid(); - BEGIN_STORE_FLAGS(); - STORE_FLAG(has_payload); - STORE_FLAG(has_shipping_option_id); - STORE_FLAG(has_order_info); - STORE_FLAG(has_telegram_payment_charge_id); - STORE_FLAG(has_provider_payment_charge_id); - STORE_FLAG(has_invoice_message_id); - END_STORE_FLAGS(); - store(m->currency, storer); - store(m->total_amount, storer); - if (has_payload) { - store(m->total_amount, storer); - } - if (has_shipping_option_id) { - store(m->invoice_payload, storer); - } - if (has_order_info) { - store(*m->order_info, storer); - } - if (has_telegram_payment_charge_id) { - store(m->telegram_payment_charge_id, storer); - } - if (has_provider_payment_charge_id) { - store(m->provider_payment_charge_id, storer); - } - if (has_invoice_message_id) { - store(m->invoice_message_id, storer); - } - break; - } - case MessageContentType::ContactRegistered: - break; - case MessageContentType::ExpiredPhoto: - break; - case MessageContentType::ExpiredVideo: - break; - case MessageContentType::CustomServiceAction: { - auto m = static_cast(content); - store(m->message, storer); - break; - } - case MessageContentType::WebsiteConnected: { - auto m = static_cast(content); - store(m->domain_name, storer); - break; - } - case MessageContentType::PassportDataSent: { - auto m = static_cast(content); - store(m->types, storer); - break; - } - case MessageContentType::PassportDataReceived: { - auto m = static_cast(content); - store(m->values, storer); - store(m->credentials, storer); - break; - } - default: - UNREACHABLE(); - } -} - -template -static void parse_caption(FormattedText &caption, ParserT &parser) { - parse(caption.text, parser); - if (parser.version() >= static_cast(Version::AddCaptionEntities)) { - parse(caption.entities, parser); - } else { - caption.entities = find_entities(caption.text, false); - } -} - -template -static void parse(unique_ptr &content, ParserT &parser) { - Td *td = parser.context()->td().get_actor_unsafe(); - CHECK(td != nullptr); - - MessageContentType content_type; - parse(content_type, parser); - - bool is_bad = false; - switch (content_type) { - case MessageContentType::Animation: { - auto m = make_unique(); - m->file_id = td->animations_manager_->parse_animation(parser); - parse_caption(m->caption, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::Audio: { - auto m = make_unique(); - m->file_id = td->audios_manager_->parse_audio(parser); - parse_caption(m->caption, parser); - bool legacy_is_listened; - parse(legacy_is_listened, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::Contact: { - auto m = make_unique(); - parse(m->contact, parser); - content = std::move(m); - break; - } - case MessageContentType::Document: { - auto m = make_unique(); - m->file_id = td->documents_manager_->parse_document(parser); - parse_caption(m->caption, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::Game: { - auto m = make_unique(); - parse(m->game, parser); - content = std::move(m); - break; - } - case MessageContentType::Invoice: { - auto m = make_unique(); - parse(m->title, parser); - parse(m->description, parser); - parse(m->photo, parser); - parse(m->start_parameter, parser); - parse(m->invoice, parser); - parse(m->payload, parser); - parse(m->provider_token, parser); - if (parser.version() >= static_cast(Version::AddMessageInvoiceProviderData)) { - parse(m->provider_data, parser); - } else { - m->provider_data.clear(); - } - parse(m->total_amount, parser); - parse(m->receipt_message_id, parser); - content = std::move(m); - break; - } - case MessageContentType::LiveLocation: { - auto m = make_unique(); - parse(m->location, parser); - parse(m->period, parser); - content = std::move(m); - break; - } - case MessageContentType::Location: { - auto m = make_unique(); - parse(m->location, parser); - content = std::move(m); - break; - } - case MessageContentType::Photo: { - auto m = make_unique(); - parse(m->photo, parser); - for (auto &photo_size : m->photo.photos) { - if (!photo_size.file_id.is_valid()) { - is_bad = true; - } - } - parse_caption(m->caption, parser); - content = std::move(m); - break; - } - case MessageContentType::Sticker: { - auto m = make_unique(); - m->file_id = td->stickers_manager_->parse_sticker(false, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::Text: { - auto m = make_unique(); - parse(m->text, parser); - parse(m->web_page_id, parser); - content = std::move(m); - break; - } - case MessageContentType::Unsupported: - content = make_unique(); - break; - case MessageContentType::Venue: { - auto m = make_unique(); - parse(m->venue, parser); - content = std::move(m); - break; - } - case MessageContentType::Video: { - auto m = make_unique(); - m->file_id = td->videos_manager_->parse_video(parser); - parse_caption(m->caption, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::VideoNote: { - auto m = make_unique(); - m->file_id = td->video_notes_manager_->parse_video_note(parser); - parse(m->is_viewed, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::VoiceNote: { - auto m = make_unique(); - m->file_id = td->voice_notes_manager_->parse_voice_note(parser); - parse_caption(m->caption, parser); - parse(m->is_listened, parser); - is_bad = !m->file_id.is_valid(); - content = std::move(m); - break; - } - case MessageContentType::ChatCreate: { - auto m = make_unique(); - parse(m->title, parser); - parse(m->participant_user_ids, parser); - content = std::move(m); - break; - } - case MessageContentType::ChatChangeTitle: { - auto m = make_unique(); - parse(m->title, parser); - content = std::move(m); - break; - } - case MessageContentType::ChatChangePhoto: { - auto m = make_unique(); - parse(m->photo, parser); - content = std::move(m); - break; - } - case MessageContentType::ChatDeletePhoto: - content = make_unique(); - break; - case MessageContentType::ChatDeleteHistory: - content = make_unique(); - break; - case MessageContentType::ChatAddUsers: { - auto m = make_unique(); - parse(m->user_ids, parser); - content = std::move(m); - break; - } - case MessageContentType::ChatJoinedByLink: - content = make_unique(); - break; - case MessageContentType::ChatDeleteUser: { - auto m = make_unique(); - parse(m->user_id, parser); - content = std::move(m); - break; - } - case MessageContentType::ChatMigrateTo: { - auto m = make_unique(); - parse(m->migrated_to_channel_id, parser); - content = std::move(m); - break; - } - case MessageContentType::ChannelCreate: { - auto m = make_unique(); - parse(m->title, parser); - content = std::move(m); - break; - } - case MessageContentType::ChannelMigrateFrom: { - auto m = make_unique(); - parse(m->title, parser); - parse(m->migrated_from_chat_id, parser); - content = std::move(m); - break; - } - case MessageContentType::PinMessage: { - auto m = make_unique(); - parse(m->message_id, parser); - content = std::move(m); - break; - } - case MessageContentType::GameScore: { - auto m = make_unique(); - parse(m->game_message_id, parser); - parse(m->game_id, parser); - parse(m->score, parser); - content = std::move(m); - break; - } - case MessageContentType::ScreenshotTaken: - content = make_unique(); - break; - case MessageContentType::ChatSetTtl: { - auto m = make_unique(); - parse(m->ttl, parser); - content = std::move(m); - break; - } - case MessageContentType::Call: { - auto m = make_unique(); - parse(m->call_id, parser); - parse(m->duration, parser); - parse(m->discard_reason, parser); - content = std::move(m); - break; - } - case MessageContentType::PaymentSuccessful: { - auto m = make_unique(); - bool has_payload; - bool has_shipping_option_id; - bool has_order_info; - bool has_telegram_payment_charge_id; - bool has_provider_payment_charge_id; - bool has_invoice_message_id; - BEGIN_PARSE_FLAGS(); - PARSE_FLAG(has_payload); - PARSE_FLAG(has_shipping_option_id); - PARSE_FLAG(has_order_info); - PARSE_FLAG(has_telegram_payment_charge_id); - PARSE_FLAG(has_provider_payment_charge_id); - PARSE_FLAG(has_invoice_message_id); - END_PARSE_FLAGS(); - parse(m->currency, parser); - parse(m->total_amount, parser); - if (has_payload) { - parse(m->total_amount, parser); - } - if (has_shipping_option_id) { - parse(m->invoice_payload, parser); - } - if (has_order_info) { - m->order_info = make_unique(); - parse(*m->order_info, parser); - } - if (has_telegram_payment_charge_id) { - parse(m->telegram_payment_charge_id, parser); - } - if (has_provider_payment_charge_id) { - parse(m->provider_payment_charge_id, parser); - } - if (has_invoice_message_id) { - parse(m->invoice_message_id, parser); - } - content = std::move(m); - break; - } - case MessageContentType::ContactRegistered: - content = make_unique(); - break; - case MessageContentType::ExpiredPhoto: - content = make_unique(); - break; - case MessageContentType::ExpiredVideo: - content = make_unique(); - break; - case MessageContentType::CustomServiceAction: { - auto m = make_unique(); - parse(m->message, parser); - content = std::move(m); - break; - } - case MessageContentType::WebsiteConnected: { - auto m = make_unique(); - parse(m->domain_name, parser); - content = std::move(m); - break; - } - case MessageContentType::PassportDataSent: { - auto m = make_unique(); - parse(m->types, parser); - content = std::move(m); - break; - } - case MessageContentType::PassportDataReceived: { - auto m = make_unique(); - parse(m->values, parser); - parse(m->credentials, parser); - content = std::move(m); - break; - } - default: - UNREACHABLE(); - } - if (is_bad) { - LOG(ERROR) << "Load a message with an invalid content of type " << content_type; - content = make_unique(); - } -} - template void MessagesManager::Message::store(StorerT &storer) const { using td::store; @@ -4373,7 +3682,7 @@ void MessagesManager::Message::store(StorerT &storer) const { if (has_media_album_id) { store(media_album_id, storer); } - store(content.get(), storer); + store_message_content(content.get(), storer); if (has_reply_markup) { store(*reply_markup, storer); } @@ -4494,7 +3803,7 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_media_album_id) { parse(media_album_id, parser); } - parse(content, parser); + parse_message_content(content, parser); if (has_reply_markup) { reply_markup = make_unique(); parse(*reply_markup, parser); @@ -4590,24 +3899,6 @@ static void parse(ScopeNotificationSettings ¬ification_settings, ParserT &par } } -template -static void store(const InputMessageText &input_message_text, StorerT &storer) { - BEGIN_STORE_FLAGS(); - STORE_FLAG(input_message_text.disable_web_page_preview); - STORE_FLAG(input_message_text.clear_draft); - END_STORE_FLAGS(); - store(input_message_text.text, storer); -} - -template -static void parse(InputMessageText &input_message_text, ParserT &parser) { - BEGIN_PARSE_FLAGS(); - PARSE_FLAG(input_message_text.disable_web_page_preview); - PARSE_FLAG(input_message_text.clear_draft); - END_PARSE_FLAGS(); - parse(input_message_text.text, parser); -} - template static void store(const DraftMessage &draft_message, StorerT &storer) { store(draft_message.date, storer); @@ -5167,96 +4458,6 @@ int32 MessagesManager::get_message_index_mask(DialogId dialog_id, const Message return mentions_mask; } -int32 MessagesManager::get_message_content_index_mask(const MessageContent *content, const Td *td, bool is_secret, - bool is_outgoing) { - switch (content->get_type()) { - case MessageContentType::Animation: - return search_messages_filter_index_mask(SearchMessagesFilter::Animation); - case MessageContentType::Audio: { - auto message_audio = static_cast(content); - auto duration = td->audios_manager_->get_audio_duration(message_audio->file_id); - return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::Audio) - : search_messages_filter_index_mask(SearchMessagesFilter::Document); - } - case MessageContentType::Document: - return search_messages_filter_index_mask(SearchMessagesFilter::Document); - case MessageContentType::Photo: - return search_messages_filter_index_mask(SearchMessagesFilter::Photo) | - search_messages_filter_index_mask(SearchMessagesFilter::PhotoAndVideo); - case MessageContentType::Text: - for (auto &entity : static_cast(content)->text.entities) { - if (entity.type == MessageEntity::Type::Url || entity.type == MessageEntity::Type::EmailAddress || - entity.type == MessageEntity::Type::TextUrl) { - return search_messages_filter_index_mask(SearchMessagesFilter::Url); - } - } - return 0; - case MessageContentType::Video: { - auto message_video = static_cast(content); - auto duration = td->videos_manager_->get_video_duration(message_video->file_id); - return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::Video) | - search_messages_filter_index_mask(SearchMessagesFilter::PhotoAndVideo) - : search_messages_filter_index_mask(SearchMessagesFilter::Document); - } - case MessageContentType::VideoNote: { - auto message_video_note = static_cast(content); - auto duration = td->video_notes_manager_->get_video_note_duration(message_video_note->file_id); - return is_secret || duration > 0 ? search_messages_filter_index_mask(SearchMessagesFilter::VideoNote) | - search_messages_filter_index_mask(SearchMessagesFilter::VoiceAndVideoNote) - : search_messages_filter_index_mask(SearchMessagesFilter::Document); - } - case MessageContentType::VoiceNote: - return search_messages_filter_index_mask(SearchMessagesFilter::VoiceNote) | - search_messages_filter_index_mask(SearchMessagesFilter::VoiceAndVideoNote); - case MessageContentType::ChatChangePhoto: - return search_messages_filter_index_mask(SearchMessagesFilter::ChatPhoto); - case MessageContentType::Call: { - int32 index_mask = search_messages_filter_index_mask(SearchMessagesFilter::Call); - auto message_call = static_cast(content); - if (!is_outgoing && (message_call->discard_reason == CallDiscardReason::Declined || - message_call->discard_reason == CallDiscardReason::Missed)) { - index_mask |= search_messages_filter_index_mask(SearchMessagesFilter::MissedCall); - } - return index_mask; - } - case MessageContentType::Contact: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Sticker: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return 0; - default: - UNREACHABLE(); - return 0; - } - return 0; -} - vector MessagesManager::get_message_ids(const vector &input_message_ids) { vector message_ids; message_ids.reserve(input_message_ids.size()); @@ -5775,7 +4976,7 @@ void MessagesManager::on_update_contact_registered(tl_object_ptrrandom_y = get_random_y(new_message->message_id); new_message->sender_user_id = user_id; new_message->date = update->date_; - new_message->content = make_unique(); + new_message->content = create_contact_registered_message_content(); new_message->have_previous = true; new_message->have_next = true; @@ -7966,159 +7167,6 @@ void MessagesManager::delete_dialog_messages_from_updates(DialogId dialog_id, co send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false); } -bool MessagesManager::is_secret_message_content(int32 ttl, MessageContentType content_type) { - if (ttl <= 0 || ttl > 60) { - return false; - } - switch (content_type) { - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Photo: - case MessageContentType::Video: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: - return true; - case MessageContentType::Contact: - case MessageContentType::Document: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Sticker: - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return false; - default: - UNREACHABLE(); - return false; - } -} - -bool MessagesManager::is_service_message_content(MessageContentType content_type) { - switch (content_type) { - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Contact: - case MessageContentType::Document: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Photo: - case MessageContentType::Sticker: - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::Video: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - return false; - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return true; - default: - UNREACHABLE(); - return false; - } -} - -bool MessagesManager::can_have_message_content_caption(MessageContentType content_type) { - switch (content_type) { - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Document: - case MessageContentType::Photo: - case MessageContentType::Video: - case MessageContentType::VoiceNote: - return true; - case MessageContentType::Contact: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Sticker: - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::VideoNote: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return false; - default: - UNREACHABLE(); - return false; - } -} - string MessagesManager::get_search_text(const Message *m) const { if (m->is_content_secret) { return string(); @@ -8126,127 +7174,6 @@ string MessagesManager::get_search_text(const Message *m) const { return get_message_content_search_text(td_, m->content.get()); } -string MessagesManager::get_message_content_search_text(const Td *td, const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::Text: { - auto *text = static_cast(content); - if (!text->web_page_id.is_valid()) { - return text->text.text; - } - return PSTRING() << text->text.text << " " << td->web_pages_manager_->get_web_page_search_text(text->web_page_id); - } - case MessageContentType::Animation: { - auto animation = static_cast(content); - return PSTRING() << td->animations_manager_->get_animation_search_text(animation->file_id) << " " - << animation->caption.text; - } - case MessageContentType::Audio: { - auto audio = static_cast(content); - return PSTRING() << td->audios_manager_->get_audio_search_text(audio->file_id) << " " << audio->caption.text; - } - case MessageContentType::Document: { - auto document = static_cast(content); - return PSTRING() << td->documents_manager_->get_document_search_text(document->file_id) << " " - << document->caption.text; - } - case MessageContentType::Photo: { - auto photo = static_cast(content); - return photo->caption.text; - } - case MessageContentType::Video: { - auto video = static_cast(content); - return PSTRING() << td->videos_manager_->get_video_search_text(video->file_id) << " " << video->caption.text; - } - case MessageContentType::Contact: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Sticker: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return string(); - default: - UNREACHABLE(); - return string(); - } -} - -bool MessagesManager::is_allowed_media_group_content(MessageContentType content_type) { - switch (content_type) { - case MessageContentType::Photo: - case MessageContentType::Video: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - return true; - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Contact: - case MessageContentType::Document: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Sticker: - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::Venue: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return false; - default: - UNREACHABLE(); - return false; - } -} - bool MessagesManager::can_forward_message(DialogId from_dialog_id, const Message *m) { if (m == nullptr) { return false; @@ -8271,17 +7198,6 @@ bool MessagesManager::can_forward_message(DialogId from_dialog_id, const Message return can_forward_message_content(m->content.get()); } -bool MessagesManager::can_forward_message_content(const MessageContent *content) { - auto content_type = content->get_type(); - if (content_type == MessageContentType::Text) { - auto *text = static_cast(content); - return !is_empty_string(text->text.text); // text can't be empty in the new message - } - - return !is_service_message_content(content_type) && content_type != MessageContentType::Unsupported && - content_type != MessageContentType::ExpiredPhoto && content_type != MessageContentType::ExpiredVideo; -} - bool MessagesManager::can_delete_channel_message(DialogParticipantStatus status, const Message *m, bool is_bot) { if (m == nullptr) { return true; @@ -9057,29 +7973,6 @@ void MessagesManager::read_channel_message_content_from_updates(Dialog *d, Messa } } -bool MessagesManager::update_opened_message_content(MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::VideoNote: { - auto video_note_content = static_cast(content); - if (video_note_content->is_viewed) { - return false; - } - video_note_content->is_viewed = true; - return true; - } - case MessageContentType::VoiceNote: { - auto voice_note_content = static_cast(content); - if (voice_note_content->is_listened) { - return false; - } - voice_note_content->is_listened = true; - return true; - } - default: - return false; - } -} - bool MessagesManager::read_message_content(Dialog *d, Message *m, bool is_local_read, const char *source) { CHECK(m != nullptr) << source; bool is_mention_read = update_message_contains_unread_mention(d, m, false, "read_message_content"); @@ -9503,192 +8396,6 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, } } -tl_object_ptr MessagesManager::get_message_content_object(const MessageContent *content, Td *td, - int32 message_date, - bool is_content_secret) { - CHECK(content != nullptr); - switch (content->get_type()) { - case MessageContentType::Animation: { - const MessageAnimation *m = static_cast(content); - return make_tl_object( - td->animations_manager_->get_animation_object(m->file_id, "get_message_content_object"), - get_formatted_text_object(m->caption), is_content_secret); - } - case MessageContentType::Audio: { - const MessageAudio *m = static_cast(content); - return make_tl_object(td->audios_manager_->get_audio_object(m->file_id), - get_formatted_text_object(m->caption)); - } - case MessageContentType::Contact: { - const MessageContact *m = static_cast(content); - return make_tl_object(m->contact.get_contact_object()); - } - case MessageContentType::Document: { - const MessageDocument *m = static_cast(content); - return make_tl_object(td->documents_manager_->get_document_object(m->file_id), - get_formatted_text_object(m->caption)); - } - case MessageContentType::Game: { - const MessageGame *m = static_cast(content); - return make_tl_object(m->game.get_game_object(td)); - } - case MessageContentType::Invoice: { - const MessageInvoice *m = static_cast(content); - return make_tl_object( - m->title, m->description, get_photo_object(td->file_manager_.get(), &m->photo), m->invoice.currency, - m->total_amount, m->start_parameter, m->invoice.is_test, m->invoice.need_shipping_address, - m->receipt_message_id.get()); - } - case MessageContentType::LiveLocation: { - const MessageLiveLocation *m = static_cast(content); - auto passed = max(G()->unix_time_cached() - message_date, 0); - return make_tl_object(m->location.get_location_object(), m->period, - max(0, m->period - passed)); - } - case MessageContentType::Location: { - const MessageLocation *m = static_cast(content); - return make_tl_object(m->location.get_location_object(), 0, 0); - } - case MessageContentType::Photo: { - const MessagePhoto *m = static_cast(content); - return make_tl_object(get_photo_object(td->file_manager_.get(), &m->photo), - get_formatted_text_object(m->caption), is_content_secret); - } - case MessageContentType::Sticker: { - const MessageSticker *m = static_cast(content); - return make_tl_object(td->stickers_manager_->get_sticker_object(m->file_id)); - } - case MessageContentType::Text: { - const MessageText *m = static_cast(content); - return make_tl_object(get_formatted_text_object(m->text), - td->web_pages_manager_->get_web_page_object(m->web_page_id)); - } - case MessageContentType::Unsupported: { - return make_tl_object(); - } - case MessageContentType::Venue: { - const MessageVenue *m = static_cast(content); - return make_tl_object(m->venue.get_venue_object()); - } - case MessageContentType::Video: { - const MessageVideo *m = static_cast(content); - return make_tl_object(td->videos_manager_->get_video_object(m->file_id), - get_formatted_text_object(m->caption), is_content_secret); - } - case MessageContentType::VideoNote: { - const MessageVideoNote *m = static_cast(content); - return make_tl_object(td->video_notes_manager_->get_video_note_object(m->file_id), - m->is_viewed, is_content_secret); - } - case MessageContentType::VoiceNote: { - const MessageVoiceNote *m = static_cast(content); - return make_tl_object(td->voice_notes_manager_->get_voice_note_object(m->file_id), - get_formatted_text_object(m->caption), m->is_listened); - } - case MessageContentType::ChatCreate: { - const MessageChatCreate *m = static_cast(content); - return make_tl_object( - m->title, td->contacts_manager_->get_user_ids_object(m->participant_user_ids)); - } - case MessageContentType::ChatChangeTitle: { - const MessageChatChangeTitle *m = static_cast(content); - return make_tl_object(m->title); - } - case MessageContentType::ChatChangePhoto: { - const MessageChatChangePhoto *m = static_cast(content); - return make_tl_object(get_photo_object(td->file_manager_.get(), &m->photo)); - } - case MessageContentType::ChatDeletePhoto: - return make_tl_object(); - case MessageContentType::ChatDeleteHistory: - return make_tl_object(); - case MessageContentType::ChatAddUsers: { - const MessageChatAddUsers *m = static_cast(content); - return make_tl_object(td->contacts_manager_->get_user_ids_object(m->user_ids)); - } - case MessageContentType::ChatJoinedByLink: - return make_tl_object(); - case MessageContentType::ChatDeleteUser: { - const MessageChatDeleteUser *m = static_cast(content); - return make_tl_object( - td->contacts_manager_->get_user_id_object(m->user_id, "messageChatDeleteMember")); - } - case MessageContentType::ChatMigrateTo: { - const MessageChatMigrateTo *m = static_cast(content); - return make_tl_object( - td->contacts_manager_->get_supergroup_id_object(m->migrated_to_channel_id, "messageChatUpgradeTo")); - } - case MessageContentType::ChannelCreate: { - const MessageChannelCreate *m = static_cast(content); - return make_tl_object(m->title); - } - case MessageContentType::ChannelMigrateFrom: { - const MessageChannelMigrateFrom *m = static_cast(content); - return make_tl_object( - m->title, - td->contacts_manager_->get_basic_group_id_object(m->migrated_from_chat_id, "messageChatUpgradeFrom")); - } - case MessageContentType::PinMessage: { - const MessagePinMessage *m = static_cast(content); - return make_tl_object(m->message_id.get()); - } - case MessageContentType::GameScore: { - const MessageGameScore *m = static_cast(content); - return make_tl_object(m->game_message_id.get(), m->game_id, m->score); - } - case MessageContentType::ScreenshotTaken: - return make_tl_object(); - case MessageContentType::ChatSetTtl: { - const MessageChatSetTtl *m = static_cast(content); - return make_tl_object(m->ttl); - } - case MessageContentType::Call: { - const MessageCall *m = static_cast(content); - return make_tl_object(get_call_discard_reason_object(m->discard_reason), m->duration); - } - case MessageContentType::PaymentSuccessful: { - const MessagePaymentSuccessful *m = static_cast(content); - if (td->auth_manager_->is_bot()) { - return make_tl_object( - m->invoice_message_id.get(), m->currency, m->total_amount, m->invoice_payload, m->shipping_option_id, - get_order_info_object(m->order_info), m->telegram_payment_charge_id, m->provider_payment_charge_id); - } else { - return make_tl_object(m->invoice_message_id.get(), m->currency, - m->total_amount); - } - } - case MessageContentType::ContactRegistered: - return make_tl_object(); - case MessageContentType::ExpiredPhoto: - return make_tl_object(); - case MessageContentType::ExpiredVideo: - return make_tl_object(); - case MessageContentType::CustomServiceAction: { - const MessageCustomServiceAction *m = static_cast(content); - return make_tl_object(m->message); - } - case MessageContentType::WebsiteConnected: { - const MessageWebsiteConnected *m = static_cast(content); - return make_tl_object(m->domain_name); - } - case MessageContentType::PassportDataSent: { - const MessagePassportDataSent *m = static_cast(content); - return make_tl_object(get_passport_element_types_object(m->types)); - } - case MessageContentType::PassportDataReceived: { - const MessagePassportDataReceived *m = static_cast(content); - return make_tl_object( - get_encrypted_passport_element_object(td->file_manager_.get(), m->values), - get_encrypted_credentials_object(m->credentials)); - } - default: - UNREACHABLE(); - return nullptr; - } - UNREACHABLE(); - return nullptr; -} - MessageId MessagesManager::get_message_id(const tl_object_ptr &message_ptr) { int32 constructor_id = message_ptr->get_id(); switch (constructor_id) { @@ -9902,32 +8609,7 @@ void MessagesManager::on_message_ttl_expired_impl(Dialog *d, Message *message) { CHECK(message->ttl > 0); CHECK(d->dialog_id.get_type() != DialogType::SecretChat); delete_message_files(message); - switch (message->content->get_type()) { - case MessageContentType::Photo: - message->content = make_unique(); - break; - case MessageContentType::Video: - message->content = make_unique(); - break; - case MessageContentType::Unsupported: - // can happen if message content file id is broken - break; - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - // can happen if message content has been reget from somewhere - break; - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Document: - case MessageContentType::Sticker: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: - // can happen if server will send a document with a wrong content - message->content = make_unique(); - break; - default: - UNREACHABLE(); - } + update_expired_message_content(message->content); message->ttl = 0; message->ttl_expires_at = 0; if (message->reply_markup != nullptr) { @@ -10519,7 +9201,7 @@ void MessagesManager::on_secret_chat_screenshot_taken(SecretChatId secret_chat_i message_info.date = date; message_info.random_id = random_id; message_info.flags = MESSAGE_FLAG_HAS_FROM_ID; - message_info.content = make_unique(); + message_info.content = create_screenshot_taken_message_content(); Dialog *d = get_dialog_force(message_info.dialog_id); if (d == nullptr) { @@ -10553,7 +9235,7 @@ void MessagesManager::on_secret_chat_ttl_changed(SecretChatId secret_chat_id, Us message_info.date = date; message_info.random_id = random_id; message_info.flags = MESSAGE_FLAG_HAS_FROM_ID; - message_info.content = make_unique(ttl); + message_info.content = create_chat_set_ttl_message_content(ttl); Dialog *d = get_dialog_force(message_info.dialog_id); if (d == nullptr) { @@ -11227,8 +9909,8 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, bool need_update = false; bool is_content_changed = false; - merge_message_contents(td_, m, m->content.get(), new_content.get(), dialog_id, false, is_content_changed, - need_update); + merge_message_contents(td_, m->content.get(), new_content.get(), need_message_changed_warning(m), dialog_id, false, + is_content_changed, need_update); if (is_content_changed || need_update) { m->content = std::move(new_content); @@ -11240,18 +9922,6 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, } } -WebPageId MessagesManager::get_message_content_web_page_id(const MessageContent *content) { - if (content->get_type() == MessageContentType::Text) { - return static_cast(content)->web_page_id; - } - return WebPageId(); -} - -void MessagesManager::set_message_content_web_page_id(MessageContent *content, WebPageId web_page_id) { - CHECK(content->get_type() == MessageContentType::Text); - static_cast(content)->web_page_id = web_page_id; -} - void MessagesManager::on_update_message_web_page(FullMessageId full_message_id, bool have_web_page) { waiting_for_web_page_messages_.erase(full_message_id); auto dialog_id = full_message_id.get_dialog_id(); @@ -12503,19 +11173,12 @@ MessagesManager::Message *MessagesManager::get_message_force(FullMessageId full_ } MessageId MessagesManager::get_replied_message_id(const Message *m) { - switch (m->content->get_type()) { - case MessageContentType::PinMessage: - CHECK(!m->reply_to_message_id.is_valid()); - return static_cast(m->content.get())->message_id; - case MessageContentType::GameScore: - CHECK(!m->reply_to_message_id.is_valid()); - return static_cast(m->content.get())->game_message_id; - case MessageContentType::PaymentSuccessful: - CHECK(!m->reply_to_message_id.is_valid()); - return static_cast(m->content.get())->invoice_message_id; - default: - return m->reply_to_message_id; + auto message_id = get_message_content_replied_message_id(m->content.get()); + if (message_id.is_valid()) { + CHECK(!m->reply_to_message_id.is_valid()); + return message_id; } + return m->reply_to_message_id; } void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message_id, Promise &&promise, @@ -13692,13 +12355,6 @@ void MessagesManager::close_dialog(Dialog *d) { } } -tl_object_ptr MessagesManager::get_input_message_text_object( - const InputMessageText &input_message_text) const { - return make_tl_object(get_formatted_text_object(input_message_text.text), - input_message_text.disable_web_page_preview, - input_message_text.clear_draft); -} - tl_object_ptr MessagesManager::get_draft_message_object( const unique_ptr &draft_message) const { if (draft_message == nullptr) { @@ -14690,7 +13346,7 @@ vector MessagesManager::get_active_live_location_messages(Promise CHECK(m != nullptr); CHECK(m->content->get_type() == MessageContentType::LiveLocation); - auto live_period = static_cast(m->content.get())->period; + auto live_period = get_message_content_live_location_period(m->content.get()); if (live_period <= G()->unix_time() - m->date) { // bool is_expired flag? // live location is expired continue; @@ -14750,7 +13406,7 @@ void MessagesManager::try_add_active_live_location(DialogId dialog_id, const Mes return; } - auto live_period = static_cast(m->content.get())->period; + auto live_period = get_message_content_live_location_period(m->content.get()); if (live_period <= G()->unix_time() - m->date + 1) { // bool is_expired flag? // live location is expired return; @@ -15601,28 +14257,6 @@ tl_object_ptr MessagesManager::get_messages_object( return td_api::make_object(total_count, std::move(messages)); } -bool MessagesManager::need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot) { - if (is_bot) { - return false; - } - - switch (dialog_id.get_type()) { - case DialogType::User: - return !contacts_manager->is_user_bot(dialog_id.get_user_id()); - case DialogType::SecretChat: { - auto user_id = contacts_manager->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - return !contacts_manager->is_user_bot(user_id); - } - case DialogType::Chat: - case DialogType::Channel: - case DialogType::None: - return false; - default: - UNREACHABLE(); - return false; - } -} - Result MessagesManager::process_input_caption(DialogId dialog_id, tl_object_ptr &&text, bool is_bot) const { @@ -15635,460 +14269,6 @@ Result MessagesManager::process_input_caption(DialogId dialog_id, return FormattedText{std::move(text->text_), std::move(entities)}; } -Result MessagesManager::process_input_message_text( - const ContactsManager *contacts_manager, DialogId dialog_id, - tl_object_ptr &&input_message_content, bool is_bot, bool for_draft) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageText::ID); - auto input_message_text = static_cast(input_message_content.get()); - if (input_message_text->text_ == nullptr) { - if (for_draft) { - return InputMessageText{FormattedText(), input_message_text->disable_web_page_preview_, - input_message_text->clear_draft_}; - } - - return Status::Error(400, "Message text can't be empty"); - } - - TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_))); - TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, false, - need_skip_bot_commands(contacts_manager, dialog_id, is_bot), for_draft)); - return InputMessageText{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)}, - input_message_text->disable_web_page_preview_, input_message_text->clear_draft_}; -} - -Result> MessagesManager::process_input_message_location( - tl_object_ptr &&input_message_content) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageLocation::ID); - auto input_location = static_cast(input_message_content.get()); - - Location location(input_location->location_); - if (location.empty()) { - return Status::Error(400, "Wrong location specified"); - } - - auto period = input_location->live_period_; - if (period != 0 && (period < MIN_LIVE_LOCATION_PERIOD || period > MAX_LIVE_LOCATION_PERIOD)) { - return Status::Error(400, "Wrong live location period specified"); - } - - return std::make_pair(std::move(location), period); -} - -Result MessagesManager::process_input_message_venue( - tl_object_ptr &&input_message_content) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageVenue::ID); - auto venue = std::move(static_cast(input_message_content.get())->venue_); - - if (venue == nullptr) { - return Status::Error(400, "Venue can't be empty"); - } - - if (!clean_input_string(venue->title_)) { - return Status::Error(400, "Venue title must be encoded in UTF-8"); - } - if (!clean_input_string(venue->address_)) { - return Status::Error(400, "Venue address must be encoded in UTF-8"); - } - if (!clean_input_string(venue->provider_)) { - return Status::Error(400, "Venue provider must be encoded in UTF-8"); - } - if (!clean_input_string(venue->id_)) { - return Status::Error(400, "Venue identifier must be encoded in UTF-8"); - } - if (!clean_input_string(venue->type_)) { - return Status::Error(400, "Venue type must be encoded in UTF-8"); - } - - Venue result(venue); - if (result.empty()) { - return Status::Error(400, "Wrong venue location specified"); - } - - return result; -} - -Result MessagesManager::process_input_message_contact( - tl_object_ptr &&input_message_content) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageContact::ID); - auto contact = std::move(static_cast(input_message_content.get())->contact_); - - if (!clean_input_string(contact->phone_number_)) { - return Status::Error(400, "Phone number must be encoded in UTF-8"); - } - if (!clean_input_string(contact->first_name_)) { - return Status::Error(400, "First name must be encoded in UTF-8"); - } - if (!clean_input_string(contact->last_name_)) { - return Status::Error(400, "Last name must be encoded in UTF-8"); - } - if (!clean_input_string(contact->vcard_)) { - return Status::Error(400, "vCard must be encoded in UTF-8"); - } - - return Contact(contact->phone_number_, contact->first_name_, contact->last_name_, contact->vcard_, contact->user_id_); -} - -Result MessagesManager::process_input_message_game( - const ContactsManager *contacts_manager, tl_object_ptr &&input_message_content) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageGame::ID); - auto input_message_game = move_tl_object_as(input_message_content); - - UserId bot_user_id(input_message_game->bot_user_id_); - if (!contacts_manager->have_input_user(bot_user_id)) { - return Status::Error(400, "Game owner bot is not accessible"); - } - - if (!clean_input_string(input_message_game->game_short_name_)) { - return Status::Error(400, "Game short name must be encoded in UTF-8"); - } - - // TODO validate game_short_name - if (input_message_game->game_short_name_.empty()) { - return Status::Error(400, "Game short name must be non-empty"); - } - - return Game(bot_user_id, std::move(input_message_game->game_short_name_)); -} - -SecretInputMedia MessagesManager::get_secret_input_media(const MessageContent *content, Td *td, - tl_object_ptr input_file, - BufferSlice thumbnail, int32 layer) { - switch (content->get_type()) { - case MessageContentType::Animation: { - auto m = static_cast(content); - return td->animations_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, - std::move(thumbnail)); - } - case MessageContentType::Audio: { - auto m = static_cast(content); - return td->audios_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, - std::move(thumbnail)); - } - case MessageContentType::Contact: { - auto m = static_cast(content); - return m->contact.get_secret_input_media_contact(); - } - case MessageContentType::Document: { - auto m = static_cast(content); - return td->documents_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, - std::move(thumbnail)); - } - case MessageContentType::Location: { - auto m = static_cast(content); - return m->location.get_secret_input_media_geo_point(); - } - case MessageContentType::Photo: { - auto m = static_cast(content); - return photo_get_secret_input_media(td->file_manager_.get(), m->photo, std::move(input_file), m->caption.text, - std::move(thumbnail)); - } - case MessageContentType::Sticker: { - auto m = static_cast(content); - return td->stickers_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail)); - } - case MessageContentType::Text: { - CHECK(input_file == nullptr); - CHECK(thumbnail.empty()); - auto m = static_cast(content); - return td->web_pages_manager_->get_secret_input_media(m->web_page_id); - } - case MessageContentType::Venue: { - auto m = static_cast(content); - return m->venue.get_secret_input_media_venue(); - } - case MessageContentType::Video: { - auto m = static_cast(content); - return td->videos_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text, - std::move(thumbnail)); - } - case MessageContentType::VideoNote: { - auto m = static_cast(content); - return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail), - layer); - } - case MessageContentType::VoiceNote: { - auto m = static_cast(content); - return td->voice_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text); - } - case MessageContentType::LiveLocation: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::Unsupported: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - break; - default: - UNREACHABLE(); - } - return SecretInputMedia{}; -} - -tl_object_ptr MessagesManager::get_input_invoice(const Invoice &invoice) { - int32 flags = 0; - if (invoice.is_test) { - flags |= telegram_api::invoice::TEST_MASK; - } - if (invoice.need_name) { - flags |= telegram_api::invoice::NAME_REQUESTED_MASK; - } - if (invoice.need_phone_number) { - flags |= telegram_api::invoice::PHONE_REQUESTED_MASK; - } - if (invoice.need_email_address) { - flags |= telegram_api::invoice::EMAIL_REQUESTED_MASK; - } - if (invoice.need_shipping_address) { - flags |= telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK; - } - if (invoice.send_phone_number_to_provider) { - flags |= telegram_api::invoice::PHONE_TO_PROVIDER_MASK; - } - if (invoice.send_email_address_to_provider) { - flags |= telegram_api::invoice::EMAIL_TO_PROVIDER_MASK; - } - if (invoice.is_flexible) { - flags |= telegram_api::invoice::FLEXIBLE_MASK; - } - - auto prices = transform(invoice.price_parts, [](const LabeledPricePart &price) { - return telegram_api::make_object(price.label, price.amount); - }); - return make_tl_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, invoice.currency, std::move(prices)); -} - -tl_object_ptr MessagesManager::get_input_web_document(const FileManager *file_manager, - const Photo &photo) { - if (photo.id == -2) { - return nullptr; - } - - CHECK(photo.photos.size() == 1); - const PhotoSize &size = photo.photos[0]; - CHECK(size.file_id.is_valid()); - - vector> attributes; - if (size.dimensions.width != 0 && size.dimensions.height != 0) { - attributes.push_back( - make_tl_object(size.dimensions.width, size.dimensions.height)); - } - - auto file_view = file_manager->get_file_view(size.file_id); - CHECK(file_view.has_url()); - - auto file_name = get_url_file_name(file_view.url()); - return make_tl_object( - file_view.url(), size.size, MimeType::from_extension(PathView(file_name).extension(), "image/jpeg"), - std::move(attributes)); -} - -tl_object_ptr MessagesManager::get_input_media_invoice( - const FileManager *file_manager, const MessageInvoice *message_invoice) { - CHECK(message_invoice != nullptr); - int32 flags = 0; - auto input_web_document = get_input_web_document(file_manager, message_invoice->photo); - if (input_web_document != nullptr) { - flags |= telegram_api::inputMediaInvoice::PHOTO_MASK; - } - - return make_tl_object( - flags, message_invoice->title, message_invoice->description, std::move(input_web_document), - get_input_invoice(message_invoice->invoice), BufferSlice(message_invoice->payload), - message_invoice->provider_token, - telegram_api::make_object( - message_invoice->provider_data.empty() ? "null" : message_invoice->provider_data), - message_invoice->start_parameter); -} - -tl_object_ptr MessagesManager::get_input_media( - const MessageContent *content, Td *td, tl_object_ptr input_file, - tl_object_ptr input_thumbnail, int32 ttl) { - switch (content->get_type()) { - case MessageContentType::Animation: { - auto m = static_cast(content); - return td->animations_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); - } - case MessageContentType::Audio: { - auto m = static_cast(content); - return td->audios_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); - } - case MessageContentType::Contact: { - auto m = static_cast(content); - return m->contact.get_input_media_contact(); - } - case MessageContentType::Document: { - auto m = static_cast(content); - return td->documents_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); - } - case MessageContentType::Game: { - auto m = static_cast(content); - return m->game.get_input_media_game(td); - } - case MessageContentType::Invoice: { - auto m = static_cast(content); - return get_input_media_invoice(td->file_manager_.get(), m); - } - case MessageContentType::LiveLocation: { - auto m = static_cast(content); - return make_tl_object(m->location.get_input_geo_point(), m->period); - } - case MessageContentType::Location: { - auto m = static_cast(content); - return m->location.get_input_media_geo_point(); - } - case MessageContentType::Photo: { - auto m = static_cast(content); - return photo_get_input_media(td->file_manager_.get(), m->photo, std::move(input_file), ttl); - } - case MessageContentType::Sticker: { - auto m = static_cast(content); - return td->stickers_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); - } - case MessageContentType::Venue: { - auto m = static_cast(content); - return m->venue.get_input_media_venue(); - } - case MessageContentType::Video: { - auto m = static_cast(content); - return td->videos_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail), ttl); - } - case MessageContentType::VideoNote: { - auto m = static_cast(content); - return td->video_notes_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); - } - case MessageContentType::VoiceNote: { - auto m = static_cast(content); - return td->voice_notes_manager_->get_input_media(m->file_id, std::move(input_file)); - } - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - break; - default: - UNREACHABLE(); - } - return nullptr; -} - -void MessagesManager::delete_message_content_thumbnail(MessageContent *content, Td *td) { - switch (content->get_type()) { - case MessageContentType::Animation: { - auto m = static_cast(content); - return td->animations_manager_->delete_animation_thumbnail(m->file_id); - } - case MessageContentType::Audio: { - auto m = static_cast(content); - return td->audios_manager_->delete_audio_thumbnail(m->file_id); - } - case MessageContentType::Document: { - auto m = static_cast(content); - return td->documents_manager_->delete_document_thumbnail(m->file_id); - } - case MessageContentType::Photo: { - auto m = static_cast(content); - return photo_delete_thumbnail(m->photo); - } - case MessageContentType::Sticker: { - auto m = static_cast(content); - return td->stickers_manager_->delete_sticker_thumbnail(m->file_id); - } - case MessageContentType::Video: { - auto m = static_cast(content); - return td->videos_manager_->delete_video_thumbnail(m->file_id); - } - case MessageContentType::VideoNote: { - auto m = static_cast(content); - return td->video_notes_manager_->delete_video_note_thumbnail(m->file_id); - } - case MessageContentType::Contact: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Venue: - case MessageContentType::VoiceNote: - case MessageContentType::Text: - case MessageContentType::Unsupported: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - break; - default: - UNREACHABLE(); - } -} - MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, MessageId reply_to_message_id, bool disable_notification, bool from_background, unique_ptr &&content, @@ -16409,183 +14589,8 @@ MessageId MessagesManager::get_reply_to_message_id(Dialog *d, MessageId message_ return message_id; } -const FormattedText *MessagesManager::get_message_content_text(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::Text: - return &static_cast(content)->text; - case MessageContentType::Game: - return &static_cast(content)->game.get_text(); - default: - return get_message_content_caption(content); - } -} - -const FormattedText *MessagesManager::get_message_content_caption(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::Animation: - return &static_cast(content)->caption; - case MessageContentType::Audio: - return &static_cast(content)->caption; - case MessageContentType::Document: - return &static_cast(content)->caption; - case MessageContentType::Photo: - return &static_cast(content)->caption; - case MessageContentType::Video: - return &static_cast(content)->caption; - case MessageContentType::VoiceNote: - return &static_cast(content)->caption; - default: - return nullptr; - } -} - -int32 MessagesManager::get_message_content_duration(const MessageContent *content, const Td *td) { - CHECK(content != nullptr); - switch (content->get_type()) { - case MessageContentType::Animation: { - auto animation_file_id = static_cast(content)->file_id; - return td->animations_manager_->get_animation_duration(animation_file_id); - } - case MessageContentType::Audio: { - auto audio_file_id = static_cast(content)->file_id; - return td->audios_manager_->get_audio_duration(audio_file_id); - } - case MessageContentType::Video: { - auto video_file_id = static_cast(content)->file_id; - return td->videos_manager_->get_video_duration(video_file_id); - } - case MessageContentType::VideoNote: { - auto video_note_file_id = static_cast(content)->file_id; - return td->video_notes_manager_->get_video_note_duration(video_note_file_id); - } - case MessageContentType::VoiceNote: { - auto voice_file_id = static_cast(content)->file_id; - return td->voice_notes_manager_->get_voice_note_duration(voice_file_id); - } - default: - return 0; - } -} - -FileId MessagesManager::get_message_content_file_id(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::Animation: - return static_cast(content)->file_id; - case MessageContentType::Audio: - return static_cast(content)->file_id; - case MessageContentType::Document: - return static_cast(content)->file_id; - case MessageContentType::Photo: - for (auto &size : static_cast(content)->photo.photos) { - if (size.type == 'i') { - return size.file_id; - } - } - break; - case MessageContentType::Sticker: - return static_cast(content)->file_id; - case MessageContentType::Video: - return static_cast(content)->file_id; - case MessageContentType::VideoNote: - return static_cast(content)->file_id; - case MessageContentType::VoiceNote: - return static_cast(content)->file_id; - default: - break; - } - return FileId(); -} - -void MessagesManager::update_message_content_file_id_remote(MessageContent *content, FileId file_id) { - if (file_id.get_remote() == 0) { - return; - } - FileId *old_file_id = [&]() { - switch (content->get_type()) { - case MessageContentType::Animation: - return &static_cast(content)->file_id; - case MessageContentType::Audio: - return &static_cast(content)->file_id; - case MessageContentType::Document: - return &static_cast(content)->file_id; - case MessageContentType::Sticker: - return &static_cast(content)->file_id; - case MessageContentType::Video: - return &static_cast(content)->file_id; - case MessageContentType::VideoNote: - return &static_cast(content)->file_id; - case MessageContentType::VoiceNote: - return &static_cast(content)->file_id; - default: - return static_cast(nullptr); - } - }(); - if (old_file_id != nullptr && *old_file_id == file_id && old_file_id->get_remote() == 0) { - *old_file_id = file_id; - } -} - -FileId MessagesManager::get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td) { - switch (content->get_type()) { - case MessageContentType::Animation: - return td->animations_manager_->get_animation_thumbnail_file_id( - static_cast(content)->file_id); - case MessageContentType::Audio: - return td->audios_manager_->get_audio_thumbnail_file_id(static_cast(content)->file_id); - case MessageContentType::Document: - return td->documents_manager_->get_document_thumbnail_file_id( - static_cast(content)->file_id); - case MessageContentType::Photo: - for (auto &size : static_cast(content)->photo.photos) { - if (size.type == 't') { - return size.file_id; - } - } - break; - case MessageContentType::Sticker: - return td->stickers_manager_->get_sticker_thumbnail_file_id( - static_cast(content)->file_id); - case MessageContentType::Video: - return td->videos_manager_->get_video_thumbnail_file_id(static_cast(content)->file_id); - case MessageContentType::VideoNote: - return td->video_notes_manager_->get_video_note_thumbnail_file_id( - static_cast(content)->file_id); - case MessageContentType::VoiceNote: - return FileId(); - default: - break; - } - return FileId(); -} - vector MessagesManager::get_message_file_ids(const Message *message) const { - auto content = message->content.get(); - switch (content->get_type()) { - case MessageContentType::Photo: - return transform(static_cast(content)->photo.photos, - [](auto &size) { return size.file_id; }); - case MessageContentType::Animation: - case MessageContentType::Audio: - case MessageContentType::Document: - case MessageContentType::Sticker: - case MessageContentType::Video: - case MessageContentType::VideoNote: - case MessageContentType::VoiceNote: { - vector result; - result.reserve(2); - FileId file_id = get_message_content_file_id(content); - if (file_id.is_valid()) { - result.push_back(file_id); - } - FileId thumbnail_file_id = get_message_content_thumbnail_file_id(content, td_); - if (file_id.is_valid()) { - result.push_back(thumbnail_file_id); - } - return result; - } - default: - return {}; - } + return get_message_content_file_ids(message->content.get(), td_); } void MessagesManager::cancel_upload_message_content_files(const MessageContent *content) { @@ -16683,120 +14688,6 @@ bool MessagesManager::is_message_auto_read(DialogId dialog_id, bool is_outgoing) } } -void MessagesManager::add_message_content_dependencies(Dependencies &dependencies, - const MessageContent *message_content) { - switch (message_content->get_type()) { - case MessageContentType::Text: { - auto content = static_cast(message_content); - dependencies.web_page_ids.insert(content->web_page_id); - break; - } - case MessageContentType::Animation: - break; - case MessageContentType::Audio: - break; - case MessageContentType::Contact: { - auto content = static_cast(message_content); - dependencies.user_ids.insert(content->contact.get_user_id()); - break; - } - case MessageContentType::Document: - break; - case MessageContentType::Game: { - auto content = static_cast(message_content); - dependencies.user_ids.insert(content->game.get_bot_user_id()); - break; - } - case MessageContentType::Invoice: - break; - case MessageContentType::LiveLocation: - break; - case MessageContentType::Location: - break; - case MessageContentType::Photo: - break; - case MessageContentType::Sticker: - break; - case MessageContentType::Venue: - break; - case MessageContentType::Video: - break; - case MessageContentType::VideoNote: - break; - case MessageContentType::VoiceNote: - break; - case MessageContentType::ChatCreate: { - auto content = static_cast(message_content); - dependencies.user_ids.insert(content->participant_user_ids.begin(), content->participant_user_ids.end()); - break; - } - case MessageContentType::ChatChangeTitle: - break; - case MessageContentType::ChatChangePhoto: - break; - case MessageContentType::ChatDeletePhoto: - break; - case MessageContentType::ChatDeleteHistory: - break; - case MessageContentType::ChatAddUsers: { - auto content = static_cast(message_content); - dependencies.user_ids.insert(content->user_ids.begin(), content->user_ids.end()); - break; - } - case MessageContentType::ChatJoinedByLink: - break; - case MessageContentType::ChatDeleteUser: { - auto content = static_cast(message_content); - dependencies.user_ids.insert(content->user_id); - break; - } - case MessageContentType::ChatMigrateTo: { - auto content = static_cast(message_content); - dependencies.channel_ids.insert(content->migrated_to_channel_id); - break; - } - case MessageContentType::ChannelCreate: - break; - case MessageContentType::ChannelMigrateFrom: { - auto content = static_cast(message_content); - dependencies.chat_ids.insert(content->migrated_from_chat_id); - break; - } - case MessageContentType::PinMessage: - break; - case MessageContentType::GameScore: - break; - case MessageContentType::ScreenshotTaken: - break; - case MessageContentType::ChatSetTtl: - break; - case MessageContentType::Unsupported: - break; - case MessageContentType::Call: - break; - case MessageContentType::PaymentSuccessful: - break; - case MessageContentType::ContactRegistered: - break; - case MessageContentType::ExpiredPhoto: - break; - case MessageContentType::ExpiredVideo: - break; - case MessageContentType::CustomServiceAction: - break; - case MessageContentType::WebsiteConnected: - break; - case MessageContentType::PassportDataSent: - break; - case MessageContentType::PassportDataReceived: - break; - default: - UNREACHABLE(); - break; - } - add_formatted_text_dependencies(dependencies, get_message_content_text(message_content)); -} - void MessagesManager::add_message_dependencies(Dependencies &dependencies, DialogId dialog_id, const Message *m) { dependencies.user_ids.insert(m->sender_user_id); dependencies.user_ids.insert(m->via_bot_user_id); @@ -17110,309 +15001,6 @@ Result MessagesManager::process_input_message_content( return std::move(content); } -unique_ptr MessagesManager::create_text_message_content(string text, vector entities, - WebPageId web_page_id) { - return make_unique(FormattedText{std::move(text), std::move(entities)}, web_page_id); -} - -Result MessagesManager::create_input_message_content( - DialogId dialog_id, tl_object_ptr &&input_message_content, Td *td, - FormattedText caption, FileId file_id, PhotoSize thumbnail, vector sticker_file_ids) { - CHECK(input_message_content != nullptr); - LOG(INFO) << "Create InputMessageContent with file " << file_id << " and thumbnail " << thumbnail.file_id; - - FileView file_view; - string file_name; - string mime_type; - if (file_id.is_valid()) { - file_view = td->file_manager_->get_file_view(file_id); - auto suggested_name = file_view.suggested_name(); - const PathView path_view(suggested_name); - file_name = path_view.file_name().str(); - mime_type = MimeType::from_extension(path_view.extension()); - } - - bool disable_web_page_preview = false; - bool clear_draft = false; - unique_ptr content; - UserId via_bot_user_id; - int32 ttl = 0; - bool is_bot = td->auth_manager_->is_bot(); - switch (input_message_content->get_id()) { - case td_api::inputMessageText::ID: { - TRY_RESULT(input_message_text, process_input_message_text(td->contacts_manager_.get(), dialog_id, - std::move(input_message_content), is_bot)); - disable_web_page_preview = input_message_text.disable_web_page_preview; - clear_draft = input_message_text.clear_draft; - - WebPageId web_page_id; - bool can_add_web_page_previews = - dialog_id.get_type() != DialogType::Channel || - td->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_add_web_page_previews(); - if (!is_bot && !disable_web_page_preview && can_add_web_page_previews) { - web_page_id = td->web_pages_manager_->get_web_page_by_url( - get_first_url(input_message_text.text.text, input_message_text.text.entities)); - } - content = make_unique(std::move(input_message_text.text), web_page_id); - break; - } - case td_api::inputMessageAnimation::ID: { - auto input_animation = static_cast(input_message_content.get()); - - td->animations_manager_->create_animation( - file_id, thumbnail, std::move(file_name), std::move(mime_type), input_animation->duration_, - get_dimensions(input_animation->width_, input_animation->height_), false); - - content = make_unique(file_id, std::move(caption)); - break; - } - case td_api::inputMessageAudio::ID: { - auto input_audio = static_cast(input_message_content.get()); - - if (!clean_input_string(input_audio->title_)) { - return Status::Error(400, "Audio title must be encoded in UTF-8"); - } - if (!clean_input_string(input_audio->performer_)) { - return Status::Error(400, "Audio performer must be encoded in UTF-8"); - } - - td->audios_manager_->create_audio(file_id, thumbnail, std::move(file_name), std::move(mime_type), - input_audio->duration_, std::move(input_audio->title_), - std::move(input_audio->performer_), false); - - content = make_unique(file_id, std::move(caption)); - break; - } - case td_api::inputMessageDocument::ID: - td->documents_manager_->create_document(file_id, thumbnail, std::move(file_name), std::move(mime_type), false); - - content = make_unique(file_id, std::move(caption)); - break; - case td_api::inputMessagePhoto::ID: { - auto input_photo = static_cast(input_message_content.get()); - - if (input_photo->width_ < 0 || input_photo->width_ > 10000) { - return Status::Error(400, "Wrong photo width"); - } - if (input_photo->height_ < 0 || input_photo->height_ > 10000) { - return Status::Error(400, "Wrong photo height"); - } - ttl = input_photo->ttl_; - - auto message_photo = make_unique(); - - if (file_view.has_remote_location() && !file_view.remote_location().is_web()) { - message_photo->photo.id = file_view.remote_location().get_id(); - } - message_photo->photo.date = G()->unix_time(); - - PhotoSize s; - s.type = 'i'; - s.dimensions = get_dimensions(input_photo->width_, input_photo->height_); - s.size = static_cast(file_view.size()); - s.file_id = file_id; - - if (thumbnail.file_id.is_valid()) { - message_photo->photo.photos.push_back(std::move(thumbnail)); - } - - message_photo->photo.photos.push_back(s); - - message_photo->photo.has_stickers = !sticker_file_ids.empty(); - message_photo->photo.sticker_file_ids = std::move(sticker_file_ids); - - message_photo->caption = std::move(caption); - - content = std::move(message_photo); - break; - } - case td_api::inputMessageSticker::ID: { - auto input_sticker = static_cast(input_message_content.get()); - td->stickers_manager_->create_sticker( - file_id, thumbnail, get_dimensions(input_sticker->width_, input_sticker->height_), true, nullptr, nullptr); - - content = make_unique(file_id); - break; - } - case td_api::inputMessageVideo::ID: { - auto input_video = static_cast(input_message_content.get()); - - ttl = input_video->ttl_; - - bool has_stickers = !sticker_file_ids.empty(); - td->videos_manager_->create_video(file_id, thumbnail, has_stickers, std::move(sticker_file_ids), - std::move(file_name), std::move(mime_type), input_video->duration_, - get_dimensions(input_video->width_, input_video->height_), - input_video->supports_streaming_, false); - - content = make_unique(file_id, std::move(caption)); - break; - } - case td_api::inputMessageVideoNote::ID: { - auto input_video_note = static_cast(input_message_content.get()); - - auto length = input_video_note->length_; - if (length < 0 || length >= 640) { - return Status::Error(400, "Wrong video note length"); - } - - td->video_notes_manager_->create_video_note(file_id, thumbnail, input_video_note->duration_, - get_dimensions(length, length), false); - - content = make_unique(file_id, false); - break; - } - case td_api::inputMessageVoiceNote::ID: { - auto input_voice_note = static_cast(input_message_content.get()); - - td->voice_notes_manager_->create_voice_note(file_id, std::move(mime_type), input_voice_note->duration_, - std::move(input_voice_note->waveform_), false); - - content = make_unique(file_id, std::move(caption), false); - break; - } - case td_api::inputMessageLocation::ID: { - TRY_RESULT(location, process_input_message_location(std::move(input_message_content))); - if (location.second == 0) { - content = make_unique(std::move(location.first)); - } else { - content = make_unique(std::move(location.first), location.second); - } - break; - } - case td_api::inputMessageVenue::ID: { - TRY_RESULT(venue, process_input_message_venue(std::move(input_message_content))); - content = make_unique(std::move(venue)); - break; - } - case td_api::inputMessageContact::ID: { - TRY_RESULT(contact, process_input_message_contact(std::move(input_message_content))); - content = make_unique(std::move(contact)); - break; - } - case td_api::inputMessageGame::ID: { - TRY_RESULT(game, process_input_message_game(td->contacts_manager_.get(), std::move(input_message_content))); - via_bot_user_id = game.get_bot_user_id(); - if (via_bot_user_id == td->contacts_manager_->get_my_id("send_message")) { - via_bot_user_id = UserId(); - } - - content = make_unique(std::move(game)); - break; - } - case td_api::inputMessageInvoice::ID: { - if (!is_bot) { - return Status::Error(400, "Invoices can be sent only by bots"); - } - - auto input_message_invoice = move_tl_object_as(input_message_content); - if (!clean_input_string(input_message_invoice->title_)) { - return Status::Error(400, "Invoice title must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->description_)) { - return Status::Error(400, "Invoice description must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->photo_url_)) { - return Status::Error(400, "Invoice photo URL must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->start_parameter_)) { - return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->provider_token_)) { - return Status::Error(400, "Invoice provider token must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->provider_data_)) { - return Status::Error(400, "Invoice provider data must be encoded in UTF-8"); - } - if (!clean_input_string(input_message_invoice->invoice_->currency_)) { - return Status::Error(400, "Invoice currency must be encoded in UTF-8"); - } - - auto message_invoice = make_unique(); - message_invoice->title = std::move(input_message_invoice->title_); - message_invoice->description = std::move(input_message_invoice->description_); - - auto r_http_url = parse_url(input_message_invoice->photo_url_); - if (r_http_url.is_error()) { - if (!input_message_invoice->photo_url_.empty()) { - LOG(INFO) << "Can't register url " << input_message_invoice->photo_url_; - } - message_invoice->photo.id = -2; - } else { - auto url = r_http_url.ok().get_url(); - auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp); - if (r_invoice_file_id.is_error()) { - LOG(INFO) << "Can't register url " << url; - message_invoice->photo.id = -2; - } else { - auto invoice_file_id = r_invoice_file_id.move_as_ok(); - - PhotoSize s; - s.type = 'u'; - s.dimensions = get_dimensions(input_message_invoice->photo_width_, input_message_invoice->photo_height_); - s.size = input_message_invoice->photo_size_; // TODO use invoice_file_id size - s.file_id = invoice_file_id; - - message_invoice->photo.photos.push_back(s); - } - } - message_invoice->start_parameter = std::move(input_message_invoice->start_parameter_); - - message_invoice->invoice.currency = std::move(input_message_invoice->invoice_->currency_); - message_invoice->invoice.price_parts.reserve(input_message_invoice->invoice_->price_parts_.size()); - int64 total_amount = 0; - const int64 MAX_AMOUNT = 9999'9999'9999; - for (auto &price : input_message_invoice->invoice_->price_parts_) { - if (!clean_input_string(price->label_)) { - return Status::Error(400, "Invoice price label must be encoded in UTF-8"); - } - message_invoice->invoice.price_parts.emplace_back(std::move(price->label_), price->amount_); - if (price->amount_ < -MAX_AMOUNT || price->amount_ > MAX_AMOUNT) { - return Status::Error(400, "Too big amount of currency specified"); - } - total_amount += price->amount_; - } - if (total_amount <= 0) { - return Status::Error(400, "Total price must be positive"); - } - if (total_amount > MAX_AMOUNT) { - return Status::Error(400, "Total price is too big"); - } - message_invoice->total_amount = total_amount; - - message_invoice->invoice.is_test = input_message_invoice->invoice_->is_test_; - message_invoice->invoice.need_name = input_message_invoice->invoice_->need_name_; - message_invoice->invoice.need_phone_number = input_message_invoice->invoice_->need_phone_number_; - message_invoice->invoice.need_email_address = input_message_invoice->invoice_->need_email_address_; - message_invoice->invoice.need_shipping_address = input_message_invoice->invoice_->need_shipping_address_; - message_invoice->invoice.send_phone_number_to_provider = - input_message_invoice->invoice_->send_phone_number_to_provider_; - message_invoice->invoice.send_email_address_to_provider = - input_message_invoice->invoice_->send_email_address_to_provider_; - message_invoice->invoice.is_flexible = input_message_invoice->invoice_->is_flexible_; - if (message_invoice->invoice.send_phone_number_to_provider) { - message_invoice->invoice.need_phone_number = true; - } - if (message_invoice->invoice.send_email_address_to_provider) { - message_invoice->invoice.need_email_address = true; - } - if (message_invoice->invoice.is_flexible) { - message_invoice->invoice.need_shipping_address = true; - } - - message_invoice->payload = std::move(input_message_invoice->payload_); - message_invoice->provider_token = std::move(input_message_invoice->provider_token_); - message_invoice->provider_data = std::move(input_message_invoice->provider_data_); - - content = std::move(message_invoice); - break; - } - default: - UNREACHABLE(); - } - return InputMessageContent{std::move(content), disable_web_page_preview, clear_draft, ttl, via_bot_user_id}; -} - Result> MessagesManager::send_message_group( DialogId dialog_id, MessageId reply_to_message_id, bool disable_notification, bool from_background, vector> &&input_message_contents) { @@ -18319,7 +15907,7 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo // there is no caption to edit, but bot can edit inline reply_markup return true; } - return G()->unix_time_cached() - m->date < static_cast(m->content.get())->period; + return G()->unix_time_cached() - m->date < get_message_content_live_location_period(m->content.get()); } case MessageContentType::Contact: case MessageContentType::Location: @@ -19567,8 +17155,8 @@ Result MessagesManager::send_dialog_set_ttl_message(DialogId dialog_i TRY_STATUS(can_send_message(dialog_id)); bool need_update_dialog_pos = false; - Message *m = - get_message_to_send(d, MessageId(), false, false, make_unique(ttl), &need_update_dialog_pos); + Message *m = get_message_to_send(d, MessageId(), false, false, create_chat_set_ttl_message_content(ttl), + &need_update_dialog_pos); send_update_new_message(d, m, true); if (need_update_dialog_pos) { @@ -19600,7 +17188,7 @@ Status MessagesManager::send_screenshot_taken_notification_message(DialogId dial if (dialog_type == DialogType::User) { bool need_update_dialog_pos = false; - const Message *m = get_message_to_send(d, MessageId(), false, false, make_unique(), + const Message *m = get_message_to_send(d, MessageId(), false, false, create_screenshot_taken_message_content(), &need_update_dialog_pos); do_send_screenshot_taken_notification_message(dialog_id, m, 0); @@ -20167,131 +17755,6 @@ void MessagesManager::check_send_message_result(int64 random_id, DialogId dialog } } -bool MessagesManager::merge_message_content_file_id(Td *td, MessageContent *message_content, FileId new_file_id) { - if (!new_file_id.is_valid()) { - return false; - } - - LOG(INFO) << "Merge message content of a message with file " << new_file_id; - MessageContentType content_type = message_content->get_type(); - switch (content_type) { - case MessageContentType::Animation: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->animations_manager_->merge_animations(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::Audio: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->audios_manager_->merge_audios(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::Document: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->documents_manager_->merge_documents(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::Photo: { - auto content = static_cast(message_content); - Photo *photo = &content->photo; - if (!photo->photos.empty() && photo->photos.back().type == 'i') { - FileId &old_file_id = photo->photos.back().file_id; - if (old_file_id != new_file_id) { - LOG_STATUS(td->file_manager_->merge(new_file_id, old_file_id)); - old_file_id = new_file_id; - return true; - } - } - break; - } - case MessageContentType::Sticker: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->stickers_manager_->merge_stickers(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::Video: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->videos_manager_->merge_videos(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::VideoNote: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->video_notes_manager_->merge_video_notes(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::VoiceNote: { - auto content = static_cast(message_content); - if (new_file_id != content->file_id) { - td->voice_notes_manager_->merge_voice_notes(new_file_id, content->file_id, false); - content->file_id = new_file_id; - return true; - } - break; - } - case MessageContentType::Contact: - case MessageContentType::Game: - case MessageContentType::Invoice: - case MessageContentType::LiveLocation: - case MessageContentType::Location: - case MessageContentType::Text: - case MessageContentType::Venue: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Unsupported: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type; - break; - default: - UNREACHABLE(); - break; - } - return false; -} - FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageId new_message_id, int32 date, FileId new_file_id, const char *source) { CHECK(source != nullptr); @@ -22366,974 +19829,6 @@ ScopeNotificationSettings MessagesManager::get_scope_notification_settings( return {mute_until, std::move(sound), show_preview}; } -unique_ptr MessagesManager::get_secret_document_message_content( - Td *td, tl_object_ptr file, - tl_object_ptr &&document, - vector> &&attributes, DialogId owner_dialog_id, - FormattedText &&caption, bool is_opened) { - return get_document_message_content( - td->documents_manager_->on_get_document({std::move(file), std::move(document), std::move(attributes)}, - owner_dialog_id), - std::move(caption), is_opened); -} - -unique_ptr MessagesManager::get_document_message_content( - Td *td, tl_object_ptr &&document, DialogId owner_dialog_id, FormattedText &&caption, - bool is_opened, MultiPromiseActor *load_data_multipromise_ptr) { - return get_document_message_content( - td->documents_manager_->on_get_document(std::move(document), owner_dialog_id, load_data_multipromise_ptr), - std::move(caption), is_opened); -} - -unique_ptr MessagesManager::get_document_message_content( - std::pair &&parsed_document, FormattedText &&caption, bool is_opened) { - auto document_type = parsed_document.first; - auto file_id = parsed_document.second; - if (document_type != DocumentsManager::DocumentType::Unknown) { - CHECK(file_id.is_valid()); - } - switch (document_type) { - case DocumentsManager::DocumentType::Animation: - return make_unique(file_id, std::move(caption)); - case DocumentsManager::DocumentType::Audio: - return make_unique(file_id, std::move(caption)); - case DocumentsManager::DocumentType::General: - return make_unique(file_id, std::move(caption)); - case DocumentsManager::DocumentType::Sticker: - return make_unique(file_id); - case DocumentsManager::DocumentType::Unknown: - return make_unique(); - case DocumentsManager::DocumentType::Video: - return make_unique(file_id, std::move(caption)); - case DocumentsManager::DocumentType::VideoNote: - return make_unique(file_id, is_opened); - case DocumentsManager::DocumentType::VoiceNote: - return make_unique(file_id, std::move(caption), is_opened); - default: - UNREACHABLE(); - return nullptr; - } -} - -FormattedText MessagesManager::get_secret_media_caption(string &&message_text, string &&message_caption) { - FormattedText caption; - if (message_text.empty()) { - caption.text = std::move(message_caption); - } else if (message_caption.empty()) { - caption.text = std::move(message_text); - } else { - caption.text = message_text + "\n\n" + message_caption; - } - - caption.entities = find_entities(caption.text, false); - return caption; -} - -FormattedText MessagesManager::get_message_text(const ContactsManager *contacts_manager, string message_text, - vector> &&server_entities, - int32 send_date, const char *source) { - auto entities = get_message_entities(contacts_manager, std::move(server_entities), source); - auto status = fix_formatted_text(message_text, entities, true, true, true, false); - if (status.is_error()) { - if (send_date == 0 || send_date > 1497000000) { // approximate fix date - LOG(ERROR) << "Receive error " << status << " while parsing message from " << source << " with content \"" - << message_text << "\" sent at " << send_date << " with entities " << format::as_array(entities); - } - if (!clean_input_string(message_text)) { - message_text.clear(); - } - entities.clear(); - } - return FormattedText{std::move(message_text), std::move(entities)}; -} - -template -static tl_object_ptr secret_to_telegram(FromT &from); - -// fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation; -static auto secret_to_telegram(secret_api::fileLocationUnavailable &file_location) { - return make_tl_object(file_location.volume_id_, file_location.local_id_, - file_location.secret_); -} - -// fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; -static auto secret_to_telegram(secret_api::fileLocation &file_location) { - return make_tl_object(file_location.dc_id_, file_location.volume_id_, - file_location.local_id_, file_location.secret_); -} - -// photoSizeEmpty#e17e23c type:string = PhotoSize; -static auto secret_to_telegram(secret_api::photoSizeEmpty &empty) { - if (!clean_input_string(empty.type_)) { - empty.type_.clear(); - } - return make_tl_object(empty.type_); -} - -// photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; -static auto secret_to_telegram(secret_api::photoSize &photo_size) { - if (!clean_input_string(photo_size.type_)) { - photo_size.type_.clear(); - } - return make_tl_object(photo_size.type_, - secret_to_telegram(*photo_size.location_), - photo_size.w_, photo_size.h_, photo_size.size_); -} - -// photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; -static auto secret_to_telegram(secret_api::photoCachedSize &photo_size) { - if (!clean_input_string(photo_size.type_)) { - photo_size.type_.clear(); - } - return make_tl_object( - photo_size.type_, secret_to_telegram(*photo_size.location_), photo_size.w_, - photo_size.h_, photo_size.bytes_.clone()); -} - -// documentAttributeImageSize #6c37c15c w:int h:int = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeImageSize &image_size) { - return make_tl_object(image_size.w_, image_size.h_); -} - -// documentAttributeAnimated #11b58939 = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeAnimated &animated) { - return make_tl_object(); -} - -// documentAttributeSticker23 #fb0a5727 = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeSticker23 &sticker) { - return make_tl_object( - 0, false /*ignored*/, "", make_tl_object(), nullptr); -} -static auto secret_to_telegram(secret_api::inputStickerSetEmpty &sticker_set) { - return make_tl_object(); -} -static auto secret_to_telegram(secret_api::inputStickerSetShortName &sticker_set) { - if (!clean_input_string(sticker_set.short_name_)) { - sticker_set.short_name_.clear(); - } - return make_tl_object(sticker_set.short_name_); -} - -// documentAttributeSticker #3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeSticker &sticker) { - if (!clean_input_string(sticker.alt_)) { - sticker.alt_.clear(); - } - return make_tl_object( - 0, false /*ignored*/, sticker.alt_, secret_to_telegram(*sticker.stickerset_), - nullptr); -} - -// documentAttributeVideo #5910cccb duration:int w:int h:int = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeVideo &video) { - return make_tl_object(0, false /*ignored*/, false /*ignored*/, video.duration_, - video.w_, video.h_); -} - -// documentAttributeFilename #15590068 file_name:string = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeFilename &filename) { - if (!clean_input_string(filename.file_name_)) { - filename.file_name_.clear(); - } - return make_tl_object(filename.file_name_); -} - -// documentAttributeVideo66#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeVideo66 &video) { - return make_tl_object( - (video.flags_ & secret_api::documentAttributeVideo66::ROUND_MESSAGE_MASK) != 0 - ? telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK - : 0, - video.round_message_, false, video.duration_, video.w_, video.h_); -} - -static auto telegram_documentAttributeAudio(bool is_voice_note, int duration, string title, string performer, - BufferSlice waveform) { - if (!clean_input_string(title)) { - title.clear(); - } - if (!clean_input_string(performer)) { - performer.clear(); - } - - int32 flags = 0; - if (is_voice_note) { - flags |= telegram_api::documentAttributeAudio::VOICE_MASK; - } - if (!title.empty()) { - flags |= telegram_api::documentAttributeAudio::TITLE_MASK; - } - if (!performer.empty()) { - flags |= telegram_api::documentAttributeAudio::PERFORMER_MASK; - } - if (waveform.size()) { - flags |= telegram_api::documentAttributeAudio::WAVEFORM_MASK; - } - return make_tl_object(flags, is_voice_note, duration, std::move(title), - std::move(performer), std::move(waveform)); -} - -// documentAttributeAudio23 #51448e5 duration:int = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeAudio23 &audio) { - return telegram_documentAttributeAudio(false, audio.duration_, "", "", Auto()); -} -// documentAttributeAudio45 #ded218e0 duration:int title:string performer:string = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeAudio45 &audio) { - return telegram_documentAttributeAudio(false, audio.duration_, audio.title_, audio.performer_, Auto()); -} - -// documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string -// performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute; -static auto secret_to_telegram(secret_api::documentAttributeAudio &audio) { - return telegram_documentAttributeAudio((audio.flags_ & secret_api::documentAttributeAudio::VOICE_MASK) != 0, - audio.duration_, audio.title_, audio.performer_, audio.waveform_.clone()); -} - -static auto secret_to_telegram(std::vector> &attributes) { - std::vector> res; - for (auto &attribute : attributes) { - auto telegram_attribute = secret_to_telegram(*attribute); - if (telegram_attribute) { - res.push_back(std::move(telegram_attribute)); - } - } - return res; -} - -// decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int -// thumb:PhotoSize dc_id:int attributes:Vector = DecryptedMessageMedia; -static auto secret_to_telegram_document(secret_api::decryptedMessageMediaExternalDocument &from) { - if (!clean_input_string(from.mime_type_)) { - from.mime_type_.clear(); - } - return make_tl_object(from.id_, from.access_hash_, from.date_, from.mime_type_, from.size_, - secret_to_telegram(*from.thumb_), from.dc_id_, - 0, secret_to_telegram(from.attributes_)); -} - -template -static tl_object_ptr secret_to_telegram(FromT &from) { - tl_object_ptr res; - downcast_call(from, [&](auto &p) { res = secret_to_telegram(p); }); - return res; -} - -unique_ptr MessagesManager::get_secret_message_content( - Td *td, string message_text, tl_object_ptr file, - tl_object_ptr &&media, - vector> &&secret_entities, DialogId owner_dialog_id, - MultiPromiseActor &load_data_multipromise) { - auto entities = get_message_entities(std::move(secret_entities)); - auto status = fix_formatted_text(message_text, entities, true, false, true, false); - if (status.is_error()) { - LOG(WARNING) << "Receive error " << status << " while parsing secret message \"" << message_text - << "\" with entities " << format::as_array(entities); - if (!clean_input_string(message_text)) { - message_text.clear(); - } - entities.clear(); - } - - if (media == nullptr) { - return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); - } - - int32 constructor_id = media->get_id(); - if (message_text.size()) { - if (constructor_id != secret_api::decryptedMessageMediaEmpty::ID) { - LOG(INFO) << "Receive non-empty message text and media"; - } else { - return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); - } - } - - // support of old layer and old constructions - switch (constructor_id) { - case secret_api::decryptedMessageMediaVideo::ID: { - auto video = move_tl_object_as(media); - std::vector> attributes; - attributes.emplace_back( - make_tl_object(video->duration_, video->w_, video->h_)); - media = make_tl_object( - std::move(video->thumb_), video->thumb_w_, video->thumb_h_, video->mime_type_, video->size_, - std::move(video->key_), std::move(video->iv_), std::move(attributes), std::move(video->caption_)); - - constructor_id = secret_api::decryptedMessageMediaDocument::ID; - break; - } - } - - bool is_media_empty = false; - switch (constructor_id) { - case secret_api::decryptedMessageMediaEmpty::ID: - LOG(ERROR) << "Receive empty message text and media"; - is_media_empty = true; - break; - case secret_api::decryptedMessageMediaGeoPoint::ID: { - auto message_geo_point = move_tl_object_as(media); - - auto m = make_unique(Location(std::move(message_geo_point))); - if (m->location.empty()) { - is_media_empty = true; - break; - } - - return std::move(m); - } - case secret_api::decryptedMessageMediaVenue::ID: { - auto message_venue = move_tl_object_as(media); - - if (!clean_input_string(message_venue->title_)) { - message_venue->title_.clear(); - } - if (!clean_input_string(message_venue->address_)) { - message_venue->address_.clear(); - } - if (!clean_input_string(message_venue->provider_)) { - message_venue->provider_.clear(); - } - if (!clean_input_string(message_venue->venue_id_)) { - message_venue->venue_id_.clear(); - } - - auto m = make_unique(Venue(Location(message_venue->lat_, message_venue->long_, 0), - std::move(message_venue->title_), std::move(message_venue->address_), - std::move(message_venue->provider_), std::move(message_venue->venue_id_), - string())); - if (m->venue.empty()) { - is_media_empty = true; - break; - } - - return std::move(m); - } - case secret_api::decryptedMessageMediaContact::ID: { - auto message_contact = move_tl_object_as(media); - if (!clean_input_string(message_contact->phone_number_)) { - message_contact->phone_number_.clear(); - } - if (!clean_input_string(message_contact->first_name_)) { - message_contact->first_name_.clear(); - } - if (!clean_input_string(message_contact->last_name_)) { - message_contact->last_name_.clear(); - } - return make_unique( - Contact(std::move(message_contact->phone_number_), std::move(message_contact->first_name_), - std::move(message_contact->last_name_), string(), message_contact->user_id_)); - } - case secret_api::decryptedMessageMediaWebPage::ID: { - auto media_web_page = move_tl_object_as(media); - if (!clean_input_string(media_web_page->url_)) { - media_web_page->url_.clear(); - } - auto r_http_url = parse_url(media_web_page->url_); - if (r_http_url.is_error()) { - is_media_empty = true; - break; - } - auto url = r_http_url.ok().get_url(); - - auto web_page_id = td->web_pages_manager_->get_web_page_by_url(url, load_data_multipromise.get_promise()); - auto result = make_unique(FormattedText{std::move(message_text), std::move(entities)}, web_page_id); - if (!result->web_page_id.is_valid()) { - load_data_multipromise.add_promise( - PromiseCreator::lambda([td, url, &web_page_id = result->web_page_id](Result result) { - if (result.is_ok()) { - web_page_id = td->web_pages_manager_->get_web_page_by_url(url); - } - })); - } - return std::move(result); - } - case secret_api::decryptedMessageMediaExternalDocument::ID: { - auto external_document = move_tl_object_as(media); - auto document = secret_to_telegram_document(*external_document); - return get_document_message_content(td, std::move(document), owner_dialog_id, - FormattedText{std::move(message_text), std::move(entities)}, false, - &load_data_multipromise); - } - } - if (file == nullptr && !is_media_empty) { - LOG(ERROR) << "Received secret message with media, but without a file"; - is_media_empty = true; - } - if (is_media_empty) { - return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); - } - switch (constructor_id) { - case secret_api::decryptedMessageMediaPhoto::ID: { - auto message_photo = move_tl_object_as(media); - if (!clean_input_string(message_photo->caption_)) { - message_photo->caption_.clear(); - } - return make_unique( - get_photo(td->file_manager_.get(), std::move(file), std::move(message_photo), owner_dialog_id), - get_secret_media_caption(std::move(message_text), std::move(message_photo->caption_))); - } - case secret_api::decryptedMessageMediaDocument::ID: { - auto message_document = move_tl_object_as(media); - if (!clean_input_string(message_document->caption_)) { - message_document->caption_.clear(); - } - if (!clean_input_string(message_document->mime_type_)) { - message_document->mime_type_.clear(); - } - auto attributes = secret_to_telegram(message_document->attributes_); - message_document->attributes_.clear(); - return get_secret_document_message_content( - td, std::move(file), std::move(message_document), std::move(attributes), owner_dialog_id, - get_secret_media_caption(std::move(message_text), std::move(message_document->caption_)), false); - } - default: - LOG(ERROR) << "Unsupported: " << to_string(media); - return make_unique(); - } -} - -unique_ptr MessagesManager::get_message_content(Td *td, FormattedText message, - tl_object_ptr &&media, - DialogId owner_dialog_id, bool is_content_read, - UserId via_bot_user_id, int32 *ttl) { - if (media == nullptr) { - return make_unique(std::move(message), WebPageId()); - } - - int32 constructor_id = media->get_id(); - if (message.text.size()) { - if (constructor_id != telegram_api::messageMediaEmpty::ID) { - LOG(INFO) << "Receive non-empty message text and media for message from " << owner_dialog_id; - } else { - return make_unique(std::move(message), WebPageId()); - } - } - switch (constructor_id) { - case telegram_api::messageMediaEmpty::ID: - LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id; - return make_unique(std::move(message), WebPageId()); - case telegram_api::messageMediaPhoto::ID: { - auto message_photo = move_tl_object_as(media); - if ((message_photo->flags_ & telegram_api::messageMediaPhoto::PHOTO_MASK) == 0) { - if ((message_photo->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) == 0) { - LOG(ERROR) << "Receive messageMediaPhoto without photo and TTL: " << oneline(to_string(message_photo)); - break; - } - - return make_unique(); - } - - auto photo_ptr = std::move(message_photo->photo_); - int32 photo_id = photo_ptr->get_id(); - if (photo_id == telegram_api::photoEmpty::ID) { - return make_unique(); - } - CHECK(photo_id == telegram_api::photo::ID); - - if (ttl != nullptr && (message_photo->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) != 0) { - *ttl = message_photo->ttl_seconds_; - } - return make_unique( - get_photo(td->file_manager_.get(), move_tl_object_as(photo_ptr), owner_dialog_id), - std::move(message)); - } - case telegram_api::messageMediaGeo::ID: { - auto message_geo_point = move_tl_object_as(media); - - auto m = make_unique(Location(std::move(message_geo_point->geo_))); - if (m->location.empty()) { - break; - } - - return std::move(m); - } - case telegram_api::messageMediaGeoLive::ID: { - auto message_geo_point_live = move_tl_object_as(media); - int32 period = message_geo_point_live->period_; - auto location = Location(std::move(message_geo_point_live->geo_)); - if (location.empty()) { - break; - } - - if (period <= 0) { - LOG(ERROR) << "Receive wrong live location period = " << period; - return make_unique(std::move(location)); - } - return make_unique(std::move(location), period); - } - case telegram_api::messageMediaVenue::ID: { - auto message_venue = move_tl_object_as(media); - - auto m = - make_unique(Venue(message_venue->geo_, std::move(message_venue->title_), - std::move(message_venue->address_), std::move(message_venue->provider_), - std::move(message_venue->venue_id_), std::move(message_venue->venue_type_))); - if (m->venue.empty()) { - break; - } - - return std::move(m); - } - case telegram_api::messageMediaContact::ID: { - auto message_contact = move_tl_object_as(media); - if (message_contact->user_id_ != 0) { - td->contacts_manager_->get_user_id_object(UserId(message_contact->user_id_), - "messageMediaContact"); // to ensure updateUser - } - return make_unique(Contact( - std::move(message_contact->phone_number_), std::move(message_contact->first_name_), - std::move(message_contact->last_name_), std::move(message_contact->vcard_), message_contact->user_id_)); - } - case telegram_api::messageMediaDocument::ID: { - auto message_document = move_tl_object_as(media); - if ((message_document->flags_ & telegram_api::messageMediaDocument::DOCUMENT_MASK) == 0) { - if ((message_document->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) == 0) { - LOG(ERROR) << "Receive messageMediaDocument without document and TTL: " - << oneline(to_string(message_document)); - break; - } - - return make_unique(); - } - - auto document_ptr = std::move(message_document->document_); - int32 document_id = document_ptr->get_id(); - if (document_id == telegram_api::documentEmpty::ID) { - break; - } - CHECK(document_id == telegram_api::document::ID); - - if (ttl != nullptr && (message_document->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) != 0) { - *ttl = message_document->ttl_seconds_; - } - return get_document_message_content(td, move_tl_object_as(document_ptr), owner_dialog_id, - std::move(message), is_content_read, nullptr); - } - case telegram_api::messageMediaGame::ID: { - auto message_game = move_tl_object_as(media); - - auto m = make_unique(Game(td, std::move(message_game->game_), owner_dialog_id)); - if (m->game.empty()) { - break; - } - - m->game.set_bot_user_id(via_bot_user_id); - m->game.set_text(std::move(message)); - - return std::move(m); - } - case telegram_api::messageMediaInvoice::ID: { - auto message_invoice = move_tl_object_as(media); - - MessageId receipt_message_id; - if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) { - receipt_message_id = MessageId(ServerMessageId(message_invoice->receipt_msg_id_)); - if (!receipt_message_id.is_valid()) { - LOG(ERROR) << "Receive as receipt message " << receipt_message_id << " in " << owner_dialog_id; - receipt_message_id = MessageId(); - } - } - bool need_shipping_address = - (message_invoice->flags_ & telegram_api::messageMediaInvoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0; - bool is_test = (message_invoice->flags_ & telegram_api::messageMediaInvoice::TEST_MASK) != 0; - return td::make_unique( - std::move(message_invoice->title_), std::move(message_invoice->description_), - get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id), - std::move(message_invoice->start_param_), message_invoice->total_amount_, - std::move(message_invoice->currency_), is_test, need_shipping_address, receipt_message_id); - } - case telegram_api::messageMediaWebPage::ID: { - auto media_web_page = move_tl_object_as(media); - auto web_page_id = td->web_pages_manager_->on_get_web_page(std::move(media_web_page->webpage_), owner_dialog_id); - return make_unique(std::move(message), web_page_id); - } - - case telegram_api::messageMediaUnsupported::ID: { - return make_unique(); - } - default: - UNREACHABLE(); - } - - // explicit empty media message - return make_unique(std::move(message), WebPageId()); -} - -unique_ptr MessagesManager::dup_message_content(Td *td, DialogId dialog_id, - const MessageContent *content, bool for_forward) { - CHECK(content != nullptr); - - bool to_secret = dialog_id.get_type() == DialogType::SecretChat; - auto fix_file_id = [dialog_id, to_secret, file_manager = td->file_manager_.get()](FileId file_id) { - auto file_view = file_manager->get_file_view(file_id); - if (to_secret && !file_view.is_encrypted_secret()) { - auto download_file_id = file_manager->dup_file_id(file_id); - file_id = file_manager - ->register_generate(FileType::Encrypted, FileLocationSource::FromServer, file_view.suggested_name(), - PSTRING() << "#file_id#" << download_file_id.get(), dialog_id, file_view.size()) - .ok(); - } - return file_manager->dup_file_id(file_id); - }; - - FileId thumbnail_file_id; - if (to_secret) { - thumbnail_file_id = get_message_content_thumbnail_file_id(content, td); - } - switch (content->get_type()) { - case MessageContentType::Animation: { - auto result = make_unique(*static_cast(content)); - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->animations_manager_->dup_animation(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::Audio: { - auto result = make_unique(*static_cast(content)); - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->audios_manager_->dup_audio(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::Contact: - return make_unique(*static_cast(content)); - case MessageContentType::Document: { - auto result = make_unique(*static_cast(content)); - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->documents_manager_->dup_document(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::Game: - return make_unique(*static_cast(content)); - case MessageContentType::Invoice: - return make_unique(*static_cast(content)); - case MessageContentType::LiveLocation: - if (to_secret || for_forward) { - return make_unique(Location(static_cast(content)->location)); - } else { - return make_unique(*static_cast(content)); - } - case MessageContentType::Location: - return make_unique(*static_cast(content)); - case MessageContentType::Photo: { - auto result = make_unique(*static_cast(content)); - - if (result->photo.photos.size() > 2 && !to_secret) { - // already sent photo - return std::move(result); - } - - // Find 'i' or largest - CHECK(!result->photo.photos.empty()); - PhotoSize photo; - for (const auto &size : result->photo.photos) { - if (size.type == 'i') { - photo = size; - } - } - if (photo.type == 0) { - for (const auto &size : result->photo.photos) { - if (photo.type == 0 || photo < size) { - photo = size; - } - } - } - - // Find 't' or smallest - PhotoSize thumbnail; - for (const auto &size : result->photo.photos) { - if (size.type == 't') { - thumbnail = size; - } - } - if (thumbnail.type == 0) { - for (const auto &size : result->photo.photos) { - if (size.type != photo.type && (thumbnail.type == 0 || size < thumbnail)) { - thumbnail = size; - } - } - } - - result->photo.photos.clear(); - if (thumbnail.type != 0) { - thumbnail.type = 't'; - result->photo.photos.push_back(std::move(thumbnail)); - } - photo.type = 'i'; - result->photo.photos.push_back(std::move(photo)); - - if (photo_has_input_media(td->file_manager_.get(), result->photo, to_secret)) { - return std::move(result); - } - - result->photo.photos.back().file_id = fix_file_id(result->photo.photos.back().file_id); - if (thumbnail.type != 0) { - result->photo.photos[0].file_id = td->file_manager_->dup_file_id(result->photo.photos[0].file_id); - } - return std::move(result); - } - case MessageContentType::Sticker: { - auto result = make_unique(*static_cast(content)); - if (td->stickers_manager_->has_input_media(result->file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->stickers_manager_->dup_sticker(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::Text: - return make_unique(*static_cast(content)); - case MessageContentType::Venue: - return make_unique(*static_cast(content)); - case MessageContentType::Video: { - auto result = make_unique(*static_cast(content)); - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->videos_manager_->dup_video(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::VideoNote: { - auto result = make_unique(*static_cast(content)); - result->is_viewed = false; - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->video_notes_manager_->dup_video_note(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::VoiceNote: { - auto result = make_unique(*static_cast(content)); - result->is_listened = false; - if (td->documents_manager_->has_input_media(result->file_id, thumbnail_file_id, to_secret)) { - return std::move(result); - } - result->file_id = td->voice_notes_manager_->dup_voice_note(fix_file_id(result->file_id), result->file_id); - CHECK(result->file_id.is_valid()); - return std::move(result); - } - case MessageContentType::Unsupported: - case MessageContentType::ChatCreate: - case MessageContentType::ChatChangeTitle: - case MessageContentType::ChatChangePhoto: - case MessageContentType::ChatDeletePhoto: - case MessageContentType::ChatDeleteHistory: - case MessageContentType::ChatAddUsers: - case MessageContentType::ChatJoinedByLink: - case MessageContentType::ChatDeleteUser: - case MessageContentType::ChatMigrateTo: - case MessageContentType::ChannelCreate: - case MessageContentType::ChannelMigrateFrom: - case MessageContentType::PinMessage: - case MessageContentType::GameScore: - case MessageContentType::ScreenshotTaken: - case MessageContentType::ChatSetTtl: - case MessageContentType::Call: - case MessageContentType::PaymentSuccessful: - case MessageContentType::ContactRegistered: - case MessageContentType::ExpiredPhoto: - case MessageContentType::ExpiredVideo: - case MessageContentType::CustomServiceAction: - case MessageContentType::WebsiteConnected: - case MessageContentType::PassportDataSent: - case MessageContentType::PassportDataReceived: - return nullptr; - default: - UNREACHABLE(); - } - UNREACHABLE(); - return nullptr; -} - -unique_ptr MessagesManager::get_action_message_content( - Td *td, tl_object_ptr &&action, DialogId owner_dialog_id, - MessageId reply_to_message_id) { - CHECK(action != nullptr); - - switch (action->get_id()) { - case telegram_api::messageActionEmpty::ID: - LOG(ERROR) << "Receive empty message action in " << owner_dialog_id; - break; - case telegram_api::messageActionChatCreate::ID: { - auto chat_create = move_tl_object_as(action); - - vector participant_user_ids; - participant_user_ids.reserve(chat_create->users_.size()); - for (auto &user : chat_create->users_) { - UserId user_id(user); - if (user_id.is_valid()) { - participant_user_ids.push_back(user_id); - } else { - LOG(ERROR) << "Receive messageActionChatCreate with invalid " << user_id << " in " << owner_dialog_id; - } - } - - return td::make_unique(std::move(chat_create->title_), std::move(participant_user_ids)); - } - case telegram_api::messageActionChatEditTitle::ID: { - auto chat_edit_title = move_tl_object_as(action); - return td::make_unique(std::move(chat_edit_title->title_)); - } - case telegram_api::messageActionChatEditPhoto::ID: { - auto chat_edit_photo = move_tl_object_as(action); - - auto photo_ptr = std::move(chat_edit_photo->photo_); - int32 photo_id = photo_ptr->get_id(); - if (photo_id == telegram_api::photoEmpty::ID) { - break; - } - CHECK(photo_id == telegram_api::photo::ID); - - return make_unique( - get_photo(td->file_manager_.get(), move_tl_object_as(photo_ptr), owner_dialog_id)); - } - case telegram_api::messageActionChatDeletePhoto::ID: { - return make_unique(); - } - case telegram_api::messageActionHistoryClear::ID: { - return make_unique(); - } - case telegram_api::messageActionChatAddUser::ID: { - auto chat_add_user = move_tl_object_as(action); - - vector user_ids; - user_ids.reserve(chat_add_user->users_.size()); - for (auto &user : chat_add_user->users_) { - UserId user_id(user); - if (user_id.is_valid()) { - user_ids.push_back(user_id); - } else { - LOG(ERROR) << "Receive messageActionChatAddUser with invalid " << user_id << " in " << owner_dialog_id; - } - } - - return td::make_unique(std::move(user_ids)); - } - case telegram_api::messageActionChatJoinedByLink::ID: - return make_unique(); - case telegram_api::messageActionChatDeleteUser::ID: { - auto chat_delete_user = move_tl_object_as(action); - - UserId user_id(chat_delete_user->user_id_); - if (!user_id.is_valid()) { - LOG(ERROR) << "Receive messageActionChatDeleteUser with invalid " << user_id << " in " << owner_dialog_id; - break; - } - - return make_unique(user_id); - } - case telegram_api::messageActionChatMigrateTo::ID: { - auto chat_migrate_to = move_tl_object_as(action); - - ChannelId migrated_to_channel_id(chat_migrate_to->channel_id_); - if (!migrated_to_channel_id.is_valid()) { - LOG(ERROR) << "Receive messageActionChatMigrateTo with invalid " << migrated_to_channel_id << " in " - << owner_dialog_id; - break; - } - - return make_unique(migrated_to_channel_id); - } - case telegram_api::messageActionChannelCreate::ID: { - auto channel_create = move_tl_object_as(action); - return td::make_unique(std::move(channel_create->title_)); - } - case telegram_api::messageActionChannelMigrateFrom::ID: { - auto channel_migrate_from = move_tl_object_as(action); - - ChatId chat_id(channel_migrate_from->chat_id_); - LOG_IF(ERROR, !chat_id.is_valid()) << "Receive messageActionChannelMigrateFrom with invalid " << chat_id << " in " - << owner_dialog_id; - - return td::make_unique(std::move(channel_migrate_from->title_), chat_id); - } - case telegram_api::messageActionPinMessage::ID: { - if (!reply_to_message_id.is_valid()) { - LOG(ERROR) << "Receive pinned message with " << reply_to_message_id << " in " << owner_dialog_id; - reply_to_message_id = MessageId(); - } - return make_unique(reply_to_message_id); - } - case telegram_api::messageActionGameScore::ID: { - if (!reply_to_message_id.is_valid()) { - LOG_IF(ERROR, !td->auth_manager_->is_bot()) - << "Receive game score with " << reply_to_message_id << " in " << owner_dialog_id; - reply_to_message_id = MessageId(); - } - auto game_score = move_tl_object_as(action); - return make_unique(reply_to_message_id, game_score->game_id_, game_score->score_); - } - case telegram_api::messageActionPhoneCall::ID: { - auto phone_call = move_tl_object_as(action); - auto duration = - (phone_call->flags_ & telegram_api::messageActionPhoneCall::DURATION_MASK) != 0 ? phone_call->duration_ : 0; - return make_unique(phone_call->call_id_, duration, get_call_discard_reason(phone_call->reason_)); - } - case telegram_api::messageActionPaymentSent::ID: { - LOG_IF(ERROR, td->auth_manager_->is_bot()) << "Receive MessageActionPaymentSent in " << owner_dialog_id; - if (!reply_to_message_id.is_valid()) { - LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id; - reply_to_message_id = MessageId(); - } - auto payment_sent = move_tl_object_as(action); - return td::make_unique(reply_to_message_id, std::move(payment_sent->currency_), - payment_sent->total_amount_); - } - case telegram_api::messageActionPaymentSentMe::ID: { - LOG_IF(ERROR, !td->auth_manager_->is_bot()) << "Receive MessageActionPaymentSentMe in " << owner_dialog_id; - if (!reply_to_message_id.is_valid()) { - LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id; - reply_to_message_id = MessageId(); - } - auto payment_sent = move_tl_object_as(action); - auto result = td::make_unique(reply_to_message_id, std::move(payment_sent->currency_), - payment_sent->total_amount_); - result->invoice_payload = payment_sent->payload_.as_slice().str(); - result->shipping_option_id = std::move(payment_sent->shipping_option_id_); - result->order_info = get_order_info(std::move(payment_sent->info_)); - result->telegram_payment_charge_id = std::move(payment_sent->charge_->id_); - result->provider_payment_charge_id = std::move(payment_sent->charge_->provider_charge_id_); - return std::move(result); - } - case telegram_api::messageActionScreenshotTaken::ID: { - return make_unique(); - } - case telegram_api::messageActionCustomAction::ID: { - auto custom_action = move_tl_object_as(action); - return td::make_unique(std::move(custom_action->message_)); - } - case telegram_api::messageActionBotAllowed::ID: { - auto bot_allowed = move_tl_object_as(action); - return td::make_unique(std::move(bot_allowed->domain_)); - } - case telegram_api::messageActionSecureValuesSent::ID: { - LOG_IF(ERROR, td->auth_manager_->is_bot()) << "Receive MessageActionSecureValuesSent in " << owner_dialog_id; - auto secure_values = move_tl_object_as(action); - return td::make_unique(get_secure_value_types(secure_values->types_)); - } - case telegram_api::messageActionSecureValuesSentMe::ID: { - LOG_IF(ERROR, !td->auth_manager_->is_bot()) << "Receive MessageActionSecureValuesSentMe in " << owner_dialog_id; - auto secure_values = move_tl_object_as(action); - return td::make_unique( - get_encrypted_secure_values(td->file_manager_.get(), std::move(secure_values->values_)), - get_encrypted_secure_credentials(std::move(secure_values->credentials_))); - } - default: - UNREACHABLE(); - } - // explicit empty or wrong action - return make_unique(FormattedText(), WebPageId()); -} - int32 MessagesManager::get_random_y(MessageId message_id) { return static_cast(static_cast(message_id.get() * 2101234567u)); } @@ -23363,37 +19858,6 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(DialogId dialog return add_message_to_dialog(d, std::move(message), from_update, need_update, need_update_dialog_pos, source); } -int32 MessagesManager::get_message_content_new_participant_count(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::ChatAddUsers: - return narrow_cast(static_cast(content)->user_ids.size()); - case MessageContentType::ChatJoinedByLink: - return 1; - case MessageContentType::ChatDeleteUser: - return -1; - default: - return 0; - } -} - -MessageId MessagesManager::get_message_content_pinned_message_id(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::PinMessage: - return static_cast(content)->message_id; - default: - return MessageId(); - } -} - -UserId MessagesManager::get_message_content_deleted_user_id(const MessageContent *content) { - switch (content->get_type()) { - case MessageContentType::ChatDeleteUser: - return static_cast(content)->user_id; - default: - return UserId(); - } -} - MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, unique_ptr message, bool from_update, bool *need_update, bool *need_update_dialog_pos, const char *source) { @@ -24430,8 +20894,7 @@ void MessagesManager::update_message(Dialog *d, unique_ptr &old_message } } -bool MessagesManager::need_message_text_changed_warning(const Message *old_message, const MessageText *old_content, - const MessageText *new_content) { +bool MessagesManager::need_message_changed_warning(const Message *old_message) { if (old_message->edit_date > 0) { // message was edited return false; @@ -24440,455 +20903,9 @@ bool MessagesManager::need_message_text_changed_warning(const Message *old_messa // original message may be edited return false; } - if (new_content->text.text == "Unsupported characters" || - new_content->text.text == "This channel is blocked because it was used to spread pornographic content.") { - // message contained unsupported characters, text is replaced - return false; - } - if (old_message->message_id.is_yet_unsent() && !old_content->text.entities.empty() && - old_content->text.entities[0].offset == 0 && - (new_content->text.entities.empty() || new_content->text.entities[0].offset != 0) && - old_content->text.text != new_content->text.text && ends_with(old_content->text.text, new_content->text.text)) { - // server has deleted first entity and ltrim the message - return false; - } - for (auto &entity : new_content->text.entities) { - if (entity.type == MessageEntity::Type::PhoneNumber) { - // TODO remove after find_phone_numbers is implemented - return false; - } - } return true; } -void MessagesManager::merge_location_access_hash(Location &first, Location &second) { - if (second.get_access_hash() != 0) { - first.set_access_hash(second.get_access_hash()); - } else { - second.set_access_hash(first.get_access_hash()); - } -} - -void MessagesManager::merge_message_contents(Td *td, const Message *old_message, MessageContent *old_content, - MessageContent *new_content, DialogId dialog_id, bool need_merge_files, - bool &is_content_changed, bool &need_update) { - MessageContentType content_type = new_content->get_type(); - CHECK(old_content->get_type() == content_type); - - switch (content_type) { - case MessageContentType::Text: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->text.text != new_->text.text) { - if (need_message_text_changed_warning(old_message, old_, new_)) { - LOG(ERROR) << "Message text has changed from " - << to_string(get_message_content_object(old_content, td, old_message->date, - old_message->is_content_secret)) - << ". New content is " - << to_string(get_message_content_object(new_content, td, old_message->date, - old_message->is_content_secret)); - } - need_update = true; - } - if (old_->text.entities != new_->text.entities) { - const int32 MAX_CUSTOM_ENTITIES_COUNT = 100; // server-side limit - if (need_message_text_changed_warning(old_message, old_, new_) && - old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT) { - LOG(WARNING) << "Entities has changed from " - << to_string(get_message_content_object(old_content, td, old_message->date, - old_message->is_content_secret)) - << ". New content is " - << to_string(get_message_content_object(new_content, td, old_message->date, - old_message->is_content_secret)); - } - need_update = true; - } - if (old_->web_page_id != new_->web_page_id) { - LOG(INFO) << "Old: " << old_->web_page_id << ", new: " << new_->web_page_id; - is_content_changed = true; - need_update |= td->web_pages_manager_->have_web_page(old_->web_page_id) || - td->web_pages_manager_->have_web_page(new_->web_page_id); - } - break; - } - case MessageContentType::Animation: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->animations_manager_->merge_animations(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->caption != new_->caption) { - need_update = true; - } - break; - } - case MessageContentType::Audio: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->audios_manager_->merge_audios(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->caption != new_->caption) { - need_update = true; - } - break; - } - case MessageContentType::Contact: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->contact != new_->contact) { - need_update = true; - } - break; - } - case MessageContentType::Document: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->documents_manager_->merge_documents(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->caption != new_->caption) { - need_update = true; - } - break; - } - case MessageContentType::Game: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->game != new_->game) { - need_update = true; - } - break; - } - case MessageContentType::Invoice: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->title != new_->title || old_->description != new_->description || old_->photo != new_->photo || - old_->start_parameter != new_->start_parameter || old_->invoice != new_->invoice || - old_->total_amount != new_->total_amount || old_->receipt_message_id != new_->receipt_message_id) { - need_update = true; - } - if (old_->payload != new_->payload || old_->provider_token != new_->provider_token || - old_->provider_data != new_->provider_data) { - is_content_changed = true; - } - break; - } - case MessageContentType::LiveLocation: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->location != new_->location) { - need_update = true; - } - if (old_->period != new_->period) { - need_update = true; - } - if (old_->location.get_access_hash() != new_->location.get_access_hash()) { - is_content_changed = true; - merge_location_access_hash(old_->location, new_->location); - } - break; - } - case MessageContentType::Location: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->location != new_->location) { - need_update = true; - } - if (old_->location.get_access_hash() != new_->location.get_access_hash()) { - is_content_changed = true; - merge_location_access_hash(old_->location, new_->location); - } - break; - } - case MessageContentType::Photo: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - const Photo *old_photo = &old_->photo; - Photo *new_photo = &new_->photo; - if (old_photo->date != new_photo->date) { - is_content_changed = true; - } - if (old_photo->id != new_photo->id || old_->caption != new_->caption) { - need_update = true; - } - if (old_photo->photos != new_photo->photos) { - if (need_merge_files && - (old_photo->photos.size() == 1 || (old_photo->photos.size() == 2 && old_photo->photos[0].type == 't')) && - old_photo->photos.back().type == 'i' && !new_photo->photos.empty()) { - // first time get info about sent photo - if (old_photo->photos.size() == 2) { - new_photo->photos.push_back(old_photo->photos[0]); - } - new_photo->photos.push_back(old_photo->photos.back()); - - FileId old_file_id = get_message_content_file_id(old_content); - FileView old_file_view = td->file_manager_->get_file_view(old_file_id); - FileId new_file_id = new_photo->photos[0].file_id; - FileView new_file_view = td->file_manager_->get_file_view(new_file_id); - if (!old_file_view.has_remote_location()) { - CHECK(new_file_view.has_remote_location()); - CHECK(!new_file_view.remote_location().is_web()); - FileId file_id = td->file_manager_->register_remote( - FullRemoteFileLocation(FileType::Photo, new_file_view.remote_location().get_id(), - new_file_view.remote_location().get_access_hash(), 0, 0, 0, DcId::invalid()), - FileLocationSource::FromServer, dialog_id, old_photo->photos.back().size, 0, ""); - LOG_STATUS(td->file_manager_->merge(file_id, old_file_id)); - } - } - - // get sent photo again - auto new_photos_size = new_photo->photos.size(); - auto old_photos_size = old_photo->photos.size(); - if (old_photos_size == 2 + new_photos_size && old_photo->photos[new_photos_size].type == 't') { - new_photo->photos.push_back(old_photo->photos[new_photos_size]); - } - if (old_photos_size == 1 + new_photo->photos.size() && old_photo->photos.back().type == 'i') { - new_photo->photos.push_back(old_photo->photos.back()); - } - if (old_photo->photos != new_photo->photos) { - new_photo->photos.resize( - new_photos_size); // return previous size, because we shouldn't add local photo sizes - need_update = true; - } - } - break; - } - case MessageContentType::Sticker: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->stickers_manager_->merge_stickers(new_->file_id, old_->file_id, false))) { - need_update = true; - } - break; - } - case MessageContentType::Venue: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->venue != new_->venue) { - need_update = true; - } - if (old_->venue.location().get_access_hash() != new_->venue.location().get_access_hash()) { - is_content_changed = true; - merge_location_access_hash(old_->venue.location(), new_->venue.location()); - } - break; - } - case MessageContentType::Video: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->videos_manager_->merge_videos(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->caption != new_->caption) { - need_update = true; - } - break; - } - case MessageContentType::VideoNote: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->video_notes_manager_->merge_video_notes(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->is_viewed != new_->is_viewed) { - need_update = true; - } - break; - } - case MessageContentType::VoiceNote: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (new_->file_id != old_->file_id && - (!need_merge_files || td->voice_notes_manager_->merge_voice_notes(new_->file_id, old_->file_id, false))) { - need_update = true; - } - if (old_->caption != new_->caption) { - need_update = true; - } - if (old_->is_listened != new_->is_listened) { - need_update = true; - } - break; - } - case MessageContentType::ChatCreate: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->title != new_->title || old_->participant_user_ids != new_->participant_user_ids) { - need_update = true; - } - break; - } - case MessageContentType::ChatChangeTitle: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->title != new_->title) { - need_update = true; - } - break; - } - case MessageContentType::ChatChangePhoto: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->photo != new_->photo) { - need_update = true; - } - break; - } - case MessageContentType::ChatDeletePhoto: - break; - case MessageContentType::ChatDeleteHistory: - break; - case MessageContentType::ChatAddUsers: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->user_ids != new_->user_ids) { - need_update = true; - } - break; - } - case MessageContentType::ChatJoinedByLink: - break; - case MessageContentType::ChatDeleteUser: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->user_id != new_->user_id) { - need_update = true; - } - break; - } - case MessageContentType::ChatMigrateTo: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->migrated_to_channel_id != new_->migrated_to_channel_id) { - need_update = true; - } - break; - } - case MessageContentType::ChannelCreate: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->title != new_->title) { - need_update = true; - } - break; - } - case MessageContentType::ChannelMigrateFrom: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->title != new_->title || old_->migrated_from_chat_id != new_->migrated_from_chat_id) { - need_update = true; - } - break; - } - case MessageContentType::PinMessage: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->message_id != new_->message_id) { - need_update = true; - } - break; - } - case MessageContentType::GameScore: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->game_message_id != new_->game_message_id || old_->game_id != new_->game_id || - old_->score != new_->score) { - need_update = true; - } - break; - } - case MessageContentType::ScreenshotTaken: - break; - case MessageContentType::ChatSetTtl: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->ttl != new_->ttl) { - LOG(ERROR) << "Ttl has changed from " << old_->ttl << " to " << new_->ttl; - need_update = true; - } - break; - } - case MessageContentType::Call: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->call_id != new_->call_id) { - is_content_changed = true; - } - if (old_->duration != new_->duration || old_->discard_reason != new_->discard_reason) { - need_update = true; - } - break; - } - case MessageContentType::PaymentSuccessful: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->invoice_message_id != new_->invoice_message_id || old_->currency != new_->currency || - old_->total_amount != new_->total_amount || old_->invoice_payload != new_->invoice_payload || - old_->shipping_option_id != new_->shipping_option_id || - old_->telegram_payment_charge_id != new_->telegram_payment_charge_id || - old_->provider_payment_charge_id != new_->provider_payment_charge_id || - ((old_->order_info != nullptr || new_->order_info != nullptr) && - (old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info))) { - need_update = true; - } - break; - } - case MessageContentType::ContactRegistered: - break; - case MessageContentType::ExpiredPhoto: - break; - case MessageContentType::ExpiredVideo: - break; - case MessageContentType::CustomServiceAction: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->message != new_->message) { - need_update = true; - } - break; - } - case MessageContentType::WebsiteConnected: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->domain_name != new_->domain_name) { - need_update = true; - } - break; - } - case MessageContentType::PassportDataSent: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->types != new_->types) { - need_update = true; - } - break; - } - case MessageContentType::PassportDataReceived: { - auto old_ = static_cast(old_content); - auto new_ = static_cast(new_content); - if (old_->values != new_->values) { - need_update = true; - } - if (old_->credentials != new_->credentials) { - need_update = true; - } - break; - } - case MessageContentType::Unsupported: - break; - default: - UNREACHABLE(); - break; - } -} - bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_message, unique_ptr new_content, bool need_send_update_message_content, bool need_merge_files) { @@ -24945,8 +20962,8 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me } } } else { - merge_message_contents(td_, old_message, old_content.get(), new_content.get(), dialog_id, need_merge_files, - is_content_changed, need_update); + merge_message_contents(td_, old_content.get(), new_content.get(), need_message_changed_warning(old_message), + dialog_id, need_merge_files, is_content_changed, need_update); } if (need_finish_upload) { // the file is likely to be already merged with a server file, but if not we need to diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 8660bdf0a..7509e605a 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -19,28 +19,21 @@ #include "td/db/binlog/BinlogEvent.h" #include "td/telegram/AccessRights.h" -#include "td/telegram/CallDiscardReason.h" #include "td/telegram/ChannelId.h" -#include "td/telegram/ChatId.h" -#include "td/telegram/Contact.h" #include "td/telegram/Dependencies.h" #include "td/telegram/DialogId.h" #include "td/telegram/DialogParticipant.h" -#include "td/telegram/DocumentsManager.h" #include "td/telegram/files/FileId.h" -#include "td/telegram/Game.h" #include "td/telegram/Global.h" -#include "td/telegram/Location.h" +#include "td/telegram/InputMessageText.h" +#include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessagesDb.h" #include "td/telegram/net/NetQuery.h" -#include "td/telegram/Payments.h" -#include "td/telegram/Photo.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretInputMedia.h" -#include "td/telegram/SecureValue.h" #include "td/telegram/UserId.h" #include "td/telegram/WebPageId.h" @@ -70,638 +63,6 @@ class Td; class MultiSequenceDispatcher; -enum class MessageContentType : int32 { - None = -1, - Text, - Animation, - Audio, - Document, - Photo, - Sticker, - Video, - VoiceNote, - Contact, - Location, - Venue, - ChatCreate, - ChatChangeTitle, - ChatChangePhoto, - ChatDeletePhoto, - ChatDeleteHistory, - ChatAddUsers, - ChatJoinedByLink, - ChatDeleteUser, - ChatMigrateTo, - ChannelCreate, - ChannelMigrateFrom, - PinMessage, - Game, - GameScore, - ScreenshotTaken, - ChatSetTtl, - Unsupported, - Call, - Invoice, - PaymentSuccessful, - VideoNote, - ContactRegistered, - ExpiredPhoto, - ExpiredVideo, - LiveLocation, - CustomServiceAction, - WebsiteConnected, - PassportDataSent, - PassportDataReceived -}; - -StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type); - -// Do not forget to update MessagesManager::update_message_content when one of the inheritors of this class changes -class MessageContent { - public: - MessageContent() = default; - MessageContent(const MessageContent &) = default; - MessageContent &operator=(const MessageContent &) = default; - MessageContent(MessageContent &&) = default; - MessageContent &operator=(MessageContent &&) = default; - - virtual MessageContentType get_type() const = 0; - virtual ~MessageContent() = default; -}; - -class MessageText : public MessageContent { - public: - FormattedText text; - WebPageId web_page_id; - - MessageText() = default; - MessageText(FormattedText text, WebPageId web_page_id) : text(std::move(text)), web_page_id(web_page_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::Text; - } -}; - -class MessageAnimation : public MessageContent { - public: - FileId file_id; - - FormattedText caption; - - MessageAnimation() = default; - MessageAnimation(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Animation; - } -}; - -class MessageAudio : public MessageContent { - public: - FileId file_id; - - FormattedText caption; - - MessageAudio() = default; - MessageAudio(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Audio; - } -}; - -class MessageDocument : public MessageContent { - public: - FileId file_id; - - FormattedText caption; - - MessageDocument() = default; - MessageDocument(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Document; - } -}; - -class MessagePhoto : public MessageContent { - public: - Photo photo; - - FormattedText caption; - - MessagePhoto() = default; - MessagePhoto(Photo &&photo, FormattedText &&caption) : photo(std::move(photo)), caption(std::move(caption)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Photo; - } -}; - -class MessageSticker : public MessageContent { - public: - FileId file_id; - - MessageSticker() = default; - explicit MessageSticker(FileId file_id) : file_id(file_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::Sticker; - } -}; - -class MessageVideo : public MessageContent { - public: - FileId file_id; - - FormattedText caption; - - MessageVideo() = default; - MessageVideo(FileId file_id, FormattedText &&caption) : file_id(file_id), caption(std::move(caption)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Video; - } -}; - -class MessageVoiceNote : public MessageContent { - public: - FileId file_id; - - FormattedText caption; - bool is_listened; - - MessageVoiceNote() = default; - MessageVoiceNote(FileId file_id, FormattedText &&caption, bool is_listened) - : file_id(file_id), caption(std::move(caption)), is_listened(is_listened) { - } - - MessageContentType get_type() const override { - return MessageContentType::VoiceNote; - } -}; - -class MessageContact : public MessageContent { - public: - Contact contact; - - MessageContact() = default; - explicit MessageContact(Contact &&contact) : contact(std::move(contact)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Contact; - } -}; - -class MessageLocation : public MessageContent { - public: - Location location; - - MessageLocation() = default; - explicit MessageLocation(Location &&location) : location(std::move(location)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Location; - } -}; - -class MessageVenue : public MessageContent { - public: - Venue venue; - - MessageVenue() = default; - explicit MessageVenue(Venue &&venue) : venue(std::move(venue)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Venue; - } -}; - -class MessageChatCreate : public MessageContent { - public: - string title; - vector participant_user_ids; - - MessageChatCreate() = default; - MessageChatCreate(string &&title, vector &&participant_user_ids) - : title(std::move(title)), participant_user_ids(std::move(participant_user_ids)) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatCreate; - } -}; - -class MessageChatChangeTitle : public MessageContent { - public: - string title; - - MessageChatChangeTitle() = default; - explicit MessageChatChangeTitle(string &&title) : title(std::move(title)) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatChangeTitle; - } -}; - -class MessageChatChangePhoto : public MessageContent { - public: - Photo photo; - - MessageChatChangePhoto() = default; - explicit MessageChatChangePhoto(Photo &&photo) : photo(std::move(photo)) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatChangePhoto; - } -}; - -class MessageChatDeletePhoto : public MessageContent { - public: - MessageContentType get_type() const override { - return MessageContentType::ChatDeletePhoto; - } -}; - -class MessageChatDeleteHistory : public MessageContent { - public: - MessageContentType get_type() const override { - return MessageContentType::ChatDeleteHistory; - } -}; - -class MessageChatAddUsers : public MessageContent { - public: - vector user_ids; - - MessageChatAddUsers() = default; - explicit MessageChatAddUsers(vector &&user_ids) : user_ids(std::move(user_ids)) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatAddUsers; - } -}; - -class MessageChatJoinedByLink : public MessageContent { - public: - MessageContentType get_type() const override { - return MessageContentType::ChatJoinedByLink; - } -}; - -class MessageChatDeleteUser : public MessageContent { - public: - UserId user_id; - - MessageChatDeleteUser() = default; - explicit MessageChatDeleteUser(UserId user_id) : user_id(user_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatDeleteUser; - } -}; - -class MessageChatMigrateTo : public MessageContent { - public: - ChannelId migrated_to_channel_id; - - MessageChatMigrateTo() = default; - explicit MessageChatMigrateTo(ChannelId migrated_to_channel_id) : migrated_to_channel_id(migrated_to_channel_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatMigrateTo; - } -}; - -class MessageChannelCreate : public MessageContent { - public: - string title; - - MessageChannelCreate() = default; - explicit MessageChannelCreate(string &&title) : title(std::move(title)) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChannelCreate; - } -}; - -class MessageChannelMigrateFrom : public MessageContent { - public: - string title; - ChatId migrated_from_chat_id; - - MessageChannelMigrateFrom() = default; - MessageChannelMigrateFrom(string &&title, ChatId migrated_from_chat_id) - : title(std::move(title)), migrated_from_chat_id(migrated_from_chat_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChannelMigrateFrom; - } -}; - -class MessagePinMessage : public MessageContent { - public: - MessageId message_id; - - MessagePinMessage() = default; - explicit MessagePinMessage(MessageId message_id) : message_id(message_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::PinMessage; - } -}; - -class MessageGame : public MessageContent { - public: - Game game; - - MessageGame() = default; - explicit MessageGame(Game &&game) : game(std::move(game)) { - } - - MessageContentType get_type() const override { - return MessageContentType::Game; - } -}; - -class MessageGameScore : public MessageContent { - public: - MessageId game_message_id; - int64 game_id; - int32 score; - - MessageGameScore() = default; - MessageGameScore(MessageId game_message_id, int64 game_id, int32 score) - : game_message_id(game_message_id), game_id(game_id), score(score) { - } - - MessageContentType get_type() const override { - return MessageContentType::GameScore; - } -}; - -class MessageScreenshotTaken : public MessageContent { - public: - MessageContentType get_type() const override { - return MessageContentType::ScreenshotTaken; - } -}; - -class MessageChatSetTtl : public MessageContent { - public: - int32 ttl; - - MessageChatSetTtl() = default; - explicit MessageChatSetTtl(int32 ttl) : ttl(ttl) { - } - - MessageContentType get_type() const override { - return MessageContentType::ChatSetTtl; - } -}; - -class MessageUnsupported - : public MessageContent { // TODO save a layer in which the message was received to - // automatically reget it if the layer changes - public: - MessageContentType get_type() const override { - return MessageContentType::Unsupported; - } -}; - -class MessageCall : public MessageContent { - public: - int64 call_id; - int32 duration; - CallDiscardReason discard_reason; - - MessageCall() = default; - MessageCall(int64 call_id, int32 duration, CallDiscardReason discard_reason) - : call_id(call_id), duration(duration), discard_reason(discard_reason) { - } - - MessageContentType get_type() const override { - return MessageContentType::Call; - } -}; - -class MessageInvoice : public MessageContent { - public: - string title; - string description; - Photo photo; - string start_parameter; - - // InputMessageInvoice - Invoice invoice; - string payload; - string provider_token; - string provider_data; - - // MessageInvoice - int64 total_amount = 0; - MessageId receipt_message_id; - - MessageInvoice() = default; - MessageInvoice(string &&title, string &&description, Photo &&photo, string &&start_parameter, int64 total_amount, - string &¤cy, bool is_test, bool need_shipping_address, MessageId receipt_message_id) - : title(std::move(title)) - , description(std::move(description)) - , photo(std::move(photo)) - , start_parameter(std::move(start_parameter)) - , invoice(std::move(currency), is_test, need_shipping_address) - , payload() - , provider_token() - , provider_data() - , total_amount(total_amount) - , receipt_message_id(receipt_message_id) { - } - - MessageContentType get_type() const override { - return MessageContentType::Invoice; - } -}; - -class MessagePaymentSuccessful : public MessageContent { - public: - MessageId invoice_message_id; - string currency; - int64 total_amount = 0; - - // bots only part - string invoice_payload; - string shipping_option_id; - unique_ptr order_info; - string telegram_payment_charge_id; - string provider_payment_charge_id; - - MessagePaymentSuccessful() = default; - MessagePaymentSuccessful(MessageId invoice_message_id, string &¤cy, int64 total_amount) - : invoice_message_id(invoice_message_id), currency(std::move(currency)), total_amount(total_amount) { - } - - MessageContentType get_type() const override { - return MessageContentType::PaymentSuccessful; - } -}; - -class MessageVideoNote : public MessageContent { - public: - FileId file_id; - - bool is_viewed = false; - - MessageVideoNote() = default; - MessageVideoNote(FileId file_id, bool is_viewed) : file_id(file_id), is_viewed(is_viewed) { - } - - MessageContentType get_type() const override { - return MessageContentType::VideoNote; - } -}; - -class MessageContactRegistered : public MessageContent { - public: - MessageContentType get_type() const override { - return MessageContentType::ContactRegistered; - } -}; - -class MessageExpiredPhoto : public MessageContent { - public: - MessageExpiredPhoto() = default; - - MessageContentType get_type() const override { - return MessageContentType::ExpiredPhoto; - } -}; - -class MessageExpiredVideo : public MessageContent { - public: - MessageExpiredVideo() = default; - - MessageContentType get_type() const override { - return MessageContentType::ExpiredVideo; - } -}; - -class MessageLiveLocation : public MessageContent { - public: - Location location; - int32 period; - - MessageLiveLocation() = default; - MessageLiveLocation(Location &&location, int32 period) : location(std::move(location)), period(period) { - } - - MessageContentType get_type() const override { - return MessageContentType::LiveLocation; - } -}; - -class MessageCustomServiceAction : public MessageContent { - public: - string message; - - MessageCustomServiceAction() = default; - explicit MessageCustomServiceAction(string &&message) : message(std::move(message)) { - } - - MessageContentType get_type() const override { - return MessageContentType::CustomServiceAction; - } -}; - -class MessageWebsiteConnected : public MessageContent { - public: - string domain_name; - - MessageWebsiteConnected() = default; - explicit MessageWebsiteConnected(string &&domain_name) : domain_name(std::move(domain_name)) { - } - - MessageContentType get_type() const override { - return MessageContentType::WebsiteConnected; - } -}; - -class MessagePassportDataSent : public MessageContent { - public: - vector types; - - MessagePassportDataSent() = default; - explicit MessagePassportDataSent(vector &&types) : types(std::move(types)) { - } - - MessageContentType get_type() const override { - return MessageContentType::PassportDataSent; - } -}; - -class MessagePassportDataReceived : public MessageContent { - public: - vector values; - EncryptedSecureCredentials credentials; - - MessagePassportDataReceived() = default; - MessagePassportDataReceived(vector &&values, EncryptedSecureCredentials &&credentials) - : values(std::move(values)), credentials(std::move(credentials)) { - } - - MessageContentType get_type() const override { - return MessageContentType::PassportDataReceived; - } -}; - -struct InputMessageContent { - unique_ptr content; - bool disable_web_page_preview = false; - bool clear_draft = false; - int32 ttl = 0; - UserId via_bot_user_id; - - InputMessageContent(unique_ptr &&content, bool disable_web_page_preview, bool clear_draft, int32 ttl, - UserId via_bot_user_id) - : content(std::move(content)) - , disable_web_page_preview(disable_web_page_preview) - , clear_draft(clear_draft) - , ttl(ttl) - , via_bot_user_id(via_bot_user_id) { - } -}; - -class InputMessageText { - public: - FormattedText text; - bool disable_web_page_preview = false; - bool clear_draft = false; - InputMessageText() = default; - InputMessageText(FormattedText text, bool disable_web_page_preview, bool clear_draft) - : text(std::move(text)), disable_web_page_preview(disable_web_page_preview), clear_draft(clear_draft) { - } -}; - -bool operator==(const InputMessageText &lhs, const InputMessageText &rhs); -bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs); - class DraftMessage { public: int32 date; @@ -778,27 +139,6 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, ScopeNotificatio << notification_settings.show_preview << ", " << notification_settings.is_synchronized << "]"; } -inline constexpr size_t search_messages_filter_size() { - return static_cast(SearchMessagesFilter::Size) - 1; -} - -inline int32 search_messages_filter_index(SearchMessagesFilter filter) { - CHECK(filter != SearchMessagesFilter::Empty); - return static_cast(filter) - 1; -} - -inline int32 search_messages_filter_index_mask(SearchMessagesFilter filter) { - if (filter == SearchMessagesFilter::Empty) { - return 0; - } - return 1 << search_messages_filter_index(filter); -} - -inline int32 search_calls_filter_index(SearchMessagesFilter filter) { - CHECK(filter == SearchMessagesFilter::Call || filter == SearchMessagesFilter::MissedCall); - return static_cast(filter) - static_cast(SearchMessagesFilter::Call); -} - class DialogDate { int64 order; DialogId dialog_id; @@ -1079,30 +419,6 @@ class MessagesManager : public Actor { tl_object_ptr &input_message_content, bool is_bot) const; - static Result process_input_message_text( - const ContactsManager *contacts_manager, DialogId dialog_id, - tl_object_ptr &&input_message_content, bool is_bot, - bool for_draft = false) TD_WARN_UNUSED_RESULT; - - static Result> process_input_message_location( - tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; - - static Result process_input_message_venue(tl_object_ptr &&input_message_content) - TD_WARN_UNUSED_RESULT; - - static Result process_input_message_contact( - tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; - - static Result process_input_message_game(const ContactsManager *contacts_manager, - tl_object_ptr &&input_message_content) - TD_WARN_UNUSED_RESULT; - - static bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot); - - static FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text, - vector> &&server_entities, - int32 send_date, const char *source); - Result send_message(DialogId dialog_id, MessageId reply_to_message_id, bool disable_notification, bool from_background, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content) @@ -1948,9 +1264,6 @@ class MessagesManager : public Actor { static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 3 * 86400; static constexpr int32 USERNAME_CACHE_EXPIRE_TIME_SHORT = 900; - static constexpr int32 MIN_LIVE_LOCATION_PERIOD = 60; // seconds, server side limit - static constexpr int32 MAX_LIVE_LOCATION_PERIOD = 86400; // seconds, server side limit - static constexpr int32 MAX_RESEND_DELAY = 86400; // seconds, some resonable limit static constexpr int32 MAX_PRELOADED_DIALOGS = 1000; @@ -1984,16 +1297,9 @@ class MessagesManager : public Actor { FullMessageId on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message, bool have_previous, bool have_next, const char *source); - static unique_ptr create_text_message_content(string text, vector entities, - WebPageId web_page_id); - Result process_input_message_content( DialogId dialog_id, tl_object_ptr &&input_message_content) const; - static Result create_input_message_content( - DialogId dialog_id, tl_object_ptr &&input_message_content, Td *td, - FormattedText caption, FileId file_id, PhotoSize thumbnail, vector sticker_file_ids); - Message *get_message_to_send(Dialog *d, MessageId reply_to_message_id, bool disable_notification, bool from_background, unique_ptr &&content, bool *need_update_dialog_pos, unique_ptr forward_info = nullptr); @@ -2041,25 +1347,6 @@ class MessagesManager : public Actor { bool disable_notification, bool from_background, bool in_game_share) TD_WARN_UNUSED_RESULT; - static SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td, - tl_object_ptr input_file, - BufferSlice thumbnail, int32 layer); - - static tl_object_ptr get_input_invoice(const Invoice &invoice); - - static tl_object_ptr get_input_web_document(const FileManager *file_manager, - const Photo &photo); - - static tl_object_ptr get_input_media_invoice(const FileManager *file_manager, - const MessageInvoice *message_invoice); - - static tl_object_ptr get_input_media(const MessageContent *content, Td *td, - tl_object_ptr input_file, - tl_object_ptr input_thumbnail, - int32 ttl); - - static void delete_message_content_thumbnail(MessageContent *content, Td *td); - void do_send_media(DialogId dialog_id, Message *m, FileId file_id, FileId thumbnail_file_id, tl_object_ptr input_file, tl_object_ptr input_thumbnail); @@ -2104,18 +1391,8 @@ class MessagesManager : public Actor { bool is_message_unload_enabled() const; - static bool is_allowed_media_group_content(MessageContentType content_type); - static bool can_forward_message(DialogId from_dialog_id, const Message *m); - static bool can_forward_message_content(const MessageContent *content); - - static bool is_secret_message_content(int32 ttl, MessageContentType content_type); - - static bool is_service_message_content(MessageContentType content_type); - - static bool can_have_message_content_caption(MessageContentType content_type); - static bool can_delete_channel_message(DialogParticipantStatus status, const Message *m, bool is_bot); bool can_revoke_message(DialogId dialog_id, const Message *m) const; @@ -2162,8 +1439,6 @@ class MessagesManager : public Actor { bool update_message_contains_unread_mention(Dialog *d, Message *m, bool contains_unread_mention, const char *source); - static bool update_opened_message_content(MessageContent *content); - void read_message_content_from_updates(MessageId message_id); void read_channel_message_content_from_updates(Dialog *d, MessageId message_id); @@ -2246,15 +1521,6 @@ class MessagesManager : public Actor { int32 get_message_index_mask(DialogId dialog_id, const Message *m) const; - static int32 get_message_content_index_mask(const MessageContent *content, const Td *td, bool is_secret, - bool is_outgoing); - - static int32 get_message_content_new_participant_count(const MessageContent *content); - - static MessageId get_message_content_pinned_message_id(const MessageContent *content); - - static UserId get_message_content_deleted_user_id(const MessageContent *content); - Message *add_message_to_dialog(DialogId dialog_id, unique_ptr message, bool from_update, bool *need_update, bool *need_update_dialog_pos, const char *source); @@ -2281,20 +1547,7 @@ class MessagesManager : public Actor { void update_message(Dialog *d, unique_ptr &old_message, unique_ptr new_message, bool need_send_update_message_content, bool *need_update_dialog_pos); - static WebPageId get_message_content_web_page_id(const MessageContent *content); - - static void set_message_content_web_page_id(MessageContent *content, WebPageId web_page_id); - - static bool need_message_text_changed_warning(const Message *old_message, const MessageText *old_content, - const MessageText *new_content); - - static void merge_location_access_hash(Location &first, Location &second); - - static void merge_message_contents(Td *td, const Message *old_message, MessageContent *old_content, - MessageContent *new_content, DialogId dialog_id, bool need_merge_files, - bool &is_content_changed, bool &need_update); - - static bool merge_message_content_file_id(Td *td, MessageContent *message_content, FileId new_file_id); + static bool need_message_changed_warning(const Message *old_message); bool update_message_content(DialogId dialog_id, Message *old_message, unique_ptr new_content, bool need_send_update_message_content, bool need_merge_files); @@ -2422,9 +1675,6 @@ class MessagesManager : public Actor { void add_dialog_last_database_message(Dialog *d, unique_ptr &&last_database_message); - tl_object_ptr get_input_message_text_object( - const InputMessageText &input_message_text) const; - tl_object_ptr get_draft_message_object(const unique_ptr &draft_message) const; tl_object_ptr get_chat_type_object(DialogId dialog_id) const; @@ -2501,56 +1751,6 @@ class MessagesManager : public Actor { static unique_ptr get_draft_message(ContactsManager *contacts_manager, tl_object_ptr &&draft_message_ptr); - static FormattedText get_secret_media_caption(string &&message_text, string &&message_caption); - - static unique_ptr get_secret_document_message_content( - Td *td, tl_object_ptr file, - tl_object_ptr &&document, - vector> &&attributes, DialogId owner_dialog_id, - FormattedText &&caption, bool is_opened); - - static unique_ptr get_document_message_content(Td *td, - tl_object_ptr &&document, - DialogId owner_dialog_id, FormattedText &&caption, - bool is_opened, - MultiPromiseActor *load_data_multipromise_ptr); - - static unique_ptr get_document_message_content( - std::pair &&parsed_document, FormattedText &&caption, bool is_opened); - - static unique_ptr get_secret_message_content( - Td *td, string message_text, tl_object_ptr file, - tl_object_ptr &&media, - vector> &&secret_entities, DialogId owner_dialog_id, - MultiPromiseActor &load_data_multipromise); - - static unique_ptr get_message_content(Td *td, FormattedText message_text, - tl_object_ptr &&media, - DialogId owner_dialog_id, bool is_content_read, - UserId via_bot_user_id, int32 *ttl); - - static unique_ptr dup_message_content(Td *td, DialogId dialog_id, const MessageContent *content, - bool for_forward); - - static unique_ptr get_action_message_content(Td *td, - tl_object_ptr &&action, - DialogId owner_dialog_id, MessageId reply_to_message_id); - - static tl_object_ptr get_message_content_object(const MessageContent *content, Td *td, - int32 message_date, bool is_content_secret); - - static const FormattedText *get_message_content_text(const MessageContent *content); - - static const FormattedText *get_message_content_caption(const MessageContent *content); - - static int32 get_message_content_duration(const MessageContent *content, const Td *td); - - static FileId get_message_content_file_id(const MessageContent *content); - - static void update_message_content_file_id_remote(MessageContent *content, FileId file_id); - - static FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td); - vector get_message_file_ids(const Message *message) const; void cancel_upload_message_content_files(const MessageContent *content); @@ -2743,8 +1943,6 @@ class MessagesManager : public Actor { string get_search_text(const Message *m) const; - static string get_message_content_search_text(const Td *td, const MessageContent *content); - unique_ptr parse_dialog(DialogId dialog_id, const BufferSlice &value); void load_calls_db_state(); @@ -2756,8 +1954,6 @@ class MessagesManager : public Actor { static void dump_debug_message_op(const Dialog *d, int priority = 0); - static void add_message_content_dependencies(Dependencies &dependencies, const MessageContent *message_content); - static void add_message_dependencies(Dependencies &dependencies, DialogId dialog_id, const Message *m); static void add_dialog_dependencies(Dependencies &dependencies, DialogId dialog_id); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 0c6fa6103..fded31935 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -44,6 +44,7 @@ #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" #include "td/telegram/PasswordManager.h" +#include "td/telegram/Payments.h" #include "td/telegram/Photo.h" #include "td/telegram/PrivacyManager.h" #include "td/telegram/SecretChatId.h"