// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // 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/CustomEmojiId.h" #include "td/telegram/DialogId.h" #include "td/telegram/secret_api.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" #include "td/utils/common.h" #include "td/utils/FlatHashSet.h" #include "td/utils/HashTableUtils.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include namespace td { class Dependencies; class MultiPromiseActor; class Td; class UserManager; class MessageEntity { public: enum class Type : int32 { Mention, Hashtag, BotCommand, Url, EmailAddress, Bold, Italic, Code, Pre, PreCode, TextUrl, MentionName, Cashtag, PhoneNumber, Underline, Strikethrough, BlockQuote, BankCardNumber, MediaTimestamp, Spoiler, CustomEmoji, Size }; Type type = Type::Size; int32 offset = -1; int32 length = -1; int32 media_timestamp = -1; string argument; UserId user_id; CustomEmojiId custom_emoji_id; MessageEntity() = default; MessageEntity(Type type, int32 offset, int32 length, string argument = "") : type(type), offset(offset), length(length), argument(std::move(argument)) { } MessageEntity(int32 offset, int32 length, UserId user_id) : type(Type::MentionName), offset(offset), length(length), user_id(user_id) { } MessageEntity(Type type, int32 offset, int32 length, int32 media_timestamp) : type(type), offset(offset), length(length), media_timestamp(media_timestamp) { CHECK(type == Type::MediaTimestamp); } MessageEntity(Type type, int32 offset, int32 length, CustomEmojiId custom_emoji_id) : type(type), offset(offset), length(length), custom_emoji_id(custom_emoji_id) { CHECK(type == Type::CustomEmoji); } tl_object_ptr get_text_entity_object() const; bool operator==(const MessageEntity &other) const { return offset == other.offset && length == other.length && type == other.type && media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id && custom_emoji_id == other.custom_emoji_id; } bool operator<(const MessageEntity &other) const { if (offset != other.offset) { return offset < other.offset; } if (length != other.length) { return length > other.length; } auto priority = get_type_priority(type); auto other_priority = get_type_priority(other.type); return priority < other_priority; } bool operator!=(const MessageEntity &rhs) const { return !(*this == rhs); } template void store(StorerT &storer) const; template void parse(ParserT &parser); private: tl_object_ptr get_text_entity_type_object() const; static int get_type_priority(Type type); }; StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Type &message_entity_type); StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity); struct FormattedText { string text; vector entities; template void store(StorerT &storer) const; template void parse(ParserT &parser); }; struct FormattedTextHash { uint32 operator()(const FormattedText &formatted_text) const { auto hash = Hash()(formatted_text.text); for (auto &entity : formatted_text.entities) { hash = combine_hashes(hash, Hash()(static_cast(entity.type))); hash = combine_hashes(hash, Hash()(entity.length)); hash = combine_hashes(hash, Hash()(entity.offset)); } return hash; } }; StringBuilder &operator<<(StringBuilder &string_builder, const FormattedText &text); inline bool operator==(const FormattedText &lhs, const FormattedText &rhs) { return lhs.text == rhs.text && lhs.entities == rhs.entities; } inline bool operator!=(const FormattedText &lhs, const FormattedText &rhs) { return !(lhs == rhs); } const FlatHashSet &get_valid_short_usernames(); Result> get_message_entities(const UserManager *user_manager, vector> &&input_entities, bool allow_all = false); vector> get_text_entities_object(const vector &entities, bool skip_bot_commands, int32 max_media_timestamp); td_api::object_ptr get_formatted_text_object(const FormattedText &text, bool skip_bot_commands, int32 max_media_timestamp); void remove_premium_custom_emoji_entities(const Td *td, vector &entities, bool remove_unknown); void remove_unallowed_entities(const Td *td, FormattedText &text, DialogId dialog_id); vector find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps); vector find_mentions(Slice str); vector find_bot_commands(Slice str); vector find_hashtags(Slice str); vector find_cashtags(Slice str); vector find_bank_card_numbers(Slice str); vector find_tg_urls(Slice str); bool is_email_address(Slice str); vector> find_urls(Slice str); // slice + is_email_address vector> find_media_timestamps(Slice str); // slice + media_timestamp void remove_empty_entities(vector &entities); Slice get_first_url(const FormattedText &text); bool is_visible_url(const FormattedText &text, const string &url); Result> parse_markdown(string &text); Result> parse_markdown_v2(string &text); FormattedText parse_markdown_v3(FormattedText text); FormattedText get_markdown_v3(FormattedText text); Result> parse_html(string &str); vector> get_input_message_entities(const UserManager *user_manager, const vector &entities, const char *source); vector> get_input_message_entities(const UserManager *user_manager, const FormattedText *text, const char *source); vector> get_input_secret_message_entities( const vector &entities, int32 layer); vector get_message_entities(Td *td, vector> &&secret_entities, bool is_premium, MultiPromiseActor &load_data_multipromise); telegram_api::object_ptr get_input_text_with_entities(const UserManager *user_manager, const FormattedText &text, const char *source); FormattedText get_formatted_text(const UserManager *user_manager, string &&text, vector> &&server_entities, bool skip_media_timestamps, bool skip_trim, const char *source); FormattedText get_formatted_text(const UserManager *user_manager, telegram_api::object_ptr text_with_entities, bool skip_media_timestamps, bool skip_trim, const char *source); void fix_entities(vector &entities); // like clean_input_string but also validates entities Status fix_formatted_text(string &text, vector &entities, bool allow_empty, bool skip_new_entities, bool skip_bot_commands, bool skip_media_timestamps, bool skip_trim, int32 *ltrim_count = nullptr) TD_WARN_UNUSED_RESULT; FormattedText get_message_text(const UserManager *user_manager, string message_text, vector> &&server_entities, bool skip_new_entities, bool skip_media_timestamps, int32 send_date, bool from_album, const char *source); void truncate_formatted_text(FormattedText &text, size_t length); td_api::object_ptr extract_input_caption( tl_object_ptr &input_message_content); Result get_formatted_text(const Td *td, DialogId dialog_id, td_api::object_ptr &&text, bool is_bot, bool allow_empty, bool skip_media_timestamps, bool skip_trim, int32 *ltrim_count = nullptr); void add_formatted_text_dependencies(Dependencies &dependencies, const FormattedText *text); bool has_media_timestamps(const FormattedText *text, int32 min_media_timestamp, int32 max_media_timestamp); bool has_bot_commands(const FormattedText *text); bool need_always_skip_bot_commands(const UserManager *user_manager, DialogId dialog_id, bool is_bot); } // namespace td