Merge remote-tracking branch 'td/master'
This commit is contained in:
commit
fa1204d3b5
@ -544,6 +544,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/MessageId.h
|
||||
td/telegram/MessageLinkInfo.h
|
||||
td/telegram/MessageReplyInfo.h
|
||||
td/telegram/MessageThreadInfo.h
|
||||
td/telegram/MessagesDb.h
|
||||
td/telegram/MessageSearchFilter.h
|
||||
td/telegram/MessagesManager.h
|
||||
|
@ -203,11 +203,9 @@ For C++ projects that use CMake, the best approach is to build `TDLib` as part o
|
||||
There are several libraries that you could use in your CMake project:
|
||||
|
||||
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a JSON interface. This has a simple C interface, so it can be easily used with any programming language that is able to execute C functions.
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) and [td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) documentation for more information.
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for more information.
|
||||
* Td::TdStatic — static library with C++ interface for general usage.
|
||||
See [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) and [Log](https://core.telegram.org/tdlib/docs/classtd_1_1_log.html) documentation for more information.
|
||||
* Td::TdCoreStatic — static library with low-level C++ interface intended mostly for internal usage.
|
||||
See [ClientActor](https://core.telegram.org/tdlib/docs/classtd_1_1_client_actor.html) and [Log](https://core.telegram.org/tdlib/docs/classtd_1_1_log.html) documentation for more information.
|
||||
See [ClientManager](https://core.telegram.org/tdlib/docs/classtd_1_1_client_manager.html) and [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) documentation for more information.
|
||||
|
||||
For example, part of your CMakeLists.txt may look like this:
|
||||
```
|
||||
@ -245,7 +243,7 @@ git checkout td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h
|
||||
## Using from other programming languages
|
||||
`TDLib` provides efficient native C++, Java, and .NET interfaces.
|
||||
But for most use cases we suggest to use the JSON interface, which can be easily used with any programming language that is able to execute C functions.
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) and [td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) documentation for detailed JSON interface description,
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for detailed JSON interface description,
|
||||
the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of
|
||||
all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||
|
||||
|
@ -626,7 +626,7 @@ function onOptionsChanged() {
|
||||
cmake = 'cmake3';
|
||||
packages += ' gperf';
|
||||
} else {
|
||||
commands.push(sudo + 'dnf --enablerepo=PowerTools install gperf');
|
||||
commands.push(sudo + 'dnf --enablerepo=powertools install gperf');
|
||||
}
|
||||
packages += ' ' + cmake;
|
||||
if (target === 'JNI') {
|
||||
|
@ -7,5 +7,5 @@ Then you can run the example:
|
||||
python tdjson_example.py
|
||||
```
|
||||
|
||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html),
|
||||
[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
|
||||
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||
|
@ -11,5 +11,5 @@ cmake --build . --target install
|
||||
|
||||
Then you can open and build the example with the latest Xcode.
|
||||
|
||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html),
|
||||
[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
|
||||
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||
|
@ -759,6 +759,8 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
|
||||
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
|
||||
//@can_get_statistics True, if the message statistics are available
|
||||
//@can_get_message_thread True, if the message thread info is available
|
||||
//@can_get_media_timestamp_links True, if media timestamp links can be generated for media timestamp entities in the message text, caption or web page description
|
||||
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
|
||||
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
|
||||
//@contains_unread_mention True, if the message contains an unread mention for the current user
|
||||
//@date Point in time (Unix timestamp) when the message was sent
|
||||
@ -776,7 +778,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
|
||||
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
|
||||
//@content Content of the message
|
||||
//@reply_markup Reply markup for the message; may be null
|
||||
message id:int53 sender:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_statistics:Bool can_get_message_thread:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
|
||||
message id:int53 sender:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_statistics:Bool can_get_message_thread:Bool can_get_media_timestamp_links:Bool has_timestamped_media:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
|
||||
|
||||
//@description Contains a list of messages @total_count Approximate total count of messages found @messages List of messages; messages may be null
|
||||
messages total_count:int32 messages:vector<message> = Messages;
|
||||
@ -1852,6 +1854,9 @@ textEntityTypeTextUrl url:string = TextEntityType;
|
||||
//@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user
|
||||
textEntityTypeMentionName user_id:int32 = TextEntityType;
|
||||
|
||||
//@description A media timestamp @media_timestamp Timestamp from which a video/audio/video note/voice note playing should start, in seconds. The media can be in the content or the web page preview of the current message, or in the same places in the replied message
|
||||
textEntityTypeMediaTimestamp media_timestamp:int32 = TextEntityType;
|
||||
|
||||
|
||||
//@description A thumbnail to be sent along with a file; must be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
|
||||
//@width Thumbnail width, usually shouldn't exceed 320. Use 0 if unknown @height Thumbnail height, usually shouldn't exceed 320. Use 0 if unknown
|
||||
@ -3058,7 +3063,7 @@ internalLinkTypeChatInvite = InternalLinkType;
|
||||
//@description The link is a link to the filter settings section of the app
|
||||
internalLinkTypeFilterSettings = InternalLinkType;
|
||||
|
||||
//@description The link is a link to a game. Call searchPublicChat with the given bot username, check that the user is a bot, ask the current user to select a group to send the game, and then call sendMessage with inputMessageGame
|
||||
//@description The link is a link to a game. Call searchPublicChat with the given bot username, check that the user is a bot, ask the current user to select a chat to send the game, and then call sendMessage with inputMessageGame
|
||||
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
|
||||
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
|
||||
|
||||
@ -3119,7 +3124,7 @@ messageLink link:string is_public:Bool = MessageLink;
|
||||
//@is_public True, if the link is a public link for a message in a chat
|
||||
//@chat_id If found, identifier of the chat to which the message belongs, 0 otherwise
|
||||
//@message If found, the linked message; may be null
|
||||
//@media_timestamp Timestamp from which the video/audio/video note/voice note playing should start, in seconds; 0 if not specified. The media can be in the message content or in its link preview
|
||||
//@media_timestamp Timestamp from which the video/audio/video note/voice note playing should start, in seconds; 0 if not specified. The media can be in the message content or in its web page preview
|
||||
//@for_album True, if the whole media album to which the message belongs is linked
|
||||
//@for_comment True, if the message is linked as a channel post comment or from a message thread
|
||||
messageLinkInfo is_public:Bool chat_id:int53 message:message media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLinkInfo;
|
||||
@ -4027,10 +4032,10 @@ getChannelDifference channel_difference_id:int64 = Ok;
|
||||
getRemoteFile remote_file_id:string file_type:FileType = File;
|
||||
|
||||
//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (chat.position.order, chat.id) in descending order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1).
|
||||
//-For optimal performance the number of returned chats is chosen by the library
|
||||
//-For optimal performance, the number of returned chats is chosen by TDLib
|
||||
//@chat_list The chat list in which to return chats
|
||||
//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from
|
||||
//@limit The maximum number of chats to be returned. It is possible that fewer chats than the limit are returned even if the end of the list is not reached
|
||||
//@limit The maximum number of chats to be returned. For optimal performance, the number of returned chats is chosen by TDLib and can be smaller than the specified limit, even if the end of the list is not reached
|
||||
getChats chat_list:ChatList offset_order:int64 offset_chat_id:int53 limit:int32 = Chats;
|
||||
|
||||
//@description Searches a public chat by its username. Currently only private chats, supergroups and channels can be public. Returns the chat if found; otherwise an error is returned @username Username to be resolved
|
||||
@ -4084,21 +4089,21 @@ getGroupsInCommon user_id:int32 offset_chat_id:int53 limit:int32 = Chats;
|
||||
|
||||
|
||||
//@description Returns messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id).
|
||||
//-For optimal performance the number of returned messages is chosen by the library. This is an offline request if only_local is true
|
||||
//-For optimal performance, the number of returned messages is chosen by TDLib. This is an offline request if only_local is true
|
||||
//@chat_id Chat identifier
|
||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
||||
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset up to 99 to get additionally some newer messages
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@only_local If true, returns only messages that are available locally without sending network requests
|
||||
getChatHistory chat_id:int53 from_message_id:int53 offset:int32 limit:int32 only_local:Bool = Messages;
|
||||
|
||||
//@description Returns messages in a message thread of a message. Can be used only if message.can_get_message_thread == true. Message thread of a channel message is in the channel's linked supergroup.
|
||||
//-The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). For optimal performance the number of returned messages is chosen by the library
|
||||
//-The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
|
||||
//@chat_id Chat identifier
|
||||
//@message_id Message identifier, which thread history needs to be returned
|
||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
||||
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset up to 99 to get additionally some newer messages
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. Fewer messages may be returned than specified by the limit, even if the end of the message thread history has not been reached
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 offset:int32 limit:int32 = Messages;
|
||||
|
||||
//@description Deletes all messages in the chat. Use Chat.can_be_deleted_only_for_self and Chat.can_be_deleted_for_all_users fields to find whether and how the method can be applied to the chat
|
||||
@ -4109,41 +4114,41 @@ deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
|
||||
deleteChat chat_id:int53 = Ok;
|
||||
|
||||
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
|
||||
//-(searchSecretMessages should be used instead), or without an enabled message database. For optimal performance the number of returned messages is chosen by the library
|
||||
//-(searchSecretMessages should be used instead), or without an enabled message database. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@chat_id Identifier of the chat in which to search messages
|
||||
//@query Query to search for
|
||||
//@sender If not null, only messages sent by the specified sender will be returned. Not supported in secret chats
|
||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
||||
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset to get the specified message and some newer messages
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than -offset. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than -offset. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@filter Filter for message content in the search results
|
||||
//@message_thread_id If not 0, only messages in the specified thread will be returned; supergroups only
|
||||
searchChatMessages chat_id:int53 query:string sender:MessageSender from_message_id:int53 offset:int32 limit:int32 filter:SearchMessagesFilter message_thread_id:int53 = Messages;
|
||||
|
||||
//@description Searches for messages in all chats except secret chats. Returns the results in reverse chronological order (i.e., in order of decreasing (date, chat_id, message_id)).
|
||||
//-For optimal performance the number of returned messages is chosen by the library
|
||||
//-For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@chat_list Chat list in which to search messages; pass null to search in all chats regardless of their chat list. Only Main and Archive chat lists are supported
|
||||
//@query Query to search for
|
||||
//@offset_date The date of the message starting from which the results should be fetched. Use 0 or any date in the future to get results from the last message
|
||||
//@offset_chat_id The chat identifier of the last found message, or 0 for the first request
|
||||
//@offset_message_id The message identifier of the last found message, or 0 for the first request
|
||||
//@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
||||
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@filter Filter for message content in the search results; searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention, searchMessagesFilterUnreadMention, searchMessagesFilterFailedToSend and searchMessagesFilterPinned are unsupported in this function
|
||||
//@min_date If not 0, the minimum date of the messages to return
|
||||
//@max_date If not 0, the maximum date of the messages to return
|
||||
searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:int53 offset_message_id:int53 limit:int32 filter:SearchMessagesFilter min_date:int32 max_date:int32 = Messages;
|
||||
|
||||
//@description Searches for messages in secret chats. Returns the results in reverse chronological order. For optimal performance the number of returned messages is chosen by the library
|
||||
//@description Searches for messages in secret chats. Returns the results in reverse chronological order. For optimal performance, the number of returned messages is chosen by TDLib
|
||||
//@chat_id Identifier of the chat in which to search. Specify 0 to search in all secret chats
|
||||
//@query Query to search for. If empty, searchChatMessages should be used instead
|
||||
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
||||
//@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
||||
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@filter A filter for message content in the search results
|
||||
searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter:SearchMessagesFilter = FoundMessages;
|
||||
|
||||
//@description Searches for call messages. Returns the results in reverse chronological order (i. e., in order of decreasing message_id). For optimal performance the number of returned messages is chosen by the library
|
||||
//@description Searches for call messages. Returns the results in reverse chronological order (i. e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
|
||||
//@from_message_id Identifier of the message from which to search; use 0 to get results from the last message
|
||||
//@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached @only_missed If true, returns only messages with missed calls
|
||||
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit @only_missed If true, returns only messages with missed calls
|
||||
searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages;
|
||||
|
||||
//@description Deletes all call messages @revoke Pass true to delete the messages for all users
|
||||
@ -4164,11 +4169,11 @@ getChatMessageCount chat_id:int53 filter:SearchMessagesFilter return_local:Bool
|
||||
//@description Returns all scheduled messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id) @chat_id Chat identifier
|
||||
getChatScheduledMessages chat_id:int53 = Messages;
|
||||
|
||||
//@description Returns forwarded copies of a channel message to different public channels. For optimal performance the number of returned messages is chosen by the library
|
||||
//@description Returns forwarded copies of a channel message to different public channels. For optimal performance, the number of returned messages is chosen by TDLib
|
||||
//@chat_id Chat identifier of the message
|
||||
//@message_id Message identifier
|
||||
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. Fewer messages may be returned than specified by the limit, even if the end of the list has not been reached
|
||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
getMessagePublicForwards chat_id:int53 message_id:int53 offset:string limit:int32 = FoundMessages;
|
||||
|
||||
|
||||
@ -4179,10 +4184,10 @@ removeNotification notification_group_id:int32 notification_id:int32 = Ok;
|
||||
removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = Ok;
|
||||
|
||||
|
||||
//@description Returns an HTTPS link to a message in a chat. Available only for already sent messages in supergroups and channels. This is an offline request
|
||||
//@description Returns an HTTPS link to a message in a chat. Available only for already sent messages in supergroups and channels, or if message.can_get_media_timestamp_links and a media timestamp link is generated. This is an offline request
|
||||
//@chat_id Identifier of the chat to which the message belongs
|
||||
//@message_id Identifier of the message
|
||||
//@media_timestamp If not 0, timestamp from which the video/audio/video note/voice note playing should start, in seconds. The media can be in the message content or in its link preview
|
||||
//@media_timestamp If not 0, timestamp from which the video/audio/video note/voice note playing should start, in seconds. The media can be in the message content or in its web page preview
|
||||
//@for_album Pass true to create a link for the whole media album
|
||||
//@for_comment Pass true to create a link to the message as a channel post comment, or from a message thread
|
||||
getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLink;
|
||||
@ -4193,7 +4198,7 @@ getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bo
|
||||
//@for_album Pass true to return an HTML code for embedding of the whole media album
|
||||
getMessageEmbeddingCode chat_id:int53 message_id:int53 for_album:Bool = Text;
|
||||
|
||||
//@description Returns information about a public or private message link @url The message link
|
||||
//@description Returns information about a public or private message link. Can be called for any internal link of the type internalLinkTypeMessage @url The message link
|
||||
getMessageLinkInfo url:string = MessageLinkInfo;
|
||||
|
||||
|
||||
@ -4341,11 +4346,11 @@ getJsonString json_value:JsonValue = Text;
|
||||
//@option_ids 0-based identifiers of answer options, chosen by the user. User can choose more than 1 answer option only is the poll allows multiple answers
|
||||
setPollAnswer chat_id:int53 message_id:int53 option_ids:vector<int32> = Ok;
|
||||
|
||||
//@description Returns users voted for the specified option in a non-anonymous polls. For the optimal performance the number of returned users is chosen by the library
|
||||
//@description Returns users voted for the specified option in a non-anonymous polls. For optimal performance, the number of returned users is chosen by TDLib
|
||||
//@chat_id Identifier of the chat to which the poll belongs @message_id Identifier of the message containing the poll
|
||||
//@option_id 0-based identifier of the answer option
|
||||
//@offset Number of users to skip in the result; must be non-negative
|
||||
//@limit The maximum number of users to be returned; must be positive and can't be greater than 50. Fewer users may be returned than specified by the limit, even if the end of the voter list has not been reached
|
||||
//@limit The maximum number of users to be returned; must be positive and can't be greater than 50. For optimal performance, the number of returned users is chosen by TDLib and can be smaller than the specified limit, even if the end of the voter list has not been reached
|
||||
getPollVoters chat_id:int53 message_id:int53 option_id:int32 offset:int32 limit:int32 = Users;
|
||||
|
||||
//@description Stops a poll. A poll in a message can be stopped when the message has can_be_edited flag set
|
||||
@ -4435,7 +4440,7 @@ openMessageContent chat_id:int53 message_id:int53 = Ok;
|
||||
//@description Returns information about the type of an internal link. Returns a 404 error if the link is not internal. Can be called before authorization @link The link
|
||||
getInternalLinkType link:string = InternalLinkType;
|
||||
|
||||
//@description Returns information about an action to be done when the current user clicks an external link. Don't use this method for links from secret chats if link preview is disabled in secret chats @link The link
|
||||
//@description Returns information about an action to be done when the current user clicks an external link. Don't use this method for links from secret chats if web page preview is disabled in secret chats @link The link
|
||||
getExternalLinkInfo link:string = LoginUrlInfo;
|
||||
|
||||
//@description Returns an HTTP URL which can be used to automatically authorize the current user on a website after clicking an HTTP link. Use the method getExternalLinkInfo to find whether a prior user confirmation is needed
|
||||
@ -4945,9 +4950,9 @@ getInstalledStickerSets is_masks:Bool = StickerSets;
|
||||
//@description Returns a list of archived sticker sets @is_masks Pass true to return mask stickers sets; pass false to return ordinary sticker sets @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return
|
||||
getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = StickerSets;
|
||||
|
||||
//@description Returns a list of trending sticker sets. For the optimal performance the number of returned sticker sets is chosen by the library
|
||||
//@description Returns a list of trending sticker sets. For optimal performance, the number of returned sticker sets is chosen by TDLib
|
||||
//@offset The offset from which to return the sticker sets; must be non-negative
|
||||
//@limit The maximum number of sticker sets to be returned; must be non-negative. Fewer sticker sets may be returned than specified by the limit, even if the end of the list has not been reached
|
||||
//@limit The maximum number of sticker sets to be returned; must be non-negative. For optimal performance, the number of returned sticker sets is chosen by TDLib and can be smaller than the specified limit, even if the end of the list has not been reached
|
||||
getTrendingStickerSets offset:int32 limit:int32 = StickerSets;
|
||||
|
||||
//@description Returns a list of sticker sets attached to a file. Currently only photos and videos can have attached sticker sets @file_id File identifier
|
||||
|
@ -1485,7 +1485,8 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
|
||||
Slice key = key_value->key_;
|
||||
telegram_api::JSONValue *value = key_value->value_.get();
|
||||
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config") {
|
||||
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config" ||
|
||||
key == "stickers_emoji_cache_time") {
|
||||
continue;
|
||||
}
|
||||
if (key == "ignore_restriction_reasons") {
|
||||
|
@ -9540,7 +9540,9 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s
|
||||
|
||||
Dependencies dependencies;
|
||||
dependencies.channel_ids.insert(channel_id);
|
||||
add_dialog_and_dependencies(dependencies, DialogId(channel_full->linked_channel_id));
|
||||
// must not depend on the linked_dialog_id itself, because message database can be disabled
|
||||
// the Dialog will be forcely created in update_channel_full
|
||||
add_dialog_dependencies(dependencies, DialogId(channel_full->linked_channel_id));
|
||||
dependencies.chat_ids.insert(channel_full->migrated_from_chat_id);
|
||||
dependencies.user_ids.insert(channel_full->bot_user_ids.begin(), channel_full->bot_user_ids.end());
|
||||
dependencies.user_ids.insert(channel_full->invite_link.get_creator_user_id());
|
||||
|
@ -240,8 +240,9 @@ void CountryInfoManager::do_get_phone_number_info(string phone_number_prefix, st
|
||||
result += pattern[current_pattern_pos++];
|
||||
}
|
||||
if (current_pattern_pos == pattern.size()) {
|
||||
result += c;
|
||||
} else if (pattern[current_pattern_pos] == 'X') {
|
||||
result += ' ';
|
||||
}
|
||||
if (current_pattern_pos >= pattern.size() || pattern[current_pattern_pos] == 'X') {
|
||||
result += c;
|
||||
current_pattern_pos++;
|
||||
} else {
|
||||
|
@ -45,13 +45,13 @@ unique_ptr<DraftMessage> get_draft_message(ContactsManager *contacts_manager,
|
||||
}
|
||||
|
||||
auto entities = get_message_entities(contacts_manager, std::move(draft->entities_), "draftMessage");
|
||||
auto status = fix_formatted_text(draft->message_, entities, true, true, true, true);
|
||||
auto status = fix_formatted_text(draft->message_, entities, true, true, true, true, true);
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << "Receive error " << status << " while parsing draft " << draft->message_;
|
||||
if (!clean_input_string(draft->message_)) {
|
||||
draft->message_.clear();
|
||||
}
|
||||
entities = find_entities(draft->message_, false);
|
||||
entities = find_entities(draft->message_, false, true);
|
||||
}
|
||||
result->input_message_text.text = FormattedText{std::move(draft->message_), std::move(entities)};
|
||||
result->input_message_text.disable_web_page_preview = (flags & telegram_api::draftMessage::NO_WEBPAGE_MASK) != 0;
|
||||
|
@ -93,7 +93,7 @@ const FormattedText &Game::get_text() const {
|
||||
|
||||
tl_object_ptr<td_api::game> Game::get_game_object(Td *td, bool skip_bot_commands) const {
|
||||
return make_tl_object<td_api::game>(
|
||||
id_, short_name_, title_, get_formatted_text_object(text_, skip_bot_commands), description_,
|
||||
id_, short_name_, title_, get_formatted_text_object(text_, skip_bot_commands, -1), description_,
|
||||
get_photo_object(td->file_manager_.get(), photo_),
|
||||
td->animations_manager_->get_animation_object(animation_file_id_, "get_game_object"));
|
||||
}
|
||||
|
@ -39,22 +39,24 @@ Result<InputMessageText> process_input_message_text(const ContactsManager *conta
|
||||
}
|
||||
|
||||
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_)));
|
||||
auto need_skip_commands = need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot);
|
||||
auto need_skip_bot_commands = need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot);
|
||||
bool parse_markdown = G()->shared_config().get_option_boolean("always_parse_markdown");
|
||||
TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, parse_markdown,
|
||||
need_skip_commands, for_draft));
|
||||
need_skip_bot_commands, is_bot || for_draft || parse_markdown, for_draft));
|
||||
InputMessageText result{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)},
|
||||
input_message_text->disable_web_page_preview_, input_message_text->clear_draft_};
|
||||
if (G()->shared_config().get_option_boolean("always_parse_markdown")) {
|
||||
if (parse_markdown) {
|
||||
result.text = parse_markdown_v3(std::move(result.text));
|
||||
fix_formatted_text(result.text.text, result.text.entities, for_draft, false, need_skip_commands, for_draft)
|
||||
fix_formatted_text(result.text.text, result.text.entities, for_draft, false, need_skip_bot_commands,
|
||||
is_bot || for_draft, for_draft)
|
||||
.ensure();
|
||||
}
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
// used only for draft
|
||||
td_api::object_ptr<td_api::inputMessageText> get_input_message_text_object(const InputMessageText &input_message_text) {
|
||||
return td_api::make_object<td_api::inputMessageText>(get_formatted_text_object(input_message_text.text, false),
|
||||
return td_api::make_object<td_api::inputMessageText>(get_formatted_text_object(input_message_text.text, false, -1),
|
||||
input_message_text.disable_web_page_preview,
|
||||
input_message_text.clear_draft);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "td/telegram/MessageEntity.h"
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/MessagesManager.h"
|
||||
#include "td/telegram/misc.h"
|
||||
#include "td/telegram/ServerMessageId.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
@ -191,7 +192,7 @@ class LinkManager::InternalLinkMessageDraft final : public InternalLink {
|
||||
bool contains_link_ = false;
|
||||
|
||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||
return td_api::make_object<td_api::internalLinkTypeMessageDraft>(get_formatted_text_object(text_, true),
|
||||
return td_api::make_object<td_api::internalLinkTypeMessageDraft>(get_formatted_text_object(text_, true, -1),
|
||||
contains_link_);
|
||||
}
|
||||
|
||||
@ -329,6 +330,55 @@ class LinkManager::InternalLinkVoiceChat final : public InternalLink {
|
||||
}
|
||||
};
|
||||
|
||||
class GetDeepLinkInfoQuery final : public Td::ResultHandler {
|
||||
Promise<td_api::object_ptr<td_api::deepLinkInfo>> promise_;
|
||||
|
||||
public:
|
||||
explicit GetDeepLinkInfoQuery(Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise)
|
||||
: promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(Slice link) {
|
||||
send_query(G()->net_query_creator().create_unauth(telegram_api::help_getDeepLinkInfo(link.str())));
|
||||
}
|
||||
|
||||
void on_result(uint64 id, BufferSlice packet) final {
|
||||
auto result_ptr = fetch_result<telegram_api::help_getDeepLinkInfo>(packet);
|
||||
if (result_ptr.is_error()) {
|
||||
return on_error(id, result_ptr.move_as_error());
|
||||
}
|
||||
|
||||
auto result = result_ptr.move_as_ok();
|
||||
switch (result->get_id()) {
|
||||
case telegram_api::help_deepLinkInfoEmpty::ID:
|
||||
return promise_.set_value(nullptr);
|
||||
case telegram_api::help_deepLinkInfo::ID: {
|
||||
auto info = telegram_api::move_object_as<telegram_api::help_deepLinkInfo>(result);
|
||||
bool need_update = (info->flags_ & telegram_api::help_deepLinkInfo::UPDATE_APP_MASK) != 0;
|
||||
|
||||
auto entities = get_message_entities(nullptr, std::move(info->entities_), "GetDeepLinkInfoQuery");
|
||||
auto status = fix_formatted_text(info->message_, entities, true, true, true, true, true);
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << "Receive error " << status << " while parsing deep link info " << info->message_;
|
||||
if (!clean_input_string(info->message_)) {
|
||||
info->message_.clear();
|
||||
}
|
||||
entities = find_entities(info->message_, true, true);
|
||||
}
|
||||
FormattedText text{std::move(info->message_), std::move(entities)};
|
||||
return promise_.set_value(
|
||||
td_api::make_object<td_api::deepLinkInfo>(get_formatted_text_object(text, true, -1), need_update));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void on_error(uint64 id, Status status) final {
|
||||
promise_.set_error(std::move(status));
|
||||
}
|
||||
};
|
||||
|
||||
class RequestUrlAuthQuery final : public Td::ResultHandler {
|
||||
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> promise_;
|
||||
string url_;
|
||||
@ -955,7 +1005,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_message_dra
|
||||
} else {
|
||||
full_text.text = url.str();
|
||||
}
|
||||
if (fix_formatted_text(full_text.text, full_text.entities, false, false, false, true).is_error()) {
|
||||
if (fix_formatted_text(full_text.text, full_text.entities, false, false, false, true, true).is_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (full_text.text[0] == '@') {
|
||||
@ -1008,6 +1058,22 @@ void LinkManager::update_autologin_domains(string autologin_token, vector<string
|
||||
}
|
||||
}
|
||||
|
||||
void LinkManager::get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise) {
|
||||
Slice link_scheme("tg:");
|
||||
if (begins_with(link, link_scheme)) {
|
||||
link.remove_prefix(link_scheme.size());
|
||||
if (begins_with(link, "//")) {
|
||||
link.remove_prefix(2);
|
||||
}
|
||||
}
|
||||
size_t pos = 0;
|
||||
while (pos < link.size() && link[pos] != '/' && link[pos] != '?' && link[pos] != '#') {
|
||||
pos++;
|
||||
}
|
||||
link.truncate(pos);
|
||||
td_->create_handler<GetDeepLinkInfoQuery>(std::move(promise))->send(link);
|
||||
}
|
||||
|
||||
void LinkManager::get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise) {
|
||||
auto default_result = td_api::make_object<td_api::loginUrlInfoOpen>(link, false);
|
||||
if (G()->close_flag()) {
|
||||
|
@ -54,6 +54,8 @@ class LinkManager final : public Actor {
|
||||
void update_autologin_domains(string autologin_token, vector<string> autologin_domains,
|
||||
vector<string> url_auth_domains);
|
||||
|
||||
void get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise);
|
||||
|
||||
void get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise);
|
||||
|
||||
void get_login_url_info(FullMessageId full_message_id, int32 button_id,
|
||||
|
@ -1030,7 +1030,7 @@ static void parse_caption(FormattedText &caption, ParserT &parser) {
|
||||
if (!check_utf8(caption.text)) {
|
||||
caption.text.clear();
|
||||
}
|
||||
caption.entities = find_entities(caption.text, false);
|
||||
caption.entities = find_entities(caption.text, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1461,7 +1461,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
|
||||
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageText>(bot_inline_message);
|
||||
auto entities = get_message_entities(td->contacts_manager_.get(), std::move(inline_message->entities_),
|
||||
"botInlineMessageText");
|
||||
auto status = fix_formatted_text(inline_message->message_, entities, false, true, true, false);
|
||||
auto status = fix_formatted_text(inline_message->message_, entities, false, true, true, false, false);
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << "Receive error " << status << " while parsing botInlineMessageText " << inline_message->message_;
|
||||
break;
|
||||
@ -1525,7 +1525,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
|
||||
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageMediaAuto>(bot_inline_message);
|
||||
auto caption =
|
||||
get_message_text(td->contacts_manager_.get(), inline_message->message_, std::move(inline_message->entities_),
|
||||
true, 0, false, "create_inline_message_content");
|
||||
true, false, 0, false, "create_inline_message_content");
|
||||
if (allowed_media_content_id == td_api::inputMessageAnimation::ID) {
|
||||
result.message_content = make_unique<MessageAnimation>(file_id, std::move(caption));
|
||||
} else if (allowed_media_content_id == td_api::inputMessageAudio::ID) {
|
||||
@ -2911,12 +2911,14 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
||||
case MessageContentType::Text: {
|
||||
auto old_ = static_cast<const MessageText *>(old_content);
|
||||
auto new_ = static_cast<const MessageText *>(new_content);
|
||||
auto get_content_object = [td, dialog_id](const MessageContent *content) {
|
||||
return to_string(
|
||||
get_message_content_object(content, td, dialog_id, -1, false, false, std::numeric_limits<int32>::max()));
|
||||
};
|
||||
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, dialog_id, -1, false, false))
|
||||
<< ". New content is "
|
||||
<< to_string(get_message_content_object(new_content, td, dialog_id, -1, false, false));
|
||||
LOG(ERROR) << "Message text has changed in " << get_content_object(old_content) << ". New content is "
|
||||
<< get_content_object(new_content);
|
||||
}
|
||||
need_update = true;
|
||||
}
|
||||
@ -2925,10 +2927,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
||||
if (need_message_changed_warning && need_message_text_changed_warning(old_, new_) &&
|
||||
old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT &&
|
||||
need_message_entities_changed_warning(old_->text.entities, new_->text.entities)) {
|
||||
LOG(WARNING) << "Entities has changed from "
|
||||
<< to_string(get_message_content_object(old_content, td, dialog_id, -1, false, false))
|
||||
<< ". New content is "
|
||||
<< to_string(get_message_content_object(new_content, td, dialog_id, -1, false, false));
|
||||
LOG(WARNING) << "Entities has changed in " << get_content_object(old_content) << ". New content is "
|
||||
<< get_content_object(new_content);
|
||||
}
|
||||
need_update = true;
|
||||
}
|
||||
@ -3825,14 +3825,14 @@ unique_ptr<MessageContent> get_secret_message_content(
|
||||
}
|
||||
|
||||
auto entities = get_message_entities(std::move(secret_entities));
|
||||
auto status = fix_formatted_text(message_text, entities, true, false, true, false);
|
||||
auto status = fix_formatted_text(message_text, entities, true, false, true, td->auth_manager_->is_bot(), 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 = find_entities(message_text, true);
|
||||
entities = find_entities(message_text, true, td->auth_manager_->is_bot());
|
||||
}
|
||||
|
||||
// support of old layer and old constructions
|
||||
@ -4649,19 +4649,21 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
|
||||
|
||||
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
||||
DialogId dialog_id, int32 message_date,
|
||||
bool is_content_secret, bool skip_bot_commands) {
|
||||
bool is_content_secret, bool skip_bot_commands,
|
||||
int32 max_media_timestamp) {
|
||||
CHECK(content != nullptr);
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Animation: {
|
||||
const MessageAnimation *m = static_cast<const MessageAnimation *>(content);
|
||||
return make_tl_object<td_api::messageAnimation>(
|
||||
td->animations_manager_->get_animation_object(m->file_id, "get_message_content_object"),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands), is_content_secret);
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||
}
|
||||
case MessageContentType::Audio: {
|
||||
const MessageAudio *m = static_cast<const MessageAudio *>(content);
|
||||
return make_tl_object<td_api::messageAudio>(td->audios_manager_->get_audio_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands));
|
||||
return make_tl_object<td_api::messageAudio>(
|
||||
td->audios_manager_->get_audio_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp));
|
||||
}
|
||||
case MessageContentType::Contact: {
|
||||
const MessageContact *m = static_cast<const MessageContact *>(content);
|
||||
@ -4671,7 +4673,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
const MessageDocument *m = static_cast<const MessageDocument *>(content);
|
||||
return make_tl_object<td_api::messageDocument>(
|
||||
td->documents_manager_->get_document_object(m->file_id, PhotoFormat::Jpeg),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands));
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp));
|
||||
}
|
||||
case MessageContentType::Game: {
|
||||
const MessageGame *m = static_cast<const MessageGame *>(content);
|
||||
@ -4696,9 +4698,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
}
|
||||
case MessageContentType::Photo: {
|
||||
const MessagePhoto *m = static_cast<const MessagePhoto *>(content);
|
||||
return make_tl_object<td_api::messagePhoto>(get_photo_object(td->file_manager_.get(), m->photo),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
||||
is_content_secret);
|
||||
return make_tl_object<td_api::messagePhoto>(
|
||||
get_photo_object(td->file_manager_.get(), m->photo),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||
}
|
||||
case MessageContentType::Sticker: {
|
||||
const MessageSticker *m = static_cast<const MessageSticker *>(content);
|
||||
@ -4706,7 +4708,8 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
}
|
||||
case MessageContentType::Text: {
|
||||
const MessageText *m = static_cast<const MessageText *>(content);
|
||||
return make_tl_object<td_api::messageText>(get_formatted_text_object(m->text, skip_bot_commands),
|
||||
return make_tl_object<td_api::messageText>(
|
||||
get_formatted_text_object(m->text, skip_bot_commands, max_media_timestamp),
|
||||
td->web_pages_manager_->get_web_page_object(m->web_page_id));
|
||||
}
|
||||
case MessageContentType::Unsupported:
|
||||
@ -4717,9 +4720,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
}
|
||||
case MessageContentType::Video: {
|
||||
const MessageVideo *m = static_cast<const MessageVideo *>(content);
|
||||
return make_tl_object<td_api::messageVideo>(td->videos_manager_->get_video_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
||||
is_content_secret);
|
||||
return make_tl_object<td_api::messageVideo>(
|
||||
td->videos_manager_->get_video_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||
}
|
||||
case MessageContentType::VideoNote: {
|
||||
const MessageVideoNote *m = static_cast<const MessageVideoNote *>(content);
|
||||
@ -4728,9 +4731,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
}
|
||||
case MessageContentType::VoiceNote: {
|
||||
const MessageVoiceNote *m = static_cast<const MessageVoiceNote *>(content);
|
||||
return make_tl_object<td_api::messageVoiceNote>(td->voice_notes_manager_->get_voice_note_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
||||
m->is_listened);
|
||||
return make_tl_object<td_api::messageVoiceNote>(
|
||||
td->voice_notes_manager_->get_voice_note_object(m->file_id),
|
||||
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), m->is_listened);
|
||||
}
|
||||
case MessageContentType::ChatCreate: {
|
||||
const MessageChatCreate *m = static_cast<const MessageChatCreate *>(content);
|
||||
@ -4879,6 +4882,10 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FormattedText *get_message_content_text_mutable(MessageContent *content) {
|
||||
return const_cast<FormattedText *>(get_message_content_text(content));
|
||||
}
|
||||
|
||||
const FormattedText *get_message_content_text(const MessageContent *content) {
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Text:
|
||||
@ -4920,10 +4927,6 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td)
|
||||
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
|
||||
return td->audios_manager_->get_audio_duration(audio_file_id);
|
||||
}
|
||||
case MessageContentType::Text: {
|
||||
auto web_page_id = static_cast<const MessageText *>(content)->web_page_id;
|
||||
return td->web_pages_manager_->get_web_page_duration(web_page_id);
|
||||
}
|
||||
case MessageContentType::Video: {
|
||||
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
|
||||
return td->videos_manager_->get_video_duration(video_file_id);
|
||||
@ -4941,6 +4944,34 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td)
|
||||
}
|
||||
}
|
||||
|
||||
int32 get_message_content_media_duration(const MessageContent *content, const Td *td) {
|
||||
CHECK(content != nullptr);
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Audio: {
|
||||
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
|
||||
return td->audios_manager_->get_audio_duration(audio_file_id);
|
||||
}
|
||||
case MessageContentType::Text: {
|
||||
auto web_page_id = static_cast<const MessageText *>(content)->web_page_id;
|
||||
return td->web_pages_manager_->get_web_page_media_duration(web_page_id);
|
||||
}
|
||||
case MessageContentType::Video: {
|
||||
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
|
||||
return td->videos_manager_->get_video_duration(video_file_id);
|
||||
}
|
||||
case MessageContentType::VideoNote: {
|
||||
auto video_note_file_id = static_cast<const MessageVideoNote *>(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<const MessageVoiceNote *>(content)->file_id;
|
||||
return td->voice_notes_manager_->get_voice_note_duration(voice_file_id);
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
FileId get_message_content_upload_file_id(const MessageContent *content) {
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Animation:
|
||||
|
@ -198,7 +198,10 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
|
||||
|
||||
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
||||
DialogId dialog_id, int32 message_date,
|
||||
bool is_content_secret, bool skip_bot_commands);
|
||||
bool is_content_secret, bool skip_bot_commands,
|
||||
int32 max_media_timestamp);
|
||||
|
||||
FormattedText *get_message_content_text_mutable(MessageContent *content);
|
||||
|
||||
const FormattedText *get_message_content_text(const MessageContent *content);
|
||||
|
||||
@ -206,6 +209,8 @@ const FormattedText *get_message_content_caption(const MessageContent *content);
|
||||
|
||||
int32 get_message_content_duration(const MessageContent *content, const Td *td);
|
||||
|
||||
int32 get_message_content_media_duration(const MessageContent *content, const Td *td);
|
||||
|
||||
FileId get_message_content_upload_file_id(const MessageContent *content);
|
||||
|
||||
FileId get_message_content_any_file_id(const MessageContent *content);
|
||||
|
@ -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};
|
||||
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50, 50};
|
||||
static_assert(sizeof(types) / sizeof(types[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
|
||||
return types[static_cast<int32>(type)];
|
||||
}
|
||||
@ -72,6 +72,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
|
||||
return string_builder << "PhoneNumber";
|
||||
case MessageEntity::Type::BankCardNumber:
|
||||
return string_builder << "BankCardNumber";
|
||||
case MessageEntity::Type::MediaTimestamp:
|
||||
return string_builder << "MediaTimestamp";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return string_builder << "Impossible";
|
||||
@ -81,6 +83,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity) {
|
||||
string_builder << '[' << message_entity.type << ", offset = " << message_entity.offset
|
||||
<< ", length = " << message_entity.length;
|
||||
if (message_entity.media_timestamp >= 0) {
|
||||
string_builder << ", media_timestamp = \"" << message_entity.media_timestamp << "\"";
|
||||
}
|
||||
if (!message_entity.argument.empty()) {
|
||||
string_builder << ", argument = \"" << message_entity.argument << "\"";
|
||||
}
|
||||
@ -130,6 +135,8 @@ tl_object_ptr<td_api::TextEntityType> MessageEntity::get_text_entity_type_object
|
||||
return make_tl_object<td_api::textEntityTypePhoneNumber>();
|
||||
case MessageEntity::Type::BankCardNumber:
|
||||
return make_tl_object<td_api::textEntityTypeBankCardNumber>();
|
||||
case MessageEntity::Type::MediaTimestamp:
|
||||
return make_tl_object<td_api::textEntityTypeMediaTimestamp>(media_timestamp);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@ -141,7 +148,7 @@ tl_object_ptr<td_api::textEntity> MessageEntity::get_text_entity_object() const
|
||||
}
|
||||
|
||||
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
||||
bool skip_bot_commands) {
|
||||
bool skip_bot_commands, int32 max_media_timestamp) {
|
||||
vector<tl_object_ptr<td_api::textEntity>> result;
|
||||
result.reserve(entities.size());
|
||||
|
||||
@ -149,6 +156,9 @@ vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<
|
||||
if (skip_bot_commands && entity.type == MessageEntity::Type::BotCommand) {
|
||||
continue;
|
||||
}
|
||||
if (entity.type == MessageEntity::Type::MediaTimestamp && max_media_timestamp < entity.media_timestamp) {
|
||||
continue;
|
||||
}
|
||||
auto entity_object = entity.get_text_entity_object();
|
||||
if (entity_object->type_ != nullptr) {
|
||||
result.push_back(std::move(entity_object));
|
||||
@ -162,9 +172,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, const FormattedText &te
|
||||
return string_builder << '"' << text.text << "\" with entities " << text.entities;
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands) {
|
||||
return td_api::make_object<td_api::formattedText>(text.text,
|
||||
get_text_entities_object(text.entities, skip_bot_commands));
|
||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
|
||||
int32 max_media_timestamp) {
|
||||
return td_api::make_object<td_api::formattedText>(
|
||||
text.text, get_text_entities_object(text.entities, skip_bot_commands, max_media_timestamp));
|
||||
}
|
||||
|
||||
static bool is_word_character(uint32 code) {
|
||||
@ -430,6 +441,57 @@ static vector<Slice> match_cashtags(Slice str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static vector<Slice> match_media_timestamps(Slice str) {
|
||||
vector<Slice> result;
|
||||
const unsigned char *begin = str.ubegin();
|
||||
const unsigned char *end = str.uend();
|
||||
const unsigned char *ptr = begin;
|
||||
|
||||
while (true) {
|
||||
ptr = static_cast<const unsigned char *>(std::memchr(ptr, ':', narrow_cast<int32>(end - ptr)));
|
||||
if (ptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto media_timestamp_begin = ptr;
|
||||
while (media_timestamp_begin != begin &&
|
||||
(media_timestamp_begin[-1] == ':' || is_digit(media_timestamp_begin[-1]))) {
|
||||
media_timestamp_begin--;
|
||||
}
|
||||
auto media_timestamp_end = ptr;
|
||||
while (media_timestamp_end + 1 != end && (media_timestamp_end[1] == ':' || is_digit(media_timestamp_end[1]))) {
|
||||
media_timestamp_end++;
|
||||
}
|
||||
media_timestamp_end++;
|
||||
|
||||
if (media_timestamp_begin != ptr && media_timestamp_end != ptr + 1 && is_digit(ptr[1])) {
|
||||
ptr = media_timestamp_end;
|
||||
|
||||
if (media_timestamp_begin != begin) {
|
||||
uint32 prev;
|
||||
next_utf8_unsafe(prev_utf8_unsafe(media_timestamp_begin), &prev, "match_media_timestamps 1");
|
||||
|
||||
if (is_word_character(prev)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (media_timestamp_end != end) {
|
||||
uint32 next;
|
||||
next_utf8_unsafe(media_timestamp_end, &next, "match_media_timestamps 2");
|
||||
|
||||
if (is_word_character(next)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(media_timestamp_begin, media_timestamp_end);
|
||||
} else {
|
||||
ptr = media_timestamp_end;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static vector<Slice> match_bank_card_numbers(Slice str) {
|
||||
vector<Slice> result;
|
||||
const unsigned char *begin = str.ubegin();
|
||||
@ -1247,6 +1309,42 @@ vector<std::pair<Slice, bool>> find_urls(Slice str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<std::pair<Slice, int32>> find_media_timestamps(Slice str) {
|
||||
vector<std::pair<Slice, int32>> result;
|
||||
for (auto media_timestamp : match_media_timestamps(str)) {
|
||||
vector<Slice> parts = full_split(media_timestamp, ':');
|
||||
CHECK(parts.size() >= 2);
|
||||
if (parts.size() > 3 || parts.back().size() != 2) {
|
||||
continue;
|
||||
}
|
||||
auto seconds = to_integer<int32>(parts.back());
|
||||
if (seconds >= 60) {
|
||||
continue;
|
||||
}
|
||||
if (parts.size() == 2) {
|
||||
if (parts[0].size() > 4 || parts[0].empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto minutes = to_integer<int32>(parts[0]);
|
||||
result.emplace_back(media_timestamp, minutes * 60 + seconds);
|
||||
continue;
|
||||
} else {
|
||||
if (parts[0].size() > 2 || parts[1].size() > 2 || parts[0].empty() || parts[1].empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto minutes = to_integer<int32>(parts[1]);
|
||||
if (minutes >= 60) {
|
||||
continue;
|
||||
}
|
||||
auto hours = to_integer<int32>(parts[0]);
|
||||
result.emplace_back(media_timestamp, hours * 3600 + minutes * 60 + seconds);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int32 text_length(Slice text) {
|
||||
return narrow_cast<int32>(utf8_utf16_length(text));
|
||||
}
|
||||
@ -1291,7 +1389,8 @@ static constexpr int32 get_continuous_entities_mask() {
|
||||
get_entity_type_mask(MessageEntity::Type::EmailAddress) | get_entity_type_mask(MessageEntity::Type::TextUrl) |
|
||||
get_entity_type_mask(MessageEntity::Type::MentionName) | get_entity_type_mask(MessageEntity::Type::Cashtag) |
|
||||
get_entity_type_mask(MessageEntity::Type::PhoneNumber) |
|
||||
get_entity_type_mask(MessageEntity::Type::BankCardNumber);
|
||||
get_entity_type_mask(MessageEntity::Type::BankCardNumber) |
|
||||
get_entity_type_mask(MessageEntity::Type::MediaTimestamp);
|
||||
}
|
||||
|
||||
static constexpr int32 get_pre_entities_mask() {
|
||||
@ -1440,7 +1539,7 @@ static void remove_entities_intersecting_blockquote(vector<MessageEntity> &entit
|
||||
while (blockquote_it != blockquote_entities.end() &&
|
||||
(blockquote_it->type != MessageEntity::Type::BlockQuote ||
|
||||
blockquote_it->offset + blockquote_it->length <= entities[i].offset)) {
|
||||
blockquote_it++;
|
||||
++blockquote_it;
|
||||
}
|
||||
if (blockquote_it != blockquote_entities.end() &&
|
||||
(blockquote_it->offset + blockquote_it->length < entities[i].offset + entities[i].length ||
|
||||
@ -1456,7 +1555,52 @@ static void remove_entities_intersecting_blockquote(vector<MessageEntity> &entit
|
||||
entities.erase(entities.begin() + left_entities, entities.end());
|
||||
}
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands) {
|
||||
// keeps only non-intersecting entities
|
||||
// fixes entity offsets from UTF-8 to UTF-16 offsets
|
||||
static void fix_entity_offsets(Slice text, vector<MessageEntity> &entities) {
|
||||
if (entities.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sort_entities(entities);
|
||||
|
||||
remove_intersecting_entities(entities);
|
||||
|
||||
const unsigned char *begin = text.ubegin();
|
||||
const unsigned char *ptr = begin;
|
||||
const unsigned char *end = text.uend();
|
||||
|
||||
int32 utf16_pos = 0;
|
||||
for (auto &entity : entities) {
|
||||
int cnt = 2;
|
||||
auto entity_begin = entity.offset;
|
||||
auto entity_end = entity.offset + entity.length;
|
||||
|
||||
int32 pos = static_cast<int32>(ptr - begin);
|
||||
if (entity_begin == pos) {
|
||||
cnt--;
|
||||
entity.offset = utf16_pos;
|
||||
}
|
||||
|
||||
while (ptr != end && cnt > 0) {
|
||||
unsigned char c = ptr[0];
|
||||
utf16_pos += 1 + (c >= 0xf0);
|
||||
ptr = next_utf8_unsafe(ptr, nullptr, "fix_entity_offsets");
|
||||
|
||||
pos = static_cast<int32>(ptr - begin);
|
||||
if (entity_begin == pos) {
|
||||
cnt--;
|
||||
entity.offset = utf16_pos;
|
||||
} else if (entity_end == pos) {
|
||||
cnt--;
|
||||
entity.length = utf16_pos - entity.offset;
|
||||
}
|
||||
}
|
||||
CHECK(cnt == 0);
|
||||
}
|
||||
}
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) {
|
||||
vector<MessageEntity> entities;
|
||||
|
||||
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
|
||||
@ -1485,47 +1629,31 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands) {
|
||||
entities.emplace_back(type, offset, length);
|
||||
}
|
||||
|
||||
if (entities.empty()) {
|
||||
if (!skip_media_timestamps) {
|
||||
auto media_timestamps = find_media_timestamps(text);
|
||||
for (auto &entity : media_timestamps) {
|
||||
auto offset = narrow_cast<int32>(entity.first.begin() - text.begin());
|
||||
auto length = narrow_cast<int32>(entity.first.size());
|
||||
entities.emplace_back(MessageEntity::Type::MediaTimestamp, offset, length, entity.second);
|
||||
}
|
||||
}
|
||||
|
||||
fix_entity_offsets(text, entities);
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
sort_entities(entities);
|
||||
static vector<MessageEntity> find_media_timestamp_entities(Slice text) {
|
||||
vector<MessageEntity> entities;
|
||||
|
||||
remove_intersecting_entities(entities);
|
||||
|
||||
// fix offsets to UTF-16 offsets
|
||||
const unsigned char *begin = text.ubegin();
|
||||
const unsigned char *ptr = begin;
|
||||
const unsigned char *end = text.uend();
|
||||
|
||||
int32 utf16_pos = 0;
|
||||
for (auto &entity : entities) {
|
||||
int cnt = 2;
|
||||
auto entity_begin = entity.offset;
|
||||
auto entity_end = entity.offset + entity.length;
|
||||
|
||||
int32 pos = static_cast<int32>(ptr - begin);
|
||||
if (entity_begin == pos) {
|
||||
cnt--;
|
||||
entity.offset = utf16_pos;
|
||||
auto media_timestamps = find_media_timestamps(text);
|
||||
for (auto &entity : media_timestamps) {
|
||||
auto offset = narrow_cast<int32>(entity.first.begin() - text.begin());
|
||||
auto length = narrow_cast<int32>(entity.first.size());
|
||||
entities.emplace_back(MessageEntity::Type::MediaTimestamp, offset, length, entity.second);
|
||||
}
|
||||
|
||||
while (ptr != end && cnt > 0) {
|
||||
unsigned char c = ptr[0];
|
||||
utf16_pos += 1 + (c >= 0xf0);
|
||||
ptr = next_utf8_unsafe(ptr, nullptr, "find_entities");
|
||||
|
||||
pos = static_cast<int32>(ptr - begin);
|
||||
if (entity_begin == pos) {
|
||||
cnt--;
|
||||
entity.offset = utf16_pos;
|
||||
} else if (entity_end == pos) {
|
||||
cnt--;
|
||||
entity.length = utf16_pos - entity.offset;
|
||||
}
|
||||
}
|
||||
CHECK(cnt == 0);
|
||||
}
|
||||
fix_entity_offsets(text, entities);
|
||||
|
||||
return entities;
|
||||
}
|
||||
@ -1546,17 +1674,17 @@ static vector<MessageEntity> merge_entities(vector<MessageEntity> old_entities,
|
||||
for (auto &old_entity : old_entities) {
|
||||
while (new_it != new_end && new_it->offset + new_it->length <= old_entity.offset) {
|
||||
result.push_back(std::move(*new_it));
|
||||
new_it++;
|
||||
++new_it;
|
||||
}
|
||||
auto old_entity_end = old_entity.offset + old_entity.length;
|
||||
result.push_back(std::move(old_entity));
|
||||
while (new_it != new_end && new_it->offset < old_entity_end) {
|
||||
new_it++;
|
||||
++new_it;
|
||||
}
|
||||
}
|
||||
while (new_it != new_end) {
|
||||
result.push_back(std::move(*new_it));
|
||||
new_it++;
|
||||
++new_it;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1616,6 +1744,8 @@ string get_first_url(Slice text, const vector<MessageEntity> &entities) {
|
||||
break;
|
||||
case MessageEntity::Type::BankCardNumber:
|
||||
break;
|
||||
case MessageEntity::Type::MediaTimestamp:
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -2239,7 +2369,7 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
|
||||
}
|
||||
}
|
||||
|
||||
auto found_entities = find_entities(text, false);
|
||||
auto found_entities = find_entities(text, false, true);
|
||||
td::remove_if(found_entities, [](const auto &entity) {
|
||||
return entity.type == MessageEntity::Type::EmailAddress || entity.type == MessageEntity::Type::Url;
|
||||
});
|
||||
@ -3030,6 +3160,8 @@ vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entiti
|
||||
break;
|
||||
case MessageEntity::Type::MentionName:
|
||||
break;
|
||||
case MessageEntity::Type::MediaTimestamp:
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -3121,6 +3253,15 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
|
||||
entities.emplace_back(entity->offset_, entity->length_, user_id);
|
||||
break;
|
||||
}
|
||||
case td_api::textEntityTypeMediaTimestamp::ID: {
|
||||
auto entity_media_timestamp = static_cast<td_api::textEntityTypeMediaTimestamp *>(entity->type_.get());
|
||||
if (entity_media_timestamp->media_timestamp_ < 0) {
|
||||
return Status::Error(400, "Invalid media timestamp specified");
|
||||
}
|
||||
entities.emplace_back(MessageEntity::Type::MediaTimestamp, entity->offset_, entity->length_,
|
||||
entity_media_timestamp->media_timestamp_);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -3759,7 +3900,7 @@ static void merge_new_entities(vector<MessageEntity> &entities, vector<MessageEn
|
||||
}
|
||||
|
||||
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
||||
bool skip_bot_commands, bool for_draft) {
|
||||
bool skip_bot_commands, bool skip_media_timestamps, bool for_draft) {
|
||||
string result;
|
||||
if (entities.empty()) {
|
||||
// fast path
|
||||
@ -3867,7 +4008,9 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
|
||||
}
|
||||
|
||||
if (!skip_new_entities) {
|
||||
merge_new_entities(entities, find_entities(text, skip_bot_commands));
|
||||
merge_new_entities(entities, find_entities(text, skip_bot_commands, skip_media_timestamps));
|
||||
} else if (!skip_media_timestamps) {
|
||||
merge_new_entities(entities, find_media_timestamp_entities(text));
|
||||
}
|
||||
|
||||
// new whitespace-only entities could be added after splitting of entities
|
||||
@ -3880,11 +4023,12 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
|
||||
|
||||
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
||||
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
||||
bool skip_new_entities, int32 send_date, bool from_album, const char *source) {
|
||||
bool skip_new_entities, bool skip_media_timestamps, int32 send_date, bool from_album,
|
||||
const char *source) {
|
||||
auto entities = get_message_entities(contacts_manager, std::move(server_entities), source);
|
||||
auto debug_message_text = message_text;
|
||||
auto debug_entities = entities;
|
||||
auto status = fix_formatted_text(message_text, entities, true, skip_new_entities, true, false);
|
||||
auto status = fix_formatted_text(message_text, entities, true, skip_new_entities, true, skip_media_timestamps, false);
|
||||
if (status.is_error()) {
|
||||
// message entities in media albums can be wrong because of a long time ago fixed server-side bug
|
||||
if (!from_album && (send_date == 0 || send_date > 1600340000)) { // approximate fix date
|
||||
@ -3895,7 +4039,7 @@ FormattedText get_message_text(const ContactsManager *contacts_manager, string m
|
||||
if (!clean_input_string(message_text)) {
|
||||
message_text.clear();
|
||||
}
|
||||
entities = find_entities(message_text, false);
|
||||
entities = find_entities(message_text, false, skip_media_timestamps);
|
||||
}
|
||||
return FormattedText{std::move(message_text), std::move(entities)};
|
||||
}
|
||||
@ -3939,7 +4083,7 @@ Result<FormattedText> process_input_caption(const ContactsManager *contacts_mana
|
||||
}
|
||||
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(caption->entities_)));
|
||||
TRY_STATUS(fix_formatted_text(caption->text_, entities, true, false,
|
||||
need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot), false));
|
||||
need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot), is_bot, false));
|
||||
return FormattedText{std::move(caption->text_), std::move(entities)};
|
||||
}
|
||||
|
||||
@ -3954,6 +4098,19 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted
|
||||
}
|
||||
}
|
||||
|
||||
bool has_media_timestamps(const FormattedText *text, int32 min_media_timestamp, int32 max_media_timestamp) {
|
||||
if (text == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (auto &entity : text->entities) {
|
||||
if (entity.type == MessageEntity::Type::MediaTimestamp && min_media_timestamp <= entity.media_timestamp &&
|
||||
entity.media_timestamp <= max_media_timestamp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_bot_commands(const FormattedText *text) {
|
||||
if (text == nullptr) {
|
||||
return false;
|
||||
|
@ -48,28 +48,34 @@ class MessageEntity {
|
||||
Strikethrough,
|
||||
BlockQuote,
|
||||
BankCardNumber,
|
||||
MediaTimestamp,
|
||||
Size
|
||||
};
|
||||
Type type;
|
||||
int32 offset;
|
||||
int32 length;
|
||||
Type type = Type::Size;
|
||||
int32 offset = -1;
|
||||
int32 length = -1;
|
||||
int32 media_timestamp = -1;
|
||||
string argument;
|
||||
UserId user_id;
|
||||
|
||||
MessageEntity() = default;
|
||||
|
||||
MessageEntity(Type type, int32 offset, int32 length, string argument = "")
|
||||
: type(type), offset(offset), length(length), argument(std::move(argument)), user_id() {
|
||||
: 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), argument(), user_id(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);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::textEntity> get_text_entity_object() const;
|
||||
|
||||
bool operator==(const MessageEntity &other) const {
|
||||
return offset == other.offset && length == other.length && type == other.type && argument == other.argument &&
|
||||
user_id == other.user_id;
|
||||
return offset == other.offset && length == other.length && type == other.type &&
|
||||
media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id;
|
||||
}
|
||||
|
||||
bool operator<(const MessageEntity &other) const {
|
||||
@ -132,11 +138,12 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
|
||||
bool allow_all = false);
|
||||
|
||||
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
||||
bool skip_bot_commands);
|
||||
bool skip_bot_commands, int32 max_media_timestamp);
|
||||
|
||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands);
|
||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
|
||||
int32 max_media_timestamp);
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands);
|
||||
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);
|
||||
@ -146,6 +153,7 @@ 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
|
||||
|
||||
string get_first_url(Slice text, const vector<MessageEntity> &entities);
|
||||
|
||||
@ -178,11 +186,12 @@ vector<MessageEntity> get_message_entities(vector<tl_object_ptr<secret_api::Mess
|
||||
|
||||
// like clean_input_string but also validates entities
|
||||
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
||||
bool skip_bot_commands, bool for_draft) TD_WARN_UNUSED_RESULT;
|
||||
bool skip_bot_commands, bool skip_media_timestamps, bool for_draft) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
||||
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
||||
bool skip_new_entities, int32 send_date, bool from_album, const char *source);
|
||||
bool skip_new_entities, bool skip_media_timestamps, int32 send_date, bool from_album,
|
||||
const char *source);
|
||||
|
||||
td_api::object_ptr<td_api::formattedText> extract_input_caption(
|
||||
tl_object_ptr<td_api::InputMessageContent> &input_message_content);
|
||||
@ -192,6 +201,8 @@ Result<FormattedText> process_input_caption(const ContactsManager *contacts_mana
|
||||
|
||||
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 ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot);
|
||||
|
@ -24,6 +24,9 @@ void MessageEntity::store(StorerT &storer) const {
|
||||
if (type == Type::MentionName) {
|
||||
store(user_id, storer);
|
||||
}
|
||||
if (type == Type::MediaTimestamp) {
|
||||
store(media_timestamp, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
@ -38,6 +41,9 @@ void MessageEntity::parse(ParserT &parser) {
|
||||
if (type == Type::MentionName) {
|
||||
parse(user_id, parser);
|
||||
}
|
||||
if (type == Type::MediaTimestamp) {
|
||||
parse(media_timestamp, parser);
|
||||
}
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
|
21
td/telegram/MessageThreadInfo.h
Normal file
21
td/telegram/MessageThreadInfo.h
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
||||
//
|
||||
// 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/MessageId.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
struct MessageThreadInfo {
|
||||
DialogId dialog_id;
|
||||
vector<MessageId> message_ids;
|
||||
};
|
||||
|
||||
} // namespace td
|
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,7 @@
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/MessageLinkInfo.h"
|
||||
#include "td/telegram/MessageReplyInfo.h"
|
||||
#include "td/telegram/MessageThreadInfo.h"
|
||||
#include "td/telegram/MessagesDb.h"
|
||||
#include "td/telegram/MessageSearchFilter.h"
|
||||
#include "td/telegram/MessageTtlSetting.h"
|
||||
@ -573,17 +574,13 @@ class MessagesManager final : public Actor {
|
||||
void get_messages_from_server(vector<FullMessageId> &&message_ids, Promise<Unit> &&promise, const char *source,
|
||||
tl_object_ptr<telegram_api::InputMessage> input_message = nullptr);
|
||||
|
||||
struct MessageThreadInfo {
|
||||
DialogId dialog_id;
|
||||
vector<MessageId> message_ids;
|
||||
};
|
||||
void get_message_thread(DialogId dialog_id, MessageId message_id, Promise<MessageThreadInfo> &&promise);
|
||||
|
||||
td_api::object_ptr<td_api::messageThreadInfo> get_message_thread_info_object(const MessageThreadInfo &info);
|
||||
|
||||
void process_discussion_message(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
||||
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
||||
MessageId expected_message_id, Promise<vector<FullMessageId>> promise);
|
||||
MessageId expected_message_id, Promise<MessageThreadInfo> promise);
|
||||
|
||||
bool is_message_edited_recently(FullMessageId full_message_id, int32 seconds);
|
||||
|
||||
@ -1044,6 +1041,7 @@ class MessagesManager final : public Actor {
|
||||
bool is_mention_notification_disabled = false;
|
||||
bool is_from_scheduled = false;
|
||||
bool is_pinned = false;
|
||||
bool are_media_timestamp_entities_found = false;
|
||||
|
||||
bool is_copy = false; // for send_message
|
||||
bool from_background = false; // for send_message
|
||||
@ -1065,6 +1063,9 @@ class MessagesManager final : public Actor {
|
||||
NotificationId notification_id;
|
||||
NotificationId removed_notification_id;
|
||||
|
||||
int32 max_reply_media_timestamp = -1;
|
||||
int32 max_own_media_timestamp = -2; // to update replied messages on the first load
|
||||
|
||||
int32 view_count = 0;
|
||||
int32 forward_count = 0;
|
||||
MessageReplyInfo reply_info;
|
||||
@ -1101,6 +1102,7 @@ class MessagesManager final : public Actor {
|
||||
unique_ptr<Message> right;
|
||||
|
||||
mutable int32 last_access_date = 0;
|
||||
mutable bool is_update_sent = false; // whether the message is known to the app
|
||||
|
||||
mutable uint64 send_message_log_event_id = 0;
|
||||
|
||||
@ -1784,6 +1786,8 @@ class MessagesManager final : public Actor {
|
||||
|
||||
Status can_pin_messages(DialogId dialog_id) const;
|
||||
|
||||
static Status can_get_media_timestamp_link(DialogId dialog_id, const Message *m);
|
||||
|
||||
void cancel_edit_message_media(DialogId dialog_id, Message *m, Slice error_message);
|
||||
|
||||
void on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id,
|
||||
@ -2142,13 +2146,26 @@ class MessagesManager final : public Actor {
|
||||
|
||||
void attach_message_to_next(Dialog *d, MessageId message_id, const char *source);
|
||||
|
||||
bool update_message(Dialog *d, Message *old_message, unique_ptr<Message> new_message, bool *need_update_dialog_pos);
|
||||
bool update_message(Dialog *d, Message *old_message, unique_ptr<Message> new_message, bool *need_update_dialog_pos,
|
||||
bool is_message_in_dialog);
|
||||
|
||||
static bool need_message_changed_warning(const Message *old_message);
|
||||
|
||||
bool update_message_content(DialogId dialog_id, Message *old_message, unique_ptr<MessageContent> new_content,
|
||||
bool need_send_update_message_content, bool need_merge_files, bool is_message_in_dialog);
|
||||
|
||||
void update_message_max_reply_media_timestamp(const Dialog *d, Message *m, bool need_send_update_message_content);
|
||||
|
||||
void update_message_max_own_media_timestamp(const Dialog *d, Message *m);
|
||||
|
||||
void update_message_max_reply_media_timestamp_in_replied_messages(DialogId dialog_id, MessageId reply_to_message_id);
|
||||
|
||||
void register_message_reply(const Dialog *d, const Message *m);
|
||||
|
||||
void reregister_message_reply(const Dialog *d, const Message *m);
|
||||
|
||||
void unregister_message_reply(const Dialog *d, const Message *m);
|
||||
|
||||
void send_update_new_message(const Dialog *d, const Message *m);
|
||||
|
||||
static bool is_from_mention_notification_group(const Dialog *d, const Message *m);
|
||||
@ -2205,7 +2222,9 @@ class MessagesManager final : public Actor {
|
||||
|
||||
void send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const;
|
||||
|
||||
void send_update_message_content(DialogId dialog_id, const Message *m, const char *source);
|
||||
void send_update_message_content(DialogId dialog_id, Message *m, bool is_message_in_dialog, const char *source);
|
||||
|
||||
void send_update_message_content(const Dialog *d, Message *m, bool is_message_in_dialog, const char *source);
|
||||
|
||||
void send_update_message_content_impl(DialogId dialog_id, const Message *m, const char *source) const;
|
||||
|
||||
@ -2608,6 +2627,10 @@ class MessagesManager final : public Actor {
|
||||
|
||||
bool has_message_sender_user_id(DialogId dialog_id, const Message *m) const;
|
||||
|
||||
int32 get_message_own_max_media_timestamp(const Message *m) const;
|
||||
|
||||
static int32 get_message_max_media_timestamp(const Message *m);
|
||||
|
||||
static bool get_message_disable_web_page_preview(const Message *m);
|
||||
|
||||
static int32 get_message_flags(const Message *m);
|
||||
@ -2656,9 +2679,9 @@ class MessagesManager final : public Actor {
|
||||
|
||||
void process_discussion_message_impl(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
||||
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
||||
MessageId expected_message_id, Promise<vector<FullMessageId>> promise);
|
||||
MessageId expected_message_id, Promise<MessageThreadInfo> promise);
|
||||
|
||||
void on_get_discussion_message(DialogId dialog_id, MessageId message_id, vector<FullMessageId> full_message_ids,
|
||||
void on_get_discussion_message(DialogId dialog_id, MessageId message_id, MessageThreadInfo &&message_thread_info,
|
||||
Promise<MessageThreadInfo> &&promise);
|
||||
|
||||
static MessageId get_first_database_message_id_by_index(const Dialog *d, MessageSearchFilter filter);
|
||||
@ -3207,10 +3230,10 @@ class MessagesManager final : public Actor {
|
||||
std::unordered_map<int64, FoundMessages> found_fts_messages_; // random_id -> FoundMessages
|
||||
std::unordered_map<int64, FoundMessages> found_message_public_forwards_; // random_id -> FoundMessages
|
||||
|
||||
struct PublicMessageLinks {
|
||||
std::unordered_map<MessageId, std::pair<string, string>, MessageIdHash> links_;
|
||||
struct MessageEmbeddingCodes {
|
||||
std::unordered_map<MessageId, string, MessageIdHash> embedding_codes_;
|
||||
};
|
||||
std::unordered_map<DialogId, PublicMessageLinks, DialogIdHash> public_message_links_[2];
|
||||
std::unordered_map<DialogId, MessageEmbeddingCodes, DialogIdHash> message_embedding_codes_[2];
|
||||
|
||||
std::unordered_map<int64, tl_object_ptr<td_api::chatEvents>> chat_events_; // random_id -> chat events
|
||||
|
||||
@ -3223,6 +3246,10 @@ class MessagesManager final : public Actor {
|
||||
|
||||
std::unordered_map<FullMessageId, int32, FullMessageIdHash> replied_by_yet_unsent_messages_;
|
||||
|
||||
// full_message_id -> replies with media timestamps
|
||||
std::unordered_map<FullMessageId, std::unordered_set<MessageId, MessageIdHash>, FullMessageIdHash>
|
||||
replied_by_media_timestamp_messages_;
|
||||
|
||||
struct ActiveDialogAction {
|
||||
MessageId top_thread_message_id;
|
||||
UserId user_id;
|
||||
|
@ -109,9 +109,7 @@ void parse(PhotoSizeSource::FullLegacy &source, ParserT &parser) {
|
||||
parse(source.volume_id, parser);
|
||||
parse(source.secret, parser);
|
||||
parse(source.local_id, parser);
|
||||
if (source.local_id < 0) {
|
||||
parser.set_error("Wrong local_id");
|
||||
}
|
||||
// source.local_id can be negative in secret chat thumbnails
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
|
@ -575,7 +575,7 @@ td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id, co
|
||||
auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id;
|
||||
poll_type = td_api::make_object<td_api::pollTypeQuiz>(
|
||||
correct_option_id,
|
||||
get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation, true));
|
||||
get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation, true, -1));
|
||||
} else {
|
||||
poll_type = td_api::make_object<td_api::pollTypeRegular>(poll->allow_multiple_answers);
|
||||
}
|
||||
@ -1581,12 +1581,12 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
|
||||
auto entities =
|
||||
get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll");
|
||||
auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, false);
|
||||
auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, true, false);
|
||||
if (status.is_error()) {
|
||||
if (!clean_input_string(poll_results->solution_)) {
|
||||
poll_results->solution_.clear();
|
||||
}
|
||||
entities = find_entities(poll_results->solution_, true);
|
||||
entities = find_entities(poll_results->solution_, true, true);
|
||||
}
|
||||
FormattedText explanation{std::move(poll_results->solution_), std::move(entities)};
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/overloaded.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
@ -441,8 +441,8 @@ void SetSecureValue::start_up() {
|
||||
for (auto it = secure_value_.files.begin(); it != secure_value_.files.end();) {
|
||||
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
||||
bool is_duplicate = false;
|
||||
for (auto pit = secure_value_.files.begin(); pit != it; pit++) {
|
||||
if (file_id == file_manager->get_file_view(pit->file_id).file_id()) {
|
||||
for (auto other_it = secure_value_.files.begin(); other_it != it; ++other_it) {
|
||||
if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) {
|
||||
is_duplicate = true;
|
||||
break;
|
||||
}
|
||||
@ -458,8 +458,8 @@ void SetSecureValue::start_up() {
|
||||
for (auto it = secure_value_.translations.begin(); it != secure_value_.translations.end();) {
|
||||
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
||||
bool is_duplicate = file_id == front_side_file_id || file_id == reverse_side_file_id || file_id == selfie_file_id;
|
||||
for (auto pit = secure_value_.translations.begin(); pit != it; pit++) {
|
||||
if (file_id == file_manager->get_file_view(pit->file_id).file_id()) {
|
||||
for (auto other_it = secure_value_.translations.begin(); other_it != it; ++other_it) {
|
||||
if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) {
|
||||
is_duplicate = true;
|
||||
break;
|
||||
}
|
||||
|
@ -182,10 +182,6 @@ struct EncryptedValue {
|
||||
BufferSlice data;
|
||||
ValueHash hash;
|
||||
};
|
||||
struct EncryptedFile {
|
||||
std::string path;
|
||||
ValueHash hash;
|
||||
};
|
||||
|
||||
Result<EncryptedValue> encrypt_value(const Secret &secret, Slice data);
|
||||
Result<ValueHash> encrypt_file(const Secret &secret, std::string src, std::string dest);
|
||||
|
@ -136,8 +136,8 @@ void update_suggested_actions(vector<SuggestedAction> &suggested_actions,
|
||||
} else if (old_it == suggested_actions.end() || *new_it < *old_it) {
|
||||
added_actions.push_back(*new_it++);
|
||||
} else {
|
||||
old_it++;
|
||||
new_it++;
|
||||
++old_it;
|
||||
++new_it;
|
||||
}
|
||||
}
|
||||
CHECK(!added_actions.empty() || !removed_actions.empty());
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "td/telegram/MessageLinkInfo.h"
|
||||
#include "td/telegram/MessageSearchFilter.h"
|
||||
#include "td/telegram/MessagesManager.h"
|
||||
#include "td/telegram/MessageThreadInfo.h"
|
||||
#include "td/telegram/misc.h"
|
||||
#include "td/telegram/net/ConnectionCreator.h"
|
||||
#include "td/telegram/net/DcId.h"
|
||||
@ -440,67 +441,6 @@ class GetInviteTextQuery final : public Td::ResultHandler {
|
||||
}
|
||||
};
|
||||
|
||||
class GetDeepLinkInfoQuery final : public Td::ResultHandler {
|
||||
Promise<td_api::object_ptr<td_api::deepLinkInfo>> promise_;
|
||||
|
||||
public:
|
||||
explicit GetDeepLinkInfoQuery(Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise)
|
||||
: promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(Slice link) {
|
||||
Slice link_scheme("tg:");
|
||||
if (begins_with(link, link_scheme)) {
|
||||
link.remove_prefix(link_scheme.size());
|
||||
if (begins_with(link, "//")) {
|
||||
link.remove_prefix(2);
|
||||
}
|
||||
}
|
||||
size_t pos = 0;
|
||||
while (pos < link.size() && link[pos] != '/' && link[pos] != '?' && link[pos] != '#') {
|
||||
pos++;
|
||||
}
|
||||
link.truncate(pos);
|
||||
send_query(G()->net_query_creator().create_unauth(telegram_api::help_getDeepLinkInfo(link.str())));
|
||||
}
|
||||
|
||||
void on_result(uint64 id, BufferSlice packet) final {
|
||||
auto result_ptr = fetch_result<telegram_api::help_getDeepLinkInfo>(packet);
|
||||
if (result_ptr.is_error()) {
|
||||
return on_error(id, result_ptr.move_as_error());
|
||||
}
|
||||
|
||||
auto result = result_ptr.move_as_ok();
|
||||
switch (result->get_id()) {
|
||||
case telegram_api::help_deepLinkInfoEmpty::ID:
|
||||
return promise_.set_value(nullptr);
|
||||
case telegram_api::help_deepLinkInfo::ID: {
|
||||
auto info = telegram_api::move_object_as<telegram_api::help_deepLinkInfo>(result);
|
||||
bool need_update = (info->flags_ & telegram_api::help_deepLinkInfo::UPDATE_APP_MASK) != 0;
|
||||
|
||||
auto entities = get_message_entities(nullptr, std::move(info->entities_), "GetDeepLinkInfoQuery");
|
||||
auto status = fix_formatted_text(info->message_, entities, true, true, true, true);
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << "Receive error " << status << " while parsing deep link info " << info->message_;
|
||||
if (!clean_input_string(info->message_)) {
|
||||
info->message_.clear();
|
||||
}
|
||||
entities = find_entities(info->message_, true);
|
||||
}
|
||||
FormattedText text{std::move(info->message_), std::move(entities)};
|
||||
return promise_.set_value(
|
||||
td_api::make_object<td_api::deepLinkInfo>(get_formatted_text_object(text, true), need_update));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void on_error(uint64 id, Status status) final {
|
||||
promise_.set_error(std::move(status));
|
||||
}
|
||||
};
|
||||
|
||||
class SaveAppLogQuery final : public Td::ResultHandler {
|
||||
Promise<Unit> promise_;
|
||||
|
||||
@ -1067,13 +1007,13 @@ class GetRepliedMessageRequest final : public RequestOnceActor {
|
||||
}
|
||||
};
|
||||
|
||||
class GetMessageThreadRequest final : public RequestActor<MessagesManager::MessageThreadInfo> {
|
||||
class GetMessageThreadRequest final : public RequestActor<MessageThreadInfo> {
|
||||
DialogId dialog_id_;
|
||||
MessageId message_id_;
|
||||
|
||||
MessagesManager::MessageThreadInfo message_thread_info_;
|
||||
MessageThreadInfo message_thread_info_;
|
||||
|
||||
void do_run(Promise<MessagesManager::MessageThreadInfo> &&promise) final {
|
||||
void do_run(Promise<MessageThreadInfo> &&promise) final {
|
||||
if (get_tries() < 2) {
|
||||
promise.set_value(std::move(message_thread_info_));
|
||||
return;
|
||||
@ -1081,7 +1021,7 @@ class GetMessageThreadRequest final : public RequestActor<MessagesManager::Messa
|
||||
td->messages_manager_->get_message_thread(dialog_id_, message_id_, std::move(promise));
|
||||
}
|
||||
|
||||
void do_set_result(MessagesManager::MessageThreadInfo &&result) final {
|
||||
void do_set_result(MessageThreadInfo &&result) final {
|
||||
message_thread_info_ = std::move(result);
|
||||
}
|
||||
|
||||
@ -8347,7 +8287,7 @@ void Td::on_request(uint64 id, const td_api::getApplicationDownloadLink &request
|
||||
void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) {
|
||||
CLEAN_INPUT_STRING(request.link_);
|
||||
CREATE_REQUEST_PROMISE();
|
||||
create_handler<GetDeepLinkInfoQuery>(std::move(promise))->send(request.link_);
|
||||
link_manager_->get_deep_link_info(request.link_, std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getApplicationConfig &request) {
|
||||
@ -8510,8 +8450,9 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getTextEn
|
||||
if (!check_utf8(request.text_)) {
|
||||
return make_error(400, "Text must be encoded in UTF-8");
|
||||
}
|
||||
auto text_entities = find_entities(request.text_, false);
|
||||
return make_tl_object<td_api::textEntities>(get_text_entities_object(text_entities, false));
|
||||
auto text_entities = find_entities(request.text_, false, false);
|
||||
return make_tl_object<td_api::textEntities>(
|
||||
get_text_entities_object(text_entities, false, std::numeric_limits<int32>::max()));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseTextEntities &request) {
|
||||
@ -8546,7 +8487,7 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseTextEntiti
|
||||
}
|
||||
|
||||
return make_tl_object<td_api::formattedText>(std::move(request.text_),
|
||||
get_text_entities_object(r_entities.ok(), false));
|
||||
get_text_entities_object(r_entities.ok(), false, -1));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &request) {
|
||||
@ -8559,14 +8500,14 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &
|
||||
return make_error(400, r_entities.error().message());
|
||||
}
|
||||
auto entities = r_entities.move_as_ok();
|
||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true);
|
||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true, true);
|
||||
if (status.is_error()) {
|
||||
return make_error(400, status.error().message());
|
||||
}
|
||||
|
||||
auto parsed_text = parse_markdown_v3({std::move(request.text_->text_), std::move(entities)});
|
||||
fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).ensure();
|
||||
return get_formatted_text_object(parsed_text, true);
|
||||
fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).ensure();
|
||||
return get_formatted_text_object(parsed_text, false, std::numeric_limits<int32>::max());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText &request) {
|
||||
@ -8579,12 +8520,13 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText
|
||||
return make_error(400, r_entities.error().message());
|
||||
}
|
||||
auto entities = r_entities.move_as_ok();
|
||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true);
|
||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true, true);
|
||||
if (status.is_error()) {
|
||||
return make_error(400, status.error().message());
|
||||
}
|
||||
|
||||
return get_formatted_text_object(get_markdown_v3({std::move(request.text_->text_), std::move(entities)}), true);
|
||||
return get_formatted_text_object(get_markdown_v3({std::move(request.text_->text_), std::move(entities)}), false,
|
||||
std::numeric_limits<int32>::max());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getFileMimeType &request) {
|
||||
|
@ -95,12 +95,12 @@ TermsOfService::TermsOfService(telegram_api::object_ptr<telegram_api::help_terms
|
||||
|
||||
id_ = std::move(terms->id_->data_);
|
||||
auto entities = get_message_entities(nullptr, std::move(terms->entities_), "TermsOfService");
|
||||
auto status = fix_formatted_text(terms->text_, entities, true, true, true, false);
|
||||
auto status = fix_formatted_text(terms->text_, entities, true, true, true, true, false);
|
||||
if (status.is_error()) {
|
||||
if (!clean_input_string(terms->text_)) {
|
||||
terms->text_.clear();
|
||||
}
|
||||
entities = find_entities(terms->text_, true);
|
||||
entities = find_entities(terms->text_, true, true);
|
||||
}
|
||||
if (terms->text_.empty()) {
|
||||
id_.clear();
|
||||
|
@ -42,7 +42,7 @@ class TermsOfService {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return td_api::make_object<td_api::termsOfService>(get_formatted_text_object(text_, true), min_user_age_,
|
||||
return td_api::make_object<td_api::termsOfService>(get_formatted_text_object(text_, true, -1), min_user_age_,
|
||||
show_popup_);
|
||||
}
|
||||
|
||||
|
@ -854,7 +854,7 @@ void UpdatesManager::on_get_updates(tl_object_ptr<telegram_api::Updates> &&updat
|
||||
auto updates = move_tl_object_as<telegram_api::updatesCombined>(updates_ptr);
|
||||
td_->contacts_manager_->on_get_users(std::move(updates->users_), "updatesCombined");
|
||||
td_->contacts_manager_->on_get_chats(std::move(updates->chats_), "updatesCombined");
|
||||
on_pending_updates(std::move(updates->updates_), updates->seq_start_, updates->seq_, updates->date_,
|
||||
on_pending_updates(std::move(updates->updates_), updates->seq_start_, updates->seq_, updates->date_, Time::now(),
|
||||
std::move(promise), "telegram_api::updatesCombined");
|
||||
break;
|
||||
}
|
||||
@ -862,8 +862,8 @@ void UpdatesManager::on_get_updates(tl_object_ptr<telegram_api::Updates> &&updat
|
||||
auto updates = move_tl_object_as<telegram_api::updates>(updates_ptr);
|
||||
td_->contacts_manager_->on_get_users(std::move(updates->users_), "updates");
|
||||
td_->contacts_manager_->on_get_chats(std::move(updates->chats_), "updates");
|
||||
on_pending_updates(std::move(updates->updates_), updates->seq_, updates->seq_, updates->date_, std::move(promise),
|
||||
"telegram_api::updates");
|
||||
on_pending_updates(std::move(updates->updates_), updates->seq_, updates->seq_, updates->date_, Time::now(),
|
||||
std::move(promise), "telegram_api::updates");
|
||||
break;
|
||||
}
|
||||
case telegram_api::updateShortSentMessage::ID:
|
||||
@ -1356,9 +1356,9 @@ void UpdatesManager::on_get_difference(tl_object_ptr<telegram_api::updates_Diffe
|
||||
auto state = std::move(difference->intermediate_state_);
|
||||
if (get_pts() != std::numeric_limits<int32>::max() && state->date_ == get_date() &&
|
||||
(state->pts_ == get_pts() ||
|
||||
(min_postponed_update_pts_ != 0 && state->pts_ - 1000 >= min_postponed_update_pts_)) &&
|
||||
(min_postponed_update_pts_ != 0 && state->pts_ - 500 >= min_postponed_update_pts_)) &&
|
||||
(state->qts_ == get_qts() ||
|
||||
(min_postponed_update_qts_ != 0 && state->qts_ - 1000 >= min_postponed_update_qts_))) {
|
||||
(min_postponed_update_qts_ != 0 && state->qts_ - 500 >= min_postponed_update_qts_))) {
|
||||
on_get_updates_state(std::move(state), "get difference final slice");
|
||||
VLOG(get_difference) << "Trying to switch back from getDifference to update processing";
|
||||
break;
|
||||
@ -1401,7 +1401,7 @@ void UpdatesManager::after_get_difference() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (postponed_updates_.size()) {
|
||||
if (!postponed_updates_.empty()) {
|
||||
VLOG(get_difference) << "Begin to apply " << postponed_updates_.size() << " postponed update chunks";
|
||||
size_t total_update_count = 0;
|
||||
while (!postponed_updates_.empty()) {
|
||||
@ -1409,11 +1409,12 @@ void UpdatesManager::after_get_difference() {
|
||||
auto updates = std::move(it->second.updates);
|
||||
auto updates_seq_begin = it->second.seq_begin;
|
||||
auto updates_seq_end = it->second.seq_end;
|
||||
auto receive_time = it->second.receive_time;
|
||||
auto promise = std::move(it->second.promise);
|
||||
// ignore it->second.date, because it may be too old
|
||||
postponed_updates_.erase(it);
|
||||
auto update_count = updates.size();
|
||||
on_pending_updates(std::move(updates), updates_seq_begin, updates_seq_end, 0, std::move(promise),
|
||||
on_pending_updates(std::move(updates), updates_seq_begin, updates_seq_end, 0, receive_time, std::move(promise),
|
||||
"postponed updates");
|
||||
if (running_get_difference_) {
|
||||
VLOG(get_difference) << "Finish to apply postponed updates with " << postponed_updates_.size()
|
||||
@ -1426,15 +1427,15 @@ void UpdatesManager::after_get_difference() {
|
||||
VLOG(get_difference) << "Finish to apply " << total_update_count << " postponed updates";
|
||||
}
|
||||
|
||||
if (postponed_pts_updates_.size()) { // must be before td_->messages_manager_->after_get_difference()
|
||||
if (!postponed_pts_updates_.empty()) { // must be before td_->messages_manager_->after_get_difference()
|
||||
auto postponed_updates = std::move(postponed_pts_updates_);
|
||||
postponed_pts_updates_.clear();
|
||||
|
||||
LOG(INFO) << "Begin to apply " << postponed_updates.size() << " postponed pts updates";
|
||||
for (auto &postponed_update : postponed_updates) {
|
||||
auto &update = postponed_update.second;
|
||||
add_pending_pts_update(std::move(update.update), update.pts, update.pts_count, std::move(update.promise),
|
||||
"after get difference");
|
||||
add_pending_pts_update(std::move(update.update), update.pts, update.pts_count, update.receive_time,
|
||||
std::move(update.promise), "after get difference");
|
||||
CHECK(!running_get_difference_);
|
||||
}
|
||||
LOG(INFO) << "Finish to apply postponed pts updates, have " << postponed_pts_updates_.size()
|
||||
@ -1451,7 +1452,8 @@ void UpdatesManager::after_get_difference() {
|
||||
}
|
||||
|
||||
void UpdatesManager::on_pending_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, int32 seq_begin,
|
||||
int32 seq_end, int32 date, Promise<Unit> &&promise, const char *source) {
|
||||
int32 seq_end, int32 date, double receive_time, Promise<Unit> &&promise,
|
||||
const char *source) {
|
||||
if (get_pts() == -1) {
|
||||
init_state();
|
||||
}
|
||||
@ -1485,8 +1487,8 @@ void UpdatesManager::on_pending_updates(vector<tl_object_ptr<telegram_api::Updat
|
||||
min_postponed_update_qts_ = qts;
|
||||
}
|
||||
}
|
||||
postponed_updates_.emplace(seq_begin,
|
||||
PendingSeqUpdates(seq_begin, seq_end, date, std::move(updates), std::move(promise)));
|
||||
postponed_updates_.emplace(
|
||||
seq_begin, PendingSeqUpdates(seq_begin, seq_end, date, receive_time, std::move(updates), std::move(promise)));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1631,8 +1633,8 @@ void UpdatesManager::on_pending_updates(vector<tl_object_ptr<telegram_api::Updat
|
||||
if (running_get_difference_) {
|
||||
LOG(ERROR) << "Postpone " << updates.size() << " updates [" << seq_begin << ", " << seq_end
|
||||
<< "] with date = " << date << " from " << source;
|
||||
postponed_updates_.emplace(seq_begin,
|
||||
PendingSeqUpdates(seq_begin, seq_end, date, std::move(updates), mpas.get_promise()));
|
||||
postponed_updates_.emplace(
|
||||
seq_begin, PendingSeqUpdates(seq_begin, seq_end, date, receive_time, std::move(updates), mpas.get_promise()));
|
||||
return lock.set_value(Unit());
|
||||
}
|
||||
|
||||
@ -1668,9 +1670,9 @@ void UpdatesManager::on_pending_updates(vector<tl_object_ptr<telegram_api::Updat
|
||||
LOG_IF(WARNING, pending_seq_updates_.find(seq_begin) != pending_seq_updates_.end())
|
||||
<< "Already have pending updates with seq = " << seq_begin << ", but receive it again from " << source;
|
||||
|
||||
pending_seq_updates_.emplace(seq_begin,
|
||||
PendingSeqUpdates(seq_begin, seq_end, date, std::move(updates), mpas.get_promise()));
|
||||
set_seq_gap_timeout(MAX_UNFILLED_GAP_TIME);
|
||||
pending_seq_updates_.emplace(
|
||||
seq_begin, PendingSeqUpdates(seq_begin, seq_end, date, receive_time, std::move(updates), mpas.get_promise()));
|
||||
set_seq_gap_timeout(receive_time + MAX_UNFILLED_GAP_TIME - Time::now());
|
||||
lock.set_value(Unit());
|
||||
}
|
||||
|
||||
@ -1711,6 +1713,8 @@ void UpdatesManager::add_pending_qts_update(tl_object_ptr<telegram_api::Update>
|
||||
auto &pending_update = pending_qts_updates_[qts];
|
||||
if (pending_update.update != nullptr) {
|
||||
LOG(WARNING) << "Receive duplicate update with qts = " << qts;
|
||||
} else {
|
||||
pending_update.receive_time = Time::now();
|
||||
}
|
||||
pending_update.update = std::move(update);
|
||||
pending_update.promises.push_back(std::move(promise));
|
||||
@ -1849,7 +1853,8 @@ void UpdatesManager::process_pts_update(tl_object_ptr<telegram_api::Update> &&up
|
||||
}
|
||||
|
||||
void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 new_pts,
|
||||
int32 pts_count, Promise<Unit> &&promise, const char *source) {
|
||||
int32 pts_count, double receive_time, Promise<Unit> &&promise,
|
||||
const char *source) {
|
||||
// do not try to run getDifference from this function
|
||||
CHECK(update != nullptr);
|
||||
CHECK(source != nullptr);
|
||||
@ -1905,7 +1910,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
|
||||
if (running_get_difference_) {
|
||||
CHECK(update->get_id() == dummyUpdate::ID || update->get_id() == updateSentMessage::ID);
|
||||
}
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, std::move(promise));
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, receive_time, std::move(promise));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1913,7 +1918,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
|
||||
LOG(WARNING) << "Have old_pts (= " << old_pts << ") + pts_count (= " << pts_count << ") > new_pts (= " << new_pts
|
||||
<< "). Logged in " << G()->shared_config().get_option_integer("authorization_date") << ". Update from "
|
||||
<< source << " = " << oneline(to_string(update));
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, std::move(promise));
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, receive_time, std::move(promise));
|
||||
set_pts_gap_timeout(0.001);
|
||||
return;
|
||||
}
|
||||
@ -1929,7 +1934,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
|
||||
<< ", pts_count = " << pts_count << ". Logged in "
|
||||
<< G()->shared_config().get_option_integer("authorization_date") << ". Update from " << source << " = "
|
||||
<< oneline(to_string(update));
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, std::move(promise));
|
||||
postpone_pts_update(std::move(update), new_pts, pts_count, receive_time, std::move(promise));
|
||||
set_pts_gap_timeout(0.001);
|
||||
return;
|
||||
}
|
||||
@ -1950,11 +1955,11 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
|
||||
return;
|
||||
}
|
||||
|
||||
pending_pts_updates_.emplace(new_pts, PendingPtsUpdate(std::move(update), new_pts, pts_count, std::move(promise)));
|
||||
pending_pts_updates_.emplace(
|
||||
new_pts, PendingPtsUpdate(std::move(update), new_pts, pts_count, receive_time, std::move(promise)));
|
||||
|
||||
if (old_pts < accumulated_pts_ - accumulated_pts_count_) {
|
||||
set_pts_gap_timeout(MAX_UNFILLED_GAP_TIME);
|
||||
last_pts_gap_time_ = Time::now();
|
||||
set_pts_gap_timeout(receive_time + MAX_UNFILLED_GAP_TIME - Time::now());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1963,8 +1968,9 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
|
||||
}
|
||||
|
||||
void UpdatesManager::postpone_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count,
|
||||
Promise<Unit> &&promise) {
|
||||
postponed_pts_updates_.emplace(pts, PendingPtsUpdate(std::move(update), pts, pts_count, std::move(promise)));
|
||||
double receive_time, Promise<Unit> &&promise) {
|
||||
postponed_pts_updates_.emplace(pts,
|
||||
PendingPtsUpdate(std::move(update), pts, pts_count, receive_time, std::move(promise)));
|
||||
}
|
||||
|
||||
void UpdatesManager::process_seq_updates(int32 seq_end, int32 date,
|
||||
@ -2075,16 +2081,17 @@ void UpdatesManager::process_pending_seq_updates() {
|
||||
while (!pending_seq_updates_.empty() && !running_get_difference_) {
|
||||
auto update_it = pending_seq_updates_.begin();
|
||||
auto seq_begin = update_it->second.seq_begin;
|
||||
if (seq_begin > seq_ + 1) {
|
||||
if (seq_begin - 1 > seq_ && seq_begin - 500000000 <= seq_) {
|
||||
// the updates will be applied later
|
||||
break;
|
||||
}
|
||||
if (seq_begin == seq_ + 1) {
|
||||
if (seq_begin - 1 == seq_) {
|
||||
process_seq_updates(update_it->second.seq_end, update_it->second.date, std::move(update_it->second.updates),
|
||||
std::move(update_it->second.promise));
|
||||
} else {
|
||||
// old update
|
||||
CHECK(seq_begin != 0);
|
||||
LOG_IF(ERROR, update_it->second.seq_end > seq_)
|
||||
LOG_IF(ERROR, update_it->second.seq_end > seq_ && seq_begin - 1 < seq_)
|
||||
<< "Strange updates coming with seq_begin = " << seq_begin << ", seq_end = " << update_it->second.seq_end
|
||||
<< ", but seq = " << seq_;
|
||||
update_it->second.promise.set_value(Unit());
|
||||
@ -2095,7 +2102,15 @@ void UpdatesManager::process_pending_seq_updates() {
|
||||
seq_gap_timeout_.cancel_timeout();
|
||||
} else {
|
||||
// if after getDifference still have a gap
|
||||
set_seq_gap_timeout(MAX_UNFILLED_GAP_TIME);
|
||||
auto update_it = pending_seq_updates_.begin();
|
||||
double receive_time = update_it->second.receive_time;
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
if (++update_it == pending_seq_updates_.end()) {
|
||||
break;
|
||||
}
|
||||
receive_time = min(receive_time, update_it->second.receive_time);
|
||||
}
|
||||
set_seq_gap_timeout(receive_time + MAX_UNFILLED_GAP_TIME - Time::now());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2103,12 +2118,15 @@ void UpdatesManager::process_pending_qts_updates() {
|
||||
if (pending_qts_updates_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Process " << pending_qts_updates_.size() << " pending qts updates";
|
||||
while (!pending_qts_updates_.empty()) {
|
||||
CHECK(!running_get_difference_);
|
||||
auto update_it = pending_qts_updates_.begin();
|
||||
auto qts = update_it->first;
|
||||
if (qts > get_qts() + 1) {
|
||||
auto old_qts = get_qts();
|
||||
if (qts - 1 > old_qts && qts - 500000000 <= old_qts) {
|
||||
// the update will be applied later
|
||||
break;
|
||||
}
|
||||
auto promise = PromiseCreator::lambda([promises = std::move(update_it->second.promises)](Unit) mutable {
|
||||
@ -2116,31 +2134,41 @@ void UpdatesManager::process_pending_qts_updates() {
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
});
|
||||
if (qts == get_qts() + 1) {
|
||||
if (qts == old_qts + 1) {
|
||||
process_qts_update(std::move(update_it->second.update), qts, std::move(promise));
|
||||
} else {
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
pending_qts_updates_.erase(update_it);
|
||||
}
|
||||
|
||||
if (pending_qts_updates_.empty()) {
|
||||
qts_gap_timeout_.cancel_timeout();
|
||||
} else {
|
||||
// if after getDifference still have a gap
|
||||
set_qts_gap_timeout(MAX_UNFILLED_GAP_TIME);
|
||||
auto update_it = pending_qts_updates_.begin();
|
||||
double receive_time = update_it->second.receive_time;
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
if (++update_it == pending_qts_updates_.end()) {
|
||||
break;
|
||||
}
|
||||
receive_time = min(receive_time, update_it->second.receive_time);
|
||||
}
|
||||
set_qts_gap_timeout(receive_time + MAX_UNFILLED_GAP_TIME - Time::now());
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatesManager::set_pts_gap_timeout(double timeout) {
|
||||
if (!pts_gap_timeout_.has_timeout()) {
|
||||
if (!pts_gap_timeout_.has_timeout() || timeout < pts_gap_timeout_.get_timeout()) {
|
||||
pts_gap_timeout_.set_callback(std::move(fill_pts_gap));
|
||||
pts_gap_timeout_.set_callback_data(static_cast<void *>(td_));
|
||||
pts_gap_timeout_.set_timeout_in(timeout);
|
||||
last_pts_gap_time_ = Time::now();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatesManager::set_seq_gap_timeout(double timeout) {
|
||||
if (!seq_gap_timeout_.has_timeout()) {
|
||||
if (!seq_gap_timeout_.has_timeout() || timeout < seq_gap_timeout_.get_timeout()) {
|
||||
seq_gap_timeout_.set_callback(std::move(fill_seq_gap));
|
||||
seq_gap_timeout_.set_callback_data(static_cast<void *>(td_));
|
||||
seq_gap_timeout_.set_timeout_in(timeout);
|
||||
@ -2148,7 +2176,7 @@ void UpdatesManager::set_seq_gap_timeout(double timeout) {
|
||||
}
|
||||
|
||||
void UpdatesManager::set_qts_gap_timeout(double timeout) {
|
||||
if (!qts_gap_timeout_.has_timeout()) {
|
||||
if (!qts_gap_timeout_.has_timeout() || timeout < qts_gap_timeout_.get_timeout()) {
|
||||
qts_gap_timeout_.set_callback(std::move(fill_qts_gap));
|
||||
qts_gap_timeout_.set_callback_data(static_cast<void *>(td_));
|
||||
qts_gap_timeout_.set_timeout_in(timeout);
|
||||
@ -2159,13 +2187,13 @@ void UpdatesManager::on_pending_update(tl_object_ptr<telegram_api::Update> updat
|
||||
const char *source) {
|
||||
vector<tl_object_ptr<telegram_api::Update>> updates;
|
||||
updates.push_back(std::move(update));
|
||||
on_pending_updates(std::move(updates), seq, seq, 0, std::move(promise), source);
|
||||
on_pending_updates(std::move(updates), seq, seq, 0, Time::now(), std::move(promise), source);
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateNewMessage> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateNewMessage");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise), "updateNewMessage");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateNewChannelMessage> update, Promise<Unit> &&promise) {
|
||||
@ -2184,36 +2212,41 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadMessagesCon
|
||||
Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateReadMessagesContents");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise),
|
||||
"updateReadMessagesContents");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateEditMessage> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateEditMessage");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise), "updateEditMessage");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDeleteMessages> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
if (update->messages_.empty()) {
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), new_pts, pts_count, Promise<Unit>(), "updateDeleteMessages");
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), new_pts, pts_count, Time::now(), Promise<Unit>(),
|
||||
"updateDeleteMessages");
|
||||
promise.set_value(Unit());
|
||||
} else {
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateDeleteMessages");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise),
|
||||
"updateDeleteMessages");
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadHistoryInbox> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateReadHistoryInbox");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise),
|
||||
"updateReadHistoryInbox");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadHistoryOutbox> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updateReadHistoryOutbox");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise),
|
||||
"updateReadHistoryOutbox");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateServiceNotification> update, Promise<Unit> &&promise) {
|
||||
@ -2325,7 +2358,8 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadChannelDisc
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePinnedMessages> update, Promise<Unit> &&promise) {
|
||||
int new_pts = update->pts_;
|
||||
int pts_count = update->pts_count_;
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, std::move(promise), "updatePinnedMessages");
|
||||
add_pending_pts_update(std::move(update), new_pts, pts_count, Time::now(), std::move(promise),
|
||||
"updatePinnedMessages");
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePinnedChannelMessages> update,
|
||||
@ -2388,7 +2422,7 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePeerLocated> up
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateWebPage> update, Promise<Unit> &&promise) {
|
||||
td_->web_pages_manager_->on_get_web_page(std::move(update->webpage_), DialogId());
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), update->pts_, update->pts_count_, Promise<Unit>(),
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), update->pts_, update->pts_count_, Time::now(), Promise<Unit>(),
|
||||
"updateWebPage");
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
@ -2409,8 +2443,8 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateFolderPeers> up
|
||||
}
|
||||
|
||||
if (update->pts_ > 0) {
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), update->pts_, update->pts_count_, Promise<Unit>(),
|
||||
"updateFolderPeers");
|
||||
add_pending_pts_update(make_tl_object<dummyUpdate>(), update->pts_, update->pts_count_, Time::now(),
|
||||
Promise<Unit>(), "updateFolderPeers");
|
||||
}
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class UpdatesManager final : public Actor {
|
||||
void on_get_updates(tl_object_ptr<telegram_api::Updates> &&updates_ptr, Promise<Unit> &&promise);
|
||||
|
||||
void add_pending_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 new_pts, int32 pts_count,
|
||||
Promise<Unit> &&promise, const char *source);
|
||||
double receive_time, Promise<Unit> &&promise, const char *source);
|
||||
|
||||
static std::unordered_set<int64> get_sent_messages_random_ids(const telegram_api::Updates *updates_ptr);
|
||||
|
||||
@ -134,10 +134,16 @@ class UpdatesManager final : public Actor {
|
||||
tl_object_ptr<telegram_api::Update> update;
|
||||
int32 pts;
|
||||
int32 pts_count;
|
||||
double receive_time;
|
||||
Promise<Unit> promise;
|
||||
|
||||
PendingPtsUpdate(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count, Promise<Unit> &&promise)
|
||||
: update(std::move(update)), pts(pts), pts_count(pts_count), promise(std::move(promise)) {
|
||||
PendingPtsUpdate(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count, double receive_time,
|
||||
Promise<Unit> &&promise)
|
||||
: update(std::move(update))
|
||||
, pts(pts)
|
||||
, pts_count(pts_count)
|
||||
, receive_time(receive_time)
|
||||
, promise(std::move(promise)) {
|
||||
}
|
||||
};
|
||||
|
||||
@ -146,17 +152,24 @@ class UpdatesManager final : public Actor {
|
||||
int32 seq_begin;
|
||||
int32 seq_end;
|
||||
int32 date;
|
||||
double receive_time;
|
||||
vector<tl_object_ptr<telegram_api::Update>> updates;
|
||||
Promise<Unit> promise;
|
||||
|
||||
PendingSeqUpdates(int32 seq_begin, int32 seq_end, int32 date, vector<tl_object_ptr<telegram_api::Update>> &&updates,
|
||||
Promise<Unit> &&promise)
|
||||
: seq_begin(seq_begin), seq_end(seq_end), date(date), updates(std::move(updates)), promise(std::move(promise)) {
|
||||
PendingSeqUpdates(int32 seq_begin, int32 seq_end, int32 date, double receive_time,
|
||||
vector<tl_object_ptr<telegram_api::Update>> &&updates, Promise<Unit> &&promise)
|
||||
: seq_begin(seq_begin)
|
||||
, seq_end(seq_end)
|
||||
, date(date)
|
||||
, receive_time(receive_time)
|
||||
, updates(std::move(updates))
|
||||
, promise(std::move(promise)) {
|
||||
}
|
||||
};
|
||||
|
||||
class PendingQtsUpdate {
|
||||
public:
|
||||
double receive_time;
|
||||
tl_object_ptr<telegram_api::Update> update;
|
||||
vector<Promise<Unit>> promises;
|
||||
};
|
||||
@ -243,13 +256,13 @@ class UpdatesManager final : public Actor {
|
||||
void add_pending_qts_update(tl_object_ptr<telegram_api::Update> &&update, int32 qts, Promise<Unit> &&promise);
|
||||
|
||||
void on_pending_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, int32 seq_begin, int32 seq_end,
|
||||
int32 date, Promise<Unit> &&promise, const char *source);
|
||||
int32 date, double receive_time, Promise<Unit> &&promise, const char *source);
|
||||
|
||||
void process_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, bool force_apply,
|
||||
Promise<Unit> &&promise);
|
||||
|
||||
void postpone_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count,
|
||||
Promise<Unit> &&promise);
|
||||
double receive_time, Promise<Unit> &&promise);
|
||||
|
||||
void process_pts_update(tl_object_ptr<telegram_api::Update> &&update);
|
||||
|
||||
|
@ -825,7 +825,7 @@ int64 WebPagesManager::get_web_page_preview(td_api::object_ptr<td_api::formatted
|
||||
}
|
||||
auto entities = r_entities.move_as_ok();
|
||||
|
||||
auto result = fix_formatted_text(text->text_, entities, true, false, true, false);
|
||||
auto result = fix_formatted_text(text->text_, entities, true, false, true, true, false);
|
||||
if (result.is_error() || text->text_.empty()) {
|
||||
promise.set_value(Unit());
|
||||
return 0;
|
||||
@ -1225,7 +1225,7 @@ tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId we
|
||||
|
||||
FormattedText description;
|
||||
description.text = web_page->description;
|
||||
description.entities = find_entities(web_page->description, true);
|
||||
description.entities = find_entities(web_page->description, true, false);
|
||||
|
||||
auto r_url = parse_url(web_page->display_url);
|
||||
if (r_url.is_ok()) {
|
||||
@ -1294,11 +1294,12 @@ tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId we
|
||||
}
|
||||
}
|
||||
|
||||
auto duration = get_web_page_media_duration(web_page);
|
||||
return make_tl_object<td_api::webPage>(
|
||||
web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title,
|
||||
get_formatted_text_object(description, true), get_photo_object(td_->file_manager_.get(), web_page->photo),
|
||||
web_page->embed_url, web_page->embed_type, web_page->embed_dimensions.width, web_page->embed_dimensions.height,
|
||||
web_page->duration, web_page->author,
|
||||
get_formatted_text_object(description, true, duration == 0 ? std::numeric_limits<int32>::max() : duration),
|
||||
get_photo_object(td_->file_manager_.get(), web_page->photo), web_page->embed_url, web_page->embed_type,
|
||||
web_page->embed_dimensions.width, web_page->embed_dimensions.height, web_page->duration, web_page->author,
|
||||
web_page->document.type == Document::Type::Animation
|
||||
? td_->animations_manager_->get_animation_object(web_page->document.file_id, "get_web_page_object")
|
||||
: nullptr,
|
||||
@ -1751,14 +1752,23 @@ string WebPagesManager::get_web_page_search_text(WebPageId web_page_id) const {
|
||||
return PSTRING() << web_page->title + " " + web_page->description;
|
||||
}
|
||||
|
||||
int32 WebPagesManager::get_web_page_duration(WebPageId web_page_id) const {
|
||||
int32 WebPagesManager::get_web_page_media_duration(WebPageId web_page_id) const {
|
||||
const WebPage *web_page = get_web_page(web_page_id);
|
||||
if (web_page == nullptr) {
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
return get_web_page_media_duration(web_page);
|
||||
}
|
||||
|
||||
int32 WebPagesManager::get_web_page_media_duration(const WebPage *web_page) {
|
||||
if (web_page->document.type == Document::Type::Audio || web_page->document.type == Document::Type::Video ||
|
||||
web_page->document.type == Document::Type::VideoNote || web_page->document.type == Document::Type::VoiceNote || web_page->embed_type == "iframe") {
|
||||
return web_page->duration;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
vector<FileId> WebPagesManager::get_web_page_file_ids(const WebPage *web_page) const {
|
||||
if (web_page == nullptr) {
|
||||
return vector<FileId>();
|
||||
|
@ -91,7 +91,7 @@ class WebPagesManager final : public Actor {
|
||||
|
||||
string get_web_page_search_text(WebPageId web_page_id) const;
|
||||
|
||||
int32 get_web_page_duration(WebPageId web_page_id) const;
|
||||
int32 get_web_page_media_duration(WebPageId web_page_id) const;
|
||||
|
||||
private:
|
||||
static constexpr int32 WEBPAGE_FLAG_HAS_TYPE = 1 << 0;
|
||||
@ -174,6 +174,8 @@ class WebPagesManager final : public Actor {
|
||||
|
||||
void tear_down() final;
|
||||
|
||||
static int32 get_web_page_media_duration(const WebPage *web_page);
|
||||
|
||||
FileSourceId get_web_page_file_source_id(WebPage *web_page);
|
||||
|
||||
vector<FileId> get_web_page_file_ids(const WebPage *web_page) const;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "td/utils/port/Clocks.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
@ -35,6 +35,9 @@ class Timeout final : public Actor {
|
||||
bool has_timeout() const {
|
||||
return Actor::has_timeout();
|
||||
}
|
||||
double get_timeout() const {
|
||||
return Actor::get_timeout();
|
||||
}
|
||||
void set_timeout_in(double timeout) {
|
||||
Actor::set_timeout_in(timeout);
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ class Actor : public ObserverBase {
|
||||
void stop();
|
||||
void do_stop();
|
||||
bool has_timeout() const;
|
||||
double get_timeout() const;
|
||||
void set_timeout_in(double timeout_in);
|
||||
void set_timeout_at(double timeout_at);
|
||||
void cancel_timeout();
|
||||
|
@ -54,6 +54,9 @@ inline void Actor::do_stop() {
|
||||
inline bool Actor::has_timeout() const {
|
||||
return Scheduler::instance()->has_actor_timeout(this);
|
||||
}
|
||||
inline double Actor::get_timeout() const {
|
||||
return Scheduler::instance()->get_actor_timeout(this);
|
||||
}
|
||||
inline void Actor::set_timeout_in(double timeout_in) {
|
||||
Scheduler::instance()->set_actor_timeout_in(this, timeout_in);
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ class Scheduler {
|
||||
void finish_migrate_actor(Actor *actor);
|
||||
|
||||
bool has_actor_timeout(const Actor *actor) const;
|
||||
double get_actor_timeout(const Actor *actor) const;
|
||||
void set_actor_timeout_in(Actor *actor, double timeout);
|
||||
void set_actor_timeout_at(Actor *actor, double timeout_at);
|
||||
void cancel_actor_timeout(Actor *actor);
|
||||
@ -176,6 +177,7 @@ class Scheduler {
|
||||
void start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id);
|
||||
|
||||
bool has_actor_timeout(const ActorInfo *actor_info) const;
|
||||
double get_actor_timeout(const ActorInfo *actor_info) const;
|
||||
void set_actor_timeout_in(ActorInfo *actor_info, double timeout);
|
||||
void set_actor_timeout_at(ActorInfo *actor_info, double timeout_at);
|
||||
void cancel_actor_timeout(ActorInfo *actor_info);
|
||||
|
@ -392,6 +392,7 @@ void Scheduler::do_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id) {
|
||||
void Scheduler::start_migrate_actor(Actor *actor, int32 dest_sched_id) {
|
||||
start_migrate_actor(actor->get_info(), dest_sched_id);
|
||||
}
|
||||
|
||||
void Scheduler::start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id) {
|
||||
VLOG(actor) << "Start migrate actor: " << tag("name", actor_info) << tag("ptr", actor_info)
|
||||
<< tag("actor_count", actor_count_);
|
||||
@ -406,6 +407,11 @@ void Scheduler::start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id)
|
||||
cancel_actor_timeout(actor_info);
|
||||
}
|
||||
|
||||
double Scheduler::get_actor_timeout(const ActorInfo *actor_info) const {
|
||||
const HeapNode *heap_node = actor_info->get_heap_node();
|
||||
return heap_node->in_heap() ? timeout_queue_.get_key(heap_node) - Time::now() : 0.0;
|
||||
}
|
||||
|
||||
void Scheduler::set_actor_timeout_in(ActorInfo *actor_info, double timeout) {
|
||||
if (timeout > 1e10) {
|
||||
timeout = 1e10;
|
||||
|
@ -302,6 +302,9 @@ inline void Scheduler::finish_migrate_actor(Actor *actor) {
|
||||
inline bool Scheduler::has_actor_timeout(const Actor *actor) const {
|
||||
return has_actor_timeout(actor->get_info());
|
||||
}
|
||||
inline double Scheduler::get_actor_timeout(const Actor *actor) const {
|
||||
return get_actor_timeout(actor->get_info());
|
||||
}
|
||||
inline void Scheduler::set_actor_timeout_in(Actor *actor, double timeout) {
|
||||
set_actor_timeout_in(actor->get_info(), timeout);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ struct HeapNode {
|
||||
void remove() {
|
||||
pos_ = -1;
|
||||
}
|
||||
int pos_ = -1;
|
||||
int32 pos_ = -1;
|
||||
};
|
||||
|
||||
template <class KeyT, int K = 4>
|
||||
@ -37,6 +37,12 @@ class KHeap {
|
||||
return array_[0].key_;
|
||||
}
|
||||
|
||||
KeyT get_key(const HeapNode *node) const {
|
||||
size_t pos = static_cast<size_t>(node->pos_);
|
||||
CHECK(pos < array_.size());
|
||||
return array_[pos].key_;
|
||||
}
|
||||
|
||||
const HeapNode *top() const {
|
||||
return array_[0].node_;
|
||||
}
|
||||
@ -45,19 +51,19 @@ class KHeap {
|
||||
CHECK(!empty());
|
||||
HeapNode *result = array_[0].node_;
|
||||
result->remove();
|
||||
erase(0);
|
||||
erase(static_cast<size_t>(0));
|
||||
return result;
|
||||
}
|
||||
|
||||
void insert(KeyT key, HeapNode *node) {
|
||||
CHECK(!node->in_heap());
|
||||
array_.push_back({key, node});
|
||||
fix_up(static_cast<int>(array_.size()) - 1);
|
||||
fix_up(array_.size() - 1);
|
||||
}
|
||||
|
||||
void fix(KeyT key, HeapNode *node) {
|
||||
CHECK(node->in_heap());
|
||||
int pos = node->pos_;
|
||||
size_t pos = static_cast<size_t>(node->pos_);
|
||||
CHECK(pos < array_.size());
|
||||
KeyT old_key = array_[pos].key_;
|
||||
array_[pos].key_ = key;
|
||||
if (key < old_key) {
|
||||
@ -68,9 +74,9 @@ class KHeap {
|
||||
}
|
||||
|
||||
void erase(HeapNode *node) {
|
||||
CHECK(node->in_heap());
|
||||
int pos = node->pos_;
|
||||
size_t pos = static_cast<size_t>(node->pos_);
|
||||
node->remove();
|
||||
CHECK(pos < array_.size());
|
||||
erase(pos);
|
||||
}
|
||||
|
||||
@ -103,34 +109,34 @@ class KHeap {
|
||||
};
|
||||
vector<Item> array_;
|
||||
|
||||
void fix_up(int pos) {
|
||||
void fix_up(size_t pos) {
|
||||
auto item = array_[pos];
|
||||
|
||||
while (pos) {
|
||||
int parent_pos = (pos - 1) / K;
|
||||
auto parent_pos = (pos - 1) / K;
|
||||
auto parent_item = array_[parent_pos];
|
||||
|
||||
if (parent_item.key_ < item.key_) {
|
||||
break;
|
||||
}
|
||||
|
||||
parent_item.node_->pos_ = pos;
|
||||
parent_item.node_->pos_ = static_cast<int32>(pos);
|
||||
array_[pos] = parent_item;
|
||||
pos = parent_pos;
|
||||
}
|
||||
|
||||
item.node_->pos_ = pos;
|
||||
item.node_->pos_ = static_cast<int32>(pos);
|
||||
array_[pos] = item;
|
||||
}
|
||||
|
||||
void fix_down(int pos) {
|
||||
void fix_down(size_t pos) {
|
||||
auto item = array_[pos];
|
||||
while (true) {
|
||||
int left_pos = pos * K + 1;
|
||||
int right_pos = min(left_pos + K, static_cast<int>(array_.size()));
|
||||
int next_pos = pos;
|
||||
auto left_pos = pos * K + 1;
|
||||
auto right_pos = min(left_pos + K, array_.size());
|
||||
auto next_pos = pos;
|
||||
KeyT next_key = item.key_;
|
||||
for (int i = left_pos; i < right_pos; i++) {
|
||||
for (auto i = left_pos; i < right_pos; i++) {
|
||||
KeyT i_key = array_[i].key_;
|
||||
if (i_key < next_key) {
|
||||
next_key = i_key;
|
||||
@ -141,21 +147,24 @@ class KHeap {
|
||||
break;
|
||||
}
|
||||
array_[pos] = array_[next_pos];
|
||||
array_[pos].node_->pos_ = pos;
|
||||
array_[pos].node_->pos_ = static_cast<int32>(pos);
|
||||
pos = next_pos;
|
||||
}
|
||||
|
||||
item.node_->pos_ = pos;
|
||||
item.node_->pos_ = static_cast<int32>(pos);
|
||||
array_[pos] = item;
|
||||
}
|
||||
|
||||
void erase(int pos) {
|
||||
void erase(size_t pos) {
|
||||
array_[pos] = array_.back();
|
||||
array_.pop_back();
|
||||
if (pos < static_cast<int>(array_.size())) {
|
||||
if (pos < array_.size()) {
|
||||
fix_down(pos);
|
||||
fix_up(pos);
|
||||
}
|
||||
if (array_.capacity() > 50 && array_.size() < array_.capacity() / 4) {
|
||||
array_.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
//
|
||||
#include "td/telegram/MessageEntity.h"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
@ -17,6 +18,7 @@
|
||||
#include "td/utils/utf8.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
static void check_mention(const td::string &str, const td::vector<td::string> &expected) {
|
||||
auto result_slice = td::find_mentions(str);
|
||||
@ -172,6 +174,48 @@ TEST(MessageEntities, cashtag) {
|
||||
check_cashtag(u8"\u2122$ABC\u2122", {"$ABC"});
|
||||
}
|
||||
|
||||
static void check_media_timestamp(const td::string &str, const td::vector<std::pair<td::string, td::int32>> &expected) {
|
||||
auto result = td::transform(td::find_media_timestamps(str),
|
||||
[](auto &&entity) { return std::make_pair(entity.first.str(), entity.second); });
|
||||
if (result != expected) {
|
||||
LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result))
|
||||
<< td::tag("expected", td::format::as_array(expected));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MessageEntities, media_timestamp) {
|
||||
check_media_timestamp("", {});
|
||||
check_media_timestamp(":", {});
|
||||
check_media_timestamp(":1", {});
|
||||
check_media_timestamp("a:1", {});
|
||||
check_media_timestamp("01", {});
|
||||
check_media_timestamp("01:", {});
|
||||
check_media_timestamp("01::", {});
|
||||
check_media_timestamp("01::", {});
|
||||
check_media_timestamp("a1:1a", {});
|
||||
check_media_timestamp("a1::01a", {});
|
||||
check_media_timestamp("2001:db8::8a2e:f70:13a4", {});
|
||||
check_media_timestamp("0:00", {{"0:00", 0}});
|
||||
check_media_timestamp("+0:00", {{"0:00", 0}});
|
||||
check_media_timestamp("0:00+", {{"0:00", 0}});
|
||||
check_media_timestamp("a0:00", {});
|
||||
check_media_timestamp("0:00a", {});
|
||||
check_media_timestamp("б0:00", {});
|
||||
check_media_timestamp("0:00б", {});
|
||||
check_media_timestamp("_0:00", {});
|
||||
check_media_timestamp("0:00_", {});
|
||||
check_media_timestamp("00:00:00:00", {});
|
||||
check_media_timestamp("1:1:01 1:1:1", {{"1:1:01", 3661}});
|
||||
check_media_timestamp("0:0:00 00:00 000:00 0000:00 00000:00 00:00:00 000:00:00 00:000:00 00:00:000",
|
||||
{{"0:0:00", 0}, {"00:00", 0}, {"000:00", 0}, {"0000:00", 0}, {"00:00:00", 0}});
|
||||
check_media_timestamp("00:0:00 0:00:00 00::00 :00:00 00:00: 00:00:0 00:00:", {{"00:0:00", 0}, {"0:00:00", 0}});
|
||||
check_media_timestamp("1:1:59 1:1:-1 1:1:60", {{"1:1:59", 3719}});
|
||||
check_media_timestamp("1:59:00 1:-1:00 1:60:00", {{"1:59:00", 7140}, {"1:00", 60}});
|
||||
check_media_timestamp("59:59 60:00", {{"59:59", 3599}, {"60:00", 3600}});
|
||||
check_media_timestamp("9999:59 99:59:59 99:60:59", {{"9999:59", 599999}, {"99:59:59", 360000 - 1}});
|
||||
check_media_timestamp("2001:db8::8a2e:f70:13a4", {});
|
||||
}
|
||||
|
||||
static void check_bank_card_number(const td::string &str, const td::vector<td::string> &expected) {
|
||||
auto result_slice = td::find_bank_card_numbers(str);
|
||||
td::vector<td::string> result;
|
||||
@ -655,16 +699,16 @@ static void check_fix_formatted_text(td::string str, td::vector<td::MessageEntit
|
||||
const td::vector<td::MessageEntity> &expected_entities, bool allow_empty = true,
|
||||
bool skip_new_entities = false, bool skip_bot_commands = false,
|
||||
bool for_draft = true) {
|
||||
ASSERT_TRUE(
|
||||
td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_ok());
|
||||
ASSERT_TRUE(td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, true, for_draft)
|
||||
.is_ok());
|
||||
ASSERT_STREQ(expected_str, str);
|
||||
ASSERT_EQ(expected_entities, entities);
|
||||
}
|
||||
|
||||
static void check_fix_formatted_text(td::string str, td::vector<td::MessageEntity> entities, bool allow_empty,
|
||||
bool skip_new_entities, bool skip_bot_commands, bool for_draft) {
|
||||
ASSERT_TRUE(
|
||||
fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_error());
|
||||
ASSERT_TRUE(td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, true, for_draft)
|
||||
.is_error());
|
||||
}
|
||||
|
||||
TEST(MessageEntities, fix_formatted_text) {
|
||||
@ -1064,7 +1108,7 @@ TEST(MessageEntities, fix_formatted_text) {
|
||||
return result;
|
||||
};
|
||||
auto old_type_mask = get_type_mask(str.size(), entities);
|
||||
ASSERT_TRUE(td::fix_formatted_text(str, entities, false, false, true, false).is_ok());
|
||||
ASSERT_TRUE(td::fix_formatted_text(str, entities, false, false, true, true, false).is_ok());
|
||||
auto new_type_mask = get_type_mask(str.size(), entities);
|
||||
auto splittable_mask = (1 << 5) | (1 << 6) | (1 << 14) | (1 << 15);
|
||||
auto pre_mask = (1 << 7) | (1 << 8) | (1 << 9);
|
||||
@ -1384,7 +1428,7 @@ static void check_parse_markdown_v3(td::string text, td::vector<td::MessageEntit
|
||||
bool fix = false) {
|
||||
auto parsed_text = td::parse_markdown_v3({std::move(text), std::move(entities)});
|
||||
if (fix) {
|
||||
ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok());
|
||||
ASSERT_TRUE(td::fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).is_ok());
|
||||
}
|
||||
ASSERT_STREQ(result_text, parsed_text.text);
|
||||
ASSERT_EQ(result_entities, parsed_text.entities);
|
||||
@ -1676,9 +1720,9 @@ TEST(MessageEntities, parse_markdown_v3) {
|
||||
|
||||
td::FormattedText text{std::move(str), std::move(entities)};
|
||||
while (true) {
|
||||
ASSERT_TRUE(fix_formatted_text(text.text, text.entities, true, true, true, true).is_ok());
|
||||
ASSERT_TRUE(td::fix_formatted_text(text.text, text.entities, true, true, true, true, true).is_ok());
|
||||
auto parsed_text = td::parse_markdown_v3(text);
|
||||
ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok());
|
||||
ASSERT_TRUE(td::fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).is_ok());
|
||||
if (parsed_text == text) {
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user