Merge remote-tracking branch 'tdlib/master'

This commit is contained in:
Andrea Cavalli 2021-12-30 16:02:31 +01:00
commit d325cea988
17 changed files with 326 additions and 105 deletions

View File

@ -1,6 +1,6 @@
{
"name": "tdweb",
"version": "1.7.0",
"version": "1.8.0",
"description": "JavaScript interface for TDLib (Telegram library)",
"main": "dist/tdweb.js",
"repository": {

View File

@ -73,7 +73,7 @@ textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
textEntities entities:vector<textEntity> = TextEntities;
//@description A text with some entities @text The text @entities Entities contained in the text. Entities can be nested, but must not mutually intersect with each other.
//-Pre, Code and PreCode entities can't contain other entities. Bold, Italic, Underline and Strikethrough entities can contain and to be contained in all other entities. All other entities can't contain each other
//-Pre, Code and PreCode entities can't contain other entities. Bold, Italic, Underline, Strikethrough, and Spoiler entities can contain and to be contained in all other entities. All other entities can't contain each other
formattedText text:string entities:vector<textEntity> = FormattedText;
@ -845,10 +845,11 @@ messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalen
//@description Describes a sponsored message
//@message_id Message identifier; unique for the chat to which the sponsored message belongs among both ordinary and sponsored messages
//@sponsor_chat_id Chat identifier
//@sponsor_chat_id Sponsor chat identifier; 0 if the sponsor chat is accessible through an invite link
//@sponsor_chat_info Information about the sponsor chat; may be null unless sponsor_chat_id == 0
//@link An internal link to be opened when the sponsored message is clicked; may be null. If null, the sponsor chat needs to be opened instead
//@content Content of the message. Currently, can be only of the type messageText
sponsoredMessage message_id:int53 sponsor_chat_id:int53 link:InternalLinkType content:MessageContent = SponsoredMessage;
sponsoredMessage message_id:int53 sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
//@class NotificationSettingsScope @description Describes the types of chats to which notification settings are relevant
@ -1927,6 +1928,9 @@ textEntityTypeUnderline = TextEntityType;
//@description A strikethrough text
textEntityTypeStrikethrough = TextEntityType;
//@description A spoiler text. Not supported in secret chats
textEntityTypeSpoiler = TextEntityType;
//@description Text that must be formatted as if inside a code HTML tag
textEntityTypeCode = TextEntityType;
@ -1965,8 +1969,9 @@ messageSchedulingStateSendWhenOnline = MessageSchedulingState;
//@description Options to be used when a message is sent
//@disable_notification Pass true to disable notification for the message
//@from_background Pass true if the message is sent from the background
//@protect_content Pass true if the content of the message must be protected from forwarding and saving; for bots only
//@scheduling_state Message scheduling state; pass null to send message immediately. Messages sent to a secret chat, live location messages and self-destructing messages can't be scheduled
messageSendOptions disable_notification:Bool from_background:Bool scheduling_state:MessageSchedulingState = MessageSendOptions;
messageSendOptions disable_notification:Bool from_background:Bool protect_content:Bool scheduling_state:MessageSchedulingState = MessageSendOptions;
//@description Options to be used when a message content is copied without reference to the original sender. Service messages and messageInvoice can't be copied
//@send_copy True, if content of the message needs to be copied without reference to the original sender. Always true if the message is forwarded to a secret chat or is local
@ -1977,7 +1982,7 @@ messageCopyOptions send_copy:Bool replace_caption:Bool new_caption:formattedText
//@class InputMessageContent @description The content of a message to send
//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Code, Pre, PreCode, TextUrl and MentionName entities are allowed to be specified manually
//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Spoiler, Code, Pre, PreCode, TextUrl and MentionName entities are allowed to be specified manually
//@disable_web_page_preview True, if rich web page previews for URLs in the message text must be disabled @clear_draft True, if a chat message draft must be deleted
inputMessageText text:formattedText disable_web_page_preview:Bool clear_draft:Bool = InputMessageContent;
@ -4559,11 +4564,11 @@ editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:Messa
//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) contained in the text. Can be called synchronously @text The text in which to look for entites
getTextEntities text:string = TextEntities;
//@description Parses Bold, Italic, Underline, Strikethrough, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
//@description Parses Bold, Italic, Underline, Strikethrough, Spoiler, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
parseTextEntities text:string parse_mode:TextParseMode = FormattedText;
//@description Parses Markdown entities in a human-friendly format, ignoring markup errors. Can be called synchronously
//@text The text to parse. For example, "__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) __italic**bold italic__bold**"
//@text The text to parse. For example, "__italic__ ~~strikethrough~~ ||spoiler|| **bold** `code` ```pre``` __[italic__ text_url](telegram.org) __italic**bold italic__bold**"
parseMarkdown text:formattedText = FormattedText;
//@description Replaces text entities with Markdown formatting in a human-friendly format. Entities that can't be represented in Markdown unambiguously are kept as is. Can be called synchronously @text The text

View File

@ -118,8 +118,8 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#46a6ffb4 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> = ChatFull;
channelFull#56662e2e flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer = ChatFull;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
channelFull#e13c3d20 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -132,7 +132,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#85d6cbe2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
message#38116ee0 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
@ -371,6 +371,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -604,6 +605,7 @@ messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
@ -909,6 +911,7 @@ channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int
channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#9cf7f76a prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -1272,7 +1275,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#d151e19a flags:# random_id:bytes from_id:Peer channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
sponsoredMessage#3a836df8 flags:# random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
@ -1292,6 +1295,19 @@ messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Ve
auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut;
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
messageReactions#87b6e36 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactons:flags.1?Vector<MessageUserReaction> = MessageReactions;
messageUserReaction#932844fa user_id:long reaction:string = MessageUserReaction;
messages.messageReactionsList#a366923c flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
availableReaction#21d7c4b flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document = AvailableReaction;
messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1430,9 +1446,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
@ -1511,7 +1527,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@ -1570,6 +1586,12 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#25690ce4 flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;

View File

@ -4367,14 +4367,15 @@ Result<tl_object_ptr<telegram_api::InputUser>> ContactsManager::get_input_user(U
}
const User *u = get_user(user_id);
if (u == nullptr) {
return Status::Error(400, "User not found");
}
if (u->access_hash == -1 || u->is_min_access_hash) {
if (u == nullptr || u->access_hash == -1 || u->is_min_access_hash) {
if (td_->auth_manager_->is_bot() && user_id.is_valid()) {
return make_tl_object<telegram_api::inputUser>(user_id.get(), 0);
}
return Status::Error(400, "Have no access to the user");
if (u == nullptr) {
return Status::Error(400, "User not found");
} else {
return Status::Error(400, "Have no access to the user");
}
}
return make_tl_object<telegram_api::inputUser>(user_id.get(), u->access_hash);

View File

@ -340,6 +340,8 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionToggleNoForwards>(action_ptr);
return td_api::make_object<td_api::chatEventHasProtectedContentToggled>(action->new_value_);
}
case telegram_api::channelAdminLogEventActionChangeAvailableReactions::ID:
return nullptr;
default:
UNREACHABLE();
return nullptr;

View File

@ -1235,6 +1235,17 @@ string LinkManager::get_dialog_invite_link_hash(Slice invite_link) {
return get_url_query_hash(link_info.is_tg_, url_query);
}
string LinkManager::get_dialog_invite_link(Slice hash, bool is_internal) {
if (!is_base64url_characters(hash)) {
return string();
}
if (is_internal) {
return PSTRING() << "tg:join?invite=" << hash;
} else {
return PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << '+' << hash;
}
}
UserId LinkManager::get_link_user_id(Slice url) {
string lower_cased_url = to_lower(url);
url = lower_cased_url;

View File

@ -70,6 +70,8 @@ class LinkManager final : public Actor {
static string get_dialog_invite_link_hash(Slice invite_link);
static string get_dialog_invite_link(Slice hash, bool is_internal);
static UserId get_link_user_id(Slice url);
static Result<MessageLinkInfo> get_message_link_info(Slice url);

View File

@ -29,7 +29,7 @@
namespace td {
int MessageEntity::get_type_priority(Type type) {
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50, 50};
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50, 50, 94};
static_assert(sizeof(types) / sizeof(types[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
return types[static_cast<int32>(type)];
}
@ -74,6 +74,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
return string_builder << "BankCardNumber";
case MessageEntity::Type::MediaTimestamp:
return string_builder << "MediaTimestamp";
case MessageEntity::Type::Spoiler:
return string_builder << "Spoiler";
default:
UNREACHABLE();
return string_builder << "Impossible";
@ -137,6 +139,8 @@ tl_object_ptr<td_api::TextEntityType> MessageEntity::get_text_entity_type_object
return make_tl_object<td_api::textEntityTypeBankCardNumber>();
case MessageEntity::Type::MediaTimestamp:
return make_tl_object<td_api::textEntityTypeMediaTimestamp>(media_timestamp);
case MessageEntity::Type::Spoiler:
return make_tl_object<td_api::textEntityTypeSpoiler>();
default:
UNREACHABLE();
return nullptr;
@ -1395,7 +1399,7 @@ static constexpr int32 get_entity_type_mask(MessageEntity::Type type) {
static constexpr int32 get_splittable_entities_mask() {
return get_entity_type_mask(MessageEntity::Type::Bold) | get_entity_type_mask(MessageEntity::Type::Italic) |
get_entity_type_mask(MessageEntity::Type::Underline) |
get_entity_type_mask(MessageEntity::Type::Strikethrough);
get_entity_type_mask(MessageEntity::Type::Strikethrough) | get_entity_type_mask(MessageEntity::Type::Spoiler);
}
static constexpr int32 get_blockquote_entities_mask() {
@ -1449,15 +1453,18 @@ static int32 is_hidden_data_entity(MessageEntity::Type type) {
get_pre_entities_mask())) != 0;
}
static constexpr size_t SPLITTABLE_ENTITY_TYPE_COUNT = 4;
static constexpr size_t SPLITTABLE_ENTITY_TYPE_COUNT = 5;
static size_t get_splittable_entity_type_index(MessageEntity::Type type) {
if (static_cast<int32>(type) <= static_cast<int32>(MessageEntity::Type::Bold) + 1) {
// Bold or Italic
return static_cast<int32>(type) - static_cast<int32>(MessageEntity::Type::Bold);
} else {
} else if (static_cast<int32>(type) <= static_cast<int32>(MessageEntity::Type::Underline) + 1) {
// Underline or Strikethrough
return static_cast<int32>(type) - static_cast<int32>(MessageEntity::Type::Underline) + 2;
} else {
CHECK(type == MessageEntity::Type::Spoiler);
return 4;
}
}
@ -1765,6 +1772,8 @@ string get_first_url(Slice text, const vector<MessageEntity> &entities) {
break;
case MessageEntity::Type::MediaTimestamp:
break;
case MessageEntity::Type::Spoiler:
break;
default:
UNREACHABLE();
}
@ -1963,6 +1972,8 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
return c == '_' && text[i + 1] == '_';
case MessageEntity::Type::Strikethrough:
return c == '~';
case MessageEntity::Type::Spoiler:
return c == '|' && text[i + 1] == '|';
default:
UNREACHABLE();
return false;
@ -1990,6 +2001,15 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
case '~':
type = MessageEntity::Type::Strikethrough;
break;
case '|':
if (text[i + 1] == '|') {
i++;
type = MessageEntity::Type::Spoiler;
} else {
return Status::Error(400, PSLICE() << "Character '" << text[i]
<< "' is reserved and must be escaped with the preceding '\\'");
}
break;
case '[':
type = MessageEntity::Type::TextUrl;
break;
@ -2038,6 +2058,7 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
case MessageEntity::Type::Strikethrough:
break;
case MessageEntity::Type::Underline:
case MessageEntity::Type::Spoiler:
i++;
break;
case MessageEntity::Type::Pre:
@ -2364,14 +2385,28 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
if ((c == '_' || c == '*' || c == '~') && text[i] == text[i + 1] && unallowed_boundaries.count(utf16_offset) == 0) {
if ((c == '_' || c == '*' || c == '~' || c == '|') && text[i] == text[i + 1] &&
unallowed_boundaries.count(utf16_offset) == 0) {
auto j = i + 2;
while (j != text.size() && text[j] == text[i] && unallowed_boundaries.count(utf16_offset + j - i - 1) == 0) {
j++;
}
if (j == i + 2) {
auto type = c == '_' ? MessageEntity::Type::Italic
: (c == '*' ? MessageEntity::Type::Bold : MessageEntity::Type::Strikethrough);
auto type = [c] {
switch (c) {
case '_':
return MessageEntity::Type::Italic;
case '*':
return MessageEntity::Type::Bold;
case '~':
return MessageEntity::Type::Strikethrough;
case '|':
return MessageEntity::Type::Spoiler;
default:
UNREACHABLE();
return MessageEntity::Type::Size;
}
}();
auto index = get_splittable_entity_type_index(type);
if (splittable_entity_offset[index] != 0) {
auto length = utf16_offset - splittable_entity_offset[index] - 1;
@ -2391,7 +2426,7 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
}
// entities must be valid and can contain only splittable and continuous entities
// __italic__ ~~strikethrough~~ **bold** and [text_url](telegram.org) entities are left to be parsed
// __italic__ ~~strikethrough~~ **bold** ||spoiler|| and [text_url](telegram.org) entities are left to be parsed
static FormattedText parse_markdown_v3_without_pre(Slice text, vector<MessageEntity> entities) {
check_is_sorted(entities);
@ -2405,7 +2440,7 @@ static FormattedText parse_markdown_v3_without_pre(Slice text, vector<MessageEnt
bool have_splittable_entities = false;
for (size_t i = 0; i + 1 < text.size(); i++) {
if ((text[i] == '_' || text[i] == '*' || text[i] == '~') && text[i] == text[i + 1]) {
if ((text[i] == '_' || text[i] == '*' || text[i] == '~' || text[i] == '|') && text[i] == text[i + 1]) {
have_splittable_entities = true;
break;
}
@ -2719,6 +2754,10 @@ FormattedText get_markdown_v3(FormattedText text) {
result.text += "~~";
utf16_added += 2;
break;
case MessageEntity::Type::Spoiler:
result.text += "||";
utf16_added += 2;
break;
case MessageEntity::Type::TextUrl:
result.text += "](";
result.text += entity->argument;
@ -2757,6 +2796,10 @@ FormattedText get_markdown_v3(FormattedText text) {
result.text += "~~";
utf16_added += 2;
break;
case MessageEntity::Type::Spoiler:
result.text += "||";
utf16_added += 2;
break;
case MessageEntity::Type::TextUrl:
result.text += '[';
utf16_added++;
@ -2895,7 +2938,7 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
string tag_name = to_lower(text.substr(begin_pos + 1, i - begin_pos - 1));
if (tag_name != "a" && tag_name != "b" && tag_name != "strong" && tag_name != "i" && tag_name != "em" &&
tag_name != "s" && tag_name != "strike" && tag_name != "del" && tag_name != "u" && tag_name != "ins" &&
tag_name != "pre" && tag_name != "code") {
tag_name != "span" && tag_name != "pre" && tag_name != "code") {
return Status::Error(400, PSLICE()
<< "Unsupported start tag \"" << tag_name << "\" at byte offset " << begin_pos);
}
@ -2971,9 +3014,16 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
} else if (tag_name == "code" && attribute_name == Slice("class") &&
begins_with(attribute_value, "language-")) {
argument = attribute_value.substr(9);
} else if (tag_name == "span" && attribute_name == Slice("class") && begins_with(attribute_value, "tg-")) {
argument = attribute_value.substr(3);
}
}
if (tag_name == "span" && argument != "spoiler") {
return Status::Error(400, PSLICE()
<< "Tag \"span\" must have class \"tg-spoiler\" at byte offset " << begin_pos);
}
nested_entities.emplace_back(std::move(tag_name), std::move(argument), utf16_offset, result.size());
} else {
// end of an entity
@ -3009,6 +3059,9 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
entities.emplace_back(MessageEntity::Type::Strikethrough, entity_offset, entity_length);
} else if (tag_name == "u" || tag_name == "ins") {
entities.emplace_back(MessageEntity::Type::Underline, entity_offset, entity_length);
} else if (tag_name == "span") {
CHECK(nested_entities.back().argument == "spoiler");
entities.emplace_back(MessageEntity::Type::Spoiler, entity_offset, entity_length);
} else if (tag_name == "a") {
auto url = std::move(nested_entities.back().argument);
if (url.empty()) {
@ -3139,6 +3192,8 @@ vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entiti
break;
case MessageEntity::Type::MediaTimestamp:
break;
case MessageEntity::Type::Spoiler:
break;
default:
UNREACHABLE();
}
@ -3239,6 +3294,9 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
entities.emplace_back(MessageEntity::Type::MediaTimestamp, offset, length, entity->media_timestamp_);
break;
}
case td_api::textEntityTypeSpoiler::ID:
entities.emplace_back(MessageEntity::Type::Spoiler, offset, length);
break;
default:
UNREACHABLE();
}
@ -3319,6 +3377,11 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
entities.emplace_back(MessageEntity::Type::Strikethrough, entity->offset_, entity->length_);
break;
}
case telegram_api::messageEntitySpoiler::ID: {
auto entity = static_cast<const telegram_api::messageEntitySpoiler *>(server_entity.get());
entities.emplace_back(MessageEntity::Type::Spoiler, entity->offset_, entity->length_);
break;
}
case telegram_api::messageEntityBlockquote::ID: {
auto entity = static_cast<const telegram_api::messageEntityBlockquote *>(server_entity.get());
entities.emplace_back(MessageEntity::Type::BlockQuote, entity->offset_, entity->length_);
@ -3734,7 +3797,7 @@ static void split_entities(vector<MessageEntity> &entities, const vector<Message
auto add_entities = [&](int32 end_offset) {
auto flush_entities = [&](int32 offset) {
for (auto type : {MessageEntity::Type::Bold, MessageEntity::Type::Italic, MessageEntity::Type::Underline,
MessageEntity::Type::Strikethrough}) {
MessageEntity::Type::Strikethrough, MessageEntity::Type::Spoiler}) {
auto index = get_splittable_entity_type_index(type);
if (end_pos[index] != 0 && begin_pos[index] < offset) {
if (end_pos[index] <= offset) {
@ -4192,6 +4255,9 @@ vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(co
case MessageEntity::Type::Strikethrough:
result.push_back(make_tl_object<telegram_api::messageEntityStrike>(entity.offset, entity.length));
break;
case MessageEntity::Type::Spoiler:
result.push_back(make_tl_object<telegram_api::messageEntitySpoiler>(entity.offset, entity.length));
break;
default:
UNREACHABLE();
}

View File

@ -48,6 +48,7 @@ class MessageEntity {
BlockQuote,
BankCardNumber,
MediaTimestamp,
Spoiler,
Size
};
Type type = Type::Size;

View File

@ -3067,9 +3067,9 @@ class SendMessageActor final : public NetActorOnce {
}
auto query = G()->net_query_creator().create(telegram_api::messages_sendMessage(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer),
reply_to_message_id.get_server_message_id().get(), text, random_id, std::move(reply_markup),
std::move(entities), schedule_date, std::move(as_input_peer)));
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
std::move(input_peer), reply_to_message_id.get_server_message_id().get(), text, random_id,
std::move(reply_markup), std::move(entities), schedule_date, std::move(as_input_peer)));
if (G()->shared_config().get_option_boolean("use_quick_ack")) {
query->quick_ack_promise_ = PromiseCreator::lambda(
[random_id](Unit) {
@ -3258,10 +3258,10 @@ class SendMultiMediaActor final : public NetActorOnce {
flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_SEND_AS;
}
auto query = G()->net_query_creator().create(
telegram_api::messages_sendMultiMedia(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/,
std::move(input_peer), reply_to_message_id.get_server_message_id().get(),
std::move(input_single_media), schedule_date, std::move(as_input_peer)));
auto query = G()->net_query_creator().create(telegram_api::messages_sendMultiMedia(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer),
reply_to_message_id.get_server_message_id().get(), std::move(input_single_media), schedule_date,
std::move(as_input_peer)));
// no quick ack, because file reference errors are very likely to happen
query->debug("send to MessagesManager::MultiSequenceDispatcher");
send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
@ -3378,7 +3378,7 @@ class SendMediaActor final : public NetActorOnce {
}
auto query = G()->net_query_creator().create(telegram_api::messages_sendMedia(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer),
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer),
reply_to_message_id.get_server_message_id().get(), std::move(input_media), text, random_id,
std::move(reply_markup), std::move(entities), schedule_date, std::move(as_input_peer)));
if (G()->shared_config().get_option_boolean("use_quick_ack") && was_uploaded_) {
@ -3757,8 +3757,8 @@ class ForwardMessagesActor final : public NetActorOnce {
auto query = G()->net_query_creator().create(telegram_api::messages_forwardMessages(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
std::move(from_input_peer), MessagesManager::get_server_message_ids(message_ids), std::move(random_ids),
std::move(to_input_peer), schedule_date, std::move(as_input_peer)));
false /*ignored*/, std::move(from_input_peer), MessagesManager::get_server_message_ids(message_ids),
std::move(random_ids), std::move(to_input_peer), schedule_date, std::move(as_input_peer)));
if (G()->shared_config().get_option_boolean("use_quick_ack")) {
query->quick_ack_promise_ = PromiseCreator::lambda(
[random_ids = random_ids_](Unit) {
@ -23892,6 +23892,7 @@ unique_ptr<MessagesManager::Message> MessagesManager::create_message_to_send(
m->is_channel_post = is_channel_post;
m->is_outgoing = is_scheduled || dialog_id != DialogId(my_id);
m->from_background = options.from_background;
m->noforwards = options.protect_content;
m->view_count = is_channel_post && !is_scheduled ? 1 : 0;
m->forward_count = 0;
if ([&] {
@ -24526,6 +24527,7 @@ Result<MessagesManager::MessageSendOptions> MessagesManager::process_message_sen
if (options != nullptr) {
result.disable_notification = options->disable_notification_;
result.from_background = options->from_background_;
result.protect_content = options->protect_content_;
TRY_RESULT_ASSIGN(result.schedule_date, get_message_schedule_date(std::move(options->scheduling_state_)));
}
@ -24547,6 +24549,10 @@ Result<MessagesManager::MessageSendOptions> MessagesManager::process_message_sen
}
}
if (result.protect_content && !td_->auth_manager_->is_bot()) {
result.protect_content = false;
}
return result;
}
@ -26674,6 +26680,9 @@ int32 MessagesManager::get_message_flags(const Message *m) {
if (m->message_id.is_scheduled()) {
flags |= SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE;
}
if (m->noforwards) {
flags |= SEND_MESSAGE_FLAG_NOFORWARDS;
}
return flags;
}
@ -26983,6 +26992,9 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d
if (messages[0]->has_explicit_sender) {
flags |= SEND_MESSAGE_FLAG_HAS_SEND_AS;
}
if (messages[0]->noforwards) {
flags |= SEND_MESSAGE_FLAG_NOFORWARDS;
}
vector<int64> random_ids =
transform(messages, [this, to_dialog_id](const Message *m) { return begin_send_message(to_dialog_id, m); });
@ -27492,7 +27504,7 @@ Result<vector<MessageId>> MessagesManager::resend_messages(DialogId dialog_id, v
auto need_another_sender =
message->send_error_code == 400 && message->send_error_message == CSlice("SEND_AS_PEER_INVALID");
MessageSendOptions options(message->disable_notification, message->from_background,
MessageSendOptions options(message->disable_notification, message->from_background, message->noforwards,
get_message_schedule_date(message.get()));
Message *m = get_message_to_send(
d, message->top_thread_message_id,

View File

@ -135,6 +135,7 @@ class MessagesManager final : public Actor {
static constexpr int32 SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE = 1 << 10;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_MESSAGE = 1 << 11;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_SEND_AS = 1 << 13;
static constexpr int32 SEND_MESSAGE_FLAG_NOFORWARDS = 1 << 14;
static constexpr int32 ONLINE_MEMBER_COUNT_CACHE_EXPIRE_TIME = 30 * 60;
@ -1653,11 +1654,15 @@ class MessagesManager final : public Actor {
struct MessageSendOptions {
bool disable_notification = false;
bool from_background = false;
bool protect_content = false;
int32 schedule_date = 0;
MessageSendOptions() = default;
MessageSendOptions(bool disable_notification, bool from_background, int32 schedule_date)
: disable_notification(disable_notification), from_background(from_background), schedule_date(schedule_date) {
MessageSendOptions(bool disable_notification, bool from_background, bool protect_content, int32 schedule_date)
: disable_notification(disable_notification)
, from_background(from_background)
, protect_content(protect_content)
, schedule_date(schedule_date) {
}
};

View File

@ -10,6 +10,7 @@
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessagesManager.h"
@ -94,15 +95,17 @@ struct SponsoredMessageManager::SponsoredMessage {
DialogId sponsor_dialog_id;
ServerMessageId server_message_id;
string start_param;
string invite_hash;
unique_ptr<MessageContent> content;
SponsoredMessage() = default;
SponsoredMessage(int64 local_id, DialogId sponsor_dialog_id, ServerMessageId server_message_id, string start_param,
unique_ptr<MessageContent> content)
string invite_hash, unique_ptr<MessageContent> content)
: local_id(local_id)
, sponsor_dialog_id(sponsor_dialog_id)
, server_message_id(server_message_id)
, start_param(std::move(start_param))
, invite_hash(std::move(invite_hash))
, content(std::move(content)) {
}
};
@ -148,6 +151,7 @@ void SponsoredMessageManager::delete_cached_sponsored_messages(DialogId dialog_i
td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponsored_message_object(
DialogId dialog_id, const SponsoredMessage &sponsored_message) const {
td_api::object_ptr<td_api::chatInviteLinkInfo> chat_invite_link_info;
td_api::object_ptr<td_api::InternalLinkType> link;
switch (sponsored_message.sponsor_dialog_id.get_type()) {
case DialogType::User: {
@ -170,12 +174,23 @@ td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponso
PSTRING() << t_me << "c/" << channel_id.get() << '/' << sponsored_message.server_message_id.get());
}
break;
case DialogType::None: {
CHECK(!sponsored_message.invite_hash.empty());
auto invite_link = LinkManager::get_dialog_invite_link(sponsored_message.invite_hash, false);
chat_invite_link_info = td_->contacts_manager_->get_chat_invite_link_info_object(invite_link);
if (chat_invite_link_info == nullptr) {
LOG(ERROR) << "Failed to get invite link info for " << invite_link;
return nullptr;
}
link = td_api::make_object<td_api::internalLinkTypeChatInvite>(
LinkManager::get_dialog_invite_link(sponsored_message.invite_hash, true));
}
default:
break;
}
return td_api::make_object<td_api::sponsoredMessage>(
sponsored_message.local_id, sponsored_message.sponsor_dialog_id.get(), std::move(link),
get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
sponsored_message.local_id, sponsored_message.sponsor_dialog_id.get(), std::move(chat_invite_link_info),
std::move(link), get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
}
td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponsored_message_object(
@ -243,17 +258,41 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages(
td_->contacts_manager_->on_get_chats(std::move(sponsored_messages->chats_), "on_get_dialog_sponsored_messages");
for (auto &sponsored_message : sponsored_messages->messages_) {
DialogId sponsor_dialog_id(sponsored_message->from_id_);
if (!sponsor_dialog_id.is_valid() || !td_->messages_manager_->have_dialog_info_force(sponsor_dialog_id)) {
LOG(ERROR) << "Receive unknown sponsor " << sponsor_dialog_id;
DialogId sponsor_dialog_id;
ServerMessageId server_message_id;
string invite_hash;
if (sponsored_message->from_id_ != nullptr) {
sponsor_dialog_id = DialogId(sponsored_message->from_id_);
if (!sponsor_dialog_id.is_valid() || !td_->messages_manager_->have_dialog_info_force(sponsor_dialog_id)) {
LOG(ERROR) << "Receive unknown sponsor " << sponsor_dialog_id;
continue;
}
server_message_id = ServerMessageId(sponsored_message->channel_post_);
if (!server_message_id.is_valid() && server_message_id != ServerMessageId()) {
LOG(ERROR) << "Receive invalid channel post in " << to_string(sponsored_message);
server_message_id = ServerMessageId();
}
td_->messages_manager_->force_create_dialog(sponsor_dialog_id, "on_get_dialog_sponsored_messages");
} else if (sponsored_message->chat_invite_ != nullptr && !sponsored_message->chat_invite_hash_.empty()) {
auto invite_link = LinkManager::get_dialog_invite_link(sponsored_message->chat_invite_hash_, false);
if (invite_link.empty()) {
LOG(ERROR) << "Receive invalid invite link hash in " << to_string(sponsored_message);
continue;
}
auto chat_invite = to_string(sponsored_message->chat_invite_);
td_->contacts_manager_->on_get_dialog_invite_link_info(invite_link, std::move(sponsored_message->chat_invite_),
Promise<Unit>());
auto chat_invite_link_info = td_->contacts_manager_->get_chat_invite_link_info_object(invite_link);
if (chat_invite_link_info == nullptr) {
LOG(ERROR) << "Failed to get invite link info from " << chat_invite << " for " << to_string(sponsored_message);
continue;
}
invite_hash = std::move(sponsored_message->chat_invite_hash_);
} else {
LOG(ERROR) << "Receive " << to_string(sponsored_message);
continue;
}
auto server_message_id = ServerMessageId(sponsored_message->channel_post_);
if (!server_message_id.is_valid() && server_message_id != ServerMessageId()) {
LOG(ERROR) << "Receive invalid channel post in " << to_string(sponsored_message);
server_message_id = ServerMessageId();
}
td_->messages_manager_->force_create_dialog(sponsor_dialog_id, "on_get_dialog_sponsored_messages");
auto message_text = get_message_text(td_->contacts_manager_.get(), std::move(sponsored_message->message_),
std::move(sponsored_message->entities_), true, true, 0, false,
"on_get_dialog_sponsored_messages");
@ -279,7 +318,8 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages(
CHECK(messages->message_random_ids.count(local_id) == 0);
messages->message_random_ids[local_id] = sponsored_message->random_id_.as_slice().str();
messages->messages.emplace_back(local_id, sponsor_dialog_id, server_message_id,
std::move(sponsored_message->start_param_), std::move(content));
std::move(sponsored_message->start_param_), std::move(invite_hash),
std::move(content));
}
for (auto &promise : promises) {

View File

@ -893,8 +893,8 @@ void UpdatesManager::on_get_updates(tl_object_ptr<telegram_api::Updates> &&updat
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_,
make_tl_object<telegram_api::peerUser>(from_id), make_tl_object<telegram_api::peerUser>(update->user_id_),
std::move(update->fwd_from_), update->via_bot_id_, std::move(update->reply_to_), update->date_,
update->message_, nullptr, nullptr, std::move(update->entities_), 0, 0, nullptr, 0, string(), 0, Auto(),
update->ttl_period_);
update->message_, nullptr, nullptr, std::move(update->entities_), 0, 0, nullptr, 0, string(), 0, nullptr,
Auto(), update->ttl_period_);
on_pending_update(
make_tl_object<telegram_api::updateNewMessage>(std::move(message), update->pts_, update->pts_count_), 0,
std::move(promise), "telegram_api::updatesShortMessage");
@ -918,7 +918,7 @@ void UpdatesManager::on_get_updates(tl_object_ptr<telegram_api::Updates> &&updat
make_tl_object<telegram_api::peerUser>(update->from_id_),
make_tl_object<telegram_api::peerChat>(update->chat_id_), std::move(update->fwd_from_), update->via_bot_id_,
std::move(update->reply_to_), update->date_, update->message_, nullptr, nullptr, std::move(update->entities_),
0, 0, nullptr, 0, string(), 0, Auto(), update->ttl_period_);
0, 0, nullptr, 0, string(), 0, nullptr, Auto(), update->ttl_period_);
on_pending_update(
make_tl_object<telegram_api::updateNewMessage>(std::move(message), update->pts_, update->pts_count_), 0,
std::move(promise), "telegram_api::updatesShortChatMessage");
@ -3280,4 +3280,7 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePendingJoinRequ
// unsupported updates
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateMessageReactions> update, Promise<Unit> &&promise) {
}
} // namespace td

View File

@ -504,6 +504,8 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updatePendingJoinRequests> update, Promise<Unit> &&promise);
// unsupported updates
void on_update(tl_object_ptr<telegram_api::updateMessageReactions> update, Promise<Unit> &&promise);
};
} // namespace td

View File

@ -10,7 +10,7 @@
namespace td {
constexpr int32 MTPROTO_LAYER = 135;
constexpr int32 MTPROTO_LAYER = 136;
enum class Version : int32 {
Initial, // 0

View File

@ -1619,14 +1619,15 @@ class CliClient final : public Actor {
auto chat = as_chat_id(chat_id);
auto id = send_request(td_api::make_object<td_api::sendMessage>(
chat, as_message_thread_id(message_thread_id_), reply_to_message_id,
td_api::make_object<td_api::messageSendOptions>(disable_notification, from_background,
td_api::make_object<td_api::messageSendOptions>(disable_notification, from_background, true,
as_message_scheduling_state(schedule_date_)),
nullptr, std::move(input_message_content)));
query_id_to_send_message_info_[id].start_time = Time::now();
}
td_api::object_ptr<td_api::messageSendOptions> default_message_send_options() const {
return td_api::make_object<td_api::messageSendOptions>(false, false, as_message_scheduling_state(schedule_date_));
return td_api::make_object<td_api::messageSendOptions>(false, false, false,
as_message_scheduling_state(schedule_date_));
}
void send_get_background_url(td_api::object_ptr<td_api::BackgroundType> &&background_type) {

View File

@ -979,7 +979,7 @@ TEST(MessageEntities, fix_formatted_text) {
{td::MessageEntity::Type::Mention, 7, 6},
{td::MessageEntity::Type::Italic, 7, 6}});
// _a*b*_
// __a~b~__
check_fix_formatted_text(
"ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 1, 1}}, "ab",
{{td::MessageEntity::Type::Underline, 0, 1},
@ -1007,41 +1007,41 @@ TEST(MessageEntities, fix_formatted_text) {
{td::MessageEntity::Type::Underline, 1, 1},
{td::MessageEntity::Type::Strikethrough, 1, 1}});
// _*a*b_
check_fix_formatted_text(
"ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}}, "ab",
{{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}});
check_fix_formatted_text(
"ab",
{{td::MessageEntity::Type::Underline, 0, 1},
{td::MessageEntity::Type::Underline, 1, 1},
{td::MessageEntity::Type::Strikethrough, 0, 1}},
"ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}});
// __||a||b__
check_fix_formatted_text("ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Spoiler, 0, 1}},
"ab",
{{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Spoiler, 0, 1}});
check_fix_formatted_text("ab",
{{td::MessageEntity::Type::Underline, 0, 1},
{td::MessageEntity::Type::Underline, 1, 1},
{td::MessageEntity::Type::Spoiler, 0, 1}},
"ab",
{{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Spoiler, 0, 1}});
// _*a*_\r_*b*_
check_fix_formatted_text("a\rb",
{{td::MessageEntity::Type::Bold, 0, 1},
{td::MessageEntity::Type::Strikethrough, 0, 1},
{td::MessageEntity::Type::Italic, 0, 1},
{td::MessageEntity::Type::Bold, 2, 1},
{td::MessageEntity::Type::Strikethrough, 2, 1}},
"ab",
{{td::MessageEntity::Type::Bold, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 2}});
{td::MessageEntity::Type::Italic, 2, 1}},
"ab", {{td::MessageEntity::Type::Bold, 0, 2}, {td::MessageEntity::Type::Italic, 0, 2}});
check_fix_formatted_text("a\nb",
{{td::MessageEntity::Type::Bold, 0, 1},
{td::MessageEntity::Type::Strikethrough, 0, 1},
{td::MessageEntity::Type::Italic, 0, 1},
{td::MessageEntity::Type::Bold, 2, 1},
{td::MessageEntity::Type::Strikethrough, 2, 1}},
{td::MessageEntity::Type::Italic, 2, 1}},
"a\nb",
{{td::MessageEntity::Type::Bold, 0, 1},
{td::MessageEntity::Type::Strikethrough, 0, 1},
{td::MessageEntity::Type::Italic, 0, 1},
{td::MessageEntity::Type::Bold, 2, 1},
{td::MessageEntity::Type::Strikethrough, 2, 1}});
{td::MessageEntity::Type::Italic, 2, 1}});
// ||`a`||
check_fix_formatted_text("a", {{td::MessageEntity::Type::Pre, 0, 1}, {td::MessageEntity::Type::Spoiler, 0, 1}}, "a",
{{td::MessageEntity::Type::Pre, 0, 1}});
check_fix_formatted_text("a", {{td::MessageEntity::Type::Spoiler, 0, 1}, {td::MessageEntity::Type::Pre, 0, 1}}, "a",
{{td::MessageEntity::Type::Pre, 0, 1}});
// _`a`_
check_fix_formatted_text("a", {{td::MessageEntity::Type::Pre, 0, 1}, {td::MessageEntity::Type::Strikethrough, 0, 1}},
"a", {{td::MessageEntity::Type::Pre, 0, 1}});
check_fix_formatted_text("a", {{td::MessageEntity::Type::Strikethrough, 0, 1}, {td::MessageEntity::Type::Pre, 0, 1}},
"a", {{td::MessageEntity::Type::Pre, 0, 1}});
check_fix_formatted_text("abc",
{{td::MessageEntity::Type::Pre, 0, 3}, {td::MessageEntity::Type::Strikethrough, 1, 1}},
"abc", {{td::MessageEntity::Type::Pre, 0, 3}});
@ -1259,6 +1259,14 @@ TEST(MessageEntities, parse_html) {
check_parse_html("<i>\t</i>", "\t", {{td::MessageEntity::Type::Italic, 0, 1}});
check_parse_html("<i>\r</i>", "\r", {{td::MessageEntity::Type::Italic, 0, 1}});
check_parse_html("<i>\n</i>", "\n", {{td::MessageEntity::Type::Italic, 0, 1}});
check_parse_html("➡️ ➡️<span class = \"tg-spoiler\">➡️ ➡️</span><b>➡️ ➡️</b>",
"➡️ ➡️➡️ ➡️➡️ ➡️",
{{td::MessageEntity::Type::Spoiler, 5, 5}, {td::MessageEntity::Type::Bold, 10, 5}});
check_parse_html("🏟 🏟<span class=\"tg-spoiler\">🏟 &lt🏟</span>", "🏟 🏟🏟 <🏟",
{{td::MessageEntity::Type::Spoiler, 5, 6}});
check_parse_html("🏟 🏟<span class=\"tg-spoiler\">🏟 &gt;<b aba = caba>&lt🏟</b></span>",
"🏟 🏟🏟 ><🏟",
{{td::MessageEntity::Type::Spoiler, 5, 7}, {td::MessageEntity::Type::Bold, 9, 3}});
check_parse_html("<a href=telegram.org>\t</a>", "\t",
{{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}});
check_parse_html("<a href=telegram.org>\r</a>", "\r",
@ -1360,6 +1368,7 @@ TEST(MessageEntities, parse_markdown) {
check_parse_markdown("[telegram\\.org](asd", "Can't find end of a URL at byte offset 16");
check_parse_markdown("🏟 🏟__🏟 _🏟___", "Can't find end of Italic entity at byte offset 23");
check_parse_markdown("🏟 🏟__", "Can't find end of Underline entity at byte offset 9");
check_parse_markdown("🏟 🏟||test\\|", "Can't find end of Spoiler entity at byte offset 9");
check_parse_markdown("", "", {});
check_parse_markdown("\\\\", "\\", {});
@ -1401,6 +1410,7 @@ TEST(MessageEntities, parse_markdown) {
check_parse_markdown("🏟 🏟```🏟 \\\\\\`🏟```", "🏟 🏟 \\`🏟",
{{td::MessageEntity::Type::PreCode, 5, 5, "🏟"}});
check_parse_markdown("🏟 🏟**", "🏟 🏟", {});
check_parse_markdown("||test||", "test", {{td::MessageEntity::Type::Spoiler, 0, 4}});
check_parse_markdown("🏟 🏟``", "🏟 🏟", {});
check_parse_markdown("🏟 🏟``````", "🏟 🏟", {});
check_parse_markdown("🏟 🏟____", "🏟 🏟", {});
@ -1569,6 +1579,9 @@ TEST(MessageEntities, parse_markdown_v3) {
"[text](example.com)",
{{td::MessageEntity::Type::Strikethrough, 0, 1}, {td::MessageEntity::Type::Strikethrough, 5, 14}}, "text",
{{td::MessageEntity::Type::TextUrl, 0, 4, "http://example.com/"}});
check_parse_markdown_v3("[text](example.com)",
{{td::MessageEntity::Type::Spoiler, 0, 1}, {td::MessageEntity::Type::Spoiler, 5, 14}}, "text",
{{td::MessageEntity::Type::TextUrl, 0, 4, "http://example.com/"}});
check_parse_markdown_v3("🏟[🏟](t.me) `🏟` [🏟](t.me) `a`", "🏟🏟 🏟 🏟 a",
{{td::MessageEntity::Type::TextUrl, 2, 2, "http://t.me/"},
@ -1580,14 +1593,16 @@ TEST(MessageEntities, parse_markdown_v3) {
check_parse_markdown_v3("__\n__", "\n", {{td::MessageEntity::Type::Italic, 0, 1}});
check_parse_markdown_v3("__ __a", " a", {}, true);
check_parse_markdown_v3("__\n__a", "\na", {}, true);
check_parse_markdown_v3("**** __a__ **b** ~~c~~", "**** a b c",
check_parse_markdown_v3("**** __a__ **b** ~~c~~ ||d||", "**** a b c d",
{{td::MessageEntity::Type::Italic, 5, 1},
{td::MessageEntity::Type::Bold, 7, 1},
{td::MessageEntity::Type::Strikethrough, 9, 1}});
check_parse_markdown_v3("тест __аааа__ **бббб** ~~вввв~~", "тест аааа бббб вввв",
{td::MessageEntity::Type::Strikethrough, 9, 1},
{td::MessageEntity::Type::Spoiler, 11, 1}});
check_parse_markdown_v3("тест __аааа__ **бббб** ~~вввв~~ ||гггг||", "тест аааа бббб вввв гггг",
{{td::MessageEntity::Type::Italic, 5, 4},
{td::MessageEntity::Type::Bold, 10, 4},
{td::MessageEntity::Type::Strikethrough, 15, 4}});
{td::MessageEntity::Type::Strikethrough, 15, 4},
{td::MessageEntity::Type::Spoiler, 20, 4}});
check_parse_markdown_v3("___a___ ***b** ~c~~", "___a___ ***b** ~c~~", {});
check_parse_markdown_v3(
"__asd[ab__cd](t.me)", "asdabcd",
@ -1614,6 +1629,18 @@ TEST(MessageEntities, parse_markdown_v3) {
check_parse_markdown_v3("__#test__test", {{td::MessageEntity::Type::Strikethrough, 0, 2}}, "#testtest",
{{td::MessageEntity::Type::Italic, 0, 5}});
check_parse_markdown_v3(
"~~**~~||**a||", {{td::MessageEntity::Type::Strikethrough, 2, 1}, {td::MessageEntity::Type::Bold, 6, 1}},
"**||**a||", {{td::MessageEntity::Type::Strikethrough, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}}, true);
check_parse_markdown_v3("**||**a||",
{{td::MessageEntity::Type::Strikethrough, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}},
"||a||", {{td::MessageEntity::Type::Bold, 0, 2}}, true);
check_parse_markdown_v3("||a||", {{td::MessageEntity::Type::Bold, 0, 2}}, "a",
{{td::MessageEntity::Type::Spoiler, 0, 1}}, true);
check_parse_markdown_v3("~~||~~#test||test", "#testtest", {{td::MessageEntity::Type::Spoiler, 0, 5}});
check_parse_markdown_v3("||#test||test", {{td::MessageEntity::Type::Strikethrough, 0, 2}}, "#testtest",
{{td::MessageEntity::Type::Spoiler, 0, 5}});
check_parse_markdown_v3("__[ab_](t.me)_", "__ab__", {{td::MessageEntity::Type::TextUrl, 2, 3, "http://t.me/"}});
check_parse_markdown_v3(
"__[ab__](t.me)_", "ab_",
@ -1628,6 +1655,20 @@ TEST(MessageEntities, parse_markdown_v3) {
check_parse_markdown_v3("`a` __ab__", {{td::MessageEntity::Type::Underline, 5, 1}}, "a __ab__",
{{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Underline, 3, 1}});
check_parse_markdown_v3("||[ab|](t.me)|", "||ab||", {{td::MessageEntity::Type::TextUrl, 2, 3, "http://t.me/"}});
check_parse_markdown_v3(
"||[ab||](t.me)|", "ab|",
{{td::MessageEntity::Type::TextUrl, 0, 2, "http://t.me/"}, {td::MessageEntity::Type::Spoiler, 0, 2}});
check_parse_markdown_v3("||[||ab||](t.me)||", "||||ab||||",
{{td::MessageEntity::Type::TextUrl, 2, 6, "http://t.me/"}});
check_parse_markdown_v3(
"||[||ab||](t.me)a||", "||||aba",
{{td::MessageEntity::Type::TextUrl, 2, 4, "http://t.me/"}, {td::MessageEntity::Type::Spoiler, 6, 1}});
check_parse_markdown_v3("`a` ||ab||", {{td::MessageEntity::Type::Bold, 6, 3}}, "a ||ab||",
{{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Bold, 4, 3}});
check_parse_markdown_v3("`a` ||ab||", {{td::MessageEntity::Type::Underline, 5, 1}}, "a ||ab||",
{{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Underline, 3, 1}});
check_parse_markdown_v3("`a` @test__test__test", "a @test__test__test", {{td::MessageEntity::Type::Code, 0, 1}});
check_parse_markdown_v3("`a` #test__test__test", "a #test__test__test", {{td::MessageEntity::Type::Code, 0, 1}});
check_parse_markdown_v3("`a` __@test_test_test__", "a @test_test_test",
@ -1652,13 +1693,17 @@ TEST(MessageEntities, parse_markdown_v3) {
{{td::MessageEntity::Type::Italic, 0, 6},
{td::MessageEntity::Type::Bold, 2, 6},
{td::MessageEntity::Type::Strikethrough, 4, 6}});
check_parse_markdown_v3("__ab**cd~~ef__gh**ij~~", "abcdefghij",
check_parse_markdown_v3("__ab**cd~~ef||gh__ij**kl~~mn||", "abcdefghijklmn",
{{td::MessageEntity::Type::Italic, 0, 2},
{td::MessageEntity::Type::Bold, 2, 2},
{td::MessageEntity::Type::Italic, 2, 2},
{td::MessageEntity::Type::Strikethrough, 4, 6},
{td::MessageEntity::Type::Bold, 4, 4},
{td::MessageEntity::Type::Italic, 4, 2}},
{td::MessageEntity::Type::Bold, 4, 2},
{td::MessageEntity::Type::Italic, 4, 2},
{td::MessageEntity::Type::Strikethrough, 4, 2},
{td::MessageEntity::Type::Spoiler, 6, 8},
{td::MessageEntity::Type::Strikethrough, 6, 6},
{td::MessageEntity::Type::Bold, 6, 4},
{td::MessageEntity::Type::Italic, 6, 2}},
true);
check_parse_markdown_v3("__ab**[cd~~ef__](t.me)gh**ij~~", "abcdefghij",
{{td::MessageEntity::Type::Italic, 0, 6},
@ -1682,9 +1727,9 @@ TEST(MessageEntities, parse_markdown_v3) {
check_parse_markdown_v3(
"__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) __italic**bold "
"italic__bold**__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) "
"__italic**bold italic__bold**",
"__italic**bold italic__bold** ||spoiler||",
"italic strikethrough bold code pre italic text_url italicbold italicbolditalic strikethrough bold code pre "
"italic text_url italicbold italicbold",
"italic text_url italicbold italicbold spoiler",
{{td::MessageEntity::Type::Italic, 0, 6},
{td::MessageEntity::Type::Strikethrough, 7, 13},
{td::MessageEntity::Type::Bold, 21, 4},
@ -1702,14 +1747,15 @@ TEST(MessageEntities, parse_markdown_v3) {
{td::MessageEntity::Type::TextUrl, 107, 15, "http://telegram.org/"},
{td::MessageEntity::Type::Italic, 107, 6},
{td::MessageEntity::Type::Italic, 123, 17},
{td::MessageEntity::Type::Bold, 129, 15}});
{td::MessageEntity::Type::Bold, 129, 15},
{td::MessageEntity::Type::Spoiler, 145, 7}});
td::vector<td::string> parts{"a", " #test__a", "__", "**", "~~", "[", "](t.me)", "`"};
td::vector<td::string> parts{"a", " #test__a", "__", "**", "~~", "||", "[", "](t.me)", "`"};
td::vector<td::MessageEntity::Type> types{
td::MessageEntity::Type::Bold, td::MessageEntity::Type::Italic, td::MessageEntity::Type::Underline,
td::MessageEntity::Type::Strikethrough, td::MessageEntity::Type::Code, td::MessageEntity::Type::Pre,
td::MessageEntity::Type::PreCode, td::MessageEntity::Type::TextUrl, td::MessageEntity::Type::MentionName,
td::MessageEntity::Type::Cashtag};
td::MessageEntity::Type::Strikethrough, td::MessageEntity::Type::Spoiler, td::MessageEntity::Type::Code,
td::MessageEntity::Type::Pre, td::MessageEntity::Type::PreCode, td::MessageEntity::Type::TextUrl,
td::MessageEntity::Type::MentionName, td::MessageEntity::Type::Cashtag};
for (size_t test_n = 0; test_n < 1000; test_n++) {
td::string str;
int part_n = td::Random::fast(1, 200);
@ -1767,11 +1813,13 @@ TEST(MessageEntities, get_markdown_v3) {
check_get_markdown_v3("__ __", {}, " ", {{td::MessageEntity::Type::Italic, 0, 1}});
check_get_markdown_v3("** **", {}, " ", {{td::MessageEntity::Type::Bold, 0, 1}});
check_get_markdown_v3("~~ ~~", {}, " ", {{td::MessageEntity::Type::Strikethrough, 0, 1}});
check_get_markdown_v3("__a__ **b** ~~c~~ d", {{td::MessageEntity::Type::PreCode, 18, 1, "C++"}}, "a b c d",
check_get_markdown_v3("|| ||", {}, " ", {{td::MessageEntity::Type::Spoiler, 0, 1}});
check_get_markdown_v3("__a__ **b** ~~c~~ ||d|| e", {{td::MessageEntity::Type::PreCode, 24, 1, "C++"}}, "a b c d e",
{{td::MessageEntity::Type::Italic, 0, 1},
{td::MessageEntity::Type::Bold, 2, 1},
{td::MessageEntity::Type::Strikethrough, 4, 1},
{td::MessageEntity::Type::PreCode, 6, 1, "C++"}});
{td::MessageEntity::Type::Spoiler, 6, 1},
{td::MessageEntity::Type::PreCode, 8, 1, "C++"}});
check_get_markdown_v3("`ab` ```cd``` ef", {{td::MessageEntity::Type::PreCode, 14, 2, "C++"}}, "ab cd ef",
{{td::MessageEntity::Type::Code, 0, 2},
{td::MessageEntity::Type::Pre, 3, 2},