diff --git a/CMakeLists.txt b/CMakeLists.txt index b96a3a7ea..c37c07f0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TDLib VERSION 1.7.8 LANGUAGES CXX C) +project(TDLib VERSION 1.7.9 LANGUAGES CXX C) if (NOT DEFINED CMAKE_MODULE_PATH) set(CMAKE_MODULE_PATH "") diff --git a/README.md b/README.md index 854716390..879618890 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic) Or you could install `TDLib` and then reference it in your CMakeLists.txt like this: ``` -find_package(Td 1.7.8 REQUIRED) +find_package(Td 1.7.9 REQUIRED) target_link_libraries(YourTarget PRIVATE Td::TdStatic) ``` See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/tree/master/example/cpp/CMakeLists.txt). diff --git a/benchmark/bench_actor.cpp b/benchmark/bench_actor.cpp index 338442c62..82c88cfdd 100644 --- a/benchmark/bench_actor.cpp +++ b/benchmark/bench_actor.cpp @@ -43,7 +43,6 @@ class td::ActorTraits { }; class CreateActorBench final : public td::Benchmark { - private: td::ConcurrentScheduler scheduler_; void start_up() final { diff --git a/benchmark/bench_http.cpp b/benchmark/bench_http.cpp index 5fc8ba1f3..9e468f025 100644 --- a/benchmark/bench_http.cpp +++ b/benchmark/bench_http.cpp @@ -12,6 +12,7 @@ #include "td/actor/ConcurrentScheduler.h" #include "td/utils/buffer.h" +#include "td/utils/BufferedFd.h" #include "td/utils/logging.h" #include "td/utils/port/IPAddress.h" #include "td/utils/port/SocketFd.h" diff --git a/benchmark/bench_http_server.cpp b/benchmark/bench_http_server.cpp index 4351f03a5..88fef3814 100644 --- a/benchmark/bench_http_server.cpp +++ b/benchmark/bench_http_server.cpp @@ -13,6 +13,7 @@ #include "td/actor/ConcurrentScheduler.h" #include "td/utils/buffer.h" +#include "td/utils/BufferedFd.h" #include "td/utils/logging.h" #include "td/utils/port/SocketFd.h" #include "td/utils/Slice.h" diff --git a/benchmark/bench_queue.cpp b/benchmark/bench_queue.cpp index 2fc85bbf9..6205f37bb 100644 --- a/benchmark/bench_queue.cpp +++ b/benchmark/bench_queue.cpp @@ -17,8 +17,6 @@ // TODO: all return values must be checked #include -#include -#include #if TD_PORT_POSIX #include diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index b3c6878a0..585ba56b4 100644 --- a/example/cpp/CMakeLists.txt +++ b/example/cpp/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4 FATAL_ERROR) project(TdExample VERSION 1.0 LANGUAGES CXX) -find_package(Td 1.7.8 REQUIRED) +find_package(Td 1.7.9 REQUIRED) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs index 3b18f1dd9..39afc0ab4 100644 --- a/example/csharp/TdExample.cs +++ b/example/csharp/TdExample.cs @@ -34,13 +34,7 @@ namespace TdExample private static Td.Client CreateTdClient() { - Td.Client result = Td.Client.Create(new UpdateHandler()); - new Thread(() => - { - Thread.CurrentThread.IsBackground = true; - result.Run(); - }).Start(); - return result; + return Td.Client.Create(new UpdateHandler()); } private static void Print(string str) @@ -133,7 +127,6 @@ namespace TdExample else if (_authorizationState is TdApi.AuthorizationStateClosed) { Print("Closed"); - _client.Dispose(); // _client is closed and native resources can be disposed now if (!_needQuit) { _client = CreateTdClient(); // recreate _client after previous has closed @@ -223,6 +216,11 @@ namespace TdExample { throw new System.IO.IOException("Write access to the current directory is required"); } + new Thread(() => + { + Thread.CurrentThread.IsBackground = true; + Td.Client.Run(); + }).Start(); // create Td.Client _client = CreateTdClient(); diff --git a/example/uwp/app/MainPage.xaml.cs b/example/uwp/app/MainPage.xaml.cs index 2036150a2..7b843ec9f 100644 --- a/example/uwp/app/MainPage.xaml.cs +++ b/example/uwp/app/MainPage.xaml.cs @@ -30,30 +30,23 @@ namespace TdApp Td.Client.Execute(new TdApi.SetLogVerbosityLevel(0)); Td.Client.Execute(new TdApi.SetLogStream(new TdApi.LogStreamFile(Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "log"), 1 << 27, false))); Td.Client.SetLogMessageCallback(100, LogMessageCallback); - System.Threading.Tasks.Task.Run(() => { - try - { - _client = Td.Client.Create(_handler); - var parameters = new TdApi.TdlibParameters(); - parameters.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path; - parameters.UseSecretChats = true; - parameters.UseMessageDatabase = true; - parameters.ApiId = 94575; - parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2"; - parameters.SystemLanguageCode = "en"; - parameters.DeviceModel = "Desktop"; - parameters.ApplicationVersion = "1.0.0"; - _client.Send(new TdApi.SetTdlibParameters(parameters), null); - _client.Send(new TdApi.CheckDatabaseEncryptionKey(), null); - _client.Run(); - } - catch (Exception ex) - { - Print(ex.ToString()); - } + Td.Client.Run(); }); + + _client = Td.Client.Create(_handler); + var parameters = new TdApi.TdlibParameters(); + parameters.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path; + parameters.UseSecretChats = true; + parameters.UseMessageDatabase = true; + parameters.ApiId = 94575; + parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters.SystemLanguageCode = "en"; + parameters.DeviceModel = "Desktop"; + parameters.ApplicationVersion = "1.0.0"; + _client.Send(new TdApi.SetTdlibParameters(parameters), null); + _client.Send(new TdApi.CheckDatabaseEncryptionKey(), null); } public void Print(String str) diff --git a/example/uwp/extension.vsixmanifest b/example/uwp/extension.vsixmanifest index 22e55808c..6152c7428 100644 --- a/example/uwp/extension.vsixmanifest +++ b/example/uwp/extension.vsixmanifest @@ -1,6 +1,6 @@ - + TDLib for Universal Windows Platform TDLib is a library for building Telegram clients https://core.telegram.org/tdlib diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index d2a68ba45..0aab6e946 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -56,7 +56,7 @@ authenticationCodeTypeCall length:int32 = AuthenticationCodeType; authenticationCodeTypeFlashCall pattern:string = AuthenticationCodeType; -//@description Information about the authentication code that was sent @phone_number A phone number that is being authenticated @type Describes the way the code was sent to the user @next_type Describes the way the next code will be sent to the user; may be null @timeout Timeout before the code can be re-sent, in seconds +//@description Information about the authentication code that was sent @phone_number A phone number that is being authenticated @type The way the code was sent to the user @next_type The way the next code will be sent to the user; may be null @timeout Timeout before the code can be re-sent, in seconds authenticationCodeInfo phone_number:string type:AuthenticationCodeType next_type:AuthenticationCodeType timeout:int32 = AuthenticationCodeInfo; //@description Information about the email address authentication code that was sent @email_address_pattern Pattern of the email address to which an authentication code was sent @length Length of the code; 0 if unknown @@ -234,6 +234,10 @@ maskPointChin = MaskPoint; maskPosition point:MaskPoint x_shift:double y_shift:double scale:double = MaskPosition; +//@description Describes a color replacement for animated emoji @old_color Original animated emoji color in the RGB24 format @new_color Replacement animated emoji color in the RGB24 format +colorReplacement old_color:int32 new_color:int32 = ColorReplacement; + + //@description Represents a closed vector path. The path begins at the end point of the last command @commands List of vector path commands closedVectorPath commands:vector = ClosedVectorPath; @@ -294,6 +298,12 @@ videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:thum //@waveform A waveform representation of the voice note in 5-bit format @mime_type MIME type of the file; as defined by the sender @voice File containing the voice note voiceNote duration:int32 waveform:bytes mime_type:string voice:file = VoiceNote; +//@description Describes an animated representation of an emoji +//@sticker Animated sticker for the emoji +//@color_replacements List of colors to be replaced while the sticker is rendered +//@sound File containing the sound to be played when the animated emoji is clicked if any; may be null. The sound is encoded with the Opus codec, and stored inside an OGG container +animatedEmoji sticker:sticker color_replacements:vector sound:file = AnimatedEmoji; + //@description Describes a user contact @phone_number Phone number of the user @first_name First name of the user; 1-255 characters in length @last_name Last name of the user @vcard Additional data about the user in a form of vCard; 0-2048 bytes in length @user_id Identifier of the user, if known; otherwise 0 contact phone_number:string first_name:string last_name:string vcard:string user_id:int53 = Contact; @@ -471,7 +481,7 @@ chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_pol //@is_member True, if the user is a member of the chat chatMemberStatusCreator custom_title:string is_anonymous:Bool is_member:Bool = ChatMemberStatus; -//@description The user is a member of the chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, ban unprivileged members, and manage voice chats. In supergroups and channels, there are more detailed options for administrator privileges +//@description The user is a member of the chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, ban unprivileged members, and manage video chats. In supergroups and channels, there are more detailed options for administrator privileges //@custom_title A custom title of the administrator; 0-16 characters without emojis; applicable to supergroups only //@can_be_edited True, if the current user can edit the administrator privileges for the called user //@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only @@ -483,9 +493,9 @@ chatMemberStatusCreator custom_title:string is_anonymous:Bool is_member:Bool = C //@can_restrict_members True, if the administrator can restrict, ban, or unban chat members; always true for channels //@can_pin_messages True, if the administrator can pin messages; applicable to basic groups and supergroups only //@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them -//@can_manage_voice_chats True, if the administrator can manage voice chats +//@can_manage_video_chats True, if the administrator can manage video chats //@is_anonymous True, if the administrator isn't shown in the chat member list and sends messages anonymously; applicable to supergroups only -chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_voice_chats:Bool is_anonymous:Bool = ChatMemberStatus; +chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatMemberStatus; //@description The user is a member of the chat, without any additional privileges or restrictions chatMemberStatusMember = ChatMemberStatus; @@ -499,12 +509,12 @@ chatMemberStatusRestricted is_member:Bool restricted_until_date:int32 permission //@description The user or the chat is not a chat member chatMemberStatusLeft = ChatMemberStatus; -//@description The user or the chat was banned (and hence is not a member of the chat). Implies the user can't return to the chat, view messages, or be used as a participant identifier to join a voice chat of the chat +//@description The user or the chat was banned (and hence is not a member of the chat). Implies the user can't return to the chat, view messages, or be used as a participant identifier to join a video chat of the chat //@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Always 0 in basic groups chatMemberStatusBanned banned_until_date:int32 = ChatMemberStatus; -//@description Information about a user or a chat as a member of another chat +//@description Describes a user or a chat as a member of another chat //@member_id Identifier of the chat member. Currently, other chats can be only Left or Banned. Only supergroups and channels can have other chats as Left or Banned members and these chats must be supergroups or channels //@inviter_user_id Identifier of a user that invited/promoted/banned this member in the chat; 0 if unknown //@joined_chat_date Point in time (Unix timestamp) when the user joined the chat @@ -566,15 +576,20 @@ supergroupMembersFilterMention query:string message_thread_id:int53 = Supergroup supergroupMembersFilterBots = SupergroupMembersFilter; -//@description Contains a chat invite link @invite_link Chat invite link @creator_user_id User identifier of an administrator created the link +//@description Contains a chat invite link +//@invite_link Chat invite link +//@name Name of the link +//@creator_user_id User identifier of an administrator created the link //@date Point in time (Unix timestamp) when the link was created //@edit_date Point in time (Unix timestamp) when the link was last edited; 0 if never or unknown //@expire_date Point in time (Unix timestamp) when the link will expire; 0 if never -//@member_limit The maximum number of members, which can join the chat using the link simultaneously; 0 if not limited +//@member_limit The maximum number of members, which can join the chat using the link simultaneously; 0 if not limited. Always 0 if the link requires approval //@member_count Number of chat members, which joined the chat using the link -//@is_primary True, if the link is primary. Primary invite link can't have expire date or usage limit. There is exactly one primary invite link for each administrator with can_invite_users right at a given time +//@pending_join_request_count Number of pending join requests created using this link +//@creates_join_request True, if the link only creates join request. If true, total number of joining members will be unlimited +//@is_primary True, if the link is primary. Primary invite link can't have name, expire date or usage limit. There is exactly one primary invite link for each administrator with can_invite_users right at a given time //@is_revoked True, if the link was revoked -chatInviteLink invite_link:string creator_user_id:int53 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 is_primary:Bool is_revoked:Bool = ChatInviteLink; +chatInviteLink invite_link:string name:string creator_user_id:int53 date:int32 edit_date:int32 expire_date:int32 member_limit:int32 member_count:int32 pending_join_request_count:int32 creates_join_request:Bool is_primary:Bool is_revoked:Bool = ChatInviteLink; //@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links chatInviteLinks total_count:int32 invite_links:vector = ChatInviteLinks; @@ -588,8 +603,8 @@ chatInviteLinkCount user_id:int53 invite_link_count:int32 revoked_invite_link_co //@description Contains a list of chat invite link counts @invite_link_counts List of invite linkcounts chatInviteLinkCounts invite_link_counts:vector = ChatInviteLinkCounts; -//@description Describes a chat member joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat -chatInviteLinkMember user_id:int53 joined_chat_date:int32 = ChatInviteLinkMember; +//@description Describes a chat member joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat @approver_user_id User identifier of the chat administrator, approved user join request +chatInviteLinkMember user_id:int53 joined_chat_date:int32 approver_user_id:int53 = ChatInviteLinkMember; //@description Contains a list of chat members joined a chat by an invite link @total_count Approximate total count of chat members found @members List of chat members, joined a chat by an invite link chatInviteLinkMembers total_count:int32 members:vector = ChatInviteLinkMembers; @@ -597,13 +612,24 @@ chatInviteLinkMembers total_count:int32 members:vector = C //@description Contains information about a chat invite link //@chat_id Chat identifier of the invite link; 0 if the user has no access to the chat before joining //@accessible_for If non-zero, the amount of time for which read access to the chat will remain available, in seconds -//@type Contains information about the type of the chat +//@type Type of the chat //@title Title of the chat //@photo Chat photo; may be null +//@param_description Chat description //@member_count Number of members in the chat //@member_user_ids User identifiers of some chat members that may be known to the current user +//@creates_join_request True, if the link only creates join request //@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup -chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; +chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo description:string member_count:int32 member_user_ids:vector creates_join_request:Bool is_public:Bool = ChatInviteLinkInfo; + +//@description Describes a user that sent a join request and waits for administrator approval @user_id User identifier @date Point in time (Unix timestamp) when the user sent the join request @bio A short bio of the user +chatJoinRequest user_id:int53 date:int32 bio:string = ChatJoinRequest; + +//@description Contains a list of chat join requests @total_count Approximate total count of requests found @requests List of the requests +chatJoinRequests total_count:int32 requests:vector = ChatJoinRequests; + +//@description Contains information about pending chat join requests @total_count Total number of pending join requests @user_ids Identifiers of users sent the newest pending join requests +chatJoinRequestsInfo total_count:int32 user_ids:vector = ChatJoinRequestsInfo; //@description Represents a basic group of 0-200 users (must be upgraded to a supergroup to accommodate more than 200 users) @@ -745,7 +771,7 @@ messageReplyInfo reply_count:int32 recent_repliers:vector last_re //@description Contains information about interactions with a message //@view_count Number of times the message was viewed //@forward_count Number of times the message was forwarded -//@reply_info Contains information about direct or indirect replies to the message; may be null. Currently, available only in channels with a discussion supergroup and discussion supergroups for messages, which are not replies itself +//@reply_info Information about direct or indirect replies to the message; may be null. Currently, available only in channels with a discussion supergroup and discussion supergroups for messages, which are not replies itself messageInteractionInfo view_count:int32 forward_count:int32 reply_info:messageReplyInfo = MessageInteractionInfo; @@ -763,8 +789,8 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r //@id Message identifier; unique for the chat to which the message belongs //@sender The sender of the message //@chat_id Chat identifier -//@sending_state Information about the sending state of the message; may be null -//@scheduling_state Information about the scheduling state of the message; may be null +//@sending_state The sending state of the message; may be null +//@scheduling_state The scheduling state of the message; may be null //@is_outgoing True, if the message is outgoing //@is_pinned True, if the message is pinned //@can_be_edited True, if the message can be edited. For live location and poll messages this fields shows whether editMessageLiveLocation or stopPoll can be used with this message by the application @@ -801,6 +827,18 @@ messages total_count:int32 messages:vector = Messages; //@description Contains a list of messages found by a search @total_count Approximate total count of messages found; -1 if unknown @messages List of messages @next_offset The offset for the next request. If empty, there are no more results foundMessages total_count:int32 messages:vector next_offset:string = FoundMessages; +//@description Contains information about a message in a specific position @position 0-based message position in the full list of suitable messages @message_id Message identifier @date Point in time (Unix timestamp) when the message was sent +messagePosition position:int32 message_id:int53 date:int32 = MessagePosition; + +//@description Contains a list of message positions @total_count Total count of messages found @positions List of message positions +messagePositions total_count:int32 positions:vector = MessagePositions; + +//@description Contains information about found messages sent in a specific day @total_count Total number of found messages sent in the day @message First message sent in the day +messageCalendarDay total_count:int32 message:message = MessageCalendarDay; + +//@description Contains information about found messages, splitted by days according to the option "utc_time_offset" @total_count Total number of found messages @days Information about messages sent +messageCalendar total_count:int32 days:vector = MessageCalendar; + //@description Describes a sponsored message @id Unique sponsored message identifier @sponsor_chat_id Chat identifier //@link An internal link to be opened when the sponsored message is clicked; may be null. If null, the sponsor chat needs to be opened instead @content Content of the message @@ -923,11 +961,11 @@ chatSourcePublicServiceAnnouncement type:string text:string = ChatSource; chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPosition; -//@description Describes a voice chat -//@group_call_id Group call identifier of an active voice chat; 0 if none. Full information about the voice chat can be received through the method getGroupCall -//@has_participants True, if the voice chat has participants -//@default_participant_id Default group call participant identifier to join the voice chat; may be null -voiceChat group_call_id:int32 has_participants:Bool default_participant_id:MessageSender = VoiceChat; +//@description Describes a video chat +//@group_call_id Group call identifier of an active video chat; 0 if none. Full information about the video chat can be received through the method getGroupCall +//@has_participants True, if the video chat has participants +//@default_participant_id Default group call participant identifier to join the video chat; may be null +videoChat group_call_id:int32 has_participants:Bool default_participant_id:MessageSender = VideoChat; //@description A chat. (Can be a private chat, basic group, supergroup, or secret chat) @@ -952,12 +990,13 @@ voiceChat group_call_id:int32 has_participants:Bool default_participant_id:Messa //@notification_settings Notification settings for this chat //@message_ttl_setting Current message Time To Live setting (self-destruct timer) for the chat; 0 if not defined. TTL is counted from the time message or its content is viewed in secret chats and from the send date in other chats //@theme_name If non-empty, name of a theme, set for the chat -//@action_bar Describes actions which must be possible to do through a chat action bar; may be null -//@voice_chat Contains information about voice chat of the chat +//@action_bar Information about actions which must be possible to do through the chat action bar; may be null +//@video_chat Information about video chat of the chat +//@pending_join_requests Information about pending join requests; may be null //@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat //@draft_message A draft of a message in the chat; may be null -//@client_data Contains application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used -chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings message_ttl_setting:int32 theme_name:string action_bar:ChatActionBar voice_chat:voiceChat reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; +//@client_data Application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used +chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings message_ttl_setting:int32 theme_name:string action_bar:ChatActionBar video_chat:videoChat pending_join_requests:chatJoinRequestsInfo reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; //@description Represents a list of chats @total_count Approximate total count of chats found @chat_ids List of chat identifiers chats total_count:int32 chat_ids:vector = Chats; @@ -1087,7 +1126,7 @@ loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int53 reque //@description Contains information about a message thread //@chat_id Identifier of the chat to which the message thread belongs //@message_thread_id Message thread identifier, unique within the chat -//@reply_info Contains information about the message thread +//@reply_info Information about the message thread //@unread_message_count Approximate number of unread messages in the message thread //@messages The messages from which the thread starts. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id) //@draft_message A draft of a message in the message thread; may be null @@ -1373,7 +1412,7 @@ savedCredentials id:string title:string = SavedCredentials; //@description Applies if a user chooses some previously saved payment credentials. To use their previously saved credentials, the user must have a valid temporary password @saved_credentials_id Identifier of the saved credentials inputCredentialsSaved saved_credentials_id:string = InputCredentials; -//@description Applies if a user enters new credentials on a payment provider website @data Contains JSON-encoded data with a credential identifier from the payment provider @allow_save True, if the credential identifier can be saved on the server side +//@description Applies if a user enters new credentials on a payment provider website @data JSON-encoded data with the credential identifier from the payment provider @allow_save True, if the credential identifier can be saved on the server side inputCredentialsNew data:string allow_save:Bool = InputCredentials; //@description Applies if a user enters new credentials using Apple Pay @data JSON-encoded data with the credential identifier @@ -1396,9 +1435,9 @@ paymentFormTheme background_color:int32 text_color:int32 hint_color:int32 link_c //@url Payment form URL //@seller_bot_user_id User identifier of the seller bot //@payments_provider_user_id User identifier of the payment provider bot -//@payments_provider Contains information about the payment provider, if available, to support it natively without the need for opening the URL; may be null +//@payments_provider Information about the payment provider, if available, to support it natively without the need for opening the URL; may be null //@saved_order_info Saved server-side order information; may be null -//@saved_credentials Contains information about saved card credentials; may be null +//@saved_credentials Information about saved card credentials; may be null //@can_save_credentials True, if the user can choose to save credentials //@need_password True, if the user will be able to save credentials protected by a password they set up paymentForm id:int64 invoice:invoice url:string seller_bot_user_id:int53 payments_provider_user_id:int53 payments_provider:paymentsProviderStripe saved_order_info:orderInfo saved_credentials:savedCredentials can_save_credentials:Bool need_password:Bool = PaymentForm; @@ -1416,7 +1455,7 @@ paymentResult success:Bool verification_url:string = PaymentResult; //@date Point in time (Unix timestamp) when the payment was made //@seller_bot_user_id User identifier of the seller bot //@payments_provider_user_id User identifier of the payment provider bot -//@invoice Contains information about the invoice +//@invoice Information about the invoice //@order_info Order information; may be null //@shipping_option Chosen shipping option; may be null //@credentials_title Title of the saved credentials chosen by the buyer @@ -1624,7 +1663,7 @@ passportSuitableElement type:PassportElementType is_selfie_required:Bool is_tran passportRequiredElement suitable_elements:vector = PassportRequiredElement; //@description Contains information about a Telegram Passport authorization form that was requested @id Unique identifier of the authorization form -//@required_elements Information about the Telegram Passport elements that must be provided to complete the form +//@required_elements Telegram Passport elements that must be provided to complete the form //@privacy_policy_url URL for the privacy policy of the service; may be empty passportAuthorizationForm id:int32 required_elements:vector privacy_policy_url:string = PassportAuthorizationForm; @@ -1721,6 +1760,9 @@ messageVenue venue:venue = MessageContent; //@description A message with a user contact @contact The contact description messageContact contact:contact = MessageContent; +//@description A message with an animated emoji @animated_emoji The animated emoji @emoji The corresponding emoji +messageAnimatedEmoji animated_emoji:animatedEmoji emoji:string = MessageContent; + //@description A dice message. The dice value is randomly generated by the server //@initial_state The animated stickers with the initial dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known //@final_state The animated stickers with the final dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known @@ -1743,17 +1785,17 @@ messageInvoice title:string description:string photo:photo currency:string total //@description A message with information about an ended call @is_video True, if the call was a video call @discard_reason Reason why the call was discarded @duration Call duration, in seconds messageCall is_video:Bool discard_reason:CallDiscardReason duration:int32 = MessageContent; -//@description A new voice chat was scheduled @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall @start_date Point in time (Unix timestamp) when the group call is supposed to be started by an administrator -messageVoiceChatScheduled group_call_id:int32 start_date:int32 = MessageContent; +//@description A new video chat was scheduled @group_call_id Identifier of the video chat. The video chat can be received through the method getGroupCall @start_date Point in time (Unix timestamp) when the group call is supposed to be started by an administrator +messageVideoChatScheduled group_call_id:int32 start_date:int32 = MessageContent; -//@description A newly created voice chat @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall -messageVoiceChatStarted group_call_id:int32 = MessageContent; +//@description A newly created video chat @group_call_id Identifier of the video chat. The video chat can be received through the method getGroupCall +messageVideoChatStarted group_call_id:int32 = MessageContent; -//@description A message with information about an ended voice chat @duration Call duration, in seconds -messageVoiceChatEnded duration:int32 = MessageContent; +//@description A message with information about an ended video chat @duration Call duration, in seconds +messageVideoChatEnded duration:int32 = MessageContent; -//@description A message with information about an invite to a voice chat @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall @user_ids Invited user identifiers -messageInviteVoiceChatParticipants group_call_id:int32 user_ids:vector = MessageContent; +//@description A message with information about an invite to a video chat @group_call_id Identifier of the video chat. The video chat can be received through the method getGroupCall @user_ids Invited user identifiers +messageInviteVideoChatParticipants group_call_id:int32 user_ids:vector = MessageContent; //@description A newly created basic group @title Title of the basic group @member_user_ids User identifiers of members in the basic group messageBasicGroupChatCreate title:string member_user_ids:vector = MessageContent; @@ -1776,6 +1818,9 @@ messageChatAddMembers member_user_ids:vector = MessageContent; //@description A new member joined the chat by invite link messageChatJoinByLink = MessageContent; +//@description A new member was accepted to the chat by an administrator +messageChatJoinByRequest = MessageContent; + //@description A chat member was deleted @user_id User identifier of the deleted chat member messageChatDeleteMember user_id:int53 = MessageContent; @@ -2132,7 +2177,7 @@ stickerSet id:int64 title:string name:string thumbnail:thumbnail thumbnail_outli //@thumbnail_outline Sticker set thumbnail's outline represented as a list of closed vector paths; may be empty. The coordinate system origin is in the upper-left corner //@is_installed True, if the sticker set has been installed by the current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously //@is_official True, if the sticker set is official @is_animated True, is the stickers in the set are animated @is_masks True, if the stickers in the set are masks @is_viewed True for already viewed trending sticker sets -//@size Total number of stickers in the set @covers Contains up to the first 5 stickers from the set, depending on the context. If the application needs more stickers the full sticker set needs to be requested +//@size Total number of stickers in the set @covers Up to the first 5 stickers from the set, depending on the context. If the application needs more stickers the full sticker set needs to be requested stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail thumbnail_outline:vector is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool size:int32 covers:vector = StickerSetInfo; //@description Represents a list of sticker sets @total_count Approximate total number of sticker sets found @sets List of sticker sets @@ -2511,6 +2556,9 @@ chatEventMemberJoined = ChatEventAction; //@description A new member joined the chat by an invite link @invite_link Invite link used to join the chat chatEventMemberJoinedByInviteLink invite_link:chatInviteLink = ChatEventAction; +//@description A new member was accepted to the chat by an administrator @approver_user_id User identifier of the chat administrator, approved user join request @invite_link Invite link used to join the chat; may be null +chatEventMemberJoinedByRequest approver_user_id:int53 invite_link:chatInviteLink = ChatEventAction; + //@description A member left the chat chatEventMemberLeft = ChatEventAction; @@ -2571,20 +2619,20 @@ chatEventInviteLinkRevoked invite_link:chatInviteLink = ChatEventAction; //@description A revoked chat invite link was deleted @invite_link The invite link chatEventInviteLinkDeleted invite_link:chatInviteLink = ChatEventAction; -//@description A voice chat was created @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall -chatEventVoiceChatCreated group_call_id:int32 = ChatEventAction; +//@description A video chat was created @group_call_id Identifier of the video chat. The video chat can be received through the method getGroupCall +chatEventVideoChatCreated group_call_id:int32 = ChatEventAction; -//@description A voice chat was discarded @group_call_id Identifier of the voice chat. The voice chat can be received through the method getGroupCall -chatEventVoiceChatDiscarded group_call_id:int32 = ChatEventAction; +//@description A video chat was discarded @group_call_id Identifier of the video chat. The video chat can be received through the method getGroupCall +chatEventVideoChatDiscarded group_call_id:int32 = ChatEventAction; -//@description A voice chat participant was muted or unmuted @participant_id Identifier of the affected group call participant @is_muted New value of is_muted -chatEventVoiceChatParticipantIsMutedToggled participant_id:MessageSender is_muted:Bool = ChatEventAction; +//@description A video chat participant was muted or unmuted @participant_id Identifier of the affected group call participant @is_muted New value of is_muted +chatEventVideoChatParticipantIsMutedToggled participant_id:MessageSender is_muted:Bool = ChatEventAction; -//@description A voice chat participant volume level was changed @participant_id Identifier of the affected group call participant @volume_level New value of volume_level; 1-20000 in hundreds of percents -chatEventVoiceChatParticipantVolumeLevelChanged participant_id:MessageSender volume_level:int32 = ChatEventAction; +//@description A video chat participant volume level was changed @participant_id Identifier of the affected group call participant @volume_level New value of volume_level; 1-20000 in hundreds of percents +chatEventVideoChatParticipantVolumeLevelChanged participant_id:MessageSender volume_level:int32 = ChatEventAction; -//@description The mute_new_participants setting of a voice chat was toggled @mute_new_participants New value of the mute_new_participants setting -chatEventVoiceChatMuteNewParticipantsToggled mute_new_participants:Bool = ChatEventAction; +//@description The mute_new_participants setting of a video chat was toggled @mute_new_participants New value of the mute_new_participants setting +chatEventVideoChatMuteNewParticipantsToggled mute_new_participants:Bool = ChatEventAction; //@description Represents a chat event @id Chat event identifier @date Point in time (Unix timestamp) when the event happened @user_id Identifier of the user who performed the action that triggered the event @action Action performed by the user chatEvent id:int64 date:int32 user_id:int53 action:ChatEventAction = ChatEvent; @@ -2604,8 +2652,8 @@ chatEvents events:vector = ChatEvents; //@info_changes True, if changes in chat information need to be returned //@setting_changes True, if changes in chat settings need to be returned //@invite_link_changes True, if changes to invite links need to be returned -//@voice_chat_changes True, if voice chat actions need to be returned -chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool invite_link_changes:Bool voice_chat_changes:Bool = ChatEventLogFilters; +//@video_chat_changes True, if video chat actions need to be returned +chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool invite_link_changes:Bool video_chat_changes:Bool = ChatEventLogFilters; //@class LanguagePackStringValue @description Represents the value of a string in a language pack @@ -2704,13 +2752,13 @@ backgroundFillFreeformGradient colors:vector = BackgroundFill; backgroundTypeWallpaper is_blurred:Bool is_moving:Bool = BackgroundType; //@description A PNG or TGV (gzipped subset of SVG with MIME type "application/x-tgwallpattern") pattern to be combined with the background fill chosen by the user -//@fill Description of the background fill +//@fill Fill of the background //@intensity Intensity of the pattern when it is shown above the filled background; 0-100. //@is_inverted True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only //@is_moving True, if the background needs to be slightly moved when device is tilted backgroundTypePattern fill:BackgroundFill intensity:int32 is_inverted:Bool is_moving:Bool = BackgroundType; -//@description A filled background @fill Description of the background fill +//@description A filled background @fill The background fill backgroundTypeFill fill:BackgroundFill = BackgroundType; @@ -2905,6 +2953,9 @@ pushMessageContentChatDeleteMember member_name:string is_current_user:Bool is_le //@description A new member joined the chat by invite link pushMessageContentChatJoinByLink = PushMessageContent; +//@description A new member was accepted to the chat by an administrator +pushMessageContentChatJoinByRequest = PushMessageContent; + //@description A forwarded messages @total_count Number of forwarded messages pushMessageContentMessageForwards total_count:int32 = PushMessageContent; @@ -3194,10 +3245,13 @@ internalLinkTypeThemeSettings = InternalLinkType; //@description The link is an unknown tg: link. Call getDeepLinkInfo to process the link @link Link to be passed to getDeepLinkInfo internalLinkTypeUnknownDeepLink link:string = InternalLinkType; -//@description The link is a link to a voice chat. Call searchPublicChat with the given chat username, and then joinGoupCall with the given invite hash to process the link -//@chat_username Username of the chat with the voice chat @invite_hash If non-empty, invite hash to be used to join the voice chat without being muted by administrators -//@is_live_stream True, if the voice chat is expected to be a live stream in a channel or a broadcast group -internalLinkTypeVoiceChat chat_username:string invite_hash:string is_live_stream:Bool = InternalLinkType; +//@description The link is a link to an unsupported proxy. An alert can be shown to the user +internalLinkTypeUnsupportedProxy = InternalLinkType; + +//@description The link is a link to a video chat. Call searchPublicChat with the given chat username, and then joinGoupCall with the given invite hash to process the link +//@chat_username Username of the chat with the video chat @invite_hash If non-empty, invite hash to be used to join the video chat without being muted by administrators +//@is_live_stream True, if the video chat is expected to be a live stream in a channel or a broadcast group +internalLinkTypeVideoChat chat_username:string invite_hash:string is_live_stream:Bool = InternalLinkType; //@description Contains an HTTPS link to a message in a supergroup or channel @link Message link @is_public True, if the link will work for non-members of the chat @@ -3621,11 +3675,11 @@ updateNewMessage message:message = Update; //@chat_id The chat identifier of the sent message @message_id A temporary message identifier updateMessageSendAcknowledged chat_id:int53 message_id:int53 = Update; -//@description A message has been successfully sent @message Information about the sent message. Usually only the message identifier, date, and content are changed, but almost all other fields can also change @old_message_id The previous temporary message identifier +//@description A message has been successfully sent @message The sent message. Usually only the message identifier, date, and content are changed, but almost all other fields can also change @old_message_id The previous temporary message identifier updateMessageSendSucceeded message:message old_message_id:int53 = Update; //@description A message failed to send. Be aware that some messages being sent can be irrecoverably deleted, in which case updateDeleteMessages will be received instead of this update -//@message Contains information about the message which failed to send @old_message_id The previous temporary message identifier @error_code An error code @error_message Error message +//@message The failed to send message @old_message_id The previous temporary message identifier @error_code An error code @error_message Error message updateMessageSendFailed message:message old_message_id:int53 error_code:int32 error_message:string = Update; //@description The message content has changed @chat_id Chat identifier @message_id Message identifier @new_content New message content @@ -3677,8 +3731,8 @@ updateChatIsBlocked chat_id:int53 is_blocked:Bool = Update; //@description A chat's has_scheduled_messages field has changed @chat_id Chat identifier @has_scheduled_messages New value of has_scheduled_messages updateChatHasScheduledMessages chat_id:int53 has_scheduled_messages:Bool = Update; -//@description A chat voice chat state has changed @chat_id Chat identifier @voice_chat New value of voice_chat -updateChatVoiceChat chat_id:int53 voice_chat:voiceChat = Update; +//@description A chat video chat state has changed @chat_id Chat identifier @video_chat New value of video_chat +updateChatVideoChat chat_id:int53 video_chat:videoChat = Update; //@description The value of the default disable_notification parameter, used when a message is sent to the chat, was changed @chat_id Chat identifier @default_disable_notification The new default_disable_notification value updateChatDefaultDisableNotification chat_id:int53 default_disable_notification:Bool = Update; @@ -3707,6 +3761,9 @@ updateChatActionBar chat_id:int53 action_bar:ChatActionBar = Update; //@description The chat theme was changed @chat_id Chat identifier @theme_name The new name of the chat theme; may be empty if theme was reset to default updateChatTheme chat_id:int53 theme_name:string = Update; +//@description The chat pending join requests were changed @chat_id Chat identifier @pending_join_requests The new data about pending join requests; may be null +updateChatPendingJoinRequests chat_id:int53 pending_join_requests:chatJoinRequestsInfo = Update; + //@description The default chat reply markup was changed. Can occur because new messages with reply markup were received or because an old reply markup was hidden by the user //@chat_id Chat identifier @reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat updateChatReplyMarkup chat_id:int53 reply_markup_message_id:int53 = Update; @@ -3874,7 +3931,7 @@ updateAnimationSearchParameters provider:string emojis:vector = Update; updateSuggestedActions added_actions:vector removed_actions:vector = Update; //@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location; may be null -//@chat_type Contains information about the type of the chat, from which the query originated; may be null if unknown @query Text of the query @offset Offset of the first entry to return +//@chat_type The type of the chat, from which the query originated; may be null if unknown @query Text of the query @offset Offset of the first entry to return updateNewInlineQuery id:int64 sender_user_id:int53 user_location:location chat_type:ChatType query:string offset:string = Update; //@description The user has chosen a result of an inline query; for bots only @sender_user_id Identifier of the user who sent the query @user_location User location; may be null @@ -3914,6 +3971,9 @@ updatePollAnswer poll_id:int64 user_id:int53 option_ids:vector = Update; //@old_chat_member Previous chat member @new_chat_member New chat member updateChatMember chat_id:int53 actor_user_id:int53 date:int32 invite_link:chatInviteLink old_chat_member:chatMember new_chat_member:chatMember = Update; +//@description A user sent a join request to a chat; for bots only @chat_id Chat identifier @request Join request @invite_link The invite link, which was used to send join request; may be null +updateNewChatJoinRequest chat_id:int53 request:chatJoinRequest invite_link:chatInviteLink = Update; + //@description Contains a list of updates @updates List of updates updates updates:vector = Updates; @@ -4267,6 +4327,20 @@ getActiveLiveLocationMessages = Messages; //@description Returns the last message sent in a chat no later than the specified date @chat_id Chat identifier @date Point in time (Unix timestamp) relative to which to search for messages getChatMessageByDate chat_id:int53 date:int32 = Message; +//@description Returns sparse positions of messages of the specified type in the chat to be used for shared media scroll implementation. Returns the results in reverse chronological order (i.e., in order of decreasing message_id). +//-Cannot be used in secret chats or with searchMessagesFilterFailedToSend filter without an enabled message database +//@chat_id Identifier of the chat in which to return information about message positions +//@filter Filter for message content. Filters searchMessagesFilterEmpty, searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention and searchMessagesFilterUnreadMention are unsupported in this function +//@from_message_id The message identifier from which to return information about message positions +//@limit The expected number of message positions to be returned; 50-2000. A smaller number of positions can be returned, if there are not enough appropriate messages +getChatSparseMessagePositions chat_id:int53 filter:SearchMessagesFilter from_message_id:int53 limit:int32 = MessagePositions; + +//@description Returns information about the next messages of the specified type in the chat splitted by days. Returns the results in reverse chronological order. Can return partial result for the last returned day. Behavior of this method depends on the value of the option "utc_time_offset" +//@chat_id Identifier of the chat in which to return information about messages +//@filter Filter for message content. Filters searchMessagesFilterEmpty, searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention and searchMessagesFilterUnreadMention are unsupported in this function +//@from_message_id The message identifier from which to return information about messages; use 0 to get results from the last message +getChatMessageCalendar chat_id:int53 filter:SearchMessagesFilter from_message_id:int53 = MessageCalendar; + //@description Returns approximate number of messages of the specified type in the chat @chat_id Identifier of the chat in which to count messages @filter Filter for message content; searchMessagesFilterEmpty is unsupported in this function @return_local If true, returns count that is available locally without sending network requests, returning -1 if the number of messages is unknown getChatMessageCount chat_id:int53 filter:SearchMessagesFilter return_local:Bool = Count; @@ -4375,6 +4449,10 @@ deleteMessages chat_id:int53 message_ids:vector revoke:Bool = Ok; //@description Deletes all messages sent by the specified user to a chat. Supported only for supergroups; requires can_delete_messages administrator privileges @chat_id Chat identifier @user_id User identifier deleteChatMessagesFromUser chat_id:int53 user_id:int53 = Ok; +//@description Deletes all messages between the specified dates in a chat. Supported only for private chats and basic groups. Messages sent in the last 30 seconds will not be deleted +//@chat_id Chat identifier @min_date The minimum date of the messages to delete @max_date The maximum date of the messages to delete @revoke Pass true to try to delete chat messages for all users; private chats only +deleteChatMessagesByDate chat_id:int53 min_date:int32 max_date:int32 revoke:Bool = Ok; + //@description Edits the text of a message (or a text of a game message). Returns the edited message after the edit is completed on the server side //@chat_id The chat the message belongs to @@ -4876,16 +4954,20 @@ replacePrimaryChatInviteLink chat_id:int53 = ChatInviteLink; //@description Creates a new invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat //@chat_id Chat identifier +//@name Invite link name; 0-32 characters //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit The maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited -createChatInviteLink chat_id:int53 expire_date:int32 member_limit:int32 = ChatInviteLink; +//@creates_join_request True, if the link only creates join request. If true, member_limit must not be specified +createChatInviteLink chat_id:int53 name:string expire_date:int32 member_limit:int32 creates_join_request:Bool = ChatInviteLink; //@description Edits a non-primary invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links //@chat_id Chat identifier //@invite_link Invite link to be edited +//@name Invite link name; 0-32 characters //@expire_date Point in time (Unix timestamp) when the link will expire; pass 0 if never //@member_limit The maximum number of chat members that can join the chat by the link simultaneously; 0-99999; pass 0 if not limited -editChatInviteLink chat_id:int53 invite_link:string expire_date:int32 member_limit:int32 = ChatInviteLink; +//@creates_join_request True, if the link only creates join request. If true, member_limit must not be specified +editChatInviteLink chat_id:int53 invite_link:string name:string expire_date:int32 member_limit:int32 creates_join_request:Bool = ChatInviteLink; //@description Returns information about an invite link. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links //@chat_id Chat identifier @@ -4928,11 +5010,25 @@ checkChatInviteLink invite_link:string = ChatInviteLinkInfo; //@description Uses an invite link to add the current user to the chat if possible @invite_link Invite link to use joinChatByInviteLink invite_link:string = Chat; +//@description Returns pending join requests in a chat +//@chat_id Chat identifier +//@invite_link Invite link for which to return join requests. If empty, all join requests will be returned. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links +//@query A query to search for in the first names, last names and usernames of the users to return +//@offset_request A chat join request from which to return next requests; pass null to get results from the beginning +//@limit The maximum number of chat join requests to return +getChatJoinRequests chat_id:int53 invite_link:string query:string offset_request:chatJoinRequest limit:int32 = ChatJoinRequests; -//@description Creates a new call @user_id Identifier of the user to be called @protocol Description of the call protocols supported by the application @is_video True, if a video call needs to be created +//@description Approves pending join request in a chat @chat_id Chat identifier @user_id Identifier of the user, which request will be approved +approveChatJoinRequest chat_id:int53 user_id:int53 = Ok; + +//@description Declines pending join request in a chat @chat_id Chat identifier @user_id Identifier of the user, which request will be declined +declineChatJoinRequest chat_id:int53 user_id:int53 = Ok; + + +//@description Creates a new call @user_id Identifier of the user to be called @protocol The call protocols supported by the application @is_video True, if a video call needs to be created createCall user_id:int53 protocol:callProtocol is_video:Bool = CallId; -//@description Accepts an incoming call @call_id Call identifier @protocol Description of the call protocols supported by the application +//@description Accepts an incoming call @call_id Call identifier @protocol The call protocols supported by the application acceptCall call_id:int32 protocol:callProtocol = Ok; //@description Sends call signaling data @call_id Call identifier @data The data @@ -4948,17 +5044,17 @@ sendCallRating call_id:int32 rating:int32 comment:string problems:vector = Ok; -//@description Returns invite link to a voice chat in a public chat +//@description Returns invite link to a video chat in a public chat //@group_call_id Group call identifier //@can_self_unmute Pass true if the invite link needs to contain an invite hash, passing which to joinGroupCall would allow the invited user to unmute themselves. Requires groupCall.can_be_managed group call flag getGroupCallInviteLink group_call_id:int32 can_self_unmute:Bool = HttpUrl; @@ -5180,6 +5276,9 @@ getStickerEmojis sticker:InputFile = Emojis; //@description Searches for emojis by keywords. Supported only if the file database is enabled @text Text to search for @exact_match True, if only emojis, which exactly match text needs to be returned @input_language_codes List of possible IETF language tags of the user's input language; may be empty if unknown searchEmojis text:string exact_match:Bool input_language_codes:vector = Emojis; +//@description Returns an animated emoji corresponding to a given emoji. Returns a 404 error if the emoji has no animated emoji @emoji The emoji +getAnimatedEmoji emoji:string = AnimatedEmoji; + //@description Returns an HTTP URL which can be used to automatically log in to the translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation @language_code Language code for which the emoji replacements will be suggested getEmojiSuggestionsUrl language_code:string = HttpUrl; diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index d377bea7a..d66485596 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -118,8 +118,8 @@ chatForbidden#6592a1a7 id:long title:string = Chat; channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#4dbdc099 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string = ChatFull; -channelFull#e9b27a17 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string = ChatFull; +chatFull#46a6ffb4 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector = ChatFull; +channelFull#59cff963 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector = ChatFull; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; @@ -178,6 +178,7 @@ messageActionInviteToGroupCall#502f92f7 call:InputGroupCall users:Vector = messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction; messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction; messageActionSetChatTheme#aa786345 emoticon:string = MessageAction; +messageActionChatJoinedByRequest#ebbca3cb = MessageAction; dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -368,6 +369,8 @@ updateChannelParticipant#985d3abb flags:# channel_id:long date:int actor_id:long updateBotStopped#c4870a49 user_id:long date:int stopped:Bool qts:int = Update; updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJSON = Update; updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector = Update; +updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector = Update; +updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -539,10 +542,10 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteExported#b18105e8 flags:# revoked:flags.0?true permanent:flags.5?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int = ExportedChatInvite; +chatInviteExported#ab4a819 flags:# revoked:flags.0?true permanent:flags.5?true request_needed:flags.6?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int requested:flags.7?int title:flags.8?string = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; -chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; +chatInvite#300c44c1 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite; inputStickerSetEmpty#ffb62b95 = InputStickerSet; @@ -615,7 +618,7 @@ channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector = ChannelMessagesFilter; channelParticipant#c00c07c0 user_id:long date:int = ChannelParticipant; -channelParticipantSelf#28a8bc67 user_id:long inviter_id:long date:int = ChannelParticipant; +channelParticipantSelf#35a8bfa7 flags:# via_invite:flags.0?true user_id:long inviter_id:long date:int = ChannelParticipant; channelParticipantCreator#2fe601d3 flags:# user_id:long admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant; channelParticipantAdmin#34c3bb53 flags:# can_edit:flags.0?true self:flags.1?true user_id:long inviter_id:flags.1?long promoted_by:long date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; channelParticipantBanned#6df8014e flags:# left:flags.0?true peer:Peer kicked_by:long date:int banned_rights:ChatBannedRights = ChannelParticipant; @@ -898,6 +901,7 @@ channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvit channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -1112,7 +1116,7 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti inputTheme#3c5693e9 id:long access_hash:long = InputTheme; inputThemeSlug#f5890df1 slug:string = InputTheme; -theme#e802b8dc flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:flags.4?int = Theme; +theme#a00e67d6 flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?Vector emoticon:flags.6?string installs_count:flags.4?int = Theme; account.themesNotModified#f41eb622 = account.Themes; account.themes#9a3d8c6d hash:long themes:Vector = account.Themes; @@ -1224,7 +1228,7 @@ messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector = messages.AffectedFoundMessages; -chatInviteImporter#b5cd5f4 user_id:long date:int = ChatInviteImporter; +chatInviteImporter#8c5adfd9 flags:# requested:flags.0?true user_id:long date:int about:flags.2?string approved_by:flags.1?long = ChatInviteImporter; messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; @@ -1261,15 +1265,18 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult; account.resetPasswordOk#e926d63e = account.ResetPasswordResult; -chatTheme#ed0b5c33 emoticon:string theme:Theme dark_theme:Theme = ChatTheme; - -account.chatThemesNotModified#e011e1c4 = account.ChatThemes; -account.chatThemes#fe4cbebd hash:int themes:Vector = account.ChatThemes; - -sponsoredMessage#2a3c381f flags:# random_id:bytes from_id:Peer start_param:flags.0?string message:string entities:flags.1?Vector = SponsoredMessage; +sponsoredMessage#d151e19a flags:# random_id:bytes from_id:Peer channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector = SponsoredMessage; messages.sponsoredMessages#65a4c7d5 messages:Vector chats:Vector users:Vector = messages.SponsoredMessages; +searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod; + +messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector messages:Vector chats:Vector users:Vector = messages.SearchResultsCalendar; + +searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition; + +messages.searchResultsPositions#53b22baf count:int positions:Vector = messages.SearchResultsPositions; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1356,10 +1363,10 @@ account.resetWallPapers#bb3b9804 = Bool; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document; -account.createTheme#8432c21f flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; -account.updateTheme#5cb367d5 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; +account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?Vector = Theme; +account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector = Theme; account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; -account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool; +account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool; account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme; account.getThemes#7206e458 format:string hash:long = account.Themes; account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool; @@ -1370,7 +1377,7 @@ account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = Globa account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool; account.resetPassword#9308ce1b = account.ResetPasswordResult; account.declinePasswordReset#4c9409f6 = Bool; -account.getChatThemes#d6d71d7b hash:int = account.ChatThemes; +account.getChatThemes#d638de89 hash:long = account.Themes; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1402,7 +1409,7 @@ messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.search#a0fda762 flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; -messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory; +messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; @@ -1434,7 +1441,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#d5a5d3a1 emoticon:string hash:long = messages.Stickers; messages.getAllStickers#b8a0a1a8 hash:long = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#14b9bcd7 flags:# legacy_revoke_permanent:flags.2?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int = ExportedChatInvite; +messages.exportChatInvite#a02ce5d5 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1531,15 +1538,18 @@ messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:st messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite; -messages.editExportedChatInvite#2e4ffbe flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int = messages.ExportedChatInvite; +messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite; messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool; messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; -messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer; messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates; messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector; +messages.getSearchResultsCalendar#49f0bde9 peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; +messages.getSearchResultsPositions#6e9583a3 peer:InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions; +messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; diff --git a/td/generate/tl_writer_c.h b/td/generate/tl_writer_c.h index 030afe620..e31bc969c 100644 --- a/td/generate/tl_writer_c.h +++ b/td/generate/tl_writer_c.h @@ -276,7 +276,6 @@ class TlWriterCCommon final : public tl::TL_writer { "#include \"td/utils/logging.h\"\n" "#include \"td/utils/misc.h\"\n" "#include \"td/utils/Slice.h\"\n" - "#include \"td/utils/tl_storers.h\"\n" "\n"; } std::string gen_output_end() const final { diff --git a/td/generate/tl_writer_cpp.cpp b/td/generate/tl_writer_cpp.cpp index ea478ff1a..9853d5130 100644 --- a/td/generate/tl_writer_cpp.cpp +++ b/td/generate/tl_writer_cpp.cpp @@ -25,7 +25,8 @@ std::string TD_TL_writer_cpp::gen_output_begin() const { "#include \"td/utils/logging.h\"\n" "#include \"td/utils/SliceBuilder.h\"\n" "#include \"td/utils/tl_parsers.h\"\n" - "#include \"td/utils/tl_storers.h\"\n\n" + "#include \"td/utils/tl_storers.h\"\n" + "#include \"td/utils/TlStorerToString.h\"\n\n" "namespace td {\n" "namespace " + tl_name + @@ -33,7 +34,7 @@ std::string TD_TL_writer_cpp::gen_output_begin() const { "std::string to_string(const BaseObject &value) {\n" " TlStorerToString storer;\n" " value.store(storer, \"\");\n" - " return storer.move_as_str();\n" + " return storer.move_as_string();\n" "}\n"; } @@ -283,7 +284,7 @@ std::string TD_TL_writer_cpp::gen_var_type_fetch(const tl::arg &a) const { } std::string TD_TL_writer_cpp::get_pretty_field_name(std::string field_name) const { - if (!field_name.empty() && field_name.back() == ']') { + if (!field_name.empty() && field_name[0] == '_') { return ""; } auto equals_pos = field_name.find('='); @@ -305,16 +306,10 @@ std::string TD_TL_writer_cpp::get_pretty_class_name(std::string class_name) cons std::string TD_TL_writer_cpp::gen_vector_store(const std::string &field_name, const tl::tl_tree_type *t, const std::vector &vars, int storer_type) const { - std::string num = field_name.back() == ']' ? "2" : ""; - return "{ const array<" + gen_type_name(t) + "> &v" + num + " = " + field_name + - "; const std::uint32_t multiplicity" + num + " = static_cast(v" + num + - ".size()); const auto vector_name" + num + " = \"" + get_pretty_class_name("vector") + - "[\" + td::to_string(multiplicity" + num + ")+ \"]\"; s.store_class_begin(\"" + - get_pretty_field_name(field_name) + "\", vector_name" + num + - ".c_str()); " - "for (std::uint32_t i" + - num + " = 0; i" + num + " < multiplicity" + num + "; i" + num + "++) { " + - gen_type_store("v" + num + "[i" + num + "]", t, vars, storer_type) + " } s.store_class_end(); }"; + std::string num = !field_name.empty() && field_name[0] == '_' ? "2" : ""; + return "{ s.store_vector_begin(\"" + get_pretty_field_name(field_name) + "\", " + field_name + + ".size()); for (const auto &_value" + num + " : " + field_name + ") { " + + gen_type_store("_value" + num, t, vars, storer_type) + " } s.store_class_end(); }"; } std::string TD_TL_writer_cpp::gen_store_class_name(const tl::tl_tree_type *tree_type) const { @@ -329,7 +324,8 @@ std::string TD_TL_writer_cpp::gen_store_class_name(const tl::tl_tree_type *tree_ return "TlStoreBool"; } if (name == "True") { - return "TlStoreTrue"; + assert(false); + return ""; } if (name == "String" || name == "Bytes") { return "TlStoreString"; @@ -404,8 +400,8 @@ std::string TD_TL_writer_cpp::gen_type_store(const std::string &field_name, cons return gen_vector_store(field_name, child, vars, storer_type); } else { assert(tree_type->children.empty()); - return "if (" + field_name + " == nullptr) { s.store_field(\"" + get_pretty_field_name(field_name) + - "\", \"null\"); } else { " + field_name + "->store(s, \"" + get_pretty_field_name(field_name) + "\"); }"; + return "s.store_object_field(\"" + get_pretty_field_name(field_name) + "\", static_cast(" + + field_name + ".get()));"; } } @@ -443,6 +439,13 @@ std::string TD_TL_writer_cpp::gen_field_store(const tl::arg &a, std::vector= 0 && a.var_num < 0 && a.type->get_type() == tl::NODE_TYPE_TYPE) { + const tl::tl_tree_type *tree_type = static_cast(a.type); + if (tree_type->type->name == "True") { + return ""; + } + } + if (a.exist_var_num >= 0) { assert(a.exist_var_num < static_cast(vars.size())); assert(vars[a.exist_var_num].is_stored); diff --git a/td/generate/tl_writer_h.cpp b/td/generate/tl_writer_h.cpp index a9285ac94..3a4760ad9 100644 --- a/td/generate/tl_writer_h.cpp +++ b/td/generate/tl_writer_h.cpp @@ -164,13 +164,34 @@ std::string TD_TL_writer_h::gen_function_vars(const tl::tl_combinator *t, return res; } -std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t) const { +bool TD_TL_writer_h::need_arg_mask(const tl::arg &a, bool can_be_stored) const { + if (a.exist_var_num == -1) { + return false; + } + + if (can_be_stored) { + return true; + } + + if (a.type->get_type() != tl::NODE_TYPE_TYPE) { + return true; + } + const tl::tl_tree_type *tree_type = static_cast(a.type); + const std::string &name = tree_type->type->name; + + if (!is_built_in_simple_type(name) || name == "True") { + return false; + } + return true; +} + +std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t, bool can_be_stored) const { std::vector> flags; for (std::size_t i = 0; i < t->args.size(); i++) { const tl::arg &a = t->args[i]; - if (a.exist_var_num != -1) { + if (need_arg_mask(a, can_be_stored)) { auto name = a.name; for (auto &c : name) { c = to_upper(c); @@ -180,7 +201,7 @@ std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t) co } std::string res; if (!flags.empty()) { - res += " enum Flags : std::int32_t {"; + res += " enum Flags : std::int32_t { "; bool first = true; for (auto &p : flags) { if (first) { @@ -190,7 +211,7 @@ std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t) co } res += p.first + "_MASK = " + int_to_string(1 << p.second); } - res += "};\n"; + res += " };\n"; } return res; } diff --git a/td/generate/tl_writer_h.h b/td/generate/tl_writer_h.h index c52125050..9e2538365 100644 --- a/td/generate/tl_writer_h.h +++ b/td/generate/tl_writer_h.h @@ -20,6 +20,8 @@ class TD_TL_writer_h : public TD_TL_writer { static std::string forward_declaration(std::string type); + bool need_arg_mask(const tl::arg &a, bool can_be_stored) const; + public: TD_TL_writer_h(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type, const std::vector &ext_include) @@ -40,7 +42,7 @@ class TD_TL_writer_h : public TD_TL_writer { std::string gen_field_definition(const std::string &class_name, const std::string &type_name, const std::string &field_name) const override; - std::string gen_flags_definitions(const tl::tl_combinator *t) const override; + std::string gen_flags_definitions(const tl::tl_combinator *t, bool can_be_stored) const override; std::string gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type, std::vector &vars) const override; std::string gen_function_vars(const tl::tl_combinator *t, std::vector &vars) const override; diff --git a/td/generate/tl_writer_jni_cpp.cpp b/td/generate/tl_writer_jni_cpp.cpp index d28c582ac..330194e62 100644 --- a/td/generate/tl_writer_jni_cpp.cpp +++ b/td/generate/tl_writer_jni_cpp.cpp @@ -322,8 +322,8 @@ std::string TD_TL_writer_jni_cpp::gen_type_store(const std::string &field_name, res = gen_vector_store(field_name, child, vars, storer_type); } else { if (storer_type == 1) { - res = "if (" + field_name + " == nullptr) { s.store_field(\"" + get_pretty_field_name(field_name) + - "\", \"null\"); } else { " + field_name + "->store(s, \"" + get_pretty_field_name(field_name) + "\"); }"; + res = "s.store_object_field(\"" + get_pretty_field_name(field_name) + "\", static_cast(" + + field_name + ".get()));"; } else { res = "if (" + field_name + " != nullptr) { jobject next; " + field_name + "->store(env, next); if (next) { env->SetObjectField(s, " + field_name + diff --git a/td/telegram/AudiosManager.cpp b/td/telegram/AudiosManager.cpp index 2881c36f1..75d8ca749 100644 --- a/td/telegram/AudiosManager.cpp +++ b/td/telegram/AudiosManager.cpp @@ -10,8 +10,6 @@ #include "td/telegram/files/FileManager.h" #include "td/telegram/secret_api.h" #include "td/telegram/Td.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" #include "td/utils/logging.h" #include "td/utils/misc.h" diff --git a/td/telegram/AudiosManager.h b/td/telegram/AudiosManager.h index da9316614..0b39c5211 100644 --- a/td/telegram/AudiosManager.h +++ b/td/telegram/AudiosManager.h @@ -6,12 +6,11 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/Photo.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/AuthManager.cpp b/td/telegram/AuthManager.cpp index e2a6f39e1..0a07619db 100644 --- a/td/telegram/AuthManager.cpp +++ b/td/telegram/AuthManager.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/AuthManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/AuthManager.hpp" #include "td/telegram/ConfigManager.h" #include "td/telegram/ConfigShared.h" @@ -571,8 +568,7 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { wait_password_state_.srp_B_ = password->srp_B_.as_slice().str(); wait_password_state_.srp_id_ = password->srp_id_; wait_password_state_.hint_ = std::move(password->hint_); - wait_password_state_.has_recovery_ = - (password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0; + wait_password_state_.has_recovery_ = password->has_recovery_; break; } default: diff --git a/td/telegram/AuthManager.hpp b/td/telegram/AuthManager.hpp index cf0afc8f8..a531e68c5 100644 --- a/td/telegram/AuthManager.hpp +++ b/td/telegram/AuthManager.hpp @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/AuthManager.h" + #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/SendCodeHelper.hpp" #include "td/telegram/Version.h" diff --git a/td/telegram/BackgroundManager.cpp b/td/telegram/BackgroundManager.cpp index 3d8f5bc5c..c278855cf 100644 --- a/td/telegram/BackgroundManager.cpp +++ b/td/telegram/BackgroundManager.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/BackgroundManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/AuthManager.h" #include "td/telegram/BackgroundType.hpp" #include "td/telegram/ConfigShared.h" @@ -1101,8 +1098,8 @@ std::pair BackgroundManager::on_get_background( Background background; background.id = background_id; background.is_creator = false; - background.is_default = (wallpaper->flags_ & telegram_api::wallPaperNoFile::DEFAULT_MASK) != 0; - background.is_dark = (wallpaper->flags_ & telegram_api::wallPaperNoFile::DARK_MASK) != 0; + background.is_default = wallpaper->default_; + background.is_dark = wallpaper->dark_; background.type = BackgroundType(true, false, std::move(wallpaper->settings_)); background.name = background.type.get_link(); add_background(background, replace_type); @@ -1127,8 +1124,7 @@ std::pair BackgroundManager::on_get_background( } CHECK(document_id == telegram_api::document::ID); - int32 flags = wallpaper->flags_; - bool is_pattern = (flags & telegram_api::wallPaper::PATTERN_MASK) != 0; + bool is_pattern = wallpaper->pattern_; Document document = td_->documents_manager_->on_get_document( telegram_api::move_object_as(wallpaper->document_), DialogId(), nullptr, @@ -1142,9 +1138,9 @@ std::pair BackgroundManager::on_get_background( Background background; background.id = background_id; background.access_hash = wallpaper->access_hash_; - background.is_creator = (flags & telegram_api::wallPaper::CREATOR_MASK) != 0; - background.is_default = (flags & telegram_api::wallPaper::DEFAULT_MASK) != 0; - background.is_dark = (flags & telegram_api::wallPaper::DARK_MASK) != 0; + background.is_creator = wallpaper->creator_; + background.is_default = wallpaper->default_; + background.is_dark = wallpaper->dark_; background.type = BackgroundType(false, is_pattern, std::move(wallpaper->settings_)); background.name = std::move(wallpaper->slug_); background.file_id = document.file_id; diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp index 225ac6605..cefdcbcab 100644 --- a/td/telegram/CallActor.cpp +++ b/td/telegram/CallActor.cpp @@ -6,10 +6,6 @@ // #include "td/telegram/CallActor.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" -#include "td/telegram/telegram_api.hpp" - #include "td/telegram/ConfigShared.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DhCache.h" @@ -20,6 +16,7 @@ #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/NotificationManager.h" #include "td/telegram/Td.h" +#include "td/telegram/telegram_api.hpp" #include "td/telegram/UpdatesManager.h" #include "td/utils/algorithm.h" @@ -381,7 +378,7 @@ Status CallActor::do_update_call(telegram_api::phoneCallWaiting &call) { call_id_ = call.id_; call_access_hash_ = call.access_hash_; is_call_id_inited_ = true; - is_video_ |= (call.flags_ & telegram_api::phoneCallWaiting::VIDEO_MASK) != 0; + is_video_ |= call.video_; call_admin_user_id_ = UserId(call.admin_id_); // call_participant_user_id_ = UserId(call.participant_id_); if (call_id_promise_) { @@ -404,7 +401,7 @@ Status CallActor::do_update_call(telegram_api::phoneCallRequested &call) { call_id_ = call.id_; call_access_hash_ = call.access_hash_; is_call_id_inited_ = true; - is_video_ |= (call.flags_ & telegram_api::phoneCallRequested::VIDEO_MASK) != 0; + is_video_ |= call.video_; call_admin_user_id_ = UserId(call.admin_id_); // call_participant_user_id_ = UserId(call.participant_id_); if (call_id_promise_) { @@ -444,7 +441,7 @@ Status CallActor::do_update_call(telegram_api::phoneCallAccepted &call) { call_id_promise_.set_value(std::move(call.id_)); } } - is_video_ |= (call.flags_ & telegram_api::phoneCallAccepted::VIDEO_MASK) != 0; + is_video_ |= call.video_; dh_handshake_.set_g_a(call.g_b_.as_slice()); TRY_STATUS(dh_handshake_.run_checks(true, DhCache::instance())); std::tie(call_state_.key_fingerprint, call_state_.key) = dh_handshake_.gen_key(); @@ -468,7 +465,7 @@ Status CallActor::do_update_call(telegram_api::phoneCall &call) { } cancel_timeout(); - is_video_ |= (call.flags_ & telegram_api::phoneCall::VIDEO_MASK) != 0; + is_video_ |= call.video_; LOG(DEBUG) << "Do update call to Ready from state " << static_cast(state_); if (state_ == State::WaitAcceptResult) { @@ -487,7 +484,7 @@ Status CallActor::do_update_call(telegram_api::phoneCall &call) { call_state_.connections.emplace_back(*connection); } call_state_.protocol = CallProtocol(*call.protocol_); - call_state_.allow_p2p = (call.flags_ & telegram_api::phoneCall::P2P_ALLOWED_MASK) != 0; + call_state_.allow_p2p = call.p2p_allowed_; call_state_.type = CallState::Type::Ready; call_state_need_flush_ = true; diff --git a/td/telegram/CallManager.h b/td/telegram/CallManager.h index 85fb3f6b2..46b1a38bd 100644 --- a/td/telegram/CallManager.h +++ b/td/telegram/CallManager.h @@ -8,7 +8,6 @@ #include "td/telegram/CallActor.h" #include "td/telegram/CallId.h" - #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/CallbackQueriesManager.cpp b/td/telegram/CallbackQueriesManager.cpp index b640b9817..76b06f898 100644 --- a/td/telegram/CallbackQueriesManager.cpp +++ b/td/telegram/CallbackQueriesManager.cpp @@ -79,8 +79,8 @@ class GetBotCallbackAnswerQuery final : public Td::ResultHandler { } auto answer = result_ptr.move_as_ok(); - bool show_alert = (answer->flags_ & telegram_api::messages_botCallbackAnswer::ALERT_MASK) != 0; - promise_.set_value(td_api::make_object(answer->message_, show_alert, answer->url_)); + promise_.set_value( + td_api::make_object(answer->message_, answer->alert_, answer->url_)); } void on_error(uint64 id, Status status) final { diff --git a/td/telegram/CallbackQueriesManager.h b/td/telegram/CallbackQueriesManager.h index 77a220883..6400b2070 100644 --- a/td/telegram/CallbackQueriesManager.h +++ b/td/telegram/CallbackQueriesManager.h @@ -6,12 +6,11 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/DialogId.h" #include "td/telegram/FullMessageId.h" #include "td/telegram/MessageId.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" #include "td/actor/PromiseFuture.h" diff --git a/td/telegram/ClientActor.cpp b/td/telegram/ClientActor.cpp index a1b4a8d91..74740549d 100644 --- a/td/telegram/ClientActor.cpp +++ b/td/telegram/ClientActor.cpp @@ -6,8 +6,6 @@ // #include "td/telegram/ClientActor.h" -#include "td/telegram/td_api.h" - #include "td/telegram/net/NetQueryCounter.h" #include "td/telegram/net/NetQueryStats.h" #include "td/telegram/Td.h" diff --git a/td/telegram/ClientDotNet.cpp b/td/telegram/ClientDotNet.cpp index 57d3dc934..38bf780bd 100644 --- a/td/telegram/ClientDotNet.cpp +++ b/td/telegram/ClientDotNet.cpp @@ -56,18 +56,12 @@ public: /// of the query or with Telegram.Td.Api.Error as parameter. If it is null, nothing will be called. /// Thrown when query is null. void Send(Api::Function^ function, ClientResultHandler^ handler) { - if (function == nullptr) { - throw REF_NEW NullReferenceException("Function can't be null"); - } - - std::uint64_t queryId = Increment(currentId); + std::uint64_t requestId = Increment(currentRequestId); if (handler != nullptr) { - handlers[queryId] = handler; + handlers[requestId] = handler; } - td::Client::Request request; - request.id = queryId; - request.function = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); - client->send(std::move(request)); + auto request = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); + td::ClientManager::get_manager_singleton()->send(clientId, requestId, std::move(request)); } /// @@ -77,31 +71,32 @@ public: /// Returns request result. /// Thrown when query is null. static Api::BaseObject^ Execute(Api::Function^ function) { - if (function == nullptr) { - throw REF_NEW NullReferenceException("Function can't be null"); - } - - td::Client::Request request; - request.id = 0; - request.function = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); - return Api::FromUnmanaged(*td::Client::execute(std::move(request)).object); + auto request = td::td_api::move_object_as(ToUnmanaged(function)->get_object_ptr()); + return Api::FromUnmanaged(*td::ClientManager::execute(std::move(request))); } /// /// Launches a cycle which will fetch all results of queries to TDLib and incoming updates from TDLib. - /// Must be called once on a separate dedicated thread, on which all updates and query results will be handled. - /// Returns only when TDLib instance is closed. + /// Must be called once on a separate dedicated thread, on which all updates and query results from all Clients will be handled. + /// Never returns. /// - void Run() { + static void Run() { while (true) { - auto response = client->receive(10.0); + auto response = td::ClientManager::get_manager_singleton()->receive(300.0); if (response.object != nullptr) { - ProcessResult(response.id, Api::FromUnmanaged(*response.object)); - - if (response.object->get_id() == td::td_api::updateAuthorizationState::ID && + bool isClosed = response.object->get_id() == td::td_api::updateAuthorizationState::ID && static_cast(*response.object).authorization_state_->get_id() == - td::td_api::authorizationStateClosed::ID) { - break; + td::td_api::authorizationStateClosed::ID && response.request_id == 0; + + ClientResultHandler^ handler; + if (response.request_id == 0 ? updateHandlers.TryGetValue(response.client_id, handler) : + handlers.TryRemove(response.request_id, handler)) { + // TODO try/catch + handler->OnResult(Api::FromUnmanaged(*response.object)); + } + + if (isClosed) { + updateHandlers.TryRemove(response.client_id, handler); } } } @@ -127,38 +122,33 @@ public: static void SetLogMessageCallback(std::int32_t max_verbosity_level, LogMessageCallback^ callback) { std::lock_guard lock(logMutex); if (callback == nullptr) { - ::td::ClientManager::set_log_message_callback(max_verbosity_level, nullptr); + td::ClientManager::set_log_message_callback(max_verbosity_level, nullptr); logMessageCallback = nullptr; } else { logMessageCallback = callback; - ::td::ClientManager::set_log_message_callback(max_verbosity_level, LogMessageCallbackWrapper); + td::ClientManager::set_log_message_callback(max_verbosity_level, LogMessageCallbackWrapper); } } #endif private: Client(ClientResultHandler^ updateHandler) { - client = new td::Client(); - handlers[0] = updateHandler; - } - - ~Client() { - delete client; - } - - std::int64_t currentId = 0; - ConcurrentDictionary handlers; - td::Client *client = nullptr; - - void ProcessResult(std::uint64_t id, Api::BaseObject^ object) { - ClientResultHandler^ handler; - // update handler stays forever - if (id == 0 ? handlers.TryGetValue(id, handler) : handlers.TryRemove(id, handler)) { - // TODO try/catch - handler->OnResult(object); + clientId = td::ClientManager::get_manager_singleton()->create_client_id(); + if (updateHandler != nullptr) { + updateHandlers[clientId] = updateHandler; } + Send(REF_NEW Api::GetOption("version"), nullptr); } +#if !TD_CLI + static std::int64_t currentRequestId; +#else + static std::int64_t currentRequestId = 0; +#endif + static ConcurrentDictionary handlers; + static ConcurrentDictionary updateHandlers; + std::int32_t clientId; + #if !TD_CLI static std::mutex logMutex; static LogMessageCallback^ logMessageCallback; @@ -173,6 +163,10 @@ private: }; #if !TD_CLI +std::int64_t Client::currentRequestId = 0; +ConcurrentDictionary Client::handlers; +ConcurrentDictionary Client::updateHandlers; + std::mutex Client::logMutex; LogMessageCallback^ Client::logMessageCallback; #endif diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 90ac22b3c..df2fc0b40 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -47,6 +47,7 @@ #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/crypto.h" +#include "td/utils/emoji.h" #include "td/utils/format.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" @@ -959,6 +960,16 @@ void ConfigManager::lazy_request_config() { set_timeout_at(expire_time_.at()); } +void ConfigManager::try_request_app_config() { + if (get_app_config_queries_.size() + reget_app_config_queries_.size() != 1) { + return; + } + + auto query = G()->net_query_creator().create_unauth(telegram_api::help_getAppConfig()); + query->total_timeout_limit_ = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 1)); +} + void ConfigManager::get_app_config(Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); @@ -968,11 +979,21 @@ void ConfigManager::get_app_config(Promise } get_app_config_queries_.push_back(std::move(promise)); - if (get_app_config_queries_.size() == 1) { - auto query = G()->net_query_creator().create_unauth(telegram_api::help_getAppConfig()); - query->total_timeout_limit_ = 60 * 60 * 24; - G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 1)); + try_request_app_config(); +} + +void ConfigManager::reget_app_config(Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); } + + auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get(); + if (auth_manager != nullptr && auth_manager->is_bot()) { + return promise.set_value(Unit()); + } + + reget_app_config_queries_.push_back(std::move(promise)); + try_request_app_config(); } void ConfigManager::get_content_settings(Promise &&promise) { @@ -1065,7 +1086,7 @@ void ConfigManager::do_set_ignore_sensitive_content_restrictions(bool ignore_sen ignore_sensitive_content_restrictions); bool have_ignored_restriction_reasons = G()->shared_config().have_option("ignored_restriction_reasons"); if (have_ignored_restriction_reasons != ignore_sensitive_content_restrictions) { - get_app_config(Auto()); + reget_app_config(Auto()); } } @@ -1121,7 +1142,7 @@ void ConfigManager::on_result(NetQueryPtr res) { return; } remove_suggested_action(suggested_actions_, suggested_action); - get_app_config(Auto()); + reget_app_config(Auto()); for (auto &promise : promises) { promise.set_value(Unit()); @@ -1245,15 +1266,16 @@ void ConfigManager::on_result(NetQueryPtr res) { if (token == 1) { auto promises = std::move(get_app_config_queries_); get_app_config_queries_.clear(); - CHECK(!promises.empty()); + auto unit_promises = std::move(reget_app_config_queries_); + reget_app_config_queries_.clear(); + CHECK(!promises.empty() || !unit_promises.empty()); auto result_ptr = fetch_result(std::move(res)); if (result_ptr.is_error()) { for (auto &promise : promises) { - if (!promise) { - promise.set_value(nullptr); - } else { - promise.set_error(result_ptr.error().clone()); - } + promise.set_error(result_ptr.error().clone()); + } + for (auto &promise : unit_promises) { + promise.set_error(result_ptr.error().clone()); } return; } @@ -1261,11 +1283,10 @@ void ConfigManager::on_result(NetQueryPtr res) { auto result = result_ptr.move_as_ok(); process_app_config(result); for (auto &promise : promises) { - if (!promise) { - promise.set_value(nullptr); - } else { - promise.set_value(convert_json_value_object(result)); - } + promise.set_value(convert_json_value_object(result)); + } + for (auto &promise : unit_promises) { + promise.set_value(Unit()); } return; } @@ -1318,7 +1339,7 @@ void ConfigManager::save_config_expire(Timestamp timestamp) { } void ConfigManager::process_config(tl_object_ptr config) { - bool is_from_main_dc = G()->net_query_dispatcher().main_dc_id().get_value() == config->this_dc_; + bool is_from_main_dc = G()->net_query_dispatcher().get_main_dc_id().get_value() == config->this_dc_; LOG(INFO) << to_string(config); auto reload_in = clamp(config->expires_ - config->date_, 60, 86400); @@ -1348,8 +1369,7 @@ void ConfigManager::process_config(tl_object_ptr config) { shared_config.set_option_integer("pinned_chat_count_max", config->pinned_dialogs_count_max_); shared_config.set_option_integer("pinned_archived_chat_count_max", config->pinned_infolder_count_max_); if (is_from_main_dc || !shared_config.have_option("expect_blocking")) { - shared_config.set_option_boolean("expect_blocking", - (config->flags_ & telegram_api::config::BLOCKED_MODE_MASK) != 0); + shared_config.set_option_boolean("expect_blocking", config->blocked_mode_); } if (is_from_main_dc || !shared_config.have_option("dc_txt_domain_name")) { shared_config.set_option_string("dc_txt_domain_name", config->dc_txt_domain_name_); @@ -1383,8 +1403,7 @@ void ConfigManager::process_config(tl_object_ptr config) { if (is_from_main_dc) { shared_config.set_option_integer("edit_time_limit", config->edit_time_limit_); - shared_config.set_option_boolean("revoke_pm_inbox", - (config->flags_ & telegram_api::config::REVOKE_PM_INBOX_MASK) != 0); + shared_config.set_option_boolean("revoke_pm_inbox", config->revoke_pm_inbox_); shared_config.set_option_integer("revoke_time_limit", config->revoke_time_limit_); shared_config.set_option_integer("revoke_pm_time_limit", config->revoke_pm_time_limit_); @@ -1451,7 +1470,7 @@ void ConfigManager::process_config(tl_object_ptr config) { // shared_config.set_option_integer("push_chat_limit", config->push_chat_limit_); if (is_from_main_dc) { - get_app_config(Auto()); + reget_app_config(Auto()); if (!shared_config.have_option("can_ignore_sensitive_content_restrictions") || !shared_config.have_option("ignore_sensitive_content_restrictions")) { get_content_settings(Auto()); @@ -1478,12 +1497,14 @@ void ConfigManager::process_app_config(tl_object_ptr &c vector dice_emojis; std::unordered_map dice_emoji_index; std::unordered_map dice_emoji_success_value; + vector emoji_sounds; string animation_search_provider; string animation_search_emojis; vector suggested_actions; bool can_archive_and_mute_new_chats_from_unknown_users = false; int64 chat_read_mark_expire_period = 0; int64 chat_read_mark_size_threshold = 0; + double animated_emoji_zoom = 0.0; if (config->get_id() == telegram_api::jsonObject::ID) { for (auto &key_value : static_cast(config.get())->value_) { Slice key = key_value->key_; @@ -1511,6 +1532,10 @@ void ConfigManager::process_app_config(tl_object_ptr &c } continue; } + if (key == "emojies_animated_zoom") { + animated_emoji_zoom = get_json_value_double(std::move(key_value->value_), "emojies_animated_zoom"); + continue; + } if (key == "emojies_send_dice") { if (value->get_id() == telegram_api::jsonArray::ID) { auto emojis = std::move(static_cast(value)->value_); @@ -1563,6 +1588,46 @@ void ConfigManager::process_app_config(tl_object_ptr &c } continue; } + if (key == "emojies_sounds") { + if (value->get_id() == telegram_api::jsonObject::ID) { + auto sounds = std::move(static_cast(value)->value_); + for (auto &sound : sounds) { + CHECK(sound != nullptr); + if (sound->value_->get_id() == telegram_api::jsonObject::ID) { + string id; + string access_hash; + string file_reference_base64; + for (auto &sound_key_value : static_cast(sound->value_.get())->value_) { + if (sound_key_value->value_->get_id() != telegram_api::jsonString::ID) { + continue; + } + auto current_value = get_json_value_string(std::move(sound_key_value->value_), Slice()); + if (sound_key_value->key_ == "id") { + id = std::move(current_value); + } + if (sound_key_value->key_ == "access_hash") { + access_hash = std::move(current_value); + } + if (sound_key_value->key_ == "file_reference_base64") { + file_reference_base64 = std::move(current_value); + } + } + if (to_integer_safe(id).is_error() || to_integer_safe(access_hash).is_error() || + !is_base64url(file_reference_base64) || !is_emoji(sound->key_)) { + LOG(ERROR) << "Receive unexpected sound value " << to_string(sound); + } else { + emoji_sounds.push_back(sound->key_); + emoji_sounds.push_back(PSTRING() << id << ':' << access_hash << ':' << file_reference_base64); + } + } else { + LOG(ERROR) << "Receive unexpected emoji sound " << to_string(sound); + } + } + } else { + LOG(ERROR) << "Receive unexpected emojies_sounds " << to_string(*value); + } + continue; + } if (key == "gif_search_branding") { animation_search_provider = get_json_value_string(std::move(key_value->value_), "gif_search_branding"); continue; @@ -1722,6 +1787,13 @@ void ConfigManager::process_app_config(tl_object_ptr &c shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01')); } + shared_config.set_option_string("emoji_sounds", implode(emoji_sounds, ',')); + + if (animated_emoji_zoom <= 0 || animated_emoji_zoom > 2.0) { + shared_config.set_option_empty("animated_emoji_zoom"); + } else { + shared_config.set_option_integer("animated_emoji_zoom", static_cast(animated_emoji_zoom * 1e9)); + } if (animation_search_provider.empty()) { shared_config.set_option_empty("animation_search_provider"); } else { diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index 532719630..ac4de3823 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -10,7 +10,6 @@ #include "td/telegram/net/DcOptions.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/SuggestedAction.h" - #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -88,6 +87,8 @@ class ConfigManager final : public NetQueryCallback { void get_app_config(Promise> &&promise); + void reget_app_config(Promise &&promise); + void get_content_settings(Promise &&promise); void set_content_settings(bool ignore_sensitive_content_restrictions, Promise &&promise); @@ -114,6 +115,7 @@ class ConfigManager final : public NetQueryCallback { FloodControlStrict lazy_request_flood_control_; vector>> get_app_config_queries_; + vector> reget_app_config_queries_; vector> get_content_settings_queries_; vector> set_content_settings_queries_[2]; @@ -142,6 +144,8 @@ class ConfigManager final : public NetQueryCallback { void request_config_from_dc_impl(DcId dc_id); void process_config(tl_object_ptr config); + void try_request_app_config(); + void process_app_config(tl_object_ptr &config); void do_set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions); diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index e53a3f50a..05e26c148 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1611,7 +1611,8 @@ class ExportChatInviteQuery final : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent) { + void send(DialogId dialog_id, const string &title, int32 expire_date, int32 usage_limit, bool creates_join_request, + bool is_permanent) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { @@ -1625,12 +1626,18 @@ class ExportChatInviteQuery final : public Td::ResultHandler { if (usage_limit > 0) { flags |= telegram_api::messages_exportChatInvite::USAGE_LIMIT_MASK; } + if (creates_join_request) { + flags |= telegram_api::messages_exportChatInvite::REQUEST_NEEDED_MASK; + } if (is_permanent) { flags |= telegram_api::messages_exportChatInvite::LEGACY_REVOKE_PERMANENT_MASK; } + if (!title.empty()) { + flags |= telegram_api::messages_exportChatInvite::TITLE_MASK; + } send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite( - flags, false /*ignored*/, std::move(input_peer), expire_date, usage_limit))); + flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), expire_date, usage_limit, title))); } void on_result(uint64 id, BufferSlice packet) final { @@ -1670,7 +1677,8 @@ class EditChatInviteLinkQuery final : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(DialogId dialog_id, const string &invite_link, int32 expire_date, int32 usage_limit) { + void send(DialogId dialog_id, const string &invite_link, const string &title, int32 expire_date, int32 usage_limit, + bool creates_join_request) { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); if (input_peer == nullptr) { @@ -1678,9 +1686,12 @@ class EditChatInviteLinkQuery final : public Td::ResultHandler { } int32 flags = telegram_api::messages_editExportedChatInvite::EXPIRE_DATE_MASK | - telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK; - send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( - flags, false /*ignored*/, std::move(input_peer), invite_link, expire_date, usage_limit))); + telegram_api::messages_editExportedChatInvite::USAGE_LIMIT_MASK | + telegram_api::messages_editExportedChatInvite::REQUEST_NEEDED_MASK | + telegram_api::messages_editExportedChatInvite::TITLE_MASK; + send_query(G()->net_query_creator().create( + telegram_api::messages_editExportedChatInvite(flags, false /*ignored*/, std::move(input_peer), invite_link, + expire_date, usage_limit, creates_join_request, title))); } void on_result(uint64 id, BufferSlice packet) final { @@ -1902,8 +1913,10 @@ class GetChatInviteImportersQuery final : public Td::ResultHandler { input_user = make_tl_object(); } - send_query(G()->net_query_creator().create(telegram_api::messages_getChatInviteImporters( - std::move(input_peer), invite_link, offset_date, std::move(input_user), limit))); + int32 flags = telegram_api::messages_getChatInviteImporters::LINK_MASK; + send_query(G()->net_query_creator().create( + telegram_api::messages_getChatInviteImporters(flags, false /*ignored*/, std::move(input_peer), invite_link, + string(), offset_date, std::move(input_user), limit))); } void on_result(uint64 id, BufferSlice packet) final { @@ -1925,13 +1938,16 @@ class GetChatInviteImportersQuery final : public Td::ResultHandler { vector> invite_link_members; for (auto &importer : result->importers_) { UserId user_id(importer->user_id_); - if (!user_id.is_valid()) { - LOG(ERROR) << "Receive invalid invite link " << user_id << " in " << dialog_id_; + UserId approver_user_id(importer->approved_by_); + if (!user_id.is_valid() || (!approver_user_id.is_valid() && approver_user_id != UserId()) || + importer->requested_) { + LOG(ERROR) << "Receive invalid invite link importer: " << to_string(importer); total_count--; continue; } invite_link_members.push_back(td_api::make_object( - td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkMember"), importer->date_)); + td->contacts_manager_->get_user_id_object(user_id, "chatInviteLinkMember"), importer->date_, + td->contacts_manager_->get_user_id_object(approver_user_id, "chatInviteLinkMember"))); } promise_.set_value(td_api::make_object(total_count, std::move(invite_link_members))); } @@ -1942,6 +1958,128 @@ class GetChatInviteImportersQuery final : public Td::ResultHandler { } }; +class GetChatJoinRequestsQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + + public: + explicit GetChatJoinRequestsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, const string &invite_link, const string &query, int32 offset_date, + UserId offset_user_id, int32 limit) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + auto input_user = td->contacts_manager_->get_input_user(offset_user_id); + if (input_user == nullptr) { + input_user = make_tl_object(); + } + + int32 flags = telegram_api::messages_getChatInviteImporters::REQUESTED_MASK; + if (!invite_link.empty()) { + flags |= telegram_api::messages_getChatInviteImporters::LINK_MASK; + } + if (!query.empty()) { + flags |= telegram_api::messages_getChatInviteImporters::Q_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::messages_getChatInviteImporters(flags, false /*ignored*/, std::move(input_peer), invite_link, + query, offset_date, std::move(input_user), limit))); + } + + void on_result(uint64 id, BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetChatJoinRequestsQuery: " << to_string(result); + + td->contacts_manager_->on_get_users(std::move(result->users_), "GetChatJoinRequestsQuery"); + + int32 total_count = result->count_; + if (total_count < static_cast(result->importers_.size())) { + LOG(ERROR) << "Receive wrong total count of join requests " << total_count << " in " << dialog_id_; + total_count = static_cast(result->importers_.size()); + } + vector> join_requests; + vector recent_requesters; + for (auto &request : result->importers_) { + UserId user_id(request->user_id_); + UserId approver_user_id(request->approved_by_); + if (!user_id.is_valid() || approver_user_id.is_valid() || !request->requested_) { + LOG(ERROR) << "Receive invalid join request: " << to_string(request); + total_count--; + continue; + } + if (recent_requesters.size() < 3) { + recent_requesters.push_back(user_id.get()); + } + join_requests.push_back(td_api::make_object( + td->contacts_manager_->get_user_id_object(user_id, "chatJoinRequest"), request->date_, request->about_)); + } + td->messages_manager_->on_update_dialog_pending_join_requests(dialog_id_, total_count, + std::move(recent_requesters)); + promise_.set_value(td_api::make_object(total_count, std::move(join_requests))); + } + + void on_error(uint64 id, Status status) final { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetChatJoinRequestsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class HideChatJoinRequestQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit HideChatJoinRequestQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, UserId user_id, bool is_approved) { + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return on_error(0, Status::Error(400, "Can't access the chat")); + } + + auto input_user = td->contacts_manager_->get_input_user(user_id); + if (input_user == nullptr) { + return on_error(0, Status::Error(400, "Can't find user")); + } + + int32 flags = 0; + if (is_approved) { + flags |= telegram_api::messages_hideChatJoinRequest::APPROVED_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::messages_hideChatJoinRequest( + flags, false /*ignored*/, std::move(input_peer), std::move(input_user)))); + } + + void on_result(uint64 id, BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for HideChatJoinRequestQuery: " << to_string(result); + td->updates_manager_->on_get_updates(std::move(result), std::move(promise_)); + } + + void on_error(uint64 id, Status status) final { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "HideChatJoinRequestQuery"); + promise_.set_error(std::move(status)); + } +}; + class RevokeChatInviteLinkQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; @@ -1960,7 +2098,7 @@ class RevokeChatInviteLinkQuery final : public Td::ResultHandler { int32 flags = telegram_api::messages_editExportedChatInvite::REVOKED_MASK; send_query(G()->net_query_creator().create(telegram_api::messages_editExportedChatInvite( - flags, false /*ignored*/, std::move(input_peer), invite_link, 0, 0))); + flags, false /*ignored*/, std::move(input_peer), invite_link, 0, 0, false, string()))); } void on_result(uint64 id, BufferSlice packet) final { @@ -5176,15 +5314,11 @@ void ContactsManager::get_account_ttl(Promise &&promise) const { td_api::object_ptr ContactsManager::convert_authorization_object( tl_object_ptr &&authorization) { CHECK(authorization != nullptr); - bool is_current = (authorization->flags_ & telegram_api::authorization::CURRENT_MASK) != 0; - bool is_official_application = (authorization->flags_ & telegram_api::authorization::OFFICIAL_APP_MASK) != 0; - bool is_password_pending = (authorization->flags_ & telegram_api::authorization::PASSWORD_PENDING_MASK) != 0; - return td_api::make_object( - authorization->hash_, is_current, is_password_pending, authorization->api_id_, authorization->app_name_, - authorization->app_version_, is_official_application, authorization->device_model_, authorization->platform_, - authorization->system_version_, authorization->date_created_, authorization->date_active_, authorization->ip_, - authorization->country_, authorization->region_); + authorization->hash_, authorization->current_, authorization->password_pending_, authorization->api_id_, + authorization->app_name_, authorization->app_version_, authorization->official_app_, authorization->device_model_, + authorization->platform_, authorization->system_version_, authorization->date_created_, + authorization->date_active_, authorization->ip_, authorization->country_, authorization->region_); } void ContactsManager::confirm_qr_code_authentication(const string &link, @@ -5765,20 +5899,6 @@ void ContactsManager::search_dialogs_nearby(const Location &location, td_->create_handler(std::move(query_promise))->send(location, false, -1); } -void ContactsManager::set_location(const Location &location, Promise &&promise) { - if (location.empty()) { - return promise.set_error(Status::Error(400, "Invalid location specified")); - } - last_user_location_ = location; - try_send_set_location_visibility_query(); - - auto query_promise = PromiseCreator::lambda( - [promise = std::move(promise)](Result> result) mutable { - promise.set_value(Unit()); - }); - td_->create_handler(std::move(query_promise))->send(location, true, -1); -} - vector> ContactsManager::get_chats_nearby_object( const vector &dialogs_nearby) { return transform(dialogs_nearby, [](const DialogNearby &dialog_nearby) { @@ -5841,6 +5961,20 @@ void ContactsManager::on_get_dialogs_nearby(Result &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid location specified")); + } + last_user_location_ = location; + try_send_set_location_visibility_query(); + + auto query_promise = PromiseCreator::lambda( + [promise = std::move(promise)](Result> result) mutable { + promise.set_value(Unit()); + }); + td_->create_handler(std::move(query_promise))->send(location, true, -1); +} + void ContactsManager::set_location_visibility() { bool is_location_visible = G()->shared_config().get_option_boolean("is_location_visible"); auto pending_location_visibility_expire_date = is_location_visible ? std::numeric_limits::max() : 0; @@ -5908,6 +6042,53 @@ void ContactsManager::on_set_location_visibility_expire_date(int32 set_expire_da update_is_location_visible(); } +void ContactsManager::get_is_location_visible(Promise &&promise) { + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( + Result> result) mutable { + send_closure(actor_id, &ContactsManager::on_get_is_location_visible, std::move(result), std::move(promise)); + }); + td_->create_handler(std::move(query_promise))->send(Location(), true, -1); +} + +void ContactsManager::on_get_is_location_visible(Result> &&result, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + if (result.is_error()) { + if (result.error().message() == "GEO_POINT_INVALID" && pending_location_visibility_expire_date_ == -1 && + location_visibility_expire_date_ > 0) { + set_location_visibility_expire_date(0); + update_is_location_visible(); + } + return promise.set_value(Unit()); + } + + auto updates_ptr = result.move_as_ok(); + if (updates_ptr->get_id() != telegram_api::updates::ID) { + LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; + return promise.set_value(Unit()); + } + + auto updates = std::move(telegram_api::move_object_as(updates_ptr)->updates_); + if (updates.size() != 1 || updates[0]->get_id() != telegram_api::updatePeerLocated::ID) { + LOG(ERROR) << "Receive unexpected " << to_string(updates); + return promise.set_value(Unit()); + } + + auto peers = std::move(static_cast(updates[0].get())->peers_); + if (peers.size() != 1 || peers[0]->get_id() != telegram_api::peerSelfLocated::ID) { + LOG(ERROR) << "Receive unexpected " << to_string(peers); + return promise.set_value(Unit()); + } + + auto location_visibility_expire_date = static_cast(peers[0].get())->expires_; + if (location_visibility_expire_date != location_visibility_expire_date_) { + set_location_visibility_expire_date(location_visibility_expire_date); + update_is_location_visible(); + } + + promise.set_value(Unit()); +} + int32 ContactsManager::on_update_peer_located(vector> &&peers, bool from_update) { auto now = G()->unix_time(); @@ -6012,6 +6193,7 @@ void ContactsManager::set_location_visibility_expire_date(int32 expire_date) { } else { G()->td_db()->get_binlog_pmc()->set("location_visibility_expire_date", to_string(expire_date)); } + // the caller must call update_is_location_visible() itself } void ContactsManager::update_is_location_visible() { @@ -7258,41 +7440,52 @@ Status ContactsManager::can_manage_dialog_invite_links(DialogId dialog_id, bool return Status::OK(); } -void ContactsManager::export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, - bool is_permanent, +void ContactsManager::export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, + bool creates_join_request, bool is_permanent, Promise> &&promise) { - get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, expire_date, usage_limit, is_permanent, + get_me(PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, title = std::move(title), expire_date, + usage_limit, creates_join_request, is_permanent, promise = std::move(promise)](Result &&result) mutable { if (result.is_error()) { promise.set_error(result.move_as_error()); } else { - send_closure(actor_id, &ContactsManager::export_dialog_invite_link_impl, dialog_id, expire_date, usage_limit, - is_permanent, std::move(promise)); + send_closure(actor_id, &ContactsManager::export_dialog_invite_link_impl, dialog_id, std::move(title), expire_date, + usage_limit, creates_join_request, is_permanent, std::move(promise)); } })); } -void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, - bool is_permanent, +void ContactsManager::export_dialog_invite_link_impl(DialogId dialog_id, string title, int32 expire_date, + int32 usage_limit, bool creates_join_request, bool is_permanent, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (creates_join_request && usage_limit > 0) { + return promise.set_error( + Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); + } + auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); td_->create_handler(std::move(promise)) - ->send(dialog_id, expire_date, usage_limit, is_permanent); + ->send(dialog_id, new_title, expire_date, usage_limit, creates_join_request, is_permanent); } -void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, int32 expire_date, - int32 usage_limit, +void ContactsManager::edit_dialog_invite_link(DialogId dialog_id, const string &invite_link, string title, + int32 expire_date, int32 usage_limit, bool creates_join_request, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + if (creates_join_request && usage_limit > 0) { + return promise.set_error( + Status::Error(400, "Member limit can't be specified for links requiring administrator approval")); + } if (invite_link.empty()) { return promise.set_error(Status::Error(400, "Invite link must be non-empty")); } + auto new_title = clean_name(std::move(title), MAX_INVITE_LINK_TITLE_LENGTH); td_->create_handler(std::move(promise)) - ->send(dialog_id, invite_link, expire_date, usage_limit); + ->send(dialog_id, invite_link, new_title, expire_date, creates_join_request, usage_limit); } void ContactsManager::get_dialog_invite_link(DialogId dialog_id, const string &invite_link, @@ -7354,6 +7547,32 @@ void ContactsManager::get_dialog_invite_link_users( ->send(dialog_id, invite_link, offset_date, offset_user_id, limit); } +void ContactsManager::get_dialog_join_requests(DialogId dialog_id, const string &invite_link, const string &query, + td_api::object_ptr offset_request, int32 limit, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + + if (limit <= 0) { + return promise.set_error(Status::Error(400, "Parameter limit must be positive")); + } + + UserId offset_user_id; + int32 offset_date = 0; + if (offset_request != nullptr) { + offset_user_id = UserId(offset_request->user_id_); + offset_date = offset_request->date_; + } + + td_->create_handler(std::move(promise)) + ->send(dialog_id, invite_link, query, offset_date, offset_user_id, limit); +} + +void ContactsManager::process_dialog_join_requests(DialogId dialog_id, UserId user_id, bool is_approved, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); + td_->create_handler(std::move(promise))->send(dialog_id, user_id, is_approved); +} + void ContactsManager::revoke_dialog_invite_link(DialogId dialog_id, const string &invite_link, Promise> &&promise) { TRY_STATUS_PROMISE(promise, can_manage_dialog_invite_links(dialog_id)); @@ -8484,8 +8703,7 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { if ((u == nullptr || !u->is_received) && (user_id == get_service_notifications_user_id() || user_id == get_replies_bot_user_id() || user_id == get_anonymous_bot_user_id())) { - int32 flags = telegram_api::user::ACCESS_HASH_MASK | telegram_api::user::FIRST_NAME_MASK | - telegram_api::user::APPLY_MIN_PHOTO_MASK; + int32 flags = USER_FLAG_HAS_ACCESS_HASH | USER_FLAG_HAS_FIRST_NAME | USER_FLAG_NEED_APPLY_MIN_PHOTO; int64 profile_photo_id = 0; int32 profile_photo_dc_id = 1; string first_name; @@ -8495,26 +8713,26 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { int32 bot_info_version = 0; if (user_id == get_service_notifications_user_id()) { - flags |= telegram_api::user::PHONE_MASK | telegram_api::user::VERIFIED_MASK | telegram_api::user::SUPPORT_MASK; + flags |= USER_FLAG_HAS_PHONE_NUMBER | USER_FLAG_IS_VERIFIED | USER_FLAG_IS_SUPPORT; first_name = "Telegram"; if (G()->is_test_dc()) { - flags |= telegram_api::user::LAST_NAME_MASK; + flags |= USER_FLAG_HAS_LAST_NAME; last_name = "Notifications"; } phone_number = "42777"; profile_photo_id = 3337190045231023; } else if (user_id == get_replies_bot_user_id()) { - flags |= telegram_api::user::USERNAME_MASK | telegram_api::user::BOT_MASK; + flags |= USER_FLAG_HAS_USERNAME | USER_FLAG_IS_BOT; if (!G()->is_test_dc()) { - flags |= telegram_api::user::BOT_NOCHATS_MASK; + flags |= USER_FLAG_IS_PRIVATE_BOT; } first_name = "Replies"; username = "replies"; bot_info_version = G()->is_test_dc() ? 1 : 3; } else if (user_id == get_anonymous_bot_user_id()) { - flags |= telegram_api::user::USERNAME_MASK | telegram_api::user::BOT_MASK; + flags |= USER_FLAG_HAS_USERNAME | USER_FLAG_IS_BOT; if (!G()->is_test_dc()) { - flags |= telegram_api::user::BOT_NOCHATS_MASK; + flags |= USER_FLAG_IS_PRIVATE_BOT; } first_name = "Group"; username = G()->is_test_dc() ? "izgroupbot" : "GroupAnonymousBot"; @@ -8524,7 +8742,6 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { telegram_api::object_ptr profile_photo; if (!G()->is_test_dc() && profile_photo_id != 0) { - flags |= telegram_api::user::PHOTO_MASK; profile_photo = telegram_api::make_object(0, false /*ignored*/, profile_photo_id, BufferSlice(), profile_photo_dc_id); } @@ -9776,20 +9993,26 @@ void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, boo if (c->is_photo_changed) { td_->messages_manager_->on_dialog_photo_updated(DialogId(chat_id)); drop_chat_photos(chat_id, !c->photo.small_file_id.is_valid(), true, "update_chat"); + c->is_photo_changed = false; } if (c->is_title_changed) { td_->messages_manager_->on_dialog_title_updated(DialogId(chat_id)); + c->is_title_changed = false; } if (c->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(chat_id)); + c->is_default_permissions_changed = false; } if (c->is_is_active_changed) { update_dialogs_for_discussion(DialogId(chat_id), c->is_active && c->status.is_creator()); + c->is_is_active_changed = false; + } + if (c->is_status_changed) { + if (!c->status.can_manage_invite_links()) { + td_->messages_manager_->drop_dialog_pending_join_requests(DialogId(chat_id)); + } + c->is_status_changed = false; } - c->is_photo_changed = false; - c->is_title_changed = false; - c->is_default_permissions_changed = false; - c->is_is_active_changed = false; LOG(DEBUG) << "Update " << chat_id << ": need_save_to_database = " << c->need_save_to_database << ", is_changed = " << c->is_changed; @@ -9825,9 +10048,11 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from if (c->is_photo_changed) { td_->messages_manager_->on_dialog_photo_updated(DialogId(channel_id)); drop_channel_photos(channel_id, !c->photo.small_file_id.is_valid(), true, "update_channel"); + c->is_photo_changed = false; } if (c->is_title_changed) { td_->messages_manager_->on_dialog_title_updated(DialogId(channel_id)); + c->is_title_changed = false; } if (c->is_status_changed) { c->status.update_restrictions(); @@ -9849,6 +10074,10 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from if (!c->status.is_member()) { remove_inactive_channel(channel_id); } + if (!c->status.can_manage_invite_links()) { + td_->messages_manager_->drop_dialog_pending_join_requests(DialogId(channel_id)); + } + c->is_status_changed = false; } if (c->is_username_changed) { if (c->status.is_creator() && created_public_channels_inited_[0]) { @@ -9860,6 +10089,7 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from } } } + c->is_username_changed = false; } if (c->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(channel_id)); @@ -9867,6 +10097,7 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from RestrictedRights(false, false, false, false, false, false, false, false, false, false, false)) { remove_dialog_suggested_action(SuggestedAction{SuggestedAction::Type::ConvertToGigagroup, DialogId(channel_id)}); } + c->is_default_permissions_changed = false; } if (!td_->auth_manager_->is_bot()) { if (c->restriction_reasons.empty()) { @@ -9876,12 +10107,6 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from } } - c->is_photo_changed = false; - c->is_title_changed = false; - c->is_default_permissions_changed = false; - c->is_status_changed = false; - c->is_username_changed = false; - LOG(DEBUG) << "Update " << channel_id << ": need_save_to_database = " << c->need_save_to_database << ", is_changed = " << c->is_changed; c->need_save_to_database |= c->is_changed; @@ -10175,8 +10400,8 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u } on_update_user_full_common_chat_count(user_full, user_id, user->common_chats_count_); - on_update_user_full_need_phone_number_privacy_exception( - user_full, user_id, (user->settings_->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0); + on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, + user->settings_->need_contacts_exception_); bool can_pin_messages = user->can_pin_message_; if (user_full->can_pin_messages != can_pin_messages) { @@ -10460,6 +10685,9 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_dialog_theme_name(DialogId(chat_id), std::move(chat->theme_emoticon_)); + td_->messages_manager_->on_update_dialog_pending_join_requests(DialogId(chat_id), chat->requests_pending_, + std::move(chat->recent_requesters_)); + auto bot_commands = get_bot_commands(std::move(chat->bot_info_), &chat_full->participants); if (chat_full->bot_commands != bot_commands) { chat_full->bot_commands = std::move(bot_commands); @@ -10503,6 +10731,9 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_dialog_theme_name(DialogId(channel_id), std::move(channel->theme_emoticon_)); + td_->messages_manager_->on_update_dialog_pending_join_requests(DialogId(channel_id), channel->requests_pending_, + std::move(channel->recent_requesters_)); + { MessageTtlSetting message_ttl_setting; if ((channel->flags_ & CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL) != 0) { @@ -12557,8 +12788,10 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, invite_link_info->dialog_id = DialogId(); invite_link_info->title = chat_invite->title_; invite_link_info->photo = get_photo(td_->file_manager_.get(), std::move(chat_invite->photo_), DialogId()); + invite_link_info->description = std::move(chat_invite->about_); invite_link_info->participant_count = chat_invite->participants_count_; invite_link_info->participant_user_ids = std::move(participant_user_ids); + invite_link_info->creates_join_request = std::move(chat_invite->request_needed_); invite_link_info->is_chat = (chat_invite->flags_ & CHAT_INVITE_FLAG_IS_CHANNEL) == 0; invite_link_info->is_channel = (chat_invite->flags_ & CHAT_INVITE_FLAG_IS_CHANNEL) != 0; @@ -12823,6 +13056,7 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti bool need_drop_invite_link = c->status.can_manage_invite_links() && !status.can_manage_invite_links(); c->status = std::move(status); + c->is_status_changed = true; if (c->status.is_left()) { c->participant_count = 0; @@ -13416,14 +13650,10 @@ void ContactsManager::on_update_bot_stopped(UserId user_id, int32 date, bool is_ LOG(ERROR) << "Receive updateBotStopped by non-bot"; return; } - if (!user_id.is_valid() || date <= 0) { + if (date <= 0 || !have_user_force(user_id)) { LOG(ERROR) << "Receive invalid updateBotStopped by " << user_id << " at " << date; return; } - if (!have_user_force(user_id)) { - LOG(ERROR) << "Receive updateBotStopped by unknown " << user_id; - return; - } DialogParticipant old_dialog_participant(DialogId(get_my_id()), user_id, date, DialogParticipantStatus::Banned(0)); DialogParticipant new_dialog_participant(DialogId(get_my_id()), user_id, date, DialogParticipantStatus::Member()); @@ -13537,6 +13767,24 @@ void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId new_dialog_participant); } +void ContactsManager::on_update_chat_invite_requester(DialogId dialog_id, UserId user_id, string about, int32 date, + DialogInviteLink invite_link) { + if (!td_->auth_manager_->is_bot() || date <= 0 || !have_user_force(user_id) || + !td_->messages_manager_->have_dialog_info_force(dialog_id)) { + LOG(ERROR) << "Receive invalid updateBotChatInviteRequester by " << user_id << " in " << dialog_id << " at " + << date; + return; + } + td_->messages_manager_->force_create_dialog(dialog_id, "on_update_chat_invite_requester", true); + + send_closure(G()->td(), &Td::send_update, + td_api::make_object( + dialog_id.get(), + td_api::make_object( + get_user_id_object(user_id, "on_update_chat_invite_requester"), date, about), + invite_link.get_chat_invite_link_object(this))); +} + void ContactsManager::update_contacts_hints(const User *u, UserId user_id, bool from_database) { bool is_contact = is_user_contact(u, user_id, false); if (td_->auth_manager_->is_bot()) { @@ -15994,8 +16242,7 @@ tl_object_ptr ContactsManager::get_secret_chat_object_const( secret_chat->is_outbound, secret_chat->key_hash, secret_chat->layer); } -tl_object_ptr ContactsManager::get_chat_invite_link_info_object( - const string &invite_link) const { +tl_object_ptr ContactsManager::get_chat_invite_link_info_object(const string &invite_link) { auto it = invite_link_infos_.find(invite_link); if (it == invite_link_infos_.end()) { return nullptr; @@ -16008,8 +16255,10 @@ tl_object_ptr ContactsManager::get_chat_invite_link_ string title; const DialogPhoto *photo = nullptr; DialogPhoto invite_link_photo; + string description; int32 participant_count = 0; vector member_user_ids; + bool creates_join_request = false; bool is_public = false; bool is_member = false; td_api::object_ptr chat_type; @@ -16054,12 +16303,15 @@ tl_object_ptr ContactsManager::get_chat_invite_link_ default: UNREACHABLE(); } + description = get_dialog_about(dialog_id); } else { title = invite_link_info->title; invite_link_photo = as_fake_dialog_photo(invite_link_info->photo, dialog_id); photo = &invite_link_photo; + description = invite_link_info->description; participant_count = invite_link_info->participant_count; member_user_ids = get_user_ids_object(invite_link_info->participant_user_ids, "get_chat_invite_link_info_object"); + creates_join_request = invite_link_info->creates_join_request; is_public = invite_link_info->is_public; if (invite_link_info->is_chat) { @@ -16082,7 +16334,8 @@ tl_object_ptr ContactsManager::get_chat_invite_link_ return make_tl_object(dialog_id.get(), accessible_for, std::move(chat_type), title, get_chat_photo_info_object(td_->file_manager_.get(), photo), - participant_count, std::move(member_user_ids), is_public); + description, participant_count, std::move(member_user_ids), + creates_join_request, is_public); } UserId ContactsManager::get_support_user(Promise &&promise) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index c75638389..004a8dede 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -204,6 +204,8 @@ class ContactsManager final : public Actor { void on_update_channel_participant(ChannelId channel_id, UserId user_id, int32 date, DialogInviteLink invite_link, tl_object_ptr old_participant, tl_object_ptr new_participant); + void on_update_chat_invite_requester(DialogId dialog_id, UserId user_id, string about, int32 date, + DialogInviteLink invite_link); int32 on_update_peer_located(vector> &&peers, bool from_update); @@ -319,6 +321,8 @@ class ContactsManager final : public Actor { void set_location_visibility(); + void get_is_location_visible(Promise &&promise); + FileId get_profile_photo_file_id(int64 photo_id) const; void set_profile_photo(const td_api::object_ptr &input_photo, Promise &&promise); @@ -384,10 +388,12 @@ class ContactsManager final : public Actor { void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); - void export_dialog_invite_link(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, + void export_dialog_invite_link(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, + bool creates_join_request, bool is_permanent, Promise> &&promise); - void edit_dialog_invite_link(DialogId dialog_id, const string &link, int32 expire_date, int32 usage_limit, + void edit_dialog_invite_link(DialogId dialog_id, const string &link, string title, int32 expire_date, + int32 usage_limit, bool creates_join_request, Promise> &&promise); void get_dialog_invite_link(DialogId dialog_id, const string &invite_link, @@ -404,6 +410,12 @@ class ContactsManager final : public Actor { td_api::object_ptr offset_member, int32 limit, Promise> &&promise); + void get_dialog_join_requests(DialogId dialog_id, const string &invite_link, const string &query, + td_api::object_ptr offset_request, int32 limit, + Promise> &&promise); + + void process_dialog_join_requests(DialogId dialog_id, UserId user_id, bool is_approved, Promise &&promise); + void revoke_dialog_invite_link(DialogId dialog_id, const string &link, Promise> &&promise); @@ -568,7 +580,7 @@ class ContactsManager final : public Actor { tl_object_ptr get_chat_member_object(const DialogParticipant &dialog_participant) const; - tl_object_ptr get_chat_invite_link_info_object(const string &invite_link) const; + tl_object_ptr get_chat_invite_link_info_object(const string &invite_link); UserId get_support_user(Promise &&promise); @@ -728,6 +740,7 @@ class ContactsManager final : public Actor { bool is_title_changed = true; bool is_photo_changed = true; bool is_default_permissions_changed = true; + bool is_status_changed = true; bool is_is_active_changed = true; bool is_changed = true; // have new changes that need to be sent to the client and database bool need_save_to_database = true; // have new changes that need only to be saved to the database @@ -929,8 +942,10 @@ class ContactsManager final : public Actor { // unknown dialog string title; Photo photo; + string description; int32 participant_count = 0; vector participant_user_ids; + bool creates_join_request = false; bool is_chat = false; bool is_channel = false; bool is_public = false; @@ -973,6 +988,7 @@ class ContactsManager final : public Actor { static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit for first/last name static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat/channel description static constexpr size_t MAX_BIO_LENGTH = 70; // server side limit + static constexpr size_t MAX_INVITE_LINK_TITLE_LENGTH = 32; // server side limit static constexpr int32 MAX_GET_CHANNEL_PARTICIPANTS = 200; // server side limit static constexpr int32 CHANNEL_PARTICIPANT_CACHE_TIME = 1800; // some reasonable limit @@ -1028,6 +1044,7 @@ class ContactsManager final : public Actor { static constexpr int32 CHAT_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; static constexpr int32 CHAT_FULL_FLAG_HAS_ACTIVE_GROUP_CALL = 1 << 12; static constexpr int32 CHAT_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 14; + static constexpr int32 CHAT_FULL_FLAG_HAS_PENDING_REQUEST_COUNT = 1 << 17; static constexpr int32 CHANNEL_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHANNEL_FLAG_USER_HAS_LEFT = 1 << 2; @@ -1078,6 +1095,7 @@ class ContactsManager final : public Actor { static constexpr int32 CHANNEL_FULL_FLAG_IS_BLOCKED = 1 << 22; static constexpr int32 CHANNEL_FULL_FLAG_HAS_EXPORTED_INVITE = 1 << 23; static constexpr int32 CHANNEL_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 24; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_PENDING_REQUEST_COUNT = 1 << 28; static constexpr int32 CHAT_INVITE_FLAG_IS_CHANNEL = 1 << 0; static constexpr int32 CHAT_INVITE_FLAG_IS_BROADCAST = 1 << 1; @@ -1383,11 +1401,14 @@ class ContactsManager final : public Actor { void set_location_visibility_expire_date(int32 expire_date); + void on_get_is_location_visible(Result> &&result, Promise &&promise); + void update_is_location_visible(); static bool is_channel_public(const Channel *c); - void export_dialog_invite_link_impl(DialogId dialog_id, int32 expire_date, int32 usage_limit, bool is_permanent, + void export_dialog_invite_link_impl(DialogId dialog_id, string title, int32 expire_date, int32 usage_limit, + bool creates_join_request, bool is_permanent, Promise> &&promise); void remove_dialog_access_by_invite_link(DialogId dialog_id); diff --git a/td/telegram/CountryInfoManager.cpp b/td/telegram/CountryInfoManager.cpp index e9efe9dea..719d740af 100644 --- a/td/telegram/CountryInfoManager.cpp +++ b/td/telegram/CountryInfoManager.cpp @@ -380,6 +380,7 @@ void CountryInfoManager::on_get_country_list(const string &language_code, void CountryInfoManager::on_get_country_list_impl(const string &language_code, tl_object_ptr country_list) { CHECK(country_list != nullptr); + LOG(DEBUG) << "Receive " << to_string(country_list); auto &countries = countries_[language_code]; switch (country_list->get_id()) { case telegram_api::help_countriesListNotModified::ID: @@ -395,6 +396,8 @@ void CountryInfoManager::on_get_country_list_impl(const string &language_code, auto list = move_tl_object_as(country_list); if (countries == nullptr) { countries = make_unique(); + } else { + countries->countries.clear(); } for (auto &c : list->countries_) { CountryInfo info; @@ -441,68 +444,70 @@ const CountryInfoManager::CountryList *CountryInfoManager::get_country_list(Coun if (language_code == "en") { static const BufferSlice en = gzdecode( base64url_decode( - "eJyNW81uI0lyzhb13-qeaXs8uwdjQMDAehfwLMj6ryP_RZFFsVmk_m5JMofMUbFKU6ySVnqBve0jGAb2sNiDb_YTtH034LfYi1_" - "BUaTEKlYFsxrobqmpjMzIyIgvvohM_Wv4P3_8h__6z3_8GyHkn_70xy_whexV6uSo4k4936fRz97BZ__" - "334XqHnwtyLr29tnxzU1x9WdLtkG-G7k8YNNixafjYmPBfRqwJfwsM5epl98-e7-aaPX3Jp6rSd5XfprNqcuXAXXT8numTBLymwli-" - "Rb5VHEDPgtp8TfFKvXH4ZRm9dgvS5qR0O_" - "4TZGtfbXJccWdhdxxKLKXaA4ld44u2NUZw3YQu6qq2BYWyPoLhsrqyttnp7FsYt1Lcgi6e6B5RlZSFLENB-" - "Sk4s8YmNFN2W71c1VJrGOTj5UF8_mEukWbLrzsevtlzci30xD2Gi4DP7vXPSU-" - "89O15M32XkegbyRLHY6c9Z5WTvjMSj5l52tyUPHDMSJbkExdrPcdOa28MH9M-c_URfzdVIRnXK2Q76reEo4Y3PWc-" - "S9s5j2uzJ6dSzZ04ZlXq-Q48ng69bDYA39VtLxzqNbJaZW6M4dO2XKe1cEwSmIdGuSoypwZDxeZc5ClzXxnN29AksKSapOcVUP_" - "HkxQbNKlh9lBkrRtm67_xHO0wA6hM6M-T9uRrOPOjMeeg7507lOewRrAKvnts5Ob2FixbBtkQz90pzyro5o6q-" - "K2z1YvyEEVIttF92eKbWxFNvYXgGzZM1aUcu4Z98inKqjNeLFO_XC5pA5dIHpo8f7xeS5BD8_hjwg-qWZ5O2bT-_9Mfqh6LuU--" - "5eizd2g2IAIpgEPlxAINh3T7LmpZuLcBuSw6tMX7mTjXVU3n314XTh7dvbq3OmCLrE4kXJtOIT152GAxryuis_" - "vGvzTC5ZPFI9zScuJ89tVjEVHh-QFcU6p3oHezOEvDLFvqbwZV6uQwxroN90-hwJ8fVde6_" - "zbSEYpyQXV0Au6USoYklqQZA18v1TQSgr4j1TQdaMgwfeKXIb5tYJeMgtmCT43pCiOIReZBckwC7IiF2RNLShlDcbqBbVsFlTFKGhlua" - "ApMH9JLRglHWSjMQaMgb-wjlpWCqoOY41ywQAZQ4d5YB1Nhu9jO54l8tzWOdbq5FPNg0RZ_G2dLX5fHLCH3_" - "8OOxNFFtq11iTf1SBjQg4qVn5ap8NoLvR8ZTF-1Vrk7FWnV3UwfSSxPufkvf3EA0hNDnWn2ZyazImYfJt8rH35j4AVp__" - "cfvQgTDGcUrfjrJiNs1on2ot3X2wvIz3SeWnld5ohxeO75KA25w5D8riqifN4zSLHNQpkxPNc3O4bf0DxvNZbrZ3mPKu1DS2JKYnVk_" - "KXsL7neItxJu-s9dfT-Q_xxwE5rXmAhMUB-FB2DxBD4j2MyH5tRWWQ9dM8KoXJtStYmz6w4hXzp9h5y4YQE2uAa7XQp1_-QtN5-" - "90rfr999s5Myt0C1jw_AJwhnEfNObM78NMXNplH0RaOHT7JzqFIpYSvZ_luHThLC_IpdZ-zcWKu5tt_PbMN_YzmKmub_74v6_" - "H3slYqF5WyaSTXuCDH9Z_52AsDjvqmKgsxod4hR3XmLqh_" - "n9UxzneojeoQF3Vvwd3IozBeqGs6ycl39Uvy8W2OV2SLxhe25jFK5o55EmOkrxij7BoT63MX1VUzhtQLBaksp-" - "IsyzMbNXLUmIRAlX2Eu4ixvgH-0lgGHl6XScKzaLTIQWP2_BAg8SmVkviQhJiE_ADW9nngM2TfMec6iWlDQtYmB_YDxTjnnqyI6-" - "rGkBw3gjn3HlBsA_8V1_VN4MpN7qZzEVnHuBGPuyD7Tf4zGiOabgp9otkh3zapcx8tIso3aqkUy1jk1OIT33PZkiOcUzNjTtS8JB-" - "a1PdYPDlyBhuMPErbsAmctelTd5LF1j1ZTuSX4pbTbuRbFXLQomMPrRmUbb79KhzLVsnH1x5Nh7uzqbfI6qBs-" - "8AboMVz1AEnfRaRQqTu0OW8mG2tcNbzZ0jcmKYq9L9WE2wPawPQt0JOsb4ExK0Sjz8HW80z417zmDi-" - "W21y0uJjoHIB9bE6XC0JY7zVBXmfsZW3oz5i7vSRlkUOW3TFIBC-VRLbtweyIXcZKivu-bT65LQFcMgcL3xAcr9qbtbeiamtz-" - "RT45eQBh6gslN8VQXzVXE-bkGcRPZD42QLI3EO1RqC_UENtqAOtr5akrZiJc2DWsChQH6BYNB-WdNz6-" - "vWNfmw3vyPVQ7VdYjaQBXPcRud5XPkwIifx3z5vENOzqFWKHbgH2QdQxXv9bxHjkF-CrQN7deqJUXo6-eQj2q-" - "B4U7kgsNcR18PiQH55RHhAhZ1xSvOyJH56E7o36Ws8kaEfpXu05O2u70FfKRfmHcpzqNYTBeuw041vYZVlcBNoixpd0lh-" - "2lT1m2b1EwdXFN14b6BPRGdTbL29j9qnUse0nO6pxBTdmi_mRHDlc0oU-2P5P9tk9_wWRNTcmNy_ZgJY_VZnumkR_XbRvsPtlld0WsO_" - "haO6DOM8Z9TLG_XFjk6IIuKFDfLB4Yem5P9eKSHF54_hTtFWmJM0dq2os-" - "ObigDxTjbEY5aTOcL3Ya5KDD3GcUh1VxTui0yGnn2Z89vyzxPpepCeOkc76qx8feFPc3QxXjQ6dNjjvc52OK10yaIa6FO3BuNW_h-" - "V62TyZpYi7Z6ZG_t2nUl-zwIIh6kj32yJFeoaHl1iqdPnnf8_xgDiCd4u1kbYeYj3YG5L0NRSIydn3mUtJXN2cey1-Tw074BKiK-" - "Zoqttct1NL0GergDcHN7leGajNvv3fgN_SF3s8zfrPqH-pr-Wi-d1r8_zX33TjCNvftVsh-l6L3GmA_" - "LSFfTLhSQr5KjrpsTF0P6fNrZaEfdmtwJitf6IaTbJ6D-lnNvUvstsmHLmeTecDcZcDQvr8iifvtXcj1ts-" - "LXereozlASfvGNpZ0IVd3-RivmWVx_da1I_stvWDuIbGkie0HfKzLg3kY3YJifFrf4tNrHEnue0ROu-" - "EfGCBJ6M8wPp7Mm1kc616Rwy4NHjFOLetl8dq35ABshuNn2RDazKqQIwvQZzJBbFYW53qrRg4tz6UTD9NZ3BOz6tG6ztR7xPoTsvCsrA" - "bUw54LXspm_vbaK6ySE71aC_KDBcXgjC4nUCghfrGx7VkyLpM-bZ2Tby3qL-fUcYQ94gTntTrkmzWeWnTCpngfxhDfoVldsm9RB-_" - "FSeJ7PwvyigXUfEH9tJ57ppoYB9zaivr4Dp7_TD0VNylubl2SA9ghWj8Yao6OffLrlY2Y74KZ_Khk3mHft7qmlIdh1ufovP2Au_" - "yXELs_MrV47CAaG_o8WAU9YmNxn8yy1364ZL5Ps7kM9M1_e2ENI_tBEY_W8OI7GGtETtb68xDt88jiHoB1BWdPnSl_" - "3PEORhPX8hbkcZCnTxyLK_Edn3UDsuwPfJKN3z1VSqxxu9KRPi_x-wrtK_" - "qR1l10Ti9RxyLtE68cU4yRPcDIHl3wMZaTNPHbiV6NfOixp2KNOmsUQO-" - "W9Hg8cOEenzGspyNJutAfek3yEeLpJ895u8pC1tJjjOq1YF98R39aVnLt2muTkx4UHD6dhZj_qiUxl-t1gXOyKPxXIZ-" - "tecobDpbuN34w9VinrTmhjgEbPNFnpHcY323h-kAd02MP1EFxUM-3xwDkIR5D7Iz1uO_" - "XG5H9HgdPRH1BjsfdRfZ5Kt4xit6Paop4P5cW2b9coLUsxLYhlO1XyGEf8HiB3u-Jc3u_Qfb7LDIDcr-33QPI8KB-k3z72kPte87za_" - "sDs1P8zqLfIt_26UNIi5G13lp62Bmoscw5ed-fc4c_PMDwrO9pchJX8Fq_3yHHfXq_egSI7dXMvCVC5uiCnb1MrK791cixlUV-" - "teb8fQ4piEEBaEUI52zXD2R9ZvH9RR_qt37I_MCLLnC9txhL1ArGrru2eIwp59aUfZuc9AH3lpCNkbtyiKl0j3UbO_tDsC_" - "wg3AWhSSWH8ti-1yTA1g_zU1e_SeuZ_u30TmuUOwZW0fN6ft_rpCDzxTvw5s5byAHDXI8-PLX0OU73gNo4rphcEmOBt4Ce7u5p5TE_" - "jOwyaHNfLSPLxviWmswIn83CJcQnW6xyaYMCFC0g_Q8ybp5ZwwMgEcMniAEMA6mpn1kew92hZzZNJzy1bPeHRxW04R7savkGzt6E-" - "FtmgoIdsQ52q6RU5s9T-bMAefGepRGfOZx0orl6-" - "TADqdYvyx6ayTWtQFn9gQGz9p6T9nqKSOyLajNuTujD56PvV3Rcu7o7fPI1hHenDOHbd6F7SXtJCl64g5pc3aHab-32-" - "TYdrxH5uJnJhviet3urOUBflF5RRJjg92FvUSoSYtd5rnpXPzKgSSxDhb4AV2XLi5aAxu5vm_3yJEdFbLUQfieOAZtiH0bYt_B-Juao_" - "sA7AelA2R4dO9qzpti237rAa48Gekd5Og-JN_bX_7sFYfe4stfIXP1_S__7k44dq8nyck-6BvlS-" - "hyRd43nKJNnUf8rURJXIfaN5EvgFtbFCpHhuEw5ESpTHLynX0Lcf3so_0jU5PF_nhHjhvLJxoVrqgv5vC1YY18Pwz9-6gLXKOQ1ZeC_" - "qiW_3ZlWCf7tTmd4nGRqu1SdciwRfaH3mzHu2hD6JfDc3I8nFO-vpZGMCrnfd3wgpwO6c_" - "8lZVlz8EU97SGHXI09O4ZsIZsDjBjzjCE2mXIF57_Yxf4DUPyRWKsRc6is4l-S2IHVwS9kj669vGt8wWcGAJLWKJvicRYObwkB0MPcB_" - "lxHE_ZDggh5GeDKmdzK-40x4OyachQAqf0im44dAb08gJsv5n5P9ey_Aq0uWROiHG3WIuOwTuMKT8CTtrw9CEMTeEmBtS94XuyEFSzu-" - "-jCrkaHQfPctnGVk5_h2Es41sqq84apHD0WwX7xFzlpFNCiO7kuFb5a_gW6Nb0NsPI7qL4P42d0n3-" - "0Z35HT0Mma748vI3fdVjfyw5hFX3J0w-PqbYjBnxfW7nagcw_A3__" - "dzrhrk5Apy6QuUQAgG76lfcX971SLfV6OO2nIO2vkzLrpnkr5Cpzb5NLLTU6F3VqXcuQADrjgLIGenY2LPSPy-" - "09UIxlE3pAGCYXocO9dN8s01dRweJY1mGIRu9t5PM-K3ZdfR27zVr09hMbmqraPP9m5a5IeWA9HvFC1vzB0GqTkArswD-" - "O4Z0HKRlTcS69x0yGHHW3qPGKcyduby9dpX5Pu2GzDfXRUkoEKPBU8eJMYsVzUMKfF93He5bZCDW7ZgGD83NX0XpqzWv4V6_vbHTD2_-" - "pkixXeKyro22qx5F9UxKz61fpmP4NGepBMhJtxZ5PBu17usuJeLy16T4zu-GNPxE86FNfTNyJ__99_-9v8o_old") + "eJyNW0tz48iRrhb1lrpn2h6PfXBMMGIjvN6IHQeJN458iyJBsQlSr1uRrCFrBAIaEJAs_QHf9ifsxQeHD77t_oK27_" + "4Pvvmyl73sbROkSIBEstAR3S01VVmVlZX55ZdZpf8M__6HX_z1v3_9T0LIv_zHHz7DF7JXqpKjkjv2fJ9GP3sHn_" + "3P33LlPfiak3Vt9dnx7W1-8WdDtka-Gbg8YON8yafDfG3GfRqwOfwsNZepF1efnS0mWvy9jeeqk7PSD5Mpdfk8oO62_" + "J4pk4T8eoJYvkE-ltyAT0Ka_02-TP1hOKZpPfaLkmYk9DteKbKxryY5LrmTkDsORfYSzaFkztEGuzpD2A5iV1UV28ICWX_" + "GUFldWX12Gssm1r0ih6C7B5qnZCVFEduwR05K_oSBGd0t2y1-riqJdWzyoTRjPh9RN2_TmZdeb7-oGdl26sNew3ngp_" + "e6p8RnfrqS3djrAPSNZKnDkbPe04oJn1lIb9n5hhyU_HCIyOYkUxfrfU9OS6_MH1L-I3URfzcV4RmXS-SbsjeHIwZ3vWD-K5t4Twuzp-" + "eSDV145uUyOY48no49LPbAXxUt6xzKVXJapu7EoWM2n6Z1MIyCWIcaOSozZ8LDWeocZGk93_ntCki2sKRcJ-fl0H8AE-" + "TrdO5hdpAkbdOmyz_xHA2wQ-hMqM-37UiWcWfGYy9AXzr1KU9hDWCVvPrs5DY2VizbBNnQD90xT-" + "uobp1VftNny5fkoAyR7aL7M8U2tiIb-zNAtvQZK0ox84w75GMZ1GY8X6V-OJ9Th84QPbR4__g8V6CH5_" + "AnBJ9UsxjvYe32CdlP5Luy51Lus3_P29wN8jWIYBrwcA6BYNMhTZ-baibOrUcOyz595U463lV1_dn7FVykzs5enDud0TkWJ1KmDfuw_" + "jQM0JjXVfH53YB_esH8meJxLmkZcX63iLHo6JC8IM4p5XvQmzn8lSH2LRTX4yolclgB_cab55CDr--KS51_" + "G8koBTmnGnpONwo5Q1JzkqyB7xdyWkEB_" + "5Fyum7kJPhekYswv5bTC2bOLMDnhhTFMeQiMycZZk5W5JysqTmlqMFYPacWzZyqGDmtKOc0BeYvqDmjoINsNMaAMfAX1lGLSk7VYaxRz" + "BkgY-gwD6yjyfB9bMfzRJ7bOMdKlXyseJAo87-tstnv8j32-Lt_w85EkYV2rdTJNxXImJCD8qUflukwmgs9X1mMX5UGOX_" + "T6U0dTB9JrM8FObOfeQCpyaHuOJ1TkzkRk2-SD5XP_xWw_Phfm08ehCmGU-pmnOXTcVZpRXvxHvLNeaTHdl5a-" + "J1mSPH4NjmoTLnDkDyuauI8XrHIcYUCGfE8F7f72h9QPK90Fmtvc57F2oaWxJTE6kn5K1jfc7zZMJV3lvrr2_" + "kP8cceOa14gIT5HvhQeg8QQ-I9DMh-ZUFlkPWTPArB5Mo1rE0fWf6a-" + "WPsvGVDiIkVwLVK6NPPf6LbefvdG36vPntnJuXuAGteHgHOEM6jZpzZPfjpKxtNo2gLhw4fpedQpELC19N8twqcpQH5lLov6TgxF_" + "Ptv53ZinxG-z4rauv_nhX1-HtZKxTzStE0kmtckuPqj3zohQFHfVOVhZhQbZGjKnNn1H9I6xjnO9RGVYiLqjfjbuRRGC_" + "UNZ1k5LvqFfmwmuMN2aLxuY15jIK5Y57EGOkLxii7xsT63Ed11YQh9UJOKspbcZbmmbUKOaqNQqDKPsJdxFhfA3-" + "pzQMPr8sk4VnUGuSgNnl5DJD4lApJfEhCTEK-B2v7PPAZsu-Yc53EtCEha5MD-5FinHNPVsR1da1PjmvBlHuPKLaB_4rr-" + "jpw5Tp3t3MRWca4EY-7JPt1_iMaI5puCn2i3iJf16nzEC0iyjdqoRDLWOTU4iPfc9mcI5xTM2NOVL8i7-" + "vU91g8OXIGa4w82rZhHThr3afuKI2te7KcyC_" + "5DaddyzdK5KBBhx5aMyibfPtNOJYtkw9vPZoWdydjb5bWQdn0gRWgxXNUASd9FpFCpO7Q5ayYbSxw1vMnSNyYpir0v0YdbA9rA9A3Qk6" + "xvgTErRKPvwBbTVPj3vKYOL4bTXLS4EOgcgH1sTpcLQhjvNEGeZ-xhbejPmLu9JGGRQ4bdMEgEL5VENu3A7IhdxkqK-" + "75NLrktAFwyBwvfERyv2qu196JqY1P5GPtp5AGHqCyk39TBfNVcT5uQJxE9kPjZAMjcQ7V6IP9QQ02ow62vlqQNmJlmwc1gEOB_" + "AzBoP2ipmfW140b8n65-e_LHKrrELWBKp7jLjrLl8iBET-P-fJFi5xcQK2Qb8E_yDqGKt7rRYccg_" + "wYaBvar1ULitDXLyAfVXwPCnckFxriOviiTw4uKI8IEbKuKV53QI4uQndC_" + "TRnkzUi9K9mlZw03fEb5CP9wrhPhfYbm4BjTZ9hdRVggxhbmm1y2Jz7lKX7FjlTF9d0TahPQG9UZ7O4id1vWseyV-" + "S8yhnUlA3qj3bkcEUT-mTzE9lv-vQnTNbUlMy4bPYW8lhttmca2XHdtMHuo112V8S6g681A-q8YNzHFPvLpUWOLumMAvVN44GhZ_" + "ZUL6_I4aXnj9FekZY4c6SmveySg0v6SDHOZhSTNsP5YqtGDlrMfUFxWBXnhFaDnLZe_MnL6xzvc5maME5aF4t6fOiNcX8zVDE-" + "tJrkuMV9PqR4zaQZ4lq4BedW8Wae76X7ZJIm5pKtDvm5TaO-" + "ZIsHQdST7LAnjvQKDS2zVml1yVnH84MpgPQWbydLO8R8tNUjZzYUicjY5ZlLSV9dn3ksf0MOW-" + "EzoCrma6rYXndQS9MXqIPXBDe9Xxmqzaz93oPf0Ff6ME35zaJ_qC_lo_neafH_l9x37Qib3LddIvttit5rgP20hHw-4UoJ-" + "TI5arMhdT2kz68VhX7YrsCZLHyhHY7SeQ7qZzXzLrHdJO_bnI2mAXPnAUP7_ook7re3IdfbPs-" + "3qfuA5gBl2zc2saQNubrNh3jNLIvrt7Yd2W_uBVMPiSVNbD_gY20eTMPoFhTj0_oGn17iSHLfA3LaDn_PAElCf4Lx8WTeTONY-" + "5octmnwhHFqWS-K174jB2AzHD-LhtBmVokcWYA-oxFis6I411sVcmh5Lh15mM7inphVjdZ1xt4T1p-" + "QhWdl1aAe9lzwUjbxN9deYJWc6NVakB8sKAYndD6CQgnxi7Vtz5NxmfRp64J8bVF_PqWOI-" + "wRJziv1SJfLfHUoiM2xvswhvgOzWqTfYs6eC9OEt_7WZBXLKDmM-pv67lnqolxwK2tqI_v4PnP1LfiZoubW1fkAHaI1g-GmqFjl_" + "xqYSPmu2AmPyqZd9h3VdcUsjDM-hSdtx9wl_8UYvdHphaP7UVjQ58Hi6BHbCzuk1n20g_nzPdpOpeBvtlvL6x-" + "ZD8o4tEaXnwHYw3IyVJ_HqJ9HlncA7Cu4eypM-ZPO97BaOJa3oI8DvL0mWNxJb7js25Blv2ej9Lxu6dKiTXuFjrSlzl-X6F9QT_Suo_" + "O6TXqWGz7xBvHFGNkBzCyQ2d8iOUkTfx2olMh7zvsOV-hzhIF0LslPR4PXLjDJwzr6UiSLvSHTp18gHj6wXNWV1nIWnqMUZ0G7Ivv6E_" + "LSqZdO01y0oGCw6eTEPNftSDmcp02cE4Whf8i5NM1T3HNwbb7je9NPdZpY06oY8AGz_" + "QF6R3Gd1u4PlDHdNgjdVAc1LPt0QN5iMcQO2M97vt1BmS_w8ETUV-Q43H3kX2e8_" + "eMovejmiLez5VF9q9maC0LsW0IZbslctgFPJ6h93vi3N6tkf0ui8yA3O9t9gBSPKhbJ1-_9VC7nvPy1v7A7BS_s-g2yNdd-" + "hjSfGStVUsPOwM1lrkgZ90pd_jjIwxP-54mJ3EFr_W7LXLcpQ-" + "LR4DYXs3UWyJkjjbY2UvF6tJfjQxbWeSXS87f5ZCCGBSAVoRwzmb9QJZnFt9fdKF-" + "64bMD7zoAtdbxViiVjB23bXFY0w5s6bs2uSkC7g3h2yM3JVDTG33WDexs9sH-wI_CCdRSGL5sSi2zw05gPW3ucmb_" + "8T1bPcuOscFir1g66gZff9PJXLwieJ9eDPjDWSvRo57n_8cunzHewBNXDf0rshRz5thbzf3lILYf3o2ObSZj_bxZUNcb_" + "YG5Ge9cA7R6ebrbMyAAEU72J4nWTfvjIEe8IjeM4QAxsHUbR_Z3INdIuc2Dcd88ax3B4fVtnjUlp_" + "ZZfKVHb2J8NZNBQQ74hxtV8ipzV5GU-" + "aAc2M9SiO2XZy0YvkqObDDMdYvi94aiXWtwZk9g8HTtt5TNnrKiGwDanPuTuij52NvV7SMO3r7IrJ1hDcXzGHrd2F7STtJip64Q1qf3e" + "G2_9hNcmw73hNz8TOTDXG9breW8gC_qLwiibHBbsNeItSk-Tbz3O1c_MaBJLEOFvgBXZYuLloDG5m-b3fIkR0VstRB-J6432FD7NsQ-" + "w7G39QM3XtgPygdIMOje1cz3hTb9qoHuPBkpHeQoXuffGt__qOX73uzz3-GzNX1P__FHXHsXk-Sk33QFeVL6HJNzmpO3qbOE_" + "5WoiCuQ-3byBfArS0KlSPDcBhyolQkGfnOvoO4fvHR_pGpyWJ_vCfHtfkzjQpX1Bcz-Fq_Qr7th_5D1AWuUMjqc0F_VMt-" + "u9Kvkv3KlI7xuNiq7bbqkH6D7Pe9yY530YbQL_sX5Lg_pXx5LY1gVMb7uv4lOe3TH_kbK0ufgynuafVb5KjvPTBgDekcYMacoQ-1S5_" + "PPP_7NvAbhuSLxFiLnEdnE_2WxA6uCHolfXTp4xvnCzjRB5YwR98SibGyf0UO-h7gPsqJ435Iv0cOIz0ZUjuZX3Cn3e-" + "Tj32AFD6mY3DDvjekkROk_c_I_r2W_nWkyxN1Qoy7xVy2D9yhT_" + "kzdtaGoQljrg8x16fuK92Rg6SM330ZlMjR4CF6ls9SsnL8Owjna9mtvuKgQQ4Hk128R8xZBjbJDexSim8Vv4BvDe5Abz-M6C6C-" + "5vcZbvfN7gnp4PXIdsdX0bmvq8r5Lslj7jm7ojB19_kgynLL9_tROUYhr_Zv59zXSMn15BLX6EEQjB4T_2C-" + "9vrBvm2HHXU5lPQzp9w0T2T9AU6NcnHgb09FXpnVcicCzDgmrMAcvZ2TOwZid93uh7AOOqGNEAwTI9j56ZOvrqhjsOjpFEPg9BN3_" + "tpRvy27CZ6m7f49SksJhe1dfTZ3m2DfNdwIPqdvOUNucMgNQfAlXkA370AWs7S8kZindsWOWx5c-8J41TGzly-XPuafNt0A-" + "a7i4IEVOiw4NmDxJjmqoYhJb6P-y53NXJwx2YM4-empu_ClMX6d1DP332fqucXP1Ok-E5RWdZG6zXvozpmwaeWL_" + "MRPNqTdCLEhHuLHN7vepcV93Jx2RtyfM9nQzp8xrmwhr4Z-cf__p_7_0VkiUI") .ok()); TlBufferParser parser(&en); auto result = telegram_api::help_getCountriesList::fetch_result(parser); diff --git a/td/telegram/DialogId.h b/td/telegram/DialogId.h index 64f3cbfc5..afd593d08 100644 --- a/td/telegram/DialogId.h +++ b/td/telegram/DialogId.h @@ -6,11 +6,10 @@ // #pragma once -#include "td/telegram/telegram_api.h" - #include "td/telegram/ChannelId.h" #include "td/telegram/ChatId.h" #include "td/telegram/SecretChatId.h" +#include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" #include "td/utils/common.h" diff --git a/td/telegram/DialogInviteLink.cpp b/td/telegram/DialogInviteLink.cpp index 714e480d9..ef72dfea9 100644 --- a/td/telegram/DialogInviteLink.cpp +++ b/td/telegram/DialogInviteLink.cpp @@ -19,53 +19,61 @@ DialogInviteLink::DialogInviteLink(tl_object_ptrlink_); - LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported invite link " << invite_link_; + title_ = std::move(exported_invite->title_); creator_user_id_ = UserId(exported_invite->admin_id_); + date_ = exported_invite->date_; + expire_date_ = exported_invite->expire_date_; + usage_limit_ = exported_invite->usage_limit_; + usage_count_ = exported_invite->usage_; + edit_date_ = exported_invite->start_date_; + request_count_ = exported_invite->requested_; + creates_join_request_ = exported_invite->request_needed_; + is_revoked_ = exported_invite->revoked_; + is_permanent_ = exported_invite->permanent_; + + LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported invite link " << invite_link_; if (!creator_user_id_.is_valid()) { LOG(ERROR) << "Receive invalid " << creator_user_id_ << " as creator of a link " << invite_link_; creator_user_id_ = UserId(); } - date_ = exported_invite->date_; - if (date_ < 1000000000) { + if (date_ != 0 && date_ < 1000000000) { LOG(ERROR) << "Receive wrong date " << date_ << " as a creation date of a link " << invite_link_; date_ = 0; } - if ((exported_invite->flags_ & telegram_api::chatInviteExported::EXPIRE_DATE_MASK) != 0) { - expire_date_ = exported_invite->expire_date_; - if (expire_date_ < 1000000000) { - LOG(ERROR) << "Receive wrong date " << expire_date_ << " as an expire date of a link " << invite_link_; - expire_date_ = 0; - } + if (expire_date_ != 0 && expire_date_ < 1000000000) { + LOG(ERROR) << "Receive wrong date " << expire_date_ << " as an expire date of a link " << invite_link_; + expire_date_ = 0; } - if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_LIMIT_MASK) != 0) { - usage_limit_ = exported_invite->usage_limit_; - if (usage_limit_ < 0) { - LOG(ERROR) << "Receive wrong usage limit " << usage_limit_ << " for a link " << invite_link_; - usage_limit_ = 0; - } + if (usage_limit_ < 0) { + LOG(ERROR) << "Receive wrong usage limit " << usage_limit_ << " for a link " << invite_link_; + usage_limit_ = 0; } - if ((exported_invite->flags_ & telegram_api::chatInviteExported::USAGE_MASK) != 0) { - usage_count_ = exported_invite->usage_; - if (usage_count_ < 0) { - LOG(ERROR) << "Receive wrong usage count " << usage_count_ << " for a link " << invite_link_; - usage_count_ = 0; - } + if (usage_count_ < 0) { + LOG(ERROR) << "Receive wrong usage count " << usage_count_ << " for a link " << invite_link_; + usage_count_ = 0; } - if ((exported_invite->flags_ & telegram_api::chatInviteExported::START_DATE_MASK) != 0) { - edit_date_ = exported_invite->start_date_; - if (edit_date_ < 1000000000) { - LOG(ERROR) << "Receive wrong date " << edit_date_ << " as an edit date of a link " << invite_link_; - edit_date_ = 0; - } + if (edit_date_ != 0 && edit_date_ < 1000000000) { + LOG(ERROR) << "Receive wrong date " << edit_date_ << " as an edit date of a link " << invite_link_; + edit_date_ = 0; + } + if (request_count_ < 0) { + LOG(ERROR) << "Receive wrong pending join request count " << request_count_ << " for a link " << invite_link_; + request_count_ = 0; } - is_revoked_ = exported_invite->revoked_; - is_permanent_ = exported_invite->permanent_; - if (is_permanent_ && (usage_limit_ > 0 || expire_date_ > 0 || edit_date_ > 0)) { + if (is_permanent_ && (!title_.empty() || expire_date_ > 0 || usage_limit_ > 0 || edit_date_ > 0 || + request_count_ > 0 || creates_join_request_)) { LOG(ERROR) << "Receive wrong permanent " << *this; + title_.clear(); expire_date_ = 0; usage_limit_ = 0; edit_date_ = 0; + request_count_ = 0; + creates_join_request_ = false; + } + if (creates_join_request_ && usage_limit_ > 0) { + LOG(ERROR) << "Receive wrong permanent " << *this; + usage_limit_ = 0; } } @@ -81,15 +89,18 @@ td_api::object_ptr DialogInviteLink::get_chat_invite_lin } return td_api::make_object( - invite_link_, contacts_manager->get_user_id_object(creator_user_id_, "get_chat_invite_link_object"), date_, - edit_date_, expire_date_, usage_limit_, usage_count_, is_permanent_, is_revoked_); + invite_link_, title_, contacts_manager->get_user_id_object(creator_user_id_, "get_chat_invite_link_object"), + date_, edit_date_, expire_date_, usage_limit_, usage_count_, request_count_, creates_join_request_, is_permanent_, + is_revoked_); } bool operator==(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { - return lhs.invite_link_ == rhs.invite_link_ && lhs.creator_user_id_ == rhs.creator_user_id_ && - lhs.date_ == rhs.date_ && lhs.edit_date_ == rhs.edit_date_ && lhs.expire_date_ == rhs.expire_date_ && - lhs.usage_limit_ == rhs.usage_limit_ && lhs.usage_count_ == rhs.usage_count_ && - lhs.is_permanent_ == rhs.is_permanent_ && lhs.is_revoked_ == rhs.is_revoked_; + return lhs.invite_link_ == rhs.invite_link_ && lhs.title_ == rhs.title_ && + lhs.creator_user_id_ == rhs.creator_user_id_ && lhs.date_ == rhs.date_ && lhs.edit_date_ == rhs.edit_date_ && + lhs.expire_date_ == rhs.expire_date_ && lhs.usage_limit_ == rhs.usage_limit_ && + lhs.usage_count_ == rhs.usage_count_ && lhs.request_count_ == rhs.request_count_ && + lhs.creates_join_request_ == rhs.creates_join_request_ && lhs.is_permanent_ == rhs.is_permanent_ && + lhs.is_revoked_ == rhs.is_revoked_; } bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { @@ -97,10 +108,12 @@ bool operator!=(const DialogInviteLink &lhs, const DialogInviteLink &rhs) { } StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink &invite_link) { - return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << " by " << invite_link.creator_user_id_ - << " created at " << invite_link.date_ << " edited at " << invite_link.edit_date_ - << " expiring at " << invite_link.expire_date_ << " used by " << invite_link.usage_count_ - << " with usage limit " << invite_link.usage_limit_ << "]"; + return string_builder << "ChatInviteLink[" << invite_link.invite_link_ << '(' << invite_link.title_ << ')' + << (invite_link.creates_join_request_ ? " creating join request" : "") << " by " + << invite_link.creator_user_id_ << " created at " << invite_link.date_ << " edited at " + << invite_link.edit_date_ << " expiring at " << invite_link.expire_date_ << " used by " + << invite_link.usage_count_ << " with usage limit " << invite_link.usage_limit_ << " and " + << invite_link.request_count_ << "pending join requests]"; } } // namespace td diff --git a/td/telegram/DialogInviteLink.h b/td/telegram/DialogInviteLink.h index 1baedb5c8..86f12ca04 100644 --- a/td/telegram/DialogInviteLink.h +++ b/td/telegram/DialogInviteLink.h @@ -21,12 +21,15 @@ class ContactsManager; class DialogInviteLink { string invite_link_; + string title_; UserId creator_user_id_; int32 date_ = 0; int32 edit_date_ = 0; int32 expire_date_ = 0; int32 usage_limit_ = 0; int32 usage_count_ = 0; + int32 request_count_ = 0; + bool creates_join_request_ = false; bool is_revoked_ = false; bool is_permanent_ = false; @@ -66,6 +69,8 @@ class DialogInviteLink { bool has_usage_limit = usage_limit_ != 0; bool has_usage_count = usage_count_ != 0; bool has_edit_date = edit_date_ != 0; + bool has_request_count = request_count_ != 0; + bool has_title = !title_.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(is_revoked_); STORE_FLAG(is_permanent_); @@ -73,6 +78,9 @@ class DialogInviteLink { STORE_FLAG(has_usage_limit); STORE_FLAG(has_usage_count); STORE_FLAG(has_edit_date); + STORE_FLAG(has_request_count); + STORE_FLAG(creates_join_request_); + STORE_FLAG(has_title); END_STORE_FLAGS(); store(invite_link_, storer); store(creator_user_id_, storer); @@ -89,6 +97,12 @@ class DialogInviteLink { if (has_edit_date) { store(edit_date_, storer); } + if (has_request_count) { + store(request_count_, storer); + } + if (has_title) { + store(title_, storer); + } } template @@ -98,6 +112,8 @@ class DialogInviteLink { bool has_usage_limit; bool has_usage_count; bool has_edit_date; + bool has_request_count; + bool has_title; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_revoked_); PARSE_FLAG(is_permanent_); @@ -105,6 +121,9 @@ class DialogInviteLink { PARSE_FLAG(has_usage_limit); PARSE_FLAG(has_usage_count); PARSE_FLAG(has_edit_date); + PARSE_FLAG(has_request_count); + PARSE_FLAG(creates_join_request_); + PARSE_FLAG(has_title); END_PARSE_FLAGS(); parse(invite_link_, parser); parse(creator_user_id_, parser); @@ -121,6 +140,15 @@ class DialogInviteLink { if (has_edit_date) { parse(edit_date_, parser); } + if (has_request_count) { + parse(request_count_, parser); + } + if (has_title) { + parse(title_, parser); + } + if (creates_join_request_) { + usage_limit_ = 0; + } } }; diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index d11e3de81..5fb684899 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -422,7 +422,7 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptris_anonymous_, custom_title, true /*st->can_be_edited_*/, st->can_manage_chat_, st->can_change_info_, st->can_post_messages_, st->can_edit_messages_, st->can_delete_messages_, st->can_invite_users_, - st->can_restrict_members_, st->can_pin_messages_, st->can_promote_members_, st->can_manage_voice_chats_); + st->can_restrict_members_, st->can_pin_messages_, st->can_promote_members_, st->can_manage_video_chats_); } case td_api::chatMemberStatusMember::ID: return DialogParticipantStatus::Member(); @@ -711,16 +711,15 @@ DialogParticipant::DialogParticipant(tl_object_ptr(participant_ptr); - bool is_anonymous = (participant->admin_rights_->flags_ & telegram_api::chatAdminRights::ANONYMOUS_MASK) != 0; *this = {DialogId(UserId(participant->user_id_)), UserId(), 0, - DialogParticipantStatus::Creator(true, is_anonymous, std::move(participant->rank_))}; + DialogParticipantStatus::Creator(true, participant->admin_rights_->anonymous_, + std::move(participant->rank_))}; break; } case telegram_api::channelParticipantAdmin::ID: { auto participant = move_tl_object_as(participant_ptr); - bool can_be_edited = (participant->flags_ & telegram_api::channelParticipantAdmin::CAN_EDIT_MASK) != 0; *this = {DialogId(UserId(participant->user_id_)), UserId(participant->promoted_by_), participant->date_, - get_dialog_participant_status(can_be_edited, std::move(participant->admin_rights_), + get_dialog_participant_status(participant->can_edit_, std::move(participant->admin_rights_), std::move(participant->rank_))}; break; } @@ -731,9 +730,8 @@ DialogParticipant::DialogParticipant(tl_object_ptr(participant_ptr); - auto is_member = (participant->flags_ & telegram_api::channelParticipantBanned::LEFT_MASK) == 0; *this = {DialogId(participant->peer_), UserId(participant->kicked_by_), participant->date_, - get_dialog_participant_status(is_member, std::move(participant->banned_rights_))}; + get_dialog_participant_status(!participant->left_, std::move(participant->banned_rights_))}; break; } default: diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 40aa050ee..48acc0441 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -17,6 +17,7 @@ #include "td/telegram/misc.h" #include "td/telegram/net/DcId.h" #include "td/telegram/Photo.h" +#include "td/telegram/PhotoSizeSource.h" #include "td/telegram/secret_api.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" @@ -270,9 +271,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo if (document_type != Document::Type::VoiceNote) { for (auto &thumb : document->thumbs_) { - auto photo_size = - get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, file_reference, - DcId::create(dc_id), owner_dialog_id, std::move(thumb), thumbnail_format); + auto photo_size = get_photo_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 0), + id, access_hash, file_reference, DcId::create(dc_id), owner_dialog_id, + std::move(thumb), thumbnail_format); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); @@ -284,8 +285,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo } for (auto &thumb : document->video_thumbs_) { if (thumb->type_ == "v") { - animated_thumbnail = get_animation_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, - file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb)); + animated_thumbnail = + get_animation_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 0), id, + access_hash, file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb)); if (animated_thumbnail.file_id.is_valid()) { break; } diff --git a/td/telegram/DocumentsManager.h b/td/telegram/DocumentsManager.h index 46b83db7e..f3c23e6aa 100644 --- a/td/telegram/DocumentsManager.h +++ b/td/telegram/DocumentsManager.h @@ -6,16 +6,15 @@ // #pragma once -#include "td/telegram/secret_api.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/DialogId.h" #include "td/telegram/Document.h" #include "td/telegram/EncryptedFile.h" #include "td/telegram/files/FileId.h" #include "td/telegram/Photo.h" +#include "td/telegram/secret_api.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/DraftMessage.cpp b/td/telegram/DraftMessage.cpp index ed2587019..f6ca4265f 100644 --- a/td/telegram/DraftMessage.cpp +++ b/td/telegram/DraftMessage.cpp @@ -55,7 +55,7 @@ unique_ptr get_draft_message(ContactsManager *contacts_manager, 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; + result->input_message_text.disable_web_page_preview = draft->no_webpage_; result->input_message_text.clear_draft = false; return result; diff --git a/td/telegram/FileReferenceManager.cpp b/td/telegram/FileReferenceManager.cpp index e2f8f4d4b..dd044785f 100644 --- a/td/telegram/FileReferenceManager.cpp +++ b/td/telegram/FileReferenceManager.cpp @@ -8,6 +8,7 @@ #include "td/telegram/AnimationsManager.h" #include "td/telegram/BackgroundManager.h" +#include "td/telegram/ConfigManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/Global.h" @@ -59,6 +60,7 @@ fileSourceFavoriteStickers = FileSource; // repa fileSourceBackground background_id:int64 access_hash:int64 = FileSource; // repaired with account.getWallPaper fileSourceBasicGroupFull basic_group_id:int32 = FileSource; // repaired with messages.getFullChat fileSourceSupergroupFull supergroup_id:int32 = FileSource; // repaired with messages.getFullChannel +fileSourceAppConfig = FileSource; // repaired with help.getAppConfig, not reliable */ FileSourceId FileReferenceManager::get_current_file_source_id() const { @@ -118,6 +120,11 @@ FileSourceId FileReferenceManager::create_channel_full_file_source(ChannelId cha return add_file_source_id(source, PSLICE() << "full " << channel_id); } +FileSourceId FileReferenceManager::create_app_config_file_source() { + FileSourceAppConfig source; + return add_file_source_id(source, "app config"); +} + bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_source_id) { bool is_added = nodes_[node_id].file_source_ids.add(file_source_id); VLOG(file_references) << "Add " << (is_added ? "new" : "old") << ' ' << file_source_id << " for file " << node_id; @@ -291,6 +298,9 @@ void FileReferenceManager::send_query(Destination dest, FileSourceId file_source [&](const FileSourceChannelFull &source) { send_closure_later(G()->contacts_manager(), &ContactsManager::reload_channel_full, source.channel_id, std::move(promise), "repair file reference"); + }, + [&](const FileSourceAppConfig &source) { + send_closure_later(G()->config_manager(), &ConfigManager::reget_app_config, std::move(promise)); })); } diff --git a/td/telegram/FileReferenceManager.h b/td/telegram/FileReferenceManager.h index 939b6db2e..870db8711 100644 --- a/td/telegram/FileReferenceManager.h +++ b/td/telegram/FileReferenceManager.h @@ -51,6 +51,7 @@ class FileReferenceManager final : public Actor { FileSourceId create_background_file_source(BackgroundId background_id, int64 access_hash); FileSourceId create_chat_full_file_source(ChatId chat_id); FileSourceId create_channel_full_file_source(ChannelId channel_id); + FileSourceId create_app_config_file_source(); using NodeId = FileId; void repair_file_reference(NodeId node_id, Promise<> promise); @@ -134,12 +135,15 @@ class FileReferenceManager final : public Actor { struct FileSourceChannelFull { ChannelId channel_id; }; + struct FileSourceAppConfig { + // empty + }; // append only using FileSource = Variant; + FileSourceBackground, FileSourceChatFull, FileSourceChannelFull, FileSourceAppConfig>; vector file_sources_; int64 query_generation_{0}; diff --git a/td/telegram/FileReferenceManager.hpp b/td/telegram/FileReferenceManager.hpp index 7923f7439..516d73d73 100644 --- a/td/telegram/FileReferenceManager.hpp +++ b/td/telegram/FileReferenceManager.hpp @@ -49,7 +49,8 @@ void FileReferenceManager::store_file_source(FileSourceId file_source_id, Storer td::store(source.access_hash, storer); }, [&](const FileSourceChatFull &source) { td::store(source.chat_id, storer); }, - [&](const FileSourceChannelFull &source) { td::store(source.channel_id, storer); })); + [&](const FileSourceChannelFull &source) { td::store(source.channel_id, storer); }, + [&](const FileSourceAppConfig &source) {})); } template @@ -111,6 +112,8 @@ FileSourceId FileReferenceManager::parse_file_source(Td *td, ParserT &parser) { td::parse(channel_id, parser); return td->contacts_manager_->get_channel_full_file_source_id(channel_id); } + case 12: + return td->stickers_manager_->get_app_config_file_source_id(); default: parser.set_error("Invalid type in FileSource"); return FileSourceId(); diff --git a/td/telegram/Game.cpp b/td/telegram/Game.cpp index ca3ce7469..84cff59c6 100644 --- a/td/telegram/Game.cpp +++ b/td/telegram/Game.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/Game.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/AnimationsManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/Document.h" diff --git a/td/telegram/GroupCallParticipant.cpp b/td/telegram/GroupCallParticipant.cpp index d9fee5ab5..d0b1ba795 100644 --- a/td/telegram/GroupCallParticipant.cpp +++ b/td/telegram/GroupCallParticipant.cpp @@ -32,7 +32,7 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptrflags_ & telegram_api::groupCallParticipant::VOLUME_BY_ADMIN_MASK) == 0; + is_volume_level_local = !participant->volume_by_admin_; } if (!participant->left_) { joined_date = participant->date_; diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index 174a4cfa8..34728f313 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -6,11 +6,6 @@ // #include "td/telegram/InlineQueriesManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/td_api.hpp" -#include "td/telegram/telegram_api.h" -#include "td/telegram/telegram_api.hpp" - #include "td/telegram/AccessRights.h" #include "td/telegram/AnimationsManager.h" #include "td/telegram/AudiosManager.h" @@ -31,19 +26,20 @@ #include "td/telegram/MessageEntity.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" +#include "td/telegram/net/DcId.h" #include "td/telegram/Payments.h" #include "td/telegram/Photo.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" +#include "td/telegram/td_api.hpp" #include "td/telegram/TdDb.h" #include "td/telegram/TdParameters.h" +#include "td/telegram/telegram_api.hpp" #include "td/telegram/Venue.h" #include "td/telegram/VideosManager.h" #include "td/telegram/VoiceNotesManager.h" -#include "td/telegram/net/DcId.h" - #include "td/utils/algorithm.h" #include "td/utils/base64.h" #include "td/utils/buffer.h" diff --git a/td/telegram/JsonValue.cpp b/td/telegram/JsonValue.cpp index 110bf35e7..a9a5838fe 100644 --- a/td/telegram/JsonValue.cpp +++ b/td/telegram/JsonValue.cpp @@ -217,6 +217,15 @@ int32 get_json_value_int(telegram_api::object_ptr &&jso return 0; } +double get_json_value_double(telegram_api::object_ptr &&json_value, Slice name) { + CHECK(json_value != nullptr); + if (json_value->get_id() == telegram_api::jsonNumber::ID) { + return static_cast(json_value.get())->value_; + } + LOG(ERROR) << "Expected Double as " << name << ", but found " << to_string(json_value); + return 0.0; +} + string get_json_value_string(telegram_api::object_ptr &&json_value, Slice name) { CHECK(json_value != nullptr); if (json_value->get_id() == telegram_api::jsonString::ID) { diff --git a/td/telegram/JsonValue.h b/td/telegram/JsonValue.h index 51b1a748d..4adb8b84c 100644 --- a/td/telegram/JsonValue.h +++ b/td/telegram/JsonValue.h @@ -30,6 +30,8 @@ bool get_json_value_bool(telegram_api::object_ptr &&jso int32 get_json_value_int(telegram_api::object_ptr &&json_value, Slice name); +double get_json_value_double(telegram_api::object_ptr &&json_value, Slice name); + string get_json_value_string(telegram_api::object_ptr &&json_value, Slice name); } // namespace td diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index 32d3a66be..2378c2c2f 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -12,11 +12,7 @@ #include "td/telegram/misc.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/Td.h" - -#include "td/telegram/misc.h" -#include "td/telegram/td_api.h" #include "td/telegram/td_api.hpp" -#include "td/telegram/telegram_api.h" #include "td/db/DbKey.h" #include "td/db/SqliteDb.h" @@ -1609,9 +1605,9 @@ Result LanguagePackManager::get_language_info info.native_name_ = std::move(language->native_name_); info.base_language_code_ = std::move(language->base_lang_code_); info.plural_code_ = std::move(language->plural_code_); - info.is_official_ = (language->flags_ & telegram_api::langPackLanguage::OFFICIAL_MASK) != 0; - info.is_rtl_ = (language->flags_ & telegram_api::langPackLanguage::RTL_MASK) != 0; - info.is_beta_ = (language->flags_ & telegram_api::langPackLanguage::BETA_MASK) != 0; + info.is_official_ = language->official_; + info.is_rtl_ = language->rtl_; + info.is_beta_ = language->beta_; info.is_from_database_ = false; info.total_string_count_ = language->strings_count_; info.translated_string_count_ = language->translated_count_; diff --git a/td/telegram/LanguagePackManager.h b/td/telegram/LanguagePackManager.h index 9fb45e49d..a6ca0927d 100644 --- a/td/telegram/LanguagePackManager.h +++ b/td/telegram/LanguagePackManager.h @@ -7,7 +7,6 @@ #pragma once #include "td/telegram/net/NetQuery.h" - #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/LinkManager.cpp b/td/telegram/LinkManager.cpp index 8aea83355..299f52c2d 100644 --- a/td/telegram/LinkManager.cpp +++ b/td/telegram/LinkManager.cpp @@ -354,13 +354,19 @@ class LinkManager::InternalLinkUnknownDeepLink final : public InternalLink { } }; +class LinkManager::InternalLinkUnsupportedProxy final : public InternalLink { + td_api::object_ptr get_internal_link_type_object() const final { + return td_api::make_object(); + } +}; + class LinkManager::InternalLinkVoiceChat final : public InternalLink { string dialog_username_; string invite_hash_; bool is_live_stream_; td_api::object_ptr get_internal_link_type_object() const final { - return td_api::make_object(dialog_username_, invite_hash_, is_live_stream_); + return td_api::make_object(dialog_username_, invite_hash_, is_live_stream_); } public: @@ -395,8 +401,6 @@ class GetDeepLinkInfoQuery final : public Td::ResultHandler { return promise_.set_value(nullptr); case telegram_api::help_deepLinkInfo::ID: { auto info = telegram_api::move_object_as(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()) { @@ -408,7 +412,7 @@ class GetDeepLinkInfoQuery final : public Td::ResultHandler { } FormattedText text{std::move(info->message_), std::move(entities)}; return promise_.set_value( - td_api::make_object(get_formatted_text_object(text, true, -1), need_update)); + td_api::make_object(get_formatted_text_object(text, true, -1), info->update_app_)); } default: UNREACHABLE(); @@ -463,11 +467,9 @@ class RequestUrlAuthQuery final : public Td::ResultHandler { return on_error(id, Status::Error(500, "Receive invalid bot_user_id")); } td->contacts_manager_->on_get_user(std::move(request->bot_), "RequestUrlAuthQuery"); - bool request_write_access = - (request->flags_ & telegram_api::urlAuthResultRequest::REQUEST_WRITE_ACCESS_MASK) != 0; promise_.set_value(td_api::make_object( url_, request->domain_, td->contacts_manager_->get_user_id_object(bot_user_id, "RequestUrlAuthQuery"), - request_write_access)); + request->request_write_access_)); break; } case telegram_api::urlAuthResultAccepted::ID: { @@ -863,6 +865,8 @@ unique_ptr LinkManager::parse_tg_link_query(Slice que if (0 < port && port < 65536) { return td::make_unique( get_arg("server"), port, td_api::make_object(get_arg("user"), get_arg("pass"))); + } else { + return td::make_unique(); } } } else if (path.size() == 1 && path[0] == "proxy") { @@ -872,6 +876,8 @@ unique_ptr LinkManager::parse_tg_link_query(Slice que if (0 < port && port < 65536 && mtproto::ProxySecret::from_link(get_arg("secret")).is_ok()) { return td::make_unique(get_arg("server"), port, td_api::make_object(get_arg("secret"))); + } else { + return td::make_unique(); } } } else if (path.size() == 1 && path[0] == "privatepost") { @@ -982,6 +988,8 @@ unique_ptr LinkManager::parse_t_me_link_query(Slice q if (0 < port && port < 65536) { return td::make_unique( get_arg("server"), port, td_api::make_object(get_arg("user"), get_arg("pass"))); + } else { + return td::make_unique(); } } } else if (path[0] == "proxy") { @@ -991,6 +999,8 @@ unique_ptr LinkManager::parse_t_me_link_query(Slice q if (0 < port && port < 65536 && mtproto::ProxySecret::from_link(get_arg("secret")).is_ok()) { return td::make_unique(get_arg("server"), port, td_api::make_object(get_arg("secret"))); + } else { + return td::make_unique(); } } } else if (path[0] == "bg") { @@ -1156,14 +1166,14 @@ void LinkManager::get_external_link_info(string &&link, Promise> &&result) mutable { - if (result.is_error()) { - return promise.set_value(td_api::make_object(link, false)); - } - send_closure(G()->link_manager(), &LinkManager::get_external_link_info, std::move(link), std::move(promise)); - }); - return send_closure(G()->config_manager(), &ConfigManager::get_app_config, std::move(query_promise)); + auto query_promise = + PromiseCreator::lambda([link = std::move(link), promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + return promise.set_value(td_api::make_object(link, false)); + } + send_closure(G()->link_manager(), &LinkManager::get_external_link_info, std::move(link), std::move(promise)); + }); + return send_closure(G()->config_manager(), &ConfigManager::reget_app_config, std::move(query_promise)); } if (autologin_token_.empty()) { diff --git a/td/telegram/LinkManager.h b/td/telegram/LinkManager.h index 9790340c3..e55c7e57f 100644 --- a/td/telegram/LinkManager.h +++ b/td/telegram/LinkManager.h @@ -98,6 +98,7 @@ class LinkManager final : public Actor { class InternalLinkTheme; class InternalLinkThemeSettings; class InternalLinkUnknownDeepLink; + class InternalLinkUnsupportedProxy; class InternalLinkVoiceChat; struct LinkInfo { diff --git a/td/telegram/Location.h b/td/telegram/Location.h index 04432130b..6f27d7e68 100644 --- a/td/telegram/Location.h +++ b/td/telegram/Location.h @@ -7,9 +7,8 @@ #pragma once #include "td/telegram/Global.h" -#include "td/telegram/SecretInputMedia.h" - #include "td/telegram/secret_api.h" +#include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/Log.cpp b/td/telegram/Log.cpp index 884ca5661..366fec9de 100644 --- a/td/telegram/Log.cpp +++ b/td/telegram/Log.cpp @@ -8,7 +8,6 @@ #include "td/telegram/Client.h" #include "td/telegram/Logging.h" - #include "td/telegram/td_api.h" #include "td/utils/common.h" diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 6829f2928..3505d2eb5 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -319,6 +319,12 @@ class MessageChatAddUsers final : public MessageContent { class MessageChatJoinedByLink final : public MessageContent { public: + bool is_approved = false; + + MessageChatJoinedByLink() = default; + explicit MessageChatJoinedByLink(bool is_approved) : is_approved(is_approved) { + } + MessageContentType get_type() const final { return MessageContentType::ChatJoinedByLink; } @@ -866,8 +872,13 @@ static void store(const MessageContent *content, StorerT &storer) { store(m->user_ids, storer); break; } - case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatJoinedByLink: { + auto m = static_cast(content); + BEGIN_STORE_FLAGS(); + STORE_FLAG(m->is_approved); + END_STORE_FLAGS(); break; + } case MessageContentType::ChatDeleteUser: { const auto *m = static_cast(content); store(m->user_id, storer); @@ -1229,9 +1240,18 @@ static void parse(unique_ptr &content, ParserT &parser) { content = std::move(m); break; } - case MessageContentType::ChatJoinedByLink: - content = make_unique(); + case MessageContentType::ChatJoinedByLink: { + auto m = make_unique(); + if (parser.version() >= static_cast(Version::AddInviteLinksRequiringApproval)) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(m->is_approved); + END_PARSE_FLAGS(); + } else { + m->is_approved = false; + } + content = std::move(m); break; + } case MessageContentType::ChatDeleteUser: { auto m = make_unique(); parse(m->user_id, parser); @@ -1492,8 +1512,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id, break; } - result.disable_web_page_preview = - (inline_message->flags_ & telegram_api::botInlineMessageText::NO_WEBPAGE_MASK) != 0; + result.disable_web_page_preview = inline_message->no_webpage_; WebPageId web_page_id; if (!result.disable_web_page_preview) { web_page_id = td->web_pages_manager_->get_web_page_by_url(get_first_url(inline_message->message_, entities)); @@ -3141,7 +3160,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo old_file_view.main_remote_location().get_access_hash() != new_file_view.remote_location().get_access_hash()) { FileId file_id = td->file_manager_->register_remote( - FullRemoteFileLocation({FileType::Photo, 'i'}, new_file_view.remote_location().get_id(), + FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::Photo, 'i'), + new_file_view.remote_location().get_id(), new_file_view.remote_location().get_access_hash(), DcId::invalid(), new_file_view.remote_location().get_file_reference().str()), FileLocationSource::FromServer, dialog_id, old_photo->photos.back().size, 0, ""); @@ -3255,8 +3275,14 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo } break; } - case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatJoinedByLink: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->is_approved != new_->is_approved) { + need_update = true; + } break; + } case MessageContentType::ChatDeleteUser: { const auto *old_ = static_cast(old_content); const auto *new_ = static_cast(new_content); @@ -3584,12 +3610,22 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File return false; } +static bool can_be_animated_emoji(const FormattedText &text) { + return text.entities.empty() && is_emoji(text.text); +} + void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, const char *source) { switch (content->get_type()) { - case MessageContentType::Text: - return td->web_pages_manager_->register_web_page(static_cast(content)->web_page_id, - full_message_id, source); + case MessageContentType::Text: { + auto text = static_cast(content); + if (text->web_page_id.is_valid()) { + td->web_pages_manager_->register_web_page(text->web_page_id, full_message_id, source); + } else if (can_be_animated_emoji(text->text)) { + td->stickers_manager_->register_emoji(text->text.text, full_message_id, source); + } + return; + } case MessageContentType::Poll: return td->poll_manager_->register_poll(static_cast(content)->poll_id, full_message_id, source); @@ -3608,12 +3644,16 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const auto new_content_type = new_content->get_type(); if (old_content_type == new_content_type) { switch (old_content_type) { - case MessageContentType::Text: - if (static_cast(old_content)->web_page_id == - static_cast(new_content)->web_page_id) { + case MessageContentType::Text: { + auto old_text = static_cast(old_content); + auto new_text = static_cast(new_content); + if (old_text->web_page_id == new_text->web_page_id && + (old_text->text == new_text->text || + (!can_be_animated_emoji(old_text->text) && !can_be_animated_emoji(new_text->text)))) { return; } break; + } case MessageContentType::Poll: if (static_cast(old_content)->poll_id == static_cast(new_content)->poll_id) { @@ -3639,9 +3679,15 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, const char *source) { switch (content->get_type()) { - case MessageContentType::Text: - return td->web_pages_manager_->unregister_web_page(static_cast(content)->web_page_id, - full_message_id, source); + case MessageContentType::Text: { + auto text = static_cast(content); + if (text->web_page_id.is_valid()) { + td->web_pages_manager_->unregister_web_page(text->web_page_id, full_message_id, source); + } else if (can_be_animated_emoji(text->text)) { + td->stickers_manager_->unregister_emoji(text->text.text, full_message_id, source); + } + return; + } case MessageContentType::Poll: return td->poll_manager_->unregister_poll(static_cast(content)->poll_id, full_message_id, source); @@ -3803,9 +3849,9 @@ static auto secret_to_telegram_document(secret_api::decryptedMessageMediaExterna } vector> thumbnails; thumbnails.push_back(secret_to_telegram(*from.thumb_)); - return make_tl_object( - telegram_api::document::THUMBS_MASK, from.id_, from.access_hash_, BufferSlice(), from.date_, from.mime_type_, - from.size_, std::move(thumbnails), Auto(), from.dc_id_, secret_to_telegram(from.attributes_)); + return make_tl_object(0, from.id_, from.access_hash_, BufferSlice(), from.date_, + from.mime_type_, from.size_, std::move(thumbnails), Auto(), from.dc_id_, + secret_to_telegram(from.attributes_)); } template @@ -4056,11 +4102,14 @@ unique_ptr get_secret_message_content( unique_ptr get_message_content(Td *td, FormattedText message, tl_object_ptr &&media, DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id, - int32 *ttl) { + int32 *ttl, bool *disable_web_page_preview) { if (!td->auth_manager_->was_authorized() && !G()->close_flag() && media != nullptr) { LOG(ERROR) << "Receive without authorization " << to_string(media); media = nullptr; } + if (disable_web_page_preview != nullptr) { + *disable_web_page_preview = false; + } int32 constructor_id = media == nullptr ? telegram_api::messageMediaEmpty::ID : media->get_id(); switch (constructor_id) { @@ -4068,10 +4117,13 @@ unique_ptr get_message_content(Td *td, FormattedText message, if (message.text.empty()) { LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id; } + if (disable_web_page_preview != nullptr) { + *disable_web_page_preview = true; + } return make_unique(std::move(message), WebPageId()); case telegram_api::messageMediaPhoto::ID: { auto message_photo = move_tl_object_as(media); - if ((message_photo->flags_ & telegram_api::messageMediaPhoto::PHOTO_MASK) == 0) { + if (message_photo->photo_ == nullptr) { if ((message_photo->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) == 0) { LOG(ERROR) << "Receive messageMediaPhoto without photo and TTL: " << oneline(to_string(message_photo)); break; @@ -4151,7 +4203,7 @@ unique_ptr get_message_content(Td *td, FormattedText message, } case telegram_api::messageMediaDocument::ID: { auto message_document = move_tl_object_as(media); - if ((message_document->flags_ & telegram_api::messageMediaDocument::DOCUMENT_MASK) == 0) { + if (message_document->document_ == nullptr) { if ((message_document->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) == 0) { LOG(ERROR) << "Receive messageMediaDocument without document and TTL: " << oneline(to_string(message_document)); @@ -4189,6 +4241,9 @@ unique_ptr get_message_content(Td *td, FormattedText message, get_input_invoice(move_tl_object_as(media), td, owner_dialog_id)); case telegram_api::messageMediaWebPage::ID: { auto media_web_page = move_tl_object_as(media); + if (disable_web_page_preview != nullptr) { + *disable_web_page_preview = (media_web_page->webpage_ == nullptr); + } auto web_page_id = td->web_pages_manager_->on_get_web_page(std::move(media_web_page->webpage_), owner_dialog_id); return make_unique(std::move(message), web_page_id); } @@ -4208,6 +4263,9 @@ unique_ptr get_message_content(Td *td, FormattedText message, } // explicit empty media message + if (disable_web_page_preview != nullptr) { + *disable_web_page_preview = true; + } return make_unique(std::move(message), WebPageId()); } @@ -4520,7 +4578,7 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(std::move(user_ids)); } case telegram_api::messageActionChatJoinedByLink::ID: - return make_unique(); + return make_unique(false); case telegram_api::messageActionChatDeleteUser::ID: { auto chat_delete_user = move_tl_object_as(action); @@ -4590,13 +4648,12 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(action); auto duration = (phone_call->flags_ & telegram_api::messageActionPhoneCall::DURATION_MASK) != 0 ? phone_call->duration_ : 0; - auto is_video = (phone_call->flags_ & telegram_api::messageActionPhoneCall::VIDEO_MASK) != 0; if (duration < 0) { LOG(ERROR) << "Receive invalid " << oneline(to_string(phone_call)); break; } return make_unique(phone_call->call_id_, duration, get_call_discard_reason(phone_call->reason_), - is_video); + phone_call->video_); } case telegram_api::messageActionPaymentSent::ID: { if (td->auth_manager_->is_bot()) { @@ -4715,6 +4772,8 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(action); return td::make_unique(std::move(set_chat_theme->emoticon_)); } + case telegram_api::messageActionChatJoinedByRequest::ID: + return make_unique(true); default: UNREACHABLE(); } @@ -4783,6 +4842,12 @@ tl_object_ptr get_message_content_object(const MessageCo } case MessageContentType::Text: { const auto *m = static_cast(content); + if (can_be_animated_emoji(m->text) && !m->web_page_id.is_valid()) { + auto animated_emoji = td->stickers_manager_->get_animated_emoji_object(m->text.text); + if (animated_emoji != nullptr) { + return td_api::make_object(std::move(animated_emoji), m->text.text); + } + } return make_tl_object( get_formatted_text_object(m->text, skip_bot_commands, max_media_timestamp), td->web_pages_manager_->get_web_page_object(m->web_page_id)); @@ -4832,8 +4897,13 @@ tl_object_ptr get_message_content_object(const MessageCo return make_tl_object( td->contacts_manager_->get_user_ids_object(m->user_ids, "MessageChatAddUsers")); } - case MessageContentType::ChatJoinedByLink: + case MessageContentType::ChatJoinedByLink: { + const MessageChatJoinedByLink *m = static_cast(content); + if (m->is_approved) { + return make_tl_object(); + } return make_tl_object(); + } case MessageContentType::ChatDeleteUser: { const auto *m = static_cast(content); return make_tl_object( @@ -4933,19 +5003,19 @@ tl_object_ptr get_message_content_object(const MessageCo case MessageContentType::GroupCall: { const auto *m = static_cast(content); if (m->duration >= 0) { - return make_tl_object(m->duration); + return make_tl_object(m->duration); } else { auto group_call_id = td->group_call_manager_->get_group_call_id(m->input_group_call_id, DialogId()).get(); if (m->schedule_date > 0) { - return make_tl_object(group_call_id, m->schedule_date); + return make_tl_object(group_call_id, m->schedule_date); } else { - return make_tl_object(group_call_id); + return make_tl_object(group_call_id); } } } case MessageContentType::InviteToGroupCall: { const auto *m = static_cast(content); - return make_tl_object( + return make_tl_object( td->group_call_manager_->get_group_call_id(m->input_group_call_id, DialogId()).get(), td->contacts_manager_->get_user_ids_object(m->user_ids, "MessageInviteToGroupCall")); } @@ -5315,8 +5385,8 @@ void get_message_content_animated_emoji_click_sticker(const MessageContent *cont return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); } - auto &text = static_cast(content)->text; - if (!text.entities.empty()) { + const auto &text = static_cast(content)->text; + if (!can_be_animated_emoji(text)) { return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); } td->stickers_manager_->get_animated_emoji_click_sticker(text.text, full_message_id, std::move(promise)); diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index f75eb02fd..8e5c3bffe 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -189,7 +189,7 @@ unique_ptr get_secret_message_content( unique_ptr get_message_content(Td *td, FormattedText message_text, tl_object_ptr &&media, DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id, - int32 *ttl); + int32 *ttl, bool *disable_web_page_preview); enum class MessageContentDupType : int32 { Send, SendViaBot, Forward, Copy }; diff --git a/td/telegram/MessagesDb.cpp b/td/telegram/MessagesDb.cpp index 3b11a1b8d..841cac167 100644 --- a/td/telegram/MessagesDb.cpp +++ b/td/telegram/MessagesDb.cpp @@ -7,7 +7,6 @@ #include "td/telegram/MessagesDb.h" #include "td/telegram/logevent/LogEvent.h" -#include "td/telegram/MessageSearchFilter.h" #include "td/telegram/Version.h" #include "td/db/SqliteConnectionSafe.h" @@ -231,6 +230,12 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { "ORDER BY rowid DESC LIMIT ?3) ORDER BY search_id DESC")); for (int32 i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { + TRY_RESULT_ASSIGN( + get_message_ids_stmts_[i], + db_.get_statement( + PSLICE() << "SELECT message_id FROM messages WHERE dialog_id = ?1 AND message_id < ?2 AND (index_mask & " + << (1 << i) << ") != 0 ORDER BY message_id DESC LIMIT 1000000")); + TRY_RESULT_ASSIGN( get_messages_from_index_stmts_[i].desc_stmt_, db_.get_statement( @@ -335,7 +340,7 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { if (search_id != 0) { // add dialog_id to text text += PSTRING() << " \a" << dialog_id.get(); - if (index_mask) { + if (index_mask != 0) { for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { if ((index_mask & (1 << i))) { text += PSTRING() << " \a\a" << i; @@ -470,16 +475,16 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { return Status::Error("Not found"); } MessageId received_message_id(stmt.view_int64(0)); - MessagesDbDialogMessage result{received_message_id, BufferSlice(stmt.view_blob(1))}; + Slice data = stmt.view_blob(1); if (is_scheduled_server) { CHECK(received_message_id.is_scheduled()); CHECK(received_message_id.is_scheduled_server()); CHECK(received_message_id.get_scheduled_server_message_id() == message_id.get_scheduled_server_message_id()); } else { LOG_CHECK(received_message_id == message_id) - << received_message_id << ' ' << message_id << ' ' << get_message_info(result, true).first; + << received_message_id << ' ' << message_id << ' ' << get_message_info(received_message_id, data, true).first; } - return std::move(result); + return MessagesDbDialogMessage{received_message_id, BufferSlice(data)}; } Result get_message_by_unique_message_id(ServerMessageId unique_message_id) final { @@ -612,10 +617,72 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { return std::make_pair(std::move(messages), next_expires_till); } + Result get_dialog_message_calendar(MessagesDbDialogCalendarQuery query) final { + auto &stmt = get_messages_from_index_stmts_[message_search_filter_index(query.filter)].desc_stmt_; + SCOPE_EXIT { + stmt.reset(); + }; + int32 limit = 1000; + stmt.bind_int64(1, query.dialog_id.get()).ensure(); + stmt.bind_int64(2, query.from_message_id.get()).ensure(); + stmt.bind_int32(3, limit).ensure(); + + vector messages; + vector total_counts; + stmt.step().ensure(); + int32 current_day = std::numeric_limits::max(); + while (stmt.has_row()) { + auto data_slice = stmt.view_blob(0); + MessageId message_id(stmt.view_int64(1)); + auto info = get_message_info(message_id, data_slice, false); + auto day = (query.tz_offset + info.second) / 86400; + if (day >= current_day) { + CHECK(!total_counts.empty()); + total_counts.back()++; + } else { + current_day = day; + messages.push_back(MessagesDbDialogMessage{message_id, BufferSlice(data_slice)}); + total_counts.push_back(1); + } + stmt.step().ensure(); + } + return MessagesDbCalendar{std::move(messages), std::move(total_counts)}; + } + + Result get_dialog_sparse_message_positions( + MessagesDbGetDialogSparseMessagePositionsQuery query) final { + auto &stmt = get_message_ids_stmts_[message_search_filter_index(query.filter)]; + SCOPE_EXIT { + stmt.reset(); + }; + stmt.bind_int64(1, query.dialog_id.get()).ensure(); + stmt.bind_int64(2, query.from_message_id.get()).ensure(); + + vector message_ids; + stmt.step().ensure(); + while (stmt.has_row()) { + message_ids.push_back(MessageId(stmt.view_int64(0))); + stmt.step().ensure(); + } + + int32 limit = min(query.limit, static_cast(message_ids.size())); + double delta = static_cast(message_ids.size()) / limit; + vector positions; + positions.reserve(limit); + for (int32 i = 0; i < limit; i++) { + auto position = static_cast((i + 0.5) * delta); + auto message_id = message_ids[position]; + TRY_RESULT(message, get_message({query.dialog_id, message_id})); + auto date = get_message_info(message).second; + positions.push_back(MessagesDbMessagePosition{position, date, message_id}); + } + + return MessagesDbMessagePositions{static_cast(message_ids.size()), std::move(positions)}; + } + Result> get_messages(MessagesDbMessagesQuery query) final { - if (query.index_mask != 0) { - return get_messages_from_index(query.dialog_id, query.from_message_id, query.index_mask, query.offset, - query.limit); + if (query.filter != MessageSearchFilter::Empty) { + return get_messages_from_index(query.dialog_id, query.from_message_id, query.filter, query.offset, query.limit); } return get_messages_impl(get_messages_stmt_, query.dialog_id, query.from_message_id, query.offset, query.limit); } @@ -699,7 +766,7 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { get_messages_fts_stmt_.reset(); }; - LOG(INFO) << tag("query", query.query) << query.dialog_id << tag("index_mask", query.index_mask) + LOG(INFO) << tag("query", query.query) << query.dialog_id << tag("filter", query.filter) << tag("from_search_id", query.from_search_id) << tag("limit", query.limit); string words = prepare_query(query.query); LOG(INFO) << tag("from", query.query) << tag("to", words); @@ -710,18 +777,8 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } // index_mask kludge - if (query.index_mask != 0) { - int index_i = -1; - for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { - if (query.index_mask == (1 << i)) { - index_i = i; - break; - } - } - if (index_i == -1) { - return Status::Error("Union of index types is not supported"); - } - words += PSTRING() << " \"\a\a" << index_i << "\""; + if (query.filter != MessageSearchFilter::Empty) { + words += PSTRING() << " \"\a\a" << message_search_filter_index(query.filter) << "\""; } auto &stmt = get_messages_fts_stmt_; @@ -750,44 +807,20 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } Result> get_messages_from_index(DialogId dialog_id, MessageId from_message_id, - int32 index_mask, int32 offset, int32 limit) { - CHECK(index_mask != 0); - LOG_CHECK(index_mask < (1 << MESSAGES_DB_INDEX_COUNT)) << tag("index_mask", index_mask); - int index_i = -1; - for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { - if (index_mask == (1 << i)) { - index_i = i; - break; - } - } - if (index_i == -1) { - return Status::Error("Union is not supported"); - } - - auto &stmt = get_messages_from_index_stmts_[index_i]; + MessageSearchFilter filter, int32 offset, + int32 limit) { + auto &stmt = get_messages_from_index_stmts_[message_search_filter_index(filter)]; return get_messages_impl(stmt, dialog_id, from_message_id, offset, limit); } Result get_calls(MessagesDbCallsQuery query) final { - CHECK(query.index_mask != 0); - LOG_CHECK(query.index_mask < (1 << MESSAGES_DB_INDEX_COUNT)) << tag("index_mask", query.index_mask); - int index_i = -1; - for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { - if (query.index_mask == (1 << i)) { - index_i = i; - break; - } - } - if (index_i == -1) { - return Status::Error("Union is not supported"); - } int32 pos; - if (index_i + 1 == static_cast(MessageSearchFilter::Call)) { + if (query.filter == MessageSearchFilter::Call) { pos = 0; - } else if (index_i + 1 == static_cast(MessageSearchFilter::MissedCall)) { + } else if (query.filter == MessageSearchFilter::MissedCall) { pos = 1; } else { - return Status::Error(PSLICE() << "Index mask is not Call or MissedCall " << query.index_mask); + return Status::Error(PSLICE() << "Filter is not Call or MissedCall: " << query.filter); } auto &stmt = get_calls_stmts_[pos]; @@ -840,6 +873,7 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { SqliteStatement get_scheduled_messages_stmt_; SqliteStatement get_messages_from_notification_id_stmt_; + std::array get_message_ids_stmts_; std::array get_messages_from_index_stmts_; std::array get_calls_stmts_; @@ -928,7 +962,11 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } static std::pair get_message_info(const MessagesDbDialogMessage &message, bool from_data = false) { - LogEventParser message_date_parser(message.data.as_slice()); + return get_message_info(message.message_id, message.data.as_slice(), from_data); + } + + static std::pair get_message_info(MessageId message_id, Slice data, bool from_data) { + LogEventParser message_date_parser(data); int32 flags; int32 flags2 = 0; int32 flags3 = 0; @@ -940,17 +978,17 @@ class MessagesDbImpl final : public MessagesDbSyncInterface { } } bool has_sender = (flags & (1 << 10)) != 0; - MessageId message_id; - td::parse(message_id, message_date_parser); + MessageId data_message_id; + td::parse(data_message_id, message_date_parser); UserId sender_user_id; if (has_sender) { td::parse(sender_user_id, message_date_parser); } int32 date; td::parse(date, message_date_parser); - LOG(INFO) << "Loaded " << message.message_id << "(aka " << message_id << ") sent at " << date << " by " + LOG(INFO) << "Loaded " << message_id << "(aka " << data_message_id << ") sent at " << date << " by " << sender_user_id; - return {from_data ? message_id : message.message_id, date}; + return {from_data ? data_message_id : message_id, date}; } }; @@ -1016,6 +1054,15 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { std::move(promise)); } + void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, Promise promise) final { + send_closure_later(impl_, &Impl::get_dialog_message_calendar, std::move(query), std::move(promise)); + } + + void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) final { + send_closure_later(impl_, &Impl::get_dialog_sparse_message_positions, std::move(query), std::move(promise)); + } + void get_messages(MessagesDbMessagesQuery query, Promise> promise) final { send_closure_later(impl_, &Impl::get_messages, std::move(query), std::move(promise)); } @@ -1107,6 +1154,17 @@ class MessagesDbAsync final : public MessagesDbAsyncInterface { promise.set_result(sync_db_->get_dialog_message_by_date(dialog_id, first_message_id, last_message_id, date)); } + void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, Promise promise) { + add_read_query(); + promise.set_result(sync_db_->get_dialog_message_calendar(std::move(query))); + } + + void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) { + add_read_query(); + promise.set_result(sync_db_->get_dialog_sparse_message_positions(std::move(query))); + } + void get_messages(MessagesDbMessagesQuery query, Promise> promise) { add_read_query(); promise.set_result(sync_db_->get_messages(std::move(query))); diff --git a/td/telegram/MessagesDb.h b/td/telegram/MessagesDb.h index 33abd123b..f2017db93 100644 --- a/td/telegram/MessagesDb.h +++ b/td/telegram/MessagesDb.h @@ -9,6 +9,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/FullMessageId.h" #include "td/telegram/MessageId.h" +#include "td/telegram/MessageSearchFilter.h" #include "td/telegram/NotificationId.h" #include "td/telegram/ServerMessageId.h" @@ -28,7 +29,7 @@ class SqliteDb; struct MessagesDbMessagesQuery { DialogId dialog_id; - int32 index_mask{0}; + MessageSearchFilter filter{MessageSearchFilter::Empty}; MessageId from_message_id; int32 offset{0}; int32 limit{100}; @@ -45,10 +46,40 @@ struct MessagesDbMessage { BufferSlice data; }; +struct MessagesDbDialogCalendarQuery { + DialogId dialog_id; + MessageSearchFilter filter{MessageSearchFilter::Empty}; + MessageId from_message_id; + int32 tz_offset{0}; +}; + +struct MessagesDbCalendar { + vector messages; + vector total_counts; +}; + +struct MessagesDbGetDialogSparseMessagePositionsQuery { + DialogId dialog_id; + MessageSearchFilter filter{MessageSearchFilter::Empty}; + MessageId from_message_id; + int32 limit{0}; +}; + +struct MessagesDbMessagePosition { + int32 position{0}; + int32 date{0}; + MessageId message_id; +}; + +struct MessagesDbMessagePositions { + int32 total_count{0}; + vector positions; +}; + struct MessagesDbFtsQuery { string query; DialogId dialog_id; - int32 index_mask{0}; + MessageSearchFilter filter{MessageSearchFilter::Empty}; int64 from_search_id{0}; int32 limit{100}; }; @@ -58,10 +89,11 @@ struct MessagesDbFtsResult { }; struct MessagesDbCallsQuery { - int32 index_mask{0}; + MessageSearchFilter filter{MessageSearchFilter::Empty}; int32 from_unique_message_id{0}; int32 limit{100}; }; + struct MessagesDbCallsResult { vector messages; }; @@ -88,6 +120,11 @@ class MessagesDbSyncInterface { virtual Result get_dialog_message_by_date(DialogId dialog_id, MessageId first_message_id, MessageId last_message_id, int32 date) = 0; + virtual Result get_dialog_message_calendar(MessagesDbDialogCalendarQuery query) = 0; + + virtual Result get_dialog_sparse_message_positions( + MessagesDbGetDialogSparseMessagePositionsQuery query) = 0; + virtual Result> get_messages(MessagesDbMessagesQuery query) = 0; virtual Result> get_scheduled_messages(DialogId dialog_id, int32 limit) = 0; virtual Result> get_messages_from_notification_id(DialogId dialog_id, @@ -139,6 +176,12 @@ class MessagesDbAsyncInterface { virtual void get_dialog_message_by_date(DialogId dialog_id, MessageId first_message_id, MessageId last_message_id, int32 date, Promise promise) = 0; + virtual void get_dialog_message_calendar(MessagesDbDialogCalendarQuery query, + Promise promise) = 0; + + virtual void get_dialog_sparse_message_positions(MessagesDbGetDialogSparseMessagePositionsQuery query, + Promise promise) = 0; + virtual void get_messages(MessagesDbMessagesQuery query, Promise> promise) = 0; virtual void get_scheduled_messages(DialogId dialog_id, int32 limit, Promise> promise) = 0; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 6390f78c6..8fe5cf470 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -465,14 +465,16 @@ class GetMessagesQuery final : public Td::ResultHandler { class GetChannelMessagesQuery final : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; + MessageId last_new_message_id_; public: explicit GetChannelMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(ChannelId channel_id, tl_object_ptr &&input_channel, - vector> &&message_ids) { + vector> &&message_ids, MessageId last_new_message_id) { channel_id_ = channel_id; + last_new_message_id_ = last_new_message_id; CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( telegram_api::channels_getMessages(std::move(input_channel), std::move(message_ids)))); @@ -486,12 +488,14 @@ class GetChannelMessagesQuery final : public Td::ResultHandler { auto info = td->messages_manager_->get_messages_info(result_ptr.move_as_ok(), "GetChannelMessagesQuery"); LOG_IF(ERROR, !info.is_channel_messages) << "Receive ordinary messages in GetChannelMessagesQuery"; - if (!td->auth_manager_->is_bot()) { // bots can receive messageEmpty because of their privacy mode + // messages with invalid big identifiers can be received as messageEmpty + // bots can receive messageEmpty because of their privacy mode + if (last_new_message_id_.is_valid() && !td->auth_manager_->is_bot()) { vector empty_message_ids; for (auto &message : info.messages) { if (message->get_id() == telegram_api::messageEmpty::ID) { auto message_id = MessagesManager::get_message_id(message, false); - if (message_id.is_valid()) { + if (message_id.is_valid() && message_id <= last_new_message_id_) { empty_message_ids.push_back(message_id); } } @@ -2168,6 +2172,70 @@ class ReadDiscussionQuery final : public Td::ResultHandler { } }; +class GetSearchResultCalendarQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + MessageId from_message_id_; + MessageSearchFilter filter_; + int64 random_id_; + + public: + explicit GetSearchResultCalendarQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, MessageId from_message_id, MessageSearchFilter filter, int64 random_id) { + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + dialog_id_ = dialog_id; + from_message_id_ = from_message_id; + filter_ = filter; + random_id_ = random_id; + + send_query(G()->net_query_creator().create(telegram_api::messages_getSearchResultsCalendar( + std::move(input_peer), get_input_messages_filter(filter), from_message_id.get_server_message_id().get(), 0))); + } + + void on_result(uint64 id, BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetSearchResultCalendarQuery: " << to_string(result); + td->contacts_manager_->on_get_users(std::move(result->users_), "GetSearchResultCalendarQuery"); + td->contacts_manager_->on_get_chats(std::move(result->chats_), "GetSearchResultCalendarQuery"); + + MessagesManager::MessagesInfo info; + info.messages = std::move(result->messages_); + info.total_count = result->count_; + info.is_channel_messages = dialog_id_.get_type() == DialogType::Channel; + + td->messages_manager_->get_channel_difference_if_needed( + dialog_id_, std::move(info), + PromiseCreator::lambda([actor_id = td->messages_manager_actor_.get(), dialog_id = dialog_id_, + from_message_id = from_message_id_, filter = filter_, random_id = random_id_, + periods = std::move(result->periods_), + promise = std::move(promise_)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + auto info = result.move_as_ok(); + send_closure(actor_id, &MessagesManager::on_get_message_search_result_calendar, dialog_id, from_message_id, + filter, random_id, info.total_count, std::move(info.messages), std::move(periods), + std::move(promise)); + } + })); + } + + void on_error(uint64 id, Status status) final { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SearchMessagesQuery"); + td->messages_manager_->on_failed_get_message_search_result_calendar(dialog_id_, random_id_); + promise_.set_error(std::move(status)); + } +}; + class SearchMessagesQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -2273,6 +2341,45 @@ class SearchMessagesQuery final : public Td::ResultHandler { } }; +class GetSearchResultPositionsQuery final : public Td::ResultHandler { + Promise> promise_; + DialogId dialog_id_; + MessageSearchFilter filter_; + + public: + explicit GetSearchResultPositionsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, MessageSearchFilter filter, MessageId from_message_id, int32 limit) { + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + return promise_.set_error(Status::Error(400, "Can't access the chat")); + } + dialog_id_ = dialog_id; + filter_ = filter; + + send_query(G()->net_query_creator().create( + telegram_api::messages_getSearchResultsPositions(std::move(input_peer), get_input_messages_filter(filter), + from_message_id.get_server_message_id().get(), limit))); + } + + void on_result(uint64 id, BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + td->messages_manager_->on_get_dialog_sparse_message_positions(dialog_id_, filter_, result_ptr.move_as_ok(), + std::move(promise_)); + } + + void on_error(uint64 id, Status status) final { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetSearchResultPositionsQuery"); + promise_.set_error(std::move(status)); + } +}; + class GetSearchCountersQuery final : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -2590,7 +2697,7 @@ class DeleteHistoryQuery final : public Td::ResultHandler { send_query(G()->net_query_creator().create( telegram_api::messages_deleteHistory(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), - max_message_id_.get_server_message_id().get()))); + max_message_id_.get_server_message_id().get(), 0, 0))); } public: @@ -2677,6 +2784,74 @@ class DeleteChannelHistoryQuery final : public Td::ResultHandler { } }; +class DeleteMessagesByDateQuery final : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + int32 min_date_; + int32 max_date_; + bool revoke_; + + void send_request() { + auto input_peer = td->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read); + if (input_peer == nullptr) { + return promise_.set_error(Status::Error(400, "Chat is not accessible")); + } + + int32 flags = telegram_api::messages_deleteHistory::JUST_CLEAR_MASK | + telegram_api::messages_deleteHistory::MIN_DATE_MASK | + telegram_api::messages_deleteHistory::MAX_DATE_MASK; + if (revoke_) { + flags |= telegram_api::messages_deleteHistory::REVOKE_MASK; + } + + send_query(G()->net_query_creator().create(telegram_api::messages_deleteHistory( + flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), 0, min_date_, max_date_))); + } + + public: + explicit DeleteMessagesByDateQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, int32 min_date, int32 max_date, bool revoke) { + dialog_id_ = dialog_id; + min_date_ = min_date; + max_date_ = max_date; + revoke_ = revoke; + + send_request(); + } + + void on_result(uint64 id, BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto affected_history = result_ptr.move_as_ok(); + CHECK(affected_history->get_id() == telegram_api::messages_affectedHistory::ID); + + if (affected_history->pts_count_ > 0) { + affected_history->pts_count_ = 0; // force receiving real updates from the server + auto promise = affected_history->offset_ > 0 ? Promise() : std::move(promise_); + td->updates_manager_->add_pending_pts_update(make_tl_object(), affected_history->pts_, + affected_history->pts_count_, Time::now(), std::move(promise), + "DeleteMessagesByDateQuery"); + } else if (affected_history->offset_ <= 0) { + promise_.set_value(Unit()); + } + + if (affected_history->offset_ > 0) { + send_request(); + return; + } + } + + void on_error(uint64 id, Status status) final { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "DeleteMessagesByDateQuery"); + promise_.set_error(std::move(status)); + } +}; + class DeletePhoneCallHistoryQuery final : public Td::ResultHandler { Promise promise_; bool revoke_; @@ -5182,6 +5357,7 @@ void MessagesManager::Message::parse(ParserT &parser) { parse(max_reply_media_timestamp, parser); } + CHECK(content != nullptr); is_content_secret |= is_secret_message_content(ttl, content->get_type()); // repair is_content_secret for old messages if (hide_edit_date && content->get_type() == MessageContentType::LiveLocation) { @@ -5251,7 +5427,8 @@ void MessagesManager::Dialog::store(StorerT &storer) const { bool has_default_join_group_call_as_dialog_id = default_join_group_call_as_dialog_id.is_valid(); bool store_has_bots = dialog_type == DialogType::Chat || dialog_type == DialogType::Channel; bool has_theme_name = !theme_name.empty(); - bool has_flags3 = false; + bool has_flags3 = true; + bool has_pending_join_requests = pending_join_request_count != 0; BEGIN_STORE_FLAGS(); STORE_FLAG(has_draft_message); STORE_FLAG(has_last_database_message); @@ -5321,6 +5498,11 @@ void MessagesManager::Dialog::store(StorerT &storer) const { STORE_FLAG(has_flags3); END_STORE_FLAGS(); } + if (has_flags3) { + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_pending_join_requests); + END_STORE_FLAGS(); + } store(last_new_message_id, storer); store(server_unread_count, storer); @@ -5415,6 +5597,10 @@ void MessagesManager::Dialog::store(StorerT &storer) const { if (has_theme_name) { store(theme_name, storer); } + if (has_pending_join_requests) { + store(pending_join_request_count, storer); + store(pending_join_request_user_ids, storer); + } } // do not forget to resolve dialog dependencies including dependencies of last_message @@ -5449,6 +5635,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) { bool has_default_join_group_call_as_dialog_id = false; bool has_theme_name = false; bool has_flags3 = false; + bool has_pending_join_requests = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_draft_message); PARSE_FLAG(has_last_database_message); @@ -5542,6 +5729,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) { } if (has_flags3) { BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_pending_join_requests); END_PARSE_FLAGS(); } @@ -5671,6 +5859,10 @@ void MessagesManager::Dialog::parse(ParserT &parser) { if (has_theme_name) { parse(theme_name, parser); } + if (has_pending_join_requests) { + parse(pending_join_request_count, parser); + parse(pending_join_request_user_ids, parser); + } } template @@ -6535,7 +6727,6 @@ void MessagesManager::save_auth_notification_ids() { void MessagesManager::on_update_service_notification(tl_object_ptr &&update, bool skip_new_entities, Promise &&promise) { - int32 ttl = 0; bool has_date = (update->flags_ & telegram_api::updateServiceNotification::INBOX_DATE_MASK) != 0; auto date = has_date ? update->inbox_date_ : G()->unix_time(); if (date <= 0) { @@ -6558,11 +6749,13 @@ void MessagesManager::on_update_service_notification(tl_object_ptrmessage_), std::move(update->entities_), skip_new_entities, !is_user, date, false, "on_update_service_notification"); DialogId owner_dialog_id = is_user ? get_service_notifications_dialog()->dialog_id : DialogId(); + int32 ttl = 0; + bool disable_web_page_preview = false; auto content = get_message_content(td_, std::move(message_text), std::move(update->media_), owner_dialog_id, false, - UserId(), &ttl); + UserId(), &ttl, &disable_web_page_preview); bool is_content_secret = is_secret_message_content(ttl, content->get_type()); - if ((update->flags_ & telegram_api::updateServiceNotification::POPUP_MASK) != 0) { + if (update->popup_) { send_closure(G()->td(), &Td::send_update, td_api::make_object( update->type_, get_message_content_object(content.get(), td_, owner_dialog_id, date, @@ -6579,6 +6772,7 @@ void MessagesManager::on_update_service_notification(tl_object_ptrsender_user_id = dialog_id.get_user_id(); new_message->date = date; new_message->ttl = ttl; + new_message->disable_web_page_preview = disable_web_page_preview; new_message->is_content_secret = is_content_secret; new_message->content = std::move(content); new_message->have_previous = true; @@ -8371,10 +8565,8 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id, bool ignore_privacy_exception) { CHECK(peer_settings != nullptr); if (dialog_id.get_type() == DialogType::User && !ignore_privacy_exception) { - auto need_phone_number_privacy_exception = - (peer_settings->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0; td_->contacts_manager_->on_update_user_need_phone_number_privacy_exception(dialog_id.get_user_id(), - need_phone_number_privacy_exception); + peer_settings->need_contacts_exception_); } Dialog *d = get_dialog_force(dialog_id, "on_get_peer_settings"); @@ -8382,15 +8574,15 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id, return; } - auto can_report_spam = (peer_settings->flags_ & telegram_api::peerSettings::REPORT_SPAM_MASK) != 0; - auto can_add_contact = (peer_settings->flags_ & telegram_api::peerSettings::ADD_CONTACT_MASK) != 0; - auto can_block_user = (peer_settings->flags_ & telegram_api::peerSettings::BLOCK_CONTACT_MASK) != 0; - auto can_share_phone_number = (peer_settings->flags_ & telegram_api::peerSettings::SHARE_CONTACT_MASK) != 0; - auto can_report_location = (peer_settings->flags_ & telegram_api::peerSettings::REPORT_GEO_MASK) != 0; - auto can_unarchive = (peer_settings->flags_ & telegram_api::peerSettings::AUTOARCHIVED_MASK) != 0; + auto can_report_spam = peer_settings->report_spam_; + auto can_add_contact = peer_settings->add_contact_; + auto can_block_user = peer_settings->block_contact_; + auto can_share_phone_number = peer_settings->share_contact_; + auto can_report_location = peer_settings->report_geo_; + auto can_unarchive = peer_settings->autoarchived_; auto distance = (peer_settings->flags_ & telegram_api::peerSettings::GEO_DISTANCE_MASK) != 0 ? peer_settings->geo_distance_ : -1; - auto can_invite_members = (peer_settings->flags_ & telegram_api::peerSettings::INVITE_MEMBERS_MASK) != 0; + auto can_invite_members = peer_settings->invite_members_; if (d->can_report_spam == can_report_spam && d->can_add_contact == can_add_contact && d->can_block_user == can_block_user && d->can_share_phone_number == can_share_phone_number && d->can_report_location == can_report_location && d->can_unarchive == can_unarchive && d->distance == distance && @@ -9655,6 +9847,71 @@ void MessagesManager::on_failed_public_dialogs_search(const string &query, Statu } } +void MessagesManager::on_get_message_search_result_calendar( + DialogId dialog_id, MessageId from_message_id, MessageSearchFilter filter, int64 random_id, int32 total_count, + vector> &&messages, + vector> &&periods, Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto it = found_dialog_message_calendars_.find(random_id); + CHECK(it != found_dialog_message_calendars_.end()); + + int32 received_message_count = 0; + for (auto &message : messages) { + auto new_full_message_id = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, + false, false, false, "on_get_message_search_result_calendar"); + if (new_full_message_id == FullMessageId()) { + total_count--; + continue; + } + + if (new_full_message_id.get_dialog_id() != dialog_id) { + LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << dialog_id; + total_count--; + continue; + } + + received_message_count++; + } + if (total_count < received_message_count) { + LOG(ERROR) << "Receive " << received_message_count << " valid messages out of " << total_count << " in " + << messages.size() << " messages"; + total_count = received_message_count; + } + + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + auto &old_message_count = d->message_count_by_index[message_search_filter_index(filter)]; + if (old_message_count != total_count) { + old_message_count = total_count; + on_dialog_updated(dialog_id, "on_get_message_search_result_calendar"); + } + + vector> days; + for (auto &period : periods) { + auto message_id = MessageId(ServerMessageId(period->min_msg_id_)); + const auto *m = get_message(d, message_id); + if (m == nullptr) { + LOG(ERROR) << "Failed to find " << message_id; + continue; + } + if (period->count_ <= 0) { + LOG(ERROR) << "Receive " << to_string(period); + continue; + } + days.push_back(td_api::make_object( + period->count_, get_message_object(dialog_id, m, "on_get_message_search_result_calendar"))); + } + it->second = td_api::make_object(total_count, std::move(days)); + promise.set_value(Unit()); +} + +void MessagesManager::on_failed_get_message_search_result_calendar(DialogId dialog_id, int64 random_id) { + auto it = found_dialog_message_calendars_.find(random_id); + CHECK(it != found_dialog_message_calendars_.end()); + found_dialog_message_calendars_.erase(it); +} + void MessagesManager::on_get_dialog_messages_search_result( DialogId dialog_id, const string &query, DialogId sender_dialog_id, MessageId from_message_id, int32 offset, int32 limit, MessageSearchFilter filter, MessageId top_thread_message_id, int64 random_id, int32 total_count, @@ -10401,13 +10658,13 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector message_ids_; @@ -10489,15 +10746,15 @@ class MessagesManager::DeleteMessagesFromServerLogEvent { } }; -uint64 MessagesManager::save_delete_messages_from_server_log_event(DialogId dialog_id, - const vector &message_ids, bool revoke) { - DeleteMessagesFromServerLogEvent log_event{dialog_id, message_ids, revoke}; - return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteMessagesFromServer, +uint64 MessagesManager::save_delete_messages_on_server_log_event(DialogId dialog_id, + const vector &message_ids, bool revoke) { + DeleteMessagesOnServerLogEvent log_event{dialog_id, message_ids, revoke}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteMessagesOnServer, get_log_event_storer(log_event)); } -void MessagesManager::delete_messages_from_server(DialogId dialog_id, vector message_ids, bool revoke, - uint64 log_event_id, Promise &&promise) { +void MessagesManager::delete_messages_on_server(DialogId dialog_id, vector message_ids, bool revoke, + uint64 log_event_id, Promise &&promise) { if (message_ids.empty()) { return promise.set_value(Unit()); } @@ -10505,7 +10762,7 @@ void MessagesManager::delete_messages_from_server(DialogId dialog_id, vectorparameters().use_message_db) { - log_event_id = save_delete_messages_from_server_log_event(dialog_id, message_ids, revoke); + log_event_id = save_delete_messages_on_server_log_event(dialog_id, message_ids, revoke); } auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); @@ -10522,7 +10779,7 @@ void MessagesManager::delete_messages_from_server(DialogId dialog_id, vector random_ids; - auto d = get_dialog_force(dialog_id, "delete_messages_from_server"); + auto d = get_dialog_force(dialog_id, "delete_messages_on_server"); CHECK(d != nullptr); for (auto &message_id : message_ids) { auto *m = get_message(d, message_id); @@ -10544,7 +10801,7 @@ void MessagesManager::delete_messages_from_server(DialogId dialog_id, vector message_ids_; @@ -10562,22 +10819,22 @@ class MessagesManager::DeleteScheduledMessagesFromServerLogEvent { } }; -uint64 MessagesManager::save_delete_scheduled_messages_from_server_log_event(DialogId dialog_id, - const vector &message_ids) { - DeleteScheduledMessagesFromServerLogEvent log_event{dialog_id, message_ids}; - return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteScheduledMessagesFromServer, +uint64 MessagesManager::save_delete_scheduled_messages_on_server_log_event(DialogId dialog_id, + const vector &message_ids) { + DeleteScheduledMessagesOnServerLogEvent log_event{dialog_id, message_ids}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteScheduledMessagesOnServer, get_log_event_storer(log_event)); } -void MessagesManager::delete_scheduled_messages_from_server(DialogId dialog_id, vector message_ids, - uint64 log_event_id, Promise &&promise) { +void MessagesManager::delete_scheduled_messages_on_server(DialogId dialog_id, vector message_ids, + uint64 log_event_id, Promise &&promise) { if (message_ids.empty()) { return promise.set_value(Unit()); } LOG(INFO) << "Delete " << format::as_array(message_ids) << " in " << dialog_id << " from server"; if (log_event_id == 0 && G()->parameters().use_message_db) { - log_event_id = save_delete_scheduled_messages_from_server_log_event(dialog_id, message_ids); + log_event_id = save_delete_scheduled_messages_on_server_log_event(dialog_id, message_ids); } auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); @@ -10660,11 +10917,11 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from set_dialog_max_unavailable_message_id(dialog_id, last_new_message_id, false, "delete_dialog_history"); - delete_dialog_history_from_server(dialog_id, last_new_message_id, remove_from_dialog_list, revoke, allow_error, 0, - std::move(promise)); + delete_dialog_history_on_server(dialog_id, last_new_message_id, remove_from_dialog_list, revoke, allow_error, 0, + std::move(promise)); } -class MessagesManager::DeleteDialogHistoryFromServerLogEvent { +class MessagesManager::DeleteDialogHistoryOnServerLogEvent { public: DialogId dialog_id_; MessageId max_message_id_; @@ -10694,21 +10951,21 @@ class MessagesManager::DeleteDialogHistoryFromServerLogEvent { } }; -uint64 MessagesManager::save_delete_dialog_history_from_server_log_event(DialogId dialog_id, MessageId max_message_id, - bool remove_from_dialog_list, bool revoke) { - DeleteDialogHistoryFromServerLogEvent log_event{dialog_id, max_message_id, remove_from_dialog_list, revoke}; - return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteDialogHistoryFromServer, +uint64 MessagesManager::save_delete_dialog_history_on_server_log_event(DialogId dialog_id, MessageId max_message_id, + bool remove_from_dialog_list, bool revoke) { + DeleteDialogHistoryOnServerLogEvent log_event{dialog_id, max_message_id, remove_from_dialog_list, revoke}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteDialogHistoryOnServer, get_log_event_storer(log_event)); } -void MessagesManager::delete_dialog_history_from_server(DialogId dialog_id, MessageId max_message_id, - bool remove_from_dialog_list, bool revoke, bool allow_error, - uint64 log_event_id, Promise &&promise) { +void MessagesManager::delete_dialog_history_on_server(DialogId dialog_id, MessageId max_message_id, + bool remove_from_dialog_list, bool revoke, bool allow_error, + uint64 log_event_id, Promise &&promise) { LOG(INFO) << "Delete history in " << dialog_id << " up to " << max_message_id << " from server"; if (log_event_id == 0 && G()->parameters().use_message_db) { log_event_id = - save_delete_dialog_history_from_server_log_event(dialog_id, max_message_id, remove_from_dialog_list, revoke); + save_delete_dialog_history_on_server_log_event(dialog_id, max_message_id, remove_from_dialog_list, revoke); } auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); @@ -10736,10 +10993,10 @@ void MessagesManager::delete_dialog_history_from_server(DialogId dialog_id, Mess } void MessagesManager::delete_all_call_messages(bool revoke, Promise &&promise) { - delete_all_call_messages_from_server(revoke, 0, std::move(promise)); + delete_all_call_messages_on_server(revoke, 0, std::move(promise)); } -class MessagesManager::DeleteAllCallMessagesFromServerLogEvent { +class MessagesManager::DeleteAllCallMessagesOnServerLogEvent { public: bool revoke_; @@ -10758,15 +11015,15 @@ class MessagesManager::DeleteAllCallMessagesFromServerLogEvent { } }; -uint64 MessagesManager::save_delete_all_call_messages_from_server_log_event(bool revoke) { - DeleteAllCallMessagesFromServerLogEvent log_event{revoke}; - return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteAllCallMessagesFromServer, +uint64 MessagesManager::save_delete_all_call_messages_on_server_log_event(bool revoke) { + DeleteAllCallMessagesOnServerLogEvent log_event{revoke}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteAllCallMessagesOnServer, get_log_event_storer(log_event)); } -void MessagesManager::delete_all_call_messages_from_server(bool revoke, uint64 log_event_id, Promise &&promise) { +void MessagesManager::delete_all_call_messages_on_server(bool revoke, uint64 log_event_id, Promise &&promise) { if (log_event_id == 0) { - log_event_id = save_delete_all_call_messages_from_server_log_event(revoke); + log_event_id = save_delete_all_call_messages_on_server_log_event(revoke); } auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); @@ -10841,8 +11098,6 @@ void MessagesManager::delete_dialog_messages_from_user(DialogId dialog_id, UserI bool is_bot = td_->auth_manager_->is_bot(); CHECK(!is_bot); - LOG(INFO) << "Receive deleteChatMessagesFromUser request to delete all messages in " << dialog_id << " from the user " - << user_id; Dialog *d = get_dialog_force(dialog_id, "delete_dialog_messages_from_user"); if (d == nullptr) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -10950,6 +11205,126 @@ void MessagesManager::delete_all_channel_messages_from_user_on_server(ChannelId ->send(channel_id, user_id); } +void MessagesManager::delete_dialog_messages_by_date(DialogId dialog_id, int32 min_date, int32 max_date, bool revoke, + Promise &&promise) { + bool is_bot = td_->auth_manager_->is_bot(); + CHECK(!is_bot); + + Dialog *d = get_dialog_force(dialog_id, "delete_dialog_messages_by_date"); + if (d == nullptr) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(400, "Can't access the chat")); + } + + if (min_date > max_date) { + return promise.set_error(Status::Error(400, "Wrong date interval specified")); + } + + const int32 telegram_launch_date = 1376438400; + if (max_date < telegram_launch_date) { + return promise.set_value(Unit()); + } + if (min_date < telegram_launch_date) { + min_date = telegram_launch_date; + } + + auto current_date = max(G()->unix_time(), 1635000000); + if (min_date >= current_date - 30) { + return promise.set_value(Unit()); + } + if (max_date >= current_date - 30) { + max_date = current_date - 31; + } + CHECK(min_date <= max_date); + + switch (dialog_id.get_type()) { + case DialogType::User: + break; + case DialogType::Chat: + if (revoke) { + return promise.set_error(Status::Error(400, "Bulk message revocation is unsupported in basic group chats")); + } + break; + case DialogType::Channel: + return promise.set_error(Status::Error(400, "Bulk message deletion is unsupported in supergroup chats")); + case DialogType::SecretChat: + return promise.set_error(Status::Error(400, "Bulk message deletion is unsupported in secret chats")); + case DialogType::None: + default: + UNREACHABLE(); + break; + } + + // TODO delete in database by dates + + vector message_ids; + find_messages_by_date(d->messages.get(), min_date, max_date, message_ids); + + bool need_update_dialog_pos = false; + vector deleted_message_ids; + for (auto message_id : message_ids) { + auto m = delete_message(d, message_id, true, &need_update_dialog_pos, DELETE_MESSAGE_USER_REQUEST_SOURCE); + CHECK(m != nullptr); + deleted_message_ids.push_back(m->message_id.get()); + } + + if (need_update_dialog_pos) { + send_update_chat_last_message(d, "delete_dialog_messages_by_date"); + } + send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false); + + delete_dialog_messages_by_date_on_server(dialog_id, min_date, max_date, revoke, 0, std::move(promise)); +} + +class MessagesManager::DeleteDialogMessagesByDateOnServerLogEvent { + public: + DialogId dialog_id_; + int32 min_date_; + int32 max_date_; + bool revoke_; + + template + void store(StorerT &storer) const { + BEGIN_STORE_FLAGS(); + STORE_FLAG(revoke_); + END_STORE_FLAGS(); + td::store(dialog_id_, storer); + td::store(min_date_, storer); + td::store(max_date_, storer); + } + + template + void parse(ParserT &parser) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(revoke_); + END_PARSE_FLAGS(); + td::parse(dialog_id_, parser); + td::parse(min_date_, parser); + td::parse(max_date_, parser); + } +}; + +uint64 MessagesManager::save_delete_dialog_messages_by_date_on_server_log_event(DialogId dialog_id, int32 min_date, + int32 max_date, bool revoke) { + DeleteDialogMessagesByDateOnServerLogEvent log_event{dialog_id, min_date, max_date, revoke}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteDialogMessagesByDateOnServer, + get_log_event_storer(log_event)); +} + +void MessagesManager::delete_dialog_messages_by_date_on_server(DialogId dialog_id, int32 min_date, int32 max_date, + bool revoke, uint64 log_event_id, + Promise &&promise) { + if (log_event_id == 0 && G()->parameters().use_chat_info_db) { + log_event_id = save_delete_dialog_messages_by_date_on_server_log_event(dialog_id, min_date, max_date, revoke); + } + + td_->create_handler(get_erase_log_event_promise(log_event_id, std::move(promise))) + ->send(dialog_id, min_date, max_date, revoke); +} + int32 MessagesManager::get_unload_dialog_delay() const { constexpr int32 DIALOG_UNLOAD_DELAY = 60; // seconds constexpr int32 DIALOG_UNLOAD_BOT_DELAY = 1800; // seconds @@ -13312,7 +13687,7 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( message_info.forward_header ? message_info.forward_header->date_ : message_info.date, message_info.media_album_id != 0, new_source.c_str()), std::move(message->media_), message_info.dialog_id, is_content_read, message_info.via_bot_user_id, - &message_info.ttl); + &message_info.ttl, &message_info.disable_web_page_preview); message_info.reply_markup = message->flags_ & MESSAGE_FLAG_HAS_REPLY_MARKUP ? std::move(message->reply_markup_) : nullptr; message_info.restriction_reasons = get_restriction_reasons(std::move(message->restriction_reason_)); @@ -13555,6 +13930,7 @@ std::pair> MessagesManager::creat message->date = date; message->ttl_period = ttl_period; message->ttl = ttl; + message->disable_web_page_preview = message_info.disable_web_page_preview; message->edit_date = edit_date; message->random_id = message_info.random_id; message->forward_info = get_message_forward_info(std::move(message_info.forward_header)); @@ -13722,7 +14098,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f unique_ptr old_message = delete_message(d, old_message_id, false, &need_update_dialog_pos, "add sent message"); if (old_message == nullptr) { - delete_sent_message_from_server(dialog_id, new_message->message_id); + delete_sent_message_on_server(dialog_id, new_message->message_id); being_readded_message_id_ = FullMessageId(); return FullMessageId(); } @@ -14315,8 +14691,9 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, FormattedText new_message_text = get_message_text( td_->contacts_manager_.get(), old_message_text->text, std::move(entities), true, td_->auth_manager_->is_bot(), m->forward_info ? m->forward_info->date : m->date, m->media_album_id != 0, "on_update_sent_text_message"); - auto new_content = get_message_content(td_, std::move(new_message_text), std::move(message_media), dialog_id, - true /*likely ignored*/, UserId() /*likely ignored*/, nullptr /*ignored*/); + auto new_content = + get_message_content(td_, std::move(new_message_text), std::move(message_media), dialog_id, + true /*likely ignored*/, UserId() /*likely ignored*/, nullptr /*ignored*/, nullptr); if (new_content->get_type() != MessageContentType::Text) { LOG(ERROR) << "Text message content has changed to " << new_content->get_type(); return; @@ -14595,7 +14972,7 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vectorpts_, "get channel"); } } - bool is_marked_as_unread = (dialog->flags_ & telegram_api::dialog::UNREAD_MARK_MASK) != 0; + bool is_marked_as_unread = dialog->unread_mark_; if (is_marked_as_unread != d->is_marked_as_unread) { set_dialog_is_marked_as_unread(d, is_marked_as_unread); } @@ -15533,7 +15910,7 @@ void MessagesManager::load_dialog_filter(const DialogFilter *filter, bool force, if (!input_dialog_ids.empty() && !force) { const size_t MAX_SLICE_SIZE = 100; - MultiPromiseActorSafe mpas{"GetFilterDialogsFromServerMultiPromiseActor"}; + MultiPromiseActorSafe mpas{"GetFilterDialogsOnServerMultiPromiseActor"}; mpas.add_promise(std::move(promise)); mpas.set_ignore_errors(true); auto lock = mpas.get_promise(); @@ -17258,15 +17635,15 @@ Status MessagesManager::can_get_message_viewers(FullMessageId full_message_id) { } Status MessagesManager::can_get_message_viewers(DialogId dialog_id, const Message *m) const { + if (td_->auth_manager_->is_bot()) { + return Status::Error(400, "User is bot"); + } if (!m->is_outgoing) { return Status::Error(400, "Can't get viewers of incoming messages"); } if (G()->unix_time() - m->date > G()->shared_config().get_option_integer("chat_read_mark_expire_period", 7 * 86400)) { return Status::Error(400, "Message is too old"); } - if (td_->auth_manager_->is_bot()) { - return Status::Error(400, "User is bot"); - } int32 participant_count = 0; switch (dialog_id.get_type()) { @@ -17553,7 +17930,7 @@ void MessagesManager::get_messages_from_server(vector &&message_i } } - MultiPromiseActorSafe mpas{"GetMessagesFromServerMultiPromiseActor"}; + MultiPromiseActorSafe mpas{"GetMessagesOnServerMultiPromiseActor"}; mpas.add_promise(std::move(promise)); auto lock = mpas.get_promise(); @@ -17582,8 +17959,10 @@ void MessagesManager::get_messages_from_server(vector &&message_i mpas.get_promise().set_error(Status::Error(400, "Can't access the chat")); continue; } + const auto *d = get_dialog_force(DialogId(it.first)); td_->create_handler(mpas.get_promise()) - ->send(it.first, std::move(input_channel), std::move(it.second)); + ->send(it.first, std::move(input_channel), std::move(it.second), + d == nullptr ? MessageId() : d->last_new_message_id); } lock.set_value(Unit()); } @@ -20155,13 +20534,23 @@ string MessagesManager::get_dialog_theme_name(const Dialog *d) const { return d->theme_name; } -td_api::object_ptr MessagesManager::get_voice_chat_object(const Dialog *d) const { +td_api::object_ptr MessagesManager::get_chat_join_requests_info_object( + const Dialog *d) const { + if (d->pending_join_request_count == 0) { + return nullptr; + } + return td_api::make_object( + d->pending_join_request_count, td_->contacts_manager_->get_user_ids_object(d->pending_join_request_user_ids, + "get_chat_join_requests_info_object")); +} + +td_api::object_ptr MessagesManager::get_video_chat_object(const Dialog *d) const { auto active_group_call_id = td_->group_call_manager_->get_group_call_id(d->active_group_call_id, d->dialog_id); auto default_participant_alias = d->default_join_group_call_as_dialog_id.is_valid() - ? get_message_sender_object_const(d->default_join_group_call_as_dialog_id, "get_voice_chat_object") + ? get_message_sender_object_const(d->default_join_group_call_as_dialog_id, "get_video_chat_object") : nullptr; - return make_tl_object(active_group_call_id.get(), + return make_tl_object(active_group_call_id.get(), active_group_call_id.is_valid() ? !d->is_group_call_empty : false, std::move(default_participant_alias)); } @@ -20194,7 +20583,7 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * } break; case DialogType::Chat: - // chats can't be deleted only for self with deleteChatHistory + // chats can be deleted only for self with deleteChatHistory can_delete_for_self = true; break; case DialogType::Channel: @@ -20228,7 +20617,7 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * return make_tl_object( d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_dialog_title(d->dialog_id), get_chat_photo_info_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)), - get_dialog_permissions(d->dialog_id).get_chat_permissions_object(), + get_dialog_default_permissions(d->dialog_id).get_chat_permissions_object(), get_message_object(d->dialog_id, get_message(d, d->last_message_id), "get_chat_object"), get_chat_positions_object(d), d->is_marked_as_unread, d->is_blocked, get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users, can_report_dialog(d->dialog_id), @@ -20236,7 +20625,8 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * d->last_read_inbox_message_id.get(), d->last_read_outbox_message_id.get(), d->unread_mention_count, get_chat_notification_settings_object(&d->notification_settings), d->message_ttl_setting.get_message_ttl_setting_object(), get_dialog_theme_name(d), get_chat_action_bar_object(d), - get_voice_chat_object(d), d->reply_markup_message_id.get(), std::move(draft_message), d->client_data); + get_video_chat_object(d), get_chat_join_requests_info_object(d), d->reply_markup_message_id.get(), + std::move(draft_message), d->client_data); } tl_object_ptr MessagesManager::get_chat_object(DialogId dialog_id) const { @@ -21062,7 +21452,7 @@ std::pair> MessagesManager::get_message_thread_histo from_message_id = MessageId::max(); } if (!from_message_id.is_valid()) { - promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of the chat message or 0")); + promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of a chat message or 0")); return {}; } @@ -21181,6 +21571,163 @@ std::pair> MessagesManager::get_message_thread_histo return {}; } +td_api::object_ptr MessagesManager::get_dialog_message_calendar(DialogId dialog_id, + MessageId from_message_id, + MessageSearchFilter filter, + int64 &random_id, bool use_db, + Promise &&promise) { + if (random_id != 0) { + // request has already been sent before + auto it = found_dialog_message_calendars_.find(random_id); + if (it != found_dialog_message_calendars_.end()) { + auto result = std::move(it->second); + found_dialog_message_calendars_.erase(it); + promise.set_value(Unit()); + return result; + } + random_id = 0; + } + LOG(INFO) << "Get message calendar in " << dialog_id << " filtered by " << filter << " from " << from_message_id; + + if (from_message_id.get() > MessageId::max().get()) { + from_message_id = MessageId::max(); + } + + if (!from_message_id.is_valid() && from_message_id != MessageId()) { + promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of a chat message or 0")); + return {}; + } + from_message_id = from_message_id.get_next_server_message_id(); + + const Dialog *d = get_dialog_force(dialog_id, "get_dialog_message_calendar"); + if (d == nullptr) { + promise.set_error(Status::Error(400, "Chat not found")); + return {}; + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { + promise.set_error(Status::Error(400, "Can't access the chat")); + return {}; + } + + do { + random_id = Random::secure_int64(); + } while (random_id == 0 || found_dialog_message_calendars_.find(random_id) != found_dialog_message_calendars_.end()); + found_dialog_message_calendars_[random_id]; // reserve place for result + + if (filter == MessageSearchFilter::Empty || filter == MessageSearchFilter::Call || + filter == MessageSearchFilter::MissedCall || filter == MessageSearchFilter::Mention || + filter == MessageSearchFilter::UnreadMention) { + promise.set_error(Status::Error(400, "The filter is not supported")); + return {}; + } + + // Trying to use database + if (use_db && G()->parameters().use_message_db) { + MessageId first_db_message_id = get_first_database_message_id_by_index(d, filter); + int32 message_count = d->message_count_by_index[message_search_filter_index(filter)]; + auto fixed_from_message_id = from_message_id; + if (fixed_from_message_id == MessageId()) { + fixed_from_message_id = MessageId::max(); + } + LOG(INFO) << "Get message calendar in " << dialog_id << " from " << fixed_from_message_id << ", have up to " + << first_db_message_id << ", message_count = " << message_count; + if (first_db_message_id < fixed_from_message_id && message_count != -1) { + LOG(INFO) << "Get message calendar from database in " << dialog_id << " from " << fixed_from_message_id; + auto new_promise = + PromiseCreator::lambda([random_id, dialog_id, fixed_from_message_id, first_db_message_id, filter, + promise = std::move(promise)](Result r_calendar) mutable { + send_closure(G()->messages_manager(), &MessagesManager::on_get_message_calendar_from_database, random_id, + dialog_id, fixed_from_message_id, first_db_message_id, filter, std::move(r_calendar), + std::move(promise)); + }); + MessagesDbDialogCalendarQuery db_query; + db_query.dialog_id = dialog_id; + db_query.filter = filter; + db_query.from_message_id = fixed_from_message_id; + db_query.tz_offset = static_cast(G()->shared_config().get_option_integer("utc_time_offset")); + G()->td_db()->get_messages_db_async()->get_dialog_message_calendar(db_query, std::move(new_promise)); + return {}; + } + } + if (filter == MessageSearchFilter::FailedToSend) { + promise.set_value(Unit()); + return {}; + } + + LOG(DEBUG) << "Get message calendar from server in " << dialog_id << " from " << from_message_id; + + switch (dialog_id.get_type()) { + case DialogType::None: + case DialogType::User: + case DialogType::Chat: + case DialogType::Channel: + td_->create_handler(std::move(promise)) + ->send(dialog_id, from_message_id, filter, random_id); + break; + case DialogType::SecretChat: + promise.set_value(Unit()); + break; + default: + UNREACHABLE(); + promise.set_error(Status::Error(500, "Search messages is not supported")); + } + return {}; +} + +void MessagesManager::on_get_message_calendar_from_database(int64 random_id, DialogId dialog_id, + MessageId from_message_id, MessageId first_db_message_id, + MessageSearchFilter filter, + Result r_calendar, + Promise promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + if (r_calendar.is_error()) { + LOG(ERROR) << "Failed to get message calendar from the database: " << r_calendar.error(); + if (first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat && + filter != MessageSearchFilter::FailedToSend) { + found_dialog_message_calendars_.erase(random_id); + } + return promise.set_value(Unit()); + } + CHECK(!from_message_id.is_scheduled()); + CHECK(!first_db_message_id.is_scheduled()); + + auto calendar = r_calendar.move_as_ok(); + + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + + auto it = found_dialog_message_calendars_.find(random_id); + CHECK(it != found_dialog_message_calendars_.end()); + CHECK(it->second == nullptr); + + vector> periods; + periods.reserve(calendar.messages.size()); + for (size_t i = 0; i < calendar.messages.size(); i++) { + auto m = on_get_message_from_database(d, calendar.messages[i], false, "on_get_message_calendar_from_database"); + if (m != nullptr && first_db_message_id <= m->message_id) { + CHECK(!m->message_id.is_scheduled()); + periods.emplace_back(m->message_id, calendar.total_counts[i]); + } + } + + if (periods.empty() && first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat) { + LOG(INFO) << "No messages found in database"; + found_dialog_message_calendars_.erase(it); + } else { + auto total_count = d->message_count_by_index[message_search_filter_index(filter)]; + vector> days; + for (auto &period : periods) { + const auto *m = get_message(d, period.first); + CHECK(m != nullptr); + days.push_back(td_api::make_object( + period.second, get_message_object(dialog_id, m, "on_get_message_calendar_from_database"))); + } + it->second = td_api::make_object(total_count, std::move(days)); + } + promise.set_value(Unit()); +} + std::pair> MessagesManager::search_dialog_messages( DialogId dialog_id, const string &query, const td_api::object_ptr &sender, MessageId from_message_id, int32 offset, int32 limit, MessageSearchFilter filter, MessageId top_thread_message_id, @@ -21223,7 +21770,7 @@ std::pair> MessagesManager::search_dialog_messages( } if (!from_message_id.is_valid() && from_message_id != MessageId()) { - promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of the chat message or 0")); + promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of a chat message or 0")); return result; } from_message_id = from_message_id.get_next_server_message_id(); @@ -21341,7 +21888,7 @@ std::pair> MessagesManager::search_dialog_messages( }); MessagesDbMessagesQuery db_query; db_query.dialog_id = dialog_id; - db_query.index_mask = message_search_filter_index_mask(filter); + db_query.filter = filter; db_query.from_message_id = fixed_from_message_id; db_query.offset = offset; db_query.limit = limit; @@ -21411,7 +21958,7 @@ std::pair> MessagesManager::search_call_messages(Me } if (!from_message_id.is_valid() && from_message_id != MessageId()) { - promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of the chat message or 0")); + promise.set_error(Status::Error(400, "Parameter from_message_id must be identifier of a chat message or 0")); return result; } from_message_id = from_message_id.get_next_server_message_id(); @@ -21439,7 +21986,7 @@ std::pair> MessagesManager::search_call_messages(Me LOG(INFO) << "Search messages in database from " << fixed_from_message_id << " and with limit " << limit; MessagesDbCallsQuery db_query; - db_query.index_mask = message_search_filter_index_mask(filter); + db_query.filter = filter; db_query.from_unique_message_id = fixed_from_message_id.get_server_message_id().get(); db_query.limit = limit; G()->td_db()->get_messages_db_async()->get_calls( @@ -21857,7 +22404,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo MessageId from_message_id, MessageId first_db_message_id, MessageSearchFilter filter, int32 offset, int32 limit, Result> r_messages, - Promise<> promise) { + Promise promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (r_messages.is_error()) { @@ -21911,7 +22458,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo } it->second.first = message_count; if (res.empty() && first_db_message_id != MessageId::min() && dialog_id.get_type() != DialogType::SecretChat) { - LOG(INFO) << "No messages in database found"; + LOG(INFO) << "No messages found in database"; found_dialog_messages_.erase(it); } else { LOG(INFO) << "Found " << res.size() << " messages out of " << message_count << " in database"; @@ -21975,7 +22522,7 @@ MessagesManager::FoundMessages MessagesManager::offline_search_messages(DialogId MessagesDbFtsQuery fts_query; fts_query.query = query; fts_query.dialog_id = dialog_id; - fts_query.index_mask = message_search_filter_index_mask(filter); + fts_query.filter = filter; if (!offset.empty()) { auto r_from_search_id = to_integer_safe(offset); if (r_from_search_id.is_error()) { @@ -22061,7 +22608,7 @@ void MessagesManager::on_messages_db_calls_result(Result it->second.first = calls_db_state_.message_count_by_index[call_message_search_filter_index(filter)]; if (res.empty() && first_db_message_id != MessageId::min()) { - LOG(INFO) << "No messages in database found"; + LOG(INFO) << "No messages found in database"; found_call_messages_.erase(it); } @@ -22194,6 +22741,23 @@ MessageId MessagesManager::find_message_by_date(const Message *m, int32 date) { return m->message_id; } +void MessagesManager::find_messages_by_date(const Message *m, int32 min_date, int32 max_date, + vector &message_ids) { + if (m == nullptr) { + return; + } + + if (m->date >= min_date) { + find_messages_by_date(m->left.get(), min_date, max_date, message_ids); + if (m->date <= max_date) { + message_ids.push_back(m->message_id); + } + } + if (m->date <= max_date) { + find_messages_by_date(m->right.get(), min_date, max_date, message_ids); + } +} + void MessagesManager::on_get_dialog_message_by_date_from_database(DialogId dialog_id, int32 date, int64 random_id, Result result, Promise promise) { @@ -22294,6 +22858,90 @@ tl_object_ptr MessagesManager::get_dialog_message_by_date_objec return get_message_object(full_message_id, "get_dialog_message_by_date_object"); } +void MessagesManager::get_dialog_sparse_message_positions( + DialogId dialog_id, MessageSearchFilter filter, MessageId from_message_id, int32 limit, + Promise> &&promise) { + const Dialog *d = get_dialog_force(dialog_id, "get_dialog_sparse_message_positions"); + if (d == nullptr) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + if (limit < 50 || limit > 2000) { // server-side limits + return promise.set_error(Status::Error(400, "Invalid limit specified")); + } + + if (filter == MessageSearchFilter::Empty || filter == MessageSearchFilter::Call || + filter == MessageSearchFilter::MissedCall || filter == MessageSearchFilter::Mention || + filter == MessageSearchFilter::UnreadMention || filter == MessageSearchFilter::Pinned) { + return promise.set_error(Status::Error(400, "The filter is not supported")); + } + + if (from_message_id.is_scheduled()) { + return promise.set_error(Status::Error(400, "Invalid from_message_id specified")); + } + if (!from_message_id.is_valid() || from_message_id > d->last_new_message_id) { + if (d->last_new_message_id.is_valid()) { + from_message_id = d->last_new_message_id.get_next_message_id(MessageType::Server); + } else { + from_message_id = MessageId::max(); + } + } else { + from_message_id = from_message_id.get_next_server_message_id(); + } + + if (filter == MessageSearchFilter::FailedToSend || dialog_id.get_type() == DialogType::SecretChat) { + if (!G()->parameters().use_message_db) { + return promise.set_error(Status::Error(400, "Unsupported without message database")); + } + + LOG(INFO) << "Get sparse message positions from database"; + auto new_promise = + PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + + auto positions = result.move_as_ok(); + promise.set_value(td_api::make_object( + positions.total_count, transform(positions.positions, [](const MessagesDbMessagePosition &position) { + return td_api::make_object(position.position, position.message_id.get(), + position.date); + }))); + }); + MessagesDbGetDialogSparseMessagePositionsQuery db_query; + db_query.dialog_id = dialog_id; + db_query.filter = filter; + db_query.from_message_id = from_message_id; + db_query.limit = limit; + G()->td_db()->get_messages_db_async()->get_dialog_sparse_message_positions(db_query, std::move(new_promise)); + return; + } + + switch (dialog_id.get_type()) { + case DialogType::User: + case DialogType::Chat: + case DialogType::Channel: + td_->create_handler(std::move(promise)) + ->send(dialog_id, filter, from_message_id, limit); + break; + case DialogType::SecretChat: + case DialogType::None: + default: + UNREACHABLE(); + } +} + +void MessagesManager::on_get_dialog_sparse_message_positions( + DialogId dialog_id, MessageSearchFilter filter, + telegram_api::object_ptr positions, + Promise> &&promise) { + auto message_positions = transform( + positions->positions_, [](const telegram_api::object_ptr &position) { + return td_api::make_object( + position->offset_, MessageId(ServerMessageId(position->msg_id_)).get(), position->date_); + }); + promise.set_value(td_api::make_object(positions->count_, std::move(message_positions))); +} + void MessagesManager::get_dialog_message_count(DialogId dialog_id, MessageSearchFilter filter, bool return_local, Promise &&promise) { LOG(INFO) << "Get " << (return_local ? "local " : "") << "number of messages in " << dialog_id << " filtered by " @@ -22320,7 +22968,7 @@ void MessagesManager::get_dialog_message_count(DialogId dialog_id, MessageSearch LOG(INFO) << "Get number of messages in " << dialog_id << " filtered by " << filter << " from the server"; - switch (dialog_id.get_type()) { + switch (dialog_type) { case DialogType::User: case DialogType::Chat: case DialogType::Channel: @@ -24206,7 +24854,7 @@ void MessagesManager::on_upload_message_media_success(DialogId dialog_id, Messag auto caption = get_message_content_caption(m->content.get()); auto content = get_message_content(td_, caption == nullptr ? FormattedText() : *caption, std::move(media), dialog_id, - false, UserId(), nullptr); + false, UserId(), nullptr, nullptr); if (update_message_content(dialog_id, m, std::move(content), true, true, true) && m->message_id == d->last_message_id) { @@ -25920,14 +26568,13 @@ void MessagesManager::unregister_message_reply(const Dialog *d, const Message *m } bool MessagesManager::get_message_disable_web_page_preview(const Message *m) { - // m->disable_web_page_preview is known only for sent from this client messages - if (m->disable_web_page_preview) { - return true; - } if (m->content->get_type() != MessageContentType::Text) { return false; } - return !has_message_content_web_page(m->content.get()); + if (has_message_content_web_page(m->content.get())) { + return false; + } + return m->disable_web_page_preview; } int32 MessagesManager::get_message_flags(const Message *m) { @@ -26075,7 +26722,7 @@ unique_ptr MessagesManager::get_message_for message_id = MessageId(); } } - if ((flags & telegram_api::messageFwdHeader::SAVED_FROM_PEER_MASK) != 0) { + if (forward_header->saved_from_peer_ != nullptr) { from_dialog_id = DialogId(forward_header->saved_from_peer_); from_message_id = MessageId(ServerMessageId(forward_header->saved_from_msg_id_)); if (!from_dialog_id.is_valid() || !from_message_id.is_valid()) { @@ -27184,7 +27831,7 @@ bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_messag being_sent_messages_.erase(it); if (!have_message_force({dialog_id, old_message_id}, "on_update_message_id")) { - delete_sent_message_from_server(dialog_id, new_message_id); + delete_sent_message_on_server(dialog_id, new_message_id); return true; } @@ -27214,7 +27861,7 @@ bool MessagesManager::on_update_scheduled_message_id(int64 random_id, ScheduledS being_sent_messages_.erase(it); if (!have_message_force({dialog_id, old_message_id}, "on_update_scheduled_message_id")) { - delete_sent_message_from_server(dialog_id, MessageId(new_message_id, std::numeric_limits::max())); + delete_sent_message_on_server(dialog_id, MessageId(new_message_id, std::numeric_limits::max())); return true; } @@ -27733,7 +28380,7 @@ Result> MessagesManager::do_get_message_notifica // ignore first_db_message_id, notifications can be nonconsecutive MessagesDbMessagesQuery db_query; db_query.dialog_id = d->dialog_id; - db_query.index_mask = message_search_filter_index_mask(MessageSearchFilter::UnreadMention); + db_query.filter = MessageSearchFilter::UnreadMention; db_query.from_message_id = from_message_id; db_query.offset = 0; db_query.limit = limit; @@ -27855,7 +28502,7 @@ void MessagesManager::do_get_message_notifications_from_database(Dialog *d, bool // ignore first_db_message_id, notifications can be nonconsecutive MessagesDbMessagesQuery db_query; db_query.dialog_id = dialog_id; - db_query.index_mask = message_search_filter_index_mask(MessageSearchFilter::UnreadMention); + db_query.filter = MessageSearchFilter::UnreadMention; db_query.from_message_id = from_message_id; db_query.offset = 0; db_query.limit = limit; @@ -28936,12 +29583,25 @@ void MessagesManager::send_update_chat_theme(const Dialog *d) { send_update_secret_chats_with_user_theme(d); } -void MessagesManager::send_update_chat_voice_chat(const Dialog *d) { +void MessagesManager::send_update_chat_pending_join_requests(const Dialog *d) { + if (td_->auth_manager_->is_bot()) { + return; + } + CHECK(d != nullptr); - LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_voice_chat"; - on_dialog_updated(d->dialog_id, "send_update_chat_voice_chat"); + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_pending_join_requests"; + on_dialog_updated(d->dialog_id, "send_update_chat_pending_join_requests"); send_closure(G()->td(), &Td::send_update, - td_api::make_object(d->dialog_id.get(), get_voice_chat_object(d))); + td_api::make_object(d->dialog_id.get(), + get_chat_join_requests_info_object(d))); +} + +void MessagesManager::send_update_chat_video_chat(const Dialog *d) { + CHECK(d != nullptr); + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_video_chat"; + on_dialog_updated(d->dialog_id, "send_update_chat_video_chat"); + send_closure(G()->td(), &Td::send_update, + td_api::make_object(d->dialog_id.get(), get_video_chat_object(d))); } void MessagesManager::send_update_chat_message_ttl_setting(const Dialog *d) { @@ -29088,7 +29748,7 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI being_readded_message_id_ = {dialog_id, old_message_id}; unique_ptr sent_message = delete_message(d, old_message_id, false, &need_update_dialog_pos, source); if (sent_message == nullptr) { - delete_sent_message_from_server(dialog_id, new_message_id); + delete_sent_message_on_server(dialog_id, new_message_id); being_readded_message_id_ = FullMessageId(); return {}; } @@ -29932,6 +30592,9 @@ void MessagesManager::on_update_dialog_theme_name(DialogId dialog_id, string the LOG(ERROR) << "Receive theme in invalid " << dialog_id; return; } + if (td_->auth_manager_->is_bot()) { + return; + } auto d = get_dialog_force(dialog_id, "on_update_dialog_theme_name"); if (d == nullptr) { @@ -29944,20 +30607,116 @@ void MessagesManager::on_update_dialog_theme_name(DialogId dialog_id, string the void MessagesManager::set_dialog_theme_name(Dialog *d, string theme_name) { CHECK(d != nullptr); + if (td_->auth_manager_->is_bot()) { + return; + } + bool is_changed = d->theme_name != theme_name; if (!is_changed && d->is_theme_name_inited) { return; } d->theme_name = std::move(theme_name); d->is_theme_name_inited = true; - on_dialog_updated(d->dialog_id, "set_dialog_theme_name"); if (is_changed) { LOG(INFO) << "Set " << d->dialog_id << " theme to \"" << theme_name << '"'; send_update_chat_theme(d); + } else { + on_dialog_updated(d->dialog_id, "set_dialog_theme_name"); } } +void MessagesManager::drop_dialog_pending_join_requests(DialogId dialog_id) { + CHECK(dialog_id.is_valid()); + if (td_->auth_manager_->is_bot()) { + return; + } + auto d = get_dialog(dialog_id); // called from update_chat/channel, must not create the dialog + if (d != nullptr && d->is_update_new_chat_sent) { + set_dialog_pending_join_requests(d, 0, {}); + } +} + +void MessagesManager::on_update_dialog_pending_join_requests(DialogId dialog_id, int32 pending_join_request_count, + vector pending_requesters) { + if (!dialog_id.is_valid()) { + LOG(ERROR) << "Receive pending join request count in invalid " << dialog_id; + return; + } + if (td_->auth_manager_->is_bot()) { + return; + } + + auto d = get_dialog_force(dialog_id, "on_update_dialog_pending_join_request_count"); + if (d == nullptr) { + // nothing to do + return; + } + + auto pending_join_request_user_ids = UserId::get_user_ids(pending_requesters); + td::remove_if(pending_join_request_user_ids, [](UserId user_id) { return !user_id.is_valid(); }); + set_dialog_pending_join_requests(d, pending_join_request_count, std::move(pending_join_request_user_ids)); +} + +void MessagesManager::fix_pending_join_requests(DialogId dialog_id, int32 &pending_join_request_count, + vector &pending_join_request_user_ids) const { + bool need_drop_pending_join_requests = [&] { + if (pending_join_request_count < 0) { + return true; + } + switch (dialog_id.get_type()) { + case DialogType::User: + case DialogType::SecretChat: + return true; + case DialogType::Chat: { + auto chat_id = dialog_id.get_chat_id(); + auto status = td_->contacts_manager_->get_chat_status(chat_id); + if (!status.can_manage_invite_links()) { + return true; + } + break; + } + case DialogType::Channel: { + auto channel_id = dialog_id.get_channel_id(); + auto status = td_->contacts_manager_->get_channel_permissions(channel_id); + if (!status.can_manage_invite_links()) { + return true; + } + break; + } + case DialogType::None: + default: + UNREACHABLE(); + } + return false; + }(); + if (need_drop_pending_join_requests) { + pending_join_request_count = 0; + pending_join_request_user_ids.clear(); + } else if (static_cast(pending_join_request_count) < pending_join_request_user_ids.size()) { + LOG(ERROR) << "Fix pending join request count from " << pending_join_request_count << " to " + << pending_join_request_user_ids.size(); + pending_join_request_count = narrow_cast(pending_join_request_user_ids.size()); + } +} + +void MessagesManager::set_dialog_pending_join_requests(Dialog *d, int32 pending_join_request_count, + vector pending_join_request_user_ids) { + if (td_->auth_manager_->is_bot()) { + return; + } + + CHECK(d != nullptr); + fix_pending_join_requests(d->dialog_id, pending_join_request_count, pending_join_request_user_ids); + if (d->pending_join_request_count == pending_join_request_count && + d->pending_join_request_user_ids == pending_join_request_user_ids) { + return; + } + d->pending_join_request_count = pending_join_request_count; + d->pending_join_request_user_ids = std::move(pending_join_request_user_ids); + send_update_chat_pending_join_requests(d); +} + void MessagesManager::repair_dialog_scheduled_messages(Dialog *d) { if (td_->auth_manager_->is_bot() || d->dialog_id.get_type() == DialogType::SecretChat) { return; @@ -30152,10 +30911,10 @@ void MessagesManager::on_update_dialog_group_call(DialogId dialog_id, bool has_a d->active_group_call_id = InputGroupCallId(); d->has_active_group_call = false; d->is_group_call_empty = false; - send_update_chat_voice_chat(d); + send_update_chat_video_chat(d); } else if (d->has_active_group_call && has_active_group_call) { d->is_group_call_empty = is_group_call_empty; - send_update_chat_voice_chat(d); + send_update_chat_video_chat(d); } else { d->has_active_group_call = has_active_group_call; d->is_group_call_empty = is_group_call_empty; @@ -30184,7 +30943,7 @@ void MessagesManager::on_update_dialog_group_call_id(DialogId dialog_id, InputGr d->is_group_call_empty = false; } } - send_update_chat_voice_chat(d); + send_update_chat_video_chat(d); } } @@ -30214,7 +30973,7 @@ void MessagesManager::on_update_dialog_default_join_group_call_as_dialog_id(Dial if (d->default_join_group_call_as_dialog_id != default_join_as_dialog_id) { d->default_join_group_call_as_dialog_id = default_join_as_dialog_id; - send_update_chat_voice_chat(d); + send_update_chat_video_chat(d); } } @@ -30376,7 +31135,7 @@ void MessagesManager::on_dialog_permissions_updated(DialogId dialog_id) { if (d != nullptr && d->is_update_new_chat_sent) { send_closure(G()->td(), &Td::send_update, td_api::make_object( - dialog_id.get(), get_dialog_permissions(dialog_id).get_chat_permissions_object())); + dialog_id.get(), get_dialog_default_permissions(dialog_id).get_chat_permissions_object())); } } @@ -30607,7 +31366,7 @@ void MessagesManager::on_get_dialog_notification_settings_query_finished(DialogI } } -class MessagesManager::GetDialogFromServerLogEvent { +class MessagesManager::RegetDialogLogEvent { public: DialogId dialog_id_; @@ -30622,10 +31381,9 @@ class MessagesManager::GetDialogFromServerLogEvent { } }; -uint64 MessagesManager::save_get_dialog_from_server_log_event(DialogId dialog_id) { - GetDialogFromServerLogEvent log_event{dialog_id}; - return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::GetDialogFromServer, - get_log_event_storer(log_event)); +uint64 MessagesManager::save_reget_dialog_log_event(DialogId dialog_id) { + RegetDialogLogEvent log_event{dialog_id}; + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::RegetDialog, get_log_event_storer(log_event)); } void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 log_event_id, @@ -30657,7 +31415,7 @@ void MessagesManager::send_get_dialog_query(DialogId dialog_id, Promise && } if (log_event_id == 0 && G()->parameters().use_message_db) { - log_event_id = save_get_dialog_from_server_log_event(dialog_id); + log_event_id = save_reget_dialog_log_event(dialog_id); } if (log_event_id != 0) { auto result = get_dialog_query_log_event_id_.emplace(dialog_id, log_event_id); @@ -30823,7 +31581,7 @@ string MessagesManager::get_dialog_username(DialogId dialog_id) const { } } -RestrictedRights MessagesManager::get_dialog_permissions(DialogId dialog_id) const { +RestrictedRights MessagesManager::get_dialog_default_permissions(DialogId dialog_id) const { switch (dialog_id.get_type()) { case DialogType::User: return td_->contacts_manager_->get_user_default_permissions(dialog_id.get_user_id()); @@ -31476,7 +32234,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, auto new_permissions = get_restricted_rights(permissions); // TODO this can be wrong if there was previous change permissions requests - if (get_dialog_permissions(dialog_id) == new_permissions) { + if (get_dialog_default_permissions(dialog_id) == new_permissions) { return promise.set_value(Unit()); } @@ -31718,7 +32476,7 @@ tl_object_ptr MessagesManager::get_ch if (filters->invite_link_changes_) { flags |= telegram_api::channelAdminLogEventsFilter::INVITES_MASK; } - if (filters->voice_chat_changes_) { + if (filters->video_chat_changes_) { flags |= telegram_api::channelAdminLogEventsFilter::GROUP_CALL_MASK; } @@ -31791,6 +32549,21 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob return make_tl_object( invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); } + case telegram_api::channelAdminLogEventActionParticipantJoinByRequest::ID: { + auto action = move_tl_object_as(action_ptr); + DialogInviteLink invite_link(std::move(action->invite_)); + if (!invite_link.is_valid()) { + LOG(ERROR) << "Wrong invite link: " << invite_link; + return nullptr; + } + UserId approver_user_id(action->approved_by_); + if (!approver_user_id.is_valid()) { + return nullptr; + } + return make_tl_object( + td_->contacts_manager_->get_user_id_object(approver_user_id, "chatEventMemberJoinedByRequest"), + invite_link.get_chat_invite_link_object(td_->contacts_manager_.get())); + } case telegram_api::channelAdminLogEventActionParticipantLeave::ID: return make_tl_object(); case telegram_api::channelAdminLogEventActionParticipantInvite::ID: { @@ -32022,7 +32795,7 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob if (!input_group_call_id.is_valid()) { return nullptr; } - return make_tl_object( + return make_tl_object( td_->group_call_manager_->get_group_call_id(input_group_call_id, DialogId(channel_id)).get()); } case telegram_api::channelAdminLogEventActionDiscardGroupCall::ID: { @@ -32031,7 +32804,7 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob if (!input_group_call_id.is_valid()) { return nullptr; } - return make_tl_object( + return make_tl_object( td_->group_call_manager_->get_group_call_id(input_group_call_id, DialogId(channel_id)).get()); } case telegram_api::channelAdminLogEventActionParticipantMute::ID: { @@ -32040,8 +32813,8 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob if (!participant.is_valid()) { return nullptr; } - return make_tl_object( - get_message_sender_object(participant.dialog_id, "chatEventVoiceChatParticipantIsMutedToggled"), true); + return make_tl_object( + get_message_sender_object(participant.dialog_id, "chatEventVideoChatParticipantIsMutedToggled"), true); } case telegram_api::channelAdminLogEventActionParticipantUnmute::ID: { auto action = move_tl_object_as(action_ptr); @@ -32049,8 +32822,8 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob if (!participant.is_valid()) { return nullptr; } - return make_tl_object( - get_message_sender_object(participant.dialog_id, "chatEventVoiceChatParticipantIsMutedToggled"), false); + return make_tl_object( + get_message_sender_object(participant.dialog_id, "chatEventVideoChatParticipantIsMutedToggled"), false); } case telegram_api::channelAdminLogEventActionParticipantVolume::ID: { auto action = move_tl_object_as(action_ptr); @@ -32058,13 +32831,13 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob if (!participant.is_valid()) { return nullptr; } - return make_tl_object( - get_message_sender_object(participant.dialog_id, "chatEventVoiceChatParticipantVolumeLevelChanged"), + return make_tl_object( + get_message_sender_object(participant.dialog_id, "chatEventVideoChatParticipantVolumeLevelChanged"), participant.volume_level); } case telegram_api::channelAdminLogEventActionToggleGroupCallSetting::ID: { auto action = move_tl_object_as(action_ptr); - return make_tl_object(action->join_muted_); + return make_tl_object(action->join_muted_); } case telegram_api::channelAdminLogEventActionChangeHistoryTTL::ID: { auto action = move_tl_object_as(action_ptr); @@ -33964,6 +34737,9 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr // old_message->disable_notification = new_message->disable_notification; // need_send_update = true; } + if (old_message->disable_web_page_preview != new_message->disable_web_page_preview) { + old_message->disable_web_page_preview = new_message->disable_web_page_preview; + } if (!is_scheduled && update_message_contains_unread_mention(d, old_message, new_message->contains_unread_mention, "update_message")) { @@ -34491,6 +35267,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, on_dialog_updated(dialog_id, "pending update_dialog_group_call"); } } + fix_pending_join_requests(dialog_id, d->pending_join_request_count, d->pending_join_request_user_ids); if (!is_loaded_from_database) { CHECK(order == DEFAULT_ORDER); @@ -35553,6 +36330,9 @@ unique_ptr MessagesManager::parse_dialog(DialogId dialo if (d->draft_message != nullptr) { add_formatted_text_dependencies(dependencies, &d->draft_message->input_message_text.text); } + for (auto user_id : d->pending_join_request_user_ids) { + dependencies.user_ids.insert(user_id); + } if (!resolve_dependencies_force(td_, dependencies, source)) { send_get_dialog_query(dialog_id, Auto(), 0, source); } @@ -36556,7 +37336,7 @@ void MessagesManager::on_get_channel_difference( on_update_dialog_notify_settings(dialog_id, std::move(dialog->notify_settings_), "updates.channelDifferenceTooLong"); - bool is_marked_as_unread = (dialog->flags_ & telegram_api::dialog::UNREAD_MARK_MASK) != 0; + bool is_marked_as_unread = dialog->unread_mark_; if (is_marked_as_unread != d->is_marked_as_unread) { set_dialog_is_marked_as_unread(d, is_marked_as_unread); } @@ -37212,17 +37992,17 @@ void MessagesManager::on_binlog_events(vector &&events) { do_delete_message_log_event(log_event); break; } - case LogEvent::HandlerType::DeleteMessagesFromServer: { + case LogEvent::HandlerType::DeleteMessagesOnServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - DeleteMessagesFromServerLogEvent log_event; + DeleteMessagesOnServerLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); auto dialog_id = log_event.dialog_id_; - Dialog *d = get_dialog_force(dialog_id, "DeleteMessagesFromServerLogEvent"); + Dialog *d = get_dialog_force(dialog_id, "DeleteMessagesOnServerLogEvent"); if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; @@ -37230,20 +38010,20 @@ void MessagesManager::on_binlog_events(vector &&events) { d->deleted_message_ids.insert(log_event.message_ids_.begin(), log_event.message_ids_.end()); - delete_messages_from_server(dialog_id, std::move(log_event.message_ids_), log_event.revoke_, event.id_, Auto()); + delete_messages_on_server(dialog_id, std::move(log_event.message_ids_), log_event.revoke_, event.id_, Auto()); break; } - case LogEvent::HandlerType::DeleteScheduledMessagesFromServer: { + case LogEvent::HandlerType::DeleteScheduledMessagesOnServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - DeleteScheduledMessagesFromServerLogEvent log_event; + DeleteScheduledMessagesOnServerLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); auto dialog_id = log_event.dialog_id_; - Dialog *d = get_dialog_force(dialog_id, "DeleteScheduledMessagesFromServerLogEvent"); + Dialog *d = get_dialog_force(dialog_id, "DeleteScheduledMessagesOnServerLogEvent"); if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; @@ -37254,34 +38034,34 @@ void MessagesManager::on_binlog_events(vector &&events) { d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id()); } - delete_scheduled_messages_from_server(dialog_id, std::move(log_event.message_ids_), event.id_, Auto()); + delete_scheduled_messages_on_server(dialog_id, std::move(log_event.message_ids_), event.id_, Auto()); break; } - case LogEvent::HandlerType::DeleteDialogHistoryFromServer: { + case LogEvent::HandlerType::DeleteDialogHistoryOnServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - DeleteDialogHistoryFromServerLogEvent log_event; + DeleteDialogHistoryOnServerLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); auto dialog_id = log_event.dialog_id_; - Dialog *d = get_dialog_force(dialog_id, "DeleteDialogHistoryFromServerLogEvent"); + Dialog *d = get_dialog_force(dialog_id, "DeleteDialogHistoryOnServerLogEvent"); if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - delete_dialog_history_from_server(dialog_id, log_event.max_message_id_, log_event.remove_from_dialog_list_, - log_event.revoke_, true, event.id_, Auto()); + delete_dialog_history_on_server(dialog_id, log_event.max_message_id_, log_event.remove_from_dialog_list_, + log_event.revoke_, true, event.id_, Auto()); break; } - case LogEvent::HandlerType::DeleteAllCallMessagesFromServer: { - DeleteAllCallMessagesFromServerLogEvent log_event; + case LogEvent::HandlerType::DeleteAllCallMessagesOnServer: { + DeleteAllCallMessagesOnServerLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); - delete_all_call_messages_from_server(log_event.revoke_, event.id_, Auto()); + delete_all_call_messages_on_server(log_event.revoke_, event.id_, Auto()); break; } case LogEvent::HandlerType::BlockMessageSenderFromRepliesOnServer: { @@ -37319,6 +38099,26 @@ void MessagesManager::on_binlog_events(vector &&events) { delete_all_channel_messages_from_user_on_server(channel_id, user_id, event.id_, Auto()); break; } + case LogEvent::HandlerType::DeleteDialogMessagesByDateOnServer: { + if (!G()->parameters().use_message_db) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + DeleteDialogMessagesByDateOnServerLogEvent log_event; + log_event_parse(log_event, event.data_).ensure(); + + auto dialog_id = log_event.dialog_id_; + Dialog *d = get_dialog_force(dialog_id, "DeleteDialogMessagesByDateOnServerLogEvent"); + if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + delete_dialog_messages_by_date_on_server(dialog_id, log_event.min_date_, log_event.max_date_, log_event.revoke_, + event.id_, Auto()); + break; + } case LogEvent::HandlerType::ReadHistoryOnServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); @@ -37612,28 +38412,28 @@ void MessagesManager::on_binlog_events(vector &&events) { set_dialog_folder_id_on_server(dialog_id, true); break; } - case LogEvent::HandlerType::GetDialogFromServer: { + case LogEvent::HandlerType::RegetDialog: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - GetDialogFromServerLogEvent log_event; + RegetDialogLogEvent log_event; log_event_parse(log_event, event.data_).ensure(); auto dialog_id = log_event.dialog_id_; Dependencies dependencies; add_dialog_dependencies(dependencies, dialog_id); - resolve_dependencies_force(td_, dependencies, "GetDialogFromServerLogEvent"); + resolve_dependencies_force(td_, dependencies, "RegetDialogLogEvent"); - get_dialog_force(dialog_id, "GetDialogFromServerLogEvent"); // load it if exists + get_dialog_force(dialog_id, "RegetDialogLogEvent"); // load it if exists if (!have_input_peer(dialog_id, AccessRights::Read)) { binlog_erase(G()->td_db()->get_binlog(), event.id_); break; } - send_get_dialog_query(dialog_id, Auto(), event.id_, "GetDialogFromServerLogEvent"); + send_get_dialog_query(dialog_id, Auto(), event.id_, "RegetDialogLogEvent"); break; } case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer: { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index d82bff13e..b58e7dcc1 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -48,14 +48,13 @@ #include "td/telegram/ReportReason.h" #include "td/telegram/RestrictionReason.h" #include "td/telegram/ScheduledServerMessageId.h" +#include "td/telegram/secret_api.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/ServerMessageId.h" -#include "td/telegram/UserId.h" - -#include "td/telegram/secret_api.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" #include "td/actor/actor.h" #include "td/actor/MultiPromise.h" @@ -208,6 +207,13 @@ class MessagesManager final : public Actor { vector> &&peers); void on_failed_public_dialogs_search(const string &query, Status &&error); + void on_get_message_search_result_calendar(DialogId dialog_id, MessageId from_message_id, MessageSearchFilter filter, + int64 random_id, int32 total_count, + vector> &&messages, + vector> &&periods, + Promise &&promise); + void on_failed_get_message_search_result_calendar(DialogId dialog_id, int64 random_id); + void on_get_dialog_messages_search_result(DialogId dialog_id, const string &query, DialogId sender_dialog_id, MessageId from_message_id, int32 offset, int32 limit, MessageSearchFilter filter, MessageId top_thread_message_id, @@ -295,6 +301,9 @@ class MessagesManager final : public Actor { void on_update_dialog_theme_name(DialogId dialog_id, string theme_name); + void on_update_dialog_pending_join_requests(DialogId dialog_id, int32 pending_join_request_count, + vector pending_requesters); + void on_update_dialog_has_scheduled_server_messages(DialogId dialog_id, bool has_scheduled_server_messages); void on_update_dialog_folder_id(DialogId dialog_id, FolderId folder_id); @@ -365,6 +374,9 @@ class MessagesManager final : public Actor { void delete_dialog_messages_from_user(DialogId dialog_id, UserId user_id, Promise &&promise); + void delete_dialog_messages_by_date(DialogId dialog_id, int32 min_date, int32 max_date, bool revoke, + Promise &&promise); + void on_dialog_deleted(DialogId dialog_id, Promise &&promise); void on_update_dialog_group_call_rights(DialogId dialog_id); @@ -692,6 +704,10 @@ class MessagesManager final : public Actor { int32 limit, int64 &random_id, Promise &&promise); + td_api::object_ptr get_dialog_message_calendar(DialogId dialog_id, MessageId from_message_id, + MessageSearchFilter filter, int64 &random_id, + bool use_db, Promise &&promise); + std::pair> search_dialog_messages(DialogId dialog_id, const string &query, const td_api::object_ptr &sender, MessageId from_message_id, int32 offset, int32 limit, @@ -733,6 +749,15 @@ class MessagesManager final : public Actor { void on_get_dialog_message_by_date_fail(int64 random_id); + void get_dialog_sparse_message_positions(DialogId dialog_id, MessageSearchFilter filter, MessageId from_message_id, + int32 limit, + Promise> &&promise); + + void on_get_dialog_sparse_message_positions( + DialogId dialog_id, MessageSearchFilter filter, + telegram_api::object_ptr positions, + Promise> &&promise); + void get_dialog_message_count(DialogId dialog_id, MessageSearchFilter filter, bool return_local, Promise &&promise); @@ -779,6 +804,8 @@ class MessagesManager final : public Actor { void on_dialog_linked_channel_updated(DialogId dialog_id, ChannelId old_linked_channel_id, ChannelId new_linked_channel_id) const; + void drop_dialog_pending_join_requests(DialogId dialog_id); + void on_resolved_username(const string &username, DialogId dialog_id); void drop_username(const string &username); @@ -944,6 +971,7 @@ class MessagesManager final : public Actor { int32 date = 0; int32 ttl_period = 0; int32 ttl = 0; + bool disable_web_page_preview = false; int64 random_id = 0; tl_object_ptr forward_header; MessageId reply_to_message_id; @@ -1180,6 +1208,8 @@ class MessagesManager final : public Actor { InputGroupCallId expected_active_group_call_id; DialogId default_join_group_call_as_dialog_id; string theme_name; + int32 pending_join_request_count = 0; + vector pending_join_request_user_ids; FolderId folder_id; vector dialog_list_ids; // TODO replace with mask @@ -1628,21 +1658,21 @@ class MessagesManager final : public Actor { }; class BlockMessageSenderFromRepliesOnServerLogEvent; - class ToggleDialogReportSpamStateOnServerLogEvent; + class DeleteAllCallMessagesOnServerLogEvent; class DeleteAllChannelMessagesFromUserOnServerLogEvent; - class DeleteDialogHistoryFromServerLogEvent; - class DeleteAllCallMessagesFromServerLogEvent; + class DeleteDialogHistoryOnServerLogEvent; + class DeleteDialogMessagesByDateOnServerLogEvent; class DeleteMessageLogEvent; - class DeleteMessagesFromServerLogEvent; - class DeleteScheduledMessagesFromServerLogEvent; + class DeleteMessagesOnServerLogEvent; + class DeleteScheduledMessagesOnServerLogEvent; class ForwardMessagesLogEvent; class GetChannelDifferenceLogEvent; - class GetDialogFromServerLogEvent; class ReadAllDialogMentionsOnServerLogEvent; class ReadHistoryInSecretChatLogEvent; class ReadHistoryOnServerLogEvent; class ReadMessageContentsOnServerLogEvent; class ReadMessageThreadHistoryOnServerLogEvent; + class RegetDialogLogEvent; class ReorderPinnedDialogsOnServerLogEvent; class ResetAllNotificationSettingsOnServerLogEvent; class SaveDialogDraftMessageOnServerLogEvent; @@ -1654,6 +1684,7 @@ class MessagesManager final : public Actor { class ToggleDialogIsBlockedOnServerLogEvent; class ToggleDialogIsMarkedAsUnreadOnServerLogEvent; class ToggleDialogIsPinnedOnServerLogEvent; + class ToggleDialogReportSpamStateOnServerLogEvent; class UnpinAllDialogMessagesOnServerLogEvent; class UpdateDialogNotificationSettingsOnServerLogEvent; class UpdateScopeNotificationSettingsOnServerLogEvent; @@ -1975,18 +2006,18 @@ class MessagesManager final : public Actor { void do_delete_all_dialog_messages(Dialog *d, unique_ptr &message, bool is_permanently_deleted, vector &deleted_message_ids); - void delete_sent_message_from_server(DialogId dialog_id, MessageId message_id); + void delete_sent_message_on_server(DialogId dialog_id, MessageId message_id); - void delete_messages_from_server(DialogId dialog_id, vector message_ids, bool revoke, uint64 log_event_id, - Promise &&promise); + void delete_messages_on_server(DialogId dialog_id, vector message_ids, bool revoke, uint64 log_event_id, + Promise &&promise); - void delete_scheduled_messages_from_server(DialogId dialog_id, vector message_ids, uint64 log_event_id, - Promise &&promise); + void delete_scheduled_messages_on_server(DialogId dialog_id, vector message_ids, uint64 log_event_id, + Promise &&promise); - void delete_dialog_history_from_server(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, - bool revoke, bool allow_error, uint64 log_event_id, Promise &&promise); + void delete_dialog_history_on_server(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, + bool revoke, bool allow_error, uint64 log_event_id, Promise &&promise); - void delete_all_call_messages_from_server(bool revoke, uint64 log_event_id, Promise &&promise); + void delete_all_call_messages_on_server(bool revoke, uint64 log_event_id, Promise &&promise); void block_message_sender_from_replies_on_server(MessageId message_id, bool need_delete_message, bool need_delete_all_messages, bool report_spam, uint64 log_event_id, @@ -1995,12 +2026,17 @@ class MessagesManager final : public Actor { void delete_all_channel_messages_from_user_on_server(ChannelId channel_id, UserId user_id, uint64 log_event_id, Promise &&promise); + void delete_dialog_messages_by_date_on_server(DialogId dialog_id, int32 min_date, int32 max_date, bool revoke, + uint64 log_event_id, Promise &&promise); + void read_all_dialog_mentions_on_server(DialogId dialog_id, uint64 log_event_id, Promise &&promise); void unpin_all_dialog_messages_on_server(DialogId dialog_id, uint64 log_event_id, Promise &&promise); static MessageId find_message_by_date(const Message *m, int32 date); + static void find_messages_by_date(const Message *m, int32 min_date, int32 max_date, vector &message_ids); + static void find_messages(const Message *m, vector &message_ids, const std::function &condition); @@ -2339,7 +2375,9 @@ class MessagesManager final : public Actor { void send_update_chat_theme(const Dialog *d); - void send_update_chat_voice_chat(const Dialog *d); + void send_update_chat_pending_join_requests(const Dialog *d); + + void send_update_chat_video_chat(const Dialog *d); void send_update_chat_message_ttl_setting(const Dialog *d); @@ -2438,6 +2476,12 @@ class MessagesManager final : public Actor { void set_dialog_theme_name(Dialog *d, string theme_name); + void fix_pending_join_requests(DialogId dialog_id, int32 &pending_join_request_count, + vector &pending_join_request_user_ids) const; + + void set_dialog_pending_join_requests(Dialog *d, int32 pending_join_request_count, + vector pending_join_request_user_ids); + void repair_dialog_scheduled_messages(Dialog *d); void set_dialog_has_scheduled_server_messages(Dialog *d, bool has_scheduled_server_messages); @@ -2530,7 +2574,9 @@ class MessagesManager final : public Actor { string get_dialog_theme_name(const Dialog *d) const; - td_api::object_ptr get_voice_chat_object(const Dialog *d) const; + td_api::object_ptr get_chat_join_requests_info_object(const Dialog *d) const; + + td_api::object_ptr get_video_chat_object(const Dialog *d) const; td_api::object_ptr get_chat_object(const Dialog *d) const; @@ -2774,13 +2820,17 @@ class MessagesManager final : public Actor { static MessageId get_first_database_message_id_by_index(const Dialog *d, MessageSearchFilter filter); + void on_get_message_calendar_from_database(int64 random_id, DialogId dialog_id, MessageId from_message_id, + MessageId first_db_message_id, MessageSearchFilter filter, + Result r_calendar, Promise promise); + void on_search_dialog_messages_db_result(int64 random_id, DialogId dialog_id, MessageId from_message_id, MessageId first_db_message_id, MessageSearchFilter filter, int32 offset, int32 limit, Result> r_messages, - Promise<> promise); + Promise promise); void on_messages_db_fts_result(Result result, string offset, int32 limit, int64 random_id, - Promise<> &&promise); + Promise &&promise); void on_messages_db_calls_result(Result result, int64 random_id, MessageId first_db_message_id, MessageSearchFilter filter, Promise &&promise); @@ -2822,7 +2872,7 @@ class MessagesManager final : public Actor { string get_dialog_username(DialogId dialog_id) const; - RestrictedRights get_dialog_permissions(DialogId dialog_id) const; + RestrictedRights get_dialog_default_permissions(DialogId dialog_id) const; bool get_dialog_has_scheduled_messages(const Dialog *d) const; @@ -3044,16 +3094,16 @@ class MessagesManager final : public Actor { static uint64 save_toggle_dialog_report_spam_state_on_server_log_event(DialogId dialog_id, bool is_spam_dialog); - static uint64 save_delete_messages_from_server_log_event(DialogId dialog_id, const vector &message_ids, - bool revoke); + static uint64 save_delete_messages_on_server_log_event(DialogId dialog_id, const vector &message_ids, + bool revoke); - static uint64 save_delete_scheduled_messages_from_server_log_event(DialogId dialog_id, - const vector &message_ids); + static uint64 save_delete_scheduled_messages_on_server_log_event(DialogId dialog_id, + const vector &message_ids); - static uint64 save_delete_dialog_history_from_server_log_event(DialogId dialog_id, MessageId max_message_id, - bool remove_from_dialog_list, bool revoke); + static uint64 save_delete_dialog_history_on_server_log_event(DialogId dialog_id, MessageId max_message_id, + bool remove_from_dialog_list, bool revoke); - static uint64 save_delete_all_call_messages_from_server_log_event(bool revoke); + static uint64 save_delete_all_call_messages_on_server_log_event(bool revoke); static uint64 save_block_message_sender_from_replies_on_server_log_event(MessageId message_id, bool need_delete_message, @@ -3062,6 +3112,9 @@ class MessagesManager final : public Actor { static uint64 save_delete_all_channel_messages_from_user_on_server_log_event(ChannelId channel_id, UserId user_id); + static uint64 save_delete_dialog_messages_by_date_on_server_log_event(DialogId dialog_id, int32 min_date, + int32 max_date, bool revoke); + static uint64 save_read_all_dialog_mentions_on_server_log_event(DialogId dialog_id); static uint64 save_toggle_dialog_is_pinned_on_server_log_event(DialogId dialog_id, bool is_pinned); @@ -3080,7 +3133,7 @@ class MessagesManager final : public Actor { static uint64 save_reset_all_notification_settings_on_server_log_event(); - static uint64 save_get_dialog_from_server_log_event(DialogId dialog_id); + static uint64 save_reget_dialog_log_event(DialogId dialog_id); static uint64 save_forward_messages_log_event(DialogId to_dialog_id, DialogId from_dialog_id, const vector &messages, @@ -3289,6 +3342,7 @@ class MessagesManager final : public Actor { std::unordered_map get_dialog_message_by_date_results_; + std::unordered_map> found_dialog_message_calendars_; std::unordered_map>> found_dialog_messages_; // random_id -> [total_count, [message_id]...] std::unordered_map found_dialog_messages_dialog_id_; // random_id -> dialog_id diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index 2aad75c56..f69ef4ea8 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -2915,6 +2915,9 @@ string NotificationManager::convert_loc_key(const string &loc_key) { if (loc_key == "CHAT_ADD_YOU") { return "MESSAGE_CHAT_ADD_MEMBERS_YOU"; } + if (loc_key == "CHAT_REQ_JOINED") { + return "MESSAGE_CHAT_JOIN_BY_REQUEST"; + } break; } return string(); @@ -3276,13 +3279,10 @@ Status NotificationManager::process_push_notification_payload(string payload, bo } } - int32 flags = telegram_api::user::FIRST_NAME_MASK | telegram_api::user::MIN_MASK; + int32 flags = USER_FLAG_IS_INACCESSIBLE; if (sender_access_hash != -1) { // set phone number flag to show that this is a full access hash - flags |= telegram_api::user::ACCESS_HASH_MASK | telegram_api::user::PHONE_MASK; - } - if (sender_photo != nullptr) { - flags |= telegram_api::user::PHOTO_MASK; + flags |= USER_FLAG_HAS_ACCESS_HASH | USER_FLAG_HAS_PHONE_NUMBER; } auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name; auto user = telegram_api::make_object( @@ -3620,7 +3620,7 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess } if (sender_user_id.is_valid() && !td_->contacts_manager_->have_user_force(sender_user_id)) { - int32 flags = telegram_api::user::FIRST_NAME_MASK | telegram_api::user::MIN_MASK; + int32 flags = USER_FLAG_IS_INACCESSIBLE; auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name; auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index 12688e427..fba5235d9 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -150,6 +150,10 @@ class NotificationManager final : public Actor { static constexpr int32 ANNOUNCEMENT_ID_CACHE_TIME = 7 * 86400; + static constexpr int32 USER_FLAG_HAS_ACCESS_HASH = 1 << 0; + static constexpr int32 USER_FLAG_HAS_PHONE_NUMBER = 1 << 4; + static constexpr int32 USER_FLAG_IS_INACCESSIBLE = 1 << 20; + class AddMessagePushNotificationLogEvent; class EditMessagePushNotificationLogEvent; diff --git a/td/telegram/NotificationType.cpp b/td/telegram/NotificationType.cpp index a50f11fdc..459fcfd7b 100644 --- a/td/telegram/NotificationType.cpp +++ b/td/telegram/NotificationType.cpp @@ -213,6 +213,9 @@ class NotificationTypePushMessage final : public NotificationType { if (key == "MESSAGE_CHAT_JOIN_BY_LINK") { return td_api::make_object(); } + if (key == "MESSAGE_CHAT_JOIN_BY_REQUEST") { + return td_api::make_object(); + } if (key == "MESSAGE_CONTACT") { return td_api::make_object(arg, is_pinned); } diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index ee0dbbc47..1bcb1ceb0 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -778,9 +778,8 @@ void PasswordManager::do_get_state(Promise promise) { state.current_srp_B = password->srp_B_.as_slice().str(); state.current_srp_id = password->srp_id_; state.password_hint = std::move(password->hint_); - state.has_recovery_email_address = - (password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0; - state.has_secure_values = (password->flags_ & telegram_api::account_password::HAS_SECURE_VALUES_MASK) != 0; + state.has_recovery_email_address = password->has_recovery_; + state.has_secure_values = password->has_secure_values_; } else { state.has_password = false; send_closure(actor_id, &PasswordManager::drop_cached_secret); diff --git a/td/telegram/Payments.cpp b/td/telegram/Payments.cpp index 12b763529..ae61a3d1b 100644 --- a/td/telegram/Payments.cpp +++ b/td/telegram/Payments.cpp @@ -304,9 +304,8 @@ class GetPaymentFormQuery final : public Td::ResultHandler { LOG(ERROR) << "Receive invalid seller " << seller_bot_user_id; return on_error(id, Status::Error(500, "Receive invalid seller identifier")); } - bool can_save_credentials = - (payment_form->flags_ & telegram_api::payments_paymentForm::CAN_SAVE_CREDENTIALS_MASK) != 0; - bool need_password = (payment_form->flags_ & telegram_api::payments_paymentForm::PASSWORD_MISSING_MASK) != 0; + bool can_save_credentials = payment_form->can_save_credentials_; + bool need_password = payment_form->password_missing_; promise_.set_value(make_tl_object( payment_form->form_id_, convert_invoice(std::move(payment_form->invoice_)), std::move(payment_form->url_), td->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentForm seller"), @@ -649,9 +648,8 @@ InputInvoice get_input_invoice(tl_object_ptr result.photo = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id); result.start_parameter = std::move(message_invoice->start_param_); result.invoice.currency = std::move(message_invoice->currency_); - result.invoice.is_test = (message_invoice->flags_ & telegram_api::messageMediaInvoice::TEST_MASK) != 0; - result.invoice.need_shipping_address = - (message_invoice->flags_ & telegram_api::messageMediaInvoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0; + result.invoice.is_test = message_invoice->test_; + result.invoice.need_shipping_address = message_invoice->shipping_address_requested_; // result.payload = string(); // result.provider_token = string(); // result.provider_data = string(); @@ -674,9 +672,8 @@ InputInvoice get_input_invoice(tl_object_ptrfile_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id); // result.start_parameter = string(); result.invoice.currency = std::move(message_invoice->currency_); - result.invoice.is_test = (message_invoice->flags_ & telegram_api::messageMediaInvoice::TEST_MASK) != 0; - result.invoice.need_shipping_address = - (message_invoice->flags_ & telegram_api::messageMediaInvoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0; + result.invoice.is_test = message_invoice->test_; + result.invoice.need_shipping_address = message_invoice->shipping_address_requested_; // result.payload = string(); // result.provider_token = string(); // result.provider_data = string(); diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index d72cf5334..bf19db3f6 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -163,17 +163,17 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 auto profile_photo = move_tl_object_as(profile_photo_ptr); auto dc_id = DcId::create(profile_photo->dc_id_); - result.has_animation = (profile_photo->flags_ & telegram_api::userProfilePhoto::HAS_VIDEO_MASK) != 0; + result.has_animation = profile_photo->has_video_; result.id = profile_photo->photo_id_; if (!G()->shared_config().get_option_boolean("disable_minithumbnails")) { result.minithumbnail = profile_photo->stripped_thumb_.as_slice().str(); } - result.small_file_id = - register_photo(file_manager, {DialogId(user_id), user_access_hash, false}, result.id, 0 /*access_hash*/, - "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); - result.big_file_id = - register_photo(file_manager, {DialogId(user_id), user_access_hash, true}, result.id, 0 /*access_hash*/, - "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); + result.small_file_id = register_photo( + file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, false), result.id, + 0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); + result.big_file_id = register_photo( + file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, true), result.id, + 0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); break; } default: @@ -227,12 +227,14 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6 auto chat_photo = move_tl_object_as(chat_photo_ptr); auto dc_id = DcId::create(chat_photo->dc_id_); - result.has_animation = (chat_photo->flags_ & telegram_api::chatPhoto::HAS_VIDEO_MASK) != 0; + result.has_animation = chat_photo->has_video_; result.minithumbnail = chat_photo->stripped_thumb_.as_slice().str(); - result.small_file_id = register_photo(file_manager, {dialog_id, dialog_access_hash, false}, chat_photo->photo_id_, - 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); - result.big_file_id = register_photo(file_manager, {dialog_id, dialog_access_hash, true}, chat_photo->photo_id_, 0, - "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); + result.small_file_id = + register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, false), + chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); + result.big_file_id = + register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, true), + chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); break; } @@ -299,7 +301,7 @@ ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 u auto remote = file_view.remote_location(); CHECK(remote.is_photo()); CHECK(!remote.is_web()); - remote.set_source({DialogId(user_id), user_access_hash, is_big}); + remote.set_source(PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, is_big)); return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(), file_view.size(), file_view.expected_size(), file_view.remote_name()); }; @@ -341,7 +343,8 @@ PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice auto photo_id = -(Random::secure_int64() & std::numeric_limits::max()); res.file_id = file_manager->register_remote( - FullRemoteFileLocation(PhotoSizeSource(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id, string()), + FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id, + string()), FileLocationSource::FromServer, owner_dialog_id, res.size, 0, PSTRING() << static_cast(photo_id) << ".jpg"); file_manager->set_content(res.file_id, std::move(bytes)); @@ -717,7 +720,7 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && res.id = photo->id_; res.date = photo->date_; - res.has_stickers = (photo->flags_ & telegram_api::photo::HAS_STICKERS_MASK) != 0; + res.has_stickers = photo->has_stickers_; if (res.is_empty()) { LOG(ERROR) << "Receive photo with identifier " << res.id.get(); @@ -726,9 +729,9 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && DcId dc_id = DcId::create(photo->dc_id_); for (auto &size_ptr : photo->sizes_) { - auto photo_size = get_photo_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_, - photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id, - std::move(size_ptr), PhotoFormat::Jpeg); + auto photo_size = get_photo_size(file_manager, PhotoSizeSource::thumbnail(FileType::Photo, 0), photo->id_, + photo->access_hash_, photo->file_reference_.as_slice().str(), dc_id, + owner_dialog_id, std::move(size_ptr), PhotoFormat::Jpeg); if (photo_size.get_offset() == 0) { PhotoSize &size = photo_size.get<0>(); if (size.type == 0 || size.type == 't' || size.type == 'i' || size.type == 'u' || size.type == 'v') { @@ -746,9 +749,9 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr && } for (auto &size_ptr : photo->video_sizes_) { - auto animation = - get_animation_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_, - photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id, std::move(size_ptr)); + auto animation = get_animation_size(file_manager, PhotoSizeSource::thumbnail(FileType::Photo, 0), photo->id_, + photo->access_hash_, photo->file_reference_.as_slice().str(), dc_id, + owner_dialog_id, std::move(size_ptr)); if (animation.type != 0 && animation.dimensions.width == animation.dimensions.height) { res.animations.push_back(std::move(animation)); } @@ -996,12 +999,8 @@ tl_object_ptr convert_photo_to_profile_photo( if (!have_photo_small || !have_photo_big) { return nullptr; } - int32 flags = 0; - if (!photo->video_sizes_.empty()) { - flags |= telegram_api::userProfilePhoto::HAS_VIDEO_MASK; - } - return make_tl_object(flags, false /*ignored*/, photo->id_, BufferSlice(), - photo->dc_id_); + bool has_video = !photo->video_sizes_.empty(); + return make_tl_object(0, has_video, photo->id_, BufferSlice(), photo->dc_id_); } } // namespace td diff --git a/td/telegram/PhotoSizeSource.cpp b/td/telegram/PhotoSizeSource.cpp index a3923a2fe..e7b57908d 100644 --- a/td/telegram/PhotoSizeSource.cpp +++ b/td/telegram/PhotoSizeSource.cpp @@ -218,7 +218,7 @@ static bool operator==(const PhotoSizeSource::StickerSetThumbnailVersion &lhs, } bool operator==(const PhotoSizeSource &lhs, const PhotoSizeSource &rhs) { - return lhs.variant == rhs.variant; + return lhs.variant_ == rhs.variant_; } bool operator!=(const PhotoSizeSource &lhs, const PhotoSizeSource &rhs) { diff --git a/td/telegram/PhotoSizeSource.h b/td/telegram/PhotoSizeSource.h index c67f5c69e..731f5ae85 100644 --- a/td/telegram/PhotoSizeSource.h +++ b/td/telegram/PhotoSizeSource.h @@ -8,15 +8,12 @@ #include "td/telegram/DialogId.h" #include "td/telegram/files/FileType.h" - #include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/StringBuilder.h" #include "td/utils/Variant.h" -#include - namespace td { struct PhotoSizeSource { @@ -141,37 +138,47 @@ struct PhotoSizeSource { }; PhotoSizeSource() = default; - PhotoSizeSource(FileType file_type, int32 thumbnail_type) : variant(Thumbnail(file_type, thumbnail_type)) { + + static PhotoSizeSource thumbnail(FileType file_type, int32 thumbnail_type) { + return PhotoSizeSource(Thumbnail(file_type, thumbnail_type)); } - PhotoSizeSource(DialogId dialog_id, int64 dialog_access_hash, bool is_big) { + + static PhotoSizeSource dialog_photo(DialogId dialog_id, int64 dialog_access_hash, bool is_big) { if (is_big) { - variant = DialogPhotoBig(dialog_id, dialog_access_hash); + return PhotoSizeSource(DialogPhotoBig(dialog_id, dialog_access_hash)); } else { - variant = DialogPhotoSmall(dialog_id, dialog_access_hash); + return PhotoSizeSource(DialogPhotoSmall(dialog_id, dialog_access_hash)); } } - PhotoSizeSource(int64 sticker_set_id, int64 sticker_set_access_hash) - : variant(StickerSetThumbnail(sticker_set_id, sticker_set_access_hash)) { + + static PhotoSizeSource sticker_set_thumbnail(int64 sticker_set_id, int64 sticker_set_access_hash) { + return PhotoSizeSource(StickerSetThumbnail(sticker_set_id, sticker_set_access_hash)); } - PhotoSizeSource(std::nullptr_t, int64 volume_id, int32 local_id, int64 secret) - : variant(FullLegacy(volume_id, local_id, secret)) { + + static PhotoSizeSource full_legacy(int64 volume_id, int32 local_id, int64 secret) { + return PhotoSizeSource(FullLegacy(volume_id, local_id, secret)); } - PhotoSizeSource(DialogId dialog_id, int64 dialog_access_hash, bool is_big, int64 volume_id, int32 local_id) { + + static PhotoSizeSource dialog_photo_legacy(DialogId dialog_id, int64 dialog_access_hash, bool is_big, int64 volume_id, + int32 local_id) { if (is_big) { - variant = DialogPhotoBigLegacy(dialog_id, dialog_access_hash, volume_id, local_id); + return PhotoSizeSource(DialogPhotoBigLegacy(dialog_id, dialog_access_hash, volume_id, local_id)); } else { - variant = DialogPhotoSmallLegacy(dialog_id, dialog_access_hash, volume_id, local_id); + return PhotoSizeSource(DialogPhotoSmallLegacy(dialog_id, dialog_access_hash, volume_id, local_id)); } } - PhotoSizeSource(int64 sticker_set_id, int64 sticker_set_access_hash, int64 volume_id, int32 local_id) - : variant(StickerSetThumbnailLegacy(sticker_set_id, sticker_set_access_hash, volume_id, local_id)) { + + static PhotoSizeSource sticker_set_thumbnail_legacy(int64 sticker_set_id, int64 sticker_set_access_hash, + int64 volume_id, int32 local_id) { + return PhotoSizeSource(StickerSetThumbnailLegacy(sticker_set_id, sticker_set_access_hash, volume_id, local_id)); } - PhotoSizeSource(int64 sticker_set_id, int64 sticker_set_access_hash, int32 version) - : variant(StickerSetThumbnailVersion(sticker_set_id, sticker_set_access_hash, version)) { + + static PhotoSizeSource sticker_set_thumbnail(int64 sticker_set_id, int64 sticker_set_access_hash, int32 version) { + return PhotoSizeSource(StickerSetThumbnailVersion(sticker_set_id, sticker_set_access_hash, version)); } Type get_type() const { - auto offset = variant.get_offset(); + auto offset = variant_.get_offset(); CHECK(offset >= 0); return static_cast(offset); } @@ -179,58 +186,58 @@ struct PhotoSizeSource { FileType get_file_type() const; Thumbnail &thumbnail() { - return variant.get(); + return variant_.get(); } const Legacy &legacy() const { - return variant.get(); + return variant_.get(); } const Thumbnail &thumbnail() const { - return variant.get(); + return variant_.get(); } const DialogPhoto &dialog_photo() const { - switch (variant.get_offset()) { + switch (variant_.get_offset()) { case 2: - return variant.get(); + return variant_.get(); case 3: - return variant.get(); + return variant_.get(); case 6: - return variant.get(); + return variant_.get(); case 7: - return variant.get(); + return variant_.get(); default: UNREACHABLE(); - return variant.get(); + return variant_.get(); } } const StickerSetThumbnail &sticker_set_thumbnail() const { - switch (variant.get_offset()) { + switch (variant_.get_offset()) { case 4: - return variant.get(); + return variant_.get(); case 8: - return variant.get(); + return variant_.get(); case 9: - return variant.get(); + return variant_.get(); default: UNREACHABLE(); - return variant.get(); + return variant_.get(); } } const FullLegacy &full_legacy() const { - return variant.get(); + return variant_.get(); } const DialogPhotoLegacy &dialog_photo_legacy() const { - if (variant.get_offset() == 6) { - return variant.get(); + if (variant_.get_offset() == 6) { + return variant_.get(); } else { - return variant.get(); + return variant_.get(); } } const StickerSetThumbnailLegacy &sticker_set_thumbnail_legacy() const { - return variant.get(); + return variant_.get(); } const StickerSetThumbnailVersion &sticker_set_thumbnail_version() const { - return variant.get(); + return variant_.get(); } // returns unique representation of the source @@ -249,7 +256,11 @@ struct PhotoSizeSource { private: Variant - variant; + variant_; + + template + explicit PhotoSizeSource(const T &variant) : variant_(variant) { + } }; bool operator==(const PhotoSizeSource &lhs, const PhotoSizeSource &rhs); diff --git a/td/telegram/PhotoSizeSource.hpp b/td/telegram/PhotoSizeSource.hpp index 838a70439..c1ce48207 100644 --- a/td/telegram/PhotoSizeSource.hpp +++ b/td/telegram/PhotoSizeSource.hpp @@ -180,12 +180,12 @@ void parse(PhotoSizeSource::StickerSetThumbnailVersion &source, ParserT &parser) template void PhotoSizeSource::store(StorerT &storer) const { - td::store(variant, storer); + td::store(variant_, storer); } template void PhotoSizeSource::parse(ParserT &parser) { - td::parse(variant, parser); + td::parse(variant_, parser); } } // namespace td diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index 94a54c1ad..a4b1cf096 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -1491,7 +1491,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptrflags_ & telegram_api::pollResults::MIN_MASK) != 0; + bool is_min = poll_results->min_; bool has_total_voters = (poll_results->flags_ & telegram_api::pollResults::TOTAL_VOTERS_MASK) != 0; if (has_total_voters && poll_results->total_voters_ != poll->total_voter_count) { poll->total_voter_count = poll_results->total_voters_; @@ -1510,14 +1510,14 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptrflags_ & telegram_api::pollAnswerVoters::CHOSEN_MASK) != 0; + bool is_chosen = poll_result->chosen_; if (is_chosen != option.is_chosen) { option.is_chosen = is_chosen; is_changed = true; } } if (!is_min || poll_server_is_closed) { - bool is_correct = (poll_result->flags_ & telegram_api::pollAnswerVoters::CORRECT_MASK) != 0; + bool is_correct = poll_result->correct_; if (is_correct) { if (correct_option_id != -1) { LOG(ERROR) << "Receive more than 1 correct answers " << correct_option_id << " and " << option_index; diff --git a/td/telegram/PrivacyManager.cpp b/td/telegram/PrivacyManager.cpp index 60c426c15..6355fba9d 100644 --- a/td/telegram/PrivacyManager.cpp +++ b/td/telegram/PrivacyManager.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/PrivacyManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/ChannelId.h" #include "td/telegram/ChatId.h" #include "td/telegram/ContactsManager.h" diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 94628cfa2..3f89aab37 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -8,12 +8,10 @@ #include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryCreator.h" -#include "td/telegram/SecretChatId.h" -#include "td/telegram/ServerMessageId.h" -#include "td/telegram/UniqueId.h" - #include "td/telegram/secret_api.hpp" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/telegram_api.hpp" +#include "td/telegram/UniqueId.h" #include "td/mtproto/PacketInfo.h" #include "td/mtproto/PacketStorer.h" @@ -693,7 +691,7 @@ void SecretChatActor::do_close_chat_impl(bool delete_history, bool is_already_di context_->secret_chat_db()->erase_value(pfs_state_); context_->secret_chat_db()->erase_value(seq_no_state_); - MultiPromiseActorSafe mpas{"DeleteMessagesFromServerMultiPromiseActor"}; + MultiPromiseActorSafe mpas{"CloseSecretChatMultiPromiseActor"}; mpas.add_promise( PromiseCreator::lambda([actor_id = actor_id(this), log_event_id, promise = std::move(promise)](Unit) mutable { send_closure(actor_id, &SecretChatActor::on_closed, log_event_id, std::move(promise)); diff --git a/td/telegram/SecureManager.h b/td/telegram/SecureManager.h index b26766fd9..4919cfcec 100644 --- a/td/telegram/SecureManager.h +++ b/td/telegram/SecureManager.h @@ -9,10 +9,9 @@ #include "td/telegram/net/NetQuery.h" #include "td/telegram/SecureStorage.h" #include "td/telegram/SecureValue.h" -#include "td/telegram/UserId.h" - #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index ccc09867f..afba2e777 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -15,9 +15,6 @@ #include "td/telegram/misc.h" #include "td/telegram/net/DcId.h" #include "td/telegram/Payments.h" - -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.hpp" #include "td/utils/algorithm.h" @@ -245,10 +242,9 @@ SuitableSecureValue get_suitable_secure_value( const tl_object_ptr &secure_required_type) { SuitableSecureValue result; result.type = get_secure_value_type(secure_required_type->type_); - auto flags = secure_required_type->flags_; - result.is_selfie_required = (flags & telegram_api::secureRequiredType::SELFIE_REQUIRED_MASK) != 0; - result.is_translation_required = (flags & telegram_api::secureRequiredType::TRANSLATION_REQUIRED_MASK) != 0; - result.is_native_name_required = (flags & telegram_api::secureRequiredType::NATIVE_NAMES_MASK) != 0; + result.is_selfie_required = secure_required_type->selfie_required_; + result.is_translation_required = secure_required_type->translation_required_; + result.is_native_name_required = secure_required_type->native_names_; return result; } diff --git a/td/telegram/SecureValue.h b/td/telegram/SecureValue.h index ba8b38861..e177609e6 100644 --- a/td/telegram/SecureValue.h +++ b/td/telegram/SecureValue.h @@ -6,11 +6,10 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/SecureStorage.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/optional.h" diff --git a/td/telegram/SponsoredMessageManager.cpp b/td/telegram/SponsoredMessageManager.cpp index d4d4407bf..0fce4c90e 100644 --- a/td/telegram/SponsoredMessageManager.cpp +++ b/td/telegram/SponsoredMessageManager.cpp @@ -7,18 +7,21 @@ #include "td/telegram/SponsoredMessageManager.h" #include "td/telegram/ChannelId.h" +#include "td/telegram/ConfigShared.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" #include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/net/NetQueryCreator.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/logging.h" +#include "td/utils/SliceBuilder.h" #include "td/utils/Status.h" #include @@ -95,13 +98,16 @@ class ViewSponsoredMessageQuery final : public Td::ResultHandler { struct SponsoredMessageManager::SponsoredMessage { int32 local_id = 0; DialogId sponsor_dialog_id; + ServerMessageId server_message_id; string start_param; unique_ptr content; SponsoredMessage() = default; - SponsoredMessage(int32 local_id, DialogId sponsor_dialog_id, string start_param, unique_ptr content) + SponsoredMessage(int32 local_id, DialogId sponsor_dialog_id, ServerMessageId server_message_id, string start_param, + unique_ptr content) : local_id(local_id) , sponsor_dialog_id(sponsor_dialog_id) + , server_message_id(server_message_id) , start_param(std::move(start_param)) , content(std::move(content)) { } @@ -162,6 +168,13 @@ td_api::object_ptr SponsoredMessageManager::get_sponso link = td_api::make_object(bot_username, sponsored_message.start_param); break; } + case DialogType::Channel: { + auto channel_id = sponsored_message.sponsor_dialog_id.get_channel_id(); + auto t_me = G()->shared_config().get_option_string("t_me_url", "https://t.me/"); + link = td_api::make_object( + PSTRING() << t_me << "/c" << channel_id.get() << '/' << sponsored_message.server_message_id.get()); + break; + } default: break; } @@ -239,22 +252,30 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages( LOG(ERROR) << "Receive unknown sponsor " << sponsor_dialog_id; continue; } + auto server_message_id = ServerMessageId(sponsored_message->channel_post_); + if (!server_message_id.is_valid() && server_message_id != ServerMessageId()) { + LOG(ERROR) << "Receive invalid channel post in " << to_string(sponsored_message); + server_message_id = ServerMessageId(); + } td_->messages_manager_->force_create_dialog(sponsor_dialog_id, "on_get_dialog_sponsored_messages"); auto message_text = get_message_text(td_->contacts_manager_.get(), std::move(sponsored_message->message_), std::move(sponsored_message->entities_), true, true, 0, false, "on_get_dialog_sponsored_messages"); int32 ttl = 0; - auto content = get_message_content(td_, std::move(message_text), nullptr, sponsor_dialog_id, true, UserId(), &ttl); + bool disable_web_page_preview = false; + auto content = get_message_content(td_, std::move(message_text), nullptr, sponsor_dialog_id, true, UserId(), &ttl, + &disable_web_page_preview); if (ttl != 0) { LOG(ERROR) << "Receive sponsored message with TTL " << ttl; continue; } + CHECK(disable_web_page_preview); CHECK(current_sponsored_message_id_ < std::numeric_limits::max()); auto local_id = ++current_sponsored_message_id_; messages->message_random_ids[local_id] = sponsored_message->random_id_.as_slice().str(); - messages->messages.emplace_back(local_id, sponsor_dialog_id, std::move(sponsored_message->start_param_), - std::move(content)); + messages->messages.emplace_back(local_id, sponsor_dialog_id, server_message_id, + std::move(sponsored_message->start_param_), std::move(content)); } for (auto &promise : promises) { diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 219dccc38..718e69c51 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -25,6 +25,8 @@ #include "td/telegram/misc.h" #include "td/telegram/net/DcId.h" #include "td/telegram/net/MtprotoHeader.h" +#include "td/telegram/net/NetQueryDispatcher.h" +#include "td/telegram/PhotoSizeSource.h" #include "td/telegram/secret_api.h" #include "td/telegram/StickerSetId.hpp" #include "td/telegram/StickersManager.hpp" @@ -42,10 +44,12 @@ #include "td/actor/SleepActor.h" #include "td/utils/algorithm.h" +#include "td/utils/base64.h" #include "td/utils/emoji.h" #include "td/utils/format.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" +#include "td/utils/MimeType.h" #include "td/utils/misc.h" #include "td/utils/PathView.h" #include "td/utils/Random.h" @@ -1231,11 +1235,12 @@ class StickersManager::UploadStickerFileCallback final : public FileManager::Upl StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { upload_sticker_file_callback_ = std::make_shared(); + on_update_animated_emoji_zoom(); + on_update_recent_stickers_limit( narrow_cast(G()->shared_config().get_option_integer("recent_stickers_limit", 200))); on_update_favorite_stickers_limit( narrow_cast(G()->shared_config().get_option_integer("favorite_stickers_limit", 5))); - on_update_dice_emojis(); next_click_animated_emoji_message_time_ = Time::now(); next_update_animated_emoji_clicked_time_ = Time::now(); @@ -1280,6 +1285,13 @@ void StickersManager::init() { on_update_dice_success_values(); + on_update_emoji_sounds(); + + on_update_disable_animated_emojis(); + if (!disable_animated_emojis_) { + load_special_sticker_set(add_special_sticker_set(SpecialStickerSetType::animated_emoji())); + } + if (G()->parameters().use_file_db) { auto old_featured_sticker_set_count_str = G()->td_db()->get_binlog_pmc()->get("old_featured_sticker_set_count"); if (!old_featured_sticker_set_count_str.empty()) { @@ -1358,10 +1370,12 @@ void StickersManager::load_special_sticker_set_by_type(const SpecialStickerSetTy } void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) { + CHECK(!td_->auth_manager_->is_bot()); if (sticker_set.is_being_loaded_) { return; } sticker_set.is_being_loaded_ = true; + LOG(INFO) << "Load " << sticker_set.type_.type_ << " " << sticker_set.id_; if (sticker_set.id_.is_valid()) { auto promise = PromiseCreator::lambda([actor_id = actor_id(this), type = sticker_set.type_](Result &&result) { send_closure(actor_id, &StickersManager::on_load_special_sticker_set, type, @@ -1399,6 +1413,15 @@ void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &t special_sticker_set.is_being_loaded_ = false; + if (type.type_ == SpecialStickerSetType::animated_emoji()) { + auto promises = std::move(pending_get_animated_emoji_queries_); + reset_to_empty(pending_get_animated_emoji_queries_); + for (auto &promise : promises) { + promise.set_value(Unit()); + } + return; + } + CHECK(special_sticker_set.id_.is_valid()); auto sticker_set = get_sticker_set(special_sticker_set.id_); CHECK(sticker_set != nullptr); @@ -1459,7 +1482,7 @@ tl_object_ptr StickersManager::get_mask_point_object(int32 po } vector> StickersManager::get_sticker_minithumbnail( - CSlice path, StickerSetId sticker_set_id, int64 document_id) { + CSlice path, StickerSetId sticker_set_id, int64 document_id, double zoom) { if (path.empty()) { return {}; } @@ -1514,8 +1537,8 @@ vector> StickersManager::get_sticke } return sign * res; }; - auto make_point = [](double x, double y) { - return td_api::make_object(x, y); + auto make_point = [zoom](double x, double y) { + return td_api::make_object(x * zoom, y * zoom); }; vector> result; @@ -1696,7 +1719,8 @@ vector> StickersManager::get_sticke return result; } -tl_object_ptr StickersManager::get_sticker_object(FileId file_id) const { +tl_object_ptr StickersManager::get_sticker_object(FileId file_id, bool for_animated_emoji, + bool for_clicked_animated_emoji) const { if (!file_id.is_valid()) { return nullptr; } @@ -1732,11 +1756,18 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i } } auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format); + int32 width = sticker->dimensions.width; + int32 height = sticker->dimensions.height; + double zoom = 1.0; + if (sticker->is_animated && (for_animated_emoji || for_clicked_animated_emoji)) { + zoom = for_clicked_animated_emoji ? 3 * animated_emoji_zoom_ : animated_emoji_zoom_; + width = static_cast(width * zoom + 0.5); + height = static_cast(height * zoom + 0.5); + } return make_tl_object( - sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height, sticker->alt, sticker->is_animated, - sticker->is_mask, std::move(mask_position), - get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id, document_id), std::move(thumbnail_object), - td_->file_manager_->get_file_object(file_id)); + sticker->set_id.get(), width, height, sticker->alt, sticker->is_animated, sticker->is_mask, + std::move(mask_position), get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id, document_id, zoom), + std::move(thumbnail_object), td_->file_manager_->get_file_object(file_id)); } tl_object_ptr StickersManager::get_stickers_object(const vector &sticker_ids) const { @@ -1773,7 +1804,7 @@ tl_object_ptr StickersManager::get_dice_stickers_object(co } auto get_sticker = [&](int32 value) { - return get_sticker_object(sticker_set->sticker_ids[value]); + return get_sticker_object(sticker_set->sticker_ids[value], true); }; if (emoji == "🎰") { @@ -1840,7 +1871,7 @@ tl_object_ptr StickersManager::get_sticker_set_object(Sticke sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2), + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2, 1.0), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis)); } @@ -1886,13 +1917,127 @@ tl_object_ptr StickersManager::get_sticker_set_info_obje sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); return make_tl_object( sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail), - get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3), + get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3, 1.0), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, sticker_set->was_loaded ? narrow_cast(sticker_set->sticker_ids.size()) : sticker_set->sticker_count, std::move(stickers)); } +const StickersManager::StickerSet *StickersManager::get_animated_emoji_sticker_set() { + if (td_->auth_manager_->is_bot() || disable_animated_emojis_) { + return nullptr; + } + auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji()); + if (!special_sticker_set.id_.is_valid()) { + load_special_sticker_set(special_sticker_set); + return nullptr; + } + + auto sticker_set = get_sticker_set(special_sticker_set.id_); + CHECK(sticker_set != nullptr); + if (!sticker_set->was_loaded) { + load_special_sticker_set(special_sticker_set); + return nullptr; + } + + return sticker_set; +} + +std::pair StickersManager::get_animated_emoji_sticker(const StickerSet *sticker_set, const string &emoji) { + if (sticker_set == nullptr) { + return {}; + } + + auto emoji_without_modifiers = remove_emoji_modifiers(emoji).str(); + auto it = sticker_set->emoji_stickers_map_.find(emoji_without_modifiers); + if (it == sticker_set->emoji_stickers_map_.end()) { + return {}; + } + + // trying to find full emoji match + for (const auto &sticker_id : it->second) { + auto emoji_it = sticker_set->sticker_emojis_map_.find(sticker_id); + CHECK(emoji_it != sticker_set->sticker_emojis_map_.end()); + if (td::contains(emoji_it->second, emoji)) { + return {sticker_id, 0}; + } + } + + // trying to find match without Fitzpatrick modifiers + int modifier_id = get_fitzpatrick_modifier(emoji); + if (modifier_id > 0) { + for (const auto &sticker_id : it->second) { + auto emoji_it = sticker_set->sticker_emojis_map_.find(sticker_id); + CHECK(emoji_it != sticker_set->sticker_emojis_map_.end()); + if (td::contains(emoji_it->second, Slice(emoji).remove_suffix(4))) { + return {sticker_id, modifier_id}; + } + } + } + + // there is no match + return {}; +} + +std::pair StickersManager::get_animated_emoji_sticker(const string &emoji) { + return get_animated_emoji_sticker(get_animated_emoji_sticker_set(), emoji); +} + +FileId StickersManager::get_animated_emoji_sound_file_id(const string &emoji) const { + auto it = emoji_sounds_.find(remove_fitzpatrick_modifier(emoji).str()); + if (it == emoji_sounds_.end()) { + return {}; + } + return it->second; +} + +vector> StickersManager::get_color_replacements_object( + int fitzpatrick_modifier) { + vector> result; + switch (fitzpatrick_modifier) { + case 0: + break; + case 2: + case 3: + case 4: + case 5: + case 6: { + static int32 old_colors[] = {0xf77e41, 0xffb139, 0xffd140, 0xffdf79}; + static int32 new_colors[] = {0xcb7b55, 0xf6b689, 0xffcda7, 0xffdfc5, 0xa45a38, 0xdf986b, 0xedb183, + 0xf4c3a0, 0x703a17, 0xab673d, 0xc37f4e, 0xd89667, 0x4a2409, 0x7d3e0e, + 0x965529, 0xa96337, 0x200f0a, 0x412924, 0x593d37, 0x63453f}; + for (size_t i = 0; i < 4; i++) { + result.push_back(td_api::make_object(old_colors[i], + new_colors[(fitzpatrick_modifier - 2) * 4 + i])); + } + break; + } + default: + UNREACHABLE(); + } + return result; +} + +td_api::object_ptr StickersManager::get_animated_emoji_object(const string &emoji) { + auto it = emoji_messages_.find(emoji); + if (it == emoji_messages_.end()) { + return get_animated_emoji_object(get_animated_emoji_sticker(emoji), get_animated_emoji_sound_file_id(emoji)); + } else { + return get_animated_emoji_object(it->second.animated_emoji_sticker, it->second.sound_file_id); + } +} + +td_api::object_ptr StickersManager::get_animated_emoji_object( + std::pair animated_sticker, FileId sound_file_id) const { + if (!animated_sticker.first.is_valid()) { + return nullptr; + } + return td_api::make_object( + get_sticker_object(animated_sticker.first, true), get_color_replacements_object(animated_sticker.second), + sound_file_id.is_valid() ? td_->file_manager_->get_file_object(sound_file_id) : nullptr); +} + tl_object_ptr StickersManager::get_input_sticker_set(StickerSetId sticker_set_id) const { auto sticker_set = get_sticker_set(sticker_set_id); if (sticker_set == nullptr) { @@ -2021,9 +2166,9 @@ std::pair StickersManager::on_get_sticker_document( string minithumbnail; auto thumbnail_format = has_webp_thumbnail(document->thumbs_) ? PhotoFormat::Webp : PhotoFormat::Jpeg; for (auto &thumb : document->thumbs_) { - auto photo_size = get_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, document_id, - document->access_hash_, document->file_reference_.as_slice().str(), dc_id, - DialogId(), std::move(thumb), thumbnail_format); + auto photo_size = get_photo_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 0), + document_id, document->access_hash_, document->file_reference_.as_slice().str(), + dc_id, DialogId(), std::move(thumb), thumbnail_format); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); @@ -2547,17 +2692,19 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptraccess_hash_); bool is_installed = (set->flags_ & telegram_api::stickerSet::INSTALLED_DATE_MASK) != 0; - bool is_archived = (set->flags_ & telegram_api::stickerSet::ARCHIVED_MASK) != 0; - bool is_official = (set->flags_ & telegram_api::stickerSet::OFFICIAL_MASK) != 0; - bool is_animated = (set->flags_ & telegram_api::stickerSet::ANIMATED_MASK) != 0; - bool is_masks = (set->flags_ & telegram_api::stickerSet::MASKS_MASK) != 0; + bool is_archived = set->archived_; + bool is_official = set->official_; + bool is_animated = set->animated_; + bool is_masks = set->masks_; PhotoSize thumbnail; string minithumbnail; for (auto &thumb : set->thumbs_) { - auto photo_size = get_photo_size(td_->file_manager_.get(), {set_id.get(), s->access_hash, set->thumb_version_}, 0, - 0, "", DcId::create(set->thumb_dc_id_), DialogId(), std::move(thumb), - is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); + auto photo_size = + get_photo_size(td_->file_manager_.get(), + PhotoSizeSource::sticker_set_thumbnail(set_id.get(), s->access_hash, set->thumb_version_), 0, 0, + "", DcId::create(set->thumb_dc_id_), DialogId(), std::move(thumb), + is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp); if (photo_size.get_offset() == 0) { if (!thumbnail.file_id.is_valid()) { thumbnail = std::move(photo_size.get<0>()); @@ -2807,6 +2954,11 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s update_sticker_set(s); update_load_requests(s, true, Status::OK()); send_update_installed_sticker_sets(); + + if (set_id == add_special_sticker_set(SpecialStickerSetType::animated_emoji()).id_) { + try_update_animated_emoji_messages(); + } + return set_id; } @@ -2864,14 +3016,6 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty CHECK(s->is_inited); CHECK(s->is_loaded); - /* - if (type.type_ == SpecialStickerSetType::animated_emoji_click()) { - for (auto &sticker_id : s->sticker_ids) { - // TODO get supported emoji and their numbers - } - } - */ - LOG(INFO) << "Receive special sticker set " << type.type_ << ": " << sticker_set_id << ' ' << s->access_hash << ' ' << s->short_name; auto &sticker_set = add_special_sticker_set(type.type_); @@ -2890,6 +3034,7 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty << ' ' << sticker_set.short_name_); if (type.type_ == SpecialStickerSetType::animated_emoji()) { G()->shared_config().set_option_string(PSLICE() << type.type_ << "_name", sticker_set.short_name_); + try_update_animated_emoji_messages(); } else if (!type.get_dice_emoji().empty()) { sticker_set.is_being_loaded_ = true; } @@ -3959,6 +4104,66 @@ void StickersManager::on_update_dice_success_values() { }); } +void StickersManager::on_update_emoji_sounds() { + if (G()->close_flag() || !is_inited_ || td_->auth_manager_->is_bot()) { + return; + } + + auto emoji_sounds_str = G()->shared_config().get_option_string("emoji_sounds"); + if (emoji_sounds_str == emoji_sounds_str_) { + return; + } + + LOG(INFO) << "Change emoji sounds to " << emoji_sounds_str; + emoji_sounds_str_ = std::move(emoji_sounds_str); + + vector old_file_ids; + for (auto &emoji_sound : emoji_sounds_) { + old_file_ids.push_back(emoji_sound.second); + } + emoji_sounds_.clear(); + + vector new_file_ids; + auto sounds = full_split(Slice(emoji_sounds_str_), ','); + CHECK(sounds.size() % 2 == 0); + for (size_t i = 0; i < sounds.size(); i += 2) { + vector parts = full_split(sounds[i + 1], ':'); + CHECK(parts.size() == 3); + auto id = to_integer(parts[0]); + auto access_hash = to_integer(parts[1]); + auto dc_id = G()->net_query_dispatcher().get_main_dc_id(); + auto file_reference = base64url_decode(parts[2]).move_as_ok(); + int32 expected_size = 7000; + auto suggested_file_name = PSTRING() << static_cast(id) << '.' + << MimeType::to_extension("audio/ogg", "oga"); + auto file_id = td_->file_manager_->register_remote( + FullRemoteFileLocation(FileType::VoiceNote, id, access_hash, dc_id, std::move(file_reference)), + FileLocationSource::FromServer, DialogId(), 0, expected_size, std::move(suggested_file_name)); + CHECK(file_id.is_valid()); + emoji_sounds_.emplace(remove_fitzpatrick_modifier(sounds[i]).str(), file_id); + new_file_ids.push_back(file_id); + } + td_->file_manager_->change_files_source(get_app_config_file_source_id(), old_file_ids, new_file_ids); + + try_update_animated_emoji_messages(); +} + +void StickersManager::on_update_disable_animated_emojis() { + if (G()->close_flag() || !is_inited_ || td_->auth_manager_->is_bot()) { + return; + } + + auto disable_animated_emojis = G()->shared_config().get_option_boolean("disable_animated_emoji"); + if (disable_animated_emojis == disable_animated_emojis_) { + return; + } + disable_animated_emojis_ = disable_animated_emojis; + if (!disable_animated_emojis_) { + load_special_sticker_set(add_special_sticker_set(SpecialStickerSetType::animated_emoji())); + } + try_update_animated_emoji_messages(); +} + void StickersManager::on_update_sticker_sets() { // TODO better support archived_sticker_set_ids_[0].clear(); @@ -3970,6 +4175,26 @@ void StickersManager::on_update_sticker_sets() { reload_installed_sticker_sets(true, true); } +void StickersManager::try_update_animated_emoji_messages() { + auto sticker_set = get_animated_emoji_sticker_set(); + vector full_message_ids; + for (auto &it : emoji_messages_) { + auto new_animated_sticker = get_animated_emoji_sticker(sticker_set, it.first); + auto new_sound_file_id = get_animated_emoji_sound_file_id(it.first); + if (new_animated_sticker != it.second.animated_emoji_sticker || + (new_animated_sticker.first.is_valid() && new_sound_file_id != it.second.sound_file_id)) { + it.second.animated_emoji_sticker = new_animated_sticker; + it.second.sound_file_id = new_sound_file_id; + for (auto full_message_id : it.second.full_message_ids) { + full_message_ids.push_back(full_message_id); + } + } + } + for (auto full_message_id : full_message_ids) { + td_->messages_manager_->on_external_update_message_content(full_message_id); + } +} + void StickersManager::register_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source) { CHECK(!emoji.empty()); @@ -3985,8 +4210,7 @@ void StickersManager::register_dice(const string &emoji, int32 value, FullMessag if (!td::contains(dice_emojis_, emoji)) { if (full_message_id.get_message_id().is_any_server() && full_message_id.get_dialog_id().get_type() != DialogType::SecretChat) { - send_closure(G()->config_manager(), &ConfigManager::get_app_config, - Promise>()); + send_closure(G()->config_manager(), &ConfigManager::reget_app_config, Promise()); } return; } @@ -4028,8 +4252,74 @@ void StickersManager::unregister_dice(const string &emoji, int32 value, FullMess } } +void StickersManager::register_emoji(const string &emoji, FullMessageId full_message_id, const char *source) { + CHECK(!emoji.empty()); + if (td_->auth_manager_->is_bot()) { + return; + } + + LOG(INFO) << "Register emoji " << emoji << " from " << full_message_id << " from " << source; + auto &emoji_messages = emoji_messages_[emoji]; + if (emoji_messages.full_message_ids.empty()) { + emoji_messages.animated_emoji_sticker = get_animated_emoji_sticker(emoji); + emoji_messages.sound_file_id = get_animated_emoji_sound_file_id(emoji); + } + bool is_inserted = emoji_messages.full_message_ids.insert(full_message_id).second; + LOG_CHECK(is_inserted) << source << ' ' << emoji << ' ' << full_message_id; +} + +void StickersManager::unregister_emoji(const string &emoji, FullMessageId full_message_id, const char *source) { + CHECK(!emoji.empty()); + if (td_->auth_manager_->is_bot()) { + return; + } + + LOG(INFO) << "Unregister emoji " << emoji << " from " << full_message_id << " from " << source; + auto it = emoji_messages_.find(emoji); + CHECK(it != emoji_messages_.end()); + auto &full_message_ids = it->second.full_message_ids; + auto is_deleted = full_message_ids.erase(full_message_id) > 0; + LOG_CHECK(is_deleted) << source << ' ' << emoji << ' ' << full_message_id; + + if (full_message_ids.empty()) { + emoji_messages_.erase(it); + } +} + +void StickersManager::get_animated_emoji(string emoji, bool is_recursive, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji()); + auto sticker_set = get_sticker_set(special_sticker_set.id_); + if (sticker_set == nullptr || !sticker_set->was_loaded) { + if (is_recursive) { + return promise.set_value(nullptr); + } + + pending_get_animated_emoji_queries_.push_back( + PromiseCreator::lambda([actor_id = actor_id(this), emoji = std::move(emoji), + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &StickersManager::get_animated_emoji, std::move(emoji), true, std::move(promise)); + } + })); + load_special_sticker_set(special_sticker_set); + return; + } + + promise.set_value(get_animated_emoji_object(get_animated_emoji_sticker(sticker_set, emoji), + get_animated_emoji_sound_file_id(emoji))); +} + void StickersManager::get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id, Promise> &&promise) { + if (disable_animated_emojis_ || td_->auth_manager_->is_bot()) { + return promise.set_value(nullptr); + } + auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click()); if (!special_sticker_set.id_.is_valid()) { // don't wait for the first load of the sticker set from the server @@ -4064,7 +4354,7 @@ int StickersManager::get_emoji_number(Slice emoji) { return emoji[0] - '0'; } -vector StickersManager::get_animated_emoji_stickers(const StickerSet *sticker_set, Slice emoji) const { +vector StickersManager::get_animated_emoji_click_stickers(const StickerSet *sticker_set, Slice emoji) const { vector result; for (auto sticker_id : sticker_set->sticker_ids) { auto s = get_sticker(sticker_id); @@ -4076,7 +4366,7 @@ vector StickersManager::get_animated_emoji_stickers(const StickerSet *st if (result.empty()) { const static vector heart_emojis{"💛", "💙", "💚", "💜", "🧡", "🖤", "🤎", "🤍"}; if (td::contains(heart_emojis, emoji)) { - return get_animated_emoji_stickers(sticker_set, Slice("❤")); + return get_animated_emoji_click_stickers(sticker_set, Slice("❤")); } } return result; @@ -4091,13 +4381,17 @@ void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *stic return promise.set_error(Status::Error(400, "Message is not an animated emoji message")); } + if (disable_animated_emojis_ || td_->auth_manager_->is_bot()) { + return promise.set_value(nullptr); + } + auto now = Time::now(); if (last_clicked_animated_emoji_ == message_text && last_clicked_animated_emoji_full_message_id_ == full_message_id && next_click_animated_emoji_message_time_ >= now + 2 * MIN_ANIMATED_EMOJI_CLICK_DELAY) { return promise.set_value(nullptr); } - auto all_sticker_ids = get_animated_emoji_stickers(sticker_set, message_text); + auto all_sticker_ids = get_animated_emoji_click_stickers(sticker_set, message_text); vector> found_stickers; for (auto sticker_id : all_sticker_ids) { auto it = sticker_set->sticker_emojis_map_.find(sticker_id); @@ -4143,7 +4437,7 @@ void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *stic } if (now >= next_click_animated_emoji_message_time_) { next_click_animated_emoji_message_time_ = now + MIN_ANIMATED_EMOJI_CLICK_DELAY; - promise.set_value(get_sticker_object(result.second)); + promise.set_value(get_sticker_object(result.second, false, true)); } else { create_actor("SendClickAnimatedEmojiMessageResponse", next_click_animated_emoji_message_time_ - now, PromiseCreator::lambda([actor_id = actor_id(this), sticker_id = result.second, @@ -4159,7 +4453,7 @@ void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *stic void StickersManager::send_click_animated_emoji_message_response( FileId sticker_id, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); - promise.set_value(get_sticker_object(sticker_id)); + promise.set_value(get_sticker_object(sticker_id, false, true)); } void StickersManager::timeout_expired() { @@ -4247,6 +4541,10 @@ bool StickersManager::is_sent_animated_emoji_click(DialogId dialog_id, Slice emo } Status StickersManager::on_animated_emoji_message_clicked(Slice emoji, FullMessageId full_message_id, string data) { + if (td_->auth_manager_->is_bot() || disable_animated_emojis_) { + return Status::OK(); + } + TRY_RESULT(value, json_decode(data)); if (value.type() != JsonValue::Type::Object) { return Status::Error("Expected an object"); @@ -4327,7 +4625,7 @@ void StickersManager::schedule_update_animated_emoji_clicked(const StickerSet *s return; } - auto all_sticker_ids = get_animated_emoji_stickers(sticker_set, emoji); + auto all_sticker_ids = get_animated_emoji_click_stickers(sticker_set, emoji); std::unordered_map sticker_ids; for (auto sticker_id : all_sticker_ids) { auto it = sticker_set->sticker_emojis_map_.find(sticker_id); @@ -4361,7 +4659,7 @@ void StickersManager::schedule_update_animated_emoji_clicked(const StickerSet *s } void StickersManager::send_update_animated_emoji_clicked(FullMessageId full_message_id, FileId sticker_id) { - if (G()->close_flag()) { + if (G()->close_flag() || disable_animated_emojis_ || td_->auth_manager_->is_bot()) { return; } if (td_->messages_manager_->is_message_edited_recently(full_message_id, 2)) { @@ -4373,9 +4671,10 @@ void StickersManager::send_update_animated_emoji_clicked(FullMessageId full_mess return; } - send_closure(G()->td(), &Td::send_update, - td_api::make_object( - dialog_id.get(), full_message_id.get_message_id().get(), get_sticker_object(sticker_id))); + send_closure( + G()->td(), &Td::send_update, + td_api::make_object( + dialog_id.get(), full_message_id.get_message_id().get(), get_sticker_object(sticker_id, false, true))); } void StickersManager::view_featured_sticker_sets(const vector &sticker_set_ids) { @@ -6188,6 +6487,11 @@ void StickersManager::save_recent_stickers_to_database(bool is_attached) { } } +void StickersManager::on_update_animated_emoji_zoom() { + animated_emoji_zoom_ = + static_cast(G()->shared_config().get_option_integer("animated_emoji_zoom", 625000000)) * 1e-9; +} + void StickersManager::on_update_recent_stickers_limit(int32 recent_stickers_limit) { if (recent_stickers_limit != recent_stickers_limit_) { if (recent_stickers_limit > 0) { @@ -6378,6 +6682,13 @@ int64 StickersManager::get_favorite_stickers_hash() const { return get_recent_stickers_hash(favorite_sticker_ids_); } +FileSourceId StickersManager::get_app_config_file_source_id() { + if (!app_config_file_source_id_.is_valid()) { + app_config_file_source_id_ = td_->file_reference_manager_->create_app_config_file_source(); + } + return app_config_file_source_id_; +} + FileSourceId StickersManager::get_favorite_stickers_file_source_id() { if (!favorite_stickers_file_source_id_.is_valid()) { favorite_stickers_file_source_id_ = td_->file_reference_manager_->create_favorite_stickers_file_source(); diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index aa1940e77..3c0cedd97 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -51,7 +51,8 @@ class StickersManager final : public Actor { void init(); - tl_object_ptr get_sticker_object(FileId file_id) const; + tl_object_ptr get_sticker_object(FileId file_id, bool for_animated_emoji = false, + bool for_clicked_animated_emoji = false) const; tl_object_ptr get_stickers_object(const vector &sticker_ids) const; @@ -65,12 +66,21 @@ class StickersManager final : public Actor { const vector &sticker_set_ids, size_t covers_limit) const; + td_api::object_ptr get_animated_emoji_object(const string &emoji); + tl_object_ptr get_input_sticker_set(StickerSetId sticker_set_id) const; void register_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source); void unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source); + void register_emoji(const string &emoji, FullMessageId full_message_id, const char *source); + + void unregister_emoji(const string &emoji, FullMessageId full_message_id, const char *source); + + void get_animated_emoji(string emoji, bool is_recursive, + Promise> &&promise); + void get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id, Promise> &&promise); @@ -144,10 +154,16 @@ class StickersManager final : public Actor { void on_uninstall_sticker_set(StickerSetId set_id); + void on_update_animated_emoji_zoom(); + + void on_update_disable_animated_emojis(); + void on_update_dice_emojis(); void on_update_dice_success_values(); + void on_update_emoji_sounds(); + void on_update_sticker_sets(); void on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids); @@ -233,6 +249,8 @@ class StickersManager final : public Actor { void on_get_favorite_stickers_failed(bool is_repair, Status error); + FileSourceId get_app_config_file_source_id(); + FileSourceId get_favorite_stickers_file_source_id(); vector get_favorite_stickers(Promise &&promise); @@ -429,7 +447,7 @@ class StickersManager final : public Actor { static vector> get_sticker_minithumbnail(CSlice path, StickerSetId sticker_set_id, - int64 document_id); + int64 document_id, double zoom); static tl_object_ptr get_mask_point_object(int32 point); @@ -597,9 +615,24 @@ class StickersManager final : public Actor { bool update_sticker_set_cache(const StickerSet *sticker_set, Promise &promise); + static vector> get_color_replacements_object(int fitzpatrick_modifier); + + const StickerSet *get_animated_emoji_sticker_set(); + + static std::pair get_animated_emoji_sticker(const StickerSet *sticker_set, const string &emoji); + + std::pair get_animated_emoji_sticker(const string &emoji); + + FileId get_animated_emoji_sound_file_id(const string &emoji) const; + + td_api::object_ptr get_animated_emoji_object(std::pair animated_sticker, + FileId sound_file_id) const; + + void try_update_animated_emoji_messages(); + static int get_emoji_number(Slice emoji); - vector get_animated_emoji_stickers(const StickerSet *sticker_set, Slice emoji) const; + vector get_animated_emoji_click_stickers(const StickerSet *sticker_set, Slice emoji) const; void choose_animated_emoji_click_sticker(const StickerSet *sticker_set, Slice message_text, FullMessageId full_message_id, double start_time, @@ -727,6 +760,8 @@ class StickersManager final : public Actor { vector favorite_sticker_file_ids_; FileSourceId favorite_stickers_file_source_id_; + FileSourceId app_config_file_source_id_; + vector archived_sticker_set_ids_[2]; int32 total_archived_sticker_set_count_[2] = {-1, -1}; @@ -768,6 +803,8 @@ class StickersManager final : public Actor { std::unordered_map> pending_set_sticker_set_thumbnails_; + vector> pending_get_animated_emoji_queries_; + double next_click_animated_emoji_message_time_ = 0; double next_update_animated_emoji_clicked_time_ = 0; vector pending_get_animated_emoji_click_stickers_; @@ -798,11 +835,25 @@ class StickersManager final : public Actor { std::unordered_map> dice_messages_; + struct EmojiMessages { + std::unordered_set full_message_ids; + std::pair animated_emoji_sticker; + FileId sound_file_id; + }; + std::unordered_map emoji_messages_; + string dice_emojis_str_; vector dice_emojis_; string dice_success_values_str_; vector> dice_success_values_; + + string emoji_sounds_str_; + std::unordered_map emoji_sounds_; + + double animated_emoji_zoom_ = 0.0; + + bool disable_animated_emojis_ = false; }; } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index dd84a1152..43e06b1a6 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -131,6 +131,7 @@ #include "td/utils/MimeType.h" #include "td/utils/misc.h" #include "td/utils/PathView.h" +#include "td/utils/port/Clocks.h" #include "td/utils/port/IPAddress.h" #include "td/utils/port/path.h" #include "td/utils/port/SocketFd.h" @@ -1350,6 +1351,35 @@ class GetMessageThreadHistoryRequest final : public RequestActor<> { } }; +class GetChatMessageCalendarRequest final : public RequestActor<> { + DialogId dialog_id_; + MessageId from_message_id_; + MessageSearchFilter filter_; + int64 random_id_; + + td_api::object_ptr calendar_; + + void do_run(Promise &&promise) final { + calendar_ = td->messages_manager_->get_dialog_message_calendar(dialog_id_, from_message_id_, filter_, random_id_, + get_tries() == 3, std::move(promise)); + } + + void do_send_result() final { + send_result(std::move(calendar_)); + } + + public: + GetChatMessageCalendarRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 from_message_id, + tl_object_ptr filter) + : RequestActor(std::move(td), request_id) + , dialog_id_(dialog_id) + , from_message_id_(from_message_id) + , filter_(get_message_search_filter(filter)) + , random_id_(0) { + set_tries(3); + } +}; + class SearchChatMessagesRequest final : public RequestActor<> { DialogId dialog_id_; string query_; @@ -2874,7 +2904,7 @@ void Td::on_get_promo_data(Result(promo_data_ptr); expires_at = promo->expires_; - bool is_proxy = (promo->flags_ & telegram_api::help_promoData::PROXY_MASK) != 0; + bool is_proxy = promo->proxy_; messages_manager_->on_get_sponsored_dialog( std::move(promo->peer_), is_proxy ? DialogSource::mtproto_proxy() @@ -3267,7 +3297,8 @@ void Td::on_result(NetQueryPtr query) { bool Td::is_internal_config_option(Slice name) { switch (name[0]) { case 'a': - return name == "animation_search_emojis" || name == "animation_search_provider" || name == "auth"; + return name == "animated_emoji_zoom" || name == "animation_search_emojis" || + name == "animation_search_provider" || name == "auth"; case 'b': return name == "base_language_pack_version"; case 'c': @@ -3277,7 +3308,7 @@ bool Td::is_internal_config_option(Slice name) { case 'd': return name == "dc_txt_domain_name" || name == "dice_emojis" || name == "dice_success_values"; case 'e': - return name == "edit_time_limit"; + return name == "edit_time_limit" || name == "emoji_sounds"; case 'i': return name == "ignored_restriction_reasons"; case 'l': @@ -3317,12 +3348,17 @@ void Td::on_config_option_updated(const string &name) { return animations_manager_->on_update_animation_search_emojis(G()->shared_config().get_option_string(name)); } else if (name == "animation_search_provider") { return animations_manager_->on_update_animation_search_provider(G()->shared_config().get_option_string(name)); + } else if (name == "animated_emoji_zoom") { + // update animated emoji zoom only at launch + return; } else if (name == "recent_stickers_limit") { return stickers_manager_->on_update_recent_stickers_limit( narrow_cast(G()->shared_config().get_option_integer(name))); } else if (name == "favorite_stickers_limit") { stickers_manager_->on_update_favorite_stickers_limit( narrow_cast(G()->shared_config().get_option_integer(name))); + } else if (name == "disable_animated_emoji") { + stickers_manager_->on_update_disable_animated_emojis(); } else if (name == "my_id") { G()->set_my_id(G()->shared_config().get_option_integer(name)); } else if (name == "session_count") { @@ -3361,6 +3397,10 @@ void Td::on_config_option_updated(const string &name) { return send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, false, -1); } else if (name == "base_language_pack_version") { return send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, true, -1); + } else if (name == "utc_time_offset") { + if (G()->mtproto_header().set_tz_offset(static_cast(G()->shared_config().get_option_integer(name)))) { + G()->net_query_dispatcher().update_mtproto_header(); + } } else if (name == "notification_group_count_max") { send_closure(notification_manager_actor_, &NotificationManager::on_notification_group_count_max_changed, true); } else if (name == "notification_group_size_max") { @@ -3377,6 +3417,8 @@ void Td::on_config_option_updated(const string &name) { return send_closure(stickers_manager_actor_, &StickersManager::on_update_dice_emojis); } else if (name == "dice_success_values") { return send_closure(stickers_manager_actor_, &StickersManager::on_update_dice_success_values); + } else if (name == "emoji_sounds") { + return send_closure(stickers_manager_actor_, &StickersManager::on_update_emoji_sounds); } else if (is_internal_config_option(name)) { return; } @@ -3854,6 +3896,7 @@ Status Td::init(DbKey key) { options_.language_pack = G()->shared_config().get_option_string("localization_target"); options_.language_code = G()->shared_config().get_option_string("language_pack_id"); options_.parameters = G()->shared_config().get_option_string("connection_parameters"); + options_.tz_offset = static_cast(G()->shared_config().get_option_integer("utc_time_offset")); options_.is_emulator = G()->shared_config().get_option_boolean("is_emulator"); // options_.proxy = Proxy(); G()->set_mtproto_header(make_unique(options_)); @@ -4010,6 +4053,7 @@ void Td::init_options_and_network() { if (!G()->shared_config().have_option("suggested_video_note_audio_bitrate")) { G()->shared_config().set_option_integer("suggested_video_note_audio_bitrate", 64); } + G()->shared_config().set_option_integer("utc_time_offset", Clocks::tz_offset()); init_connection_creator(); @@ -4453,9 +4497,9 @@ Status Td::set_parameters(td_api::object_ptr parameters options_.application_version += ", TDLib "; options_.application_version += TDLIB_VERSION; } - options_.language_pack = ""; - options_.language_code = ""; - options_.parameters = ""; + options_.language_pack = string(); + options_.language_code = string(); + options_.parameters = string(); options_.is_emulator = false; options_.proxy = Proxy(); @@ -5354,6 +5398,11 @@ void Td::on_request(uint64 id, const td_api::getMessageThreadHistory &request) { request.offset_, request.limit_); } +void Td::on_request(uint64 id, td_api::getChatMessageCalendar &request) { + CHECK_IS_USER(); + CREATE_REQUEST(GetChatMessageCalendarRequest, request.chat_id_, request.from_message_id_, std::move(request.filter_)); +} + void Td::on_request(uint64 id, td_api::searchChatMessages &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.query_); @@ -5409,7 +5458,15 @@ void Td::on_request(uint64 id, const td_api::getChatMessageByDate &request) { CREATE_REQUEST(GetChatMessageByDateRequest, request.chat_id_, request.date_); } -void Td::on_request(uint64 id, td_api::getChatMessageCount &request) { +void Td::on_request(uint64 id, const td_api::getChatSparseMessagePositions &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_dialog_sparse_message_positions( + DialogId(request.chat_id_), get_message_search_filter(request.filter_), MessageId(request.from_message_id_), + request.limit_, std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::getChatMessageCount &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { @@ -5465,6 +5522,13 @@ void Td::on_request(uint64 id, const td_api::deleteChatMessagesFromUser &request std::move(promise)); } +void Td::on_request(uint64 id, const td_api::deleteChatMessagesByDate &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->delete_dialog_messages_by_date(DialogId(request.chat_id_), request.min_date_, request.max_date_, + request.revoke_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::readAllChatMentions &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); @@ -5806,13 +5870,13 @@ void Td::on_request(uint64 id, td_api::sendCallDebugInformation &request) { std::move(request.debug_information_), std::move(promise)); } -void Td::on_request(uint64 id, const td_api::getVoiceChatAvailableParticipants &request) { +void Td::on_request(uint64 id, const td_api::getVideoChatAvailableParticipants &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); group_call_manager_->get_group_call_join_as(DialogId(request.chat_id_), std::move(promise)); } -void Td::on_request(uint64 id, const td_api::setVoiceChatDefaultParticipant &request) { +void Td::on_request(uint64 id, const td_api::setVideoChatDefaultParticipant &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); group_call_manager_->set_group_call_default_join_as( @@ -5820,7 +5884,7 @@ void Td::on_request(uint64 id, const td_api::setVoiceChatDefaultParticipant &req std::move(promise)); } -void Td::on_request(uint64 id, td_api::createVoiceChat &request) { +void Td::on_request(uint64 id, td_api::createVideoChat &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.title_); CREATE_REQUEST_PROMISE(); @@ -6322,20 +6386,25 @@ void Td::on_request(uint64 id, td_api::getChatAdministrators &request) { void Td::on_request(uint64 id, const td_api::replacePrimaryChatInviteLink &request) { CREATE_REQUEST_PROMISE(); - contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), 0, 0, true, std::move(promise)); + contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), string(), 0, 0, false, true, + std::move(promise)); } -void Td::on_request(uint64 id, const td_api::createChatInviteLink &request) { +void Td::on_request(uint64 id, td_api::createChatInviteLink &request) { + CLEAN_INPUT_STRING(request.name_); CREATE_REQUEST_PROMISE(); - contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), request.expire_date_, request.member_limit_, - false, std::move(promise)); + contacts_manager_->export_dialog_invite_link(DialogId(request.chat_id_), std::move(request.name_), + request.expire_date_, request.member_limit_, + request.creates_join_request_, false, std::move(promise)); } void Td::on_request(uint64 id, td_api::editChatInviteLink &request) { + CLEAN_INPUT_STRING(request.name_); CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); - contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, request.expire_date_, - request.member_limit_, std::move(promise)); + contacts_manager_->edit_dialog_invite_link(DialogId(request.chat_id_), request.invite_link_, std::move(request.name_), + request.expire_date_, request.member_limit_, request.creates_join_request_, + std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatInviteLink &request) { @@ -6369,6 +6438,27 @@ void Td::on_request(uint64 id, td_api::getChatInviteLinkMembers &request) { std::move(promise)); } +void Td::on_request(uint64 id, td_api::getChatJoinRequests &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.invite_link_); + CLEAN_INPUT_STRING(request.query_); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_dialog_join_requests(DialogId(request.chat_id_), request.invite_link_, request.query_, + std::move(request.offset_request_), request.limit_, std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::approveChatJoinRequest &request) { + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->process_dialog_join_requests(DialogId(request.chat_id_), UserId(request.user_id_), true, + std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::declineChatJoinRequest &request) { + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->process_dialog_join_requests(DialogId(request.chat_id_), UserId(request.user_id_), false, + std::move(promise)); +} + void Td::on_request(uint64 id, td_api::revokeChatInviteLink &request) { CLEAN_INPUT_STRING(request.invite_link_); CREATE_REQUEST_PROMISE(); @@ -6999,6 +7089,13 @@ void Td::on_request(uint64 id, td_api::searchEmojis &request) { std::move(request.input_language_codes_)); } +void Td::on_request(uint64 id, td_api::getAnimatedEmoji &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.emoji_); + CREATE_REQUEST_PROMISE(); + stickers_manager_->get_animated_emoji(std::move(request.emoji_), false, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::getEmojiSuggestionsUrl &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.language_code_); @@ -7246,6 +7343,14 @@ void Td::on_request(uint64 id, td_api::getOption &request) { send_closure_later(config_manager_, &ConfigManager::get_content_settings, std::move(promise)); return; } + if (!is_bot && request.name_ == "is_location_visible") { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result &&result) { + // the option is already updated on success, ignore errors + send_closure(actor_id, &Td::send_result, id, G()->shared_config().get_option_value("is_location_visible")); + }); + send_closure_later(contacts_manager_actor_, &ContactsManager::get_is_location_visible, std::move(promise)); + return; + } break; case 'o': if (request.name_ == "online") { @@ -7379,6 +7484,9 @@ void Td::on_request(uint64 id, td_api::setOption &request) { } break; case 'd': + if (!is_bot && set_boolean_option("disable_animated_emoji")) { + return; + } if (!is_bot && set_boolean_option("disable_contact_registered_notifications")) { return; } @@ -7560,6 +7668,20 @@ void Td::on_request(uint64 id, td_api::setOption &request) { return; } break; + case 'u': + if (set_boolean_option("use_pfs")) { + return; + } + if (set_boolean_option("use_quick_ack")) { + return; + } + if (set_boolean_option("use_storage_optimizer")) { + return; + } + if (set_integer_option("utc_time_offset", -12 * 60 * 60, 14 * 60 * 60)) { + return; + } + break; case 'X': case 'x': { if (request.name_.size() > 255) { @@ -7586,17 +7708,6 @@ void Td::on_request(uint64 id, td_api::setOption &request) { } return send_closure(actor_id(this), &Td::send_result, id, make_tl_object()); } - case 'u': - if (set_boolean_option("use_pfs")) { - return; - } - if (set_boolean_option("use_quick_ack")) { - return; - } - if (set_boolean_option("use_storage_optimizer")) { - return; - } - break; } return send_error_raw(id, 400, "Option can't be set"); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 60d45c2a6..69a6ea94f 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -253,7 +253,7 @@ class Td final : public Actor { static td_api::object_ptr static_request(td_api::object_ptr function); private: - static constexpr const char *TDLIB_VERSION = "1.7.8"; + static constexpr const char *TDLIB_VERSION = "1.7.9"; static constexpr int64 ONLINE_ALARM_ID = 0; static constexpr int64 PING_SERVER_ALARM_ID = -1; static constexpr int32 PING_SERVER_TIMEOUT = 300; @@ -623,6 +623,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::getMessageThreadHistory &request); + void on_request(uint64 id, td_api::getChatMessageCalendar &request); + void on_request(uint64 id, td_api::searchChatMessages &request); void on_request(uint64 id, td_api::searchSecretMessages &request); @@ -639,7 +641,9 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::getChatMessageByDate &request); - void on_request(uint64 id, td_api::getChatMessageCount &request); + void on_request(uint64 id, const td_api::getChatSparseMessagePositions &request); + + void on_request(uint64 id, const td_api::getChatMessageCount &request); void on_request(uint64 id, const td_api::getChatScheduledMessages &request); @@ -653,6 +657,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::deleteChatMessagesFromUser &request); + void on_request(uint64 id, const td_api::deleteChatMessagesByDate &request); + void on_request(uint64 id, const td_api::readAllChatMentions &request); void on_request(uint64 id, td_api::sendMessage &request); @@ -735,11 +741,11 @@ class Td final : public Actor { void on_request(uint64 id, td_api::sendCallDebugInformation &request); - void on_request(uint64 id, const td_api::getVoiceChatAvailableParticipants &request); + void on_request(uint64 id, const td_api::getVideoChatAvailableParticipants &request); - void on_request(uint64 id, const td_api::setVoiceChatDefaultParticipant &request); + void on_request(uint64 id, const td_api::setVideoChatDefaultParticipant &request); - void on_request(uint64 id, td_api::createVoiceChat &request); + void on_request(uint64 id, td_api::createVideoChat &request); void on_request(uint64 id, const td_api::getGroupCall &request); @@ -869,7 +875,7 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::replacePrimaryChatInviteLink &request); - void on_request(uint64 id, const td_api::createChatInviteLink &request); + void on_request(uint64 id, td_api::createChatInviteLink &request); void on_request(uint64 id, td_api::editChatInviteLink &request); @@ -881,6 +887,12 @@ class Td final : public Actor { void on_request(uint64 id, td_api::getChatInviteLinkMembers &request); + void on_request(uint64 id, td_api::getChatJoinRequests &request); + + void on_request(uint64 id, const td_api::approveChatJoinRequest &request); + + void on_request(uint64 id, const td_api::declineChatJoinRequest &request); + void on_request(uint64 id, td_api::revokeChatInviteLink &request); void on_request(uint64 id, td_api::deleteRevokedChatInviteLink &request); @@ -1043,6 +1055,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::searchEmojis &request); + void on_request(uint64 id, td_api::getAnimatedEmoji &request); + void on_request(uint64 id, td_api::getEmojiSuggestionsUrl &request); void on_request(uint64 id, const td_api::getFavoriteStickers &request); diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index 6449ba5e8..56c09e075 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -91,14 +91,14 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &binlog_p break; case LogEvent::HandlerType::SendMessage: case LogEvent::HandlerType::DeleteMessage: - case LogEvent::HandlerType::DeleteMessagesFromServer: + case LogEvent::HandlerType::DeleteMessagesOnServer: case LogEvent::HandlerType::ReadHistoryOnServer: case LogEvent::HandlerType::ReadMessageContentsOnServer: case LogEvent::HandlerType::ForwardMessages: case LogEvent::HandlerType::SendBotStartMessage: case LogEvent::HandlerType::SendScreenshotTakenNotificationMessage: case LogEvent::HandlerType::SendInlineQueryResultMessage: - case LogEvent::HandlerType::DeleteDialogHistoryFromServer: + case LogEvent::HandlerType::DeleteDialogHistoryOnServer: case LogEvent::HandlerType::ReadAllDialogMentionsOnServer: case LogEvent::HandlerType::DeleteAllChannelMessagesFromUserOnServer: case LogEvent::HandlerType::ToggleDialogIsPinnedOnServer: @@ -108,17 +108,18 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &binlog_p case LogEvent::HandlerType::UpdateScopeNotificationSettingsOnServer: case LogEvent::HandlerType::ResetAllNotificationSettingsOnServer: case LogEvent::HandlerType::ToggleDialogReportSpamStateOnServer: - case LogEvent::HandlerType::GetDialogFromServer: + case LogEvent::HandlerType::RegetDialog: case LogEvent::HandlerType::GetChannelDifference: case LogEvent::HandlerType::ReadHistoryInSecretChat: case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer: case LogEvent::HandlerType::SetDialogFolderIdOnServer: - case LogEvent::HandlerType::DeleteScheduledMessagesFromServer: + case LogEvent::HandlerType::DeleteScheduledMessagesOnServer: case LogEvent::HandlerType::ToggleDialogIsBlockedOnServer: case LogEvent::HandlerType::ReadMessageThreadHistoryOnServer: case LogEvent::HandlerType::BlockMessageSenderFromRepliesOnServer: case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer: - case LogEvent::HandlerType::DeleteAllCallMessagesFromServer: + case LogEvent::HandlerType::DeleteAllCallMessagesOnServer: + case LogEvent::HandlerType::DeleteDialogMessagesByDateOnServer: events.to_messages_manager.push_back(event.clone()); break; case LogEvent::HandlerType::AddMessagePushNotification: diff --git a/td/telegram/TermsOfService.cpp b/td/telegram/TermsOfService.cpp index 75c4dd61a..27f6274cc 100644 --- a/td/telegram/TermsOfService.cpp +++ b/td/telegram/TermsOfService.cpp @@ -106,9 +106,8 @@ TermsOfService::TermsOfService(telegram_api::object_ptrtext_), std::move(entities)}; - min_user_age_ = - ((terms->flags_ & telegram_api::help_termsOfService::MIN_AGE_CONFIRM_MASK) != 0 ? terms->min_age_confirm_ : 0); - show_popup_ = (terms->flags_ & telegram_api::help_termsOfService::POPUP_MASK) != 0; + min_user_age_ = terms->min_age_confirm_; + show_popup_ = terms->popup_; } void get_terms_of_service(Td *td, Promise> promise) { diff --git a/td/telegram/TermsOfService.h b/td/telegram/TermsOfService.h index 344c57e0a..07dff58e2 100644 --- a/td/telegram/TermsOfService.h +++ b/td/telegram/TermsOfService.h @@ -6,11 +6,10 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageEntity.hpp" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/actor/PromiseFuture.h" diff --git a/td/telegram/ThemeManager.cpp b/td/telegram/ThemeManager.cpp index c2c219992..b22ea27cf 100644 --- a/td/telegram/ThemeManager.cpp +++ b/td/telegram/ThemeManager.cpp @@ -16,6 +16,7 @@ #include "td/utils/algorithm.h" #include "td/utils/buffer.h" +#include "td/utils/emoji.h" #include "td/utils/logging.h" #include "td/utils/Random.h" #include "td/utils/Time.h" @@ -24,14 +25,14 @@ namespace td { class GetChatThemesQuery final : public Td::ResultHandler { - Promise> promise_; + Promise> promise_; public: - explicit GetChatThemesQuery(Promise> &&promise) + explicit GetChatThemesQuery(Promise> &&promise) : promise_(std::move(promise)) { } - void send(int32 hash) { + void send(int64 hash) { send_query(G()->net_query_creator().create(telegram_api::account_getChatThemes(hash))); } @@ -111,8 +112,7 @@ void ThemeManager::ChatTheme::store(StorerT &storer) const { BEGIN_STORE_FLAGS(); END_STORE_FLAGS(); td::store(emoji, storer); - td::store(light_id, storer); - td::store(dark_id, storer); + td::store(id, storer); td::store(light_theme, storer); td::store(dark_theme, storer); } @@ -122,8 +122,7 @@ void ThemeManager::ChatTheme::parse(ParserT &parser) { BEGIN_PARSE_FLAGS(); END_PARSE_FLAGS(); td::parse(emoji, parser); - td::parse(light_id, parser); - td::parse(dark_id, parser); + td::parse(id, parser); td::parse(light_theme, parser); td::parse(dark_theme, parser); } @@ -180,29 +179,57 @@ void ThemeManager::loop() { } auto request_promise = PromiseCreator::lambda( - [actor_id = actor_id(this)](Result> result) { + [actor_id = actor_id(this)](Result> result) { send_closure(actor_id, &ThemeManager::on_get_chat_themes, std::move(result)); }); td_->create_handler(std::move(request_promise))->send(chat_themes_.hash); } +bool ThemeManager::is_dark_base_theme(BaseTheme base_theme) { + switch (base_theme) { + case BaseTheme::Classic: + case BaseTheme::Day: + case BaseTheme::Arctic: + return false; + case BaseTheme::Night: + case BaseTheme::Tinted: + return true; + default: + UNREACHABLE(); + return false; + } +} + void ThemeManager::on_update_theme(telegram_api::object_ptr &&theme, Promise &&promise) { CHECK(theme != nullptr); bool is_changed = false; + bool was_light = false; + bool was_dark = false; for (auto &chat_theme : chat_themes_.themes) { - if (chat_theme.light_id == theme->id_ || chat_theme.dark_id == theme->id_) { - auto theme_settings = get_chat_theme_settings(std::move(theme->settings_)); - if (theme_settings.message_colors.empty()) { - break; - } - if (chat_theme.light_id == theme->id_ && chat_theme.light_theme != theme_settings) { - chat_theme.light_theme = theme_settings; - is_changed = true; - } - if (chat_theme.dark_id == theme->id_ && chat_theme.dark_theme != theme_settings) { - chat_theme.dark_theme = theme_settings; - is_changed = true; + if (chat_theme.id == theme->id_) { + for (auto &settings : theme->settings_) { + auto theme_settings = get_chat_theme_settings(std::move(settings)); + if (theme_settings.message_colors.empty()) { + continue; + } + if (is_dark_base_theme(theme_settings.base_theme)) { + if (!was_dark) { + was_dark = true; + if (chat_theme.dark_theme != theme_settings) { + chat_theme.dark_theme = std::move(theme_settings); + is_changed = true; + } + } + } else { + if (!was_light) { + was_light = true; + if (chat_theme.light_theme != theme_settings) { + chat_theme.light_theme = std::move(theme_settings); + is_changed = true; + } + } + } } } } @@ -254,7 +281,7 @@ void ThemeManager::send_update_chat_themes() const { send_closure(G()->td(), &Td::send_update, get_update_chat_themes_object()); } -void ThemeManager::on_get_chat_themes(Result> result) { +void ThemeManager::on_get_chat_themes(Result> result) { if (result.is_error()) { set_timeout_in(Random::fast(40, 60)); return; @@ -265,29 +292,49 @@ void ThemeManager::on_get_chat_themes(Resultget_id() == telegram_api::account_chatThemesNotModified::ID) { + if (chat_themes_ptr->get_id() == telegram_api::account_themesNotModified::ID) { return; } - CHECK(chat_themes_ptr->get_id() == telegram_api::account_chatThemes::ID); - auto chat_themes = telegram_api::move_object_as(chat_themes_ptr); + CHECK(chat_themes_ptr->get_id() == telegram_api::account_themes::ID); + auto chat_themes = telegram_api::move_object_as(chat_themes_ptr); chat_themes_.hash = chat_themes->hash_; chat_themes_.themes.clear(); - for (auto &chat_theme : chat_themes->themes_) { - if (chat_theme->emoticon_.empty()) { - LOG(ERROR) << "Receive " << to_string(chat_theme); + for (auto &theme : chat_themes->themes_) { + if (!is_emoji(theme->emoticon_) || !theme->for_chat_) { + LOG(ERROR) << "Receive " << to_string(theme); continue; } - ChatTheme theme; - theme.emoji = std::move(chat_theme->emoticon_); - theme.light_id = chat_theme->theme_->id_; - theme.dark_id = chat_theme->dark_theme_->id_; - theme.light_theme = get_chat_theme_settings(std::move(chat_theme->theme_->settings_)); - theme.dark_theme = get_chat_theme_settings(std::move(chat_theme->dark_theme_->settings_)); - if (theme.light_theme.message_colors.empty() || theme.dark_theme.message_colors.empty()) { + bool was_light = false; + bool was_dark = false; + ChatTheme chat_theme; + chat_theme.emoji = std::move(theme->emoticon_); + chat_theme.id = theme->id_; + for (auto &settings : theme->settings_) { + auto theme_settings = get_chat_theme_settings(std::move(settings)); + if (theme_settings.message_colors.empty()) { + continue; + } + if (is_dark_base_theme(theme_settings.base_theme)) { + if (!was_dark) { + was_dark = true; + if (chat_theme.dark_theme != theme_settings) { + chat_theme.dark_theme = std::move(theme_settings); + } + } + } else { + if (!was_light) { + was_light = true; + if (chat_theme.light_theme != theme_settings) { + chat_theme.light_theme = std::move(theme_settings); + } + } + } + } + if (chat_theme.light_theme.message_colors.empty() || chat_theme.dark_theme.message_colors.empty()) { continue; } - chat_themes_.themes.push_back(std::move(theme)); + chat_themes_.themes.push_back(std::move(chat_theme)); } save_chat_themes(); diff --git a/td/telegram/ThemeManager.h b/td/telegram/ThemeManager.h index 444b4816c..f39698665 100644 --- a/td/telegram/ThemeManager.h +++ b/td/telegram/ThemeManager.h @@ -59,8 +59,7 @@ class ThemeManager final : public Actor { struct ChatTheme { string emoji; - int64 light_id = 0; - int64 dark_id = 0; + int64 id = 0; ThemeSettings light_theme; ThemeSettings dark_theme; @@ -72,7 +71,7 @@ class ThemeManager final : public Actor { }; struct ChatThemes { - int32 hash = 0; + int64 hash = 0; double next_reload_time = 0; vector themes; @@ -89,7 +88,9 @@ class ThemeManager final : public Actor { void tear_down() final; - void on_get_chat_themes(Result> result); + static bool is_dark_base_theme(BaseTheme base_theme); + + void on_get_chat_themes(Result> result); td_api::object_ptr get_theme_settings_object(const ThemeSettings &settings) const; diff --git a/td/telegram/TopDialogCategory.cpp b/td/telegram/TopDialogCategory.cpp index 10a4238f9..b79f62843 100644 --- a/td/telegram/TopDialogCategory.cpp +++ b/td/telegram/TopDialogCategory.cpp @@ -56,6 +56,7 @@ TopDialogCategory get_top_dialog_category(const td_api::object_ptr &category) { CHECK(category != nullptr); switch (category->get_id()) { diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 2ce4d7fdd..d783ef8c1 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/UpdatesManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.hpp" - #include "td/telegram/AnimationsManager.h" #include "td/telegram/AuthManager.h" #include "td/telegram/CallbackQueriesManager.h" @@ -46,7 +43,9 @@ #include "td/telegram/StickerSetId.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" +#include "td/telegram/td_api.h" #include "td/telegram/TdDb.h" +#include "td/telegram/telegram_api.hpp" #include "td/telegram/ThemeManager.h" #include "td/telegram/WebPagesManager.h" @@ -177,6 +176,28 @@ void UpdatesManager::tear_down() { parent_.reset(); } +void UpdatesManager::hangup_shared() { + ref_cnt_--; + if (ref_cnt_ == 0) { + stop(); + } +} + +void UpdatesManager::hangup() { + pending_pts_updates_.clear(); + postponed_pts_updates_.clear(); + postponed_updates_.clear(); + pending_seq_updates_.clear(); + pending_qts_updates_.clear(); + + hangup_shared(); +} + +ActorShared UpdatesManager::create_reference() { + ref_cnt_++; + return actor_shared(this, 1); +} + void UpdatesManager::fill_pts_gap(void *td) { CHECK(td != nullptr); if (G()->close_flag()) { @@ -676,6 +697,7 @@ bool UpdatesManager::is_acceptable_message(const telegram_api::Message *message_ case telegram_api::messageActionGroupCallScheduled::ID: case telegram_api::messageActionSetMessagesTTL::ID: case telegram_api::messageActionSetChatTheme::ID: + case telegram_api::messageActionChatJoinedByRequest::ID: break; case telegram_api::messageActionChatCreate::ID: { auto chat_create = static_cast(action); @@ -695,13 +717,9 @@ bool UpdatesManager::is_acceptable_message(const telegram_api::Message *message_ } break; } - case telegram_api::messageActionChatJoinedByLink::ID: { - auto chat_joined_by_link = static_cast(action); - if (!is_acceptable_user(UserId(chat_joined_by_link->inviter_id_))) { - return false; - } + case telegram_api::messageActionChatJoinedByLink::ID: + // inviter_id_ isn't used break; - } case telegram_api::messageActionChatDeleteUser::ID: { auto chat_delete_user = static_cast(action); if (!is_acceptable_user(UserId(chat_delete_user->user_id_))) { @@ -1656,7 +1674,7 @@ void UpdatesManager::on_pending_updates(vector &&result) mutable { + mpas.add_promise([actor_id = create_reference(), promise = std::move(promise)](Result &&result) mutable { send_closure(actor_id, &UpdatesManager::on_pending_updates_processed, std::move(result), std::move(promise)); }); auto lock = mpas.get_promise(); @@ -1777,7 +1795,7 @@ void UpdatesManager::on_pending_updates(vector &&result, Promise &&promise) { +void UpdatesManager::on_pending_updates_processed(Result result, Promise promise) { promise.set_result(std::move(result)); } @@ -2123,6 +2141,14 @@ void UpdatesManager::process_qts_update(tl_object_ptr &&up add_qts(qts).set_value(Unit()); break; } + case telegram_api::updateBotChatInviteRequester::ID: { + auto update = move_tl_object_as(update_ptr); + td_->contacts_manager_->on_update_chat_invite_requester(DialogId(update->peer_), UserId(update->user_id_), + std::move(update->about_), update->date_, + DialogInviteLink(std::move(update->invite_))); + add_qts(qts).set_value(Unit()); + break; + } default: UNREACHABLE(); break; @@ -2778,6 +2804,7 @@ bool UpdatesManager::is_qts_update(const telegram_api::Update *update) { case telegram_api::updateBotStopped::ID: case telegram_api::updateChatParticipant::ID: case telegram_api::updateChannelParticipant::ID: + case telegram_api::updateBotChatInviteRequester::ID: return true; default: return false; @@ -2796,6 +2823,8 @@ int32 UpdatesManager::get_update_qts(const telegram_api::Update *update) { return static_cast(update)->qts_; case telegram_api::updateChannelParticipant::ID: return static_cast(update)->qts_; + case telegram_api::updateBotChatInviteRequester::ID: + return static_cast(update)->qts_; default: return 0; } @@ -2921,9 +2950,8 @@ void UpdatesManager::on_update(tl_object_ptr u } void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - FolderId folder_id(update->flags_ & telegram_api::updateDialogPinned::FOLDER_ID_MASK ? update->folder_id_ : 0); - td_->messages_manager_->on_update_dialog_is_pinned( - folder_id, DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogPinned::PINNED_MASK) != 0); + td_->messages_manager_->on_update_dialog_is_pinned(FolderId(update->folder_id_), DialogId(update->peer_), + update->pinned_); promise.set_value(Unit()); } @@ -2934,8 +2962,7 @@ void UpdatesManager::on_update(tl_object_ptr } void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - td_->messages_manager_->on_update_dialog_is_marked_as_unread( - DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogUnreadMark::UNREAD_MASK) != 0); + td_->messages_manager_->on_update_dialog_is_marked_as_unread(DialogId(update->peer_), update->unread_); promise.set_value(Unit()); } @@ -3040,8 +3067,7 @@ void UpdatesManager::on_update(tl_object_ptr up } void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { - bool is_masks = (update->flags_ & telegram_api::updateStickerSetsOrder::MASKS_MASK) != 0; - td_->stickers_manager_->on_update_sticker_sets_order(is_masks, + td_->stickers_manager_->on_update_sticker_sets_order(update->masks_, StickersManager::convert_sticker_set_ids(update->order_)); promise.set_value(Unit()); } @@ -3211,10 +3237,22 @@ void UpdatesManager::on_update(tl_object_ptr update, + Promise &&promise) { + auto qts = update->qts_; + add_pending_qts_update(std::move(update), qts, std::move(promise)); +} + void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { td_->theme_manager_->on_update_theme(std::move(update->theme_), std::move(promise)); } +void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + td_->messages_manager_->on_update_dialog_pending_join_requests(DialogId(update->peer_), update->requests_pending_, + std::move(update->recent_requesters_)); + promise.set_value(Unit()); +} + // unsupported updates } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 42bf659e4..ee80cc0d6 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -23,6 +23,7 @@ #include "td/utils/logging.h" #include "td/utils/Status.h" #include "td/utils/tl_storers.h" +#include "td/utils/TlStorerToString.h" #include #include @@ -180,6 +181,7 @@ class UpdatesManager final : public Actor { Td *td_; ActorShared<> parent_; + int32 ref_cnt_ = 1; PtsManager pts_manager_; PtsManager qts_manager_; @@ -224,6 +226,12 @@ class UpdatesManager final : public Actor { void tear_down() final; + void hangup_shared() final; + + void hangup() final; + + ActorShared create_reference(); + int32 get_pts() const { return pts_manager_.mem_pts(); } @@ -267,7 +275,7 @@ class UpdatesManager final : public Actor { void on_pending_updates(vector> &&updates, int32 seq_begin, int32 seq_end, int32 date, double receive_time, Promise &&promise, const char *source); - void on_pending_updates_processed(Result &&result, Promise &&promise); + void on_pending_updates_processed(Result result, Promise promise); void process_updates(vector> &&updates, bool force_apply, Promise &&promise); @@ -487,9 +495,12 @@ class UpdatesManager final : public Actor { void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); void on_update(tl_object_ptr update, Promise &&promise); + void on_update(tl_object_ptr update, Promise &&promise); + // unsupported updates }; diff --git a/td/telegram/Version.h b/td/telegram/Version.h index 70cf0b911..2a8fbb5ee 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -10,7 +10,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 133; +constexpr int32 MTPROTO_LAYER = 134; enum class Version : int32 { Initial, // 0 @@ -47,6 +47,7 @@ enum class Version : int32 { SupportBannedChannels, RemovePhotoVolumeAndLocalId, Support64BitIds, + AddInviteLinksRequiringApproval, Next }; diff --git a/td/telegram/VideoNotesManager.h b/td/telegram/VideoNotesManager.h index 30ff1c253..3a4b79162 100644 --- a/td/telegram/VideoNotesManager.h +++ b/td/telegram/VideoNotesManager.h @@ -6,12 +6,11 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/Photo.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/VideosManager.h b/td/telegram/VideosManager.h index 1e2853509..b8d98ab3c 100644 --- a/td/telegram/VideosManager.h +++ b/td/telegram/VideosManager.h @@ -6,12 +6,11 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/Photo.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/VoiceNotesManager.h b/td/telegram/VoiceNotesManager.h index 21a920d33..0e54999d4 100644 --- a/td/telegram/VoiceNotesManager.h +++ b/td/telegram/VoiceNotesManager.h @@ -6,11 +6,10 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 36b483bdf..37c6ea58c 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -2039,8 +2039,8 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); - bool need_autoplay = (page_block->flags_ & telegram_api::pageBlockVideo::AUTOPLAY_MASK) != 0; - bool is_looped = (page_block->flags_ & telegram_api::pageBlockVideo::LOOP_MASK) != 0; + bool need_autoplay = page_block->autoplay_; + bool is_looped = page_block->loop_; auto animations_it = animations.find(page_block->video_id_); if (animations_it != animations.end()) { LOG_IF(ERROR, !is_looped) << "Receive non-looped animation"; @@ -2067,8 +2067,8 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); - bool is_full_width = (page_block->flags_ & telegram_api::pageBlockEmbed::FULL_WIDTH_MASK) != 0; - bool allow_scrolling = (page_block->flags_ & telegram_api::pageBlockEmbed::ALLOW_SCROLLING_MASK) != 0; + bool is_full_width = page_block->full_width_; + bool allow_scrolling = page_block->allow_scrolling_; bool has_dimensions = (page_block->flags_ & telegram_api::pageBlockEmbed::W_MASK) != 0; auto it = (page_block->flags_ & telegram_api::pageBlockEmbed::POSTER_PHOTO_ID_MASK) != 0 ? photos.find(page_block->poster_photo_id_) @@ -2158,23 +2158,22 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); - auto is_bordered = (page_block->flags_ & telegram_api::pageBlockTable::BORDERED_MASK) != 0; - auto is_striped = (page_block->flags_ & telegram_api::pageBlockTable::STRIPED_MASK) != 0; + auto is_bordered = page_block->bordered_; + auto is_striped = page_block->striped_; auto cells = transform(std::move(page_block->rows_), [&](tl_object_ptr &&row) { return transform(std::move(row->cells_), [&](tl_object_ptr &&table_cell) { WebPageBlockTableCell cell; - auto flags = table_cell->flags_; - cell.is_header = (flags & telegram_api::pageTableCell::HEADER_MASK) != 0; - cell.align_center = (flags & telegram_api::pageTableCell::ALIGN_CENTER_MASK) != 0; + cell.is_header = table_cell->header_; + cell.align_center = table_cell->align_center_; if (!cell.align_center) { - cell.align_right = (flags & telegram_api::pageTableCell::ALIGN_RIGHT_MASK) != 0; + cell.align_right = table_cell->align_right_; if (!cell.align_right) { cell.align_left = true; } } - cell.valign_middle = (flags & telegram_api::pageTableCell::VALIGN_MIDDLE_MASK) != 0; + cell.valign_middle = table_cell->valign_middle_; if (!cell.valign_middle) { - cell.valign_bottom = (flags & telegram_api::pageTableCell::VALIGN_BOTTOM_MASK) != 0; + cell.valign_bottom = table_cell->valign_bottom_; if (!cell.valign_bottom) { cell.valign_top = true; } @@ -2182,10 +2181,10 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptrtext_ != nullptr) { cell.text = get_rich_text(std::move(table_cell->text_), documents); } - if ((flags & telegram_api::pageTableCell::COLSPAN_MASK) != 0) { + if ((table_cell->flags_ & telegram_api::pageTableCell::COLSPAN_MASK) != 0) { cell.colspan = table_cell->colspan_; } - if ((flags & telegram_api::pageTableCell::ROWSPAN_MASK) != 0) { + if ((table_cell->flags_ & telegram_api::pageTableCell::ROWSPAN_MASK) != 0) { cell.rowspan = table_cell->rowspan_; } return cell; @@ -2196,7 +2195,7 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); - auto is_open = (page_block->flags_ & telegram_api::pageBlockDetails::OPEN_MASK) != 0; + auto is_open = page_block->open_; return td::make_unique(get_rich_text(std::move(page_block->title_), documents), get_web_page_blocks(td, std::move(page_block->blocks_), animations, audios, documents, photos, videos, voice_notes), diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index bd795bbba..497f529ae 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -6,8 +6,6 @@ // #include "td/telegram/WebPagesManager.h" -#include "td/telegram/secret_api.h" - #include "td/telegram/AnimationsManager.h" #include "td/telegram/AudiosManager.h" #include "td/telegram/AuthManager.h" @@ -23,6 +21,7 @@ #include "td/telegram/MessageEntity.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/Photo.h" +#include "td/telegram/secret_api.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" @@ -1457,12 +1456,12 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ web_page->instant_view.page_blocks = get_web_page_blocks(td_, std::move(page->blocks_), animations, audios, documents, photos, videos, voice_notes); web_page->instant_view.view_count = (page->flags_ & telegram_api::page::VIEWS_MASK) != 0 ? page->views_ : 0; - web_page->instant_view.is_v2 = (page->flags_ & telegram_api::page::V2_MASK) != 0; - web_page->instant_view.is_rtl = (page->flags_ & telegram_api::page::RTL_MASK) != 0; + web_page->instant_view.is_v2 = page->v2_; + web_page->instant_view.is_rtl = page->rtl_; web_page->instant_view.hash = hash; web_page->instant_view.url = std::move(page->url_); web_page->instant_view.is_empty = false; - web_page->instant_view.is_full = (page->flags_ & telegram_api::page::PART_MASK) == 0; + web_page->instant_view.is_full = !page->part_; web_page->instant_view.is_loaded = true; LOG(DEBUG) << "Receive web page instant view: " diff --git a/td/telegram/WebPagesManager.h b/td/telegram/WebPagesManager.h index d63c73128..ac82c0612 100644 --- a/td/telegram/WebPagesManager.h +++ b/td/telegram/WebPagesManager.h @@ -6,14 +6,13 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/FullMessageId.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/telegram/WebPageId.h" #include "td/actor/actor.h" diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index e2696565e..c818f0466 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2098,6 +2098,13 @@ class CliClient final : public Actor { string limit; get_args(args, chat_id, limit); send_request(td_api::make_object(as_chat_id(chat_id), as_limit(limit))); + } else if (op == "gcmca") { + string chat_id; + string filter; + string from_message_id; + get_args(args, chat_id, filter, from_message_id); + send_request(td_api::make_object( + as_chat_id(chat_id), as_search_messages_filter(filter), as_message_id(from_message_id))); } else if (op == "SearchAudio" || op == "SearchDocument" || op == "SearchPhoto" || op == "SearchChatPhoto") { string chat_id; string offset_message_id; @@ -2106,6 +2113,19 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id), query.query, nullptr, as_message_id(offset_message_id), 0, query.limit, as_search_messages_filter(op), 0)); + } else if (op == "gcmbd") { + string chat_id; + int32 date; + get_args(args, chat_id, date); + send_request(td_api::make_object(as_chat_id(chat_id), date)); + } else if (op == "gcsmp") { + string chat_id; + string filter; + string from_message_id; + string limit; + get_args(args, chat_id, filter, from_message_id, limit); + send_request(td_api::make_object( + as_chat_id(chat_id), as_search_messages_filter(filter), as_message_id(from_message_id), as_limit(limit))); } else if (op == "gcmc") { string chat_id; string filter; @@ -2517,6 +2537,8 @@ class CliClient final : public Actor { send_request(td_api::make_object(args, true, vector())); } else if (op == "seru") { send_request(td_api::make_object(args, false, vector{"ru_RU"})); + } else if (op == "gae") { + send_request(td_api::make_object(args)); } else if (op == "gesu") { send_request(td_api::make_object(args)); } else { @@ -2643,11 +2665,6 @@ class CliClient final : public Actor { for_album)); } else if (op == "gmli") { send_request(td_api::make_object(args)); - } else if (op == "gcmbd") { - string chat_id; - int32 date; - get_args(args, chat_id, date); - send_request(td_api::make_object(as_chat_id(chat_id), date)); } else if (op == "gf" || op == "GetFile") { send_request(td_api::make_object(as_file_id(args))); } else if (op == "gfdps") { @@ -2783,19 +2800,19 @@ class CliClient final : public Actor { } else if (op == "scdi" || op == "SendCallDebugInformation") { send_request(td_api::make_object(as_call_id(args), "{}")); } else if (op == "gvcap") { - send_request(td_api::make_object(as_chat_id(args))); + send_request(td_api::make_object(as_chat_id(args))); } else if (op == "svcdp") { string chat_id; string participant_id; get_args(args, chat_id, participant_id); - send_request(td_api::make_object(as_chat_id(chat_id), + send_request(td_api::make_object(as_chat_id(chat_id), as_message_sender(participant_id))); } else if (op == "cvc") { string chat_id; string title; int32 start_date; get_args(args, chat_id, title, start_date); - send_request(td_api::make_object(as_chat_id(chat_id), title, start_date)); + send_request(td_api::make_object(as_chat_id(chat_id), title, start_date)); } else if (op == "ggc") { send_request(td_api::make_object(as_group_call_id(args))); } else if (op == "ggcss") { @@ -2940,18 +2957,23 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "ccilt") { string chat_id; + string name; int32 expire_date; int32 member_limit; - get_args(args, chat_id, expire_date, member_limit); - send_request(td_api::make_object(as_chat_id(chat_id), expire_date, member_limit)); + bool creates_join_request; + get_args(args, chat_id, name, expire_date, member_limit, creates_join_request); + send_request(td_api::make_object(as_chat_id(chat_id), name, expire_date, + member_limit, creates_join_request)); } else if (op == "ecil") { string chat_id; string invite_link; + string name; int32 expire_date; int32 member_limit; - get_args(args, chat_id, invite_link, expire_date, member_limit); - send_request( - td_api::make_object(as_chat_id(chat_id), invite_link, expire_date, member_limit)); + bool creates_join_request; + get_args(args, chat_id, invite_link, name, expire_date, member_limit, creates_join_request); + send_request(td_api::make_object(as_chat_id(chat_id), invite_link, name, expire_date, + member_limit, creates_join_request)); } else if (op == "rcil") { string chat_id; string invite_link; @@ -2984,7 +3006,30 @@ class CliClient final : public Actor { get_args(args, chat_id, invite_link, offset_user_id, offset_date, limit); send_request(td_api::make_object( as_chat_id(chat_id), invite_link, - td_api::make_object(as_user_id(offset_user_id), offset_date), as_limit(limit))); + td_api::make_object(as_user_id(offset_user_id), offset_date, 0), + as_limit(limit))); + } else if (op == "gcjr") { + string chat_id; + string invite_link; + string query; + string offset_user_id; + int32 offset_date; + string limit; + get_args(args, chat_id, invite_link, query, offset_user_id, offset_date, limit); + send_request(td_api::make_object( + as_chat_id(chat_id), invite_link, query, + td_api::make_object(as_user_id(offset_user_id), offset_date, string()), + as_limit(limit))); + } else if (op == "acjr") { + string chat_id; + string user_id; + get_args(args, chat_id, user_id); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(user_id))); + } else if (op == "dcjr") { + string chat_id; + string user_id; + get_args(args, chat_id, user_id); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_id(user_id))); } else if (op == "drcil") { string chat_id; string invite_link; @@ -3329,7 +3374,8 @@ class CliClient final : public Actor { string bot_id; string query; get_args(args, bot_id, query); - send_request(td_api::make_object(as_user_id(bot_id), 0, nullptr, query, "")); + send_request(td_api::make_object(as_user_id(bot_id), as_chat_id(bot_id), nullptr, + query, "")); } else if (op == "giqro") { string bot_id; string offset; @@ -3680,6 +3726,14 @@ class CliClient final : public Actor { get_args(args, chat_id, remove_from_the_chat_list, revoke); send_request( td_api::make_object(as_chat_id(chat_id), remove_from_the_chat_list, revoke)); + } else if (op == "dcmbd") { + string chat_id; + int32 min_date; + int32 max_date; + bool revoke; + get_args(args, chat_id, min_date, max_date, revoke); + send_request( + td_api::make_object(as_chat_id(chat_id), min_date, max_date, revoke)); } else if (op == "dmfu") { string chat_id; string user_id; diff --git a/td/telegram/files/FileDownloader.cpp b/td/telegram/files/FileDownloader.cpp index 41e8ee766..ea0622056 100644 --- a/td/telegram/files/FileDownloader.cpp +++ b/td/telegram/files/FileDownloader.cpp @@ -6,8 +6,6 @@ // #include "td/telegram/files/FileDownloader.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileLoaderUtils.h" #include "td/telegram/files/FileType.h" diff --git a/td/telegram/files/FileDownloader.h b/td/telegram/files/FileDownloader.h index cb3f215e3..a38706ba0 100644 --- a/td/telegram/files/FileDownloader.h +++ b/td/telegram/files/FileDownloader.h @@ -6,13 +6,12 @@ // #pragma once -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileEncryptionKey.h" #include "td/telegram/files/FileLoader.h" #include "td/telegram/files/FileLocation.h" #include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQuery.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/port/FileFd.h" diff --git a/td/telegram/files/FileGenerateManager.cpp b/td/telegram/files/FileGenerateManager.cpp index 9845f0706..2d11c86a6 100644 --- a/td/telegram/files/FileGenerateManager.cpp +++ b/td/telegram/files/FileGenerateManager.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/files/FileGenerateManager.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileLoaderUtils.h" #include "td/telegram/files/FileManager.h" @@ -17,6 +14,8 @@ #include "td/telegram/net/NetQuery.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/Td.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/format.h" diff --git a/td/telegram/files/FileHashUploader.cpp b/td/telegram/files/FileHashUploader.cpp index 797f12160..86cdf3d98 100644 --- a/td/telegram/files/FileHashUploader.cpp +++ b/td/telegram/files/FileHashUploader.cpp @@ -6,12 +6,11 @@ // #include "td/telegram/files/FileHashUploader.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileType.h" #include "td/telegram/Global.h" #include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryDispatcher.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/files/FileLocation.h b/td/telegram/files/FileLocation.h index 03f44129a..9e4b9436f 100644 --- a/td/telegram/files/FileLocation.h +++ b/td/telegram/files/FileLocation.h @@ -6,12 +6,11 @@ // #pragma once -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileBitmask.h" #include "td/telegram/files/FileType.h" #include "td/telegram/net/DcId.h" #include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/telegram_api.h" #include "td/utils/base64.h" #include "td/utils/buffer.h" @@ -325,11 +324,11 @@ class FullRemoteFileLocation { return photo().source_; case LocationType::Common: case LocationType::Web: - return PhotoSizeSource(nullptr, 0, 0, 0); + return PhotoSizeSource::full_legacy(0, 0, 0); case LocationType::None: default: UNREACHABLE(); - return PhotoSizeSource(nullptr, 0, 0, 0); + return PhotoSizeSource::full_legacy(0, 0, 0); } } diff --git a/td/telegram/files/FileLocation.hpp b/td/telegram/files/FileLocation.hpp index 0c5c10cbf..06e5f72eb 100644 --- a/td/telegram/files/FileLocation.hpp +++ b/td/telegram/files/FileLocation.hpp @@ -66,7 +66,7 @@ void PhotoRemoteFileLocation::parse(ParserT &parser) { int64 secret; parse(secret, parser); parse(local_id, parser); - source = PhotoSizeSource(nullptr, volume_id, local_id, secret); + source = PhotoSizeSource::full_legacy(volume_id, local_id, secret); } if (parser.get_error() != nullptr) { @@ -75,7 +75,7 @@ void PhotoRemoteFileLocation::parse(ParserT &parser) { switch (source.get_type()) { case PhotoSizeSource::Type::Legacy: - source_ = PhotoSizeSource(nullptr, volume_id, local_id, source.legacy().secret); + source_ = PhotoSizeSource::full_legacy(volume_id, local_id, source.legacy().secret); break; case PhotoSizeSource::Type::FullLegacy: case PhotoSizeSource::Type::Thumbnail: @@ -85,13 +85,14 @@ void PhotoRemoteFileLocation::parse(ParserT &parser) { case PhotoSizeSource::Type::DialogPhotoBig: { auto &dialog_photo = source.dialog_photo(); bool is_big = source.get_type() == PhotoSizeSource::Type::DialogPhotoBig; - source_ = PhotoSizeSource(dialog_photo.dialog_id, dialog_photo.dialog_access_hash, is_big, volume_id, local_id); + source_ = PhotoSizeSource::dialog_photo_legacy(dialog_photo.dialog_id, dialog_photo.dialog_access_hash, is_big, + volume_id, local_id); break; } case PhotoSizeSource::Type::StickerSetThumbnail: { auto &sticker_set_thumbnail = source.sticker_set_thumbnail(); - source_ = PhotoSizeSource(sticker_set_thumbnail.sticker_set_id, sticker_set_thumbnail.sticker_set_access_hash, - volume_id, local_id); + source_ = PhotoSizeSource::sticker_set_thumbnail_legacy( + sticker_set_thumbnail.sticker_set_id, sticker_set_thumbnail.sticker_set_access_hash, volume_id, local_id); break; } default: diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 192187862..deecfe286 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -6,8 +6,6 @@ // #include "td/telegram/files/FileManager.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/ConfigShared.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileData.h" diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 4b2d7557f..9ae9fe94c 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -6,9 +6,6 @@ // #pragma once -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/DialogId.h" #include "td/telegram/files/FileDbId.h" #include "td/telegram/files/FileEncryptionKey.h" @@ -20,6 +17,8 @@ #include "td/telegram/files/FileType.h" #include "td/telegram/Location.h" #include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" diff --git a/td/telegram/files/FileStats.cpp b/td/telegram/files/FileStats.cpp index 52b9d4bf7..9bdc512bc 100644 --- a/td/telegram/files/FileStats.cpp +++ b/td/telegram/files/FileStats.cpp @@ -6,9 +6,8 @@ // #include "td/telegram/files/FileStats.h" -#include "td/telegram/td_api.h" - #include "td/telegram/files/FileLoaderUtils.h" +#include "td/telegram/td_api.h" #include "td/utils/algorithm.h" #include "td/utils/common.h" diff --git a/td/telegram/files/FileUploader.cpp b/td/telegram/files/FileUploader.cpp index e7429cb54..7899011f9 100644 --- a/td/telegram/files/FileUploader.cpp +++ b/td/telegram/files/FileUploader.cpp @@ -6,14 +6,12 @@ // #include "td/telegram/files/FileUploader.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/files/FileLoaderUtils.h" - #include "td/telegram/Global.h" #include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/SecureStorage.h" +#include "td/telegram/telegram_api.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h index dded1e5ff..cd5641546 100644 --- a/td/telegram/logevent/LogEvent.h +++ b/td/telegram/logevent/LogEvent.h @@ -72,14 +72,14 @@ class LogEvent { StopPoll = 0x21, SendMessage = 0x100, DeleteMessage = 0x101, - DeleteMessagesFromServer = 0x102, + DeleteMessagesOnServer = 0x102, ReadHistoryOnServer = 0x103, ForwardMessages = 0x104, ReadMessageContentsOnServer = 0x105, SendBotStartMessage = 0x106, SendScreenshotTakenNotificationMessage = 0x107, SendInlineQueryResultMessage = 0x108, - DeleteDialogHistoryFromServer = 0x109, + DeleteDialogHistoryOnServer = 0x109, ReadAllDialogMentionsOnServer = 0x10a, DeleteAllChannelMessagesFromUserOnServer = 0x10b, ToggleDialogIsPinnedOnServer = 0x10c, @@ -89,16 +89,17 @@ class LogEvent { UpdateScopeNotificationSettingsOnServer = 0x110, ResetAllNotificationSettingsOnServer = 0x111, ToggleDialogReportSpamStateOnServer = 0x112, - GetDialogFromServer = 0x113, + RegetDialog = 0x113, ReadHistoryInSecretChat = 0x114, ToggleDialogIsMarkedAsUnreadOnServer = 0x115, SetDialogFolderIdOnServer = 0x116, - DeleteScheduledMessagesFromServer = 0x117, + DeleteScheduledMessagesOnServer = 0x117, ToggleDialogIsBlockedOnServer = 0x118, ReadMessageThreadHistoryOnServer = 0x119, BlockMessageSenderFromRepliesOnServer = 0x120, UnpinAllDialogMessagesOnServer = 0x121, - DeleteAllCallMessagesFromServer = 0x122, + DeleteAllCallMessagesOnServer = 0x122, + DeleteDialogMessagesByDateOnServer = 0x123, GetChannelDifference = 0x140, AddMessagePushNotification = 0x200, EditMessagePushNotification = 0x201, diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index 0e696ca29..79bbd927b 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -295,7 +295,7 @@ ActorId ConnectionCreator::get_dns_resolver() { void ConnectionCreator::ping_proxy(int32 proxy_id, Promise promise) { CHECK(!close_flag_); if (proxy_id == 0) { - auto main_dc_id = G()->net_query_dispatcher().main_dc_id(); + auto main_dc_id = G()->net_query_dispatcher().get_main_dc_id(); bool prefer_ipv6 = G()->shared_config().get_option_boolean("prefer_ipv6"); auto infos = dc_options_set_.find_all_connections(main_dc_id, false, false, prefer_ipv6, false); if (infos.empty()) { @@ -361,7 +361,7 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address return promise.set_error(Status::Error(400, "Unknown proxy identifier")); } const Proxy &proxy = it->second; - auto main_dc_id = G()->net_query_dispatcher().main_dc_id(); + auto main_dc_id = G()->net_query_dispatcher().get_main_dc_id(); FindConnectionExtra extra; auto r_socket_fd = find_connection(proxy, ip_address, main_dc_id, false, extra); if (r_socket_fd.is_error()) { diff --git a/td/telegram/net/ConnectionCreator.h b/td/telegram/net/ConnectionCreator.h index ba6934ebe..520c0708a 100644 --- a/td/telegram/net/ConnectionCreator.h +++ b/td/telegram/net/ConnectionCreator.h @@ -6,13 +6,12 @@ // #pragma once -#include "td/telegram/td_api.h" - #include "td/telegram/net/DcId.h" #include "td/telegram/net/DcOptions.h" #include "td/telegram/net/DcOptionsSet.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/net/Proxy.h" +#include "td/telegram/td_api.h" #include "td/mtproto/AuthData.h" #include "td/mtproto/ConnectionManager.h" diff --git a/td/telegram/net/MtprotoHeader.cpp b/td/telegram/net/MtprotoHeader.cpp index 831b4468c..9c388a73e 100644 --- a/td/telegram/net/MtprotoHeader.cpp +++ b/td/telegram/net/MtprotoHeader.cpp @@ -13,7 +13,6 @@ #include "td/tl/tl_object_store.h" -#include "td/utils/port/Clocks.h" #include "td/utils/tl_helpers.h" namespace td { @@ -89,12 +88,12 @@ class HeaderStorer { for (auto &value : values) { if (value->key_ == "tz_offset") { has_tz_offset = true; - break; + value->value_ = make_tl_object(options.tz_offset); } } if (!has_tz_offset) { values.push_back(make_tl_object( - "tz_offset", make_tl_object(Clocks::tz_offset()))); + "tz_offset", make_tl_object(options.tz_offset))); } } TlStoreBoxedUnknown::store(json_value, storer); diff --git a/td/telegram/net/MtprotoHeader.h b/td/telegram/net/MtprotoHeader.h index b5b0754c3..f6d1f83ee 100644 --- a/td/telegram/net/MtprotoHeader.h +++ b/td/telegram/net/MtprotoHeader.h @@ -24,6 +24,7 @@ class MtprotoHeader { string language_pack; string language_code; string parameters; + int32 tz_offset = 0; bool is_emulator = false; Proxy proxy; }; @@ -77,6 +78,16 @@ class MtprotoHeader { return true; } + bool set_tz_offset(int32 tz_offset) { + if (options_.tz_offset == tz_offset) { + return false; + } + + options_.tz_offset = tz_offset; + default_header_ = gen_header(options_, false); + return true; + } + Slice get_default_header() const { return default_header_; } diff --git a/td/telegram/net/NetQueryDispatcher.h b/td/telegram/net/NetQueryDispatcher.h index e65a9fc32..673a1955d 100644 --- a/td/telegram/net/NetQueryDispatcher.h +++ b/td/telegram/net/NetQueryDispatcher.h @@ -52,8 +52,8 @@ class NetQueryDispatcher { void update_valid_dc(DcId dc_id); - DcId main_dc_id() const { - return DcId::internal(main_dc_id_.load()); + DcId get_main_dc_id() const { + return DcId::internal(main_dc_id_.load(std::memory_order_relaxed)); } void set_main_dc_id(int32 new_main_dc_id); diff --git a/td/telegram/net/PublicRsaKeyWatchdog.h b/td/telegram/net/PublicRsaKeyWatchdog.h index 73e4e9f14..b74022e76 100644 --- a/td/telegram/net/PublicRsaKeyWatchdog.h +++ b/td/telegram/net/PublicRsaKeyWatchdog.h @@ -6,12 +6,10 @@ // #pragma once -#include "td/telegram/net/PublicRsaKeyShared.h" - #include "td/telegram/net/NetActor.h" #include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/net/NetQueryDispatcher.h" - +#include "td/telegram/net/PublicRsaKeyShared.h" #include "td/telegram/telegram_api.h" #include "td/actor/actor.h" diff --git a/td/telegram/net/Session.cpp b/td/telegram/net/Session.cpp index c7289d869..979d39468 100644 --- a/td/telegram/net/Session.cpp +++ b/td/telegram/net/Session.cpp @@ -6,8 +6,6 @@ // #include "td/telegram/net/Session.h" -#include "td/telegram/telegram_api.h" - #include "td/telegram/ConfigShared.h" #include "td/telegram/DhCache.h" #include "td/telegram/Global.h" @@ -18,6 +16,7 @@ #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/net/NetType.h" #include "td/telegram/StateManager.h" +#include "td/telegram/telegram_api.h" #include "td/telegram/UniqueId.h" #include "td/mtproto/DhCallback.h" diff --git a/td/telegram/td_c_client.cpp b/td/telegram/td_c_client.cpp index a2670dc3f..05116778a 100644 --- a/td/telegram/td_c_client.cpp +++ b/td/telegram/td_c_client.cpp @@ -8,7 +8,6 @@ #include "td/telegram/Client.h" #include "td/telegram/Log.h" - #include "td/telegram/td_tdc_api_inner.h" #include diff --git a/td/tl/tl_object_store.h b/td/tl/tl_object_store.h index 191ec1418..196465ac8 100644 --- a/td/tl/tl_object_store.h +++ b/td/tl/tl_object_store.h @@ -47,14 +47,6 @@ class TlStoreBool { } }; -class TlStoreTrue { - public: - template - static void store(const bool &x, StorerT &storer) { - // currently nothing to do - } -}; - class TlStoreBinary { public: template diff --git a/tdactor/td/actor/impl/Scheduler.h b/tdactor/td/actor/impl/Scheduler.h index 715960e7a..94c9b8f99 100644 --- a/tdactor/td/actor/impl/Scheduler.h +++ b/tdactor/td/actor/impl/Scheduler.h @@ -12,7 +12,6 @@ #include "td/utils/common.h" #include "td/utils/Heap.h" #include "td/utils/logging.h" -#include "td/utils/MpscPollableQueue.h" #include "td/utils/ObjectPool.h" #include "td/utils/port/detail/PollableFd.h" #include "td/utils/port/PollFlags.h" @@ -20,7 +19,6 @@ #include "td/utils/Time.h" #include -#include #include #include diff --git a/tdnet/td/net/HttpInboundConnection.h b/tdnet/td/net/HttpInboundConnection.h index 31e2633bf..4073792d2 100644 --- a/tdnet/td/net/HttpInboundConnection.h +++ b/tdnet/td/net/HttpInboundConnection.h @@ -11,6 +11,7 @@ #include "td/actor/actor.h" +#include "td/utils/BufferedFd.h" #include "td/utils/port/SocketFd.h" #include "td/utils/Status.h" diff --git a/tdnet/td/net/HttpOutboundConnection.h b/tdnet/td/net/HttpOutboundConnection.h index 900489026..f9ed2a2cb 100644 --- a/tdnet/td/net/HttpOutboundConnection.h +++ b/tdnet/td/net/HttpOutboundConnection.h @@ -12,6 +12,7 @@ #include "td/actor/actor.h" +#include "td/utils/BufferedFd.h" #include "td/utils/port/SocketFd.h" #include "td/utils/Status.h" diff --git a/tdnet/td/net/Wget.cpp b/tdnet/td/net/Wget.cpp index 6873aca0e..5912f1f4a 100644 --- a/tdnet/td/net/Wget.cpp +++ b/tdnet/td/net/Wget.cpp @@ -11,6 +11,7 @@ #include "td/net/SslStream.h" #include "td/utils/buffer.h" +#include "td/utils/BufferedFd.h" #include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/misc.h" diff --git a/tdtl/td/tl/tl_generate.cpp b/tdtl/td/tl/tl_generate.cpp index 1747bc5f0..6fe1cedae 100644 --- a/tdtl/td/tl/tl_generate.cpp +++ b/tdtl/td/tl/tl_generate.cpp @@ -248,7 +248,7 @@ static void write_function(tl_outputer &out, const tl_combinator *t, const std:: out.append(w.gen_class_begin(class_name, w.gen_base_function_class_name(), false)); int required_args = gen_field_definitions(out, t, class_name, w); - out.append(w.gen_flags_definitions(t)); + out.append(w.gen_flags_definitions(t, true)); std::vector vars(t->var_count); out.append(w.gen_function_vars(t, vars)); @@ -304,7 +304,6 @@ static void write_constructor(tl_outputer &out, const tl_combinator *t, const st out.append(w.gen_class_begin(class_name, base_class, is_proxy)); int required_args = gen_field_definitions(out, t, class_name, w); - out.append(w.gen_flags_definitions(t)); bool can_be_parsed = false; bool is_can_be_parsed_inited = false; @@ -334,6 +333,7 @@ static void write_constructor(tl_outputer &out, const tl_combinator *t, const st can_be_stored = true; } + out.append(w.gen_flags_definitions(t, can_be_stored)); if (w.is_default_constructor_generated(t, can_be_parsed, can_be_stored)) { write_class_constructor(out, t, class_name, true, w); } diff --git a/tdtl/td/tl/tl_writer.h b/tdtl/td/tl/tl_writer.h index 06dd9640f..f8724851f 100644 --- a/tdtl/td/tl/tl_writer.h +++ b/tdtl/td/tl/tl_writer.h @@ -96,7 +96,7 @@ class TL_writer { virtual std::string gen_field_definition(const std::string &class_name, const std::string &type_name, const std::string &field_name) const = 0; - virtual std::string gen_flags_definitions(const tl_combinator *t) const { + virtual std::string gen_flags_definitions(const tl_combinator *t, bool can_be_stored) const { return ""; } diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 34e4f0428..43580fe75 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -262,6 +262,7 @@ set(TDUTILS_SOURCE td/utils/tl_parsers.h td/utils/tl_storers.h td/utils/TlDowncastHelper.h + td/utils/TlStorerToString.h td/utils/translit.h td/utils/TsCerr.h td/utils/TsFileLog.h diff --git a/tdutils/td/utils/TlDowncastHelper.h b/tdutils/td/utils/TlDowncastHelper.h index 4ca64db64..173b77431 100644 --- a/tdutils/td/utils/TlDowncastHelper.h +++ b/tdutils/td/utils/TlDowncastHelper.h @@ -7,7 +7,7 @@ #pragma once #include "td/utils/common.h" -#include "td/utils/tl_storers.h" +#include "td/utils/TlStorerToString.h" namespace td { diff --git a/tdutils/td/utils/TlStorerToString.h b/tdutils/td/utils/TlStorerToString.h new file mode 100644 index 000000000..a8578b965 --- /dev/null +++ b/tdutils/td/utils/TlStorerToString.h @@ -0,0 +1,180 @@ +// +// 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/utils/common.h" +#include "td/utils/SharedSlice.h" +#include "td/utils/Slice.h" +#include "td/utils/SliceBuilder.h" +#include "td/utils/UInt.h" + +namespace td { + +class TlStorerToString { + string result; + size_t shift = 0; + + void store_field_begin(const char *name) { + result.append(shift, ' '); + if (name && name[0]) { + result += name; + result += " = "; + } + } + + void store_field_end() { + result += '\n'; + } + + void store_long(int64 value) { + result += (PSLICE() << value).c_str(); + } + + void store_binary(Slice data) { + static const char *hex = "0123456789ABCDEF"; + + result.append("{ ", 2); + for (auto c : data) { + unsigned char byte = c; + result += hex[byte >> 4]; + result += hex[byte & 15]; + result += ' '; + } + result += '}'; + } + + public: + TlStorerToString() = default; + TlStorerToString(const TlStorerToString &other) = delete; + TlStorerToString &operator=(const TlStorerToString &other) = delete; + + void store_field(const char *name, bool value) { + store_field_begin(name); + result += (value ? "true" : "false"); + store_field_end(); + } + + void store_field(const char *name, int32 value) { + store_field(name, static_cast(value)); + } + + void store_field(const char *name, int64 value) { + store_field_begin(name); + store_long(value); + store_field_end(); + } + + void store_field(const char *name, double value) { + store_field_begin(name); + result += (PSLICE() << value).c_str(); + store_field_end(); + } + + void store_field(const char *name, const char *value) { + store_field_begin(name); + result += value; + store_field_end(); + } + + void store_field(const char *name, const string &value) { + store_field_begin(name); + result += '"'; + result += value; + result += '"'; + store_field_end(); + } + + void store_field(const char *name, const SecureString &value) { + store_field_begin(name); + result.append(""); + store_field_end(); + } + + template + void store_field(const char *name, const T &value) { + store_field_begin(name); + result.append(value.data(), value.size()); + store_field_end(); + } + + void store_bytes_field(const char *name, const SecureString &value) { + store_field_begin(name); + result.append(""); + store_field_end(); + } + + template + void store_bytes_field(const char *name, const BytesT &value) { + static const char *hex = "0123456789ABCDEF"; + + store_field_begin(name); + result.append("bytes ["); + store_long(static_cast(value.size())); + result.append("] { "); + size_t len = min(static_cast(64), value.size()); + for (size_t i = 0; i < len; i++) { + int b = value[static_cast(i)] & 0xff; + result += hex[b >> 4]; + result += hex[b & 15]; + result += ' '; + } + if (len < value.size()) { + result.append("..."); + } + result += '}'; + store_field_end(); + } + + template + void store_object_field(const char *name, const ObjectT *value) { + if (value == nullptr) { + store_field(name, "null"); + } else { + value->store(*this, name); + } + } + + void store_field(const char *name, const UInt128 &value) { + store_field_begin(name); + store_binary(as_slice(value)); + store_field_end(); + } + + void store_field(const char *name, const UInt256 &value) { + store_field_begin(name); + store_binary(as_slice(value)); + store_field_end(); + } + + void store_vector_begin(const char *field_name, size_t vector_size) { + store_field_begin(field_name); + result += "vector["; + result += (PSLICE() << vector_size).c_str(); + result += "] {\n"; + shift += 2; + } + + void store_class_begin(const char *field_name, const char *class_name) { + store_field_begin(field_name); + result += class_name; + result += " {\n"; + shift += 2; + } + + void store_class_end() { + CHECK(shift >= 2); + shift -= 2; + result.append(shift, ' '); + result += "}\n"; + } + + string move_as_string() { + return std::move(result); + } +}; + +} // namespace td diff --git a/tdutils/td/utils/emoji.cpp b/tdutils/td/utils/emoji.cpp index 918aff2ff..6243c8996 100644 --- a/tdutils/td/utils/emoji.cpp +++ b/tdutils/td/utils/emoji.cpp @@ -839,6 +839,25 @@ bool is_emoji(Slice str) { return emojis.count(str) != 0; } +int get_fitzpatrick_modifier(Slice emoji) { + if (emoji.size() < 4 || emoji[emoji.size() - 4] != '\xF0' || emoji[emoji.size() - 3] != '\x9F' || + emoji[emoji.size() - 2] != '\x8F') { + return 0; + } + auto c = static_cast(emoji.back()); + if (c < 0xBB || c > 0xBF) { + return 0; + } + return (c - 0xBB) + 2; +} + +Slice remove_fitzpatrick_modifier(Slice emoji) { + while (get_fitzpatrick_modifier(emoji) != 0) { + emoji.remove_suffix(4); + } + return emoji; +} + Slice remove_emoji_modifiers(Slice emoji) { static const Slice modifiers[] = {u8"\uFE0E" /* variation selector-15 */, u8"\uFE0F" /* variation selector-16 */, diff --git a/tdutils/td/utils/emoji.h b/tdutils/td/utils/emoji.h index 4a0020187..f23084eef 100644 --- a/tdutils/td/utils/emoji.h +++ b/tdutils/td/utils/emoji.h @@ -14,6 +14,12 @@ namespace td { // checks whether the string is an emoji; variation selectors are ignored bool is_emoji(Slice str); +// checks whether emoji ends on a Fitzpatrick modifier and returns it's number or 0 +int get_fitzpatrick_modifier(Slice emoji); + +// removes all Fitzpatrick modifier from the end of the string +Slice remove_fitzpatrick_modifier(Slice emoji); + // removes all emoji modifiers from the end of the string Slice remove_emoji_modifiers(Slice emoji); diff --git a/tdutils/td/utils/port/path.cpp b/tdutils/td/utils/port/path.cpp index b256ca57f..d7c705597 100644 --- a/tdutils/td/utils/port/path.cpp +++ b/tdutils/td/utils/port/path.cpp @@ -75,7 +75,10 @@ Status mkpath(CSlice path, int32 mode) { } } if (last_error.is_error()) { - return first_error; + if (last_error.message() == first_error.message() && last_error.code() == first_error.code()) { + return first_error; + } + return last_error.move_as_error_suffix(PSLICE() << ": " << first_error); } return Status::OK(); } diff --git a/tdutils/td/utils/tl_storers.h b/tdutils/td/utils/tl_storers.h index 0865088dd..e5870551c 100644 --- a/tdutils/td/utils/tl_storers.h +++ b/tdutils/td/utils/tl_storers.h @@ -8,11 +8,8 @@ #include "td/utils/common.h" #include "td/utils/logging.h" -#include "td/utils/SharedSlice.h" #include "td/utils/Slice.h" -#include "td/utils/SliceBuilder.h" #include "td/utils/StorerBase.h" -#include "td/utils/UInt.h" #include @@ -46,6 +43,7 @@ class TlStorerUnsafe { std::memcpy(buf_, slice.begin(), slice.size()); buf_ += slice.size(); } + void store_storer(const Storer &storer) { size_t size = storer.store(buf_); buf_ += size; @@ -142,152 +140,6 @@ class TlStorerCalcLength { } }; -class TlStorerToString { - std::string result; - size_t shift = 0; - - void store_field_begin(const char *name) { - result.append(shift, ' '); - if (name && name[0]) { - result += name; - result += " = "; - } - } - - void store_field_end() { - result += '\n'; - } - - void store_long(int64 value) { - result += (PSLICE() << value).c_str(); - } - - void store_binary(Slice data) { - static const char *hex = "0123456789ABCDEF"; - - result.append("{ ", 2); - for (auto c : data) { - unsigned char byte = c; - result += hex[byte >> 4]; - result += hex[byte & 15]; - result += ' '; - } - result += '}'; - } - - public: - TlStorerToString() = default; - TlStorerToString(const TlStorerToString &other) = delete; - TlStorerToString &operator=(const TlStorerToString &other) = delete; - - void store_field(const char *name, bool value) { - store_field_begin(name); - result += (value ? "true" : "false"); - store_field_end(); - } - - void store_field(const char *name, int32 value) { - store_field(name, static_cast(value)); - } - - void store_field(const char *name, int64 value) { - store_field_begin(name); - store_long(value); - store_field_end(); - } - - void store_field(const char *name, double value) { - store_field_begin(name); - result += (PSLICE() << value).c_str(); - store_field_end(); - } - - void store_field(const char *name, const char *value) { - store_field_begin(name); - result += value; - store_field_end(); - } - - void store_field(const char *name, const string &value) { - store_field_begin(name); - result += '"'; - result += value; - result += '"'; - store_field_end(); - } - - void store_field(const char *name, const SecureString &value) { - store_field_begin(name); - result.append(""); - store_field_end(); - } - - template - void store_field(const char *name, const T &value) { - store_field_begin(name); - result.append(value.data(), value.size()); - store_field_end(); - } - - void store_bytes_field(const char *name, const SecureString &value) { - store_field_begin(name); - result.append(""); - store_field_end(); - } - - template - void store_bytes_field(const char *name, const BytesT &value) { - static const char *hex = "0123456789ABCDEF"; - - store_field_begin(name); - result.append("bytes ["); - store_long(static_cast(value.size())); - result.append("] { "); - size_t len = min(static_cast(64), value.size()); - for (size_t i = 0; i < len; i++) { - int b = value[static_cast(i)] & 0xff; - result += hex[b >> 4]; - result += hex[b & 15]; - result += ' '; - } - if (len < value.size()) { - result.append("..."); - } - result += '}'; - store_field_end(); - } - - void store_field(const char *name, const UInt128 &value) { - store_field_begin(name); - store_binary(as_slice(value)); - store_field_end(); - } - - void store_field(const char *name, const UInt256 &value) { - store_field_begin(name); - store_binary(as_slice(value)); - store_field_end(); - } - - void store_class_begin(const char *field_name, const char *class_name) { - store_field_begin(field_name); - result += class_name; - result += " {\n"; - shift += 2; - } - - void store_class_end() { - CHECK(shift >= 2); - shift -= 2; - result.append(shift, ' '); - result += "}\n"; - } - - std::string move_as_str() { - return std::move(result); - } -}; - template size_t tl_calc_length(const T &data) { TlStorerCalcLength storer_calc_length; diff --git a/test/link.cpp b/test/link.cpp index 2af303cce..52d0f6533 100644 --- a/test/link.cpp +++ b/test/link.cpp @@ -171,8 +171,11 @@ TEST(Link, parse_internal_link) { auto unknown_deep_link = [](const td::string &link) { return td::td_api::make_object(link); }; - auto voice_chat = [](const td::string &chat_username, const td::string &invite_hash, bool is_live_stream) { - return td::td_api::make_object(chat_username, invite_hash, is_live_stream); + auto unsupported_proxy = [] { + return td::td_api::make_object(); + }; + auto video_chat = [](const td::string &chat_username, const td::string &invite_hash, bool is_live_stream) { + return td::td_api::make_object(chat_username, invite_hash, is_live_stream); }; parse_internal_link("t.me/levlam/1", message("tg:resolve?domain=levlam&post=1")); @@ -468,17 +471,22 @@ TEST(Link, parse_internal_link) { proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF")); parse_internal_link("t.me/proxy?server=1.2.3.4&port=80adasdas&secret=1234567890abcdef1234567890ABCDEF", proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF")); - parse_internal_link("t.me/proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF", nullptr); - parse_internal_link("t.me/proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF", nullptr); - parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=", nullptr); - parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=12", nullptr); + parse_internal_link("t.me/proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); + parse_internal_link("t.me/proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); + parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=", unsupported_proxy()); + parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=12", unsupported_proxy()); parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=1234567890abcdef1234567890ABCDEF", proxy_mtproto("google.com", 80, "1234567890abcdef1234567890ABCDEF")); parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=dd1234567890abcdef1234567890ABCDEF", proxy_mtproto("google.com", 80, "dd1234567890abcdef1234567890ABCDEF")); - parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF", nullptr); - parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=ee1234567890abcdef1234567890ABCDEF", nullptr); - parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=ee1234567890abcdef1234567890ABCDEF0", nullptr); + parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); + parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=ee1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); + parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=ee1234567890abcdef1234567890ABCDEF0", + unsupported_proxy()); parse_internal_link("t.me/proxy?server=google.com&port=8%30&secret=ee1234567890abcdef1234567890ABCDEF%30%30", proxy_mtproto("google.com", 80, "ee1234567890abcdef1234567890ABCDEF00")); parse_internal_link( @@ -491,24 +499,21 @@ TEST(Link, parse_internal_link) { proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF")); parse_internal_link("tg:proxy?server=1.2.3.4&port=80adasdas&secret=1234567890abcdef1234567890ABCDEF", proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF")); - parse_internal_link( - "tg:proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF", - unknown_deep_link("tg://proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF")); - parse_internal_link( - "tg:proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF", - unknown_deep_link("tg://proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF")); + parse_internal_link("tg:proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); + parse_internal_link("tg:proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=1234567890abcdef1234567890ABCDEF", proxy_mtproto("google.com", 80, "1234567890abcdef1234567890ABCDEF")); parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=dd1234567890abcdef1234567890ABCDEF", proxy_mtproto("google.com", 80, "dd1234567890abcdef1234567890ABCDEF")); - parse_internal_link( - "tg:proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF", - unknown_deep_link("tg://proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF")); + parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF", + unsupported_proxy()); parse_internal_link("t.me/socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", "")); parse_internal_link("t.me/socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", "")); - parse_internal_link("t.me/socks?server=1.2.3.4&port=adasdas", nullptr); - parse_internal_link("t.me/socks?server=1.2.3.4&port=65536", nullptr); + parse_internal_link("t.me/socks?server=1.2.3.4&port=adasdas", unsupported_proxy()); + parse_internal_link("t.me/socks?server=1.2.3.4&port=65536", unsupported_proxy()); parse_internal_link("t.me/socks?server=google.com&port=8%30", proxy_socks("google.com", 80, "", "")); parse_internal_link("t.me/socks?server=google.com&port=8%30&user=1&pass=", proxy_socks("google.com", 80, "1", "")); parse_internal_link("t.me/socks?server=google.com&port=8%30&user=&pass=2", proxy_socks("google.com", 80, "", "2")); @@ -516,35 +521,34 @@ TEST(Link, parse_internal_link) { parse_internal_link("tg:socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", "")); parse_internal_link("tg:socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", "")); - parse_internal_link("tg:socks?server=1.2.3.4&port=adasdas", - unknown_deep_link("tg://socks?server=1.2.3.4&port=adasdas")); - parse_internal_link("tg:socks?server=1.2.3.4&port=65536", unknown_deep_link("tg://socks?server=1.2.3.4&port=65536")); + parse_internal_link("tg:socks?server=1.2.3.4&port=adasdas", unsupported_proxy()); + parse_internal_link("tg:socks?server=1.2.3.4&port=65536", unsupported_proxy()); parse_internal_link("tg:socks?server=google.com&port=8%30", proxy_socks("google.com", 80, "", "")); parse_internal_link("tg:socks?server=google.com&port=8%30&user=1&pass=", proxy_socks("google.com", 80, "1", "")); parse_internal_link("tg:socks?server=google.com&port=8%30&user=&pass=2", proxy_socks("google.com", 80, "", "2")); parse_internal_link("tg:socks?server=google.com&port=80&user=1&pass=2", proxy_socks("google.com", 80, "1", "2")); - parse_internal_link("tg:resolve?domain=username&voice%63hat=aasdasd", voice_chat("username", "aasdasd", false)); - parse_internal_link("tg:resolve?domain=username&video%63hat=aasdasd", voice_chat("username", "aasdasd", false)); - parse_internal_link("tg:resolve?domain=username&livestream=aasdasd", voice_chat("username", "aasdasd", true)); - parse_internal_link("TG://resolve?domain=username&voicechat=", voice_chat("username", "", false)); + parse_internal_link("tg:resolve?domain=username&voice%63hat=aasdasd", video_chat("username", "aasdasd", false)); + parse_internal_link("tg:resolve?domain=username&video%63hat=aasdasd", video_chat("username", "aasdasd", false)); + parse_internal_link("tg:resolve?domain=username&livestream=aasdasd", video_chat("username", "aasdasd", true)); + parse_internal_link("TG://resolve?domain=username&voicechat=", video_chat("username", "", false)); parse_internal_link("TG://test@resolve?domain=username&voicechat=", nullptr); parse_internal_link("tg:resolve:80?domain=username&voicechat=", nullptr); parse_internal_link("tg:http://resolve?domain=username&voicechat=", nullptr); parse_internal_link("tg:https://resolve?domain=username&voicechat=", nullptr); parse_internal_link("tg:resolve?domain=&voicechat=", unknown_deep_link("tg://resolve?domain=&voicechat=")); - parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", voice_chat("telegram", "0", false)); + parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", video_chat("telegram", "0", false)); - parse_internal_link("t.me/username/0/a//s/as?voicechat=", voice_chat("username", "", false)); - parse_internal_link("t.me/username/0/a//s/as?videochat=2", voice_chat("username", "2", false)); - parse_internal_link("t.me/username/0/a//s/as?livestream=3", voice_chat("username", "3", true)); - parse_internal_link("t.me/username/aasdas?test=1&voicechat=#12312", voice_chat("username", "", false)); - parse_internal_link("t.me/username/0?voicechat=", voice_chat("username", "", false)); - parse_internal_link("t.me/username/-1?voicechat=asdasd", voice_chat("username", "asdasd", false)); - parse_internal_link("t.me/username?voicechat=", voice_chat("username", "", false)); + parse_internal_link("t.me/username/0/a//s/as?voicechat=", video_chat("username", "", false)); + parse_internal_link("t.me/username/0/a//s/as?videochat=2", video_chat("username", "2", false)); + parse_internal_link("t.me/username/0/a//s/as?livestream=3", video_chat("username", "3", true)); + parse_internal_link("t.me/username/aasdas?test=1&voicechat=#12312", video_chat("username", "", false)); + parse_internal_link("t.me/username/0?voicechat=", video_chat("username", "", false)); + parse_internal_link("t.me/username/-1?voicechat=asdasd", video_chat("username", "asdasd", false)); + parse_internal_link("t.me/username?voicechat=", video_chat("username", "", false)); parse_internal_link("t.me/username#voicechat=asdas", public_chat("username")); parse_internal_link("t.me//username?voicechat=", nullptr); - parse_internal_link("https://telegram.dog/tele%63ram?voi%63e%63hat=t%63st", voice_chat("telecram", "tcst", false)); + parse_internal_link("https://telegram.dog/tele%63ram?voi%63e%63hat=t%63st", video_chat("telecram", "tcst", false)); parse_internal_link("tg:resolve?domain=username&start=aasdasd", bot_start("username", "aasdasd")); parse_internal_link("TG://resolve?domain=username&start=", bot_start("username", "")); diff --git a/test/mtproto.cpp b/test/mtproto.cpp index 11fc2e724..a9a0cb09c 100644 --- a/test/mtproto.cpp +++ b/test/mtproto.cpp @@ -32,6 +32,7 @@ #include "td/actor/PromiseFuture.h" #include "td/utils/base64.h" +#include "td/utils/BufferedFd.h" #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/logging.h"