1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026
//
// 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/FormattedDate.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 <utility>
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,
ExpandableBlockQuote,
FormattedDate,
Size
};
Type type = Type::Size;
int32 offset = -1;
int32 length = -1;
int32 media_timestamp = -1;
FormattedDate date;
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);
}
MessageEntity(Type type, int32 offset, int32 length, FormattedDate date)
: type(type), offset(offset), length(length), date(date) {
CHECK(type == Type::FormattedDate);
}
tl_object_ptr<td_api::textEntity> get_text_entity_object(const UserManager *user_manager) const;
bool operator==(const MessageEntity &other) const {
return offset == other.offset && length == other.length && type == other.type &&
media_timestamp == other.media_timestamp && date == other.date && 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 <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
private:
tl_object_ptr<td_api::TextEntityType> get_text_entity_type_object(const UserManager *user_manager) 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<MessageEntity> entities;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
struct FormattedTextHash {
uint32 operator()(const FormattedText &formatted_text) const {
auto hash = Hash<string>()(formatted_text.text);
for (auto &entity : formatted_text.entities) {
hash = combine_hashes(hash, Hash<int32>()(static_cast<int32>(entity.type)));
hash = combine_hashes(hash, Hash<int32>()(entity.length));
hash = combine_hashes(hash, Hash<int32>()(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<Slice, SliceHash> &get_valid_short_usernames();
Result<vector<MessageEntity>> get_message_entities(const UserManager *user_manager,
vector<tl_object_ptr<td_api::textEntity>> &&input_entities,
bool allow_all = false);
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const UserManager *user_manager,
const vector<MessageEntity> &entities,
bool skip_bot_commands, int32 max_media_timestamp);
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const UserManager *user_manager,
const FormattedText &text, bool skip_bot_commands,
int32 max_media_timestamp);
bool is_allowed_quote_entity_type(MessageEntity::Type type);
bool keep_only_custom_emoji(FormattedText &text);
void remove_premium_custom_emoji_entities(const Td *td, vector<MessageEntity> &entities, bool remove_unknown);
void remove_unallowed_entities(const Td *td, FormattedText &text, DialogId dialog_id);
bool remove_unallowed_quote_entities(FormattedText &text);
bool remove_unallowed_quote_user_entities(FormattedText &text, bool skip_bot_commands, bool skip_media_timestamps);
bool is_found_entity_type(MessageEntity::Type type, bool skip_bot_commands, bool skip_media_timestamps);
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
vector<Slice> find_mentions(Slice str);
vector<Slice> find_bot_commands(Slice str);
vector<Slice> find_hashtags(Slice str);
vector<Slice> find_cashtags(Slice str);
vector<Slice> find_bank_card_numbers(Slice str);
vector<Slice> find_tg_urls(Slice str);
bool is_email_address(Slice str);
vector<std::pair<Slice, bool>> find_urls(Slice str); // slice + is_email_address
vector<std::pair<Slice, int32>> find_media_timestamps(Slice str); // slice + media_timestamp
void remove_empty_entities(vector<MessageEntity> &entities);
Slice get_first_url(const FormattedText &text);
bool is_visible_url(const FormattedText &text, const string &url);
Result<vector<MessageEntity>> parse_markdown(string &text);
Result<vector<MessageEntity>> parse_markdown_v2(string &text);
FormattedText parse_markdown_v3(FormattedText text);
FormattedText get_markdown_v3(FormattedText text);
Result<vector<MessageEntity>> parse_html(string &str);
vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(const UserManager *user_manager,
const vector<MessageEntity> &entities,
const char *source);
vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(const UserManager *user_manager,
const FormattedText *text,
const char *source);
vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entities(
const vector<MessageEntity> &entities, int32 layer);
vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities,
bool is_premium, MultiPromiseActor &load_data_multipromise);
telegram_api::object_ptr<telegram_api::textWithEntities> 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<telegram_api::object_ptr<telegram_api::MessageEntity>> &&server_entities,
bool skip_media_timestamps, bool skip_trim, const char *source);
FormattedText get_formatted_text(const UserManager *user_manager,
telegram_api::object_ptr<telegram_api::textWithEntities> text_with_entities,
bool skip_media_timestamps, bool skip_trim, const char *source);
void fix_entities(vector<MessageEntity> &entities);
// like clean_input_string but also validates entities
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool allow_empty_string,
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<tl_object_ptr<telegram_api::MessageEntity>> &&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);
Result<FormattedText> get_formatted_text(const Td *td, DialogId dialog_id,
td_api::object_ptr<td_api::formattedText> &&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
|