Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-06-21 18:38:28 +02:00
commit a116cc9b19
135 changed files with 4359 additions and 1243 deletions

View File

@ -85,9 +85,8 @@ function(td_set_up_compiler)
add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096)
endif()
if (NOT ANDROID) # _FILE_OFFSET_BITS is broken in NDK r15, r15b and r17 and doesn't work prior to Android 7.0
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
# _FILE_OFFSET_BITS is broken in Android NDK r15, r15b and r17 and doesn't work prior to Android 7.0
add_definitions(-D_FILE_OFFSET_BITS=64)
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lsocket -lnsl")

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TDLib VERSION 1.8.3 LANGUAGES CXX C)
project(TDLib VERSION 1.8.4 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
@ -287,6 +287,7 @@ set(TDLIB_SOURCE
td/telegram/AudiosManager.cpp
td/telegram/AuthManager.cpp
td/telegram/AutoDownloadSettings.cpp
td/telegram/AvailableReaction.cpp
td/telegram/BackgroundManager.cpp
td/telegram/BackgroundType.cpp
td/telegram/BotCommand.cpp
@ -410,6 +411,7 @@ set(TDLIB_SOURCE
td/telegram/PhotoSize.cpp
td/telegram/PhotoSizeSource.cpp
td/telegram/PollManager.cpp
td/telegram/Premium.cpp
td/telegram/QueryCombiner.cpp
td/telegram/RecentDialogList.cpp
td/telegram/ReplyMarkup.cpp
@ -484,6 +486,7 @@ set(TDLIB_SOURCE
td/telegram/AudiosManager.h
td/telegram/AuthManager.h
td/telegram/AutoDownloadSettings.h
td/telegram/AvailableReaction.h
td/telegram/BackgroundId.h
td/telegram/BackgroundManager.h
td/telegram/BackgroundType.h
@ -644,6 +647,7 @@ set(TDLIB_SOURCE
td/telegram/PhotoSizeSource.h
td/telegram/PollId.h
td/telegram/PollManager.h
td/telegram/Premium.h
td/telegram/PrivacyManager.h
td/telegram/PtsManager.h
td/telegram/PublicDialogType.h

View File

@ -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.8.3 REQUIRED)
find_package(Td 1.8.4 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).

View File

@ -928,6 +928,7 @@ function onOptionsChanged() {
if (use_vcpkg) {
commands.push('git clone https://github.com/Microsoft/vcpkg.git');
commands.push('cd vcpkg');
commands.push('git checkout 1b1ae50e1a69f7c659bd7d731e80b358d21c86ad');
commands.push(local + 'bootstrap-vcpkg.bat');
if (target === 'C++/CX') {
commands.push(local + 'vcpkg.exe install gperf:x86-windows openssl:arm-uwp openssl:arm64-uwp openssl:x64-uwp openssl:x86-uwp zlib:arm-uwp zlib:arm64-uwp zlib:x64-uwp zlib:x86-uwp');

View File

@ -166,8 +166,7 @@ TDLib can be used from the Dart programming language through the [JSON](https://
See [dart_tdlib](https://github.com/periodicaidan/dart_tdlib) or [Dart wrapper for TDLib](https://github.com/tdlight-team/tdlight/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49) for an example of a TDLib Dart bindings through FFI.
See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib), [tdlib](https://github.com/pqhaz/tdlib),
[tdlib-dart](https://github.com/drewpayment/tdlib-dart), [tdlib-dart](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib), [tdlib-dart](https://github.com/drewpayment/tdlib-dart), [tdlib-dart](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
<a name="rust"></a>
## Using TDLib in Rust projects

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
project(TdExample VERSION 1.0 LANGUAGES CXX)
find_package(Td 1.8.3 REQUIRED)
find_package(Td 1.8.4 REQUIRED)
add_executable(tdjson_example tdjson_example.cpp)
target_link_libraries(tdjson_example PRIVATE Td::TdJson)

View File

@ -65,7 +65,7 @@ add_custom_target(build_java
)
add_custom_target(generate_javadoc
COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY}/../docs org.drinkless.tdlib
COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding UTF-8 -charset UTF-8 -d ${JAVA_OUTPUT_DIRECTORY}/../docs org.drinkless.tdlib
WORKING_DIRECTORY ${TD_API_JAVA_PATH}
COMMENT "Generating Javadoc documentation"
DEPENDS td_generate_java_api

View File

@ -1,6 +1,6 @@
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
<Metadata>
<Identity Id="Telegram.Td.UWP" Version="1.8.3" Language="en-US" Publisher="Telegram LLC" />
<Identity Id="Telegram.Td.UWP" Version="1.8.4" Language="en-US" Publisher="Telegram LLC" />
<DisplayName>TDLib for Universal Windows Platform</DisplayName>
<Description>TDLib is a library for building Telegram clients</Description>
<MoreInfo>https://core.telegram.org/tdlib</MoreInfo>

View File

@ -78,7 +78,7 @@ documentAttributeAudio45#ded218e0 duration:int title:string performer:string = D
decryptedMessage46#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage;
decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
decryptedMessageMediaDocument46#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
@ -117,6 +117,10 @@ messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
// layer 143
decryptedMessageMediaDocument#6abd9782 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:long key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
---functions---
test.dummyFunction = Bool;

View File

@ -142,7 +142,7 @@ temporaryPasswordState has_password:Bool valid_for:int32 = TemporaryPasswordStat
//@download_offset Download will be started from this offset. downloaded_prefix_size is calculated from this offset
//@downloaded_prefix_size If is_downloading_completed is false, then only some prefix of the file starting from download_offset is ready to be read. downloaded_prefix_size is the size of that prefix in bytes
//@downloaded_size Total downloaded file size, in bytes. Can be used only for calculating download progress. The actual file size may be bigger, and some parts of it may contain garbage
localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_active:Bool is_downloading_completed:Bool download_offset:int32 downloaded_prefix_size:int32 downloaded_size:int32 = LocalFile;
localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_active:Bool is_downloading_completed:Bool download_offset:int53 downloaded_prefix_size:int53 downloaded_size:int53 = LocalFile;
//@description Represents a remote file
//@id Remote file identifier; may be empty. Can be used by the current user across application restarts or even from other devices. Uniquely identifies a file, but a file can have a lot of different valid identifiers.
@ -152,7 +152,7 @@ localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_
//@is_uploading_active True, if the file is currently being uploaded (or a remote copy is being generated by some other means)
//@is_uploading_completed True, if a remote copy is fully available
//@uploaded_size Size of the remote available part of the file, in bytes; 0 if unknown
remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_completed:Bool uploaded_size:int32 = RemoteFile;
remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_completed:Bool uploaded_size:int53 = RemoteFile;
//@description Represents a file
//@id Unique file identifier
@ -160,7 +160,7 @@ remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_comp
//@expected_size Approximate file size in bytes in case the exact file size is unknown. Can be used to show download/upload progress
//@local Information about the local copy of the file
//@remote Information about the remote copy of the file
file id:int32 size:int32 expected_size:int32 local:localFile remote:remoteFile = File;
file id:int32 size:int53 expected_size:int53 local:localFile remote:remoteFile = File;
//@class InputFile @description Points to a file
@ -179,7 +179,7 @@ inputFileLocal path:string = InputFile;
//@description A file generated by the application @original_path Local path to a file from which the file is generated; may be empty if there is no such file
//@conversion String specifying the conversion applied to the original file; must be persistent across application restarts. Conversions beginning with '#' are reserved for internal TDLib usage
//@expected_size Expected size of the generated file, in bytes; 0 if unknown
inputFileGenerated original_path:string conversion:string expected_size:int32 = InputFile;
inputFileGenerated original_path:string conversion:string expected_size:int53 = InputFile;
//@description Describes an image in JPEG format @type Image type (see https://core.telegram.org/constructor/photoSize)
@ -296,8 +296,8 @@ photo has_stickers:Bool minithumbnail:minithumbnail sizes:vector<photoSize> = Ph
//@description Describes a sticker @set_id The identifier of the sticker set to which the sticker belongs; 0 if none @width Sticker width; as defined by the sender @height Sticker height; as defined by the sender
//@emoji Emoji corresponding to the sticker @type Sticker type @outline Sticker's outline represented as a list of closed vector paths; may be empty. The coordinate system origin is in the upper-left corner
//@thumbnail Sticker thumbnail in WEBP or JPEG format; may be null @sticker File containing the sticker
sticker set_id:int64 width:int32 height:int32 emoji:string type:StickerType outline:vector<closedVectorPath> thumbnail:thumbnail sticker:file = Sticker;
//@thumbnail Sticker thumbnail in WEBP or JPEG format; may be null @premium_animation Premium animation of the sticker; may be null. If present, only Premium users can send the sticker @sticker File containing the sticker
sticker set_id:int64 width:int32 height:int32 emoji:string type:StickerType outline:vector<closedVectorPath> thumbnail:thumbnail premium_animation:file sticker:file = Sticker;
//@description Describes a video file @duration Duration of the video, in seconds; as defined by the sender @width Video width; as defined by the sender @height Video height; as defined by the sender
//@file_name Original name of the file; as defined by the sender @mime_type MIME type of the file; as defined by the sender
@ -311,9 +311,11 @@ video duration:int32 width:int32 height:int32 file_name:string mime_type:string
//@thumbnail Video thumbnail in JPEG format; as defined by the sender; may be null @video File containing the video
videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:thumbnail video:file = VideoNote;
//@description Describes a voice note. The voice note must be encoded with the Opus codec, and stored inside an OGG container. Voice notes can have only a single audio channel @duration Duration of the voice note, in seconds; as defined by the sender
//@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 a voice note. The voice note must be encoded with the Opus codec, and stored inside an OGG container. Voice notes can have only a single audio channel
//@duration Duration of the voice note, in seconds; as defined by the sender @waveform A waveform representation of the voice note in 5-bit format
//@mime_type MIME type of the file; as defined by the sender @is_recognized True, if speech recognition is completed; Premium users only
//@recognized_text Recognized text of the voice note; Premium users only. Call recognizeSpeech to get recognized text of the voice note @voice File containing the voice note
voiceNote duration:int32 waveform:bytes mime_type:string is_recognized:Bool recognized_text:string voice:file = VoiceNote;
//@description Describes an animated representation of an emoji
//@sticker Animated sticker for the emoji
@ -419,8 +421,9 @@ animatedChatPhoto length:int32 file:file main_frame_timestamp:double = AnimatedC
//@added_date Point in time (Unix timestamp) when the photo has been added
//@minithumbnail Photo minithumbnail; may be null
//@sizes Available variants of the photo in JPEG format, in different size
//@animation Animated variant of the photo in MPEG4 format; may be null
chatPhoto id:int64 added_date:int32 minithumbnail:minithumbnail sizes:vector<photoSize> animation:animatedChatPhoto = ChatPhoto;
//@animation A big (640x640) animated variant of the photo in MPEG4 format; may be null
//@small_animation A small (160x160) animated variant of the photo in MPEG4 format; may be null even the big animation is available
chatPhoto id:int64 added_date:int32 minithumbnail:minithumbnail sizes:vector<photoSize> animation:animatedChatPhoto small_animation:animatedChatPhoto = ChatPhoto;
//@description Contains a list of chat or user profile photos @total_count Total number of photos @photos List of photos
chatPhotos total_count:int32 photos:vector<chatPhoto> = ChatPhotos;
@ -477,6 +480,7 @@ chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messa
//@is_contact The user is a contact of the current user
//@is_mutual_contact The user is a contact of the current user and the current user is a contact of the user
//@is_verified True, if the user is verified
//@is_premium True, if the user is a Telegram Premium user
//@is_support True, if the user is Telegram support account
//@restriction_reason If non-empty, it contains a human-readable description of the reason why access to this user must be restricted
//@is_scam True, if many users reported this user as a scam
@ -484,16 +488,20 @@ chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messa
//@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. Identifier of the user can't be passed to any method except GetUser
//@type Type of the user
//@language_code IETF language tag of the user's language; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string = User;
//@added_to_attachment_menu True, if the user added the current bot to attachment menu; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
//@description Contains information about a bot
//@share_text The text that is shown on the bot's profile page and is sent together with the link when users share the bot
//@param_description The text shown in the chat with the bot if the chat is empty
//@photo Photo shown in the chat with the bot if the chat is empty; may be null
//@animation Animation shown in the chat with the bot if the chat is empty; may be null
//@menu_button Information about a button to show instead of the bot commands menu button; may be null if ordinary bot commands menu must be shown
//@commands List of the bot commands
//@default_group_administrator_rights Default administrator rights for adding the bot to basic group and supergroup chats; may be null
//@default_channel_administrator_rights Default administrator rights for adding the bot to channels; may be null
botInfo share_text:string description:string menu_button:botMenuButton commands:vector<botCommand> default_group_administrator_rights:chatAdministratorRights default_channel_administrator_rights:chatAdministratorRights = BotInfo;
botInfo share_text:string description:string photo:photo animation:animation menu_button:botMenuButton commands:vector<botCommand> default_group_administrator_rights:chatAdministratorRights default_channel_administrator_rights:chatAdministratorRights = BotInfo;
//@description Contains full information about a user
//@photo User profile photo; may be null
@ -503,10 +511,10 @@ botInfo share_text:string description:string menu_button:botMenuButton commands:
//@has_private_calls True, if the user can't be called due to their privacy settings
//@has_private_forwards True, if the user can't be linked in forwarded messages due to their privacy settings
//@need_phone_number_privacy_exception True, if the current user needs to explicitly allow to share their phone number with the user when the method addContact is used
//@bio A short user bio
//@bio A short user bio; may be null for bots
//@group_in_common_count Number of group chats where both the other user and the current user are a member; 0 for the current user
//@bot_info For bots, information about the bot; may be null
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool need_phone_number_privacy_exception:Bool bio:string group_in_common_count:int32 bot_info:botInfo = UserFullInfo;
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool need_phone_number_privacy_exception:Bool bio:formattedText group_in_common_count:int32 bot_info:botInfo = UserFullInfo;
//@description Represents a list of users @total_count Approximate total number of users found @user_ids A list of user identifiers
users total_count:int32 user_ids:vector<int53> = Users;
@ -695,6 +703,8 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb
//@has_linked_chat True, if the channel has a discussion group, or the supergroup is the designated discussion group for a channel
//@has_location True, if the supergroup is connected to a location, i.e. the supergroup is a location-based supergroup
//@sign_messages True, if messages sent to the channel need to contain information about the sender. This field is only applicable to channels
//@join_to_send_messages True, if users need to join the supergroup before they can send messages. Always true for channels and non-discussion supergroups
//@join_by_request True, if all users directly joining the supergroup need to be approved by supergroup administrators. Always false for channels and supergroups without username, location, or a linked chat
//@is_slow_mode_enabled True, if the slow mode is enabled in the supergroup
//@is_channel True, if the supergroup is a channel
//@is_broadcast_group True, if the supergroup is a broadcast group, i.e. only administrators can send messages and there is no limit on the number of members
@ -702,7 +712,7 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted
//@is_scam True, if many users reported this supergroup or channel as a scam
//@is_fake True, if many users reported this supergroup or channel as a fake account
supergroup id:int53 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
supergroup id:int53 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool join_to_send_messages:Bool join_by_request:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
//@description Contains full information about a supergroup or channel
//@photo Chat photo; may be null
@ -748,7 +758,7 @@ secretChatStateClosed = SecretChatState;
//@is_outbound True, if the chat was created by the current user; otherwise false
//@key_hash Hash of the currently used key for comparison with the hash of the chat partner's key. This is a string of 36 little-endian bytes, which must be split into groups of 2 bits, each denoting a pixel of one of 4 colors FFFFFF, D5E6F3, 2D5775, and 2F99C9.
//-The pixels must be used to make a 12x12 square image filled from left to right, top to bottom. Alternatively, the first 32 bytes of the hash can be converted to the hexadecimal format and printed as 32 2-digit hex numbers
//@layer Secret chat layer; determines features supported by the chat partner's application. Nested text entities and underline and strikethrough entities are supported if the layer >= 101
//@layer Secret chat layer; determines features supported by the chat partner's application. Nested text entities and underline and strikethrough entities are supported if the layer >= 101, files bigger than 2000MB are supported if the layer >= 143
secretChat id:int32 user_id:int53 state:SecretChatState is_outbound:Bool key_hash:bytes layer:int32 = SecretChat;
@ -897,11 +907,12 @@ messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalen
//@description Describes a sponsored message
//@message_id Message identifier; unique for the chat to which the sponsored message belongs among both ordinary and sponsored messages
//@is_recommended True, if the message needs to be labeled as "recommended" instead of "sponsored"
//@sponsor_chat_id Sponsor chat identifier; 0 if the sponsor chat is accessible through an invite link
//@sponsor_chat_info Information about the sponsor chat; may be null unless sponsor_chat_id == 0
//@link An internal link to be opened when the sponsored message is clicked; may be null if the sponsor chat needs to be opened instead
//@content Content of the message. Currently, can be only of the type messageText
sponsoredMessage message_id:int53 sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
sponsoredMessage message_id:int53 is_recommended:Bool sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
//@description Describes a file added to file download list
@ -980,9 +991,9 @@ chatTypeSecret secret_chat_id:int32 user_id:int53 = ChatType;
//@title The title of the filter; 1-12 characters without line feeds
//@icon_name The chosen icon name for short filter representation. If non-empty, must be one of "All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom", "Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love", "Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work".
//-If empty, use getChatFilterDefaultIconName to get default icon name for the filter
//@pinned_chat_ids The chat identifiers of pinned chats in the filtered chat list
//@included_chat_ids The chat identifiers of always included chats in the filtered chat list
//@excluded_chat_ids The chat identifiers of always excluded chats in the filtered chat list
//@pinned_chat_ids The chat identifiers of pinned chats in the filtered chat list. There can be up to GetOption("chat_filter_chosen_chat_count_max") pinned and always included non-secret chats and the same number of secret chats, but the limit can be increased with Telegram Premium
//@included_chat_ids The chat identifiers of always included chats in the filtered chat list. There can be up to GetOption("chat_filter_chosen_chat_count_max") pinned and always included non-secret chats and the same number of secret chats, but the limit can be increased with Telegram Premium
//@excluded_chat_ids The chat identifiers of always excluded chats in the filtered chat list. There can be up to GetOption("chat_filter_chosen_chat_count_max") always excluded non-secret chats and the same number of secret chats, but the limit can be increased with Telegram Premium
//@exclude_muted True, if muted chats need to be excluded
//@exclude_read True, if read chats need to be excluded
//@exclude_archived True, if archived chats need to be excluded
@ -1143,7 +1154,7 @@ keyboardButtonTypeRequestLocation = KeyboardButtonType;
//@description A button that allows the user to create and send a poll when pressed; available only in private chats @force_regular If true, only regular polls must be allowed to create @force_quiz If true, only polls in quiz mode must be allowed to create
keyboardButtonTypeRequestPoll force_regular:Bool force_quiz:Bool = KeyboardButtonType;
//@description A button that opens a web app by calling getWebAppUrl @url An HTTP URL to pass to getWebAppUrl
//@description A button that opens a Web App by calling getWebAppUrl @url An HTTP URL to pass to getWebAppUrl
keyboardButtonTypeWebApp url:string = KeyboardButtonType;
@ -1159,7 +1170,7 @@ inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType;
//@description A button that opens a specified URL and automatically authorize the current user by calling getLoginUrlInfo @url An HTTP URL to pass to getLoginUrlInfo @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages
inlineKeyboardButtonTypeLoginUrl url:string id:int53 forward_text:string = InlineKeyboardButtonType;
//@description A button that opens a web app by calling openWebApp @url An HTTP URL to pass to openWebApp
//@description A button that opens a Web App by calling openWebApp @url An HTTP URL to pass to openWebApp
inlineKeyboardButtonTypeWebApp url:string = InlineKeyboardButtonType;
//@description A button that sends a callback query to a bot @data Data to be sent to the bot via a callback query
@ -1219,7 +1230,7 @@ loginUrlInfoOpen url:string skip_confirm:Bool = LoginUrlInfo;
loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int53 request_write_access:Bool = LoginUrlInfo;
//@description Contains information about a web app @launch_id Unique identifier for the web app launch @url A web app URL to open in a web view
//@description Contains information about a Web App @launch_id Unique identifier for the Web App launch @url A Web App URL to open in a web view
webAppInfo launch_id:int64 url:string = WebAppInfo;
@ -1481,19 +1492,21 @@ bankCardInfo title:string actions:vector<bankCardActionOpenUrl> = BankCardInfo;
address country_code:string state:string city:string street_line1:string street_line2:string postal_code:string = Address;
//@description Contains parameters of the app theme @background_color A color of the background in the RGB24 format @text_color A color of text in the RGB24 format
//@hint_color A color of hints in the RGB24 format @link_color A color of links in the RGB24 format @button_color A color of the buttons in the RGB24 format
//@description Contains parameters of the application theme @background_color A color of the background in the RGB24 format @secondary_background_color A secondary color for the background in the RGB24 format
//@text_color A color of text in the RGB24 format @hint_color A color of hints in the RGB24 format @link_color A color of links in the RGB24 format @button_color A color of the buttons in the RGB24 format
//@button_text_color A color of text on the buttons in the RGB24 format
themeParameters background_color:int32 text_color:int32 hint_color:int32 link_color:int32 button_color:int32 button_text_color:int32 = ThemeParameters;
themeParameters background_color:int32 secondary_background_color:int32 text_color:int32 hint_color:int32 link_color:int32 button_color:int32 button_text_color:int32 = ThemeParameters;
//@description Portion of the price of a product (e.g., "delivery cost", "tax amount") @label Label for this portion of the product price @amount Currency amount in the smallest units of the currency
labeledPricePart label:string amount:int53 = LabeledPricePart;
//@description Product invoice @currency ISO 4217 currency code
//@description Product invoice
//@currency ISO 4217 currency code
//@price_parts A list of objects used to calculate the total price of the product
//@max_tip_amount The maximum allowed amount of tip in the smallest units of the currency
//@suggested_tip_amounts Suggested amounts of tip in the smallest units of the currency
//@recurring_payment_terms_of_service_url An HTTP URL with terms of service for recurring payments. If non-empty, the invoice payment will result in recurring payments and the user must accept the terms of service before allowed to pay
//@is_test True, if the payment is a test payment
//@need_name True, if the user's name is needed for payment
//@need_phone_number True, if the user's phone number is needed for payment
@ -1502,7 +1515,7 @@ labeledPricePart label:string amount:int53 = LabeledPricePart;
//@send_phone_number_to_provider True, if the user's phone number will be sent to the provider
//@send_email_address_to_provider True, if the user's email address will be sent to the provider
//@is_flexible True, if the total price depends on the shipping method
invoice currency:string price_parts:vector<labeledPricePart> max_tip_amount:int53 suggested_tip_amounts:vector<int53> is_test:Bool need_name:Bool need_phone_number:Bool need_email_address:Bool need_shipping_address:Bool send_phone_number_to_provider:Bool send_email_address_to_provider:Bool is_flexible:Bool = Invoice;
invoice currency:string price_parts:vector<labeledPricePart> max_tip_amount:int53 suggested_tip_amounts:vector<int53> recurring_payment_terms_of_service_url:string is_test:Bool need_name:Bool need_phone_number:Bool need_email_address:Bool need_shipping_address:Bool send_phone_number_to_provider:Bool send_email_address_to_provider:Bool is_flexible:Bool = Invoice;
//@description Order information @name Name of the user @phone_number Phone number of the user @email_address Email address of the user @shipping_address Shipping address for this order; may be null
orderInfo name:string phone_number:string email_address:string shipping_address:address = OrderInfo;
@ -1513,6 +1526,7 @@ shippingOption id:string title:string price_parts:vector<labeledPricePart> = Shi
//@description Contains information about saved card credentials @id Unique identifier of the saved credentials @title Title of the saved credentials
savedCredentials id:string title:string = SavedCredentials;
//@class InputCredentials @description Contains information about the payment method chosen by the user
//@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
@ -1527,21 +1541,33 @@ inputCredentialsApplePay data:string = InputCredentials;
//@description Applies if a user enters new credentials using Google Pay @data JSON-encoded data with the credential identifier
inputCredentialsGooglePay data:string = InputCredentials;
//@class PaymentProvider @description Contains information about a payment provider
//@description Smart Glocal payment provider @public_token Public payment token
paymentProviderSmartGlocal public_token:string = PaymentProvider;
//@description Stripe payment provider @publishable_key Stripe API publishable key @need_country True, if the user country must be provided @need_postal_code True, if the user ZIP/postal code must be provided @need_cardholder_name True, if the cardholder name must be provided
paymentsProviderStripe publishable_key:string need_country:Bool need_postal_code:Bool need_cardholder_name:Bool = PaymentsProviderStripe;
paymentProviderStripe publishable_key:string need_country:Bool need_postal_code:Bool need_cardholder_name:Bool = PaymentProvider;
//@description Some other payment provider, for which a web payment form must be shown @url Payment form URL
paymentProviderOther url:string = PaymentProvider;
//@description Contains information about an invoice payment form
//@id The payment form identifier
//@invoice Full information of the invoice
//@url Payment form URL
//@invoice Full information about the invoice
//@seller_bot_user_id User identifier of the seller bot
//@payments_provider_user_id User identifier of the payment provider bot
//@payments_provider Information about the payment provider, if available, to support it natively without the need for opening the URL; may be null
//@payment_provider_user_id User identifier of the payment provider bot
//@payment_provider Information about the payment provider
//@saved_order_info Saved server-side order information; 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;
//@product_title Product title
//@product_description Product description
//@product_photo Product photo; may be null
paymentForm id:int64 invoice:invoice seller_bot_user_id:int53 payment_provider_user_id:int53 payment_provider:PaymentProvider saved_order_info:orderInfo saved_credentials:savedCredentials can_save_credentials:Bool need_password:Bool product_title:string product_description:formattedText product_photo:photo = PaymentForm;
//@description Contains a temporary identifier of validated order information, which is stored for one hour. Also contains the available shipping options @order_info_id Temporary identifier of the order information @shipping_options Available shipping options
validatedOrderInfo order_info_id:string shipping_options:vector<shippingOption> = ValidatedOrderInfo;
@ -1555,13 +1581,22 @@ paymentResult success:Bool verification_url:string = PaymentResult;
//@photo Product photo; may be null
//@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
//@payment_provider_user_id User identifier of the payment provider bot
//@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
//@tip_amount The amount of tip chosen by the buyer in the smallest units of the currency
paymentReceipt title:string description:string photo:photo date:int32 seller_bot_user_id:int53 payments_provider_user_id:int53 invoice:invoice order_info:orderInfo shipping_option:shippingOption credentials_title:string tip_amount:int53 = PaymentReceipt;
paymentReceipt title:string description:formattedText photo:photo date:int32 seller_bot_user_id:int53 payment_provider_user_id:int53 invoice:invoice order_info:orderInfo shipping_option:shippingOption credentials_title:string tip_amount:int53 = PaymentReceipt;
//@class InputInvoice @description Describe an invoice to process
//@description An invoice from a message of the type messageInvoice @chat_id Chat identifier of the message @message_id Message identifier
inputInvoiceMessage chat_id:int53 message_id:int53 = InputInvoice;
//@description An invoice from a link of the type internalLinkTypeInvoice @name Name of the invoice
inputInvoiceName name:string = InputInvoice;
//@description File with the date it was uploaded @file The file @date Point in time (Unix timestamp) when the file was uploaded
@ -1834,8 +1869,8 @@ messagePhoto photo:photo caption:formattedText is_secret:Bool = MessageContent;
//@description An expired photo message (self-destructed after TTL has elapsed)
messageExpiredPhoto = MessageContent;
//@description A sticker message @sticker The sticker description
messageSticker sticker:sticker = MessageContent;
//@description A sticker message @sticker The sticker description @is_premium True, if premium animation of the sticker must be played
messageSticker sticker:sticker is_premium:Bool = MessageContent;
//@description A video message @video The video description @caption Video caption @is_secret True, if the video thumbnail must be blurred and the video must be shown only while tapped
messageVideo video:video caption:formattedText is_secret:Bool = MessageContent;
@ -1881,7 +1916,7 @@ messagePoll poll:poll = MessageContent;
//@description A message with an invoice from a bot @title Product title @param_description Product description @photo Product photo; may be null @currency Currency for the product price @total_amount Product total price in the smallest units of the currency
//@start_parameter Unique invoice bot start_parameter. To share an invoice use the URL https://t.me/{bot_username}?start={start_parameter} @is_test True, if the invoice is a test invoice
//@need_shipping_address True, if the shipping address must be specified @receipt_message_id The identifier of the message with the receipt, after the product has been purchased
messageInvoice title:string description:string photo:photo currency:string total_amount:int53 start_parameter:string is_test:Bool need_shipping_address:Bool receipt_message_id:int53 = MessageContent;
messageInvoice title:string description:formattedText photo:photo currency:string total_amount:int53 start_parameter:string is_test:Bool need_shipping_address:Bool receipt_message_id:int53 = MessageContent;
//@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;
@ -1949,13 +1984,16 @@ messageCustomServiceAction text:string = MessageContent;
//@description A new high score was achieved in a game @game_message_id Identifier of the message with the game, can be an identifier of a deleted message @game_id Identifier of the game; may be different from the games presented in the message with the game @score New score
messageGameScore game_message_id:int53 game_id:int64 score:int32 = MessageContent;
//@description A payment has been completed @invoice_chat_id Identifier of the chat, containing the corresponding invoice message; 0 if unknown @invoice_message_id Identifier of the message with the corresponding invoice; can be an identifier of a deleted message @currency Currency for the price of the product @total_amount Total price for the product, in the smallest units of the currency
messagePaymentSuccessful invoice_chat_id:int53 invoice_message_id:int53 currency:string total_amount:int53 = MessageContent;
//@description A payment has been completed @invoice_chat_id Identifier of the chat, containing the corresponding invoice message; 0 if unknown @invoice_message_id Identifier of the message with the corresponding invoice; can be 0 or an identifier of a deleted message
//@currency Currency for the price of the product @total_amount Total price for the product, in the smallest units of the currency
//@is_recurring True, if this is a recurring payment @is_first_recurring True, if this is the first recurring payment @invoice_name Name of the invoice; may be empty if unknown
messagePaymentSuccessful invoice_chat_id:int53 invoice_message_id:int53 currency:string total_amount:int53 is_recurring:Bool is_first_recurring:Bool invoice_name:string = MessageContent;
//@description A payment has been completed; for bots only @currency Currency for price of the product
//@total_amount Total price for the product, in the smallest units of the currency @invoice_payload Invoice payload @shipping_option_id Identifier of the shipping option chosen by the user; may be empty if not applicable @order_info Information about the order; may be null
//@description A payment has been completed; for bots only @currency Currency for price of the product @total_amount Total price for the product, in the smallest units of the currency
//@is_recurring True, if this is a recurring payment @is_first_recurring True, if this is the first recurring payment
//@invoice_payload Invoice payload @shipping_option_id Identifier of the shipping option chosen by the user; may be empty if not applicable @order_info Information about the order; may be null
//@telegram_payment_charge_id Telegram payment identifier @provider_payment_charge_id Provider payment identifier
messagePaymentSuccessfulBot currency:string total_amount:int53 invoice_payload:bytes shipping_option_id:string order_info:orderInfo telegram_payment_charge_id:string provider_payment_charge_id:string = MessageContent;
messagePaymentSuccessfulBot currency:string total_amount:int53 is_recurring:Bool is_first_recurring:Bool invoice_payload:bytes shipping_option_id:string order_info:orderInfo telegram_payment_charge_id:string provider_payment_charge_id:string = MessageContent;
//@description A contact has registered with Telegram
messageContactRegistered = MessageContent;
@ -1963,10 +2001,10 @@ messageContactRegistered = MessageContent;
//@description The current user has connected a website by logging in using Telegram Login Widget on it @domain_name Domain name of the connected website
messageWebsiteConnected domain_name:string = MessageContent;
//@description Data from a web app has been sent to a bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app
//@description Data from a Web App has been sent to a bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the Web App
messageWebAppDataSent button_text:string = MessageContent;
//@description Data from a web app has been received; for bots only @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app @data Received data
//@description Data from a Web App has been received; for bots only @button_text Text of the keyboardButtonTypeWebApp button, which opened the Web App @data Received data
messageWebAppDataReceived button_text:string data:string = MessageContent;
//@description Telegram Passport data has been sent to a bot @types List of Telegram Passport element types sent
@ -2089,7 +2127,7 @@ inputMessageAudio audio:InputFile album_cover_thumbnail:inputThumbnail duration:
//@description A document message (general file) @document Document to be sent @thumbnail Document thumbnail; pass null to skip thumbnail uploading @disable_content_type_detection If true, automatic file type detection will be disabled and the document will be always sent as file. Always true for files sent to secret chats @caption Document caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
inputMessageDocument document:InputFile thumbnail:inputThumbnail disable_content_type_detection:Bool caption:formattedText = InputMessageContent;
//@description A photo message @photo Photo to send @thumbnail Photo thumbnail to be sent; pass null to skip thumbnail uploading. The thumbnail is sent to the other party only in secret chats @added_sticker_file_ids File identifiers of the stickers added to the photo, if applicable @width Photo width @height Photo height @caption Photo caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
//@description A photo message @photo Photo to send. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20 @thumbnail Photo thumbnail to be sent; pass null to skip thumbnail uploading. The thumbnail is sent to the other party only in secret chats @added_sticker_file_ids File identifiers of the stickers added to the photo, if applicable @width Photo width @height Photo height @caption Photo caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
//@ttl Photo TTL (Time To Live), in seconds (0-60). A non-zero TTL can be specified only in private chats
inputMessagePhoto photo:InputFile thumbnail:inputThumbnail added_sticker_file_ids:vector<int32> width:int32 height:int32 caption:formattedText ttl:int32 = InputMessageContent;
@ -2291,6 +2329,9 @@ stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail thumbnail_o
//@description Represents a list of sticker sets @total_count Approximate total number of sticker sets found @sets List of sticker sets
stickerSets total_count:int32 sets:vector<stickerSetInfo> = StickerSets;
//@description Represents a list of trending sticker sets @total_count Approximate total number of trending sticker sets @sets List of trending sticker sets @is_premium True, if the list contains sticker sets with premium stickers
trendingStickerSets total_count:int32 sets:vector<stickerSetInfo> is_premium:Bool = TrendingStickerSets;
//@class CallDiscardReason @description Describes the reason why a call was discarded
@ -2321,8 +2362,8 @@ callProtocol udp_p2p:Bool udp_reflector:Bool min_layer:int32 max_layer:int32 lib
//@class CallServerType @description Describes the type of a call server
//@description A Telegram call reflector @peer_tag A peer tag to be used with the reflector
callServerTypeTelegramReflector peer_tag:bytes = CallServerType;
//@description A Telegram call reflector @peer_tag A peer tag to be used with the reflector @is_tcp True, if the server uses TCP instead of UDP
callServerTypeTelegramReflector peer_tag:bytes is_tcp:Bool = CallServerType;
//@description A WebRTC server @username Username to be used for authentication @password Authentication password @supports_turn True, if the server supports TURN @supports_stun True, if the server supports STUN
callServerTypeWebrtc username:string password:string supports_turn:Bool supports_stun:Bool = CallServerType;
@ -2490,14 +2531,18 @@ addedReaction reaction:string sender_id:MessageSender = AddedReaction;
//@description Represents a list of reactions added to a message @total_count The total number of found reactions @reactions The list of added reactions @next_offset The offset for the next request. If empty, there are no more results
addedReactions total_count:int32 reactions:vector<addedReaction> next_offset:string = AddedReactions;
//@description Represents an available reaction @reaction Text representation of the reaction @needs_premium True, if Telegram Premium is needed to send the reaction
availableReaction reaction:string needs_premium:Bool = AvailableReaction;
//@description Represents a list of available reactions @reactions List of reactions
availableReactions reactions:vector<string> = AvailableReactions;
availableReactions reactions:vector<availableReaction> = AvailableReactions;
//@description Contains stickers which must be used for reaction animation rendering
//@reaction Text representation of the reaction
//@title Reaction title
//@is_active True, if the reaction can be added to new messages and enabled in chats
//@is_premium True, if the reaction is available only for Premium users
//@static_icon Static icon for the reaction
//@appear_animation Appear animation for the reaction
//@select_animation Select animation for the reaction
@ -2505,7 +2550,7 @@ availableReactions reactions:vector<string> = AvailableReactions;
//@effect_animation Effect animation for the reaction
//@around_animation Around animation for the reaction; may be null
//@center_animation Center animation for the reaction; may be null
reaction reaction:string title:string is_active:Bool static_icon:sticker appear_animation:sticker select_animation:sticker activate_animation:sticker effect_animation:sticker around_animation:sticker center_animation:sticker = Reaction;
reaction reaction:string title:string is_active:Bool is_premium:Bool static_icon:sticker appear_animation:sticker select_animation:sticker activate_animation:sticker effect_animation:sticker around_animation:sticker center_animation:sticker = Reaction;
//@description Represents a list of animations @animations List of animations
@ -2536,6 +2581,12 @@ attachmentMenuBotColor light_color:int32 dark_color:int32 = AttachmentMenuBotCol
//@description Represents a bot added to attachment menu
//@bot_user_id User identifier of the bot added to attachment menu
//@supports_self_chat True, if the bot supports opening from attachment menu in the chat with the bot
//@supports_user_chats True, if the bot supports opening from attachment menu in private chats with ordinary users
//@supports_bot_chats True, if the bot supports opening from attachment menu in private chats with other bots
//@supports_group_chats True, if the bot supports opening from attachment menu in basic group and supergroup chats
//@supports_channel_chats True, if the bot supports opening from attachment menu in channel chats
//@supports_settings True, if the bot supports "settings_button_pressed" event
//@name Name for the bot in attachment menu
//@name_color Color to highlight selected name of the bot if appropriate; may be null
//@default_icon Default attachment menu icon for the bot in SVG format; may be null
@ -2544,7 +2595,8 @@ attachmentMenuBotColor light_color:int32 dark_color:int32 = AttachmentMenuBotCol
//@android_icon Attachment menu icon for the bot in TGS format for the official Android app; may be null
//@macos_icon Attachment menu icon for the bot in TGS format for the official native macOS app; may be null
//@icon_color Color to highlight selected icon of the bot if appropriate; may be null
attachmentMenuBot bot_user_id:int53 name:string name_color:attachmentMenuBotColor default_icon:file ios_static_icon:file ios_animated_icon:file android_icon:file macos_icon:file icon_color:attachmentMenuBotColor = AttachmentMenuBot;
//@web_app_placeholder Default placeholder for opened Web Apps in SVG format; may be null
attachmentMenuBot bot_user_id:int53 supports_self_chat:Bool supports_user_chats:Bool supports_bot_chats:Bool supports_group_chats:Bool supports_channel_chats:Bool supports_settings:Bool name:string name_color:attachmentMenuBotColor default_icon:file ios_static_icon:file ios_animated_icon:file android_icon:file macos_icon:file icon_color:attachmentMenuBotColor web_app_placeholder:file = AttachmentMenuBot;
//@description Information about the message sent by answerWebAppQuery @inline_message_id Identifier of the sent inline message, if known
sentWebAppMessage inline_message_id:string = SentWebAppMessage;
@ -2864,6 +2916,109 @@ languagePackInfo id:string base_language_pack_id:string name:string native_name:
localizationTargetInfo language_packs:vector<languagePackInfo> = LocalizationTargetInfo;
//@class PremiumLimitType @description Describes type of a limit, increased for Premium users
//@description The maximum number of joined supergroups and channels
premiumLimitTypeSupergroupCount = PremiumLimitType;
//@description The maximum number of pinned chats in the main chat list
premiumLimitTypePinnedChatCount = PremiumLimitType;
//@description The maximum number of created public chats
premiumLimitTypeCreatedPublicChatCount = PremiumLimitType;
//@description The maximum number of saved animations
premiumLimitTypeSavedAnimationCount = PremiumLimitType;
//@description The maximum number of favorite stickers
premiumLimitTypeFavoriteStickerCount = PremiumLimitType;
//@description The maximum number of chat filters
premiumLimitTypeChatFilterCount = PremiumLimitType;
//@description The maximum number of pinned and always included, or always excluded chats in a chat filter
premiumLimitTypeChatFilterChosenChatCount = PremiumLimitType;
//@description The maximum number of pinned chats in the archive chat list
premiumLimitTypePinnedArchivedChatCount = PremiumLimitType;
//@description The maximum length of sent media caption
premiumLimitTypeCaptionLength = PremiumLimitType;
//@description The maximum length of the user's bio
premiumLimitTypeBioLength = PremiumLimitType;
//@class PremiumFeature @description Describes a feature available to Premium users
//@description Increased limits
premiumFeatureIncreasedLimits = PremiumFeature;
//@description Increased maximum upload file size
premiumFeatureIncreasedUploadFileSize = PremiumFeature;
//@description Improved download speed
premiumFeatureImprovedDownloadSpeed = PremiumFeature;
//@description The ability to convert voice notes to text
premiumFeatureVoiceRecognition = PremiumFeature;
//@description Disabled ads
premiumFeatureDisabledAds = PremiumFeature;
//@description Allowed to use more reactions
premiumFeatureUniqueReactions = PremiumFeature;
//@description Allowed to use premium stickers with unique effects
premiumFeatureUniqueStickers = PremiumFeature;
//@description Ability to change position of the main chat list, archive and mute all new chats from non-contacts, and completely disable notifications about the user's contacts joined Telegram
premiumFeatureAdvancedChatManagement = PremiumFeature;
//@description A badge in the user's profile
premiumFeatureProfileBadge = PremiumFeature;
//@description Profile photo animation on message and chat screens
premiumFeatureAnimatedProfilePhoto = PremiumFeature;
//@description Allowed to set a premium appllication icons
premiumFeatureAppIcons = PremiumFeature;
//@description Contains information about a limit, increased for Premium users @type The type of the limit @default_value Default value of the limit @premium_value Value of the limit for Premium users
premiumLimit type:PremiumLimitType default_value:int32 premium_value:int32 = PremiumLimit;
//@description Contains information about features, available to Premium users @features The list of available features @limits The list of limits, increased for Premium users
//@payment_link An internal link to be opened to pay for Telegram Premium if store payment isn't possible; may be null if direct payment isn't available. If the link has type internalLinkTypeBotStart, then sendBotStartMessage must be called automatically
premiumFeatures features:vector<PremiumFeature> limits:vector<premiumLimit> payment_link:InternalLinkType = PremiumFeatures;
//@class PremiumSource @description Describes a source from which the Premium features screen is opened
//@description A limit was exceeded @limit_type Type of the exceeded limit
premiumSourceLimitExceeded limit_type:PremiumLimitType = PremiumSource;
//@description A user tried to use a Premium feature @feature The used feature
premiumSourceFeature feature:PremiumFeature = PremiumSource;
//@description A user opened an internal link of the type internalLinkTypePremiumFeatures @referrer The referrer from the link
premiumSourceLink referrer:string = PremiumSource;
//@description A user opened the Premium features screen from settings
premiumSourceSettings = PremiumSource;
//@description Describes a promotion animation for a Premium feature @feature Premium feature @animation Promotion animation for the feature
premiumFeaturePromotionAnimation feature:PremiumFeature animation:animation = PremiumFeaturePromotionAnimation;
//@description Contains state of Telegram Premium subscription and promotion videos for Premium features
//@state Text description of the state of the current Premium subscription; may be empty if the current user has no Telegram Premium subscription
//@currency ISO 4217 currency code for Telegram Premium subscription payment
//@monthly_amount Monthly subscription payment for Telegram Premium subscription, in the smallest units of the currency
//@animations The list of available promotion animations for Premium features
premiumState state:formattedText currency:string monthly_amount:int53 animations:vector<premiumFeaturePromotionAnimation> = PremiumState;
//@class DeviceToken @description Represents a data needed to subscribe for push notifications through registerDevice method. To use specific push notification service, the correct application platform must be specified and a valid server authentication data must be uploaded at https://my.telegram.org
//@description A token for Firebase Cloud Messaging @token Device registration token; may be empty to deregister a device @encrypt True, if push notifications must be additionally encrypted
@ -3130,6 +3285,9 @@ pushMessageContentChatJoinByLink = PushMessageContent;
//@description A new member was accepted to the chat by an administrator
pushMessageContentChatJoinByRequest = PushMessageContent;
//@description A new recurrent payment was made by the current user @amount The paid amount
pushMessageContentRecurringPayment amount:string = PushMessageContent;
//@description A forwarded messages @total_count Number of forwarded messages
pushMessageContentMessageForwards total_count:int32 = PushMessageContent;
@ -3140,8 +3298,8 @@ pushMessageContentMediaAlbum total_count:int32 has_photos:Bool has_videos:Bool h
//@class NotificationType @description Contains detailed information about a notification
//@description New message was received @message The message
notificationTypeNewMessage message:message = NotificationType;
//@description New message was received @message The message @show_preview True, if message content must be displayed in notifications
notificationTypeNewMessage message:message show_preview:Bool = NotificationType;
//@description New secret chat was created
notificationTypeNewSecretChat = NotificationType;
@ -3416,17 +3574,33 @@ chatReportReasonPersonalDetails = ChatReportReason;
chatReportReasonCustom = ChatReportReason;
//@class InternalLinkType @description Describes an internal https://t.me or tg: link, which must be processed by the app in a special way
//@class TargetChat @description Describes the target chat to be opened
//@description The link is a link to the active sessions section of the app. Use getActiveSessions to handle the link
//@description The currently opened chat needs to be kept
targetChatCurrent = TargetChat;
//@description The chat needs to be chosen by the user among chats of the specified types
//@allow_user_chats True, if private chats with ordinary users are allowed
//@allow_bot_chats True, if private chats with other bots are allowed
//@allow_group_chats True, if basic group and supergroup chats are allowed
//@allow_channel_chats True, if channel chats are allowed
targetChatChosen allow_user_chats:Bool allow_bot_chats:Bool allow_group_chats:Bool allow_channel_chats:Bool = TargetChat;
//@description The chat needs to be open with the provided internal link @link An internal link pointing to the chat
targetChatInternalLink link:InternalLinkType = TargetChat;
//@class InternalLinkType @description Describes an internal https://t.me or tg: link, which must be processed by the application in a special way
//@description The link is a link to the active sessions section of the application. Use getActiveSessions to handle the link
internalLinkTypeActiveSessions = InternalLinkType;
//@description The link is a link to an attachment menu bot to be opened in the specified chat. Process given chat_link to open corresponding chat.
//@description The link is a link to an attachment menu bot to be opened in the specified or a chosen chat. Process given target_chat to open the chat.
//-Then call searchPublicChat with the given bot username, check that the user is a bot and can be added to attachment menu. Then use getAttachmentMenuBot to receive information about the bot.
//-If the bot isn't added to attachment menu, then user needs to confirm adding the bot to attachment menu. If user confirms adding, then use toggleBotIsAddedToAttachmentMenu to add it.
//-If attachment menu bots can't be used in the current chat, show an error to the user. If the bot is added to attachment menu, then use openWebApp with the given URL
//@chat_link An internal link pointing to a chat; may be null if the current chat needs to be kept @bot_username Username of the bot @url URL to be passed to openWebApp
internalLinkTypeAttachmentMenuBot chat_link:InternalLinkType bot_username:string url:string = InternalLinkType;
//-If the attachment menu bot can't be used in the opened chat, show an error to the user. If the bot is added to attachment menu and can be used in the chat, then use openWebApp with the given URL
//@target_chat Target chat to be opened @bot_username Username of the bot @url URL to be passed to openWebApp
internalLinkTypeAttachmentMenuBot target_chat:TargetChat bot_username:string url:string = InternalLinkType;
//@description The link contains an authentication code. Call checkAuthenticationCode with the code if the current authorization state is authorizationStateWaitCode @code The authentication code
internalLinkTypeAuthenticationCode code:string = InternalLinkType;
@ -3467,6 +3641,9 @@ internalLinkTypeFilterSettings = InternalLinkType;
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
//@description The link is a link to an invoice. Call getPaymentForm with the given invoice name to process the link @invoice_name Name of the invoice
internalLinkTypeInvoice invoice_name:string = InternalLinkType;
//@description The link is a link to a language pack. Call getLanguagePackInfo with the given language pack identifier to process the link @language_pack_id Language pack identifier
internalLinkTypeLanguagePack language_pack_id:string = InternalLinkType;
@ -3480,7 +3657,7 @@ internalLinkTypeMessage url:string = InternalLinkType;
//@text Message draft text @contains_link True, if the first line of the text contains a link. If true, the input field needs to be focused and the text after the link must be selected
internalLinkTypeMessageDraft text:formattedText contains_link:Bool = InternalLinkType;
//@description The link contains a request of Telegram passport data. Call getPassportAuthorizationForm with the given parameters to process the link if the link was received from outside of the app, otherwise ignore it
//@description The link contains a request of Telegram passport data. Call getPassportAuthorizationForm with the given parameters to process the link if the link was received from outside of the application, otherwise ignore it
//@bot_user_id User identifier of the service's bot @scope Telegram Passport element types requested by the service @public_key Service's public key @nonce Unique request identifier provided by the service
//@callback_url An HTTP URL to open once the request is finished or canceled with the parameter tg_passport=success or tg_passport=cancel respectively. If empty, then the link tgbot{bot_user_id}://passport/success or tgbot{bot_user_id}://passport/cancel needs to be opened instead
internalLinkTypePassportDataRequest bot_user_id:int53 scope:string public_key:string nonce:string callback_url:string = InternalLinkType;
@ -3489,6 +3666,9 @@ internalLinkTypePassportDataRequest bot_user_id:int53 scope:string public_key:st
//@hash Hash value from the link @phone_number Phone number value from the link
internalLinkTypePhoneNumberConfirmation hash:string phone_number:string = InternalLinkType;
//@description The link is a link to the Premium features screen of the applcation from which the user can subscribe to Telegram Premium. Call getPremiumFeatures with the given referrer to process the link @referrer Referrer specified in the link
internalLinkTypePremiumFeatures referrer:string = InternalLinkType;
//@description The link is a link to the privacy and security settings section of the app
internalLinkTypePrivacyAndSecuritySettings = InternalLinkType;
@ -3503,7 +3683,7 @@ internalLinkTypePublicChat chat_username:string = InternalLinkType;
//-"This code can be used to allow someone to log in to your Telegram account. To confirm Telegram login, please go to Settings > Devices > Scan QR and scan the code" needs to be shown
internalLinkTypeQrCodeAuthentication = InternalLinkType;
//@description The link is a link to app settings
//@description The link is a link to application settings
internalLinkTypeSettings = InternalLinkType;
//@description The link is a link to a sticker set. Call searchStickerSet with the given sticker set name to process the link and show the sticker set @sticker_set_name Name of the sticker set
@ -3669,7 +3849,7 @@ networkStatistics since_date:int32 entries:vector<NetworkStatisticsEntry> = Netw
//@preload_large_videos True, if the beginning of video files needs to be preloaded for instant playback
//@preload_next_audio True, if the next audio track needs to be preloaded while the user is listening to an audio file
//@use_less_data_for_calls True, if "use less data for calls" option needs to be enabled
autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int32 max_other_file_size:int32 video_upload_bitrate:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings;
autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int53 max_other_file_size:int53 video_upload_bitrate:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings;
//@description Contains auto-download settings presets for the current user
//@low Preset with lowest settings; supposed to be used by default when roaming
@ -3771,6 +3951,9 @@ text text:string = Text;
//@description Contains a value representing a number of seconds @seconds Number of seconds
seconds seconds:double = Seconds;
//@description Contains size of downloaded prefix of a file @size The prefix size, in bytes
fileDownloadedPrefixSize size:int53 = FileDownloadedPrefixSize;
//@description Contains information about a tg: deep link @text Text to be shown to the user @need_update_application True, if the user must be asked to update the application
deepLinkInfo text:formattedText need_update_application:Bool = DeepLinkInfo;
@ -4058,8 +4241,8 @@ updateChatIsBlocked chat_id:int53 is_blocked:Bool = Update;
//@description A chat was marked as unread or was read @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread
updateChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Update;
//@description The list of chat filters or a chat filter has changed @chat_filters The new list of chat filters
updateChatFilters chat_filters:vector<chatFilterInfo> = Update;
//@description The list of chat filters or a chat filter has changed @chat_filters The new list of chat filters @main_chat_list_position Position of the main chat list among chat filters, 0-based
updateChatFilters chat_filters:vector<chatFilterInfo> main_chat_list_position:int32 = Update;
//@description The number of online group members has changed. This update with non-zero number of online group members is sent only for currently opened chats. There is no guarantee that it will be sent just after the number of online users has changed @chat_id Identifier of the chat @online_member_count New number of online members in the chat, or 0 if unknown
updateChatOnlineMemberCount chat_id:int53 online_member_count:int32 = Update;
@ -4196,7 +4379,7 @@ updateStickerSet sticker_set:stickerSet = Update;
updateInstalledStickerSets is_masks:Bool sticker_set_ids:vector<int64> = Update;
//@description The list of trending sticker sets was updated or some of them were viewed @sticker_sets The prefix of the list of trending sticker sets with the newest trending sticker sets
updateTrendingStickerSets sticker_sets:stickerSets = Update;
updateTrendingStickerSets sticker_sets:trendingStickerSets = Update;
//@description The list of recently used stickers was updated @is_attached True, if the list of stickers attached to photo or video files was updated, otherwise the list of sent stickers is updated @sticker_ids The new list of file identifiers of recently used stickers
updateRecentStickers is_attached:Bool sticker_ids:vector<int32> = Update;
@ -4228,10 +4411,10 @@ updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService
//@description The list of users nearby has changed. The update is guaranteed to be sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description The list of bots added to attachment menu has changed @bots The new list of bots added to attachment menu. The bots must be shown in attachment menu only in private chats. The bots must not be shown on scheduled messages screen
//@description The list of bots added to attachment menu has changed @bots The new list of bots added to attachment menu. The bots must not be shown on scheduled messages screen
updateAttachmentMenuBots bots:vector<attachmentMenuBot> = Update;
//@description A message was sent by an opened web app, so the web app needs to be closed @web_app_launch_id Identifier of web app launch
//@description A message was sent by an opened Web App, so the Web App needs to be closed @web_app_launch_id Identifier of Web App launch
updateWebAppMessageSent web_app_launch_id:int64 = Update;
//@description The list of supported reactions has changed @reactions The new list of supported reactions
@ -4558,13 +4741,13 @@ checkChatUsername chat_id:int53 username:string = CheckChatUsernameResult;
//@description Returns a list of public chats of the specified type, owned by the user @type Type of the public chats to return
getCreatedPublicChats type:PublicChatType = Chats;
//@description Checks whether the maximum number of owned public chats has been reached. Returns corresponding error if the limit was reached @type Type of the public chats, for which to check the limit
//@description Checks whether the maximum number of owned public chats has been reached. Returns corresponding error if the limit was reached. The limit can be increased with Telegram Premium @type Type of the public chats, for which to check the limit
checkCreatedPublicChatsLimit type:PublicChatType = Ok;
//@description Returns a list of basic group and supergroup chats, which can be used as a discussion group for a channel. Returned basic group chats must be first upgraded to supergroups before they can be set as a discussion group. To set a returned supergroup as a discussion group, access to its old messages must be enabled using toggleSupergroupIsAllHistoryAvailable first
getSuitableDiscussionChats = Chats;
//@description Returns a list of recently inactive supergroups and channels. Can be used when user reaches limit on the number of joined supergroups and channels and receives CHANNELS_TOO_MUCH error
//@description Returns a list of recently inactive supergroups and channels. Can be used when user reaches limit on the number of joined supergroups and channels and receives CHANNELS_TOO_MUCH error. Also, the limit can be increased with Telegram Premium
getInactiveSupergroupChats = Chats;
@ -4715,6 +4898,14 @@ getMessageLinkInfo url:string = MessageLinkInfo;
//@to_language_code A two-letter ISO 639-1 language code of the language to which the message is translated
translateText text:string from_language_code:string to_language_code:string = Text;
//@description Recognizes speech in a voice note message. The message must be successfully sent and must not be scheduled. May return an error with a message "MSG_VOICE_TOO_LONG" if the voice note is too long to be recognized
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
recognizeSpeech chat_id:int53 message_id:int53 = Ok;
//@description Rates recognized speech in a voice note message @chat_id Identifier of the chat to which the message belongs @message_id Identifier of the message @is_good Pass true if the speech recognition is good
rateSpeechRecognition chat_id:int53 message_id:int53 is_good:Bool = Ok;
//@description Returns list of message sender identifiers, which can be used to send messages in a chat @chat_id Chat identifier
getChatAvailableMessageSenders chat_id:int53 = MessageSenders;
@ -4866,7 +5057,7 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup =
editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok;
//@description Returns reactions, which can be added to a message. The list can change after updateReactions, updateChatAvailableReactions for the chat, or updateMessageInteractionInfo for the message
//@description Returns reactions, which can be added to a message. The list can change after updateReactions, updateChatAvailableReactions for the chat, or updateMessageInteractionInfo for the message. The method will return Premium reactions, even the current user has no Premium subscription
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
getMessageAvailableReactions chat_id:int53 message_id:int53 = AvailableReactions;
@ -4978,30 +5169,30 @@ getInlineQueryResults bot_user_id:int53 chat_id:int53 user_location:location que
answerInlineQuery inline_query_id:int64 is_personal:Bool results:vector<InputInlineQueryResult> cache_time:int32 next_offset:string switch_pm_text:string switch_pm_parameter:string = Ok;
//@description Returns an HTTPS URL of a web app to open after keyboardButtonTypeWebApp button is pressed
//@description Returns an HTTPS URL of a Web App to open after keyboardButtonTypeWebApp button is pressed
//@bot_user_id Identifier of the target bot
//@url The URL from the keyboardButtonTypeWebApp button
//@theme Preferred web app theme; pass null to use the default theme
//@theme Preferred Web App theme; pass null to use the default theme
getWebAppUrl bot_user_id:int53 url:string theme:themeParameters = HttpUrl;
//@description Sends data received from a keyboardButtonTypeWebApp web app to a bot
//@bot_user_id Identifier of the target bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app @data Received data
//@description Sends data received from a keyboardButtonTypeWebApp Web App to a bot
//@bot_user_id Identifier of the target bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the Web App @data Received data
sendWebAppData bot_user_id:int53 button_text:string data:string = Ok;
//@description Informs TDLib that a web app is being opened from attachment menu, a botMenuButton button, an internalLinkTypeAttachmentMenuBot link, or an inlineKeyboardButtonTypeWebApp button.
//@description Informs TDLib that a Web App is being opened from attachment menu, a botMenuButton button, an internalLinkTypeAttachmentMenuBot link, or an inlineKeyboardButtonTypeWebApp button.
//-For each bot, a confirmation alert about data sent to the bot must be shown once
//@chat_id Identifier of the chat in which the web app is opened. Web apps can be opened only in private chats for now
//@bot_user_id Identifier of the bot, providing the web app
//@chat_id Identifier of the chat in which the Web App is opened
//@bot_user_id Identifier of the bot, providing the Web App
//@url The URL from an inlineKeyboardButtonTypeWebApp button, a botMenuButton button, or an internalLinkTypeAttachmentMenuBot link, or an empty string otherwise
//@theme Preferred web app theme; pass null to use the default theme
//@reply_to_message_id Identifier of the replied message for the message sent by the web app; 0 if none
//@theme Preferred Web App theme; pass null to use the default theme
//@reply_to_message_id Identifier of the replied message for the message sent by the Web App; 0 if none
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters reply_to_message_id:int53 = WebAppInfo;
//@description Informs TDLib that a previously opened web app was closed @web_app_launch_id Identifier of web app launch, received from openWebApp
//@description Informs TDLib that a previously opened Web App was closed @web_app_launch_id Identifier of Web App launch, received from openWebApp
closeWebApp web_app_launch_id:int64 = Ok;
//@description Sets the result of interaction with a web app and sends corresponding message on behalf of the user to the chat from which the query originated; for bots only
//@web_app_query_id Identifier of the web app query
//@description Sets the result of interaction with a Web App and sends corresponding message on behalf of the user to the chat from which the query originated; for bots only
//@web_app_query_id Identifier of the Web App query
//@result The result of the query
answerWebAppQuery web_app_query_id:string result:InputInlineQueryResult = SentWebAppMessage;
@ -5123,7 +5314,7 @@ addChatToList chat_id:int53 chat_list:ChatList = Ok;
//@description Returns information about a chat filter by its identifier @chat_filter_id Chat filter identifier
getChatFilter chat_filter_id:int32 = ChatFilter;
//@description Creates new chat filter. Returns information about the created chat filter @filter Chat filter
//@description Creates new chat filter. Returns information about the created chat filter. There can be up to GetOption("chat_filter_count_max") chat filters, but the limit can be increased with Telegram Premium @filter Chat filter
createChatFilter filter:chatFilter = ChatFilterInfo;
//@description Edits existing chat filter. Returns information about the edited chat filter @chat_filter_id Chat filter identifier @filter The edited chat filter
@ -5132,8 +5323,8 @@ editChatFilter chat_filter_id:int32 filter:chatFilter = ChatFilterInfo;
//@description Deletes existing chat filter @chat_filter_id Chat filter identifier
deleteChatFilter chat_filter_id:int32 = Ok;
//@description Changes the order of chat filters @chat_filter_ids Identifiers of chat filters in the new correct order
reorderChatFilters chat_filter_ids:vector<int32> = Ok;
//@description Changes the order of chat filters @chat_filter_ids Identifiers of chat filters in the new correct order @main_chat_list_position Position of the main chat list among chat filters, 0-based. Can be non-zero only for Premium users
reorderChatFilters chat_filter_ids:vector<int32> main_chat_list_position:int32 = Ok;
//@description Returns recommended chat filters for the current user
getRecommendedChatFilters = RecommendedChatFilters;
@ -5212,7 +5403,7 @@ unpinChatMessage chat_id:int53 message_id:int53 = Ok;
unpinAllChatMessages chat_id:int53 = Ok;
//@description Adds the current user as a new member to a chat. Private and secret chats can't be joined using this method @chat_id Chat identifier
//@description Adds the current user as a new member to a chat. Private and secret chats can't be joined using this method. May return an error with a message "INVITE_REQUEST_SENT" if only a join request was created @chat_id Chat identifier
joinChat chat_id:int53 = Ok;
//@description Removes the current user from chat members. Private and secret chats can't be left using this method @chat_id Chat identifier
@ -5290,7 +5481,7 @@ setScopeNotificationSettings scope:NotificationSettingsScope notification_settin
resetAllNotificationSettings = Ok;
//@description Changes the pinned state of a chat. There can be up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") pinned non-secret chats and the same number of secret chats in the main/archive chat list
//@description Changes the pinned state of a chat. There can be up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") pinned non-secret chats and the same number of secret chats in the main/archive chat list. The limit can be increased with Telegram Premium
//@chat_list Chat list in which to change the pinned state of the chat @chat_id Chat identifier @is_pinned Pass true to pin the chat; pass false to unpin it
toggleChatIsPinned chat_list:ChatList chat_id:int53 is_pinned:Bool = Ok;
@ -5311,10 +5502,10 @@ toggleBotIsAddedToAttachmentMenu bot_user_id:int53 is_added:Bool = Ok;
//@offset The starting position from which the file needs to be downloaded
//@limit Number of bytes which need to be downloaded starting from the "offset" position before the download will automatically be canceled; use 0 to download without a limit
//@synchronous Pass true to return response only after the file download has succeeded, has failed, has been canceled, or a new downloadFile request with different offset/limit parameters was sent; pass false to return file state immediately, just after the download has been started
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 synchronous:Bool = File;
downloadFile file_id:int32 priority:int32 offset:int53 limit:int53 synchronous:Bool = File;
//@description Returns file downloaded prefix size from a given offset, in bytes @file_id Identifier of the file @offset Offset from which downloaded prefix size needs to be calculated
getFileDownloadedPrefixSize file_id:int32 offset:int32 = Count;
getFileDownloadedPrefixSize file_id:int32 offset:int53 = FileDownloadedPrefixSize;
//@description Stops the downloading of a file. If a file has already been downloaded, does nothing @file_id Identifier of a file to stop downloading @only_if_pending Pass true to stop downloading only if it hasn't been started, i.e. request hasn't been sent to server
cancelDownloadFile file_id:int32 only_if_pending:Bool = Ok;
@ -5333,13 +5524,13 @@ cancelUploadFile file_id:int32 = Ok;
//@description Writes a part of a generated file. This method is intended to be used only if the application has no direct access to TDLib's file system, because it is usually slower than a direct write to the destination file
//@generation_id The identifier of the generation process @offset The offset from which to write the data to the file @data The data to write
writeGeneratedFilePart generation_id:int64 offset:int32 data:bytes = Ok;
writeGeneratedFilePart generation_id:int64 offset:int53 data:bytes = Ok;
//@description Informs TDLib on a file generation progress
//@generation_id The identifier of the generation process
//@expected_size Expected size of the generated file, in bytes; 0 if unknown
//@local_prefix_size The number of bytes already generated
setFileGenerationProgress generation_id:int64 expected_size:int32 local_prefix_size:int32 = Ok;
setFileGenerationProgress generation_id:int64 expected_size:int53 local_prefix_size:int53 = Ok;
//@description Finishes the file generation
//@generation_id The identifier of the generation process
@ -5350,7 +5541,7 @@ finishFileGeneration generation_id:int64 error:error = Ok;
//@file_id Identifier of the file. The file must be located in the TDLib file cache
//@offset The offset from which to read the file
//@count Number of bytes to read. An error will be returned if there are not enough bytes available in the file from the specified position. Pass 0 to read all available data from the specified position
readFilePart file_id:int32 offset:int32 count:int32 = FilePart;
readFilePart file_id:int32 offset:int53 count:int53 = FilePart;
//@description Deletes a file from the TDLib file cache @file_id Identifier of the file to delete
deleteFile file_id:int32 = Ok;
@ -5389,7 +5580,7 @@ removeAllFilesFromDownloads only_active:Bool only_completed:Bool delete_from_cac
searchFileDownloads query:string only_active:Bool only_completed:Bool offset:string limit:int32 = FoundFileDownloads;
//@description Returns information about a file with messages exported from another app @message_file_head Beginning of the message file; up to 100 first lines
//@description Returns information about a file with messages exported from another application @message_file_head Beginning of the message file; up to 100 first lines
getMessageFileType message_file_head:string = MessageFileType;
//@description Returns a confirmation text to be shown to the user before starting message import
@ -5461,7 +5652,7 @@ deleteAllRevokedChatInviteLinks chat_id:int53 creator_user_id:int53 = Ok;
//@description Checks the validity of an invite link for a chat and returns information about the corresponding chat @invite_link Invite link to be checked
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
//@description Uses an invite link to add the current user to the chat if possible. May return an error with a message "INVITE_REQUEST_SENT" if only a join request was created @invite_link Invite link to use
joinChatByInviteLink invite_link:string = Chat;
//@description Returns pending join requests in a chat
@ -5694,7 +5885,7 @@ getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = S
//@description Returns a list of trending sticker sets. For optimal performance, the number of returned sticker sets is chosen by TDLib
//@offset The offset from which to return the sticker sets; must be non-negative
//@limit The maximum number of sticker sets to be returned; up to 100. For optimal performance, the number of returned sticker sets is chosen by TDLib and can be smaller than the specified limit, even if the end of the list has not been reached
getTrendingStickerSets offset:int32 limit:int32 = StickerSets;
getTrendingStickerSets offset:int32 limit:int32 = TrendingStickerSets;
//@description Returns a list of sticker sets attached to a file. Currently, only photos and videos can have attached sticker sets @file_id File identifier
getAttachedStickerSets file_id:int32 = StickerSets;
@ -5752,6 +5943,9 @@ searchEmojis text:string exact_match:Bool input_language_codes:vector<string> =
//@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 all emojis, which has a corresponding animated emoji
getAllAnimatedEmojis = Emojis;
//@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;
@ -5794,7 +5988,7 @@ deleteProfilePhoto profile_photo_id:int64 = Ok;
//@description Changes the first and last name of the current user @first_name The new value of the first name for the current user; 1-64 characters @last_name The new value of the optional last name for the current user; 0-64 characters
setName first_name:string last_name:string = Ok;
//@description Changes the bio of the current user @bio The new value of the user bio; 0-70 characters without line feeds
//@description Changes the bio of the current user @bio The new value of the user bio; 0-GetOption("bio_length_max") characters without line feeds
setBio bio:string = Ok;
//@description Changes the username of the current user @username The new value of the username. Use an empty string to remove the username
@ -5883,6 +6077,12 @@ setSupergroupStickerSet supergroup_id:int53 sticker_set_id:int64 = Ok;
//@description Toggles whether sender signature is added to sent messages in a channel; requires can_change_info administrator right @supergroup_id Identifier of the channel @sign_messages New value of sign_messages
toggleSupergroupSignMessages supergroup_id:int53 sign_messages:Bool = Ok;
//@description Toggles whether joining is mandatory to send messages to a discussion supergroup; requires can_restrict_members administrator right @supergroup_id Identifier of the supergroup @join_to_send_messages New value of join_to_send_messages
toggleSupergroupJoinToSendMessages supergroup_id:int53 join_to_send_messages:Bool = Ok;
//@description Toggles whether all users directly joining the supergroup need to be approved by supergroup administrators; requires can_restrict_members administrator right @supergroup_id Identifier of the channel @join_by_request New value of join_by_request
toggleSupergroupJoinByRequest supergroup_id:int53 join_by_request:Bool = Ok;
//@description Toggles whether the message history of a supergroup is available to new members; requires can_change_info administrator right @supergroup_id The identifier of the supergroup @is_all_history_available The new value of is_all_history_available
toggleSupergroupIsAllHistoryAvailable supergroup_id:int53 is_all_history_available:Bool = Ok;
@ -5908,22 +6108,20 @@ getChatEventLog chat_id:int53 query:string from_event_id:int64 limit:int32 filte
//@description Returns an invoice payment form. This method must be called when the user presses inlineKeyboardButtonBuy
//@chat_id Chat identifier of the Invoice message
//@message_id Message identifier
//@input_invoice The invoice
//@theme Preferred payment form theme; pass null to use the default theme
getPaymentForm chat_id:int53 message_id:int53 theme:themeParameters = PaymentForm;
getPaymentForm input_invoice:InputInvoice theme:themeParameters = PaymentForm;
//@description Validates the order information provided by a user and returns the available shipping options for a flexible invoice
//@chat_id Chat identifier of the Invoice message
//@message_id Message identifier
//@input_invoice The invoice
//@order_info The order information, provided by the user; pass null if empty
//@allow_save Pass true to save the order information
validateOrderInfo chat_id:int53 message_id:int53 order_info:orderInfo allow_save:Bool = ValidatedOrderInfo;
validateOrderInfo input_invoice:InputInvoice order_info:orderInfo allow_save:Bool = ValidatedOrderInfo;
//@description Sends a filled-out payment form to the bot for final verification @chat_id Chat identifier of the Invoice message @message_id Message identifier
//@description Sends a filled-out payment form to the bot for final verification @input_invoice The invoice
//@payment_form_id Payment form identifier returned by getPaymentForm @order_info_id Identifier returned by validateOrderInfo, or an empty string @shipping_option_id Identifier of a chosen shipping option, if applicable
//@credentials The credentials chosen by user for payment @tip_amount Chosen by the user amount of tip in the smallest units of the currency
sendPaymentForm chat_id:int53 message_id:int53 payment_form_id:int64 order_info_id:string shipping_option_id:string credentials:InputCredentials tip_amount:int53 = PaymentResult;
sendPaymentForm input_invoice:InputInvoice payment_form_id:int64 order_info_id:string shipping_option_id:string credentials:InputCredentials tip_amount:int53 = PaymentResult;
//@description Returns information about a successful payment @chat_id Chat identifier of the PaymentSuccessful message @message_id Message identifier
getPaymentReceipt chat_id:int53 message_id:int53 = PaymentReceipt;
@ -5938,6 +6136,10 @@ deleteSavedOrderInfo = Ok;
deleteSavedCredentials = Ok;
//@description Creates a link for the given invoice; for bots only @invoice Information about the invoice of the type inputMessageInvoice
createInvoiceLink invoice:InputMessageContent = HttpUrl;
//@description Returns a user that can be contacted to get support
getSupportUser = User;
@ -6197,7 +6399,7 @@ addStickerToSet user_id:int53 name:string sticker:inputSticker = StickerSet;
setStickerSetThumbnail user_id:int53 name:string thumbnail:InputFile = StickerSet;
//@description Changes the position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot
//@sticker Sticker @position New position of the sticker in the set, zero-based
//@sticker Sticker @position New position of the sticker in the set, 0-based
setStickerPositionInSet sticker:InputFile position:int32 = Ok;
//@description Removes a sticker from the set to which it belongs; for bots only. The sticker set must have been created by the bot @sticker Sticker
@ -6208,6 +6410,25 @@ removeStickerFromSet sticker:InputFile = Ok;
getMapThumbnailFile location:location zoom:int32 width:int32 height:int32 scale:int32 chat_id:int53 = File;
//@description Returns information about a limit, increased for Premium users. Returns a 404 error if the limit is unknown @limit_type Type of the limit
getPremiumLimit limit_type:PremiumLimitType = PremiumLimit;
//@description Returns information about features, available to Premium users @source Source of the request; pass null if the method is called from some non-standard source
getPremiumFeatures source:PremiumSource = PremiumFeatures;
//@description Returns examples of premium stickers for demonstration purposes
getPremiumStickers = Stickers;
//@description Informs TDLib that the user viewed detailed information about a Premium feature on the Premium features screen @feature The viewed premium feature
viewPremiumFeature feature:PremiumFeature = Ok;
//@description Informs TDLib that the user clicked Premium subscription button on the Premium features screen
clickPremiumSubscriptionButton = Ok;
//@description Returns state of Telegram Premium subscription and promotion videos for Premium features
getPremiumState = PremiumState;
//@description Accepts Telegram terms of services @terms_of_service_id Terms of service identifier
acceptTermsOfService terms_of_service_id:string = Ok;

View File

@ -100,7 +100,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -115,7 +115,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
@ -140,7 +140,7 @@ messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int =
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
@ -163,8 +163,8 @@ messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageActi
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction;
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
@ -380,6 +380,7 @@ updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -404,9 +405,9 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -424,7 +425,7 @@ encryptedChatDiscarded#1e1c7c45 flags:# history_deleted:flags.0?true id:int = En
inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat;
encryptedFileEmpty#c21f497e = EncryptedFile;
encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile;
encryptedFile#a8008cd8 id:long access_hash:long size:long dc_id:int key_fingerprint:int = EncryptedFile;
inputEncryptedFileEmpty#1837c364 = InputEncryptedFile;
inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile;
@ -444,7 +445,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
documentEmpty#36f8c871 id:long = Document;
document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
document#8fd4c4d8 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:long thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
help.support#17c6b5f6 phone_number:string user:User = help.Support;
@ -552,6 +553,7 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage;
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;
chatInvitePublicJoinRequests#ed107ab7 = ExportedChatInvite;
chatInviteAlready#5a686d7c chat:Chat = 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<User> = ChatInvite;
@ -571,7 +573,7 @@ messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
botCommand#c27ac8c7 command:string description:string = BotCommand;
botInfo#e4169b5d user_id:long description:string commands:Vector<BotCommand> menu_button:BotMenuButton = BotInfo;
botInfo#8f300b57 flags:# user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector<BotCommand> menu_button:flags.3?BotMenuButton = BotInfo;
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
@ -730,7 +732,7 @@ draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
messages.featuredStickers#84c02310 hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
messages.recentStickers#88d37c56 hash:long packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
@ -813,7 +815,7 @@ dataJSON#7d748d04 data:string = DataJSON;
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
invoice#cd886e0 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> = Invoice;
invoice#3e85a91b flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> recurring_terms_url:flags.9?string = Invoice;
paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
@ -833,7 +835,7 @@ inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
payments.paymentForm#1694761b flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
payments.paymentForm#b0133b37 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
@ -864,7 +866,7 @@ phoneCallAccepted#3660c311 flags:# video:flags.6?true id:long access_hash:long d
phoneCall#967f7c67 flags:# p2p_allowed:flags.5?true video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int = PhoneCall;
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
phoneConnection#9cc123c7 flags:# tcp:flags.0?true id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
phoneConnectionWebrtc#635fe375 flags:# turn:flags.0?true stun:flags.1?true id:long ip:string ipv6:string port:int username:string password:string = PhoneConnection;
phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;
@ -962,7 +964,7 @@ dialogPeerFolder#514519e2 folder_id:int = DialogPeer;
messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets;
messages.foundStickerSets#8af09dd2 hash:long sets:Vector<StickerSetCovered> = messages.FoundStickerSets;
fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
fileHash#f39b035c offset:long limit:int hash:bytes = FileHash;
inputClientProxy#75588b3f address:string port:int = InputClientProxy;
@ -973,7 +975,7 @@ inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash
inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
secureFileEmpty#64199744 = SecureFile;
secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
secureFile#7d09c27e id:long access_hash:long size:long dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
@ -1100,7 +1102,7 @@ codeSettings#8a6469c2 flags:# allow_flashcall:flags.0?true current_number:flags.
wallPaperSettings#1dc1bca4 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings;
autoDownloadSettings#e04232f3 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int video_upload_maxbitrate:int = AutoDownloadSettings;
autoDownloadSettings#8efab953 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int = AutoDownloadSettings;
account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
@ -1172,6 +1174,7 @@ bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;
dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
dialogFilterDefault#363293ae = DialogFilter;
dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
@ -1217,7 +1220,7 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
@ -1285,7 +1288,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#3a836df8 flags:# random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
@ -1311,7 +1314,7 @@ messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true res
messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector<MessagePeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
availableReaction#c077ec01 flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;
availableReaction#c077ec01 flags:# inactive:flags.0?true premium:flags.2?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;
messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
@ -1331,7 +1334,7 @@ attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;
attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;
attachMenuBot#e93cb772 flags:# inactive:flags.0?true bot_id:long short_name:string icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;
@ -1359,6 +1362,21 @@ notificationSoundRingtone#ff6c8049 id:long = NotificationSound;
account.savedRingtone#b7263f6d = account.SavedRingtone;
account.savedRingtoneConverted#1f307eb7 document:Document = account.SavedRingtone;
attachMenuPeerTypeSameBotPM#7d6be90e = AttachMenuPeerType;
attachMenuPeerTypeBotPM#c32bfa1a = AttachMenuPeerType;
attachMenuPeerTypePM#f146d31f = AttachMenuPeerType;
attachMenuPeerTypeChat#509113f = AttachMenuPeerType;
attachMenuPeerTypeBroadcast#7bfbdefc = AttachMenuPeerType;
inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice;
inputInvoiceSlug#c326caef slug:string = InputInvoice;
payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio;
help.premiumPromo#8a4f3c29 status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> currency:string monthly_amount:long users:Vector<User> = help.PremiumPromo;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1429,7 +1447,7 @@ account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings =
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
account.verifyEmail#ecba39db email:string code:string = Bool;
account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout;
account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout;
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
account.resendPasswordEmail#7a7f2a15 = Bool;
@ -1541,7 +1559,7 @@ messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = B
messages.migrateChat#a2875319 chat_id:long = Updates;
messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document;
messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
@ -1654,11 +1672,13 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#fa04dff flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int = WebViewResult;
messages.prolongWebView#d22ad148 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int = Bool;
messages.requestWebView#91b15831 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;
messages.rateTranscribedAudio#7f1d072f peer:InputPeer msg_id:int transcription_id:long good:Bool = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1670,13 +1690,13 @@ photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
upload.getCdnFile#395f69da file_token:bytes offset:long limit:int = upload.CdnFile;
upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector<FileHash>;
upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector<FileHash>;
upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<FileHash>;
upload.getCdnFileHashes#91dc3f31 file_token:bytes offset:long = Vector<FileHash>;
upload.getFileHashes#9156982a location:InputFileLocation offset:long = Vector<FileHash>;
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
@ -1700,6 +1720,7 @@ help.getPromoData#c0977421 = help.PromoData;
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool;
help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
help.getPremiumPromo#b81b93d4 = help.PremiumPromo;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@ -1740,6 +1761,8 @@ channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bo
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@ -1751,13 +1774,18 @@ bots.getBotMenuButton#9c60eb28 user_id:InputUser = BotMenuButton;
bots.setBotBroadcastDefaultAdminRights#788464e1 admin_rights:ChatAdminRights = Bool;
bots.setBotGroupDefaultAdminRights#925ec9ea admin_rights:ChatAdminRights = Bool;
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
payments.validateRequestedInfo#db103170 flags:# save:flags.0?true peer:InputPeer msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
payments.sendPaymentForm#30c3bc9d flags:# form_id:long peer:InputPeer msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult;
payments.validateRequestedInfo#b6c8f12b flags:# save:flags.0?true invoice:InputInvoice info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
payments.sendPaymentForm#2d03522f flags:# form_id:long invoice:InputInvoice requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult;
payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
payments.assignAppStoreTransaction#d5ccfd0 flags:# restore:flags.0?true receipt:bytes = Updates;
payments.assignPlayMarketTransaction#4faa4aed purchase_token:string = Updates;
payments.canPurchasePremium#aa6a90c8 = Bool;
payments.requestRecurringPayment#146e958d user_id:InputUser recurring_init_charge:string invoice_media:InputMedia = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View File

@ -389,7 +389,8 @@ tl_object_ptr<telegram_api::InputMedia> AnimationsManager::get_input_media(
SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const {
const string &caption, BufferSlice thumbnail,
int32 layer) const {
auto *animation = get_animation(animation_file_id);
CHECK(animation != nullptr);
auto file_view = td_->file_manager_->get_file_view(animation_file_id);
@ -425,7 +426,8 @@ SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file
animation->mime_type,
file_view,
std::move(attributes),
caption};
caption,
layer};
}
void AnimationsManager::on_update_animation_search_emojis(string animation_search_emojis) {

View File

@ -46,7 +46,7 @@ class AnimationsManager final : public Actor {
SecretInputMedia get_secret_input_media(FileId animation_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const;
const string &caption, BufferSlice thumbnail, int32 layer) const;
FileId get_animation_thumbnail_file_id(FileId file_id) const;

View File

@ -11,6 +11,7 @@
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
namespace td {

View File

@ -39,6 +39,7 @@ class RequestWebViewQuery final : public Td::ResultHandler {
DialogId dialog_id_;
UserId bot_user_id_;
MessageId reply_to_message_id_;
DialogId as_dialog_id_;
bool from_attach_menu_ = false;
public:
@ -47,10 +48,12 @@ class RequestWebViewQuery final : public Td::ResultHandler {
}
void send(DialogId dialog_id, UserId bot_user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, MessageId reply_to_message_id, bool silent) {
td_api::object_ptr<td_api::themeParameters> &&theme, MessageId reply_to_message_id, bool silent,
DialogId as_dialog_id) {
dialog_id_ = dialog_id;
bot_user_id_ = bot_user_id;
reply_to_message_id_ = reply_to_message_id;
as_dialog_id_ = as_dialog_id;
int32 flags = 0;
@ -90,9 +93,17 @@ class RequestWebViewQuery final : public Td::ResultHandler {
flags |= telegram_api::messages_requestWebView::SILENT_MASK;
}
tl_object_ptr<telegram_api::InputPeer> as_input_peer;
if (as_dialog_id.is_valid()) {
as_input_peer = td_->messages_manager_->get_input_peer(as_dialog_id, AccessRights::Write);
if (as_input_peer != nullptr) {
flags |= telegram_api::messages_requestWebView::SEND_AS_MASK;
}
}
send_query(G()->net_query_creator().create(telegram_api::messages_requestWebView(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(input_user), url, start_parameter,
std::move(theme_parameters), reply_to_message_id.get_server_message_id().get())));
std::move(theme_parameters), reply_to_message_id.get_server_message_id().get(), std::move(as_input_peer))));
}
void on_result(BufferSlice packet) final {
@ -102,7 +113,8 @@ class RequestWebViewQuery final : public Td::ResultHandler {
}
auto ptr = result_ptr.move_as_ok();
td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, reply_to_message_id_);
td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, reply_to_message_id_,
as_dialog_id_);
promise_.set_value(td_api::make_object<td_api::webAppInfo>(ptr->query_id_, ptr->url_));
}
@ -120,7 +132,8 @@ class ProlongWebViewQuery final : public Td::ResultHandler {
DialogId dialog_id_;
public:
void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId reply_to_message_id, bool silent) {
void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId reply_to_message_id, bool silent,
DialogId as_dialog_id) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
@ -133,13 +146,22 @@ class ProlongWebViewQuery final : public Td::ResultHandler {
if (reply_to_message_id.is_valid()) {
flags |= telegram_api::messages_prolongWebView::REPLY_TO_MSG_ID_MASK;
}
if (silent) {
flags |= telegram_api::messages_prolongWebView::SILENT_MASK;
}
tl_object_ptr<telegram_api::InputPeer> as_input_peer;
if (as_dialog_id.is_valid()) {
as_input_peer = td_->messages_manager_->get_input_peer(as_dialog_id, AccessRights::Write);
if (as_input_peer != nullptr) {
flags |= telegram_api::messages_prolongWebView::SEND_AS_MASK;
}
}
send_query(G()->net_query_creator().create(telegram_api::messages_prolongWebView(
flags, false /*ignored*/, std::move(input_peer), r_input_user.move_as_ok(), query_id,
reply_to_message_id.get_server_message_id().get())));
reply_to_message_id.get_server_message_id().get(), std::move(as_input_peer))));
}
void on_result(BufferSlice packet) final {
@ -268,12 +290,18 @@ void AttachMenuManager::AttachMenuBotColor::parse(ParserT &parser) {
}
bool operator==(const AttachMenuManager::AttachMenuBot &lhs, const AttachMenuManager::AttachMenuBot &rhs) {
return lhs.user_id_ == rhs.user_id_ && lhs.name_ == rhs.name_ &&
return lhs.user_id_ == rhs.user_id_ && lhs.supports_self_dialog_ == rhs.supports_self_dialog_ &&
lhs.supports_user_dialogs_ == rhs.supports_user_dialogs_ &&
lhs.supports_bot_dialogs_ == rhs.supports_bot_dialogs_ &&
lhs.supports_group_dialogs_ == rhs.supports_group_dialogs_ &&
lhs.supports_broadcast_dialogs_ == rhs.supports_broadcast_dialogs_ &&
lhs.supports_settings_ == rhs.supports_settings_ && lhs.name_ == rhs.name_ &&
lhs.default_icon_file_id_ == rhs.default_icon_file_id_ &&
lhs.ios_static_icon_file_id_ == rhs.ios_static_icon_file_id_ &&
lhs.ios_animated_icon_file_id_ == rhs.ios_animated_icon_file_id_ &&
lhs.android_icon_file_id_ == rhs.android_icon_file_id_ && lhs.macos_icon_file_id_ == rhs.macos_icon_file_id_ &&
lhs.is_added_ == rhs.is_added_ && lhs.name_color_ == rhs.name_color_ && lhs.icon_color_ == rhs.icon_color_;
lhs.is_added_ == rhs.is_added_ && lhs.name_color_ == rhs.name_color_ && lhs.icon_color_ == rhs.icon_color_ &&
lhs.placeholder_file_id_ == rhs.placeholder_file_id_;
}
bool operator!=(const AttachMenuManager::AttachMenuBot &lhs, const AttachMenuManager::AttachMenuBot &rhs) {
@ -288,6 +316,9 @@ void AttachMenuManager::AttachMenuBot::store(StorerT &storer) const {
bool has_macos_icon_file_id = macos_icon_file_id_.is_valid();
bool has_name_color = name_color_ != AttachMenuBotColor();
bool has_icon_color = icon_color_ != AttachMenuBotColor();
bool has_support_flags = true;
bool has_placeholder_file_id = placeholder_file_id_.is_valid();
bool has_cache_version = cache_version_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_ios_static_icon_file_id);
STORE_FLAG(has_ios_animated_icon_file_id);
@ -296,6 +327,15 @@ void AttachMenuManager::AttachMenuBot::store(StorerT &storer) const {
STORE_FLAG(is_added_);
STORE_FLAG(has_name_color);
STORE_FLAG(has_icon_color);
STORE_FLAG(has_support_flags);
STORE_FLAG(supports_self_dialog_);
STORE_FLAG(supports_user_dialogs_);
STORE_FLAG(supports_bot_dialogs_);
STORE_FLAG(supports_group_dialogs_);
STORE_FLAG(supports_broadcast_dialogs_);
STORE_FLAG(supports_settings_);
STORE_FLAG(has_placeholder_file_id);
STORE_FLAG(has_cache_version);
END_STORE_FLAGS();
td::store(user_id_, storer);
td::store(name_, storer);
@ -318,6 +358,12 @@ void AttachMenuManager::AttachMenuBot::store(StorerT &storer) const {
if (has_icon_color) {
td::store(icon_color_, storer);
}
if (has_placeholder_file_id) {
td::store(placeholder_file_id_, storer);
}
if (has_cache_version) {
td::store(cache_version_, storer);
}
}
template <class ParserT>
@ -328,6 +374,9 @@ void AttachMenuManager::AttachMenuBot::parse(ParserT &parser) {
bool has_macos_icon_file_id;
bool has_name_color;
bool has_icon_color;
bool has_support_flags;
bool has_placeholder_file_id;
bool has_cache_version;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_ios_static_icon_file_id);
PARSE_FLAG(has_ios_animated_icon_file_id);
@ -336,6 +385,15 @@ void AttachMenuManager::AttachMenuBot::parse(ParserT &parser) {
PARSE_FLAG(is_added_);
PARSE_FLAG(has_name_color);
PARSE_FLAG(has_icon_color);
PARSE_FLAG(has_support_flags);
PARSE_FLAG(supports_self_dialog_);
PARSE_FLAG(supports_user_dialogs_);
PARSE_FLAG(supports_bot_dialogs_);
PARSE_FLAG(supports_group_dialogs_);
PARSE_FLAG(supports_broadcast_dialogs_);
PARSE_FLAG(supports_settings_);
PARSE_FLAG(has_placeholder_file_id);
PARSE_FLAG(has_cache_version);
END_PARSE_FLAGS();
td::parse(user_id_, parser);
td::parse(name_, parser);
@ -358,6 +416,18 @@ void AttachMenuManager::AttachMenuBot::parse(ParserT &parser) {
if (has_icon_color) {
td::parse(icon_color_, parser);
}
if (has_placeholder_file_id) {
td::parse(placeholder_file_id_, parser);
}
if (has_cache_version) {
td::parse(cache_version_, parser);
}
if (!has_support_flags) {
supports_self_dialog_ = true;
supports_user_dialogs_ = true;
supports_bot_dialogs_ = true;
}
}
class AttachMenuManager::AttachMenuBotsLogEvent {
@ -425,7 +495,13 @@ void AttachMenuManager::init() {
dependencies.add(attach_menu_bot.user_id_);
}
if (is_valid && dependencies.resolve_force(td_, "AttachMenuBotsLogEvent")) {
hash_ = attach_menu_bots_log_event.hash_;
bool is_cache_outdated = false;
for (auto &bot : attach_menu_bots_log_event.attach_menu_bots_) {
if (bot.cache_version_ != AttachMenuBot::CACHE_VERSION) {
is_cache_outdated = true;
}
}
hash_ = is_cache_outdated ? 0 : attach_menu_bots_log_event.hash_;
attach_menu_bots_ = std::move(attach_menu_bots_log_event.attach_menu_bots_);
} else {
LOG(ERROR) << "Ignore invalid attachment menu bots log event";
@ -493,7 +569,8 @@ void AttachMenuManager::ping_web_view() {
const auto &opened_web_view = it.second;
bool silent = td_->messages_manager_->get_dialog_silent_send_message(opened_web_view.dialog_id_);
td_->create_handler<ProlongWebViewQuery>()->send(opened_web_view.dialog_id_, opened_web_view.bot_user_id_, it.first,
opened_web_view.reply_to_message_id_, silent);
opened_web_view.reply_to_message_id_, silent,
opened_web_view.as_dialog_id_);
}
schedule_ping_web_view();
@ -517,12 +594,12 @@ void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id,
switch (dialog_id.get_type()) {
case DialogType::User:
// ok
break;
case DialogType::Chat:
case DialogType::Channel:
// ok
break;
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Web apps can be opened only in private chats"));
return promise.set_error(Status::Error(400, "Web Apps can't be opened in secret chats"));
case DialogType::None:
default:
UNREACHABLE();
@ -538,16 +615,17 @@ void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id,
}
bool silent = td_->messages_manager_->get_dialog_silent_send_message(dialog_id);
DialogId as_dialog_id = td_->messages_manager_->get_dialog_default_send_message_as_dialog_id(dialog_id);
td_->create_handler<RequestWebViewQuery>(std::move(promise))
->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), reply_to_message_id,
silent);
silent, as_dialog_id);
}
void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id,
MessageId reply_to_message_id) {
MessageId reply_to_message_id, DialogId as_dialog_id) {
if (query_id == 0) {
LOG(ERROR) << "Receive web app query identifier == 0";
LOG(ERROR) << "Receive Web App query identifier == 0";
return;
}
@ -558,6 +636,7 @@ void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId
opened_web_view.dialog_id_ = dialog_id;
opened_web_view.bot_user_id_ = bot_user_id;
opened_web_view.reply_to_message_id_ = reply_to_message_id;
opened_web_view.as_dialog_id_ = as_dialog_id;
opened_web_views_.emplace(query_id, std::move(opened_web_view));
}
@ -589,7 +668,7 @@ Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
CHECK(document_id == telegram_api::document::ID);
if (name != "default_static" && name != "ios_static" && name != "ios_animated" && name != "android_animated" &&
name != "macos_animated") {
name != "macos_animated" && name != "placeholder_static") {
LOG(ERROR) << "Have icon for " << user_id << " with name " << name;
continue;
}
@ -598,9 +677,7 @@ Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
auto parsed_document =
td_->documents_manager_->on_get_document(move_tl_object_as<telegram_api::document>(icon->icon_), DialogId());
if (parsed_document.type != expected_document_type) {
if (user_id != UserId(static_cast<int64>(5000860301)) || !G()->is_test_dc() || name != "macos_animated") {
LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id;
}
LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id;
continue;
}
bool expect_colors = false;
@ -621,6 +698,9 @@ Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
case '_':
attach_menu_bot.macos_icon_file_id_ = parsed_document.file_id;
break;
case 'h':
attach_menu_bot.placeholder_file_id_ = parsed_document.file_id;
break;
default:
UNREACHABLE();
}
@ -671,10 +751,33 @@ Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
}
}
}
for (auto &peer_type : bot->peer_types_) {
switch (peer_type->get_id()) {
case telegram_api::attachMenuPeerTypeSameBotPM::ID:
attach_menu_bot.supports_self_dialog_ = true;
break;
case telegram_api::attachMenuPeerTypeBotPM::ID:
attach_menu_bot.supports_bot_dialogs_ = true;
break;
case telegram_api::attachMenuPeerTypePM::ID:
attach_menu_bot.supports_user_dialogs_ = true;
break;
case telegram_api::attachMenuPeerTypeChat::ID:
attach_menu_bot.supports_group_dialogs_ = true;
break;
case telegram_api::attachMenuPeerTypeBroadcast::ID:
attach_menu_bot.supports_broadcast_dialogs_ = true;
break;
default:
UNREACHABLE();
break;
}
}
attach_menu_bot.supports_settings_ = bot->has_settings_;
if (!attach_menu_bot.default_icon_file_id_.is_valid()) {
return Status::Error(PSLICE() << "Have no default icon for " << user_id);
}
attach_menu_bot.cache_version_ = AttachMenuBot::CACHE_VERSION;
return std::move(attach_menu_bot);
}
@ -872,11 +975,13 @@ td_api::object_ptr<td_api::attachmentMenuBot> AttachMenuManager::get_attachment_
};
return td_api::make_object<td_api::attachmentMenuBot>(
td_->contacts_manager_->get_user_id_object(bot.user_id_, "get_attachment_menu_bot_object"), bot.name_,
td_->contacts_manager_->get_user_id_object(bot.user_id_, "get_attachment_menu_bot_object"),
bot.supports_self_dialog_, bot.supports_user_dialogs_, bot.supports_bot_dialogs_, bot.supports_group_dialogs_,
bot.supports_broadcast_dialogs_, bot.supports_settings_, bot.name_,
get_attach_menu_bot_color_object(bot.name_color_), get_file(bot.default_icon_file_id_),
get_file(bot.ios_static_icon_file_id_), get_file(bot.ios_animated_icon_file_id_),
get_file(bot.android_icon_file_id_), get_file(bot.macos_icon_file_id_),
get_attach_menu_bot_color_object(bot.icon_color_));
get_attach_menu_bot_color_object(bot.icon_color_), get_file(bot.placeholder_file_id_));
}
td_api::object_ptr<td_api::updateAttachmentMenuBots> AttachMenuManager::get_update_attachment_menu_bots_object() const {

View File

@ -35,7 +35,8 @@ class AttachMenuManager final : public Actor {
td_api::object_ptr<td_api::themeParameters> &&theme,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,
DialogId as_dialog_id);
void close_web_view(int64 query_id, Promise<Unit> &&promise);
@ -74,6 +75,12 @@ class AttachMenuManager final : public Actor {
struct AttachMenuBot {
bool is_added_ = false;
UserId user_id_;
bool supports_self_dialog_ = false;
bool supports_user_dialogs_ = false;
bool supports_bot_dialogs_ = false;
bool supports_group_dialogs_ = false;
bool supports_broadcast_dialogs_ = false;
bool supports_settings_ = false;
string name_;
AttachMenuBotColor name_color_;
FileId default_icon_file_id_;
@ -82,6 +89,10 @@ class AttachMenuManager final : public Actor {
FileId android_icon_file_id_;
FileId macos_icon_file_id_;
AttachMenuBotColor icon_color_;
FileId placeholder_file_id_;
static constexpr uint32 CACHE_VERSION = 1;
uint32 cache_version_ = 0;
template <class StorerT>
void store(StorerT &storer) const;
@ -138,6 +149,7 @@ class AttachMenuManager final : public Actor {
DialogId dialog_id_;
UserId bot_user_id_;
MessageId reply_to_message_id_;
DialogId as_dialog_id_;
};
FlatHashMap<int64, OpenedWebView> opened_web_views_;
Timeout ping_web_view_timeout_;

View File

@ -8,6 +8,7 @@
#include "td/telegram/AuthManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/Td.h"
@ -206,7 +207,8 @@ void AudiosManager::create_audio(FileId file_id, string minithumbnail, PhotoSize
SecretInputMedia AudiosManager::get_secret_input_media(FileId audio_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const {
const string &caption, BufferSlice thumbnail,
int32 layer) const {
auto *audio = get_audio(audio_file_id);
CHECK(audio != nullptr);
auto file_view = td_->file_manager_->get_file_view(audio_file_id);
@ -236,7 +238,8 @@ SecretInputMedia AudiosManager::get_secret_input_media(FileId audio_file_id,
audio->mime_type,
file_view,
std::move(attributes),
caption};
caption,
layer};
}
tl_object_ptr<telegram_api::InputMedia> AudiosManager::get_input_media(

View File

@ -41,7 +41,7 @@ class AudiosManager {
SecretInputMedia get_secret_input_media(FileId audio_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const;
const string &caption, BufferSlice thumbnail, int32 layer) const;
FileId get_audio_thumbnail_file_id(FileId file_id) const;

View File

@ -685,7 +685,7 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
} else {
status = std::move(result->error());
}
LOG_IF(ERROR, status.is_error()) << "Receive error for auth.logOut: " << status;
LOG_IF(ERROR, status.is_error() && status.error().code() != 401) << "Receive error for auth.logOut: " << status;
// state_ will stay LoggingOut, so no queries will work.
destroy_auth_keys();
if (query_id_ != 0) {
@ -693,6 +693,10 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
}
}
void AuthManager::on_authorization_lost(string source) {
if (state_ == State::LoggingOut && net_query_type_ == NetQueryType::LogOut) {
LOG(INFO) << "Ignore authorization loss because of " << source << ", while logging out";
return;
}
LOG(WARNING) << "Lost authorization because of " << source;
destroy_auth_keys();
}
@ -803,7 +807,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
} else {
td_->set_is_bot_online(true);
}
send_closure(G()->config_manager(), &ConfigManager::request_config);
send_closure(G()->config_manager(), &ConfigManager::request_config, false);
if (query_id_ != 0) {
on_query_ok();
}

View File

@ -13,6 +13,7 @@
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Status.h"
namespace td {
@ -25,9 +26,13 @@ static td_api::object_ptr<td_api::autoDownloadSettings> convert_auto_download_se
auto video_preload_large = (flags & telegram_api::autoDownloadSettings::VIDEO_PRELOAD_LARGE_MASK) != 0;
auto audio_preload_next = (flags & telegram_api::autoDownloadSettings::AUDIO_PRELOAD_NEXT_MASK) != 0;
auto phonecalls_less_data = (flags & telegram_api::autoDownloadSettings::PHONECALLS_LESS_DATA_MASK) != 0;
constexpr int32 MAX_PHOTO_SIZE = 10 * (1 << 20) /* 10 MB */;
constexpr int64 MAX_DOCUMENT_SIZE = (static_cast<int64>(1) << 52);
return td_api::make_object<td_api::autoDownloadSettings>(
!disabled, settings->photo_size_max_, settings->video_size_max_, settings->file_size_max_,
settings->video_upload_maxbitrate_, video_preload_large, audio_preload_next, phonecalls_less_data);
!disabled, clamp(settings->photo_size_max_, static_cast<int32>(0), MAX_PHOTO_SIZE),
clamp(settings->video_size_max_, static_cast<int64>(0), MAX_DOCUMENT_SIZE),
clamp(settings->file_size_max_, static_cast<int64>(0), MAX_DOCUMENT_SIZE), settings->video_upload_maxbitrate_,
video_preload_large, audio_preload_next, phonecalls_less_data);
}
class GetAutoDownloadSettingsQuery final : public Td::ResultHandler {

View File

@ -20,8 +20,8 @@ class Td;
class AutoDownloadSettings {
public:
int32 max_photo_file_size = 0;
int32 max_video_file_size = 0;
int32 max_other_file_size = 0;
int64 max_video_file_size = 0;
int64 max_other_file_size = 0;
int32 video_upload_bitrate = 0;
bool is_enabled = false;
bool preload_large_videos = false;

View File

@ -0,0 +1,61 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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)
//
#include "td/telegram/AvailableReaction.h"
#include "td/utils/algorithm.h"
namespace td {
AvailableReactionType get_reaction_type(const vector<AvailableReaction> &available_reactions, const string &reaction) {
for (auto &available_reaction : available_reactions) {
if (available_reaction.reaction_ == reaction) {
if (available_reaction.is_premium_) {
return AvailableReactionType::NeedsPremium;
}
return AvailableReactionType::Available;
}
}
return AvailableReactionType::Unavailable;
}
vector<string> get_active_reactions(const vector<string> &available_reactions,
const vector<AvailableReaction> &active_reactions) {
if (available_reactions.empty()) {
// fast path
return available_reactions;
}
if (available_reactions.size() == active_reactions.size()) {
size_t i;
for (i = 0; i < available_reactions.size(); i++) {
if (available_reactions[i] != active_reactions[i].reaction_) {
break;
}
}
if (i == available_reactions.size()) {
// fast path
return available_reactions;
}
}
vector<string> result;
for (const auto &active_reaction : active_reactions) {
if (td::contains(available_reactions, active_reaction.reaction_)) {
result.push_back(active_reaction.reaction_);
}
}
return result;
}
td_api::object_ptr<td_api::availableReaction> AvailableReaction::get_available_reaction_object() const {
return td_api::make_object<td_api::availableReaction>(reaction_, is_premium_);
}
bool operator==(const AvailableReaction &lhs, const AvailableReaction &rhs) {
return lhs.reaction_ == rhs.reaction_ && lhs.is_premium_ == rhs.is_premium_;
}
} // namespace td

View File

@ -0,0 +1,38 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
namespace td {
struct AvailableReaction {
string reaction_;
bool is_premium_;
AvailableReaction(const string &reaction, bool is_premium) : reaction_(reaction), is_premium_(is_premium) {
}
td_api::object_ptr<td_api::availableReaction> get_available_reaction_object() const;
};
bool operator==(const AvailableReaction &lhs, const AvailableReaction &rhs);
inline bool operator!=(const AvailableReaction &lhs, const AvailableReaction &rhs) {
return !(lhs == rhs);
}
enum class AvailableReactionType : int32 { Unavailable, Available, NeedsPremium };
AvailableReactionType get_reaction_type(const vector<AvailableReaction> &reactions, const string &reaction);
vector<string> get_active_reactions(const vector<string> &available_reactions,
const vector<AvailableReaction> &active_reactions);
} // namespace td

View File

@ -35,6 +35,8 @@
#include "td/utils/SliceBuilder.h"
#include "td/utils/tl_helpers.h"
#include <algorithm>
namespace td {
class GetBackgroundQuery final : public Td::ResultHandler {

View File

@ -84,7 +84,9 @@ class GetBotMenuButtonQuery final : public Td::ResultHandler {
};
unique_ptr<BotMenuButton> get_bot_menu_button(telegram_api::object_ptr<telegram_api::BotMenuButton> &&bot_menu_button) {
CHECK(bot_menu_button != nullptr);
if (bot_menu_button == nullptr) {
return nullptr;
}
switch (bot_menu_button->get_id()) {
case telegram_api::botMenuButtonCommands::ID:
return nullptr;

View File

@ -11,6 +11,7 @@
#include "td/telegram/DhCache.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/Global.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetQueryCreator.h"
@ -74,6 +75,7 @@ CallConnection::CallConnection(const telegram_api::PhoneConnection &connection)
ipv6 = conn.ipv6_;
port = conn.port_;
peer_tag = conn.peer_tag_.as_slice().str();
is_tcp = conn.tcp_;
break;
}
case telegram_api::phoneConnectionWebrtc::ID: {
@ -103,7 +105,7 @@ tl_object_ptr<td_api::callServer> CallConnection::get_call_server_object() const
auto server_type = [&]() -> tl_object_ptr<td_api::CallServerType> {
switch (type) {
case Type::Telegram:
return make_tl_object<td_api::callServerTypeTelegramReflector>(peer_tag);
return make_tl_object<td_api::callServerTypeTelegramReflector>(peer_tag, is_tcp);
case Type::Webrtc:
return make_tl_object<td_api::callServerTypeWebrtc>(username, password, supports_turn, supports_stun);
default:

View File

@ -20,6 +20,7 @@
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/Status.h"
@ -55,6 +56,7 @@ struct CallConnection {
// Telegram
string peer_tag;
bool is_tcp = false;
// WebRTC
string username;

View File

@ -13,6 +13,8 @@
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"

View File

@ -24,6 +24,7 @@
#include "td/utils/port/RwMutex.h"
#include "td/utils/port/thread.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/utf8.h"

View File

@ -22,6 +22,7 @@
#include "td/telegram/net/NetType.h"
#include "td/telegram/net/PublicRsaKeyShared.h"
#include "td/telegram/net/Session.h"
#include "td/telegram/Premium.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
@ -908,7 +909,7 @@ void ConfigManager::start_up() {
auto expire_time = load_config_expire_time();
if (expire_time.is_in_past() || true) {
request_config();
request_config(false);
} else {
expire_time_ = expire_time;
set_timeout_in(expire_time_.in());
@ -934,7 +935,7 @@ void ConfigManager::hangup() {
void ConfigManager::loop() {
if (expire_time_ && expire_time_.is_in_past()) {
request_config();
request_config(reopen_sessions_after_get_config_);
expire_time_ = {};
}
}
@ -945,17 +946,17 @@ void ConfigManager::try_stop() {
}
}
void ConfigManager::request_config() {
void ConfigManager::request_config(bool reopen_sessions) {
if (G()->close_flag()) {
return;
}
if (config_sent_cnt_ != 0) {
if (config_sent_cnt_ != 0 && !reopen_sessions) {
return;
}
lazy_request_flood_control_.add_event(static_cast<int32>(Timestamp::now().at()));
request_config_from_dc_impl(DcId::main());
request_config_from_dc_impl(DcId::main(), reopen_sessions);
}
void ConfigManager::lazy_request_config() {
@ -1085,11 +1086,13 @@ void ConfigManager::on_dc_options_update(DcOptions dc_options) {
send_closure(config_recoverer_, &ConfigRecoverer::on_dc_options_update, std::move(dc_options));
}
void ConfigManager::request_config_from_dc_impl(DcId dc_id) {
void ConfigManager::request_config_from_dc_impl(DcId dc_id, bool reopen_sessions) {
config_sent_cnt_++;
reopen_sessions_after_get_config_ |= reopen_sessions;
auto query = G()->net_query_creator().create_unauth(telegram_api::help_getConfig(), dc_id);
query->total_timeout_limit_ = 60 * 60 * 24;
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 8));
G()->net_query_dispatcher().dispatch_with_callback(std::move(query),
actor_shared(this, 8 + static_cast<uint64>(reopen_sessions)));
}
void ConfigManager::do_set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions) {
@ -1256,7 +1259,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
return;
}
CHECK(token == 8);
CHECK(token == 8 || token == 9);
CHECK(config_sent_cnt_ > 0);
config_sent_cnt_--;
auto r_config = fetch_result<telegram_api::help_getConfig>(std::move(res));
@ -1269,6 +1272,9 @@ void ConfigManager::on_result(NetQueryPtr res) {
} else {
on_dc_options_update(DcOptions());
process_config(r_config.move_as_ok());
if (token == 9) {
G()->net_query_dispatcher().update_mtproto_header();
}
}
}
@ -1472,12 +1478,20 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
double animated_emoji_zoom = 0.0;
string default_reaction;
int64 reactions_uniq_max = 0;
vector<string> premium_features;
auto &premium_limit_keys = get_premium_limit_keys();
string premium_bot_username;
string premium_invoice_slug;
bool is_premium_available = false;
int32 stickers_premium_by_emoji_num = 0;
int32 stickers_normal_by_emoji_per_premium_num = 2;
if (config->get_id() == telegram_api::jsonObject::ID) {
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
Slice key = key_value->key_;
telegram_api::JSONValue *value = key_value->value_.get();
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config" ||
key == "stickers_emoji_cache_time") {
if (key == "message_animated_emoji_max" || key == "stickers_emoji_cache_time" || key == "test" ||
key == "upload_max_fileparts_default" || key == "upload_max_fileparts_premium" ||
key == "wallet_blockchain_name" || key == "wallet_config" || key == "wallet_enabled") {
continue;
}
if (key == "ignore_restriction_reasons") {
@ -1733,6 +1747,59 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
G()->shared_config().set_option_integer("notification_sound_count_max", setting_value);
continue;
}
if (key == "premium_promo_order") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto features = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &feature : features) {
auto premium_feature = get_json_value_string(std::move(feature), key);
if (!td::contains(premium_feature, ',')) {
premium_features.push_back(std::move(premium_feature));
}
}
} else {
LOG(ERROR) << "Receive unexpected premium_promo_order " << to_string(*value);
}
continue;
}
bool is_premium_limit_key = false;
for (auto premium_limit_key : premium_limit_keys) {
if (begins_with(key, premium_limit_key)) {
auto suffix = key.substr(premium_limit_key.size());
if (suffix == "_limit_default" || suffix == "_limit_premium") {
auto setting_value = get_json_value_int(std::move(key_value->value_), key);
if (setting_value > 0) {
G()->shared_config().set_option_integer(key, setting_value);
} else {
LOG(ERROR) << "Receive invalid value " << setting_value << " for " << key;
}
is_premium_limit_key = true;
break;
}
}
}
if (is_premium_limit_key) {
continue;
}
if (key == "premium_bot_username") {
premium_bot_username = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "premium_invoice_slug") {
premium_invoice_slug = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "premium_purchase_blocked") {
is_premium_available = !get_json_value_bool(std::move(key_value->value_), key);
continue;
}
if (key == "stickers_premium_by_emoji_num") {
stickers_premium_by_emoji_num = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "stickers_normal_by_emoji_per_premium_num") {
stickers_normal_by_emoji_per_premium_num = get_json_value_int(std::move(key_value->value_), key);
continue;
}
new_values.push_back(std::move(key_value));
}
@ -1816,6 +1883,46 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
shared_config.set_option_integer("reactions_uniq_max", reactions_uniq_max);
}
bool is_premium = shared_config.get_option_boolean("is_premium");
auto chat_filter_count_max = shared_config.get_option_integer(
is_premium ? Slice("dialog_filters_limit_premium") : Slice("dialog_filters_limit_default"), is_premium ? 20 : 10);
shared_config.set_option_integer("chat_filter_count_max", static_cast<int32>(chat_filter_count_max));
auto chat_filter_chosen_chat_count_max = shared_config.get_option_integer(
is_premium ? Slice("dialog_filters_chats_limit_premium") : Slice("dialog_filters_chats_limit_default"),
is_premium ? 200 : 100);
shared_config.set_option_integer("chat_filter_chosen_chat_count_max",
static_cast<int32>(chat_filter_chosen_chat_count_max));
auto bio_length_max = shared_config.get_option_integer(
is_premium ? Slice("about_length_limit_premium") : Slice("about_length_limit_default"), is_premium ? 140 : 70);
shared_config.set_option_integer("bio_length_max", bio_length_max);
if (!is_premium_available) {
premium_bot_username.clear(); // just in case
premium_invoice_slug.clear(); // just in case
premium_features.clear(); // just in case
shared_config.set_option_empty("is_premium_available");
} else {
shared_config.set_option_boolean("is_premium_available", is_premium_available);
}
shared_config.set_option_string("premium_features", implode(premium_features, ','));
if (premium_bot_username.empty()) {
shared_config.set_option_empty("premium_bot_username");
} else {
shared_config.set_option_string("premium_bot_username", premium_bot_username);
}
if (premium_invoice_slug.empty()) {
shared_config.set_option_empty("premium_invoice_slug");
} else {
shared_config.set_option_string("premium_invoice_slug", premium_invoice_slug);
}
shared_config.set_option_integer("stickers_premium_by_emoji_num", stickers_premium_by_emoji_num);
shared_config.set_option_integer("stickers_normal_by_emoji_per_premium_num",
stickers_normal_by_emoji_per_premium_num);
shared_config.set_option_empty("default_ton_blockchain_config");
shared_config.set_option_empty("default_ton_blockchain_name");

View File

@ -81,7 +81,7 @@ class ConfigManager final : public NetQueryCallback {
public:
explicit ConfigManager(ActorShared<> parent);
void request_config();
void request_config(bool reopen_sessions);
void lazy_request_config();
@ -108,6 +108,7 @@ class ConfigManager final : public NetQueryCallback {
private:
ActorShared<> parent_;
int32 config_sent_cnt_{0};
bool reopen_sessions_after_get_config_{false};
ActorOwn<ConfigRecoverer> config_recoverer_;
int ref_cnt_{1};
Timestamp expire_time_;
@ -141,7 +142,7 @@ class ConfigManager final : public NetQueryCallback {
void on_result(NetQueryPtr res) final;
void request_config_from_dc_impl(DcId dc_id);
void request_config_from_dc_impl(DcId dc_id, bool reopen_sessions);
void process_config(tl_object_ptr<telegram_api::config> config);
void try_request_app_config();

View File

@ -6,13 +6,17 @@
//
#include "td/telegram/ContactsManager.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/BotMenuButton.h"
#include "td/telegram/ChannelParticipantFilter.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/DialogInviteLink.h"
#include "td/telegram/DialogLocation.h"
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
@ -843,6 +847,86 @@ class ToggleChannelSignaturesQuery final : public Td::ResultHandler {
}
};
class ToggleChannelJoinToSendQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit ToggleChannelJoinToSendQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, bool join_to_send) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(
telegram_api::channels_toggleJoinToSend(std::move(input_channel), join_to_send)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_toggleJoinToSend>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for ToggleChannelJoinToSendQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
if (status.message() == "CHAT_NOT_MODIFIED") {
if (!td_->auth_manager_->is_bot()) {
promise_.set_value(Unit());
return;
}
} else {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "ToggleChannelJoinToSendQuery");
}
promise_.set_error(std::move(status));
}
};
class ToggleChannelJoinRequestQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit ToggleChannelJoinRequestQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, bool join_request) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(
telegram_api::channels_toggleJoinRequest(std::move(input_channel), join_request)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_toggleJoinRequest>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for ToggleChannelJoinRequestQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
if (status.message() == "CHAT_NOT_MODIFIED") {
if (!td_->auth_manager_->is_bot()) {
promise_.set_value(Unit());
return;
}
} else {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "ToggleChannelJoinRequestQuery");
}
promise_.set_error(std::move(status));
}
};
class TogglePrehistoryHiddenQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
@ -1339,7 +1423,7 @@ class ExportChatInviteQuery final : public Td::ResultHandler {
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for ExportChatInviteQuery: " << to_string(ptr);
DialogInviteLink invite_link(std::move(ptr));
DialogInviteLink invite_link(std::move(ptr), "ExportChatInviteQuery");
if (!invite_link.is_valid()) {
return on_error(Status::Error(500, "Receive invalid invite link"));
}
@ -1401,7 +1485,7 @@ class EditChatInviteLinkQuery final : public Td::ResultHandler {
td_->contacts_manager_->on_get_users(std::move(invite->users_), "EditChatInviteLinkQuery");
DialogInviteLink invite_link(std::move(invite->invite_));
DialogInviteLink invite_link(std::move(invite->invite_), "EditChatInviteLinkQuery");
if (!invite_link.is_valid()) {
return on_error(Status::Error(500, "Receive invalid invite link"));
}
@ -1450,7 +1534,7 @@ class GetExportedChatInviteQuery final : public Td::ResultHandler {
td_->contacts_manager_->on_get_users(std::move(result->users_), "GetExportedChatInviteQuery");
DialogInviteLink invite_link(std::move(result->invite_));
DialogInviteLink invite_link(std::move(result->invite_), "GetExportedChatInviteQuery");
if (!invite_link.is_valid()) {
LOG(ERROR) << "Receive invalid invite link in " << dialog_id_;
return on_error(Status::Error(500, "Receive invalid invite link"));
@ -1512,7 +1596,7 @@ class GetExportedChatInvitesQuery final : public Td::ResultHandler {
}
vector<td_api::object_ptr<td_api::chatInviteLink>> invite_links;
for (auto &invite : result->invites_) {
DialogInviteLink invite_link(std::move(invite));
DialogInviteLink invite_link(std::move(invite), "GetExportedChatInvitesQuery");
if (!invite_link.is_valid()) {
LOG(ERROR) << "Receive invalid invite link in " << dialog_id_;
total_count--;
@ -1853,7 +1937,7 @@ class RevokeChatInviteLinkQuery final : public Td::ResultHandler {
td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery");
DialogInviteLink invite_link(std::move(invite->invite_));
DialogInviteLink invite_link(std::move(invite->invite_), "RevokeChatInviteLinkQuery");
if (!invite_link.is_valid()) {
return on_error(Status::Error(500, "Receive invalid invite link"));
}
@ -1865,8 +1949,8 @@ class RevokeChatInviteLinkQuery final : public Td::ResultHandler {
td_->contacts_manager_->on_get_users(std::move(invite->users_), "RevokeChatInviteLinkQuery replaced");
DialogInviteLink invite_link(std::move(invite->invite_));
DialogInviteLink new_invite_link(std::move(invite->new_invite_));
DialogInviteLink invite_link(std::move(invite->invite_), "RevokeChatInviteLinkQuery replaced");
DialogInviteLink new_invite_link(std::move(invite->new_invite_), "RevokeChatInviteLinkQuery new replaced");
if (!invite_link.is_valid() || !new_invite_link.is_valid()) {
return on_error(Status::Error(500, "Receive invalid invite link"));
}
@ -3528,26 +3612,28 @@ void ContactsManager::User::store(StorerT &storer) const {
STORE_FLAG(is_deleted);
STORE_FLAG(is_bot);
STORE_FLAG(can_join_groups);
STORE_FLAG(can_read_all_group_messages);
STORE_FLAG(can_read_all_group_messages); // 5
STORE_FLAG(is_inline_bot);
STORE_FLAG(need_location_bot);
STORE_FLAG(has_last_name);
STORE_FLAG(has_username);
STORE_FLAG(has_photo);
STORE_FLAG(false); // legacy is_restricted
STORE_FLAG(has_photo); // 10
STORE_FLAG(false); // legacy is_restricted
STORE_FLAG(has_language_code);
STORE_FLAG(have_access_hash);
STORE_FLAG(is_support);
STORE_FLAG(is_min_access_hash);
STORE_FLAG(is_min_access_hash); // 15
STORE_FLAG(is_scam);
STORE_FLAG(has_cache_version);
STORE_FLAG(has_is_contact);
STORE_FLAG(is_contact);
STORE_FLAG(is_mutual_contact);
STORE_FLAG(is_mutual_contact); // 20
STORE_FLAG(has_restriction_reasons);
STORE_FLAG(need_apply_min_photo);
STORE_FLAG(is_fake);
STORE_FLAG(can_be_added_to_attach_menu);
STORE_FLAG(is_premium); // 25
STORE_FLAG(attach_menu_enabled);
END_STORE_FLAGS();
store(first_name, storer);
if (has_last_name) {
@ -3619,6 +3705,8 @@ void ContactsManager::User::parse(ParserT &parser) {
PARSE_FLAG(need_apply_min_photo);
PARSE_FLAG(is_fake);
PARSE_FLAG(can_be_added_to_attach_menu);
PARSE_FLAG(is_premium);
PARSE_FLAG(attach_menu_enabled);
END_PARSE_FLAGS();
parse(first_name, parser);
if (has_last_name) {
@ -3709,6 +3797,8 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
bool has_group_administrator_rights = group_administrator_rights != AdministratorRights();
bool has_broadcast_administrator_rights = broadcast_administrator_rights != AdministratorRights();
bool has_menu_button = menu_button != nullptr;
bool has_description_photo = !description_photo.is_empty();
bool has_description_animation = description_animation_file_id.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_about);
STORE_FLAG(is_blocked);
@ -3724,6 +3814,8 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
STORE_FLAG(has_group_administrator_rights);
STORE_FLAG(has_broadcast_administrator_rights);
STORE_FLAG(has_menu_button);
STORE_FLAG(has_description_photo);
STORE_FLAG(has_description_animation);
END_STORE_FLAGS();
if (has_about) {
store(about, storer);
@ -3751,6 +3843,13 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
if (has_menu_button) {
store(menu_button, storer);
}
if (has_description_photo) {
store(description_photo, storer);
}
if (has_description_animation) {
storer.context()->td().get_actor_unsafe()->animations_manager_->store_animation(description_animation_file_id,
storer);
}
}
template <class ParserT>
@ -3764,6 +3863,8 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
bool has_group_administrator_rights;
bool has_broadcast_administrator_rights;
bool has_menu_button;
bool has_description_photo;
bool has_description_animation;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_about);
PARSE_FLAG(is_blocked);
@ -3779,6 +3880,8 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
PARSE_FLAG(has_group_administrator_rights);
PARSE_FLAG(has_broadcast_administrator_rights);
PARSE_FLAG(has_menu_button);
PARSE_FLAG(has_description_photo);
PARSE_FLAG(has_description_animation);
END_PARSE_FLAGS();
if (has_about) {
parse(about, parser);
@ -3806,6 +3909,13 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
if (has_menu_button) {
parse(menu_button, parser);
}
if (has_description_photo) {
parse(description_photo, parser);
}
if (has_description_animation) {
description_animation_file_id =
parser.context()->td().get_actor_unsafe()->animations_manager_->parse_animation(parser);
}
}
template <class StorerT>
@ -4042,6 +4152,8 @@ void ContactsManager::Channel::store(StorerT &storer) const {
STORE_FLAG(is_gigagroup);
STORE_FLAG(noforwards);
STORE_FLAG(can_be_deleted); // 25
STORE_FLAG(join_to_send);
STORE_FLAG(join_request);
END_STORE_FLAGS();
store(status, storer);
@ -4113,6 +4225,8 @@ void ContactsManager::Channel::parse(ParserT &parser) {
PARSE_FLAG(is_gigagroup);
PARSE_FLAG(noforwards);
PARSE_FLAG(can_be_deleted);
PARSE_FLAG(join_to_send);
PARSE_FLAG(join_request);
END_PARSE_FLAGS();
if (use_new_rights) {
@ -6345,7 +6459,8 @@ void ContactsManager::set_name(const string &first_name, const string &last_name
}
void ContactsManager::set_bio(const string &bio, Promise<Unit> &&promise) {
auto new_bio = strip_empty_characters(bio, MAX_BIO_LENGTH);
auto max_bio_length = static_cast<size_t>(G()->shared_config().get_option_integer("bio_length_max"));
auto new_bio = strip_empty_characters(bio, max_bio_length);
for (auto &c : new_bio) {
if (c == '\n') {
c = ' ';
@ -6482,6 +6597,36 @@ void ContactsManager::toggle_channel_sign_messages(ChannelId channel_id, bool si
td_->create_handler<ToggleChannelSignaturesQuery>(std::move(promise))->send(channel_id, sign_messages);
}
void ContactsManager::toggle_channel_join_to_send(ChannelId channel_id, bool join_to_send, Promise<Unit> &&promise) {
auto c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Supergroup not found"));
}
if (get_channel_type(c) == ChannelType::Broadcast || c->is_gigagroup) {
return promise.set_error(Status::Error(400, "The method can be called only for ordinary supergroups"));
}
if (!get_channel_permissions(c).can_restrict_members()) {
return promise.set_error(Status::Error(400, "Not enough rights"));
}
td_->create_handler<ToggleChannelJoinToSendQuery>(std::move(promise))->send(channel_id, join_to_send);
}
void ContactsManager::toggle_channel_join_request(ChannelId channel_id, bool join_request, Promise<Unit> &&promise) {
auto c = get_channel(channel_id);
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Supergroup not found"));
}
if (get_channel_type(c) == ChannelType::Broadcast || c->is_gigagroup) {
return promise.set_error(Status::Error(400, "The method can be called only for ordinary supergroups"));
}
if (!get_channel_permissions(c).can_restrict_members()) {
return promise.set_error(Status::Error(400, "Not enough rights"));
}
td_->create_handler<ToggleChannelJoinRequestQuery>(std::move(promise))->send(channel_id, join_request);
}
void ContactsManager::toggle_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available,
Promise<Unit> &&promise) {
auto c = get_channel(channel_id);
@ -8493,11 +8638,13 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
}
bool is_verified = (flags & USER_FLAG_IS_VERIFIED) != 0;
bool is_premium = (flags & USER_FLAG_IS_PREMIUM) != 0;
bool is_support = (flags & USER_FLAG_IS_SUPPORT) != 0;
bool is_deleted = (flags & USER_FLAG_IS_DELETED) != 0;
bool can_join_groups = (flags & USER_FLAG_IS_PRIVATE_BOT) == 0;
bool can_read_all_group_messages = (flags & USER_FLAG_IS_BOT_WITH_PRIVACY_DISABLED) != 0;
bool can_be_added_to_attach_menu = (flags & USER_FLAG_IS_ATTACH_MENU_BOT) != 0;
bool attach_menu_enabled = (flags & USER_FLAG_ATTACH_MENU_ENABLED) != 0;
auto restriction_reasons = get_restriction_reasons(std::move(user->restriction_reason_));
bool is_scam = (flags & USER_FLAG_IS_SCAM) != 0;
bool is_inline_bot = (flags & USER_FLAG_IS_INLINE_BOT) != 0;
@ -8520,6 +8667,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
if (is_deleted) {
// just in case
is_verified = false;
is_premium = false;
is_support = false;
is_bot = false;
can_join_groups = false;
@ -8536,15 +8684,17 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
<< "Receive not bot " << user_id << " which has bot info version from " << source;
int32 bot_info_version = has_bot_info_version ? user->bot_info_version_ : -1;
if (is_verified != u->is_verified || is_support != u->is_support || is_bot != u->is_bot ||
can_join_groups != u->can_join_groups || can_read_all_group_messages != u->can_read_all_group_messages ||
restriction_reasons != u->restriction_reasons || is_scam != u->is_scam || is_fake != u->is_fake ||
is_inline_bot != u->is_inline_bot || inline_query_placeholder != u->inline_query_placeholder ||
need_location_bot != u->need_location_bot || can_be_added_to_attach_menu != u->can_be_added_to_attach_menu) {
if (is_verified != u->is_verified || is_premium != u->is_premium || is_support != u->is_support ||
is_bot != u->is_bot || can_join_groups != u->can_join_groups ||
can_read_all_group_messages != u->can_read_all_group_messages || restriction_reasons != u->restriction_reasons ||
is_scam != u->is_scam || is_fake != u->is_fake || is_inline_bot != u->is_inline_bot ||
inline_query_placeholder != u->inline_query_placeholder || need_location_bot != u->need_location_bot ||
can_be_added_to_attach_menu != u->can_be_added_to_attach_menu || attach_menu_enabled != u->attach_menu_enabled) {
LOG_IF(ERROR, is_bot != u->is_bot && !is_deleted && !u->is_deleted && u->is_received)
<< "User.is_bot has changed for " << user_id << "/" << u->username << " from " << source << " from "
<< u->is_bot << " to " << is_bot;
u->is_verified = is_verified;
u->is_premium = is_premium;
u->is_support = is_support;
u->is_bot = is_bot;
u->can_join_groups = can_join_groups;
@ -8556,6 +8706,7 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
u->inline_query_placeholder = std::move(inline_query_placeholder);
u->need_location_bot = need_location_bot;
u->can_be_added_to_attach_menu = can_be_added_to_attach_menu;
u->attach_menu_enabled = attach_menu_enabled;
LOG(DEBUG) << "Info has changed for " << user_id;
u->is_changed = true;
@ -8888,8 +9039,8 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) {
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, user_id.get(), 1, first_name, string(), username, phone_number, std::move(profile_photo),
nullptr, bot_info_version, Auto(), string(), string());
false /*ignored*/, false /*ignored*/, false /*ignored*/, user_id.get(), 1, first_name, string(), username,
phone_number, std::move(profile_photo), nullptr, bot_info_version, Auto(), string(), string());
on_get_user(std::move(user), "get_user_force");
u = get_user(user_id);
CHECK(u != nullptr && u->is_received);
@ -10018,6 +10169,12 @@ void ContactsManager::for_each_secret_chat_with_user(UserId user_id, const std::
void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, bool from_database) {
CHECK(u != nullptr);
if (user_id == get_my_id()) {
if (G()->shared_config().get_option_boolean("is_premium") != u->is_premium) {
G()->shared_config().set_option_boolean("is_premium", u->is_premium);
send_closure(td_->config_manager_, &ConfigManager::request_config, true);
}
}
if (u->is_name_changed || u->is_username_changed || u->is_is_contact_changed) {
update_contacts_hints(u, user_id, from_database);
u->is_username_changed = false;
@ -10625,14 +10782,34 @@ void ContactsManager::on_get_user_full(tl_object_ptr<telegram_api::userFull> &&u
td_->group_call_manager_->on_update_dialog_about(DialogId(user_id), user_full->about, true);
}
string description;
Photo description_photo;
FileId description_animation_file_id;
if (user->bot_info_ != nullptr && !td_->auth_manager_->is_bot()) {
description = std::move(user->bot_info_->description_);
description_photo =
get_photo(td_->file_manager_.get(), std::move(user->bot_info_->description_photo_), DialogId(user_id));
auto document = std::move(user->bot_info_->description_document_);
if (document != nullptr) {
int32 document_id = document->get_id();
if (document_id == telegram_api::document::ID) {
auto parsed_document = td_->documents_manager_->on_get_document(
move_tl_object_as<telegram_api::document>(document), DialogId(user_id));
if (parsed_document.type == Document::Type::Animation) {
description_animation_file_id = parsed_document.file_id;
} else {
LOG(ERROR) << "Receive non-animation document in bot description";
}
}
}
on_update_user_full_commands(user_full, user_id, std::move(user->bot_info_->commands_));
on_update_user_full_menu_button(user_full, user_id, std::move(user->bot_info_->menu_button_));
}
if (user_full->description != description) {
if (user_full->description != description || user_full->description_photo != description_photo ||
user_full->description_animation_file_id != description_animation_file_id) {
user_full->description = std::move(description);
user_full->description_photo = std::move(description_photo);
user_full->description_animation_file_id = description_animation_file_id;
user_full->is_changed = true;
}
@ -11642,7 +11819,6 @@ void ContactsManager::on_update_user_full_commands(UserFull *user_full, UserId u
void ContactsManager::on_update_user_full_menu_button(UserFull *user_full, UserId user_id,
tl_object_ptr<telegram_api::BotMenuButton> &&bot_menu_button) {
CHECK(user_full != nullptr);
CHECK(bot_menu_button != nullptr);
auto new_button = get_bot_menu_button(std::move(bot_menu_button));
bool is_changed;
if (user_full->menu_button == nullptr) {
@ -11906,6 +12082,8 @@ void ContactsManager::drop_user_full(UserId user_id) {
user_full->need_phone_number_privacy_exception = false;
user_full->about = string();
user_full->description = string();
user_full->description_photo = Photo();
user_full->description_animation_file_id = FileId();
user_full->menu_button = nullptr;
user_full->commands.clear();
user_full->common_chat_count = 0;
@ -12788,17 +12966,18 @@ void ContactsManager::on_get_permanent_dialog_invite_link(DialogId dialog_id, co
}
void ContactsManager::on_update_chat_full_invite_link(ChatFull *chat_full,
tl_object_ptr<telegram_api::chatInviteExported> &&invite_link) {
tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link) {
CHECK(chat_full != nullptr);
if (update_permanent_invite_link(chat_full->invite_link, DialogInviteLink(std::move(invite_link)))) {
if (update_permanent_invite_link(chat_full->invite_link, DialogInviteLink(std::move(invite_link), "ChatFull"))) {
chat_full->is_changed = true;
}
}
void ContactsManager::on_update_channel_full_invite_link(
ChannelFull *channel_full, tl_object_ptr<telegram_api::chatInviteExported> &&invite_link) {
ChannelFull *channel_full, tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link) {
CHECK(channel_full != nullptr);
if (update_permanent_invite_link(channel_full->invite_link, DialogInviteLink(std::move(invite_link)))) {
if (update_permanent_invite_link(channel_full->invite_link,
DialogInviteLink(std::move(invite_link), "ChannelFull"))) {
channel_full->is_changed = true;
}
}
@ -14154,6 +14333,11 @@ bool ContactsManager::have_min_user(UserId user_id) const {
return users_.count(user_id) > 0;
}
bool ContactsManager::is_user_premium(UserId user_id) const {
auto u = get_user(user_id);
return u != nullptr && u->is_premium;
}
bool ContactsManager::is_user_deleted(UserId user_id) const {
auto u = get_user(user_id);
return u == nullptr || u->is_deleted;
@ -14657,7 +14841,8 @@ ContactsManager::ChatFull *ContactsManager::add_chat_full(ChatId chat_id) {
return chat_full_ptr.get();
}
bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id) {
bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id,
bool only_participants) const {
CHECK(c != nullptr);
CHECK(chat_full != nullptr);
if (!c->is_active && chat_full->version == -1) {
@ -14670,11 +14855,17 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha
return true;
}
if (c->is_active && c->status.can_manage_invite_links() && !chat_full->invite_link.is_valid()) {
if (!only_participants && c->is_active && c->status.can_manage_invite_links() && !chat_full->invite_link.is_valid()) {
LOG(INFO) << "Have outdated invite link in " << chat_id;
return true;
}
if (!only_participants &&
!is_same_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), chat_full->photo, c->photo)) {
LOG(INFO) << "Have outdated chat photo in " << chat_id;
return true;
}
LOG(DEBUG) << "Full " << chat_id << " is up-to-date with version " << chat_full->version;
return false;
}
@ -14691,7 +14882,7 @@ void ContactsManager::load_chat_full(ChatId chat_id, bool force, Promise<Unit> &
return send_get_chat_full_query(chat_id, std::move(promise), source);
}
if (is_chat_full_outdated(chat_full, c, chat_id)) {
if (is_chat_full_outdated(chat_full, c, chat_id, false)) {
LOG(INFO) << "Have outdated full " << chat_id;
if (td_->auth_manager_->is_bot() && !force) {
return send_get_chat_full_query(chat_id, std::move(promise), source);
@ -14908,6 +15099,14 @@ bool ContactsManager::get_channel_can_be_deleted(const Channel *c) {
return c->can_be_deleted;
}
bool ContactsManager::get_channel_join_to_send(const Channel *c) {
return c->join_to_send || !c->is_megagroup || !c->has_linked_channel;
}
bool ContactsManager::get_channel_join_request(const Channel *c) {
return c->join_request && c->is_megagroup && (is_channel_public(c) || c->has_linked_channel);
}
ChannelId ContactsManager::get_channel_linked_channel_id(ChannelId channel_id) {
auto channel_full = get_channel_full_const(channel_id);
if (channel_full == nullptr) {
@ -15505,7 +15704,7 @@ void ContactsManager::get_chat_participant(ChatId chat_id, UserId user_id, Promi
}
auto chat_full = get_chat_full_force(chat_id, "get_chat_participant");
if (chat_full == nullptr || (td_->auth_manager_->is_bot() && is_chat_full_outdated(chat_full, c, chat_id))) {
if (chat_full == nullptr || (td_->auth_manager_->is_bot() && is_chat_full_outdated(chat_full, c, chat_id, true))) {
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), chat_id, user_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
TRY_STATUS_PROMISE(promise, std::move(result));
@ -15515,7 +15714,7 @@ void ContactsManager::get_chat_participant(ChatId chat_id, UserId user_id, Promi
return;
}
if (is_chat_full_outdated(chat_full, c, chat_id)) {
if (is_chat_full_outdated(chat_full, c, chat_id, true)) {
send_get_chat_full_query(chat_id, Auto(), "get_chat_participant lazy");
}
@ -16011,6 +16210,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
bool has_linked_channel = (channel.flags_ & CHANNEL_FLAG_HAS_LINKED_CHAT) != 0;
bool sign_messages = (channel.flags_ & CHANNEL_FLAG_SIGN_MESSAGES) != 0;
bool join_to_send = (channel.flags_ & CHANNEL_FLAG_JOIN_TO_SEND) != 0;
bool join_request = (channel.flags_ & CHANNEL_FLAG_JOIN_REQUEST) != 0;
bool is_slow_mode_enabled = (channel.flags_ & CHANNEL_FLAG_IS_SLOW_MODE_ENABLED) != 0;
bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0;
bool is_verified = (channel.flags_ & CHANNEL_FLAG_IS_VERIFIED) != 0;
@ -16072,6 +16273,9 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
Channel *c = get_channel_force(channel_id);
if (c != nullptr) {
LOG(DEBUG) << "Receive known min " << channel_id;
auto old_join_to_send = get_channel_join_to_send(c);
auto old_join_request = get_channel_join_request(c);
on_update_channel_title(c, channel_id, std::move(channel.title_));
on_update_channel_username(c, channel_id, std::move(channel.username_));
on_update_channel_photo(c, channel_id, std::move(channel.photo_));
@ -16093,12 +16297,21 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
c->is_changed = true;
invalidate_channel_full(channel_id, !c->is_slow_mode_enabled);
}
if (c->join_to_send != join_to_send || c->join_request != join_request) {
c->join_to_send = join_to_send;
c->join_request = join_request;
c->need_save_to_database = true;
}
// sign_messages isn't known for min-channels
if (c->is_verified != is_verified) {
c->is_verified = is_verified;
c->is_changed = true;
}
if (old_join_to_send != get_channel_join_to_send(c) || old_join_request != get_channel_join_request(c)) {
c->is_changed = true;
}
update_channel(c, channel_id);
} else {
@ -16129,6 +16342,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
if (c->status.is_banned()) { // possibly uninited channel
min_channels_.erase(channel_id);
}
auto old_join_to_send = get_channel_join_to_send(c);
auto old_join_request = get_channel_join_request(c);
if (c->access_hash != access_hash) {
c->access_hash = access_hash;
c->need_save_to_database = true;
@ -16162,16 +16377,27 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
c->is_scam = is_scam;
c->is_fake = is_fake;
c->is_gigagroup = is_gigagroup;
c->join_to_send = join_to_send;
c->join_request = join_request;
c->is_changed = true;
need_invalidate_channel_full = true;
}
if (c->join_to_send != join_to_send || c->join_request != join_request) {
c->join_to_send = join_to_send;
c->join_request = join_request;
c->need_save_to_database = true;
}
if (c->is_verified != is_verified || c->sign_messages != sign_messages) {
c->is_verified = is_verified;
c->sign_messages = sign_messages;
c->is_changed = true;
}
if (old_join_to_send != get_channel_join_to_send(c) || old_join_request != get_channel_join_request(c)) {
c->is_changed = true;
}
if (c->cache_version != Channel::CACHE_VERSION) {
c->cache_version = Channel::CACHE_VERSION;
@ -16220,6 +16446,8 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
if (c->status.is_banned()) { // possibly uninited channel
min_channels_.erase(channel_id);
}
auto old_join_to_send = get_channel_join_to_send(c);
auto old_join_request = get_channel_join_request(c);
if (c->access_hash != channel.access_hash_) {
c->access_hash = channel.access_hash_;
c->need_save_to_database = true;
@ -16240,6 +16468,8 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
td_->messages_manager_->on_update_dialog_group_call(DialogId(channel_id), false, false, "receive channelForbidden");
bool sign_messages = false;
bool join_to_send = false;
bool join_request = false;
bool is_slow_mode_enabled = false;
bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0;
bool is_verified = false;
@ -16259,23 +16489,35 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
bool need_invalidate_channel_full = false;
if (c->is_slow_mode_enabled != is_slow_mode_enabled || c->is_megagroup != is_megagroup ||
!c->restriction_reasons.empty() || c->is_scam != is_scam || c->is_fake != is_fake) {
!c->restriction_reasons.empty() || c->is_scam != is_scam || c->is_fake != is_fake ||
c->join_to_send != join_to_send || c->join_request != join_request) {
// c->has_linked_channel = has_linked_channel;
c->is_slow_mode_enabled = is_slow_mode_enabled;
c->is_megagroup = is_megagroup;
c->restriction_reasons.clear();
c->is_scam = is_scam;
c->is_fake = is_fake;
c->join_to_send = join_to_send;
c->join_request = join_request;
c->is_changed = true;
need_invalidate_channel_full = true;
}
if (c->join_to_send != join_to_send || c->join_request != join_request) {
c->join_to_send = join_to_send;
c->join_request = join_request;
c->need_save_to_database = true;
}
if (c->sign_messages != sign_messages || c->is_verified != is_verified) {
c->sign_messages = sign_messages;
c->is_verified = is_verified;
c->is_changed = true;
}
if (old_join_to_send != get_channel_join_to_send(c) || old_join_request != get_channel_join_request(c)) {
c->is_changed = true;
}
bool need_drop_participant_count = c->participant_count != 0;
if (need_drop_participant_count) {
@ -16390,7 +16632,7 @@ td_api::object_ptr<td_api::UserStatus> ContactsManager::get_user_status_object(U
td_api::object_ptr<td_api::updateUser> ContactsManager::get_update_unknown_user_object(UserId user_id) {
return td_api::make_object<td_api::updateUser>(td_api::make_object<td_api::user>(
user_id.get(), "", "", "", "", td_api::make_object<td_api::userStatusEmpty>(), nullptr, false, false, false,
false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), ""));
false, false, "", false, false, false, td_api::make_object<td_api::userTypeUnknown>(), "", false));
}
int64 ContactsManager::get_user_id_object(UserId user_id, const char *source) const {
@ -16450,8 +16692,8 @@ tl_object_ptr<td_api::user> ContactsManager::get_user_object(UserId user_id, con
return make_tl_object<td_api::user>(
user_id.get(), u->first_name, u->last_name, u->username, u->phone_number, get_user_status_object(user_id, u),
get_profile_photo_object(td_->file_manager_.get(), u->photo), u->is_contact, u->is_mutual_contact, u->is_verified,
u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake, u->is_received,
std::move(type), u->language_code);
u->is_premium, u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_fake,
u->is_received, std::move(type), u->language_code, u->attach_menu_enabled);
}
vector<int64> ContactsManager::get_user_ids_object(const vector<UserId> &user_ids, const char *source) const {
@ -16475,24 +16717,33 @@ tl_object_ptr<td_api::userFullInfo> ContactsManager::get_user_full_info_object(U
CHECK(user_full != nullptr);
td_api::object_ptr<td_api::botInfo> bot_info;
bool is_bot = is_user_bot(user_id);
td_api::object_ptr<td_api::formattedText> bio_object;
if (is_bot) {
auto menu_button = get_bot_menu_button_object(td_, user_full->menu_button.get());
auto commands =
transform(user_full->commands, [](const auto &command) { return command.get_bot_command_object(); });
bot_info = td_api::make_object<td_api::botInfo>(
user_full->about, user_full->description, std::move(menu_button), std::move(commands),
user_full->about, user_full->description,
get_photo_object(td_->file_manager_.get(), user_full->description_photo),
td_->animations_manager_->get_animation_object(user_full->description_animation_file_id),
std::move(menu_button), std::move(commands),
user_full->group_administrator_rights == AdministratorRights()
? nullptr
: user_full->group_administrator_rights.get_chat_administrator_rights_object(),
user_full->broadcast_administrator_rights == AdministratorRights()
? nullptr
: user_full->broadcast_administrator_rights.get_chat_administrator_rights_object());
} else {
FormattedText bio;
bio.text = user_full->about;
bio.entities = find_entities(bio.text, true, true, !is_user_premium(user_id));
bio_object = get_formatted_text_object(bio, true, 0);
}
return make_tl_object<td_api::userFullInfo>(
get_chat_photo_object(td_->file_manager_.get(), user_full->photo), user_full->is_blocked,
user_full->can_be_called, user_full->supports_video_calls, user_full->has_private_calls,
!user_full->private_forward_name.empty(), user_full->need_phone_number_privacy_exception,
is_bot ? string() : user_full->about, user_full->common_chat_count, std::move(bot_info));
!user_full->private_forward_name.empty(), user_full->need_phone_number_privacy_exception, std::move(bio_object),
user_full->common_chat_count, std::move(bot_info));
}
td_api::object_ptr<td_api::updateBasicGroup> ContactsManager::get_update_unknown_basic_group_object(ChatId chat_id) {
@ -16550,10 +16801,10 @@ tl_object_ptr<td_api::basicGroupFullInfo> ContactsManager::get_basic_group_full_
td_api::object_ptr<td_api::updateSupergroup> ContactsManager::get_update_unknown_supergroup_object(
ChannelId channel_id) const {
auto min_channel = get_min_channel(channel_id);
bool is_megagroup = min_channel == nullptr ? false : min_channel->is_megagroup_;
return td_api::make_object<td_api::updateSupergroup>(td_api::make_object<td_api::supergroup>(
channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), 0, false,
false, false, false, min_channel == nullptr ? true : !min_channel->is_megagroup_, false, false, string(), false,
false));
false, false, !is_megagroup, false, false, !is_megagroup, false, false, string(), false, false));
}
int64 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char *source) const {
@ -16579,9 +16830,9 @@ tl_object_ptr<td_api::supergroup> ContactsManager::get_supergroup_object(Channel
}
return td_api::make_object<td_api::supergroup>(
channel_id.get(), c->username, c->date, get_channel_status(c).get_chat_member_status_object(),
c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, c->is_slow_mode_enabled,
!c->is_megagroup, c->is_gigagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons),
c->is_scam, c->is_fake);
c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, get_channel_join_to_send(c),
get_channel_join_request(c), c->is_slow_mode_enabled, !c->is_megagroup, c->is_gigagroup, c->is_verified,
get_restriction_reason_description(c->restriction_reasons), c->is_scam, c->is_fake);
}
tl_object_ptr<td_api::supergroupFullInfo> ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const {

View File

@ -349,6 +349,10 @@ class ContactsManager final : public Actor {
void toggle_channel_sign_messages(ChannelId channel_id, bool sign_messages, Promise<Unit> &&promise);
void toggle_channel_join_to_send(ChannelId channel_id, bool joint_to_send, Promise<Unit> &&promise);
void toggle_channel_join_request(ChannelId channel_id, bool join_request, Promise<Unit> &&promise);
void toggle_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available,
Promise<Unit> &&promise);
@ -450,6 +454,8 @@ class ContactsManager final : public Actor {
bool is_user_contact(UserId user_id, bool is_mutual = false) const;
bool is_user_premium(UserId user_id) const;
bool is_user_deleted(UserId user_id) const;
bool is_user_support(UserId user_id) const;
@ -654,6 +660,7 @@ class ContactsManager final : public Actor {
bool is_min_access_hash = true;
bool is_received = false;
bool is_verified = false;
bool is_premium = false;
bool is_support = false;
bool is_deleted = true;
bool is_bot = true;
@ -667,6 +674,7 @@ class ContactsManager final : public Actor {
bool is_mutual_contact = false;
bool need_apply_min_photo = false;
bool can_be_added_to_attach_menu = false;
bool attach_menu_enabled = false;
bool is_photo_inited = false;
@ -704,8 +712,10 @@ class ContactsManager final : public Actor {
Photo photo;
string about;
string description;
string private_forward_name;
string description;
Photo description_photo;
FileId description_animation_file_id;
unique_ptr<BotMenuButton> menu_button;
vector<BotCommand> commands;
@ -826,7 +836,7 @@ class ContactsManager final : public Actor {
int32 date = 0;
int32 participant_count = 0;
static constexpr uint32 CACHE_VERSION = 8;
static constexpr uint32 CACHE_VERSION = 9;
uint32 cache_version = 0;
bool has_linked_channel = false;
@ -835,6 +845,8 @@ class ContactsManager final : public Actor {
bool is_slow_mode_enabled = false;
bool noforwards = false;
bool can_be_deleted = false;
bool join_to_send = false;
bool join_request = false;
bool is_megagroup = false;
bool is_gigagroup = false;
@ -1016,7 +1028,6 @@ class ContactsManager final : public Actor {
static constexpr int32 MAX_GET_PROFILE_PHOTOS = 100; // server side limit
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
@ -1048,6 +1059,8 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FLAG_NEED_APPLY_MIN_PHOTO = 1 << 25;
static constexpr int32 USER_FLAG_IS_FAKE = 1 << 26;
static constexpr int32 USER_FLAG_IS_ATTACH_MENU_BOT = 1 << 27;
static constexpr int32 USER_FLAG_IS_PREMIUM = 1 << 28;
static constexpr int32 USER_FLAG_ATTACH_MENU_ENABLED = 1 << 29;
static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0;
static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1;
@ -1104,6 +1117,8 @@ class ContactsManager final : public Actor {
static constexpr int32 CHANNEL_FLAG_IS_FAKE = 1 << 25;
static constexpr int32 CHANNEL_FLAG_IS_GIGAGROUP = 1 << 26;
static constexpr int32 CHANNEL_FLAG_NOFORWARDS = 1 << 27;
static constexpr int32 CHANNEL_FLAG_JOIN_TO_SEND = 1 << 28;
static constexpr int32 CHANNEL_FLAG_JOIN_REQUEST = 1 << 29;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1;
@ -1214,6 +1229,8 @@ class ContactsManager final : public Actor {
static bool get_channel_sign_messages(const Channel *c);
static bool get_channel_has_linked_channel(const Channel *c);
static bool get_channel_can_be_deleted(const Channel *c);
static bool get_channel_join_to_send(const Channel *c);
static bool get_channel_join_request(const Channel *c);
void set_my_id(UserId my_id);
@ -1272,7 +1289,7 @@ class ContactsManager final : public Actor {
void on_update_chat_full_participants(ChatFull *chat_full, ChatId chat_id, vector<DialogParticipant> participants,
int32 version, bool from_update);
void on_update_chat_full_invite_link(ChatFull *chat_full,
tl_object_ptr<telegram_api::chatInviteExported> &&invite_link);
tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link);
void on_update_channel_photo(Channel *c, ChannelId channel_id,
tl_object_ptr<telegram_api::ChatPhoto> &&chat_photo_ptr);
@ -1289,7 +1306,7 @@ class ContactsManager final : public Actor {
void on_update_channel_full_photo(ChannelFull *channel_full, ChannelId channel_id, Photo photo);
void on_update_channel_full_invite_link(ChannelFull *channel_full,
tl_object_ptr<telegram_api::chatInviteExported> &&invite_link);
tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link);
void on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id,
ChannelId linked_channel_id);
void on_update_channel_full_location(ChannelFull *channel_full, ChannelId channel_id, const DialogLocation &location);
@ -1397,7 +1414,7 @@ class ContactsManager final : public Actor {
void update_channel_full(ChannelFull *channel_full, ChannelId channel_id, const char *source,
bool from_database = false);
static bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id);
bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id, bool only_participants) const;
bool is_user_contact(const User *u, UserId user_id, bool is_mutual) const;

View File

@ -39,7 +39,7 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
return td_api::make_object<td_api::chatEventMemberJoined>();
case telegram_api::channelAdminLogEventActionParticipantJoinByInvite::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantJoinByInvite>(action_ptr);
DialogInviteLink invite_link(std::move(action->invite_));
DialogInviteLink invite_link(std::move(action->invite_), "channelAdminLogEventActionParticipantJoinByInvite");
if (!invite_link.is_valid()) {
LOG(ERROR) << "Wrong invite link: " << invite_link;
return nullptr;
@ -49,11 +49,7 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionParticipantJoinByRequest::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantJoinByRequest>(action_ptr);
DialogInviteLink invite_link(std::move(action->invite_));
if (!invite_link.is_valid()) {
LOG(ERROR) << "Wrong invite link: " << invite_link;
return nullptr;
}
DialogInviteLink invite_link(std::move(action->invite_), "channelAdminLogEventActionParticipantJoinByRequest");
UserId approver_user_id(action->approved_by_);
if (!approver_user_id.is_valid()) {
return nullptr;
@ -257,8 +253,8 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionExportedInviteEdit::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionExportedInviteEdit>(action_ptr);
DialogInviteLink old_invite_link(std::move(action->prev_invite_));
DialogInviteLink new_invite_link(std::move(action->new_invite_));
DialogInviteLink old_invite_link(std::move(action->prev_invite_), "channelAdminLogEventActionExportedInviteEdit");
DialogInviteLink new_invite_link(std::move(action->new_invite_), "channelAdminLogEventActionExportedInviteEdit");
if (!old_invite_link.is_valid() || !new_invite_link.is_valid()) {
LOG(ERROR) << "Wrong edited invite link: " << old_invite_link << " -> " << new_invite_link;
return nullptr;
@ -269,7 +265,7 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionExportedInviteRevoke::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionExportedInviteRevoke>(action_ptr);
DialogInviteLink invite_link(std::move(action->invite_));
DialogInviteLink invite_link(std::move(action->invite_), "channelAdminLogEventActionExportedInviteRevoke");
if (!invite_link.is_valid()) {
LOG(ERROR) << "Wrong revoked invite link: " << invite_link;
return nullptr;
@ -279,7 +275,7 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionExportedInviteDelete::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionExportedInviteDelete>(action_ptr);
DialogInviteLink invite_link(std::move(action->invite_));
DialogInviteLink invite_link(std::move(action->invite_), "channelAdminLogEventActionExportedInviteDelete");
if (!invite_link.is_valid()) {
LOG(ERROR) << "Wrong deleted invite link: " << invite_link;
return nullptr;

View File

@ -6,18 +6,30 @@
//
#include "td/telegram/DialogFilter.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/Global.h"
#include "td/utils/algorithm.h"
#include "td/utils/emoji.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
namespace td {
unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
bool with_id) {
int32 DialogFilter::get_max_filter_dialogs() {
return narrow_cast<int32>(G()->shared_config().get_option_integer("chat_filter_chosen_chat_count_max", 100));
}
unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(
telegram_api::object_ptr<telegram_api::DialogFilter> filter_ptr, bool with_id) {
if (filter_ptr->get_id() != telegram_api::dialogFilter::ID) {
LOG(ERROR) << "Ignore " << to_string(filter_ptr);
return nullptr;
}
auto filter = telegram_api::move_object_as<telegram_api::dialogFilter>(filter_ptr);
DialogFilterId dialog_filter_id(filter->id_);
if (with_id && !dialog_filter_id.is_valid()) {
LOG(ERROR) << "Receive invalid " << to_string(filter);
@ -87,16 +99,15 @@ Status DialogFilter::check_limits() const {
auto included_secret_dialog_count = static_cast<int32>(included_dialog_ids.size()) - included_server_dialog_count;
auto pinned_secret_dialog_count = static_cast<int32>(pinned_dialog_ids.size()) - pinned_server_dialog_count;
if (excluded_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
excluded_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
auto limit = get_max_filter_dialogs();
if (excluded_server_dialog_count > limit || excluded_secret_dialog_count > limit) {
return Status::Error(400, "The maximum number of excluded chats exceeded");
}
if (included_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
included_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
if (included_server_dialog_count > limit || included_secret_dialog_count > limit) {
return Status::Error(400, "The maximum number of included chats exceeded");
}
if (included_server_dialog_count + pinned_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
included_secret_dialog_count + pinned_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
if (included_server_dialog_count + pinned_server_dialog_count > limit ||
included_secret_dialog_count + pinned_secret_dialog_count > limit) {
return Status::Error(400, "The maximum number of pinned chats exceeded");
}
@ -205,7 +216,7 @@ string DialogFilter::get_default_icon_name(const td_api::chatFilter *filter) {
return "Custom";
}
telegram_api::object_ptr<telegram_api::dialogFilter> DialogFilter::get_input_dialog_filter() const {
telegram_api::object_ptr<telegram_api::DialogFilter> DialogFilter::get_input_dialog_filter() const {
int32 flags = 0;
if (!emoji.empty()) {
flags |= telegram_api::dialogFilter::EMOTICON_MASK;

View File

@ -20,8 +20,6 @@ namespace td {
class DialogFilter {
public:
static constexpr int32 MAX_INCLUDED_FILTER_DIALOGS = 100; // server side limit
DialogFilterId dialog_filter_id;
string title;
string emoji;
@ -43,7 +41,9 @@ class DialogFilter {
template <class ParserT>
void parse(ParserT &parser);
static unique_ptr<DialogFilter> get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
static int32 get_max_filter_dialogs();
static unique_ptr<DialogFilter> get_dialog_filter(telegram_api::object_ptr<telegram_api::DialogFilter> filter_ptr,
bool with_id);
void remove_secret_chat_dialog_ids();
@ -58,7 +58,7 @@ class DialogFilter {
static string get_default_icon_name(const td_api::chatFilter *filter);
telegram_api::object_ptr<telegram_api::dialogFilter> get_input_dialog_filter() const;
telegram_api::object_ptr<telegram_api::DialogFilter> get_input_dialog_filter() const;
td_api::object_ptr<td_api::chatFilterInfo> get_chat_filter_info_object() const;

View File

@ -10,14 +10,26 @@
#include "td/telegram/LinkManager.h"
#include "td/utils/logging.h"
#include "td/utils/SliceBuilder.h"
namespace td {
DialogInviteLink::DialogInviteLink(tl_object_ptr<telegram_api::chatInviteExported> exported_invite) {
if (exported_invite == nullptr) {
DialogInviteLink::DialogInviteLink(tl_object_ptr<telegram_api::ExportedChatInvite> exported_invite_ptr,
const char *source) {
if (exported_invite_ptr == nullptr) {
return;
}
if (exported_invite_ptr->get_id() != telegram_api::chatInviteExported::ID) {
CHECK(exported_invite_ptr->get_id() == telegram_api::chatInvitePublicJoinRequests::ID)
Slice slice(source);
if (slice != "channelAdminLogEventActionParticipantJoinByRequest" && slice != "updateChatParticipant" &&
slice != "updateChannelParticipant" && slice != "updateBotChatInviteRequester") {
LOG(ERROR) << "Receive from " << source << ' ' << to_string(exported_invite_ptr);
}
return;
}
auto exported_invite = move_tl_object_as<telegram_api::chatInviteExported>(exported_invite_ptr);
invite_link_ = std::move(exported_invite->link_);
title_ = std::move(exported_invite->title_);
creator_user_id_ = UserId(exported_invite->admin_id_);
@ -31,39 +43,40 @@ DialogInviteLink::DialogInviteLink(tl_object_ptr<telegram_api::chatInviteExporte
is_revoked_ = exported_invite->revoked_;
is_permanent_ = exported_invite->permanent_;
LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported invite link " << invite_link_;
string full_source = PSTRING() << "invite link " << invite_link_ << " from " << source;
LOG_IF(ERROR, !is_valid_invite_link(invite_link_)) << "Unsupported " << full_source;
if (!creator_user_id_.is_valid()) {
LOG(ERROR) << "Receive invalid " << creator_user_id_ << " as creator of a link " << invite_link_;
LOG(ERROR) << "Receive invalid " << creator_user_id_ << " as creator of " << full_source;
creator_user_id_ = UserId();
}
if (date_ != 0 && date_ < 1000000000) {
LOG(ERROR) << "Receive wrong date " << date_ << " as a creation date of a link " << invite_link_;
LOG(ERROR) << "Receive wrong date " << date_ << " as a creation date of " << full_source;
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_;
LOG(ERROR) << "Receive wrong date " << expire_date_ << " as an expire date of " << full_source;
expire_date_ = 0;
}
if (usage_limit_ < 0) {
LOG(ERROR) << "Receive wrong usage limit " << usage_limit_ << " for a link " << invite_link_;
LOG(ERROR) << "Receive wrong usage limit " << usage_limit_ << " for " << full_source;
usage_limit_ = 0;
}
if (usage_count_ < 0) {
LOG(ERROR) << "Receive wrong usage count " << usage_count_ << " for a link " << invite_link_;
LOG(ERROR) << "Receive wrong usage count " << usage_count_ << " for " << full_source;
usage_count_ = 0;
}
if (edit_date_ != 0 && edit_date_ < 1000000000) {
LOG(ERROR) << "Receive wrong date " << edit_date_ << " as an edit date of a link " << invite_link_;
LOG(ERROR) << "Receive wrong date " << edit_date_ << " as an edit date of " << full_source;
edit_date_ = 0;
}
if (request_count_ < 0) {
LOG(ERROR) << "Receive wrong pending join request count " << request_count_ << " for a link " << invite_link_;
LOG(ERROR) << "Receive wrong pending join request count " << request_count_ << " for " << full_source;
request_count_ = 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;
LOG(ERROR) << "Receive wrong permanent " << full_source << ' ' << *this;
title_.clear();
expire_date_ = 0;
usage_limit_ = 0;
@ -72,7 +85,7 @@ DialogInviteLink::DialogInviteLink(tl_object_ptr<telegram_api::chatInviteExporte
creates_join_request_ = false;
}
if (creates_join_request_ && usage_limit_ > 0) {
LOG(ERROR) << "Receive wrong permanent " << *this;
LOG(ERROR) << "Receive wrong permanent " << full_source << ' ' << *this;
usage_limit_ = 0;
}
}
@ -113,7 +126,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogInviteLink
<< 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]";
<< invite_link.request_count_ << " pending join requests]";
}
} // namespace td

View File

@ -40,7 +40,7 @@ class DialogInviteLink {
public:
DialogInviteLink() = default;
explicit DialogInviteLink(tl_object_ptr<telegram_api::chatInviteExported> exported_invite);
DialogInviteLink(tl_object_ptr<telegram_api::ExportedChatInvite> exported_invite_ptr, const char *source);
static bool is_valid_invite_link(Slice invite_link);

View File

@ -238,13 +238,14 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
int64 id;
int64 access_hash;
int32 dc_id;
int32 size;
int64 size;
int32 date = 0;
string mime_type;
string file_reference;
string minithumbnail;
PhotoSize thumbnail;
AnimationSize animated_thumbnail;
FileId premium_animation_file_id;
FileEncryptionKey encryption_key;
bool is_web = false;
bool is_web_no_proxy = false;
@ -310,11 +311,17 @@ 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(), 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;
if (!animated_thumbnail.file_id.is_valid()) {
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));
}
} else if (thumb->type_ == "f") {
if (!premium_animation_file_id.is_valid()) {
premium_animation_file_id =
register_photo_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 'f'), id,
access_hash, file_reference, owner_dialog_id, thumb->size_, DcId::create(dc_id),
get_sticker_format_photo_format(sticker_format));
}
}
}
@ -478,8 +485,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
if (thumbnail_format == PhotoFormat::Jpeg) {
minithumbnail = string();
}
td_->stickers_manager_->create_sticker(file_id, std::move(minithumbnail), std::move(thumbnail), dimensions,
std::move(sticker), sticker_format, load_data_multipromise_ptr);
td_->stickers_manager_->create_sticker(file_id, premium_animation_file_id, std::move(minithumbnail),
std::move(thumbnail), dimensions, std::move(sticker), sticker_format,
load_data_multipromise_ptr);
break;
case Document::Type::Video:
td_->videos_manager_->create_video(file_id, std::move(minithumbnail), std::move(thumbnail),
@ -590,7 +598,8 @@ bool DocumentsManager::has_input_media(FileId file_id, FileId thumbnail_file_id,
SecretInputMedia DocumentsManager::get_secret_input_media(FileId document_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const {
const string &caption, BufferSlice thumbnail,
int32 layer) const {
const GeneralDocument *document = get_document(document_file_id);
CHECK(document != nullptr);
auto file_view = td_->file_manager_->get_file_view(document_file_id);
@ -616,7 +625,8 @@ SecretInputMedia DocumentsManager::get_secret_input_media(FileId document_file_i
document->mime_type,
file_view,
std::move(attributes),
caption};
caption,
layer};
}
tl_object_ptr<telegram_api::InputMedia> DocumentsManager::get_input_media(

View File

@ -91,7 +91,7 @@ class DocumentsManager {
SecretInputMedia get_secret_input_media(FileId document_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const;
const string &caption, BufferSlice thumbnail, int32 layer) const;
tl_object_ptr<telegram_api::InputMedia> get_input_media(FileId file_id,
tl_object_ptr<telegram_api::InputFile> input_file,

View File

@ -10,22 +10,23 @@
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/misc.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_helpers.h"
namespace td {
struct EncryptedFile {
static constexpr int32 MAGIC = 0x473d738a;
int64 id_ = 0;
int64 access_hash_ = 0;
int32 size_ = 0;
int64 size_ = 0;
int32 dc_id_ = 0;
int32 key_fingerprint_ = 0;
EncryptedFile() = default;
EncryptedFile(int64 id, int64 access_hash, int32 size, int32 dc_id, int32 key_fingerprint)
EncryptedFile(int64 id, int64 access_hash, int64 size, int32 dc_id, int32 key_fingerprint)
: id_(id), access_hash_(access_hash), size_(size), dc_id_(dc_id), key_fingerprint_(key_fingerprint) {
CHECK(size_ >= 0);
}
static unique_ptr<EncryptedFile> get_encrypted_file(tl_object_ptr<telegram_api::EncryptedFile> file_ptr) {
@ -33,16 +34,26 @@ struct EncryptedFile {
return nullptr;
}
auto file = move_tl_object_as<telegram_api::encryptedFile>(file_ptr);
if (file->size_ < 0) {
return nullptr;
}
return make_unique<EncryptedFile>(file->id_, file->access_hash_, file->size_, file->dc_id_, file->key_fingerprint_);
}
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(MAGIC, storer);
bool has_64bit_size = (size_ >= (static_cast<int64>(1) << 31));
BEGIN_STORE_FLAGS();
STORE_FLAG(has_64bit_size);
END_STORE_FLAGS();
store(id_, storer);
store(access_hash_, storer);
store(size_, storer);
if (has_64bit_size) {
store(size_, storer);
} else {
store(narrow_cast<int32>(size_), storer);
}
store(dc_id_, storer);
store(key_fingerprint_, storer);
}
@ -50,18 +61,25 @@ struct EncryptedFile {
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
int32 got_magic;
parse(got_magic, parser);
bool has_64bit_size;
BEGIN_PARSE_FLAGS();
constexpr int32 OLD_MAGIC = 0x473d738a;
if (flags_parse == OLD_MAGIC) {
flags_parse = 0;
}
PARSE_FLAG(has_64bit_size);
END_PARSE_FLAGS();
parse(id_, parser);
parse(access_hash_, parser);
parse(size_, parser);
if (has_64bit_size) {
parse(size_, parser);
} else {
int32 int_size;
parse(int_size, parser);
size_ = int_size;
}
parse(dc_id_, parser);
parse(key_fingerprint_, parser);
if (got_magic != MAGIC) {
parser.set_error("EncryptedFile magic mismatch");
}
}
};

View File

@ -8,7 +8,6 @@
#include "td/telegram/ConfigShared.h"
#include "td/telegram/net/ConnectionCreator.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/net/TempAuthKeyWatchdog.h"
#include "td/telegram/OptionManager.h"

View File

@ -8,6 +8,7 @@
#include "td/telegram/DhConfig.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/TdParameters.h"
@ -45,7 +46,6 @@ class GroupCallManager;
class LanguagePackManager;
class LinkManager;
class MessagesManager;
class MtprotoHeader;
class NetQueryDispatcher;
class NotificationManager;
class NotificationSettingsManager;

View File

@ -1235,7 +1235,7 @@ template <>
tl_object_ptr<td_api::sticker> copy(const td_api::sticker &obj) {
return td_api::make_object<td_api::sticker>(obj.set_id_, obj.width_, obj.height_, obj.emoji_, copy(obj.type_),
transform(obj.outline_, copy_closed_vector_path), copy(obj.thumbnail_),
copy(obj.sticker_));
copy(obj.premium_animation_), copy(obj.sticker_));
}
template <>
@ -1247,7 +1247,8 @@ tl_object_ptr<td_api::video> copy(const td_api::video &obj) {
template <>
tl_object_ptr<td_api::voiceNote> copy(const td_api::voiceNote &obj) {
return td_api::make_object<td_api::voiceNote>(obj.duration_, obj.waveform_, obj.mime_type_, copy(obj.voice_));
return td_api::make_object<td_api::voiceNote>(obj.duration_, obj.waveform_, obj.mime_type_, obj.is_recognized_,
obj.recognized_text_, copy(obj.voice_));
}
template <>

View File

@ -36,6 +36,7 @@
#include "td/utils/SliceBuilder.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Time.h"
#include "td/utils/utf8.h"
#include <tuple>
@ -147,6 +148,28 @@ static AdministratorRights get_administrator_rights(Slice rights, bool for_chann
for_channel ? ChannelType::Broadcast : ChannelType::Megagroup);
}
td_api::object_ptr<td_api::targetChatChosen> get_target_chat_chosen(Slice chat_types) {
bool allow_users = false;
bool allow_bots = false;
bool allow_groups = false;
bool allow_channels = false;
for (auto chat_type : full_split(chat_types, ' ')) {
if (chat_type == "users") {
allow_users = true;
} else if (chat_type == "bots") {
allow_bots = true;
} else if (chat_type == "groups") {
allow_groups = true;
} else if (chat_type == "channels") {
allow_channels = true;
}
}
if (!allow_users && !allow_bots && !allow_groups && !allow_channels) {
return nullptr;
}
return td_api::make_object<td_api::targetChatChosen>(allow_users, allow_bots, allow_groups, allow_channels);
}
class LinkManager::InternalLinkActiveSessions final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeActiveSessions>();
@ -154,18 +177,31 @@ class LinkManager::InternalLinkActiveSessions final : public InternalLink {
};
class LinkManager::InternalLinkAttachMenuBot final : public InternalLink {
td_api::object_ptr<td_api::targetChatChosen> allowed_chat_types_;
unique_ptr<InternalLink> dialog_link_;
string bot_username_;
string url_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeAttachmentMenuBot>(
dialog_link_ == nullptr ? nullptr : dialog_link_->get_internal_link_type_object(), bot_username_, url_);
td_api::object_ptr<td_api::TargetChat> target_chat;
if (dialog_link_ != nullptr) {
target_chat = td_api::make_object<td_api::targetChatInternalLink>(dialog_link_->get_internal_link_type_object());
} else if (allowed_chat_types_ != nullptr) {
target_chat = td_api::make_object<td_api::targetChatChosen>(
allowed_chat_types_->allow_user_chats_, allowed_chat_types_->allow_bot_chats_,
allowed_chat_types_->allow_group_chats_, allowed_chat_types_->allow_channel_chats_);
} else {
target_chat = td_api::make_object<td_api::targetChatCurrent>();
}
return td_api::make_object<td_api::internalLinkTypeAttachmentMenuBot>(std::move(target_chat), bot_username_, url_);
}
public:
InternalLinkAttachMenuBot(unique_ptr<InternalLink> dialog_link, string bot_username, Slice start_parameter)
: dialog_link_(std::move(dialog_link)), bot_username_(std::move(bot_username)) {
InternalLinkAttachMenuBot(td_api::object_ptr<td_api::targetChatChosen> allowed_chat_types,
unique_ptr<InternalLink> dialog_link, string bot_username, Slice start_parameter)
: allowed_chat_types_(std::move(allowed_chat_types))
, dialog_link_(std::move(dialog_link))
, bot_username_(std::move(bot_username)) {
if (!start_parameter.empty()) {
url_ = PSTRING() << "start://" << start_parameter;
}
@ -297,6 +333,18 @@ class LinkManager::InternalLinkGame final : public InternalLink {
}
};
class LinkManager::InternalLinkInvoice final : public InternalLink {
string invoice_name_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeInvoice>(invoice_name_);
}
public:
explicit InternalLinkInvoice(string invoice_name) : invoice_name_(std::move(invoice_name)) {
}
};
class LinkManager::InternalLinkLanguage final : public InternalLink {
string language_pack_id_;
@ -365,6 +413,18 @@ class LinkManager::InternalLinkPassportDataRequest final : public InternalLink {
}
};
class LinkManager::InternalLinkPremiumFeatures final : public InternalLink {
string referrer_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypePremiumFeatures>(referrer_);
}
public:
explicit InternalLinkPremiumFeatures(string referrer) : referrer_(std::move(referrer)) {
}
};
class LinkManager::InternalLinkPrivacyAndSecuritySettings final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypePrivacyAndSecuritySettings>();
@ -963,13 +1023,13 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
// resolve?domain=<username>&attach=<bot_username>
// resolve?domain=<username>&attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(
td::make_unique<InternalLinkPublicDialog>(std::move(username)), url_query.get_arg("attach").str(),
nullptr, td::make_unique<InternalLinkPublicDialog>(std::move(username)), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
} else if (url_query.has_arg("startattach")) {
// resolve?domain=<bot_username>&startattach
// resolve?domain=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(nullptr, std::move(username),
url_query.get_arg("startattach"));
// resolve?domain=<bot_username>&startattach&choose=users+bots+groups+channels
// resolve?domain=<bot_username>&startattach=<start_parameter>&choose=users+bots+groups+channels
return td::make_unique<InternalLinkAttachMenuBot>(get_target_chat_chosen(url_query.get_arg("choose")), nullptr,
std::move(username), url_query.get_arg("startattach"));
}
if (username == "telegrampassport") {
// resolve?domain=telegrampassport&bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
@ -982,8 +1042,8 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
if (!url_query.get_arg("attach").empty()) {
// resolve?phone=<phone_number>&attach=<bot_username>
// resolve?phone=<phone_number>&attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(std::move(user_link), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
return td::make_unique<InternalLinkAttachMenuBot>(
nullptr, std::move(user_link), url_query.get_arg("attach").str(), url_query.get_arg("startattach"));
}
// resolve?phone=12345
return user_link;
@ -1000,6 +1060,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
} else if (path.size() == 1 && path[0] == "passport") {
// passport?bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
return get_internal_link_passport(query, url_query.args_);
} else if (path.size() == 1 && path[0] == "premium_offer") {
// premium_offer?ref=<referrer>
return td::make_unique<InternalLinkPremiumFeatures>(get_arg("ref"));
} else if (!path.empty() && path[0] == "settings") {
if (path.size() == 2 && path[1] == "change_number") {
// settings/change_number
@ -1099,6 +1162,11 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
<< pass_arg("slug") << copy_arg("mode") << copy_arg("intensity")
<< copy_arg("bg_color") << copy_arg("rotation"));
}
} else if (path.size() == 1 && path[0] == "invoice") {
// invoice?slug=<invoice_name>
if (has_arg("slug")) {
return td::make_unique<InternalLinkInvoice>(url_query.get_arg("slug").str());
}
} else if (path.size() == 1 && (path[0] == "share" || path[0] == "msg" || path[0] == "msg_url")) {
// msg_url?url=<url>
// msg_url?url=<url>&text=<text>
@ -1156,8 +1224,8 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
if (!url_query.get_arg("attach").empty()) {
// /+<phone_number>?attach=<bot_username>
// /+<phone_number>?attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(std::move(user_link), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
return td::make_unique<InternalLinkAttachMenuBot>(
nullptr, std::move(user_link), url_query.get_arg("attach").str(), url_query.get_arg("startattach"));
}
// /+<phone_number>
return user_link;
@ -1220,6 +1288,16 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
<< url_encode(path[1]) << copy_arg("mode") << copy_arg("intensity")
<< copy_arg("bg_color") << copy_arg("rotation"));
}
} else if (path[0] == "invoice") {
if (path.size() >= 2 && !path[1].empty()) {
// /invoice/<name>
return td::make_unique<InternalLinkInvoice>(path[1]);
}
} else if (path[0][0] == '$') {
if (path[0].size() >= 2) {
// /$<invoice_name>
return td::make_unique<InternalLinkInvoice>(path[0].substr(1));
}
} else if (path[0] == "share" || path[0] == "msg") {
if (!(path.size() > 1 && (path[1] == "bookmarklet" || path[1] == "embed"))) {
// /share?url=<url>
@ -1271,13 +1349,14 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
if (!url_query.get_arg("attach").empty()) {
// /<username>?attach=<bot_username>
// /<username>?attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(td::make_unique<InternalLinkPublicDialog>(std::move(username)),
url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
return td::make_unique<InternalLinkAttachMenuBot>(
nullptr, td::make_unique<InternalLinkPublicDialog>(std::move(username)), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
} else if (url_query.has_arg("startattach")) {
// /<bot_username>?startattach
// /<bot_username>?startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(nullptr, std::move(username), url_query.get_arg("startattach"));
// /<bot_username>?startattach&choose=users+bots+groups+channels
// /<bot_username>?startattach=<start_parameter>&choose=users+bots+groups+channels
return td::make_unique<InternalLinkAttachMenuBot>(get_target_chat_chosen(url_query.get_arg("choose")), nullptr,
std::move(username), url_query.get_arg("startattach"));
}
// /<username>

View File

@ -96,11 +96,13 @@ class LinkManager final : public Actor {
class InternalLinkDialogInvite;
class InternalLinkFilterSettings;
class InternalLinkGame;
class InternalLinkInvoice;
class InternalLinkLanguage;
class InternalLinkLanguageSettings;
class InternalLinkMessage;
class InternalLinkMessageDraft;
class InternalLinkPassportDataRequest;
class InternalLinkPremiumFeatures;
class InternalLinkPrivacyAndSecuritySettings;
class InternalLinkProxy;
class InternalLinkPublicDialog;

View File

@ -14,6 +14,7 @@
#include "td/telegram/CallDiscardReason.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChatId.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/Contact.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Dependencies.h"
@ -172,9 +173,10 @@ class MessagePhoto final : public MessageContent {
class MessageSticker final : public MessageContent {
public:
FileId file_id;
bool is_premium = false;
MessageSticker() = default;
explicit MessageSticker(FileId file_id) : file_id(file_id) {
MessageSticker(FileId file_id, bool is_premium) : file_id(file_id), is_premium(is_premium) {
}
MessageContentType get_type() const final {
@ -500,9 +502,11 @@ class MessagePaymentSuccessful final : public MessageContent {
MessageId invoice_message_id;
string currency;
int64 total_amount = 0;
string invoice_payload; // or invoice_slug for users
bool is_recurring = false;
bool is_first_recurring = false;
// bots only part
string invoice_payload;
string shipping_option_id;
unique_ptr<OrderInfo> order_info;
string telegram_payment_charge_id;
@ -510,11 +514,14 @@ class MessagePaymentSuccessful final : public MessageContent {
MessagePaymentSuccessful() = default;
MessagePaymentSuccessful(DialogId invoice_dialog_id, MessageId invoice_message_id, string &&currency,
int64 total_amount)
int64 total_amount, string &&invoice_payload, bool is_recurring, bool is_first_recurring)
: invoice_dialog_id(invoice_dialog_id)
, invoice_message_id(invoice_message_id)
, currency(std::move(currency))
, total_amount(total_amount) {
, total_amount(total_amount)
, invoice_payload(std::move(invoice_payload))
, is_recurring(is_recurring || is_first_recurring)
, is_first_recurring(is_first_recurring) {
}
MessageContentType get_type() const final {
@ -843,6 +850,9 @@ static void store(const MessageContent *content, StorerT &storer) {
case MessageContentType::Sticker: {
const auto *m = static_cast<const MessageSticker *>(content);
td->stickers_manager_->store_sticker(m->file_id, false, storer, "MessageSticker");
BEGIN_STORE_FLAGS();
STORE_FLAG(m->is_premium);
END_STORE_FLAGS();
break;
}
case MessageContentType::Text: {
@ -980,6 +990,8 @@ static void store(const MessageContent *content, StorerT &storer) {
STORE_FLAG(has_invoice_message_id);
STORE_FLAG(is_correctly_stored);
STORE_FLAG(has_invoice_dialog_id);
STORE_FLAG(m->is_recurring);
STORE_FLAG(m->is_first_recurring);
END_STORE_FLAGS();
store(m->currency, storer);
store(m->total_amount, storer);
@ -1203,6 +1215,11 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
case MessageContentType::Sticker: {
auto m = make_unique<MessageSticker>();
m->file_id = td->stickers_manager_->parse_sticker(false, parser);
if (parser.version() >= static_cast<int32>(Version::AddMessageStickerFlags)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(m->is_premium);
END_PARSE_FLAGS();
}
is_bad = !m->file_id.is_valid();
content = std::move(m);
break;
@ -1383,6 +1400,8 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
PARSE_FLAG(has_invoice_message_id);
PARSE_FLAG(is_correctly_stored);
PARSE_FLAG(has_invoice_dialog_id);
PARSE_FLAG(m->is_recurring);
PARSE_FLAG(m->is_first_recurring);
END_PARSE_FLAGS();
parse(m->currency, parser);
parse(m->total_amount, parser);
@ -1645,7 +1664,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
} else if (allowed_media_content_id == td_api::inputMessagePhoto::ID) {
result.message_content = make_unique<MessagePhoto>(std::move(*photo), std::move(caption));
} else if (allowed_media_content_id == td_api::inputMessageSticker::ID) {
result.message_content = make_unique<MessageSticker>(file_id);
result.message_content = make_unique<MessageSticker>(file_id, false);
} else if (allowed_media_content_id == td_api::inputMessageVideo::ID) {
result.message_content = make_unique<MessageVideo>(file_id, std::move(caption));
} else if (allowed_media_content_id == td_api::inputMessageVoiceNote::ID) {
@ -1683,7 +1702,7 @@ unique_ptr<MessageContent> create_chat_set_ttl_message_content(int32 ttl) {
static Result<InputMessageContent> create_input_message_content(
DialogId dialog_id, tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td,
FormattedText caption, FileId file_id, PhotoSize thumbnail, vector<FileId> sticker_file_ids) {
FormattedText caption, FileId file_id, PhotoSize thumbnail, vector<FileId> sticker_file_ids, bool is_premium) {
CHECK(input_message_content != nullptr);
LOG(INFO) << "Create InputMessageContent with file " << file_id << " and thumbnail " << thumbnail.file_id;
@ -1801,7 +1820,11 @@ static Result<InputMessageContent> create_input_message_content(
PhotoSize s;
s.type = type;
s.dimensions = get_dimensions(input_photo->width_, input_photo->height_, nullptr);
s.size = static_cast<int32>(file_view.size());
auto size = file_view.size();
if (size < 0 || size >= 1000000000) {
return Status::Error(400, "Wrong photo size");
}
s.size = static_cast<int32>(size);
s.file_id = file_id;
if (thumbnail.file_id.is_valid()) {
@ -1823,11 +1846,11 @@ static Result<InputMessageContent> create_input_message_content(
emoji = std::move(input_sticker->emoji_);
td->stickers_manager_->create_sticker(file_id, string(), thumbnail,
td->stickers_manager_->create_sticker(file_id, FileId(), string(), thumbnail,
get_dimensions(input_sticker->width_, input_sticker->height_, nullptr),
nullptr, StickerFormat::Unknown, nullptr);
content = make_unique<MessageSticker>(file_id);
content = make_unique<MessageSticker>(file_id, is_premium);
break;
}
case td_api::inputMessageVideo::ID: {
@ -1990,7 +2013,7 @@ static Result<InputMessageContent> create_input_message_content(
}
Result<InputMessageContent> get_input_message_content(
DialogId dialog_id, tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td) {
DialogId dialog_id, tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td, bool is_premium) {
bool is_secret = dialog_id.get_type() == DialogType::SecretChat;
LOG(INFO) << "Get input message content from " << to_string(input_message_content);
@ -2103,7 +2126,7 @@ Result<InputMessageContent> get_input_message_content(
TRY_RESULT(caption, process_input_caption(td->contacts_manager_.get(), dialog_id,
extract_input_caption(input_message_content), td->auth_manager_->is_bot()));
return create_input_message_content(dialog_id, std::move(input_message_content), td, std::move(caption), file_id,
std::move(thumbnail), std::move(sticker_file_ids));
std::move(thumbnail), std::move(sticker_file_ids), is_premium);
}
bool can_have_input_media(const Td *td, const MessageContent *content, bool is_server) {
@ -2167,17 +2190,17 @@ bool can_have_input_media(const Td *td, const MessageContent *content, bool is_s
SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail) {
BufferSlice thumbnail, int32 layer) {
switch (content->get_type()) {
case MessageContentType::Animation: {
const auto *m = static_cast<const MessageAnimation *>(content);
return td->animations_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
std::move(thumbnail));
std::move(thumbnail), layer);
}
case MessageContentType::Audio: {
const auto *m = static_cast<const MessageAudio *>(content);
return td->audios_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
std::move(thumbnail));
std::move(thumbnail), layer);
}
case MessageContentType::Contact: {
const auto *m = static_cast<const MessageContact *>(content);
@ -2186,7 +2209,7 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
case MessageContentType::Document: {
const auto *m = static_cast<const MessageDocument *>(content);
return td->documents_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
std::move(thumbnail));
std::move(thumbnail), layer);
}
case MessageContentType::Location: {
const auto *m = static_cast<const MessageLocation *>(content);
@ -2199,7 +2222,8 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
}
case MessageContentType::Sticker: {
const auto *m = static_cast<const MessageSticker *>(content);
return td->stickers_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail));
return td->stickers_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail),
layer);
}
case MessageContentType::Text: {
CHECK(input_file == nullptr);
@ -2214,15 +2238,17 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
case MessageContentType::Video: {
const auto *m = static_cast<const MessageVideo *>(content);
return td->videos_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
std::move(thumbnail));
std::move(thumbnail), layer);
}
case MessageContentType::VideoNote: {
const auto *m = static_cast<const MessageVideoNote *>(content);
return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail));
return td->video_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), std::move(thumbnail),
layer);
}
case MessageContentType::VoiceNote: {
const auto *m = static_cast<const MessageVoiceNote *>(content);
return td->voice_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text);
return td->voice_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text,
layer);
}
case MessageContentType::Call:
case MessageContentType::Dice:
@ -3078,7 +3104,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Animation: {
const auto *old_ = static_cast<const MessageAnimation *>(old_content);
const auto *new_ = static_cast<const MessageAnimation *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->animations_manager_->merge_animations(new_->file_id, old_->file_id, false);
}
@ -3092,7 +3118,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Audio: {
const auto *old_ = static_cast<const MessageAudio *>(old_content);
const auto *new_ = static_cast<const MessageAudio *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->audios_manager_->merge_audios(new_->file_id, old_->file_id, false);
}
@ -3114,7 +3140,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Document: {
const auto *old_ = static_cast<const MessageDocument *>(old_content);
const auto *new_ = static_cast<const MessageDocument *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->documents_manager_->merge_documents(new_->file_id, old_->file_id, false);
}
@ -3252,12 +3278,15 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Sticker: {
const auto *old_ = static_cast<const MessageSticker *>(old_content);
const auto *new_ = static_cast<const MessageSticker *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->stickers_manager_->merge_stickers(new_->file_id, old_->file_id, false);
}
need_update = true;
}
if (old_->is_premium != new_->is_premium) {
need_update = true;
}
break;
}
case MessageContentType::Venue: {
@ -3275,7 +3304,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Video: {
const auto *old_ = static_cast<const MessageVideo *>(old_content);
const auto *new_ = static_cast<const MessageVideo *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->videos_manager_->merge_videos(new_->file_id, old_->file_id, false);
}
@ -3289,7 +3318,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::VideoNote: {
const auto *old_ = static_cast<const MessageVideoNote *>(old_content);
const auto *new_ = static_cast<const MessageVideoNote *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->video_notes_manager_->merge_video_notes(new_->file_id, old_->file_id, false);
}
@ -3303,7 +3332,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::VoiceNote: {
const auto *old_ = static_cast<const MessageVoiceNote *>(old_content);
const auto *new_ = static_cast<const MessageVoiceNote *>(new_content);
if (new_->file_id != old_->file_id) {
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->voice_notes_manager_->merge_voice_notes(new_->file_id, old_->file_id, false);
}
@ -3438,7 +3467,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
old_->telegram_payment_charge_id != new_->telegram_payment_charge_id ||
old_->provider_payment_charge_id != new_->provider_payment_charge_id ||
((old_->order_info != nullptr || new_->order_info != nullptr) &&
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info))) {
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info ||
old_->is_recurring != new_->is_recurring || old_->is_first_recurring != new_->is_first_recurring))) {
need_update = true;
}
break;
@ -3716,6 +3746,9 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage
}
return;
}
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->register_voice_note(static_cast<const MessageVoiceNote *>(content)->file_id,
full_message_id, source);
case MessageContentType::Poll:
return td->poll_manager_->register_poll(static_cast<const MessagePoll *>(content)->poll_id, full_message_id,
source);
@ -3744,6 +3777,12 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const
}
break;
}
case MessageContentType::VoiceNote:
if (static_cast<const MessageVoiceNote *>(old_content)->file_id ==
static_cast<const MessageVoiceNote *>(new_content)->file_id) {
return;
}
break;
case MessageContentType::Poll:
if (static_cast<const MessagePoll *>(old_content)->poll_id ==
static_cast<const MessagePoll *>(new_content)->poll_id) {
@ -3778,6 +3817,9 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa
}
return;
}
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->unregister_voice_note(static_cast<const MessageVoiceNote *>(content)->file_id,
full_message_id, source);
case MessageContentType::Poll:
return td->poll_manager_->unregister_poll(static_cast<const MessagePoll *>(content)->poll_id, full_message_id,
source);
@ -3952,7 +3994,7 @@ static tl_object_ptr<ToT> secret_to_telegram(FromT &from) {
}
static unique_ptr<MessageContent> get_document_message_content(Document &&parsed_document, FormattedText &&caption,
bool is_opened) {
bool is_opened, bool is_premium) {
auto file_id = parsed_document.file_id;
if (!parsed_document.empty()) {
CHECK(file_id.is_valid());
@ -3965,7 +4007,7 @@ static unique_ptr<MessageContent> get_document_message_content(Document &&parsed
case Document::Type::General:
return make_unique<MessageDocument>(file_id, std::move(caption));
case Document::Type::Sticker:
return make_unique<MessageSticker>(file_id);
return make_unique<MessageSticker>(file_id, is_premium);
case Document::Type::Unknown:
return make_unique<MessageUnsupported>();
case Document::Type::Video:
@ -3982,18 +4024,18 @@ static unique_ptr<MessageContent> get_document_message_content(Document &&parsed
static unique_ptr<MessageContent> get_document_message_content(Td *td, tl_object_ptr<telegram_api::document> &&document,
DialogId owner_dialog_id, FormattedText &&caption,
bool is_opened,
bool is_opened, bool is_premium,
MultiPromiseActor *load_data_multipromise_ptr) {
return get_document_message_content(
td->documents_manager_->on_get_document(std::move(document), owner_dialog_id, load_data_multipromise_ptr),
std::move(caption), is_opened);
std::move(caption), is_opened, is_premium);
}
unique_ptr<MessageContent> get_secret_message_content(
Td *td, string message_text, unique_ptr<EncryptedFile> file,
tl_object_ptr<secret_api::DecryptedMessageMedia> &&media,
vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities, DialogId owner_dialog_id,
MultiPromiseActor &load_data_multipromise) {
MultiPromiseActor &load_data_multipromise, bool is_premium) {
int32 constructor_id = media == nullptr ? secret_api::decryptedMessageMediaEmpty::ID : media->get_id();
auto caption = [&] {
switch (constructor_id) {
@ -4005,6 +4047,10 @@ unique_ptr<MessageContent> get_secret_message_content(
auto photo = static_cast<secret_api::decryptedMessageMediaPhoto *>(media.get());
return std::move(photo->caption_);
}
case secret_api::decryptedMessageMediaDocument46::ID: {
auto document = static_cast<secret_api::decryptedMessageMediaDocument46 *>(media.get());
return std::move(document->caption_);
}
case secret_api::decryptedMessageMediaDocument::ID: {
auto document = static_cast<secret_api::decryptedMessageMediaDocument *>(media.get());
return std::move(document->caption_);
@ -4036,9 +4082,18 @@ unique_ptr<MessageContent> get_secret_message_content(
// support of old layer and old constructions
switch (constructor_id) {
case secret_api::decryptedMessageMediaDocument46::ID: {
auto document = move_tl_object_as<secret_api::decryptedMessageMediaDocument46>(media);
media = make_tl_object<secret_api::decryptedMessageMediaDocument>(
std::move(document->thumb_), document->thumb_w_, document->thumb_h_, document->mime_type_, document->size_,
std::move(document->key_), std::move(document->iv_), std::move(document->attributes_), string());
constructor_id = secret_api::decryptedMessageMediaDocument::ID;
break;
}
case secret_api::decryptedMessageMediaVideo::ID: {
auto video = move_tl_object_as<secret_api::decryptedMessageMediaVideo>(media);
std::vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
attributes.emplace_back(
make_tl_object<secret_api::documentAttributeVideo>(video->duration_, video->w_, video->h_));
media = make_tl_object<secret_api::decryptedMessageMediaDocument>(
@ -4142,7 +4197,7 @@ unique_ptr<MessageContent> get_secret_message_content(
auto document = secret_to_telegram_document(*external_document);
return get_document_message_content(td, std::move(document), owner_dialog_id,
FormattedText{std::move(message_text), std::move(entities)}, false,
&load_data_multipromise);
is_premium, &load_data_multipromise);
}
default:
break;
@ -4181,7 +4236,8 @@ unique_ptr<MessageContent> get_secret_message_content(
message_document->attributes_.clear();
auto document = td->documents_manager_->on_get_document(
{std::move(file), std::move(message_document), std::move(attributes)}, owner_dialog_id);
return get_document_message_content(std::move(document), {std::move(message_text), std::move(entities)}, false);
return get_document_message_content(std::move(document), {std::move(message_text), std::move(entities)}, false,
false);
}
default:
LOG(ERROR) << "Unsupported: " << to_string(media);
@ -4315,7 +4371,7 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
*ttl = message_document->ttl_seconds_;
}
return get_document_message_content(td, move_tl_object_as<telegram_api::document>(document_ptr), owner_dialog_id,
std::move(message), is_content_read, nullptr);
std::move(message), is_content_read, !message_document->nopremium_, nullptr);
}
case telegram_api::messageMediaGame::ID: {
auto message_game = move_tl_object_as<telegram_api::messageMediaGame>(media);
@ -4527,6 +4583,7 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
}
case MessageContentType::Sticker: {
auto result = make_unique<MessageSticker>(*static_cast<const MessageSticker *>(content));
result->is_premium = G()->shared_config().get_option_boolean("is_premium");
if (td->stickers_manager_->has_input_media(result->file_id, to_secret)) {
return std::move(result);
}
@ -4754,13 +4811,17 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
LOG(ERROR) << "Receive MessageActionPaymentSent in " << owner_dialog_id;
break;
}
auto payment_sent = move_tl_object_as<telegram_api::messageActionPaymentSent>(action);
if (!reply_to_message_id.is_valid()) {
LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id;
if (reply_to_message_id != MessageId()) {
LOG(ERROR) << "Receive succesful payment message with " << reply_to_message_id << " in " << owner_dialog_id;
}
reply_in_dialog_id = DialogId();
reply_to_message_id = MessageId();
}
auto payment_sent = move_tl_object_as<telegram_api::messageActionPaymentSent>(action);
return td::make_unique<MessagePaymentSuccessful>(reply_in_dialog_id, reply_to_message_id,
std::move(payment_sent->currency_), payment_sent->total_amount_);
return td::make_unique<MessagePaymentSuccessful>(
reply_in_dialog_id, reply_to_message_id, std::move(payment_sent->currency_), payment_sent->total_amount_,
std::move(payment_sent->invoice_slug_), payment_sent->recurring_used_, payment_sent->recurring_init_);
}
case telegram_api::messageActionPaymentSentMe::ID: {
if (!td->auth_manager_->is_bot()) {
@ -4769,8 +4830,8 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
}
auto payment_sent = move_tl_object_as<telegram_api::messageActionPaymentSentMe>(action);
auto result = td::make_unique<MessagePaymentSuccessful>(
DialogId(), MessageId(), std::move(payment_sent->currency_), payment_sent->total_amount_);
result->invoice_payload = payment_sent->payload_.as_slice().str();
DialogId(), MessageId(), std::move(payment_sent->currency_), payment_sent->total_amount_,
payment_sent->payload_.as_slice().str(), payment_sent->recurring_used_, payment_sent->recurring_init_);
result->shipping_option_id = std::move(payment_sent->shipping_option_id_);
result->order_info = get_order_info(std::move(payment_sent->info_));
result->telegram_payment_charge_id = std::move(payment_sent->charge_->id_);
@ -4952,7 +5013,10 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
}
case MessageContentType::Sticker: {
const auto *m = static_cast<const MessageSticker *>(content);
return make_tl_object<td_api::messageSticker>(td->stickers_manager_->get_sticker_object(m->file_id));
auto sticker = td->stickers_manager_->get_sticker_object(m->file_id);
CHECK(sticker != nullptr);
auto is_premium = m->is_premium && sticker->premium_animation_ != nullptr;
return make_tl_object<td_api::messageSticker>(std::move(sticker), is_premium);
}
case MessageContentType::Text: {
const auto *m = static_cast<const MessageText *>(content);
@ -5066,12 +5130,14 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
const auto *m = static_cast<const MessagePaymentSuccessful *>(content);
if (td->auth_manager_->is_bot()) {
return make_tl_object<td_api::messagePaymentSuccessfulBot>(
m->currency, m->total_amount, m->invoice_payload, m->shipping_option_id,
get_order_info_object(m->order_info), m->telegram_payment_charge_id, m->provider_payment_charge_id);
m->currency, m->total_amount, m->is_recurring, m->is_first_recurring, m->invoice_payload,
m->shipping_option_id, get_order_info_object(m->order_info), m->telegram_payment_charge_id,
m->provider_payment_charge_id);
} else {
auto invoice_dialog_id = m->invoice_dialog_id.is_valid() ? m->invoice_dialog_id : dialog_id;
return make_tl_object<td_api::messagePaymentSuccessful>(invoice_dialog_id.get(), m->invoice_message_id.get(),
m->currency, m->total_amount);
m->currency, m->total_amount, m->is_recurring,
m->is_first_recurring, m->invoice_payload);
}
}
case MessageContentType::ContactRegistered:

View File

@ -30,7 +30,6 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <utility>
@ -103,13 +102,13 @@ unique_ptr<MessageContent> create_screenshot_taken_message_content();
unique_ptr<MessageContent> create_chat_set_ttl_message_content(int32 ttl);
Result<InputMessageContent> get_input_message_content(
DialogId dialog_id, tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td);
DialogId dialog_id, tl_object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td, bool is_premium);
bool can_have_input_media(const Td *td, const MessageContent *content, bool is_server);
SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail);
BufferSlice thumbnail, int32 layer);
tl_object_ptr<telegram_api::InputMedia> get_input_media(const MessageContent *content, Td *td,
tl_object_ptr<telegram_api::InputFile> input_file,
@ -185,7 +184,7 @@ unique_ptr<MessageContent> get_secret_message_content(
Td *td, string message_text, unique_ptr<EncryptedFile> file,
tl_object_ptr<secret_api::DecryptedMessageMedia> &&media,
vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities, DialogId owner_dialog_id,
MultiPromiseActor &load_data_multipromise);
MultiPromiseActor &load_data_multipromise, bool is_premium);
unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message_text,
tl_object_ptr<telegram_api::MessageMedia> &&media,

View File

@ -29,9 +29,14 @@
namespace td {
int MessageEntity::get_type_priority(Type type) {
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50, 50, 94};
static_assert(sizeof(types) / sizeof(types[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
return types[static_cast<int32>(type)];
static const int priorities[] = {
50 /*Mention*/, 50 /*Hashtag*/, 50 /*BotCommand*/, 50 /*Url*/,
50 /*EmailAddress*/, 90 /*Bold*/, 91 /*Italic*/, 20 /*Code*/,
11 /*Pre*/, 10 /*PreCode*/, 49 /*TextUrl*/, 49 /*MentionName*/,
50 /*Cashtag*/, 50 /*PhoneNumber*/, 92 /*Underline*/, 93 /*Strikethrough*/,
0 /*BlockQuote*/, 50 /*BankCardNumber*/, 50 /*MediaTimestamp*/, 94 /*Spoiler*/};
static_assert(sizeof(priorities) / sizeof(priorities[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
return priorities[static_cast<int32>(type)];
}
StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Type &message_entity_type) {
@ -1631,7 +1636,7 @@ static void fix_entity_offsets(Slice text, vector<MessageEntity> &entities) {
}
}
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) {
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps, bool skip_urls) {
vector<MessageEntity> entities;
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
@ -1650,16 +1655,16 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool ski
add_entities(MessageEntity::Type::Cashtag, find_cashtags);
// TODO find_phone_numbers
add_entities(MessageEntity::Type::BankCardNumber, find_bank_card_numbers);
add_entities(MessageEntity::Type::Url, find_tg_urls);
auto urls = find_urls(text);
for (auto &url : urls) {
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
auto length = narrow_cast<int32>(url.first.size());
entities.emplace_back(type, offset, length);
if (!skip_urls) {
add_entities(MessageEntity::Type::Url, find_tg_urls);
auto urls = find_urls(text);
for (auto &url : urls) {
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
auto length = narrow_cast<int32>(url.first.size());
entities.emplace_back(type, offset, length);
}
}
if (!skip_media_timestamps) {
auto media_timestamps = find_media_timestamps(text);
for (auto &entity : media_timestamps) {

View File

@ -143,7 +143,8 @@ vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
int32 max_media_timestamp);
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps,
bool skip_urls = false);
vector<Slice> find_mentions(Slice str);
vector<Slice> find_bot_commands(Slice str);

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AffectedHistory.h"
#include "td/telegram/AvailableReaction.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/DialogAction.h"
#include "td/telegram/DialogDate.h"
@ -431,6 +432,8 @@ class MessagesManager final : public Actor {
bool get_dialog_silent_send_message(DialogId dialog_id) const;
DialogId get_dialog_default_send_message_as_dialog_id(DialogId dialog_id) const;
Result<td_api::object_ptr<td_api::message>> send_message(
DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id,
tl_object_ptr<td_api::messageSendOptions> &&options, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
@ -535,7 +538,7 @@ class MessagesManager final : public Actor {
void set_dialog_description(DialogId dialog_id, const string &description, Promise<Unit> &&promise);
void set_active_reactions(vector<string> active_reactions);
void set_active_reactions(vector<AvailableReaction> active_reactions);
void set_dialog_available_reactions(DialogId dialog_id, vector<string> available_reactions, Promise<Unit> &&promise);
@ -660,7 +663,8 @@ class MessagesManager final : public Actor {
void delete_dialog_filter(DialogFilterId dialog_filter_id, Promise<Unit> &&promise);
void reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, Promise<Unit> &&promise);
void reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, int32 main_dialog_list_position,
Promise<Unit> &&promise);
Status delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) TD_WARN_UNUSED_RESULT;
@ -800,7 +804,7 @@ class MessagesManager final : public Actor {
vector<MessageId> get_dialog_scheduled_messages(DialogId dialog_id, bool force, bool ignore_result,
Promise<Unit> &&promise);
Result<vector<string>> get_message_available_reactions(FullMessageId full_message_id);
Result<vector<AvailableReaction>> get_message_available_reactions(FullMessageId full_message_id);
void set_message_reaction(FullMessageId full_message_id, string reaction, bool is_big, Promise<Unit> &&promise);
@ -1756,7 +1760,6 @@ class MessagesManager final : public Actor {
static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat description
static constexpr size_t MAX_DIALOG_FILTER_TITLE_LENGTH = 12; // server side limit for dialog filter title
static constexpr int32 MAX_PRIVATE_MESSAGE_TTL = 60; // server side limit
static constexpr int32 MAX_DIALOG_FILTERS = 10; // server side limit
static constexpr int32 DIALOG_FILTERS_CACHE_TIME = 86400;
static constexpr int64 SPONSORED_DIALOG_ORDER = static_cast<int64>(2147483647) << 32;
@ -2086,7 +2089,7 @@ class MessagesManager final : public Actor {
void erase_delete_messages_log_event(uint64 log_event_id);
void delete_sent_message_on_server(DialogId dialog_id, MessageId message_id);
void delete_sent_message_on_server(DialogId dialog_id, MessageId message_id, MessageId old_message_id);
void delete_messages_on_server(DialogId dialog_id, vector<MessageId> message_ids, bool revoke, uint64 log_event_id,
Promise<Unit> &&promise);
@ -2328,7 +2331,8 @@ class MessagesManager final : public Actor {
void delete_message_from_database(Dialog *d, MessageId message_id, const Message *m, bool is_permanently_deleted);
void update_reply_to_message_id(DialogId dialog_id, MessageId old_message_id, MessageId new_message_id);
void update_reply_to_message_id(DialogId dialog_id, MessageId old_message_id, MessageId new_message_id,
bool have_new_message, const char *source);
void delete_message_files(DialogId dialog_id, const Message *m) const;
@ -2381,6 +2385,10 @@ class MessagesManager final : public Actor {
void send_update_new_message(const Dialog *d, const Message *m);
bool get_dialog_show_preview(const Dialog *d) const;
bool is_message_preview_enabled(const Dialog *d, const Message *m, bool from_mentions);
static bool is_from_mention_notification_group(const Message *m);
static bool is_message_notification_active(const Dialog *d, const Message *m);
@ -2645,7 +2653,7 @@ class MessagesManager final : public Actor {
bool update_dialog_silent_send_message(Dialog *d, bool silent_send_message);
vector<string> get_message_available_reactions(const Dialog *d, const Message *m);
vector<AvailableReaction> get_message_available_reactions(const Dialog *d, const Message *m);
void on_set_message_reaction(FullMessageId full_message_id, Result<Unit> result, Promise<Unit> promise);
@ -2657,9 +2665,6 @@ class MessagesManager final : public Actor {
vector<string> get_active_reactions(const vector<string> &available_reactions) const;
static vector<string> get_active_reactions(const vector<string> &available_reactions,
const vector<string> &active_reactions);
vector<string> get_dialog_active_reactions(const Dialog *d) const;
vector<string> get_message_active_reactions(const Dialog *d, const Message *m) const;
@ -2739,7 +2744,7 @@ class MessagesManager final : public Actor {
void reload_dialog_filters();
void on_get_dialog_filters(Result<vector<tl_object_ptr<telegram_api::dialogFilter>>> r_filters, bool dummy);
void on_get_dialog_filters(Result<vector<tl_object_ptr<telegram_api::DialogFilter>>> r_filters, bool dummy);
bool need_synchronize_dialog_filters() const;
@ -2773,9 +2778,10 @@ class MessagesManager final : public Actor {
void on_delete_dialog_filter(DialogFilterId dialog_filter_id, Status result);
void reorder_dialog_filters_on_server(vector<DialogFilterId> dialog_filter_ids);
void reorder_dialog_filters_on_server(vector<DialogFilterId> dialog_filter_ids, int32 main_dialog_list_position);
void on_reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, Status result);
void on_reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, int32 main_dialog_list_position,
Status result);
void save_dialog_filters();
@ -2793,6 +2799,8 @@ class MessagesManager final : public Actor {
DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id);
const DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id) const;
int32 get_server_main_dialog_list_position() const;
static vector<DialogFilterId> get_dialog_filter_ids(const vector<unique_ptr<DialogFilter>> &dialog_filters);
static vector<FolderId> get_dialog_filter_folder_ids(const DialogFilter *filter);
@ -3539,6 +3547,8 @@ class MessagesManager final : public Actor {
vector<unique_ptr<DialogFilter>> dialog_filters_;
vector<RecommendedDialogFilter> recommended_dialog_filters_;
vector<Promise<Unit>> dialog_filter_reload_queries_;
int32 server_main_dialog_list_position_ = 0; // position of the main dialog list stored on the server
int32 main_dialog_list_position_ = 0; // local position of the main dialog list stored on the server
FlatHashMap<DialogId, string, DialogIdHash> active_get_channel_differencies_;
FlatHashMap<DialogId, uint64, DialogIdHash> get_channel_difference_to_log_event_id_;
@ -3680,7 +3690,7 @@ class MessagesManager final : public Actor {
};
FlatHashMap<FullMessageId, PendingReaction, FullMessageIdHash> pending_reactions_;
vector<string> active_reactions_;
vector<AvailableReaction> active_reactions_;
FlatHashMap<string, size_t> active_reaction_pos_;
uint32 scheduled_messages_sync_generation_ = 1;

View File

@ -2869,6 +2869,9 @@ string NotificationManager::convert_loc_key(const string &loc_key) {
if (loc_key == "MESSAGE_ROUND") {
return "MESSAGE_VIDEO_NOTE";
}
if (loc_key == "MESSAGE_RECURRING_PAY") {
return "MESSAGE_RECURRING_PAYMENT";
}
break;
case 'S':
if (loc_key == "MESSAGE_SCREENSHOT") {
@ -3053,7 +3056,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
if (loc_key == "SESSION_REVOKE") {
if (was_encrypted) {
send_closure(td_->auth_manager_actor_, &AuthManager::on_authorization_lost, "SESSION_REVOKE");
G()->log_out("SESSION_REVOKE");
} else {
LOG(ERROR) << "Receive unencrypted SESSION_REVOKE push notification";
}
@ -3306,8 +3309,8 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(),
std::move(sender_photo), nullptr, 0, Auto(), string(), string());
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name,
string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string());
td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload");
}
@ -3664,8 +3667,8 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(), string(), nullptr, nullptr, 0,
Auto(), string(), string());
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(),
string(), nullptr, nullptr, 0, Auto(), string(), string());
td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification");
}

View File

@ -582,6 +582,10 @@ const unique_ptr<NotificationSound> &NotificationSettingsManager::get_scope_noti
return get_scope_notification_settings(scope)->sound;
}
bool NotificationSettingsManager::get_scope_show_preview(NotificationSettingsScope scope) const {
return get_scope_notification_settings(scope)->show_preview;
}
bool NotificationSettingsManager::get_scope_disable_pinned_message_notifications(
NotificationSettingsScope scope) const {
return get_scope_notification_settings(scope)->disable_pinned_message_notifications;

View File

@ -44,6 +44,8 @@ class NotificationSettingsManager final : public Actor {
const unique_ptr<NotificationSound> &get_scope_notification_sound(NotificationSettingsScope scope) const;
bool get_scope_show_preview(NotificationSettingsScope scope) const;
bool get_scope_disable_pinned_message_notifications(NotificationSettingsScope scope) const;
bool get_scope_disable_mention_notifications(NotificationSettingsScope scope) const;

View File

@ -6,6 +6,7 @@
//
#include "td/telegram/NotificationSound.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
namespace td {
@ -239,7 +240,7 @@ unique_ptr<NotificationSound> get_notification_sound(telegram_api::NotificationS
case telegram_api::notificationSoundRingtone::ID: {
const auto *sound = static_cast<telegram_api::notificationSoundRingtone *>(notification_sound);
if (sound->id_ == 0 || sound->id_ == -1) {
LOG(ERROR) << "Receive ringtone with id = " << sound->id_;
LOG(ERROR) << "Receive ringtone with ID = " << sound->id_;
return make_unique<NotificationSoundNone>();
}
return td::make_unique<NotificationSoundRingtone>(sound->id_);

View File

@ -8,7 +8,6 @@
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/NotificationSoundType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"

View File

@ -49,7 +49,7 @@ class NotificationTypeMessage final : public NotificationType {
if (message_object == nullptr) {
return nullptr;
}
return td_api::make_object<td_api::notificationTypeNewMessage>(std::move(message_object));
return td_api::make_object<td_api::notificationTypeNewMessage>(std::move(message_object), show_preview_);
}
StringBuilder &to_string_builder(StringBuilder &string_builder) const final {
@ -57,9 +57,11 @@ class NotificationTypeMessage final : public NotificationType {
}
MessageId message_id_;
bool show_preview_;
public:
explicit NotificationTypeMessage(MessageId message_id) : message_id_(message_id) {
NotificationTypeMessage(MessageId message_id, bool show_preview)
: message_id_(message_id), show_preview_(show_preview) {
}
};
@ -288,6 +290,11 @@ class NotificationTypePushMessage final : public NotificationType {
return td_api::make_object<td_api::pushMessageContentPoll>(arg, false, is_pinned);
}
break;
case 'R':
if (key == "MESSAGE_RECURRING_PAYMENT") {
return td_api::make_object<td_api::pushMessageContentRecurringPayment>(arg);
}
break;
case 'S':
if (key == "MESSAGE_SECRET_PHOTO") {
return td_api::make_object<td_api::pushMessageContentPhoto>(nullptr, arg, true, false);
@ -375,8 +382,8 @@ class NotificationTypePushMessage final : public NotificationType {
}
};
unique_ptr<NotificationType> create_new_message_notification(MessageId message_id) {
return make_unique<NotificationTypeMessage>(message_id);
unique_ptr<NotificationType> create_new_message_notification(MessageId message_id, bool show_preview) {
return make_unique<NotificationTypeMessage>(message_id, show_preview);
}
unique_ptr<NotificationType> create_new_secret_chat_notification() {

View File

@ -53,7 +53,7 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr
return string_builder << *notification_type;
}
unique_ptr<NotificationType> create_new_message_notification(MessageId message_id);
unique_ptr<NotificationType> create_new_message_notification(MessageId message_id, bool show_preview);
unique_ptr<NotificationType> create_new_secret_chat_notification();

View File

@ -31,6 +31,7 @@
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
@ -93,6 +94,9 @@ OptionManager::OptionManager(Td *td, ActorShared<> parent) : td_(td), parent_(st
if (!G()->shared_config().have_option("message_caption_length_max")) {
G()->shared_config().set_option_integer("message_caption_length_max", 1024);
}
if (!G()->shared_config().have_option("bio_length_max")) {
G()->shared_config().set_option_integer("bio_length_max", 70);
}
if (!G()->shared_config().have_option("suggested_video_note_length")) {
G()->shared_config().set_option_integer("suggested_video_note_length", 384);
}
@ -111,6 +115,12 @@ OptionManager::OptionManager(Td *td, ActorShared<> parent) : td_(td), parent_(st
if (!G()->shared_config().have_option("notification_sound_count_max")) {
G()->shared_config().set_option_integer("notification_sound_count_max", G()->is_test_dc() ? 5 : 100);
}
if (!G()->shared_config().have_option("chat_filter_count_max")) {
G()->shared_config().set_option_integer("chat_filter_count_max", G()->is_test_dc() ? 3 : 10);
}
if (!G()->shared_config().have_option("chat_filter_chosen_chat_count_max")) {
G()->shared_config().set_option_integer("chat_filter_chosen_chat_count_max", G()->is_test_dc() ? 5 : 100);
}
G()->shared_config().set_option_integer("utc_time_offset", Clocks::tz_offset());
}
@ -150,17 +160,25 @@ void OptionManager::clear_options() {
bool OptionManager::is_internal_option(Slice name) {
switch (name[0]) {
case 'a':
return name == "animated_emoji_zoom" || name == "animation_search_emojis" ||
return name == "about_length_limit_default" || name == "about_length_limit_premium" ||
name == "animated_emoji_zoom" || name == "animation_search_emojis" ||
name == "animation_search_provider" || name == "auth";
case 'b':
return name == "base_language_pack_version";
case 'c':
return name == "call_receive_timeout_ms" || name == "call_ring_timeout_ms" ||
name == "caption_length_limit_default" || name == "caption_length_limit_premium" ||
name == "channels_limit_default" || name == "channels_limit_premium" ||
name == "channels_public_limit_default" || name == "channels_public_limit_premium" ||
name == "channels_read_media_period" || name == "chat_read_mark_expire_period" ||
name == "chat_read_mark_size_threshold";
case 'd':
return name == "dc_txt_domain_name" || name == "default_reaction_needs_sync" || name == "dice_emojis" ||
name == "dice_success_values";
return name == "dc_txt_domain_name" || name == "default_reaction_needs_sync" ||
name == "dialog_filters_chats_limit_default" || name == "dialog_filters_chats_limit_premium" ||
name == "dialog_filters_limit_default" || name == "dialog_filters_limit_premium" ||
name == "dialogs_folder_pinned_limit_default" || name == "dialogs_folder_pinned_limit_premium" ||
name == "dialogs_pinned_limit_default" || name == "dialogs_pinned_limit_premium" ||
name == "dice_emojis" || name == "dice_success_values";
case 'e':
return name == "edit_time_limit" || name == "emoji_sounds";
case 'i':
@ -173,11 +191,16 @@ bool OptionManager::is_internal_option(Slice name) {
return name == "notification_cloud_delay_ms" || name == "notification_default_delay_ms";
case 'o':
return name == "online_cloud_timeout_ms" || name == "online_update_period_ms" || name == "otherwise_relogin_days";
case 'p':
return name == "premium_bot_username" || name == "premium_features" || name == "premium_invoice_slug";
case 'r':
return name == "rating_e_decay" || name == "reactions_uniq_max" || name == "recent_stickers_limit" ||
name == "revoke_pm_inbox" || name == "revoke_time_limit" || name == "revoke_pm_time_limit";
case 's':
return name == "saved_animations_limit" || name == "session_count";
return name == "saved_animations_limit" || name == "saved_gifs_limit_default" ||
name == "saved_gifs_limit_premium" || name == "session_count" || name == "stickers_faved_limit_default" ||
name == "stickers_faved_limit_premium" || name == "stickers_normal_by_emoji_per_premium_num" ||
name == "stickers_premium_by_emoji_num";
case 'v':
return name == "video_note_size_max";
case 'w':

View File

@ -12,6 +12,7 @@
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/PasswordManager.h"
@ -34,6 +35,56 @@
namespace td {
namespace {
static tl_object_ptr<td_api::formattedText> get_product_description_object(const string &description) {
FormattedText result;
result.text = description;
result.entities = find_entities(result.text, true, true);
return get_formatted_text_object(result, true, 0);
}
struct InputInvoiceInfo {
DialogId dialog_id_;
telegram_api::object_ptr<telegram_api::InputInvoice> input_invoice_;
};
Result<InputInvoiceInfo> get_input_invoice_info(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice) {
if (input_invoice == nullptr) {
return Status::Error(400, "Input invoice must be non-empty");
}
InputInvoiceInfo result;
switch (input_invoice->get_id()) {
case td_api::inputInvoiceMessage::ID: {
auto invoice = td_api::move_object_as<td_api::inputInvoiceMessage>(input_invoice);
DialogId dialog_id(invoice->chat_id_);
MessageId message_id(invoice->message_id_);
TRY_RESULT(server_message_id, td->messages_manager_->get_invoice_message_id({dialog_id, message_id}));
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return Status::Error(400, "Can't access the chat");
}
result.dialog_id_ = dialog_id;
result.input_invoice_ =
make_tl_object<telegram_api::inputInvoiceMessage>(std::move(input_peer), server_message_id.get());
break;
}
case td_api::inputInvoiceName::ID: {
auto invoice = td_api::move_object_as<td_api::inputInvoiceName>(input_invoice);
result.input_invoice_ = make_tl_object<telegram_api::inputInvoiceSlug>(invoice->name_);
break;
}
default:
UNREACHABLE();
}
return std::move(result);
}
} // namespace
class SetBotShippingAnswerQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
@ -137,18 +188,45 @@ static tl_object_ptr<td_api::invoice> convert_invoice(tl_object_ptr<telegram_api
need_shipping_address = true;
}
return make_tl_object<td_api::invoice>(
std::move(invoice->currency_), std::move(labeled_prices), invoice->max_tip_amount_,
vector<int64>(invoice->suggested_tip_amounts_), is_test, need_name, need_phone_number, need_email_address,
need_shipping_address, send_phone_number_to_provider, send_email_address_to_provider, is_flexible);
return make_tl_object<td_api::invoice>(std::move(invoice->currency_), std::move(labeled_prices),
invoice->max_tip_amount_, vector<int64>(invoice->suggested_tip_amounts_),
std::move(invoice->recurring_terms_url_), is_test, need_name,
need_phone_number, need_email_address, need_shipping_address,
send_phone_number_to_provider, send_email_address_to_provider, is_flexible);
}
static tl_object_ptr<td_api::paymentsProviderStripe> convert_payment_provider(
static tl_object_ptr<td_api::PaymentProvider> convert_payment_provider(
const string &native_provider_name, tl_object_ptr<telegram_api::dataJSON> native_parameters) {
if (native_parameters == nullptr) {
return nullptr;
}
if (native_provider_name == "smartglocal") {
string data = native_parameters->data_;
auto r_value = json_decode(data);
if (r_value.is_error()) {
LOG(ERROR) << "Can't parse JSON object \"" << native_parameters->data_ << "\": " << r_value.error();
return nullptr;
}
auto value = r_value.move_as_ok();
if (value.type() != JsonValue::Type::Object) {
LOG(ERROR) << "Wrong JSON data \"" << native_parameters->data_ << '"';
return nullptr;
}
auto r_public_token = get_json_object_string_field(value.get_object(), "public_token", false);
if (r_public_token.is_error()) {
LOG(ERROR) << "Unsupported JSON data \"" << native_parameters->data_ << '"';
return nullptr;
}
if (value.get_object().size() != 1) {
LOG(ERROR) << "Unsupported JSON data \"" << native_parameters->data_ << '"';
}
return make_tl_object<td_api::paymentProviderSmartGlocal>(r_public_token.move_as_ok());
}
if (native_provider_name == "stripe") {
string data = native_parameters->data_;
auto r_value = json_decode(data);
@ -178,9 +256,9 @@ static tl_object_ptr<td_api::paymentsProviderStripe> convert_payment_provider(
LOG(ERROR) << "Unsupported JSON data \"" << native_parameters->data_ << '"';
}
return make_tl_object<td_api::paymentsProviderStripe>(r_publishable_key.move_as_ok(), r_need_country.move_as_ok(),
r_need_postal_code.move_as_ok(),
r_need_cardholder_name.move_as_ok());
return make_tl_object<td_api::paymentProviderStripe>(r_publishable_key.move_as_ok(), r_need_country.move_as_ok(),
r_need_postal_code.move_as_ok(),
r_need_cardholder_name.move_as_ok());
}
return nullptr;
@ -270,20 +348,15 @@ class GetPaymentFormQuery final : public Td::ResultHandler {
explicit GetPaymentFormQuery(Promise<tl_object_ptr<td_api::paymentForm>> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, ServerMessageId server_message_id,
tl_object_ptr<telegram_api::dataJSON> &&theme_parameters) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return on_error(Status::Error(400, "Can't access the chat"));
}
void send(InputInvoiceInfo &&input_invoice_info, tl_object_ptr<telegram_api::dataJSON> &&theme_parameters) {
dialog_id_ = input_invoice_info.dialog_id_;
int32 flags = 0;
if (theme_parameters != nullptr) {
flags |= telegram_api::payments_getPaymentForm::THEME_PARAMS_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::payments_getPaymentForm(
flags, std::move(input_peer), server_message_id.get(), std::move(theme_parameters))));
flags, std::move(input_invoice_info.input_invoice_), std::move(theme_parameters))));
}
void on_result(BufferSlice packet) final {
@ -309,13 +382,20 @@ class GetPaymentFormQuery final : public Td::ResultHandler {
}
bool can_save_credentials = payment_form->can_save_credentials_;
bool need_password = payment_form->password_missing_;
auto photo = get_web_document_photo(td_->file_manager_.get(), std::move(payment_form->photo_), dialog_id_);
auto payment_provider =
convert_payment_provider(payment_form->native_provider_, std::move(payment_form->native_params_));
if (payment_provider == nullptr) {
payment_provider = td_api::make_object<td_api::paymentProviderOther>(std::move(payment_form->url_));
}
promise_.set_value(make_tl_object<td_api::paymentForm>(
payment_form->form_id_, convert_invoice(std::move(payment_form->invoice_)), std::move(payment_form->url_),
payment_form->form_id_, convert_invoice(std::move(payment_form->invoice_)),
td_->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentForm seller"),
td_->contacts_manager_->get_user_id_object(payments_provider_user_id, "paymentForm provider"),
convert_payment_provider(payment_form->native_provider_, std::move(payment_form->native_params_)),
convert_order_info(std::move(payment_form->saved_info_)),
convert_saved_credentials(std::move(payment_form->saved_credentials_)), can_save_credentials, need_password));
std::move(payment_provider), convert_order_info(std::move(payment_form->saved_info_)),
convert_saved_credentials(std::move(payment_form->saved_credentials_)), can_save_credentials, need_password,
payment_form->title_, get_product_description_object(payment_form->description_),
get_photo_object(td_->file_manager_.get(), photo)));
}
void on_error(Status status) final {
@ -333,13 +413,9 @@ class ValidateRequestedInfoQuery final : public Td::ResultHandler {
: promise_(std::move(promise)) {
}
void send(DialogId dialog_id, ServerMessageId server_message_id,
tl_object_ptr<telegram_api::paymentRequestedInfo> requested_info, bool allow_save) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return on_error(Status::Error(400, "Can't access the chat"));
}
void send(InputInvoiceInfo &&input_invoice_info, tl_object_ptr<telegram_api::paymentRequestedInfo> requested_info,
bool allow_save) {
dialog_id_ = input_invoice_info.dialog_id_;
int32 flags = 0;
if (allow_save) {
@ -350,7 +426,7 @@ class ValidateRequestedInfoQuery final : public Td::ResultHandler {
requested_info->flags_ = 0;
}
send_query(G()->net_query_creator().create(telegram_api::payments_validateRequestedInfo(
flags, false /*ignored*/, std::move(input_peer), server_message_id.get(), std::move(requested_info))));
flags, false /*ignored*/, std::move(input_invoice_info.input_invoice_), std::move(requested_info))));
}
void on_result(BufferSlice packet) final {
@ -382,16 +458,12 @@ class SendPaymentFormQuery final : public Td::ResultHandler {
: promise_(std::move(promise)) {
}
void send(DialogId dialog_id, ServerMessageId server_message_id, int64 payment_form_id, const string &order_info_id,
void send(InputInvoiceInfo &&input_invoice_info, int64 payment_form_id, const string &order_info_id,
const string &shipping_option_id, tl_object_ptr<telegram_api::InputPaymentCredentials> input_credentials,
int64 tip_amount) {
CHECK(input_credentials != nullptr);
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return on_error(Status::Error(400, "Can't access the chat"));
}
dialog_id_ = input_invoice_info.dialog_id_;
int32 flags = 0;
if (!order_info_id.empty()) {
@ -404,7 +476,7 @@ class SendPaymentFormQuery final : public Td::ResultHandler {
flags |= telegram_api::payments_sendPaymentForm::TIP_AMOUNT_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::payments_sendPaymentForm(
flags, payment_form_id, std::move(input_peer), server_message_id.get(), order_info_id, shipping_option_id,
flags, payment_form_id, std::move(input_invoice_info.input_invoice_), order_info_id, shipping_option_id,
std::move(input_credentials), tip_amount)));
}
@ -486,8 +558,9 @@ class GetPaymentReceiptQuery final : public Td::ResultHandler {
auto photo = get_web_document_photo(td_->file_manager_.get(), std::move(payment_receipt->photo_), dialog_id_);
promise_.set_value(make_tl_object<td_api::paymentReceipt>(
payment_receipt->title_, payment_receipt->description_, get_photo_object(td_->file_manager_.get(), photo),
payment_receipt->date_, td_->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentReceipt seller"),
payment_receipt->title_, get_product_description_object(payment_receipt->description_),
get_photo_object(td_->file_manager_.get(), photo), payment_receipt->date_,
td_->contacts_manager_->get_user_id_object(seller_bot_user_id, "paymentReceipt seller"),
td_->contacts_manager_->get_user_id_object(payments_provider_user_id, "paymentReceipt provider"),
convert_invoice(std::move(payment_receipt->invoice_)), convert_order_info(std::move(payment_receipt->info_)),
convert_shipping_option(std::move(payment_receipt->shipping_)), std::move(payment_receipt->credentials_title_),
@ -561,6 +634,32 @@ class ClearSavedInfoQuery final : public Td::ResultHandler {
}
};
class ExportInvoiceQuery final : public Td::ResultHandler {
Promise<string> promise_;
public:
explicit ExportInvoiceQuery(Promise<string> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputMediaInvoice> &&input_media_invoice) {
send_query(G()->net_query_creator().create(telegram_api::payments_exportInvoice(std::move(input_media_invoice))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::payments_exportInvoice>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto link = result_ptr.move_as_ok();
promise_.set_value(std::move(link->url_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetBankCardInfoQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::bankCardInfo>> promise_;
@ -611,7 +710,8 @@ bool operator==(const Invoice &lhs, const Invoice &rhs) {
lhs.send_phone_number_to_provider == rhs.send_phone_number_to_provider &&
lhs.send_email_address_to_provider == rhs.send_email_address_to_provider &&
lhs.is_flexible == rhs.is_flexible && lhs.currency == rhs.currency && lhs.price_parts == rhs.price_parts &&
lhs.max_tip_amount == rhs.max_tip_amount && lhs.suggested_tip_amounts == rhs.suggested_tip_amounts;
lhs.max_tip_amount == rhs.max_tip_amount && lhs.suggested_tip_amounts == rhs.suggested_tip_amounts &&
lhs.recurring_payment_terms_of_service_url == rhs.recurring_payment_terms_of_service_url;
}
bool operator!=(const Invoice &lhs, const Invoice &rhs) {
@ -625,8 +725,12 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Invoice &invoice)
<< (invoice.need_email_address ? ", needs email address" : "")
<< (invoice.need_shipping_address ? ", needs shipping address" : "")
<< (invoice.send_phone_number_to_provider ? ", sends phone number to provider" : "")
<< (invoice.send_email_address_to_provider ? ", sends email address to provider" : "") << " in "
<< invoice.currency << " with price parts " << format::as_array(invoice.price_parts)
<< (invoice.send_email_address_to_provider ? ", sends email address to provider" : "")
<< (invoice.recurring_payment_terms_of_service_url.empty()
? string()
: ", recurring payments terms of service at " +
invoice.recurring_payment_terms_of_service_url)
<< " in " << invoice.currency << " with price parts " << format::as_array(invoice.price_parts)
<< " and suggested tip amounts " << invoice.suggested_tip_amounts << " up to "
<< invoice.max_tip_amount << "]";
}
@ -784,6 +888,8 @@ Result<InputInvoice> process_input_message_invoice(
result.invoice.max_tip_amount = input_invoice->invoice_->max_tip_amount_;
result.invoice.suggested_tip_amounts = std::move(input_invoice->invoice_->suggested_tip_amounts_);
result.invoice.recurring_payment_terms_of_service_url =
std::move(input_invoice->invoice_->recurring_payment_terms_of_service_url_);
result.invoice.is_test = input_invoice->invoice_->is_test_;
result.invoice.need_name = input_invoice->invoice_->need_name_;
result.invoice.need_phone_number = input_invoice->invoice_->need_phone_number_;
@ -810,10 +916,10 @@ Result<InputInvoice> process_input_message_invoice(
tl_object_ptr<td_api::messageInvoice> get_message_invoice_object(const InputInvoice &input_invoice, Td *td) {
return make_tl_object<td_api::messageInvoice>(
input_invoice.title, input_invoice.description, get_photo_object(td->file_manager_.get(), input_invoice.photo),
input_invoice.invoice.currency, input_invoice.total_amount, input_invoice.start_parameter,
input_invoice.invoice.is_test, input_invoice.invoice.need_shipping_address,
input_invoice.receipt_message_id.get());
input_invoice.title, get_product_description_object(input_invoice.description),
get_photo_object(td->file_manager_.get(), input_invoice.photo), input_invoice.invoice.currency,
input_invoice.total_amount, input_invoice.start_parameter, input_invoice.invoice.is_test,
input_invoice.invoice.need_shipping_address, input_invoice.receipt_message_id.get());
}
static tl_object_ptr<telegram_api::invoice> get_input_invoice(const Invoice &invoice) {
@ -845,14 +951,18 @@ static tl_object_ptr<telegram_api::invoice> get_input_invoice(const Invoice &inv
if (invoice.max_tip_amount != 0) {
flags |= telegram_api::invoice::MAX_TIP_AMOUNT_MASK;
}
if (!invoice.recurring_payment_terms_of_service_url.empty()) {
flags |= telegram_api::invoice::RECURRING_TERMS_URL_MASK;
}
auto prices = transform(invoice.price_parts, [](const LabeledPricePart &price) {
return telegram_api::make_object<telegram_api::labeledPrice>(price.label, price.amount);
});
return make_tl_object<telegram_api::invoice>(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, invoice.currency, std::move(prices),
invoice.max_tip_amount, vector<int64>(invoice.suggested_tip_amounts));
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, invoice.currency, std::move(prices),
invoice.max_tip_amount, vector<int64>(invoice.suggested_tip_amounts),
invoice.recurring_payment_terms_of_service_url);
}
static tl_object_ptr<telegram_api::inputWebDocument> get_input_web_document(const FileManager *file_manager,
@ -1147,9 +1257,10 @@ void answer_pre_checkout_query(Td *td, int64 pre_checkout_query_id, const string
td->create_handler<SetBotPreCheckoutAnswerQuery>(std::move(promise))->send(pre_checkout_query_id, error_message);
}
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::themeParameters> &theme,
void get_payment_form(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice,
const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<tl_object_ptr<td_api::paymentForm>> &&promise) {
TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
TRY_RESULT_PROMISE(promise, input_invoice_info, get_input_invoice_info(td, std::move(input_invoice)));
tl_object_ptr<telegram_api::dataJSON> theme_parameters;
if (theme != nullptr) {
@ -1157,12 +1268,13 @@ void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::objec
theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false);
}
td->create_handler<GetPaymentFormQuery>(std::move(promise))
->send(full_message_id.get_dialog_id(), server_message_id, std::move(theme_parameters));
->send(std::move(input_invoice_info), std::move(theme_parameters));
}
void validate_order_info(Td *td, FullMessageId full_message_id, tl_object_ptr<td_api::orderInfo> order_info,
bool allow_save, Promise<tl_object_ptr<td_api::validatedOrderInfo>> &&promise) {
TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
void validate_order_info(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice,
td_api::object_ptr<td_api::orderInfo> &&order_info, bool allow_save,
Promise<td_api::object_ptr<td_api::validatedOrderInfo>> &&promise) {
TRY_RESULT_PROMISE(promise, input_invoice_info, get_input_invoice_info(td, std::move(input_invoice)));
if (order_info != nullptr) {
if (!clean_input_string(order_info->name_)) {
@ -1197,13 +1309,14 @@ void validate_order_info(Td *td, FullMessageId full_message_id, tl_object_ptr<td
}
td->create_handler<ValidateRequestedInfoQuery>(std::move(promise))
->send(full_message_id.get_dialog_id(), server_message_id, convert_order_info(std::move(order_info)), allow_save);
->send(std::move(input_invoice_info), convert_order_info(std::move(order_info)), allow_save);
}
void send_payment_form(Td *td, FullMessageId full_message_id, int64 payment_form_id, const string &order_info_id,
const string &shipping_option_id, const tl_object_ptr<td_api::InputCredentials> &credentials,
int64 tip_amount, Promise<tl_object_ptr<td_api::paymentResult>> &&promise) {
TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
void send_payment_form(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice, int64 payment_form_id,
const string &order_info_id, const string &shipping_option_id,
const td_api::object_ptr<td_api::InputCredentials> &credentials, int64 tip_amount,
Promise<td_api::object_ptr<td_api::paymentResult>> &&promise) {
TRY_RESULT_PROMISE(promise, input_invoice_info, get_input_invoice_info(td, std::move(input_invoice)));
if (credentials == nullptr) {
return promise.set_error(Status::Error(400, "Input payment credentials must be non-empty"));
@ -1254,7 +1367,7 @@ void send_payment_form(Td *td, FullMessageId full_message_id, int64 payment_form
}
td->create_handler<SendPaymentFormQuery>(std::move(promise))
->send(full_message_id.get_dialog_id(), server_message_id, payment_form_id, order_info_id, shipping_option_id,
->send(std::move(input_invoice_info), payment_form_id, order_info_id, shipping_option_id,
std::move(input_credentials), tip_amount);
}
@ -1278,6 +1391,14 @@ void delete_saved_credentials(Td *td, Promise<Unit> &&promise) {
td->create_handler<ClearSavedInfoQuery>(std::move(promise))->send(true, false);
}
void export_invoice(Td *td, td_api::object_ptr<td_api::InputMessageContent> &&invoice, Promise<string> &&promise) {
if (invoice == nullptr) {
return promise.set_error(Status::Error(400, "Invoice must be non-empty"));
}
TRY_RESULT_PROMISE(promise, input_invoice, process_input_message_invoice(std::move(invoice), td));
td->create_handler<ExportInvoiceQuery>(std::move(promise))->send(get_input_media_invoice(input_invoice, td));
}
void get_bank_card_info(Td *td, const string &bank_card_number,
Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise) {
td->create_handler<GetBankCardInfoQuery>(std::move(promise))->send(bank_card_number);

View File

@ -39,6 +39,7 @@ struct Invoice {
vector<LabeledPricePart> price_parts;
int64 max_tip_amount = 0;
vector<int64> suggested_tip_amounts;
string recurring_payment_terms_of_service_url;
bool is_test = false;
bool need_name = false;
bool need_phone_number = false;
@ -180,15 +181,18 @@ void answer_shipping_query(Td *td, int64 shipping_query_id,
void answer_pre_checkout_query(Td *td, int64 pre_checkout_query_id, const string &error_message,
Promise<Unit> &&promise);
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::themeParameters> &theme,
void get_payment_form(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice,
const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<tl_object_ptr<td_api::paymentForm>> &&promise);
void validate_order_info(Td *td, FullMessageId full_message_id, tl_object_ptr<td_api::orderInfo> order_info,
bool allow_save, Promise<tl_object_ptr<td_api::validatedOrderInfo>> &&promise);
void validate_order_info(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice,
td_api::object_ptr<td_api::orderInfo> &&order_info, bool allow_save,
Promise<td_api::object_ptr<td_api::validatedOrderInfo>> &&promise);
void send_payment_form(Td *td, FullMessageId full_message_id, int64 payment_form_id, const string &order_info_id,
const string &shipping_option_id, const tl_object_ptr<td_api::InputCredentials> &credentials,
int64 tip_amount, Promise<tl_object_ptr<td_api::paymentResult>> &&promise);
void send_payment_form(Td *td, td_api::object_ptr<td_api::InputInvoice> &&input_invoice, int64 payment_form_id,
const string &order_info_id, const string &shipping_option_id,
const td_api::object_ptr<td_api::InputCredentials> &credentials, int64 tip_amount,
Promise<td_api::object_ptr<td_api::paymentResult>> &&promise);
void get_payment_receipt(Td *td, FullMessageId full_message_id,
Promise<tl_object_ptr<td_api::paymentReceipt>> &&promise);
@ -199,6 +203,8 @@ void delete_saved_order_info(Td *td, Promise<Unit> &&promise);
void delete_saved_credentials(Td *td, Promise<Unit> &&promise);
void export_invoice(Td *td, td_api::object_ptr<td_api::InputMessageContent> &&invoice, Promise<string> &&promise);
void get_bank_card_info(Td *td, const string &bank_card_number,
Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise);

View File

@ -30,6 +30,7 @@ void parse(LabeledPricePart &labeled_price_part, ParserT &parser) {
template <class StorerT>
void store(const Invoice &invoice, StorerT &storer) {
bool has_tip = invoice.max_tip_amount != 0;
bool is_recurring = !invoice.recurring_payment_terms_of_service_url.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(invoice.is_test);
STORE_FLAG(invoice.need_name);
@ -40,6 +41,7 @@ void store(const Invoice &invoice, StorerT &storer) {
STORE_FLAG(invoice.send_phone_number_to_provider);
STORE_FLAG(invoice.send_email_address_to_provider);
STORE_FLAG(has_tip);
STORE_FLAG(is_recurring);
END_STORE_FLAGS();
store(invoice.currency, storer);
store(invoice.price_parts, storer);
@ -47,11 +49,15 @@ void store(const Invoice &invoice, StorerT &storer) {
store(invoice.max_tip_amount, storer);
store(invoice.suggested_tip_amounts, storer);
}
if (is_recurring) {
store(invoice.recurring_payment_terms_of_service_url, storer);
}
}
template <class ParserT>
void parse(Invoice &invoice, ParserT &parser) {
bool has_tip;
bool is_recurring;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(invoice.is_test);
PARSE_FLAG(invoice.need_name);
@ -62,6 +68,7 @@ void parse(Invoice &invoice, ParserT &parser) {
PARSE_FLAG(invoice.send_phone_number_to_provider);
PARSE_FLAG(invoice.send_email_address_to_provider);
PARSE_FLAG(has_tip);
PARSE_FLAG(is_recurring);
END_PARSE_FLAGS();
parse(invoice.currency, parser);
parse(invoice.price_parts, parser);
@ -69,6 +76,9 @@ void parse(Invoice &invoice, ParserT &parser) {
parse(invoice.max_tip_amount, parser);
parse(invoice.suggested_tip_amounts, parser);
}
if (is_recurring) {
parse(invoice.recurring_payment_terms_of_service_url, parser);
}
}
template <class StorerT>

View File

@ -20,7 +20,6 @@
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/SliceBuilder.h"
#include <algorithm>
@ -307,7 +306,8 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&
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') {
if (size.type == 0 || size.type == 't' || size.type == 'i' || size.type == 'p' || size.type == 'u' ||
size.type == 'v') {
LOG(ERROR) << "Skip unallowed photo size " << size;
continue;
}
@ -358,10 +358,23 @@ tl_object_ptr<td_api::chatPhoto> get_chat_photo_object(FileManager *file_manager
return nullptr;
}
const AnimationSize *animation = photo.animations.empty() ? nullptr : &photo.animations.back();
const AnimationSize *small_animation = nullptr;
const AnimationSize *big_animation = nullptr;
for (auto &animation : photo.animations) {
if (animation.type == 'p') {
small_animation = &animation;
} else if (animation.type == 'u') {
big_animation = &animation;
}
}
if (big_animation == nullptr && small_animation != nullptr) {
LOG(ERROR) << "Have small animation without big animation in " << photo;
small_animation = nullptr;
}
return td_api::make_object<td_api::chatPhoto>(
photo.id.get(), photo.date, get_minithumbnail_object(photo.minithumbnail),
get_photo_sizes_object(file_manager, photo.photos), get_animated_chat_photo_object(file_manager, animation));
get_photo_sizes_object(file_manager, photo.photos), get_animated_chat_photo_object(file_manager, big_animation),
get_animated_chat_photo_object(file_manager, small_animation));
}
void photo_delete_thumbnail(Photo &photo) {
@ -490,11 +503,15 @@ SecretInputMedia photo_get_secret_input_media(FileManager *file_manager, const P
if (thumbnail_file_id.is_valid() && thumbnail.empty()) {
return {};
}
auto size = file_view.size();
if (size < 0 || size >= 1000000000) {
size = 0;
}
return SecretInputMedia{
std::move(input_file),
make_tl_object<secret_api::decryptedMessageMediaPhoto>(
std::move(thumbnail), thumbnail_width, thumbnail_height, width, height, narrow_cast<int32>(file_view.size()),
std::move(thumbnail), thumbnail_width, thumbnail_height, width, height, static_cast<int32>(size),
BufferSlice(encryption_key.key_slice()), BufferSlice(encryption_key.iv_slice()), caption)};
}

View File

@ -259,6 +259,10 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
if (source.get_type("get_photo_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
if (res.size < 0 || res.size > 1000000000) {
LOG(ERROR) << "Receive photo of size " << res.size;
res.size = 0;
}
res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, format);
@ -275,8 +279,8 @@ AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource sour
tl_object_ptr<telegram_api::videoSize> &&size) {
CHECK(size != nullptr);
AnimationSize res;
if (size->type_ != "v" && size->type_ != "u") {
LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size);
if (size->type_ != "p" && size->type_ != "u" && size->type_ != "v") {
LOG(ERROR) << "Unsupported videoSize \"" << size->type_ << "\" in " << to_string(size);
}
res.type = static_cast<uint8>(size->type_[0]);
if (res.type >= 128) {
@ -292,6 +296,10 @@ AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource sour
if (source.get_type("get_animation_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
if (res.size < 0 || res.size > 1000000000) {
LOG(ERROR) << "Receive animation of size " << res.size;
res.size = 0;
}
res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, PhotoFormat::Mpeg4);
@ -318,9 +326,9 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
}
auto http_url = r_http_url.move_as_ok();
auto url = http_url.get_url();
file_id = file_manager->register_remote(FullRemoteFileLocation(file_type, url, web_document->access_hash_),
FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_,
get_url_query_file_name(http_url.query_));
file_id = file_manager->register_remote(
FullRemoteFileLocation(file_type, url, web_document->access_hash_), FileLocationSource::FromServer,
owner_dialog_id, 0, static_cast<uint32>(web_document->size_), get_url_query_file_name(http_url.query_));
size = web_document->size_;
mime_type = std::move(web_document->mime_type_);
attributes = std::move(web_document->attributes_);
@ -379,6 +387,11 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
s.dimensions = dimensions;
s.size = size;
s.file_id = file_id;
if (s.size < 0 || s.size > 1000000000) {
LOG(ERROR) << "Receive web photo of size " << s.size;
s.size = 0;
}
return s;
}

View File

@ -27,8 +27,8 @@ class FileManager;
struct PhotoSize {
int32 type = 0;
Dimensions dimensions;
int32 size = 0;
Dimensions dimensions;
FileId file_id;
vector<int32> progressive_sizes;
};

View File

@ -675,6 +675,10 @@ void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id,
auto &message_ids = other_poll_messages_[poll_id];
auto is_deleted = message_ids.erase(full_message_id) > 0;
LOG_CHECK(is_deleted) << source << ' ' << poll_id << ' ' << full_message_id;
if (is_local_poll_id(poll_id)) {
CHECK(message_ids.empty());
forget_local_poll(poll_id);
}
if (message_ids.empty()) {
other_poll_messages_.erase(poll_id);
@ -686,6 +690,10 @@ void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id,
auto &message_ids = server_poll_messages_[poll_id];
auto is_deleted = message_ids.erase(full_message_id) > 0;
LOG_CHECK(is_deleted) << source << ' ' << poll_id << ' ' << full_message_id;
if (is_local_poll_id(poll_id)) {
CHECK(message_ids.empty());
forget_local_poll(poll_id);
}
if (message_ids.empty()) {
server_poll_messages_.erase(poll_id);
update_poll_timeout_.cancel_timeout(poll_id.get());
@ -1287,7 +1295,16 @@ void PollManager::on_unload_poll_timeout(PollId poll_id) {
if (G()->close_flag()) {
return;
}
CHECK(!is_local_poll_id(poll_id));
if (is_local_poll_id(poll_id)) {
LOG(INFO) << "Forget " << poll_id;
auto is_deleted = polls_.erase(poll_id) > 0;
CHECK(is_deleted);
CHECK(poll_voters_.count(poll_id) == 0);
CHECK(loaded_from_database_polls_.count(poll_id) == 0);
return;
}
if (!can_unload_poll(poll_id)) {
return;
@ -1309,6 +1326,11 @@ void PollManager::on_unload_poll_timeout(PollId poll_id) {
unload_poll_timeout_.cancel_timeout(poll_id.get());
}
void PollManager::forget_local_poll(PollId poll_id) {
CHECK(is_local_poll_id(poll_id));
unload_poll_timeout_.set_timeout_in(poll_id.get(), UNLOAD_POLL_DELAY);
}
void PollManager::on_get_poll_results(PollId poll_id, uint64 generation,
Result<tl_object_ptr<telegram_api::Updates>> result) {
auto poll = get_poll(poll_id);
@ -1692,7 +1714,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
}
if (poll->explanation != explanation && (!is_min || poll_server_is_closed)) {
if (explanation.text.empty() && !poll->explanation.text.empty()) {
LOG(ERROR) << "Can't change known " << poll_id << " explanation to empty from " << source ;
LOG(ERROR) << "Can't change known " << poll_id << " explanation to empty from " << source;
} else {
poll->explanation = std::move(explanation);
is_changed = true;

View File

@ -215,6 +215,8 @@ class PollManager final : public Actor {
void do_stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup,
uint64 log_event_id, Promise<Unit> &&promise);
void forget_local_poll(PollId poll_id);
MultiTimeout update_poll_timeout_{"UpdatePollTimeout"};
MultiTimeout close_poll_timeout_{"ClosePollTimeout"};
MultiTimeout unload_poll_timeout_{"UnloadPollTimeout"};

380
td/telegram/Premium.cpp Normal file
View File

@ -0,0 +1,380 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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)
//
#include "td/telegram/Premium.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/Application.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageEntity.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/misc.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
namespace td {
static td_api::object_ptr<td_api::PremiumFeature> get_premium_feature_object(Slice premium_feature) {
if (premium_feature == "double_limits") {
return td_api::make_object<td_api::premiumFeatureIncreasedLimits>();
}
if (premium_feature == "more_upload") {
return td_api::make_object<td_api::premiumFeatureIncreasedUploadFileSize>();
}
if (premium_feature == "faster_download") {
return td_api::make_object<td_api::premiumFeatureImprovedDownloadSpeed>();
}
if (premium_feature == "voice_to_text") {
return td_api::make_object<td_api::premiumFeatureVoiceRecognition>();
}
if (premium_feature == "no_ads") {
return td_api::make_object<td_api::premiumFeatureDisabledAds>();
}
if (premium_feature == "unique_reactions") {
return td_api::make_object<td_api::premiumFeatureUniqueReactions>();
}
if (premium_feature == "premium_stickers") {
return td_api::make_object<td_api::premiumFeatureUniqueStickers>();
}
if (premium_feature == "advanced_chat_management") {
return td_api::make_object<td_api::premiumFeatureAdvancedChatManagement>();
}
if (premium_feature == "profile_badge") {
return td_api::make_object<td_api::premiumFeatureProfileBadge>();
}
if (premium_feature == "animated_userpics") {
return td_api::make_object<td_api::premiumFeatureAnimatedProfilePhoto>();
}
if (premium_feature == "app_icons") {
return td_api::make_object<td_api::premiumFeatureAppIcons>();
}
return nullptr;
}
class GetPremiumPromoQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::premiumState>> promise_;
public:
explicit GetPremiumPromoQuery(Promise<td_api::object_ptr<td_api::premiumState>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::help_getPremiumPromo()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::help_getPremiumPromo>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto promo = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetPremiumPromoQuery: " << to_string(promo);
td_->contacts_manager_->on_get_users(std::move(promo->users_), "GetPremiumPromoQuery");
auto state = get_message_text(td_->contacts_manager_.get(), std::move(promo->status_text_),
std::move(promo->status_entities_), true, true, 0, false, "GetPremiumPromoQuery");
if (promo->video_sections_.size() != promo->videos_.size()) {
return on_error(Status::Error(500, "Receive wrong number of videos"));
}
if (promo->monthly_amount_ < 0 || promo->monthly_amount_ > 9999'9999'9999) {
return on_error(Status::Error(500, "Receive invalid monthly amount"));
}
if (promo->currency_.size() != 3) {
return on_error(Status::Error(500, "Receive invalid currency"));
}
vector<td_api::object_ptr<td_api::premiumFeaturePromotionAnimation>> animations;
for (size_t i = 0; i < promo->video_sections_.size(); i++) {
auto feature = get_premium_feature_object(promo->video_sections_[i]);
if (feature == nullptr) {
continue;
}
auto video = std::move(promo->videos_[i]);
if (video->get_id() != telegram_api::document::ID) {
LOG(ERROR) << "Receive " << to_string(video) << " for " << promo->video_sections_[i];
continue;
}
auto parsed_document = td_->documents_manager_->on_get_document(move_tl_object_as<telegram_api::document>(video),
DialogId(), nullptr, Document::Type::Animation);
if (parsed_document.type != Document::Type::Animation) {
LOG(ERROR) << "Receive " << parsed_document.type << " for " << promo->video_sections_[i];
continue;
}
auto animation_object = td_->animations_manager_->get_animation_object(parsed_document.file_id);
animations.push_back(td_api::make_object<td_api::premiumFeaturePromotionAnimation>(std::move(feature),
std::move(animation_object)));
}
promise_.set_value(td_api::make_object<td_api::premiumState>(get_formatted_text_object(state, true, 0),
std::move(promo->currency_), promo->monthly_amount_,
std::move(animations)));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
const vector<Slice> &get_premium_limit_keys() {
static const vector<Slice> limit_keys{"channels",
"saved_gifs",
"stickers_faved",
"dialog_filters",
"dialog_filters_chats",
"dialogs_pinned",
"dialogs_folder_pinned",
"channels_public",
"caption_length",
"about_length"};
return limit_keys;
}
static Slice get_limit_type_key(const td_api::PremiumLimitType *limit_type) {
CHECK(limit_type != nullptr);
switch (limit_type->get_id()) {
case td_api::premiumLimitTypeSupergroupCount::ID:
return Slice("channels");
case td_api::premiumLimitTypeSavedAnimationCount::ID:
return Slice("saved_gifs");
case td_api::premiumLimitTypeFavoriteStickerCount::ID:
return Slice("stickers_faved");
case td_api::premiumLimitTypeChatFilterCount::ID:
return Slice("dialog_filters");
case td_api::premiumLimitTypeChatFilterChosenChatCount::ID:
return Slice("dialog_filters_chats");
case td_api::premiumLimitTypePinnedChatCount::ID:
return Slice("dialogs_pinned");
case td_api::premiumLimitTypePinnedArchivedChatCount::ID:
return Slice("dialogs_folder_pinned");
case td_api::premiumLimitTypeCreatedPublicChatCount::ID:
return Slice("channels_public");
case td_api::premiumLimitTypeCaptionLength::ID:
return Slice("caption_length");
case td_api::premiumLimitTypeBioLength::ID:
return Slice("about_length");
default:
UNREACHABLE();
return Slice();
}
}
static string get_premium_source(const td_api::PremiumLimitType *limit_type) {
if (limit_type == nullptr) {
return string();
}
return PSTRING() << "double_limits__" << get_limit_type_key(limit_type);
}
static string get_premium_source(const td_api::PremiumFeature *feature) {
if (feature == nullptr) {
return string();
}
switch (feature->get_id()) {
case td_api::premiumFeatureIncreasedLimits::ID:
return "double_limits";
case td_api::premiumFeatureIncreasedUploadFileSize::ID:
return "more_upload";
case td_api::premiumFeatureImprovedDownloadSpeed::ID:
return "faster_download";
case td_api::premiumFeatureVoiceRecognition::ID:
return "voice_to_text";
case td_api::premiumFeatureDisabledAds::ID:
return "no_ads";
case td_api::premiumFeatureUniqueReactions::ID:
return "unique_reactions";
case td_api::premiumFeatureUniqueStickers::ID:
return "premium_stickers";
case td_api::premiumFeatureAdvancedChatManagement::ID:
return "advanced_chat_management";
case td_api::premiumFeatureProfileBadge::ID:
return "profile_badge";
case td_api::premiumFeatureAnimatedProfilePhoto::ID:
return "animated_userpics";
case td_api::premiumFeatureAppIcons::ID:
return "app_icons";
default:
UNREACHABLE();
}
return string();
}
static string get_premium_source(const td_api::object_ptr<td_api::PremiumSource> &source) {
if (source == nullptr) {
return string();
}
switch (source->get_id()) {
case td_api::premiumSourceLimitExceeded::ID: {
auto *limit_type = static_cast<const td_api::premiumSourceLimitExceeded *>(source.get())->limit_type_.get();
return get_premium_source(limit_type);
}
case td_api::premiumSourceFeature::ID: {
auto *feature = static_cast<const td_api::premiumSourceFeature *>(source.get())->feature_.get();
return get_premium_source(feature);
}
case td_api::premiumSourceLink::ID: {
auto &referrer = static_cast<const td_api::premiumSourceLink *>(source.get())->referrer_;
if (referrer.empty()) {
return "deeplink";
}
return PSTRING() << "deeplink_" << referrer;
}
case td_api::premiumSourceSettings::ID:
return "settings";
default:
UNREACHABLE();
return string();
}
}
static td_api::object_ptr<td_api::premiumLimit> get_premium_limit_object(Slice key) {
int32 default_limit =
static_cast<int32>(G()->shared_config().get_option_integer(PSLICE() << key << "_limit_default"));
int32 premium_limit =
static_cast<int32>(G()->shared_config().get_option_integer(PSLICE() << key << "_limit_premium"));
if (default_limit <= 0 || premium_limit <= default_limit) {
return nullptr;
}
auto type = [&]() -> td_api::object_ptr<td_api::PremiumLimitType> {
if (key == "channels") {
return td_api::make_object<td_api::premiumLimitTypeSupergroupCount>();
}
if (key == "saved_gifs") {
return td_api::make_object<td_api::premiumLimitTypeSavedAnimationCount>();
}
if (key == "stickers_faved") {
return td_api::make_object<td_api::premiumLimitTypeFavoriteStickerCount>();
}
if (key == "dialog_filters") {
return td_api::make_object<td_api::premiumLimitTypeChatFilterCount>();
}
if (key == "dialog_filters_chats") {
return td_api::make_object<td_api::premiumLimitTypeChatFilterChosenChatCount>();
}
if (key == "dialogs_pinned") {
return td_api::make_object<td_api::premiumLimitTypePinnedChatCount>();
}
if (key == "dialogs_folder_pinned") {
return td_api::make_object<td_api::premiumLimitTypePinnedArchivedChatCount>();
}
if (key == "channels_public") {
return td_api::make_object<td_api::premiumLimitTypeCreatedPublicChatCount>();
}
if (key == "caption_length") {
return td_api::make_object<td_api::premiumLimitTypeCaptionLength>();
}
if (key == "about_length") {
return td_api::make_object<td_api::premiumLimitTypeBioLength>();
}
UNREACHABLE();
return nullptr;
}();
return td_api::make_object<td_api::premiumLimit>(std::move(type), default_limit, premium_limit);
}
void get_premium_limit(const td_api::object_ptr<td_api::PremiumLimitType> &limit_type,
Promise<td_api::object_ptr<td_api::premiumLimit>> &&promise) {
if (limit_type == nullptr) {
return promise.set_error(Status::Error(400, "Limit type must be non-empty"));
}
promise.set_value(get_premium_limit_object(get_limit_type_key(limit_type.get())));
}
void get_premium_features(Td *td, const td_api::object_ptr<td_api::PremiumSource> &source,
Promise<td_api::object_ptr<td_api::premiumFeatures>> &&promise) {
auto premium_features =
full_split(G()->shared_config().get_option_string(
"premium_features",
"double_limits,more_upload,faster_download,voice_to_text,no_ads,unique_reactions,premium_stickers,"
"advanced_chat_management,profile_badge,animated_userpics,app_icons"),
',');
vector<td_api::object_ptr<td_api::PremiumFeature>> features;
for (const auto &premium_feature : premium_features) {
auto feature = get_premium_feature_object(premium_feature);
if (feature != nullptr) {
features.push_back(std::move(feature));
}
}
auto limits = transform(get_premium_limit_keys(), get_premium_limit_object);
td::remove_if(limits, [](auto &limit) { return limit == nullptr; });
auto source_str = get_premium_source(source);
if (!source_str.empty()) {
vector<tl_object_ptr<telegram_api::jsonObjectValue>> data;
vector<tl_object_ptr<telegram_api::JSONValue>> promo_order;
for (const auto &premium_feature : premium_features) {
promo_order.push_back(make_tl_object<telegram_api::jsonString>(premium_feature));
}
data.push_back(make_tl_object<telegram_api::jsonObjectValue>(
"premium_promo_order", make_tl_object<telegram_api::jsonArray>(std::move(promo_order))));
data.push_back(
make_tl_object<telegram_api::jsonObjectValue>("source", make_tl_object<telegram_api::jsonString>(source_str)));
save_app_log(td, "premium.promo_screen_show", DialogId(), make_tl_object<telegram_api::jsonObject>(std::move(data)),
Promise<Unit>());
}
td_api::object_ptr<td_api::InternalLinkType> payment_link;
auto premium_bot_username = G()->shared_config().get_option_string("premium_bot_username");
if (!premium_bot_username.empty()) {
payment_link = td_api::make_object<td_api::internalLinkTypeBotStart>(premium_bot_username, source_str);
} else {
auto premium_invoice_slug = G()->shared_config().get_option_string("premium_invoice_slug");
if (!premium_invoice_slug.empty()) {
payment_link = td_api::make_object<td_api::internalLinkTypeInvoice>(premium_invoice_slug);
}
}
promise.set_value(
td_api::make_object<td_api::premiumFeatures>(std::move(features), std::move(limits), std::move(payment_link)));
}
void view_premium_feature(Td *td, const td_api::object_ptr<td_api::PremiumFeature> &feature, Promise<Unit> &&promise) {
auto source = get_premium_source(feature.get());
if (source.empty()) {
return promise.set_error(Status::Error(400, "Feature must be non-empty"));
}
vector<tl_object_ptr<telegram_api::jsonObjectValue>> data;
data.push_back(
make_tl_object<telegram_api::jsonObjectValue>("item", make_tl_object<telegram_api::jsonString>(source)));
save_app_log(td, "premium.promo_screen_tap", DialogId(), make_tl_object<telegram_api::jsonObject>(std::move(data)),
std::move(promise));
}
void click_premium_subscription_button(Td *td, Promise<Unit> &&promise) {
vector<tl_object_ptr<telegram_api::jsonObjectValue>> data;
save_app_log(td, "premium.promo_screen_accept", DialogId(), make_tl_object<telegram_api::jsonObject>(std::move(data)),
std::move(promise));
}
void get_premium_state(Td *td, Promise<td_api::object_ptr<td_api::premiumState>> &&promise) {
td->create_handler<GetPremiumPromoQuery>(std::move(promise))->send();
}
} // namespace td

34
td/telegram/Premium.h Normal file
View File

@ -0,0 +1,34 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
class Td;
const vector<Slice> &get_premium_limit_keys();
void get_premium_limit(const td_api::object_ptr<td_api::PremiumLimitType> &limit_type,
Promise<td_api::object_ptr<td_api::premiumLimit>> &&promise);
void get_premium_features(Td *td, const td_api::object_ptr<td_api::PremiumSource> &source,
Promise<td_api::object_ptr<td_api::premiumFeatures>> &&promise);
void view_premium_feature(Td *td, const td_api::object_ptr<td_api::PremiumFeature> &feature, Promise<Unit> &&promise);
void click_premium_subscription_button(Td *td, Promise<Unit> &&promise);
void get_premium_state(Td *td, Promise<td_api::object_ptr<td_api::premiumState>> &&promise);
} // namespace td

View File

@ -537,7 +537,7 @@ static Result<InlineKeyboardButton> get_inline_keyboard_button(tl_object_ptr<td_
if (user_id.is_valid()) {
return Status::Error(400, "Link to a user can't be used in login URL buttons");
}
auto r_url = LinkManager::check_link(button_type->url_, true);
auto r_url = LinkManager::check_link(button_type->url_, true, !G()->is_test_dc());
if (r_url.is_error()) {
return Status::Error(400, PSLICE() << "Inline keyboard button login " << r_url.error().message());
}

View File

@ -633,13 +633,15 @@ void SecretChatActor::check_status(Status status) {
if (status.code() == 1) {
LOG(WARNING) << "Non-fatal error: " << status;
} else {
on_fatal_error(std::move(status));
on_fatal_error(std::move(status), false);
}
}
}
void SecretChatActor::on_fatal_error(Status status) {
LOG(ERROR) << "Fatal error: " << status;
void SecretChatActor::on_fatal_error(Status status, bool is_expected) {
if (!is_expected) {
LOG(ERROR) << "Fatal error: " << status;
}
cancel_chat(false, false, Promise<>());
}
@ -1650,19 +1652,9 @@ void SecretChatActor::on_outbound_send_message_error(uint64 state_id, Status err
state = outbound_message_states_.get(state_id);
need_sync = true;
}
} else {
bool should_fail = false;
if (error.code() == 429) {
should_fail = false;
} else if (error.code() == 400 && error.message() == "ENCRYPTION_DECLINED") {
should_fail = true;
} else {
LOG(ERROR) << "Got unknown error for encrypted service message: " << error;
should_fail = true;
}
if (should_fail) {
return on_fatal_error(std::move(error));
}
} else if (error.code() != 429) {
return on_fatal_error(std::move(error),
(error.code() == 400 && error.message() == "ENCRYPTION_DECLINED") || error.code() == 403);
}
auto query = create_net_query(*state->message);
state->net_query_id = query->id();

View File

@ -637,7 +637,7 @@ class SecretChatActor final : public NetQueryCallback {
void outbound_loop(OutboundMessageState *state, uint64 state_id);
// DiscardEncryption
void on_fatal_error(Status status);
void on_fatal_error(Status status, bool is_expected);
void do_close_chat_impl(bool delete_history, bool is_already_discarded, uint64 log_event_id, Promise<Unit> &&promise);
void on_closed(uint64 log_event_id, Promise<Unit> &&promise);

View File

@ -13,7 +13,8 @@ enum class SecretChatLayer : int32 {
Mtproto2 = 73,
NewEntities = 101,
DeleteMessagesOnClose = 123,
Current = DeleteMessagesOnClose
SupportBigFiles = 143,
Current = SupportBigFiles
};
} // namespace td

View File

@ -7,19 +7,34 @@
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/SecretChatLayer.h"
#include "td/utils/misc.h"
namespace td {
SecretInputMedia::SecretInputMedia(tl_object_ptr<telegram_api::InputEncryptedFile> input_file, BufferSlice &&thumbnail,
Dimensions thumbnail_dimensions, const string &mime_type, const FileView &file_view,
vector<tl_object_ptr<secret_api::DocumentAttribute>> &&attributes,
const string &caption)
const string &caption, int32 layer)
: input_file_(std::move(input_file)) {
auto &encryption_key = file_view.encryption_key();
decrypted_media_ = secret_api::make_object<secret_api::decryptedMessageMediaDocument>(
std::move(thumbnail), thumbnail_dimensions.width, thumbnail_dimensions.height, mime_type,
narrow_cast<int32>(file_view.size()), BufferSlice(encryption_key.key_slice()),
BufferSlice(encryption_key.iv_slice()), std::move(attributes), caption);
auto size = file_view.size();
if (layer >= static_cast<int32>(SecretChatLayer::SupportBigFiles)) {
decrypted_media_ = secret_api::make_object<secret_api::decryptedMessageMediaDocument>(
std::move(thumbnail), thumbnail_dimensions.width, thumbnail_dimensions.height, mime_type, size,
BufferSlice(encryption_key.key_slice()), BufferSlice(encryption_key.iv_slice()), std::move(attributes),
caption);
return;
}
if (size <= (2000 << 20)) {
decrypted_media_ = secret_api::make_object<secret_api::decryptedMessageMediaDocument46>(
std::move(thumbnail), thumbnail_dimensions.width, thumbnail_dimensions.height, mime_type,
narrow_cast<int32>(size), BufferSlice(encryption_key.key_slice()), BufferSlice(encryption_key.iv_slice()),
std::move(attributes), caption);
return;
}
input_file_ = nullptr;
}
} // namespace td

View File

@ -30,7 +30,8 @@ struct SecretInputMedia {
SecretInputMedia(tl_object_ptr<telegram_api::InputEncryptedFile> input_file, BufferSlice &&thumbnail,
Dimensions thumbnail_dimensions, const string &mime_type, const FileView &file_view,
vector<tl_object_ptr<secret_api::DocumentAttribute>> &&attributes, const string &caption);
vector<tl_object_ptr<secret_api::DocumentAttribute>> &&attributes, const string &caption,
int32 layer);
bool empty() const {
return decrypted_media_ == nullptr;

View File

@ -264,7 +264,7 @@ void MultiSequenceDispatcherOld::send(NetQueryPtr query) {
auto &data = it_ok.first->second;
if (it_ok.second) {
LOG(DEBUG) << "Create SequenceDispatcher" << sequence_id;
data.dispatcher_ = create_actor<SequenceDispatcher>("sequence dispatcher", actor_shared(this, sequence_id));
data.dispatcher_ = create_actor<SequenceDispatcher>("SequenceDispatcher", actor_shared(this, sequence_id));
}
data.cnt_++;
query->debug(PSTRING() << "send to SequenceDispatcher " << tag("sequence_id", sequence_id));

View File

@ -90,6 +90,7 @@ class ViewSponsoredMessageQuery final : public Td::ResultHandler {
struct SponsoredMessageManager::SponsoredMessage {
int64 local_id = 0;
bool is_recommended = false;
DialogId sponsor_dialog_id;
ServerMessageId server_message_id;
string start_param;
@ -97,9 +98,10 @@ struct SponsoredMessageManager::SponsoredMessage {
unique_ptr<MessageContent> content;
SponsoredMessage() = default;
SponsoredMessage(int64 local_id, DialogId sponsor_dialog_id, ServerMessageId server_message_id, string start_param,
string invite_hash, unique_ptr<MessageContent> content)
SponsoredMessage(int64 local_id, bool is_recommended, DialogId sponsor_dialog_id, ServerMessageId server_message_id,
string start_param, string invite_hash, unique_ptr<MessageContent> content)
: local_id(local_id)
, is_recommended(is_recommended)
, sponsor_dialog_id(sponsor_dialog_id)
, server_message_id(server_message_id)
, start_param(std::move(start_param))
@ -187,8 +189,9 @@ td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponso
break;
}
return td_api::make_object<td_api::sponsoredMessage>(
sponsored_message.local_id, sponsored_message.sponsor_dialog_id.get(), std::move(chat_invite_link_info),
std::move(link), get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
sponsored_message.local_id, sponsored_message.is_recommended, sponsored_message.sponsor_dialog_id.get(),
std::move(chat_invite_link_info), std::move(link),
get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
}
td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponsored_message_object(
@ -310,9 +313,10 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages(
auto local_id = current_sponsored_message_id_.get();
CHECK(!current_sponsored_message_id_.is_valid());
CHECK(!current_sponsored_message_id_.is_scheduled());
CHECK(messages->message_random_ids.count(local_id) == 0);
messages->message_random_ids[local_id] = sponsored_message->random_id_.as_slice().str();
messages->messages.emplace_back(local_id, sponsor_dialog_id, server_message_id,
auto is_inserted =
messages->message_random_ids.emplace(local_id, sponsored_message->random_id_.as_slice().str()).second;
CHECK(is_inserted);
messages->messages.emplace_back(local_id, sponsored_message->recommended_, sponsor_dialog_id, server_message_id,
std::move(sponsored_message->start_param_), std::move(invite_hash),
std::move(content));
}

View File

@ -9,7 +9,6 @@
#include "td/actor/PromiseFuture.h"
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
#include "td/utils/logging.h"
#include "td/utils/Time.h"

View File

@ -88,6 +88,21 @@ Slice get_sticker_format_extension(StickerFormat sticker_format) {
}
}
PhotoFormat get_sticker_format_photo_format(StickerFormat sticker_format) {
switch (sticker_format) {
case StickerFormat::Unknown:
case StickerFormat::Webp:
return PhotoFormat::Webp;
case StickerFormat::Tgs:
return PhotoFormat::Tgs;
case StickerFormat::Webm:
return PhotoFormat::Webm;
default:
UNREACHABLE();
return PhotoFormat::Webp;
}
}
bool is_sticker_format_animated(StickerFormat sticker_format) {
switch (sticker_format) {
case StickerFormat::Unknown:

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
@ -28,6 +29,8 @@ string get_sticker_format_mime_type(StickerFormat sticker_format);
Slice get_sticker_format_extension(StickerFormat sticker_format);
PhotoFormat get_sticker_format_photo_format(StickerFormat sticker_format);
bool is_sticker_format_animated(StickerFormat sticker_format);
bool is_sticker_format_vector(StickerFormat sticker_format);

View File

@ -8,6 +8,7 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/AvailableReaction.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
@ -35,6 +36,7 @@
#include "td/telegram/TdDb.h"
#include "td/telegram/TdParameters.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/Version.h"
#include "td/db/SqliteKeyValue.h"
#include "td/db/SqliteKeyValueAsync.h"
@ -745,10 +747,14 @@ class ReloadSpecialStickerSetQuery final : public Td::ResultHandler {
auto set_ptr = result_ptr.move_as_ok();
if (set_ptr->get_id() == telegram_api::messages_stickerSet::ID) {
// sticker_set_id_ needs to be replaced always
// sticker_set_id_ must be replaced always, because it could have been changed
// we must not pass sticker_set_id_ in order to allow its change
sticker_set_id_ = td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), std::move(set_ptr), true,
"ReloadSpecialStickerSetQuery");
} else if (sticker_set_id_.is_valid()) {
} else {
CHECK(set_ptr->get_id() == telegram_api::messages_stickerSetNotModified::ID);
// we received telegram_api::messages_stickerSetNotModified, and must pass sticker_set_id_ to handle it
// sticker_set_id_ can't be changed by this call
td_->stickers_manager_->on_get_messages_sticker_set(sticker_set_id_, std::move(set_ptr), false,
"ReloadSpecialStickerSetQuery");
}
@ -1237,21 +1243,31 @@ class StickersManager::StickerListLogEvent {
class StickersManager::StickerSetListLogEvent {
public:
vector<StickerSetId> sticker_set_ids;
vector<StickerSetId> sticker_set_ids_;
bool is_premium_ = false;
StickerSetListLogEvent() = default;
explicit StickerSetListLogEvent(vector<StickerSetId> sticker_set_ids) : sticker_set_ids(std::move(sticker_set_ids)) {
StickerSetListLogEvent(vector<StickerSetId> sticker_set_ids, bool is_premium)
: sticker_set_ids_(std::move(sticker_set_ids)), is_premium_(is_premium) {
}
template <class StorerT>
void store(StorerT &storer) const {
td::store(sticker_set_ids, storer);
BEGIN_STORE_FLAGS();
STORE_FLAG(is_premium_);
END_STORE_FLAGS();
td::store(sticker_set_ids_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
td::parse(sticker_set_ids, parser);
if (parser.version() >= static_cast<int32>(Version::AddStickerSetListFlags)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_premium_);
END_PARSE_FLAGS();
}
td::parse(sticker_set_ids_, parser);
}
};
@ -1872,11 +1888,14 @@ tl_object_ptr<td_api::sticker> StickersManager::get_sticker_object(FileId file_i
width = static_cast<int32>(width * zoom + 0.5);
height = static_cast<int32>(height * zoom + 0.5);
}
return make_tl_object<td_api::sticker>(
auto premium_animation_object = sticker->premium_animation_file_id.is_valid()
? td_->file_manager_->get_file_object(sticker->premium_animation_file_id)
: nullptr;
return td_api::make_object<td_api::sticker>(
sticker->set_id.get(), width, height, sticker->alt,
get_sticker_type_object(sticker->format, 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));
std::move(thumbnail_object), std::move(premium_animation_object), td_->file_manager_->get_file_object(file_id));
}
tl_object_ptr<td_api::stickers> StickersManager::get_stickers_object(const vector<FileId> &sticker_ids) const {
@ -2016,7 +2035,7 @@ tl_object_ptr<td_api::stickerSets> StickersManager::get_sticker_sets_object(int3
vector<tl_object_ptr<td_api::stickerSetInfo>> result;
result.reserve(sticker_set_ids.size());
for (auto sticker_set_id : sticker_set_ids) {
auto sticker_set_info = get_sticker_set_info_object(sticker_set_id, covers_limit);
auto sticker_set_info = get_sticker_set_info_object(sticker_set_id, covers_limit, false);
if (sticker_set_info->size_ != 0) {
result.push_back(std::move(sticker_set_info));
}
@ -2033,30 +2052,57 @@ tl_object_ptr<td_api::stickerSets> StickersManager::get_sticker_sets_object(int3
}
tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_object(StickerSetId sticker_set_id,
size_t covers_limit) const {
size_t covers_limit,
bool prefer_premium) const {
const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
CHECK(sticker_set->is_inited);
sticker_set->was_update_sent = true;
std::vector<tl_object_ptr<td_api::sticker>> stickers;
for (auto sticker_id : sticker_set->sticker_ids) {
stickers.push_back(get_sticker_object(sticker_id));
if (stickers.size() >= covers_limit) {
break;
vector<td_api::object_ptr<td_api::sticker>> stickers;
if (prefer_premium) {
vector<FileId> regular_sticker_ids;
vector<FileId> premium_sticker_ids;
std::tie(regular_sticker_ids, premium_sticker_ids) = split_stickers_by_premium(sticker_set->sticker_ids);
auto is_premium = G()->shared_config().get_option_boolean("is_premium");
size_t max_premium_stickers = is_premium ? covers_limit : 1;
if (premium_sticker_ids.size() > max_premium_stickers) {
premium_sticker_ids.resize(max_premium_stickers);
}
CHECK(premium_sticker_ids.size() <= covers_limit);
if (regular_sticker_ids.size() > covers_limit - premium_sticker_ids.size()) {
regular_sticker_ids.resize(covers_limit - premium_sticker_ids.size());
}
if (!is_premium) {
std::swap(premium_sticker_ids, regular_sticker_ids);
}
append(premium_sticker_ids, regular_sticker_ids);
for (auto sticker_id : premium_sticker_ids) {
stickers.push_back(get_sticker_object(sticker_id));
if (stickers.size() >= covers_limit) {
break;
}
}
} else {
for (auto sticker_id : sticker_set->sticker_ids) {
stickers.push_back(get_sticker_object(sticker_id));
if (stickers.size() >= covers_limit) {
break;
}
}
}
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
auto actual_count = narrow_cast<int32>(sticker_set->sticker_ids.size());
return make_tl_object<td_api::stickerSetInfo>(
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_set_minithumbnail_zoom(sticker_set)),
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
get_sticker_type_object(sticker_set->sticker_format, sticker_set->is_masks, nullptr), sticker_set->is_viewed,
sticker_set->was_loaded ? narrow_cast<int32>(sticker_set->sticker_ids.size()) : sticker_set->sticker_count,
std::move(stickers));
sticker_set->was_loaded ? actual_count : max(actual_count, sticker_set->sticker_count), std::move(stickers));
}
const StickersManager::StickerSet *StickersManager::get_animated_emoji_sticker_set() {
@ -2195,6 +2241,7 @@ FileId StickersManager::on_get_sticker(unique_ptr<Sticker> new_sticker, bool rep
<< s->m_thumbnail << " to " << new_sticker->m_thumbnail;
s->m_thumbnail = std::move(new_sticker->m_thumbnail);
}
s->premium_animation_file_id = new_sticker->premium_animation_file_id;
if (s->format != new_sticker->format && new_sticker->format != StickerFormat::Unknown) {
s->format = new_sticker->format;
}
@ -2290,6 +2337,7 @@ std::pair<int64, FileId> StickersManager::on_get_sticker_document(tl_object_ptr<
PhotoSize thumbnail;
string minithumbnail;
auto thumbnail_format = has_webp_thumbnail(document->thumbs_) ? PhotoFormat::Webp : PhotoFormat::Jpeg;
FileId premium_animation_file_id;
for (auto &thumb : document->thumbs_) {
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(),
@ -2305,9 +2353,19 @@ std::pair<int64, FileId> StickersManager::on_get_sticker_document(tl_object_ptr<
}
}
}
for (auto &thumb : document->video_thumbs_) {
if (thumb->type_ == "f") {
if (!premium_animation_file_id.is_valid()) {
premium_animation_file_id =
register_photo_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 'f'),
document_id, document->access_hash_, document->file_reference_.as_slice().str(),
DialogId(), thumb->size_, dc_id, get_sticker_format_photo_format(format));
}
}
}
create_sticker(sticker_id, std::move(minithumbnail), std::move(thumbnail), dimensions, std::move(sticker), format,
nullptr);
create_sticker(sticker_id, premium_animation_file_id, std::move(minithumbnail), std::move(thumbnail), dimensions,
std::move(sticker), format, nullptr);
return {document_id, sticker_id};
}
@ -2448,6 +2506,9 @@ vector<FileId> StickersManager::get_sticker_file_ids(FileId file_id) const {
if (sticker->m_thumbnail.file_id.is_valid()) {
result.push_back(sticker->m_thumbnail.file_id);
}
if (sticker->premium_animation_file_id.is_valid()) {
result.push_back(sticker->premium_animation_file_id);
}
return result;
}
@ -2458,7 +2519,7 @@ FileId StickersManager::dup_sticker(FileId new_id, FileId old_id) {
CHECK(new_sticker == nullptr);
new_sticker = make_unique<Sticker>(*old_sticker);
new_sticker->file_id = new_id;
// there is no reason to dup m_thumbnail
// there is no reason to dup m_thumbnail and premium_animation_file_id
new_sticker->s_thumbnail.file_id = td_->file_manager_->dup_file_id(new_sticker->s_thumbnail.file_id);
return new_id;
}
@ -2624,7 +2685,8 @@ void StickersManager::add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail) {
LOG(ERROR) << "Receive sticker thumbnail of unsupported type " << thumbnail.type;
}
void StickersManager::create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions,
void StickersManager::create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail,
PhotoSize thumbnail, Dimensions dimensions,
tl_object_ptr<telegram_api::documentAttributeSticker> sticker,
StickerFormat format, MultiPromiseActor *load_data_multipromise_ptr) {
if (format == StickerFormat::Unknown && sticker == nullptr) {
@ -2654,6 +2716,7 @@ void StickersManager::create_sticker(FileId file_id, string minithumbnail, Photo
s->minithumbnail = std::move(minithumbnail);
}
add_sticker_thumbnail(s.get(), std::move(thumbnail));
s->premium_animation_file_id = premium_animation_file_id;
if (sticker != nullptr) {
s->set_id = on_get_input_sticker_set(file_id, std::move(sticker->stickerset_), load_data_multipromise_ptr);
s->alt = std::move(sticker->alt_);
@ -2671,7 +2734,7 @@ void StickersManager::create_sticker(FileId file_id, string minithumbnail, Photo
}
}
s->format = format;
on_get_sticker(std::move(s), sticker != nullptr);
on_get_sticker(std::move(s), sticker != nullptr && load_data_multipromise_ptr == nullptr);
}
bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) const {
@ -2710,7 +2773,7 @@ bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) co
SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail) const {
BufferSlice thumbnail, int32 layer) const {
const Sticker *sticker = get_sticker(sticker_file_id);
CHECK(sticker != nullptr);
auto file_view = td_->file_manager_->get_file_view(sticker_file_id);
@ -2759,7 +2822,8 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id,
get_sticker_format_mime_type(sticker->format),
file_view,
std::move(attributes),
string()};
string(),
layer};
} else {
CHECK(!file_view.is_encrypted());
auto &remote_location = file_view.remote_location();
@ -2768,6 +2832,10 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id,
LOG(ERROR) << "Have a web sticker in " << sticker->set_id;
return {};
}
if (file_view.size() > 1000000000) {
LOG(ERROR) << "Have a sticker of size " << file_view.size() << " in " << sticker->set_id;
return {};
}
return SecretInputMedia{
nullptr, make_tl_object<secret_api::decryptedMessageMediaExternalDocument>(
remote_location.get_id(), remote_location.get_access_hash(), 0 /*date*/,
@ -3028,6 +3096,7 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
CHECK(s->is_inited);
CHECK(s->was_loaded);
s->is_loaded = true;
s->expires_at = G()->unix_time() +
(td_->auth_manager_->is_bot() ? Random::fast(10 * 60, 15 * 60) : Random::fast(30 * 60, 50 * 60));
}
@ -3037,7 +3106,7 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
auto set_id = on_get_sticker_set(std::move(set->set_), is_changed, source);
if (!set_id.is_valid()) {
return set_id;
return StickerSetId();
}
if (sticker_set_id.is_valid() && sticker_set_id != set_id) {
LOG(ERROR) << "Expected " << sticker_set_id << ", but receive " << set_id << " from " << source;
@ -3210,10 +3279,11 @@ void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &ty
td_api::object_ptr<td_api::updateReactions> StickersManager::get_update_reactions_object() const {
auto reactions = transform(reactions_.reactions_, [this](const Reaction &reaction) {
return td_api::make_object<td_api::reaction>(
reaction.reaction_, reaction.title_, reaction.is_active_, get_sticker_object(reaction.static_icon_),
get_sticker_object(reaction.appear_animation_), get_sticker_object(reaction.select_animation_),
get_sticker_object(reaction.activate_animation_), get_sticker_object(reaction.effect_animation_),
get_sticker_object(reaction.around_animation_), get_sticker_object(reaction.center_animation_));
reaction.reaction_, reaction.title_, reaction.is_active_, reaction.is_premium_,
get_sticker_object(reaction.static_icon_), get_sticker_object(reaction.appear_animation_),
get_sticker_object(reaction.select_animation_), get_sticker_object(reaction.activate_animation_),
get_sticker_object(reaction.effect_animation_), get_sticker_object(reaction.around_animation_),
get_sticker_object(reaction.center_animation_));
});
return td_api::make_object<td_api::updateReactions>(std::move(reactions));
}
@ -3251,10 +3321,10 @@ void StickersManager::load_reactions() {
}
void StickersManager::update_active_reactions() {
vector<string> active_reactions;
vector<AvailableReaction> active_reactions;
for (auto &reaction : reactions_.reactions_) {
if (reaction.is_active_) {
active_reactions.push_back(reaction.reaction_);
active_reactions.emplace_back(reaction.reaction_, reaction.is_premium_);
}
}
td_->messages_manager_->set_active_reactions(std::move(active_reactions));
@ -3282,6 +3352,7 @@ void StickersManager::on_get_available_reactions(
for (auto &available_reaction : available_reactions->reactions_) {
Reaction reaction;
reaction.is_active_ = !available_reaction->inactive_;
reaction.is_premium_ = available_reaction->premium_;
reaction.reaction_ = std::move(available_reaction->reaction_);
reaction.title_ = std::move(available_reaction->title_);
reaction.static_icon_ =
@ -3397,6 +3468,24 @@ void StickersManager::on_get_installed_sticker_sets_failed(bool is_masks, Status
fail_promises(load_installed_sticker_sets_queries_[is_masks], std::move(error));
}
std::pair<vector<FileId>, vector<FileId>> StickersManager::split_stickers_by_premium(
const vector<FileId> &sticker_ids) const {
vector<FileId> regular_sticker_ids;
vector<FileId> premium_sticker_ids;
for (const auto &sticker_id : sticker_ids) {
if (sticker_id.is_valid()) {
const Sticker *s = get_sticker(sticker_id);
CHECK(s != nullptr);
if (s->premium_animation_file_id.is_valid()) {
premium_sticker_ids.push_back(sticker_id);
} else {
regular_sticker_ids.push_back(sticker_id);
}
}
}
return {std::move(regular_sticker_ids), std::move(premium_sticker_ids)};
}
vector<FileId> StickersManager::get_stickers(string emoji, int32 limit, bool force, Promise<Unit> &&promise) {
if (limit <= 0) {
promise.set_error(Status::Error(400, "Parameter limit must be positive"));
@ -3578,15 +3667,55 @@ vector<FileId> StickersManager::get_stickers(string emoji, int32 limit, bool for
}
}
if (sorted.size() != limit_size_t) {
for (const auto &sticker_id : result) {
if (sticker_id.is_valid()) {
LOG(INFO) << "Add sticker " << sticker_id << " from installed sticker set";
vector<FileId> regular_sticker_ids;
vector<FileId> premium_sticker_ids;
std::tie(regular_sticker_ids, premium_sticker_ids) = split_stickers_by_premium(result);
if (G()->shared_config().get_option_boolean("is_premium")) {
auto normal_count = G()->shared_config().get_option_integer("stickers_normal_by_emoji_per_premium_num", 2);
if (normal_count < 0) {
normal_count = 2;
}
if (normal_count > 10) {
normal_count = 10;
}
// premium users have normal_count normal stickers per each premium
size_t normal_pos = 0;
size_t premium_pos = 0;
normal_count++;
for (size_t pos = 1; normal_pos < regular_sticker_ids.size() || premium_pos < premium_sticker_ids.size();
pos++) {
if (pos % normal_count == 0 && premium_pos < premium_sticker_ids.size()) {
auto sticker_id = premium_sticker_ids[premium_pos++];
LOG(INFO) << "Add premium sticker " << sticker_id << " from installed sticker set";
sorted.push_back(sticker_id);
} else if (normal_pos < regular_sticker_ids.size()) {
auto sticker_id = regular_sticker_ids[normal_pos++];
LOG(INFO) << "Add normal sticker " << sticker_id << " from installed sticker set";
sorted.push_back(sticker_id);
}
if (sorted.size() == limit_size_t) {
break;
}
}
} else {
for (const auto &sticker_id : regular_sticker_ids) {
LOG(INFO) << "Add normal sticker " << sticker_id << " from installed sticker set";
sorted.push_back(sticker_id);
if (sorted.size() == limit_size_t) {
break;
}
} else {
LOG(INFO) << "Skip already added sticker";
}
if (sorted.size() < limit_size_t) {
auto premium_count = G()->shared_config().get_option_integer("stickers_premium_by_emoji_num", 0);
if (premium_count > 0) {
for (const auto &sticker_id : premium_sticker_ids) {
LOG(INFO) << "Add premium sticker " << sticker_id << " from installed sticker set";
sorted.push_back(sticker_id);
if (sorted.size() == limit_size_t || --premium_count == 0) {
break;
}
}
}
}
}
}
@ -3999,9 +4128,10 @@ void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks
LOG(ERROR) << "Can't load installed sticker set list: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
return reload_installed_sticker_sets(is_masks, true);
}
CHECK(!log_event.is_premium_);
vector<StickerSetId> sets_to_load;
for (auto sticker_set_id : log_event.sticker_set_ids) {
for (auto sticker_set_id : log_event.sticker_set_ids_) {
StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
if (!sticker_set->is_inited) {
@ -4013,7 +4143,7 @@ void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks
load_sticker_sets_without_stickers(
std::move(sets_to_load),
PromiseCreator::lambda(
[is_masks, sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
[is_masks, sticker_set_ids = std::move(log_event.sticker_set_ids_)](Result<> result) mutable {
if (result.is_ok()) {
send_closure(G()->stickers_manager(), &StickersManager::on_load_installed_sticker_sets_finished, is_masks,
std::move(sticker_set_ids), true);
@ -4587,6 +4717,37 @@ void StickersManager::get_animated_emoji(string emoji, bool is_recursive,
get_animated_emoji_sound_file_id(emoji)));
}
void StickersManager::get_all_animated_emojis(bool is_recursive,
Promise<td_api::object_ptr<td_api::emojis>> &&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(td_api::make_object<td_api::emojis>());
}
pending_get_animated_emoji_queries_.push_back(PromiseCreator::lambda(
[actor_id = actor_id(this), promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &StickersManager::get_all_animated_emojis, true, std::move(promise));
}
}));
load_special_sticker_set(special_sticker_set);
return;
}
auto emojis = transform(sticker_set->sticker_ids, [&](FileId sticker_id) {
auto s = get_sticker(sticker_id);
CHECK(s != nullptr);
return s->alt;
});
promise.set_value(td_api::make_object<td_api::emojis>(std::move(emojis)));
}
void StickersManager::get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::sticker>> &&promise) {
if (disable_animated_emojis_ || td_->auth_manager_->is_bot()) {
@ -5087,8 +5248,8 @@ void StickersManager::on_get_archived_sticker_sets(
send_update_installed_sticker_sets();
}
std::pair<int32, vector<StickerSetId>> StickersManager::get_featured_sticker_sets(int32 offset, int32 limit,
Promise<Unit> &&promise) {
td_api::object_ptr<td_api::trendingStickerSets> StickersManager::get_featured_sticker_sets(int32 offset, int32 limit,
Promise<Unit> &&promise) {
if (offset < 0) {
promise.set_error(Status::Error(400, "Parameter offset must be non-negative"));
return {};
@ -5109,20 +5270,20 @@ std::pair<int32, vector<StickerSetId>> StickersManager::get_featured_sticker_set
reload_featured_sticker_sets(false);
auto set_count = static_cast<int32>(featured_sticker_set_ids_.size());
auto total_count = set_count + (old_featured_sticker_set_count_ == -1 ? 1 : old_featured_sticker_set_count_);
if (offset < set_count) {
if (limit > set_count - offset) {
limit = set_count - offset;
}
promise.set_value(Unit());
auto begin = featured_sticker_set_ids_.begin() + offset;
return {total_count, {begin, begin + limit}};
return get_trending_sticker_sets_object({begin, begin + limit});
}
if (offset == set_count && are_old_featured_sticker_sets_invalidated_) {
invalidate_old_featured_sticker_sets();
}
auto total_count = set_count + (old_featured_sticker_set_count_ == -1 ? 1 : old_featured_sticker_set_count_);
if (offset < total_count || old_featured_sticker_set_count_ == -1) {
offset -= set_count;
set_count = static_cast<int32>(old_featured_sticker_set_ids_.size());
@ -5132,7 +5293,7 @@ std::pair<int32, vector<StickerSetId>> StickersManager::get_featured_sticker_set
}
promise.set_value(Unit());
auto begin = old_featured_sticker_set_ids_.begin() + offset;
return {total_count, {begin, begin + limit}};
return get_trending_sticker_sets_object({begin, begin + limit});
}
if (offset > set_count) {
promise.set_error(
@ -5145,7 +5306,7 @@ std::pair<int32, vector<StickerSetId>> StickersManager::get_featured_sticker_set
}
promise.set_value(Unit());
return {total_count, vector<StickerSetId>()};
return get_trending_sticker_sets_object({});
}
void StickersManager::on_old_featured_sticker_sets_invalidated() {
@ -5230,6 +5391,14 @@ void StickersManager::on_get_featured_sticker_sets(
CHECK(constructor_id == telegram_api::messages_featuredStickers::ID);
auto featured_stickers = move_tl_object_as<telegram_api::messages_featuredStickers>(sticker_sets_ptr);
if (featured_stickers->premium_ != are_featured_sticker_sets_premium_) {
on_old_featured_sticker_sets_invalidated();
if (offset >= 0) {
featured_stickers->premium_ = are_featured_sticker_sets_premium_;
reload_featured_sticker_sets(true);
}
}
if (offset >= 0 && generation == old_featured_sticker_set_generation_) {
set_old_featured_sticker_set_count(featured_stickers->count_);
// the count will be fixed in on_load_old_featured_sticker_sets_finished
@ -5258,7 +5427,7 @@ void StickersManager::on_get_featured_sticker_sets(
set->is_changed = true;
}
update_sticker_set(set, "on_get_archived_sticker_sets 2");
update_sticker_set(set, "on_get_featured_sticker_sets 2");
featured_sticker_set_ids.push_back(set_id);
}
@ -5270,7 +5439,7 @@ void StickersManager::on_get_featured_sticker_sets(
if (G()->parameters().use_file_db && !G()->close_flag()) {
LOG(INFO) << "Save old trending sticker sets to database with offset " << old_featured_sticker_set_ids_.size();
CHECK(old_featured_sticker_set_ids_.size() % OLD_FEATURED_STICKER_SET_SLICE_SIZE == 0);
StickerSetListLogEvent log_event(featured_sticker_set_ids);
StickerSetListLogEvent log_event(featured_sticker_set_ids, false);
G()->td_db()->get_sqlite_pmc()->set(PSTRING() << "sssoldfeatured" << old_featured_sticker_set_ids_.size(),
log_event_store(log_event).as_slice().str(), Auto());
}
@ -5281,7 +5450,7 @@ void StickersManager::on_get_featured_sticker_sets(
return;
}
on_load_featured_sticker_sets_finished(std::move(featured_sticker_set_ids));
on_load_featured_sticker_sets_finished(std::move(featured_sticker_set_ids), featured_stickers->premium_);
LOG_IF(ERROR, featured_sticker_sets_hash_ != featured_stickers->hash_) << "Trending sticker sets hash mismatch";
@ -5290,7 +5459,7 @@ void StickersManager::on_get_featured_sticker_sets(
}
LOG(INFO) << "Save trending sticker sets to database";
StickerSetListLogEvent log_event(featured_sticker_set_ids_);
StickerSetListLogEvent log_event(featured_sticker_set_ids_, are_featured_sticker_sets_premium_);
G()->td_db()->get_sqlite_pmc()->set("sssfeatured", log_event_store(log_event).as_slice().str(), Auto());
}
@ -5353,7 +5522,7 @@ void StickersManager::on_load_featured_sticker_sets_from_database(string value)
}
vector<StickerSetId> sets_to_load;
for (auto sticker_set_id : log_event.sticker_set_ids) {
for (auto sticker_set_id : log_event.sticker_set_ids_) {
StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
if (!sticker_set->is_inited) {
@ -5362,23 +5531,25 @@ void StickersManager::on_load_featured_sticker_sets_from_database(string value)
}
load_sticker_sets_without_stickers(
std::move(sets_to_load),
PromiseCreator::lambda([sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
std::move(sets_to_load), PromiseCreator::lambda([sticker_set_ids = std::move(log_event.sticker_set_ids_),
is_premium = log_event.is_premium_](Result<> result) mutable {
if (result.is_ok()) {
send_closure(G()->stickers_manager(), &StickersManager::on_load_featured_sticker_sets_finished,
std::move(sticker_set_ids));
std::move(sticker_set_ids), is_premium);
} else {
send_closure(G()->stickers_manager(), &StickersManager::reload_featured_sticker_sets, true);
}
}));
}
void StickersManager::on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids) {
void StickersManager::on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids,
bool is_premium) {
if (!featured_sticker_set_ids_.empty() && featured_sticker_set_ids != featured_sticker_set_ids_) {
// always invalidate old featured sticker sets when current featured sticker sets change
on_old_featured_sticker_sets_invalidated();
}
featured_sticker_set_ids_ = std::move(featured_sticker_set_ids);
are_featured_sticker_sets_premium_ = is_premium;
are_featured_sticker_sets_loaded_ = true;
need_update_featured_sticker_sets_ = true;
send_update_featured_sticker_sets();
@ -5429,9 +5600,10 @@ void StickersManager::on_load_old_featured_sticker_sets_from_database(uint32 gen
LOG(ERROR) << "Can't load old trending sticker set list: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
return reload_old_featured_sticker_sets();
}
CHECK(!log_event.is_premium_);
vector<StickerSetId> sets_to_load;
for (auto sticker_set_id : log_event.sticker_set_ids) {
for (auto sticker_set_id : log_event.sticker_set_ids_) {
StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
if (!sticker_set->is_inited) {
@ -5442,7 +5614,7 @@ void StickersManager::on_load_old_featured_sticker_sets_from_database(uint32 gen
load_sticker_sets_without_stickers(
std::move(sets_to_load),
PromiseCreator::lambda(
[generation, sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
[generation, sticker_set_ids = std::move(log_event.sticker_set_ids_)](Result<> result) mutable {
if (result.is_ok()) {
send_closure(G()->stickers_manager(), &StickersManager::on_load_old_featured_sticker_sets_finished,
generation, std::move(sticker_set_ids));
@ -5647,7 +5819,8 @@ Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_i
if (format == StickerFormat::Tgs) {
int32 width = for_thumbnail ? 100 : 512;
create_sticker(file_id, string(), PhotoSize(), get_dimensions(width, width, nullptr), nullptr, format, nullptr);
create_sticker(file_id, FileId(), string(), PhotoSize(), get_dimensions(width, width, "prepare_input_file"),
nullptr, format, nullptr);
} else if (format == StickerFormat::Webm) {
td_->documents_manager_->create_document(file_id, string(), PhotoSize(), "sticker.webm", "video/webm", false);
} else {
@ -6351,7 +6524,7 @@ void StickersManager::send_update_installed_sticker_sets(bool from_database) {
if (G()->parameters().use_file_db && !from_database && !G()->close_flag()) {
LOG(INFO) << "Save installed " << (is_masks ? "mask " : "") << "sticker sets to database";
StickerSetListLogEvent log_event(installed_sticker_set_ids_[is_masks]);
StickerSetListLogEvent log_event(installed_sticker_set_ids_[is_masks], false);
G()->td_db()->get_sqlite_pmc()->set(is_masks ? "sss1" : "sss0", log_event_store(log_event).as_slice().str(),
Auto());
}
@ -6360,11 +6533,29 @@ void StickersManager::send_update_installed_sticker_sets(bool from_database) {
}
}
td_api::object_ptr<td_api::updateTrendingStickerSets> StickersManager::get_update_trending_sticker_sets_object() const {
td_api::object_ptr<td_api::trendingStickerSets> StickersManager::get_trending_sticker_sets_object(
const vector<StickerSetId> &sticker_set_ids) const {
auto total_count = static_cast<int32>(featured_sticker_set_ids_.size()) +
(old_featured_sticker_set_count_ == -1 ? 1 : old_featured_sticker_set_count_);
vector<tl_object_ptr<td_api::stickerSetInfo>> result;
result.reserve(sticker_set_ids.size());
for (auto sticker_set_id : sticker_set_ids) {
auto sticker_set_info = get_sticker_set_info_object(sticker_set_id, 5, are_featured_sticker_sets_premium_);
if (sticker_set_info->size_ != 0) {
result.push_back(std::move(sticker_set_info));
}
}
auto result_size = narrow_cast<int32>(result.size());
CHECK(total_count >= result_size);
return td_api::make_object<td_api::trendingStickerSets>(total_count, std::move(result),
are_featured_sticker_sets_premium_);
}
td_api::object_ptr<td_api::updateTrendingStickerSets> StickersManager::get_update_trending_sticker_sets_object() const {
return td_api::make_object<td_api::updateTrendingStickerSets>(
get_sticker_sets_object(total_count, featured_sticker_set_ids_, 5));
get_trending_sticker_sets_object(featured_sticker_set_ids_));
}
void StickersManager::send_update_featured_sticker_sets() {

View File

@ -85,6 +85,8 @@ class StickersManager final : public Actor {
void get_animated_emoji(string emoji, bool is_recursive,
Promise<td_api::object_ptr<td_api::animatedEmoji>> &&promise);
void get_all_animated_emojis(bool is_recursive, Promise<td_api::object_ptr<td_api::emojis>> &&promise);
void get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::sticker>> &&promise);
@ -96,9 +98,9 @@ class StickersManager final : public Actor {
bool is_active_reaction(const string &reaction) const;
void create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions,
tl_object_ptr<telegram_api::documentAttributeSticker> sticker, StickerFormat sticker_format,
MultiPromiseActor *load_data_multipromise_ptr);
void create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail, PhotoSize thumbnail,
Dimensions dimensions, tl_object_ptr<telegram_api::documentAttributeSticker> sticker,
StickerFormat sticker_format, MultiPromiseActor *load_data_multipromise_ptr);
bool has_input_media(FileId sticker_file_id, bool is_secret) const;
@ -109,7 +111,7 @@ class StickersManager final : public Actor {
SecretInputMedia get_secret_input_media(FileId sticker_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail) const;
BufferSlice thumbnail, int32 layer) const;
vector<FileId> get_stickers(string emoji, int32 limit, bool force, Promise<Unit> &&promise);
@ -187,7 +189,8 @@ class StickersManager final : public Actor {
vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets,
int32 total_count);
std::pair<int32, vector<StickerSetId>> get_featured_sticker_sets(int32 offset, int32 limit, Promise<Unit> &&promise);
td_api::object_ptr<td_api::trendingStickerSets> get_featured_sticker_sets(int32 offset, int32 limit,
Promise<Unit> &&promise);
void on_get_featured_sticker_sets(int32 offset, int32 limit, uint32 generation,
tl_object_ptr<telegram_api::messages_FeaturedStickers> &&sticker_sets_ptr);
@ -353,6 +356,7 @@ class StickersManager final : public Actor {
string minithumbnail;
PhotoSize s_thumbnail;
PhotoSize m_thumbnail;
FileId premium_animation_file_id;
FileId file_id;
StickerFormat format = StickerFormat::Unknown;
bool is_mask = false;
@ -450,6 +454,7 @@ class StickersManager final : public Actor {
string reaction_;
string title_;
bool is_active_ = false;
bool is_premium_ = false;
FileId static_icon_;
FileId appear_animation_;
FileId select_animation_;
@ -495,8 +500,8 @@ class StickersManager final : public Actor {
static tl_object_ptr<td_api::MaskPoint> get_mask_point_object(int32 point);
tl_object_ptr<td_api::stickerSetInfo> get_sticker_set_info_object(StickerSetId sticker_set_id,
size_t covers_limit) const;
tl_object_ptr<td_api::stickerSetInfo> get_sticker_set_info_object(StickerSetId sticker_set_id, size_t covers_limit,
bool prefer_premium) const;
Sticker *get_sticker(FileId file_id);
const Sticker *get_sticker(FileId file_id) const;
@ -572,7 +577,7 @@ class StickersManager final : public Actor {
void on_load_featured_sticker_sets_from_database(string value);
void on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids);
void on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids, bool is_premium);
void on_load_old_featured_sticker_sets_from_database(uint32 generation, string value);
@ -600,6 +605,9 @@ class StickersManager final : public Actor {
// any change of old_featured_sticker_set_ids_ size
void fix_old_featured_sticker_set_count();
td_api::object_ptr<td_api::trendingStickerSets> get_trending_sticker_sets_object(
const vector<StickerSetId> &sticker_set_ids) const;
td_api::object_ptr<td_api::updateTrendingStickerSets> get_update_trending_sticker_sets_object() const;
void send_update_featured_sticker_sets();
@ -634,6 +642,8 @@ class StickersManager final : public Actor {
template <class ParserT>
void parse_sticker_set(StickerSet *sticker_set, ParserT &parser);
std::pair<vector<FileId>, vector<FileId>> split_stickers_by_premium(const vector<FileId> &sticker_ids) const;
Result<std::tuple<FileId, bool, bool, StickerFormat>> prepare_input_file(
const tl_object_ptr<td_api::InputFile> &input_file, StickerFormat format, bool for_thumbnail);
@ -798,6 +808,7 @@ class StickersManager final : public Actor {
bool are_recent_stickers_loaded_[2] = {false, false};
bool are_favorite_stickers_loaded_ = false;
bool are_featured_sticker_sets_premium_ = false;
bool are_old_featured_sticker_sets_invalidated_ = false;
vector<Promise<Unit>> load_installed_sticker_sets_queries_[2];

View File

@ -33,6 +33,7 @@ void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT
bool has_minithumbnail = !sticker->minithumbnail.empty();
bool is_tgs = sticker->format == StickerFormat::Tgs;
bool is_webm = sticker->format == StickerFormat::Webm;
bool has_premium_animation = sticker->premium_animation_file_id.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(sticker->is_mask);
STORE_FLAG(has_sticker_set_access_hash);
@ -40,6 +41,7 @@ void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT
STORE_FLAG(is_tgs);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(is_webm);
STORE_FLAG(has_premium_animation);
END_STORE_FLAGS();
if (!in_sticker_set) {
store(sticker->set_id.get(), storer);
@ -63,6 +65,9 @@ void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT
if (has_minithumbnail) {
store(sticker->minithumbnail, storer);
}
if (has_premium_animation) {
store(sticker->premium_animation_file_id, storer);
}
}
template <class ParserT>
@ -77,6 +82,7 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
bool has_minithumbnail;
bool is_tgs;
bool is_webm;
bool has_premium_animation;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(sticker->is_mask);
PARSE_FLAG(has_sticker_set_access_hash);
@ -84,6 +90,7 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
PARSE_FLAG(is_tgs);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(is_webm);
PARSE_FLAG(has_premium_animation);
END_PARSE_FLAGS();
if (is_webm) {
sticker->format = StickerFormat::Webm;
@ -133,6 +140,9 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
if (has_minithumbnail) {
parse(sticker->minithumbnail, parser);
}
if (has_premium_animation) {
parse(sticker->premium_animation_file_id, parser);
}
if (parser.get_error() != nullptr || !sticker->file_id.is_valid()) {
return FileId();
}
@ -396,6 +406,7 @@ void StickersManager::Reaction::store(StorerT &storer) const {
STORE_FLAG(is_active_);
STORE_FLAG(has_around_animation);
STORE_FLAG(has_center_animation);
STORE_FLAG(is_premium_);
END_STORE_FLAGS();
td::store(reaction_, storer);
td::store(title_, storer);
@ -421,6 +432,7 @@ void StickersManager::Reaction::parse(ParserT &parser) {
PARSE_FLAG(is_active_);
PARSE_FLAG(has_around_animation);
PARSE_FLAG(has_center_animation);
PARSE_FLAG(is_premium_);
END_PARSE_FLAGS();
td::parse(reaction_, parser);
td::parse(title_, parser);

View File

@ -95,6 +95,7 @@
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/PollManager.h"
#include "td/telegram/Premium.h"
#include "td/telegram/PrivacyManager.h"
#include "td/telegram/PublicDialogType.h"
#include "td/telegram/ReportReason.h"
@ -143,9 +144,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"
#include "td/utils/port/uname.h"
#include "td/utils/Random.h"
@ -2072,16 +2071,16 @@ class GetArchivedStickerSetsRequest final : public RequestActor<> {
};
class GetTrendingStickerSetsRequest final : public RequestActor<> {
std::pair<int32, vector<StickerSetId>> sticker_set_ids_;
td_api::object_ptr<td_api::trendingStickerSets> result_;
int32 offset_;
int32 limit_;
void do_run(Promise<Unit> &&promise) final {
sticker_set_ids_ = td_->stickers_manager_->get_featured_sticker_sets(offset_, limit_, std::move(promise));
result_ = td_->stickers_manager_->get_featured_sticker_sets(offset_, limit_, std::move(promise));
}
void do_send_result() final {
send_result(td_->stickers_manager_->get_sticker_sets_object(sticker_set_ids_.first, sticker_set_ids_.second, 5));
send_result(std::move(result_));
}
public:
@ -3047,6 +3046,10 @@ void Td::request(uint64 id, tl_object_ptr<td_api::Function> function) {
}
void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
if (set_parameters_request_id_ > 0) {
pending_set_parameters_requests_.emplace_back(id, std::move(function));
return;
}
if (init_request_id_ > 0) {
pending_init_requests_.emplace_back(id, std::move(function));
return;
@ -3078,9 +3081,20 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
switch (state_) {
case State::WaitParameters: {
switch (function_id) {
case td_api::setTdlibParameters::ID:
return answer_ok_query(
id, set_parameters(std::move(move_tl_object_as<td_api::setTdlibParameters>(function)->parameters_)));
case td_api::setTdlibParameters::ID: {
auto status = set_parameters(std::move(move_tl_object_as<td_api::setTdlibParameters>(function)->parameters_));
if (status.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, std::move(status));
}
VLOG(td_init) << "Begin to check parameters";
set_parameters_request_id_ = id;
auto promise =
PromiseCreator::lambda([actor_id = actor_id(this)](Result<TdDb::CheckedParameters> r_checked_parameters) {
send_closure(actor_id, &Td::on_parameters_checked, std::move(r_checked_parameters));
});
return TdDb::check_parameters(get_database_scheduler_id(), parameters_, std::move(promise));
}
default:
if (is_preinitialization_request(function_id)) {
break;
@ -3577,6 +3591,8 @@ void Td::clear() {
LOG(DEBUG) << "TopDialogManager actor was cleared" << timer;
updates_manager_actor_.reset();
LOG(DEBUG) << "UpdatesManager actor was cleared" << timer;
voice_notes_manager_actor_.reset();
LOG(DEBUG) << "VoiceNotesManager actor was cleared" << timer;
web_pages_manager_actor_.reset();
LOG(DEBUG) << "WebPagesManager actor was cleared" << timer;
}
@ -3665,6 +3681,51 @@ void Td::complete_pending_preauthentication_requests(const T &func) {
}
}
int32 Td::get_database_scheduler_id() {
auto current_scheduler_id = Scheduler::instance()->sched_id();
auto scheduler_count = Scheduler::instance()->sched_count();
return min(current_scheduler_id + 1, scheduler_count - 1);
}
void Td::on_parameters_checked(Result<TdDb::CheckedParameters> r_checked_parameters) {
CHECK(set_parameters_request_id_ != 0);
if (r_checked_parameters.is_error()) {
send_closure(actor_id(this), &Td::send_error, set_parameters_request_id_,
Status::Error(400, r_checked_parameters.error().message()));
return finish_set_parameters();
}
auto checked_parameters = r_checked_parameters.move_as_ok();
parameters_.database_directory = std::move(checked_parameters.database_directory);
parameters_.files_directory = std::move(checked_parameters.files_directory);
is_database_encrypted_ = checked_parameters.is_database_encrypted;
state_ = State::Decrypt;
VLOG(td_init) << "Send authorizationStateWaitEncryptionKey";
send_closure(actor_id(this), &Td::send_update,
td_api::make_object<td_api::updateAuthorizationState>(
td_api::make_object<td_api::authorizationStateWaitEncryptionKey>(is_database_encrypted_)));
VLOG(td_init) << "Finish set parameters";
send_closure(actor_id(this), &Td::send_result, set_parameters_request_id_, td_api::make_object<td_api::ok>());
return finish_set_parameters();
}
void Td::finish_set_parameters() {
CHECK(set_parameters_request_id_ != 0);
set_parameters_request_id_ = 0;
if (pending_set_parameters_requests_.empty()) {
return;
}
VLOG(td_init) << "Continue to execute " << pending_set_parameters_requests_.size() << " pending requests";
auto requests = std::move(pending_set_parameters_requests_);
for (auto &request : requests) {
run_request(request.first, std::move(request.second));
}
CHECK(pending_set_parameters_requests_.size() < requests.size());
}
void Td::start_init(uint64 id, string &&key) {
VLOG(td_init) << "Begin to init database";
init_request_id_ = id;
@ -3672,9 +3733,7 @@ void Td::start_init(uint64 id, string &&key) {
auto promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<TdDb::OpenedDatabase> r_opened_database) {
send_closure(actor_id, &Td::init, std::move(r_opened_database));
});
auto current_scheduler_id = Scheduler::instance()->sched_id();
auto scheduler_count = Scheduler::instance()->sched_count();
TdDb::open(min(current_scheduler_id + 1, scheduler_count - 1), parameters_, as_db_key(std::move(key), parameters_.use_custom_db_format),
TdDb::open(get_database_scheduler_id(), parameters_, as_db_key(std::move(key), parameters_.use_custom_db_format),
std::move(promise));
}
@ -3735,11 +3794,7 @@ void Td::init(Result<TdDb::OpenedDatabase> r_opened_database) {
G()->set_my_id(G()->shared_config().get_option_integer("my_id"));
auto current_scheduler_id = Scheduler::instance()->sched_id();
auto scheduler_count = Scheduler::instance()->sched_count();
storage_manager_ = create_actor<StorageManager>("StorageManager", create_reference(),
min(current_scheduler_id + 2, scheduler_count - 1));
storage_manager_ = create_actor<StorageManager>("StorageManager", create_reference(), G()->get_gc_scheduler_id());
G()->set_storage_manager(storage_manager_.get());
VLOG(td_init) << "Send binlog events";
@ -4000,7 +4055,6 @@ void Td::init_managers() {
documents_manager_ = make_unique<DocumentsManager>(this);
video_notes_manager_ = make_unique<VideoNotesManager>(this);
videos_manager_ = make_unique<VideosManager>(this);
voice_notes_manager_ = make_unique<VoiceNotesManager>(this);
animations_manager_ = make_unique<AnimationsManager>(this, create_reference());
animations_manager_actor_ = register_actor("AnimationsManager", animations_manager_.get());
@ -4059,6 +4113,8 @@ void Td::init_managers() {
updates_manager_ = make_unique<UpdatesManager>(this, create_reference());
updates_manager_actor_ = register_actor("UpdatesManager", updates_manager_.get());
G()->set_updates_manager(updates_manager_actor_.get());
voice_notes_manager_ = make_unique<VoiceNotesManager>(this, create_reference());
voice_notes_manager_actor_ = register_actor("VoiceNotesManager", voice_notes_manager_.get());
web_pages_manager_ = make_unique<WebPagesManager>(this, create_reference());
web_pages_manager_actor_ = register_actor("WebPagesManager", web_pages_manager_.get());
G()->set_web_pages_manager(web_pages_manager_actor_.get());
@ -4234,35 +4290,6 @@ Status Td::fix_parameters(TdParameters &parameters) {
VLOG(td_init) << "Invalid api_hash";
return Status::Error(400, "Valid api_hash must be provided. Can be obtained at https://my.telegram.org");
}
auto prepare_dir = [](string dir) -> Result<string> {
CHECK(!dir.empty());
if (dir.back() != TD_DIR_SLASH) {
dir += TD_DIR_SLASH;
}
TRY_STATUS(mkpath(dir, 0750));
TRY_RESULT(real_dir, realpath(dir, true));
if (dir.back() != TD_DIR_SLASH) {
dir += TD_DIR_SLASH;
}
return real_dir;
};
auto r_database_directory = prepare_dir(parameters.database_directory);
if (r_database_directory.is_error()) {
VLOG(td_init) << "Invalid database_directory";
return Status::Error(400, PSLICE() << "Can't init database in the directory \"" << parameters.database_directory
<< "\": " << r_database_directory.error());
}
parameters.database_directory = r_database_directory.move_as_ok();
auto r_files_directory = prepare_dir(parameters.files_directory);
if (r_files_directory.is_error()) {
VLOG(td_init) << "Invalid files_directory";
return Status::Error(400, PSLICE() << "Can't init files directory \"" << parameters.files_directory
<< "\": " << r_files_directory.error());
}
parameters.files_directory = r_files_directory.move_as_ok();
return Status::OK();
}
@ -4273,8 +4300,8 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
return Status::Error(400, "Parameters aren't specified");
}
if (!clean_input_string(parameters->api_hash_) && !clean_input_string(parameters->system_language_code_) &&
!clean_input_string(parameters->device_model_) && !clean_input_string(parameters->system_version_) &&
if (!clean_input_string(parameters->api_hash_) || !clean_input_string(parameters->system_language_code_) ||
!clean_input_string(parameters->device_model_) || !clean_input_string(parameters->system_version_) ||
!clean_input_string(parameters->application_version_)) {
VLOG(td_init) << "Wrong string encoding";
return Status::Error(400, "Strings must be encoded in UTF-8");
@ -4293,11 +4320,7 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
parameters_.use_chat_info_db = parameters->use_chat_info_database_;
parameters_.use_message_db = parameters->use_message_database_;
VLOG(td_init) << "Fix parameters...";
TRY_STATUS(fix_parameters(parameters_));
VLOG(td_init) << "Check binlog encryption...";
TRY_RESULT(encryption_info, TdDb::check_encryption(parameters_));
is_database_encrypted_ = encryption_info.is_encrypted;
VLOG(td_init) << "Create MtprotoHeader::Options";
options_.api_id = parameters->api_id_;
@ -4328,12 +4351,6 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
options_.is_emulator = false;
options_.proxy = Proxy();
state_ = State::Decrypt;
VLOG(td_init) << "Send authorizationStateWaitEncryptionKey";
send_closure(actor_id(this), &Td::send_update,
td_api::make_object<td_api::updateAuthorizationState>(
td_api::make_object<td_api::authorizationStateWaitEncryptionKey>(is_database_encrypted_)));
VLOG(td_init) << "Finish set parameters";
return Status::OK();
}
@ -4822,6 +4839,20 @@ void Td::on_request(uint64 id, td_api::translateText &request) {
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::recognizeSpeech &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
voice_notes_manager_->recognize_speech({DialogId(request.chat_id_), MessageId(request.message_id_)},
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::rateSpeechRecognition &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
voice_notes_manager_->rate_speech_recognition({DialogId(request.chat_id_), MessageId(request.message_id_)},
request.is_good_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getFile &request) {
send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(FileId(request.file_id_, 0)));
}
@ -5340,8 +5371,10 @@ void Td::on_request(uint64 id, const td_api::getMessageAvailableReactions &reque
if (r_reactions.is_error()) {
send_closure(actor_id(this), &Td::send_error, id, r_reactions.move_as_error());
} else {
auto reactions =
transform(r_reactions.ok(), [](auto &reaction) { return reaction.get_available_reaction_object(); });
send_closure(actor_id(this), &Td::send_result, id,
td_api::make_object<td_api::availableReactions>(r_reactions.move_as_ok()));
td_api::make_object<td_api::availableReactions>(std::move(reactions)));
}
}
@ -6094,7 +6127,8 @@ void Td::on_request(uint64 id, const td_api::reorderChatFilters &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
messages_manager_->reorder_dialog_filters(
transform(request.chat_filter_ids_, [](int32 id) { return DialogFilterId(id); }), std::move(promise));
transform(request.chat_filter_ids_, [](int32 id) { return DialogFilterId(id); }),
request.main_chat_list_position_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::setChatTitle &request) {
@ -6529,7 +6563,7 @@ void Td::on_file_download_finished(FileId file_id) {
auto file_size = file_object->size_;
auto limit = it->second.limit;
if (limit == 0) {
limit = std::numeric_limits<int32>::max();
limit = std::numeric_limits<int64>::max();
}
if (file_object->local_->is_downloading_completed_ ||
(download_offset <= it->second.offset && download_offset + downloaded_size >= it->second.offset &&
@ -6552,7 +6586,7 @@ void Td::on_request(uint64 id, const td_api::getFileDownloadedPrefixSize &reques
return send_closure(actor_id(this), &Td::send_error, id, Status::Error(400, "Unknown file ID"));
}
send_closure(actor_id(this), &Td::send_result, id,
td_api::make_object<td_api::count>(narrow_cast<int32>(file_view.downloaded_prefix(request.offset_))));
td_api::make_object<td_api::fileDownloadedPrefixSize>(file_view.downloaded_prefix(request.offset_)));
}
void Td::on_request(uint64 id, const td_api::cancelDownloadFile &request) {
@ -6906,6 +6940,20 @@ void Td::on_request(uint64 id, const td_api::toggleSupergroupSignMessages &reque
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::toggleSupergroupJoinToSendMessages &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->toggle_channel_join_to_send(ChannelId(request.supergroup_id_), request.join_to_send_messages_,
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::toggleSupergroupJoinByRequest &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->toggle_channel_join_request(ChannelId(request.supergroup_id_), request.join_by_request_,
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::toggleSupergroupIsAllHistoryAvailable &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
@ -7136,6 +7184,12 @@ void Td::on_request(uint64 id, td_api::getAnimatedEmoji &request) {
stickers_manager_->get_animated_emoji(std::move(request.emoji_), false, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getAllAnimatedEmojis &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
stickers_manager_->get_all_animated_emojis(false, std::move(promise));
}
void Td::on_request(uint64 id, td_api::getEmojiSuggestionsUrl &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.language_code_);
@ -7517,18 +7571,17 @@ void Td::on_request(uint64 id, td_api::getBankCardInfo &request) {
get_bank_card_info(this, request.bank_card_number_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPaymentForm &request) {
void Td::on_request(uint64 id, td_api::getPaymentForm &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_payment_form(this, {DialogId(request.chat_id_), MessageId(request.message_id_)}, request.theme_,
std::move(promise));
get_payment_form(this, std::move(request.input_invoice_), request.theme_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::validateOrderInfo &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
validate_order_info(this, {DialogId(request.chat_id_), MessageId(request.message_id_)},
std::move(request.order_info_), request.allow_save_, std::move(promise));
validate_order_info(this, std::move(request.input_invoice_), std::move(request.order_info_), request.allow_save_,
std::move(promise));
}
void Td::on_request(uint64 id, td_api::sendPaymentForm &request) {
@ -7536,9 +7589,8 @@ void Td::on_request(uint64 id, td_api::sendPaymentForm &request) {
CLEAN_INPUT_STRING(request.order_info_id_);
CLEAN_INPUT_STRING(request.shipping_option_id_);
CREATE_REQUEST_PROMISE();
send_payment_form(this, {DialogId(request.chat_id_), MessageId(request.message_id_)}, request.payment_form_id_,
request.order_info_id_, request.shipping_option_id_, request.credentials_, request.tip_amount_,
std::move(promise));
send_payment_form(this, std::move(request.input_invoice_), request.payment_form_id_, request.order_info_id_,
request.shipping_option_id_, request.credentials_, request.tip_amount_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPaymentReceipt &request) {
@ -7565,6 +7617,19 @@ void Td::on_request(uint64 id, const td_api::deleteSavedCredentials &request) {
delete_saved_credentials(this, std::move(promise));
}
void Td::on_request(uint64 id, td_api::createInvoiceLink &request) {
CHECK_IS_BOT();
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<string> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(td_api::make_object<td_api::httpUrl>(result.move_as_ok()));
}
});
export_invoice(this, std::move(request.invoice_), std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::getPassportElement &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.password_);
@ -7831,6 +7896,41 @@ void Td::on_request(uint64 id, td_api::removeRecentHashtag &request) {
send_closure(hashtag_hints_, &HashtagHints::remove_hashtag, std::move(request.hashtag_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumLimit &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_premium_limit(request.limit_type_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumFeatures &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_premium_features(this, request.source_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumStickers &request) {
CHECK_IS_USER();
CREATE_REQUEST(SearchStickersRequest, "⭐️⭐️", 100);
}
void Td::on_request(uint64 id, const td_api::viewPremiumFeature &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
view_premium_feature(this, request.feature_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::clickPremiumSubscriptionButton &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
click_premium_subscription_button(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumState &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_premium_state(this, std::move(promise));
}
void Td::on_request(uint64 id, td_api::acceptTermsOfService &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.terms_of_service_id_);

View File

@ -109,7 +109,7 @@ class Td final : public Actor {
Td &operator=(Td &&) = delete;
~Td() final;
static constexpr const char *TDLIB_VERSION = "1.8.3";
static constexpr const char *TDLIB_VERSION = "1.8.4";
struct Options {
std::shared_ptr<NetQueryStats> net_query_stats;
@ -144,7 +144,6 @@ class Td final : public Actor {
unique_ptr<DocumentsManager> documents_manager_;
unique_ptr<VideoNotesManager> video_notes_manager_;
unique_ptr<VideosManager> videos_manager_;
unique_ptr<VoiceNotesManager> voice_notes_manager_;
unique_ptr<AnimationsManager> animations_manager_;
ActorOwn<AnimationsManager> animations_manager_actor_;
@ -192,6 +191,8 @@ class Td final : public Actor {
ActorOwn<TopDialogManager> top_dialog_manager_actor_;
unique_ptr<UpdatesManager> updates_manager_;
ActorOwn<UpdatesManager> updates_manager_actor_;
unique_ptr<VoiceNotesManager> voice_notes_manager_;
ActorOwn<VoiceNotesManager> voice_notes_manager_actor_;
unique_ptr<MemoryManager> memory_manager_;
ActorOwn<MemoryManager> memory_manager_actor_;
unique_ptr<WebPagesManager> web_pages_manager_;
@ -304,6 +305,7 @@ class Td final : public Actor {
enum class State : int32 { WaitParameters, Decrypt, Run, Close } state_ = State::WaitParameters;
uint64 init_request_id_ = 0;
uint64 set_parameters_request_id_ = 0;
bool is_database_encrypted_ = false;
FlatHashMap<uint64, std::shared_ptr<ResultHandler>> result_handlers_;
@ -321,14 +323,15 @@ class Td final : public Actor {
TermsOfService pending_terms_of_service_;
struct DownloadInfo {
int32 offset = -1;
int32 limit = -1;
int64 offset = -1;
int64 limit = -1;
vector<uint64> request_ids;
};
FlatHashMap<FileId, DownloadInfo, FileIdHash> pending_file_downloads_;
vector<std::pair<uint64, td_api::object_ptr<td_api::Function>>> pending_preauthentication_requests_;
vector<std::pair<uint64, td_api::object_ptr<td_api::Function>>> pending_set_parameters_requests_;
vector<std::pair<uint64, td_api::object_ptr<td_api::Function>>> pending_init_requests_;
template <class T>
@ -541,6 +544,10 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::translateText &request);
void on_request(uint64 id, const td_api::recognizeSpeech &request);
void on_request(uint64 id, const td_api::rateSpeechRecognition &request);
void on_request(uint64 id, const td_api::getFile &request);
void on_request(uint64 id, td_api::getRemoteFile &request);
@ -1043,6 +1050,10 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::toggleSupergroupSignMessages &request);
void on_request(uint64 id, const td_api::toggleSupergroupJoinToSendMessages &request);
void on_request(uint64 id, const td_api::toggleSupergroupJoinByRequest &request);
void on_request(uint64 id, const td_api::toggleSupergroupIsAllHistoryAvailable &request);
void on_request(uint64 id, const td_api::toggleSupergroupIsBroadcastGroup &request);
@ -1115,6 +1126,8 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::getAnimatedEmoji &request);
void on_request(uint64 id, const td_api::getAllAnimatedEmojis &request);
void on_request(uint64 id, td_api::getEmojiSuggestionsUrl &request);
void on_request(uint64 id, const td_api::getFavoriteStickers &request);
@ -1213,7 +1226,7 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::getBankCardInfo &request);
void on_request(uint64 id, const td_api::getPaymentForm &request);
void on_request(uint64 id, td_api::getPaymentForm &request);
void on_request(uint64 id, td_api::validateOrderInfo &request);
@ -1227,6 +1240,8 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::deleteSavedCredentials &request);
void on_request(uint64 id, td_api::createInvoiceLink &request);
void on_request(uint64 id, td_api::getPassportElement &request);
void on_request(uint64 id, td_api::getAllPassportElements &request);
@ -1291,6 +1306,18 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::removeRecentHashtag &request);
void on_request(uint64 id, const td_api::getPremiumLimit &request);
void on_request(uint64 id, const td_api::getPremiumFeatures &request);
void on_request(uint64 id, const td_api::getPremiumStickers &request);
void on_request(uint64 id, const td_api::viewPremiumFeature &request);
void on_request(uint64 id, const td_api::clickPremiumSubscriptionButton &request);
void on_request(uint64 id, const td_api::getPremiumState &request);
void on_request(uint64 id, td_api::acceptTermsOfService &request);
void on_request(uint64 id, const td_api::getCountries &request);
@ -1412,6 +1439,12 @@ class Td final : public Actor {
static DbKey as_db_key(string key, bool custom_db);
static int32 get_database_scheduler_id();
void on_parameters_checked(Result<TdDb::CheckedParameters> r_checked_parameters);
void finish_set_parameters();
void start_init(uint64 id, string &&key);
void init(Result<TdDb::OpenedDatabase> r_opened_database);

View File

@ -24,6 +24,7 @@
#include "td/db/SqliteKeyValueAsync.h"
#include "td/db/SqliteKeyValueSafe.h"
#include "td/actor/actor.h"
#include "td/actor/MultiPromise.h"
#include "td/utils/common.h"
@ -50,19 +51,6 @@ std::string get_sqlite_path(const TdParameters &parameters) {
return parameters.database_directory + db_name + ".sqlite";
}
Result<TdDb::EncryptionInfo> check_encryption(string path) {
Binlog binlog;
auto status = binlog.init(std::move(path), Binlog::Callback());
if (status.is_error() && status.code() != Binlog::Error::WrongPassword) {
LOG(WARNING) << "Failed to check binlog: " << status;
return Status::Error(400, status.message());
}
TdDb::EncryptionInfo info;
info.is_encrypted = binlog.get_info().wrong_password;
binlog.close(false /*need_sync*/).ensure();
return info;
}
Status init_binlog(Binlog &binlog, string path, BinlogKeyValue<Binlog> &binlog_pmc, BinlogKeyValue<Binlog> &config_pmc,
TdDb::OpenedDatabase &events, DbKey key) {
auto callback = [&](const BinlogEvent &event) {
@ -405,7 +393,7 @@ void TdDb::open(int32 scheduler_id, TdParameters parameters, DbKey key, Promise<
stop();
}
};
send_closure(create_actor_on_scheduler<Worker>("worker", scheduler_id), &Worker::open, std::move(parameters),
send_closure(create_actor_on_scheduler<Worker>("Worker", scheduler_id), &Worker::open, std::move(parameters),
std::move(key), std::move(promise));
return;
}
@ -502,8 +490,62 @@ void TdDb::open(int32 scheduler_id, TdParameters parameters, DbKey key, Promise<
TdDb::TdDb() = default;
TdDb::~TdDb() = default;
Result<TdDb::EncryptionInfo> TdDb::check_encryption(const TdParameters &parameters) {
return ::td::check_encryption(get_binlog_path(parameters));
void TdDb::check_parameters(int32 scheduler_id, TdParameters parameters, Promise<CheckedParameters> promise) {
if (scheduler_id >= 0 && Scheduler::instance()->sched_id() != scheduler_id) {
class Worker final : public Actor {
public:
void run(TdParameters parameters, Promise<CheckedParameters> promise) {
TdDb::check_parameters(-1, std::move(parameters), std::move(promise));
stop();
}
};
send_closure(create_actor_on_scheduler<Worker>("Worker", scheduler_id), &Worker::run, std::move(parameters),
std::move(promise));
return;
}
CheckedParameters result;
auto prepare_dir = [](string dir) -> Result<string> {
CHECK(!dir.empty());
if (dir.back() != TD_DIR_SLASH) {
dir += TD_DIR_SLASH;
}
TRY_STATUS(mkpath(dir, 0750));
TRY_RESULT(real_dir, realpath(dir, true));
if (dir.back() != TD_DIR_SLASH) {
dir += TD_DIR_SLASH;
}
return real_dir;
};
auto r_database_directory = prepare_dir(parameters.database_directory);
if (r_database_directory.is_error()) {
VLOG(td_init) << "Invalid database_directory";
return promise.set_error(Status::Error(PSLICE()
<< "Can't init database in the directory \"" << parameters.database_directory
<< "\": " << r_database_directory.error()));
}
result.database_directory = r_database_directory.move_as_ok();
parameters.database_directory = result.database_directory;
auto r_files_directory = prepare_dir(parameters.files_directory);
if (r_files_directory.is_error()) {
VLOG(td_init) << "Invalid files_directory";
return promise.set_error(Status::Error(PSLICE() << "Can't init files directory \"" << parameters.files_directory
<< "\": " << r_files_directory.error()));
}
result.files_directory = r_files_directory.move_as_ok();
Binlog binlog;
auto status = binlog.init(get_binlog_path(parameters), Binlog::Callback());
if (status.is_error() && status.code() != Binlog::Error::WrongPassword) {
LOG(WARNING) << "Failed to check binlog: " << status;
return promise.set_error(std::move(status));
}
result.is_database_encrypted = binlog.get_info().wrong_password;
binlog.close(false /*need_sync*/).ensure();
promise.set_value(std::move(result));
}
void TdDb::change_key(DbKey key, Promise<> promise) {

View File

@ -48,6 +48,13 @@ class TdDb {
TdDb &operator=(TdDb &&) = delete;
~TdDb();
struct CheckedParameters {
string database_directory;
string files_directory;
bool is_database_encrypted{false};
};
static void check_parameters(int32 scheduler_id, TdParameters parameters, Promise<CheckedParameters> promise);
struct OpenedDatabase {
unique_ptr<TdDb> database;
@ -64,11 +71,6 @@ class TdDb {
};
static void open(int32 scheduler_id, TdParameters parameters, DbKey key, Promise<OpenedDatabase> &&promise);
struct EncryptionInfo {
bool is_encrypted{false};
};
static Result<EncryptionInfo> check_encryption(const TdParameters &parameters);
static Status destroy(const TdParameters &parameters);
std::shared_ptr<FileDbInterface> get_file_db_shared();

View File

@ -270,6 +270,7 @@ string get_theme_parameters_json_string_impl(const td_api::object_ptr<td_api::th
return json_encode<string>(json_object([&theme](auto &o) {
auto get_color = &get_color_json<for_web_view>;
o("bg_color", get_color(theme->background_color_));
o("secondary_bg_color", get_color(theme->secondary_background_color_));
o("text_color", get_color(theme->text_color_));
o("hint_color", get_color(theme->hint_color_));
o("link_color", get_color(theme->link_color_));

View File

@ -53,6 +53,7 @@
#include "td/telegram/TdDb.h"
#include "td/telegram/telegram_api.hpp"
#include "td/telegram/ThemeManager.h"
#include "td/telegram/VoiceNotesManager.h"
#include "td/telegram/WebPagesManager.h"
#include "td/actor/MultiPromise.h"
@ -1236,20 +1237,35 @@ int32 UpdatesManager::get_update_edit_message_pts(const telegram_api::Updates *u
int32 pts = 0;
auto updates = get_updates(updates_ptr);
if (updates != nullptr) {
for (auto &update : *updates) {
for (auto &update_ptr : *updates) {
int32 update_pts = [&] {
switch (update->get_id()) {
switch (update_ptr->get_id()) {
case telegram_api::updateEditMessage::ID: {
auto update_ptr = static_cast<const telegram_api::updateEditMessage *>(update.get());
if (MessagesManager::get_full_message_id(update_ptr->message_, false) == full_message_id) {
return update_ptr->pts_;
auto update = static_cast<const telegram_api::updateEditMessage *>(update_ptr.get());
if (MessagesManager::get_full_message_id(update->message_, false) == full_message_id) {
return update->pts_;
}
return 0;
}
case telegram_api::updateEditChannelMessage::ID: {
auto update_ptr = static_cast<const telegram_api::updateEditChannelMessage *>(update.get());
if (MessagesManager::get_full_message_id(update_ptr->message_, false) == full_message_id) {
return update_ptr->pts_;
auto update = static_cast<const telegram_api::updateEditChannelMessage *>(update_ptr.get());
if (MessagesManager::get_full_message_id(update->message_, false) == full_message_id) {
return update->pts_;
}
return 0;
}
case telegram_api::updateNewScheduledMessage::ID: {
auto update = static_cast<const telegram_api::updateNewScheduledMessage *>(update_ptr.get());
auto new_full_message_id = MessagesManager::get_full_message_id(update->message_, true);
if (new_full_message_id.get_dialog_id() == full_message_id.get_dialog_id()) {
auto new_message_id = new_full_message_id.get_message_id();
auto old_message_id = full_message_id.get_message_id();
if (new_message_id.is_valid_scheduled() && new_message_id.is_scheduled_server() &&
old_message_id.is_valid_scheduled() && old_message_id.is_scheduled_server() &&
old_message_id.get_scheduled_server_message_id() ==
new_message_id.get_scheduled_server_message_id()) {
return -2;
}
}
return 0;
}
@ -2267,27 +2283,27 @@ void UpdatesManager::process_qts_update(tl_object_ptr<telegram_api::Update> &&up
}
case telegram_api::updateChatParticipant::ID: {
auto update = move_tl_object_as<telegram_api::updateChatParticipant>(update_ptr);
td_->contacts_manager_->on_update_chat_participant(ChatId(update->chat_id_), UserId(update->actor_id_),
update->date_, DialogInviteLink(std::move(update->invite_)),
std::move(update->prev_participant_),
std::move(update->new_participant_));
td_->contacts_manager_->on_update_chat_participant(
ChatId(update->chat_id_), UserId(update->actor_id_), update->date_,
DialogInviteLink(std::move(update->invite_), "updateChatParticipant"), std::move(update->prev_participant_),
std::move(update->new_participant_));
add_qts(qts).set_value(Unit());
break;
}
case telegram_api::updateChannelParticipant::ID: {
auto update = move_tl_object_as<telegram_api::updateChannelParticipant>(update_ptr);
td_->contacts_manager_->on_update_channel_participant(ChannelId(update->channel_id_), UserId(update->actor_id_),
update->date_, DialogInviteLink(std::move(update->invite_)),
std::move(update->prev_participant_),
std::move(update->new_participant_));
td_->contacts_manager_->on_update_channel_participant(
ChannelId(update->channel_id_), UserId(update->actor_id_), update->date_,
DialogInviteLink(std::move(update->invite_), "updateChannelParticipant"),
std::move(update->prev_participant_), std::move(update->new_participant_));
add_qts(qts).set_value(Unit());
break;
}
case telegram_api::updateBotChatInviteRequester::ID: {
auto update = move_tl_object_as<telegram_api::updateBotChatInviteRequester>(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_)));
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_), "updateBotChatInviteRequester"));
add_qts(qts).set_value(Unit());
break;
}
@ -3183,7 +3199,7 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateSavedGifs> upda
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateConfig> update, Promise<Unit> &&promise) {
send_closure(td_->config_manager_, &ConfigManager::request_config);
send_closure(td_->config_manager_, &ConfigManager::request_config, false);
promise.set_value(Unit());
}
@ -3416,6 +3432,12 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateSavedRingtones>
td_->notification_settings_manager_->reload_saved_ringtones(std::move(promise));
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateTranscribedAudio> update, Promise<Unit> &&promise) {
td_->voice_notes_manager_->on_update_transcribed_audio(std::move(update->text_), update->transcription_id_,
!update->pending_);
promise.set_value(Unit());
}
// unsupported updates
} // namespace td

View File

@ -526,6 +526,8 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateSavedRingtones> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateTranscribedAudio> update, Promise<Unit> &&promise);
// unsupported updates
};

View File

@ -10,7 +10,7 @@
namespace td {
constexpr int32 MTPROTO_LAYER = 140;
constexpr int32 MTPROTO_LAYER = 143;
enum class Version : int32 {
Initial, // 0
@ -51,6 +51,10 @@ enum class Version : int32 {
AddKeyboardButtonFlags, // 35
AddAudioFlags,
UseServerForwardAsCopy,
AddMainDialogListPosition,
AddVoiceNoteFlags,
AddMessageStickerFlags, // 40
AddStickerSetListFlags,
Next
};

View File

@ -16,7 +16,6 @@
#include "td/telegram/ConfigShared.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Status.h"
namespace td {
@ -158,7 +157,7 @@ void VideoNotesManager::create_video_note(FileId file_id, string minithumbnail,
SecretInputMedia VideoNotesManager::get_secret_input_media(FileId video_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail) const {
BufferSlice thumbnail, int32 layer) const {
const VideoNote *video_note = get_video_note(video_note_file_id);
CHECK(video_note != nullptr);
auto file_view = td_->file_manager_->get_file_view(video_note_file_id);
@ -185,7 +184,8 @@ SecretInputMedia VideoNotesManager::get_secret_input_media(FileId video_note_fil
"video/mp4",
file_view,
std::move(attributes),
string()};
string(),
layer};
}
tl_object_ptr<telegram_api::InputMedia> VideoNotesManager::get_input_media(

View File

@ -40,7 +40,7 @@ class VideoNotesManager {
SecretInputMedia get_secret_input_media(FileId video_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail) const;
BufferSlice thumbnail, int32 layer) const;
FileId get_video_note_thumbnail_file_id(FileId file_id) const;

View File

@ -204,7 +204,8 @@ void VideosManager::create_video(FileId file_id, string minithumbnail, PhotoSize
SecretInputMedia VideosManager::get_secret_input_media(FileId video_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const {
const string &caption, BufferSlice thumbnail,
int32 layer) const {
const Video *video = get_video(video_file_id);
CHECK(video != nullptr);
auto file_view = td_->file_manager_->get_file_view(video_file_id);
@ -215,7 +216,7 @@ SecretInputMedia VideosManager::get_secret_input_media(FileId video_file_id,
input_file = file_view.main_remote_location().as_input_encrypted_file();
}
if (!input_file) {
return SecretInputMedia{};
return {};
}
if (video->thumbnail.file_id.is_valid() && thumbnail.empty()) {
return {};
@ -230,7 +231,8 @@ SecretInputMedia VideosManager::get_secret_input_media(FileId video_file_id,
video->mime_type,
file_view,
std::move(attributes),
caption};
caption,
layer};
}
tl_object_ptr<telegram_api::InputMedia> VideosManager::get_input_media(

View File

@ -42,7 +42,7 @@ class VideosManager {
SecretInputMedia get_secret_input_media(FileId video_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, BufferSlice thumbnail) const;
const string &caption, BufferSlice thumbnail, int32 layer) const;
FileId get_video_thumbnail_file_id(FileId file_id) const;

View File

@ -6,8 +6,12 @@
//
#include "td/telegram/VoiceNotesManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/Dimensions.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
@ -15,12 +19,101 @@
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Status.h"
namespace td {
VoiceNotesManager::VoiceNotesManager(Td *td) : td_(td) {
class TranscribeAudioQuery final : public Td::ResultHandler {
DialogId dialog_id_;
FileId file_id_;
public:
void send(FileId file_id, FullMessageId full_message_id) {
dialog_id_ = full_message_id.get_dialog_id();
file_id_ = file_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
if (input_peer == nullptr) {
return on_error(Status::Error(400, "Can't access the chat"));
}
send_query(G()->net_query_creator().create(telegram_api::messages_transcribeAudio(
std::move(input_peer), full_message_id.get_message_id().get_server_message_id().get())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_transcribeAudio>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for TranscribeAudioQuery: " << to_string(result);
if (result->transcription_id_ == 0) {
return on_error(Status::Error(500, "Receive no recognition identifier"));
}
td_->voice_notes_manager_->on_voice_note_transcribed(file_id_, std::move(result->text_), result->transcription_id_,
!result->pending_);
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "TranscribeAudioQuery");
td_->voice_notes_manager_->on_voice_note_transcription_failed(file_id_, std::move(status));
}
};
class RateTranscribedAudioQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit RateTranscribedAudioQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, int64 transcription_id, bool is_good) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
if (input_peer == nullptr) {
return on_error(Status::Error(400, "Can't access the chat"));
}
send_query(G()->net_query_creator().create(telegram_api::messages_rateTranscribedAudio(
std::move(input_peer), full_message_id.get_message_id().get_server_message_id().get(), transcription_id,
is_good)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_rateTranscribedAudio>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.ok();
LOG(INFO) << "Receive result for RateTranscribedAudioQuery: " << result;
promise_.set_value(Unit());
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "RateTranscribedAudioQuery");
promise_.set_error(std::move(status));
}
};
VoiceNotesManager::VoiceNotesManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
voice_note_transcription_timeout_.set_callback(on_voice_note_transcription_timeout_callback);
voice_note_transcription_timeout_.set_callback_data(static_cast<void *>(this));
}
void VoiceNotesManager::tear_down() {
parent_.reset();
}
void VoiceNotesManager::on_voice_note_transcription_timeout_callback(void *voice_notes_manager_ptr,
int64 transcription_id) {
if (G()->close_flag()) {
return;
}
auto voice_notes_manager = static_cast<VoiceNotesManager *>(voice_notes_manager_ptr);
send_closure_later(voice_notes_manager->actor_id(voice_notes_manager),
&VoiceNotesManager::on_pending_voice_note_transcription_failed, transcription_id,
Status::Error(500, "Timeout expired"));
}
int32 VoiceNotesManager::get_voice_note_duration(FileId file_id) const {
@ -41,6 +134,7 @@ tl_object_ptr<td_api::voiceNote> VoiceNotesManager::get_voice_note_object(FileId
auto voice_note = it->second.get();
CHECK(voice_note != nullptr);
return make_tl_object<td_api::voiceNote>(voice_note->duration, voice_note->waveform, voice_note->mime_type,
voice_note->is_transcribed, voice_note->text,
td_->file_manager_->get_file_object(file_id));
}
@ -62,11 +156,29 @@ FileId VoiceNotesManager::on_get_voice_note(unique_ptr<VoiceNote> new_voice_note
v->duration = new_voice_note->duration;
v->waveform = new_voice_note->waveform;
}
if (new_voice_note->is_transcribed && v->transcription_id == 0) {
CHECK(!v->is_transcribed);
CHECK(new_voice_note->transcription_id != 0);
v->is_transcribed = true;
v->transcription_id = new_voice_note->transcription_id;
v->text = std::move(new_voice_note->text);
on_voice_note_transcription_updated(file_id);
}
}
return file_id;
}
VoiceNotesManager::VoiceNote *VoiceNotesManager::get_voice_note(FileId file_id) {
auto voice_note = voice_notes_.find(file_id);
if (voice_note == voice_notes_.end()) {
return nullptr;
}
CHECK(voice_note->second->file_id == file_id);
return voice_note->second.get();
}
const VoiceNotesManager::VoiceNote *VoiceNotesManager::get_voice_note(FileId file_id) const {
auto voice_note = voice_notes_.find(file_id);
if (voice_note == voice_notes_.end()) {
@ -128,12 +240,172 @@ void VoiceNotesManager::create_voice_note(FileId file_id, string mime_type, int3
on_get_voice_note(std::move(v), replace);
}
SecretInputMedia VoiceNotesManager::get_secret_input_media(FileId voice_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption) const {
auto *voice_note = get_voice_note(voice_file_id);
void VoiceNotesManager::register_voice_note(FileId voice_note_file_id, FullMessageId full_message_id,
const char *source) {
if (full_message_id.get_message_id().is_scheduled() || !full_message_id.get_message_id().is_server()) {
return;
}
LOG(INFO) << "Register voice note " << voice_note_file_id << " from " << full_message_id << " from " << source;
bool is_inserted = voice_note_messages_[voice_note_file_id].insert(full_message_id).second;
LOG_CHECK(is_inserted) << source << ' ' << voice_note_file_id << ' ' << full_message_id;
is_inserted = message_voice_notes_.emplace(full_message_id, voice_note_file_id).second;
CHECK(is_inserted);
}
void VoiceNotesManager::unregister_voice_note(FileId voice_note_file_id, FullMessageId full_message_id,
const char *source) {
if (full_message_id.get_message_id().is_scheduled() || !full_message_id.get_message_id().is_server()) {
return;
}
LOG(INFO) << "Unregister voice note " << voice_note_file_id << " from " << full_message_id << " from " << source;
auto &message_ids = voice_note_messages_[voice_note_file_id];
auto is_deleted = message_ids.erase(full_message_id) > 0;
LOG_CHECK(is_deleted) << source << ' ' << voice_note_file_id << ' ' << full_message_id;
if (message_ids.empty()) {
voice_note_messages_.erase(voice_note_file_id);
}
is_deleted = message_voice_notes_.erase(full_message_id) > 0;
CHECK(is_deleted);
}
void VoiceNotesManager::recognize_speech(FullMessageId full_message_id, Promise<Unit> &&promise) {
if (!td_->messages_manager_->have_message_force(full_message_id, "recognize_speech")) {
return promise.set_error(Status::Error(400, "Message not found"));
}
auto it = message_voice_notes_.find(full_message_id);
if (it == message_voice_notes_.end()) {
return promise.set_error(Status::Error(400, "Invalid message specified"));
}
auto file_id = it->second;
auto voice_note = get_voice_note(file_id);
CHECK(voice_note != nullptr);
auto file_view = td_->file_manager_->get_file_view(voice_file_id);
if (voice_note->is_transcribed) {
return promise.set_value(Unit());
}
auto &queries = speech_recognition_queries_[file_id];
queries.push_back(std::move(promise));
if (queries.size() == 1) {
td_->create_handler<TranscribeAudioQuery>()->send(file_id, full_message_id);
}
}
void VoiceNotesManager::on_voice_note_transcribed(FileId file_id, string &&text, int64 transcription_id,
bool is_final) {
auto voice_note = get_voice_note(file_id);
CHECK(voice_note != nullptr);
CHECK(!voice_note->is_transcribed);
CHECK(voice_note->transcription_id == 0 || voice_note->transcription_id == transcription_id);
bool is_changed = voice_note->is_transcribed != is_final || voice_note->text != text;
voice_note->transcription_id = transcription_id;
voice_note->is_transcribed = is_final;
voice_note->text = std::move(text);
if (is_changed) {
on_voice_note_transcription_updated(file_id);
}
if (is_final) {
auto it = speech_recognition_queries_.find(file_id);
CHECK(it != speech_recognition_queries_.end());
CHECK(!it->second.empty());
auto promises = std::move(it->second);
speech_recognition_queries_.erase(it);
set_promises(promises);
} else {
if (pending_voice_note_transcription_queries_.count(transcription_id) != 0) {
on_pending_voice_note_transcription_failed(transcription_id,
Status::Error(500, "Receive duplicate recognition identifier"));
}
bool is_inserted = pending_voice_note_transcription_queries_.emplace(transcription_id, file_id).second;
CHECK(is_inserted);
voice_note_transcription_timeout_.set_timeout_in(transcription_id, TRANSCRIPTION_TIMEOUT);
}
}
void VoiceNotesManager::on_voice_note_transcription_failed(FileId file_id, Status &&error) {
auto voice_note = get_voice_note(file_id);
CHECK(voice_note != nullptr);
CHECK(!voice_note->is_transcribed);
if (voice_note->transcription_id != 0) {
CHECK(pending_voice_note_transcription_queries_.count(voice_note->transcription_id) == 0);
voice_note->transcription_id = 0;
if (!voice_note->text.empty()) {
voice_note->text.clear();
on_voice_note_transcription_updated(file_id);
}
}
auto it = speech_recognition_queries_.find(file_id);
CHECK(it != speech_recognition_queries_.end());
CHECK(!it->second.empty());
auto promises = std::move(it->second);
speech_recognition_queries_.erase(it);
fail_promises(promises, std::move(error));
}
void VoiceNotesManager::on_update_transcribed_audio(string &&text, int64 transcription_id, bool is_final) {
auto it = pending_voice_note_transcription_queries_.find(transcription_id);
if (it == pending_voice_note_transcription_queries_.end()) {
return;
}
auto file_id = it->second;
pending_voice_note_transcription_queries_.erase(it);
voice_note_transcription_timeout_.cancel_timeout(transcription_id);
on_voice_note_transcribed(file_id, std::move(text), transcription_id, is_final);
}
void VoiceNotesManager::on_pending_voice_note_transcription_failed(int64 transcription_id, Status &&error) {
auto it = pending_voice_note_transcription_queries_.find(transcription_id);
if (it == pending_voice_note_transcription_queries_.end()) {
return;
}
auto file_id = it->second;
pending_voice_note_transcription_queries_.erase(it);
voice_note_transcription_timeout_.cancel_timeout(transcription_id);
on_voice_note_transcription_failed(file_id, std::move(error));
}
void VoiceNotesManager::on_voice_note_transcription_updated(FileId file_id) {
auto it = voice_note_messages_.find(file_id);
if (it != voice_note_messages_.end()) {
for (const auto &full_message_id : it->second) {
td_->messages_manager_->on_external_update_message_content(full_message_id);
}
}
}
void VoiceNotesManager::rate_speech_recognition(FullMessageId full_message_id, bool is_good, Promise<Unit> &&promise) {
if (!td_->messages_manager_->have_message_force(full_message_id, "rate_speech_recognition")) {
return promise.set_error(Status::Error(400, "Message not found"));
}
auto it = message_voice_notes_.find(full_message_id);
if (it == message_voice_notes_.end()) {
return promise.set_error(Status::Error(400, "Invalid message specified"));
}
auto file_id = it->second;
auto voice_note = get_voice_note(file_id);
CHECK(voice_note != nullptr);
if (!voice_note->is_transcribed) {
return promise.set_value(Unit());
}
CHECK(voice_note->transcription_id != 0);
td_->create_handler<RateTranscribedAudioQuery>(std::move(promise))
->send(full_message_id, voice_note->transcription_id, is_good);
}
SecretInputMedia VoiceNotesManager::get_secret_input_media(FileId voice_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption, int32 layer) const {
auto file_view = td_->file_manager_->get_file_view(voice_note_file_id);
if (!file_view.is_encrypted_secret() || file_view.encryption_key().empty()) {
return SecretInputMedia{};
}
@ -143,13 +415,16 @@ SecretInputMedia VoiceNotesManager::get_secret_input_media(FileId voice_file_id,
if (!input_file) {
return SecretInputMedia{};
}
auto *voice_note = get_voice_note(voice_note_file_id);
CHECK(voice_note != nullptr);
vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
attributes.push_back(make_tl_object<secret_api::documentAttributeAudio>(
secret_api::documentAttributeAudio::VOICE_MASK | secret_api::documentAttributeAudio::WAVEFORM_MASK,
false /*ignored*/, voice_note->duration, "", "", BufferSlice(voice_note->waveform)));
return {std::move(input_file), BufferSlice(), Dimensions(), voice_note->mime_type, file_view,
std::move(attributes), caption};
std::move(attributes), caption, layer};
}
tl_object_ptr<telegram_api::InputMedia> VoiceNotesManager::get_input_media(

View File

@ -7,20 +7,27 @@
#pragma once
#include "td/telegram/files/FileId.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/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Status.h"
namespace td {
class Td;
class VoiceNotesManager {
class VoiceNotesManager final : public Actor {
public:
explicit VoiceNotesManager(Td *td);
VoiceNotesManager(Td *td, ActorShared<> parent);
int32 get_voice_note_duration(FileId file_id) const;
@ -28,12 +35,26 @@ class VoiceNotesManager {
void create_voice_note(FileId file_id, string mime_type, int32 duration, string waveform, bool replace);
void register_voice_note(FileId voice_note_file_id, FullMessageId full_message_id, const char *source);
void unregister_voice_note(FileId voice_note_file_id, FullMessageId full_message_id, const char *source);
void recognize_speech(FullMessageId full_message_id, Promise<Unit> &&promise);
void rate_speech_recognition(FullMessageId full_message_id, bool is_good, Promise<Unit> &&promise);
void on_update_transcribed_audio(string &&text, int64 transcription_id, bool is_final);
void on_voice_note_transcribed(FileId file_id, string &&text, int64 transcription_id, bool is_final);
void on_voice_note_transcription_failed(FileId file_id, Status &&error);
tl_object_ptr<telegram_api::InputMedia> get_input_media(FileId file_id,
tl_object_ptr<telegram_api::InputFile> input_file) const;
SecretInputMedia get_secret_input_media(FileId voice_file_id,
SecretInputMedia get_secret_input_media(FileId voice_note_file_id,
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
const string &caption) const;
const string &caption, int32 layer) const;
FileId dup_voice_note(FileId new_id, FileId old_id);
@ -46,21 +67,45 @@ class VoiceNotesManager {
FileId parse_voice_note(ParserT &parser);
private:
static constexpr int32 TRANSCRIPTION_TIMEOUT = 60;
class VoiceNote {
public:
string mime_type;
int32 duration = 0;
bool is_transcribed = false;
string waveform;
int64 transcription_id = 0;
string text;
FileId file_id;
};
static void on_voice_note_transcription_timeout_callback(void *voice_notes_manager_ptr, int64 transcription_id);
VoiceNote *get_voice_note(FileId file_id);
const VoiceNote *get_voice_note(FileId file_id) const;
FileId on_get_voice_note(unique_ptr<VoiceNote> new_voice_note, bool replace);
void on_pending_voice_note_transcription_failed(int64 transcription_id, Status &&error);
void on_voice_note_transcription_updated(FileId file_id);
void tear_down() final;
Td *td_;
ActorShared<> parent_;
FlatHashMap<FileId, unique_ptr<VoiceNote>, FileIdHash> voice_notes_;
FlatHashMap<FileId, vector<Promise<Unit>>, FileIdHash> speech_recognition_queries_;
FlatHashMap<int64, FileId> pending_voice_note_transcription_queries_;
MultiTimeout voice_note_transcription_timeout_{"VoiceNoteTranscriptionTimeout"};
FlatHashMap<FileId, FlatHashSet<FullMessageId, FullMessageIdHash>, FileIdHash> voice_note_messages_;
FlatHashMap<FullMessageId, FileId, FullMessageIdHash> message_voice_notes_;
};
} // namespace td

View File

@ -9,6 +9,7 @@
#include "td/telegram/VoiceNotesManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Version.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
@ -20,18 +21,63 @@ void VoiceNotesManager::store_voice_note(FileId file_id, StorerT &storer) const
auto it = voice_notes_.find(file_id);
CHECK(it != voice_notes_.end());
const VoiceNote *voice_note = it->second.get();
store(voice_note->mime_type, storer);
store(voice_note->duration, storer);
store(voice_note->waveform, storer);
bool has_mime_type = !voice_note->mime_type.empty();
bool has_duration = voice_note->duration != 0;
bool has_waveform = !voice_note->waveform.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_mime_type);
STORE_FLAG(has_duration);
STORE_FLAG(has_waveform);
STORE_FLAG(voice_note->is_transcribed);
END_STORE_FLAGS();
if (has_mime_type) {
store(voice_note->mime_type, storer);
}
if (has_duration) {
store(voice_note->duration, storer);
}
if (has_waveform) {
store(voice_note->waveform, storer);
}
if (voice_note->is_transcribed) {
store(voice_note->transcription_id, storer);
store(voice_note->text, storer);
}
store(file_id, storer);
}
template <class ParserT>
FileId VoiceNotesManager::parse_voice_note(ParserT &parser) {
auto voice_note = make_unique<VoiceNote>();
parse(voice_note->mime_type, parser);
parse(voice_note->duration, parser);
parse(voice_note->waveform, parser);
bool has_mime_type;
bool has_duration;
bool has_waveform;
if (parser.version() >= static_cast<int32>(Version::AddVoiceNoteFlags)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_mime_type);
PARSE_FLAG(has_duration);
PARSE_FLAG(has_waveform);
PARSE_FLAG(voice_note->is_transcribed);
END_PARSE_FLAGS();
} else {
has_mime_type = true;
has_duration = true;
has_waveform = true;
voice_note->is_transcribed = false;
}
if (has_mime_type) {
parse(voice_note->mime_type, parser);
}
if (has_duration) {
parse(voice_note->duration, parser);
}
if (has_waveform) {
parse(voice_note->waveform, parser);
}
if (voice_note->is_transcribed) {
parse(voice_note->transcription_id, parser);
parse(voice_note->text, parser);
}
parse(voice_note->file_id, parser);
if (parser.get_error() != nullptr || !voice_note->file_id.is_valid()) {
return FileId();

View File

@ -352,9 +352,9 @@ class CliClient final : public Actor {
int64 id = 0;
string destination;
string source;
int32 part_size = 0;
int32 local_size = 0;
int32 size = 0;
int64 part_size = 0;
int64 local_size = 0;
int64 size = 0;
bool test_local_size_decrease = false;
};
@ -372,14 +372,14 @@ class CliClient final : public Actor {
return;
} else {
file_generation.source = update.original_path_;
file_generation.part_size = to_integer<int32>(update.conversion_);
file_generation.part_size = to_integer<int64>(update.conversion_);
file_generation.test_local_size_decrease = !update.conversion_.empty() && update.conversion_.back() == 't';
}
auto r_stat = stat(file_generation.source);
if (r_stat.is_ok()) {
auto size = r_stat.ok().size_;
if (size <= 0 || size > (2000 << 20)) {
if (size <= 0 || size > (static_cast<int64>(4000) << 20)) {
r_stat = Status::Error(400, size == 0 ? Slice("File is empty") : Slice("File is too big"));
}
}
@ -411,6 +411,8 @@ class CliClient final : public Actor {
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->application_version_ = "1.0";
send_request(
td_api::make_object<td_api::setOption>("use_pfs", td_api::make_object<td_api::optionValueBoolean>(true)));
send_request(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)));
break;
}
@ -435,7 +437,7 @@ class CliClient final : public Actor {
static char get_delimiter(Slice str) {
FlatHashSet<char> chars;
for (auto c : trim(str)) {
if (!is_alnum(c) && c != '-' && c != '.' && c != '/' && c != '\0' && static_cast<uint8>(c) <= 127) {
if (!is_alnum(c) && c != '-' && c != '@' && c != '.' && c != '/' && c != '\0' && static_cast<uint8>(c) <= 127) {
chars.insert(c);
}
}
@ -636,7 +638,7 @@ class CliClient final : public Actor {
}
static td_api::object_ptr<td_api::InputFile> as_generated_file(string original_path, string conversion,
int32 expected_size = 0) {
int64 expected_size = 0) {
return td_api::make_object<td_api::inputFileGenerated>(trim(std::move(original_path)), trim(std::move(conversion)),
expected_size);
}
@ -803,6 +805,32 @@ class CliClient final : public Actor {
arg.file_id = as_file_id(args);
}
struct InputInvoice {
int64 chat_id = 0;
int64 message_id = 0;
string invoice_name;
operator td_api::object_ptr<td_api::InputInvoice>() const {
if (invoice_name.empty()) {
return td_api::make_object<td_api::inputInvoiceMessage>(chat_id, message_id);
} else {
return td_api::make_object<td_api::inputInvoiceName>(invoice_name);
}
}
};
void get_args(string &args, InputInvoice &arg) const {
if (args.size() > 1 && args[0] == '#') {
arg.invoice_name = args;
} else {
string chat_id;
string message_id;
std::tie(chat_id, message_id) = split(args, get_delimiter(args));
arg.chat_id = as_chat_id(chat_id);
arg.message_id = as_message_id(message_id);
}
}
template <class FirstType, class SecondType, class... Types>
void get_args(string &args, FirstType &first_arg, SecondType &second_arg, Types &...other_args) const {
string arg;
@ -825,6 +853,17 @@ class CliClient final : public Actor {
result_str += " }";
break;
}
case td_api::trendingStickerSets::ID: {
auto sticker_sets = static_cast<const td_api::trendingStickerSets *>(result.get());
result_str = PSTRING() << "TrendingStickerSets { is_premium = " << sticker_sets->is_premium_
<< ", total_count = " << sticker_sets->total_count_
<< ", count = " << sticker_sets->sets_.size();
for (auto &sticker_set : sticker_sets->sets_) {
result_str += PSTRING() << ", " << sticker_set->name_;
}
result_str += " }";
break;
}
default:
break;
}
@ -1650,7 +1689,7 @@ class CliClient final : public Actor {
}
static td_api::object_ptr<td_api::themeParameters> get_theme_parameters() {
return td_api::make_object<td_api::themeParameters>(0, -1, 256, 65536, 123456789, 65535);
return td_api::make_object<td_api::themeParameters>(0, 1, -1, 256, 65536, 123456789, 65535);
}
static td_api::object_ptr<td_api::BackgroundFill> get_background_fill(int32 color) {
@ -2000,40 +2039,36 @@ class CliClient final : public Actor {
} else if (op == "gbci") {
send_request(td_api::make_object<td_api::getBankCardInfo>(args));
} else if (op == "gpf") {
ChatId chat_id;
MessageId message_id;
get_args(args, chat_id, message_id);
send_request(td_api::make_object<td_api::getPaymentForm>(chat_id, message_id, get_theme_parameters()));
InputInvoice input_invoice;
get_args(args, input_invoice);
send_request(td_api::make_object<td_api::getPaymentForm>(input_invoice, get_theme_parameters()));
} else if (op == "voi") {
ChatId chat_id;
MessageId message_id;
InputInvoice input_invoice;
bool allow_save;
get_args(args, chat_id, message_id, allow_save);
send_request(td_api::make_object<td_api::validateOrderInfo>(chat_id, message_id, nullptr, allow_save));
get_args(args, input_invoice, allow_save);
send_request(td_api::make_object<td_api::validateOrderInfo>(input_invoice, nullptr, allow_save));
} else if (op == "spfs") {
ChatId chat_id;
MessageId message_id;
InputInvoice input_invoice;
int64 tip_amount;
int64 payment_form_id;
string order_info_id;
string shipping_option_id;
string saved_credentials_id;
get_args(args, chat_id, message_id, tip_amount, payment_form_id, order_info_id, shipping_option_id,
get_args(args, input_invoice, tip_amount, payment_form_id, order_info_id, shipping_option_id,
saved_credentials_id);
send_request(td_api::make_object<td_api::sendPaymentForm>(
chat_id, message_id, payment_form_id, order_info_id, shipping_option_id,
input_invoice, payment_form_id, order_info_id, shipping_option_id,
td_api::make_object<td_api::inputCredentialsSaved>(saved_credentials_id), tip_amount));
} else if (op == "spfn") {
ChatId chat_id;
MessageId message_id;
InputInvoice input_invoice;
int64 tip_amount;
int64 payment_form_id;
string order_info_id;
string shipping_option_id;
string data;
get_args(args, chat_id, message_id, tip_amount, payment_form_id, order_info_id, shipping_option_id, data);
get_args(args, input_invoice, tip_amount, payment_form_id, order_info_id, shipping_option_id, data);
send_request(td_api::make_object<td_api::sendPaymentForm>(
chat_id, message_id, payment_form_id, order_info_id, shipping_option_id,
input_invoice, payment_form_id, order_info_id, shipping_option_id,
td_api::make_object<td_api::inputCredentialsNew>(data, true), tip_amount));
} else if (op == "gpre") {
ChatId chat_id;
@ -2522,6 +2557,22 @@ class CliClient final : public Actor {
execute(td_api::make_object<td_api::getPhoneNumberInfoSync>(rand_bool() ? "en" : "", args));
} else if (op == "gadl") {
send_request(td_api::make_object<td_api::getApplicationDownloadLink>());
} else if (op == "gprl") {
auto limit_type = td_api::make_object<td_api::premiumLimitTypeChatFilterCount>();
send_request(td_api::make_object<td_api::getPremiumLimit>(std::move(limit_type)));
} else if (op == "gprf") {
auto source = td_api::make_object<td_api::premiumSourceLimitExceeded>(
td_api::make_object<td_api::premiumLimitTypeChatFilterCount>());
send_request(td_api::make_object<td_api::getPremiumFeatures>(std::move(source)));
} else if (op == "gprst") {
send_request(td_api::make_object<td_api::getPremiumStickers>());
} else if (op == "vprf") {
auto feature = td_api::make_object<td_api::premiumFeatureProfileBadge>();
send_request(td_api::make_object<td_api::viewPremiumFeature>(std::move(feature)));
} else if (op == "cprsb") {
send_request(td_api::make_object<td_api::clickPremiumSubscriptionButton>());
} else if (op == "gprs") {
send_request(td_api::make_object<td_api::getPremiumState>());
} else if (op == "atos") {
send_request(td_api::make_object<td_api::acceptTermsOfService>(args));
} else if (op == "gdli") {
@ -2718,6 +2769,8 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::searchEmojis>(args, false, vector<string>{"ru_RU"}));
} else if (op == "gae") {
send_request(td_api::make_object<td_api::getAnimatedEmoji>(args));
} else if (op == "gaae") {
send_request(td_api::make_object<td_api::getAllAnimatedEmojis>());
} else if (op == "gesu") {
send_request(td_api::make_object<td_api::getEmojiSuggestionsUrl>(args));
} else if (op == "gsan") {
@ -2856,19 +2909,30 @@ class CliClient final : public Actor {
string to_language_code;
get_args(args, text, from_language_code, to_language_code);
send_request(td_api::make_object<td_api::translateText>(text, from_language_code, to_language_code));
} else if (op == "rs") {
ChatId chat_id;
MessageId message_id;
get_args(args, chat_id, message_id);
send_request(td_api::make_object<td_api::recognizeSpeech>(chat_id, message_id));
} else if (op == "rsr") {
ChatId chat_id;
MessageId message_id;
bool is_good;
get_args(args, chat_id, message_id, is_good);
send_request(td_api::make_object<td_api::rateSpeechRecognition>(chat_id, message_id, is_good));
} else if (op == "gf" || op == "GetFile") {
FileId file_id;
get_args(args, file_id);
send_request(td_api::make_object<td_api::getFile>(file_id));
} else if (op == "gfdps") {
FileId file_id;
int32 offset;
int64 offset;
get_args(args, file_id, offset);
send_request(td_api::make_object<td_api::getFileDownloadedPrefixSize>(file_id, offset));
} else if (op == "rfp") {
FileId file_id;
int32 offset;
int32 count;
int64 offset;
int64 count;
get_args(args, file_id, offset, count);
send_request(td_api::make_object<td_api::readFilePart>(file_id, offset, count));
} else if (op == "grf") {
@ -2886,8 +2950,8 @@ class CliClient final : public Actor {
width, height, scale, chat_id));
} else if (op == "df" || op == "DownloadFile" || op == "dff" || op == "dfs") {
FileId file_id;
int32 offset;
int32 limit;
int64 offset;
int64 limit;
int32 priority;
get_args(args, file_id, offset, limit, priority);
if (priority <= 0) {
@ -3921,7 +3985,7 @@ class CliClient final : public Actor {
ChatId chat_id;
string photo_path;
string conversion;
int32 expected_size;
int64 expected_size;
get_args(args, chat_id, photo_path, conversion, expected_size);
send_message(chat_id, td_api::make_object<td_api::inputMessagePhoto>(
as_generated_file(photo_path, conversion, expected_size), nullptr, vector<int32>(), 0,
@ -4127,7 +4191,11 @@ class CliClient final : public Actor {
} else if (op == "dcf") {
send_request(td_api::make_object<td_api::deleteChatFilter>(as_chat_filter_id(args)));
} else if (op == "rcf") {
send_request(td_api::make_object<td_api::reorderChatFilters>(as_chat_filter_ids(args)));
int32 main_chat_list_position;
string chat_filter_ids;
get_args(args, main_chat_list_position, chat_filter_ids);
send_request(td_api::make_object<td_api::reorderChatFilters>(as_chat_filter_ids(chat_filter_ids),
main_chat_list_position));
} else if (op == "grcf") {
send_request(td_api::make_object<td_api::getRecommendedChatFilters>());
} else if (op == "gcfdin") {
@ -4375,6 +4443,18 @@ class CliClient final : public Actor {
get_args(args, supergroup_id, sign_messages);
send_request(
td_api::make_object<td_api::toggleSupergroupSignMessages>(as_supergroup_id(supergroup_id), sign_messages));
} else if (op == "tsgjtsm") {
string supergroup_id;
bool join_to_send_message;
get_args(args, supergroup_id, join_to_send_message);
send_request(td_api::make_object<td_api::toggleSupergroupJoinToSendMessages>(as_supergroup_id(supergroup_id),
join_to_send_message));
} else if (op == "tsgjbr") {
string supergroup_id;
bool join_by_request;
get_args(args, supergroup_id, join_by_request);
send_request(
td_api::make_object<td_api::toggleSupergroupJoinByRequest>(as_supergroup_id(supergroup_id), join_by_request));
} else if (op == "scar") {
ChatId chat_id;
string available_reactions;
@ -4847,7 +4927,7 @@ class CliClient final : public Actor {
if (it->part_size > left_size) {
it->part_size = left_size;
}
BufferSlice block(it->part_size);
BufferSlice block(narrow_cast<size_t>(it->part_size));
FileFd::open(it->source, FileFd::Flags::Read).move_as_ok().pread(block.as_slice(), it->local_size).ensure();
if (rand_bool()) {
auto open_flags = FileFd::Flags::Write | (it->local_size ? 0 : FileFd::Flags::Truncate | FileFd::Flags::Create);

Some files were not shown because too many files have changed in this diff Show More