Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-08-13 22:56:08 +02:00
commit 70ad9c53c5
141 changed files with 34298 additions and 31909 deletions

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TDLib VERSION 1.8.4 LANGUAGES CXX C)
project(TDLib VERSION 1.8.5 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
@ -418,6 +418,7 @@ set(TDLIB_SOURCE
td/telegram/PhotoSizeSource.cpp
td/telegram/PollManager.cpp
td/telegram/Premium.cpp
td/telegram/PremiumGiftOption.cpp
td/telegram/QueryCombiner.cpp
td/telegram/RecentDialogList.cpp
td/telegram/ReplyMarkup.cpp
@ -437,6 +438,7 @@ set(TDLIB_SOURCE
td/telegram/StateManager.cpp
td/telegram/StickerFormat.cpp
td/telegram/StickersManager.cpp
td/telegram/StickerType.cpp
td/telegram/StorageManager.cpp
td/telegram/MemoryManager.cpp
td/telegram/SuggestedAction.cpp
@ -655,6 +657,7 @@ set(TDLIB_SOURCE
td/telegram/PollId.h
td/telegram/PollManager.h
td/telegram/Premium.h
td/telegram/PremiumGiftOption.h
td/telegram/PrivacyManager.h
td/telegram/PtsManager.h
td/telegram/PublicDialogType.h
@ -684,6 +687,7 @@ set(TDLIB_SOURCE
td/telegram/StickerFormat.h
td/telegram/StickerSetId.h
td/telegram/StickersManager.h
td/telegram/StickerType.h
td/telegram/StorageManager.h
td/telegram/MemoryManager.h
td/telegram/SuggestedAction.h
@ -735,6 +739,7 @@ set(TDLIB_SOURCE
td/telegram/PhotoSizeSource.hpp
td/telegram/PollId.hpp
td/telegram/PollManager.hpp
td/telegram/PremiumGiftOption.hpp
td/telegram/ReplyMarkup.hpp
td/telegram/SecureValue.hpp
td/telegram/SendCodeHelper.hpp

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.4 REQUIRED)
find_package(Td 1.8.5 REQUIRED)
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/CMakeLists.txt).

View File

@ -145,7 +145,7 @@ See [example/ios](https://github.com/tdlight-team/tdlight/tree/master/example/io
See [TDLibKit](https://github.com/Swiftgram/TDLibKit), [tdlib-swift](https://github.com/modestman/tdlib-swift), or [TDLib-iOS](https://github.com/leoMehlig/TDLib-iOS), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
See also the source code of [Moc](https://github.com/ggoraa/moc) - a native and powerful macOS and iPadOS Telegram client, optimized for moderating large communities and personal use.
See also the source code of [Moc](https://github.com/mock-foundation/moc) - a native and powerful macOS and iPadOS Telegram client, optimized for moderating large communities and personal use.
See [example/swift](https://github.com/tdlight-team/tdlight/tree/master/example/swift) for an example of a macOS Swift application.

3
example/android/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
SDK/
tdlib/
third-party/

View File

@ -0,0 +1,44 @@
<?php
if ($argc !== 2) {
exit();
}
$file = file_get_contents($argv[1]);
if (strpos($file, 'androidx.annotation.IntDef') !== false) {
exit();
}
$file = str_replace('import androidx.annotation.Nullable;', 'import androidx.annotation.IntDef;'.PHP_EOL.
'import androidx.annotation.Nullable;'.PHP_EOL.
PHP_EOL.
'import java.lang.annotation.Retention;'.PHP_EOL.
'import java.lang.annotation.RetentionPolicy;', $file);
preg_match_all('/public static class ([A-Za-z0-9]+) extends ([A-Za-z0-9]+)/', $file, $matches, PREG_SET_ORDER);
$children = [];
foreach ($matches as $val) {
if ($val[2] === 'Object') {
continue;
}
$children[$val[2]][] = PHP_EOL.' '.$val[1].'.CONSTRUCTOR';
}
$file = preg_replace_callback('/public abstract static class ([A-Za-z0-9]+)(<R extends Object>)? extends Object [{]/',
function ($val) use ($children) {
return $val[0].PHP_EOL.' @Retention(RetentionPolicy.SOURCE)'.PHP_EOL.' @IntDef({'.implode(',', $children[$val[1]]).<<<'EOL'
})
public @interface Constructors {}
/**
* @return identifier uniquely determining type of the object.
*/
@Constructors
@Override
public abstract int getConstructor();
EOL;
},
$file);
file_put_contents($argv[1], $file);

View File

@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR)
project(TdAndroid VERSION 1.0 LANGUAGES CXX)
option(TD_ENABLE_JNI "Enable JNI-compatible TDLib API" ON)
set(TD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..)
if (CMAKE_CROSSCOMPILING)
set(CMAKE_MODULE_PATH "${TD_DIR}/CMake")
include(TdSetUpCompiler)
td_set_up_compiler()
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -flto=thin -Oz")
list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}")
add_subdirectory(${TD_DIR} td)
add_library(tdjni SHARED "${TD_DIR}/example/java/td_jni.cpp")
target_link_libraries(tdjni PRIVATE Td::TdStatic)
target_compile_definitions(tdjni PRIVATE PACKAGE_NAME="org/drinkless/tdlib")
add_custom_command(TARGET tdjni POST_BUILD
COMMAND ${CMAKE_COMMAND} -E rename $<TARGET_FILE:tdjni> $<TARGET_FILE:tdjni>.debug
COMMAND ${CMAKE_STRIP} --strip-debug --strip-unneeded $<TARGET_FILE:tdjni>.debug -o $<TARGET_FILE:tdjni>)
else()
add_subdirectory(${TD_DIR} td)
set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib")
set(TD_API_JAVA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${TD_API_JAVA_PACKAGE}/TdApi.java")
set(TD_API_TLO_PATH "${TD_DIR}/td/generate/auto/tlo/td_api.tlo")
set(TD_API_TL_PATH "${TD_DIR}/td/generate/scheme/td_api.tl")
set(JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH "${TD_DIR}/td/generate/JavadocTlDocumentationGenerator.php")
set(GENERATE_JAVA_CMD td_generate_java_api TdApi ${TD_API_TLO_PATH} ${CMAKE_CURRENT_SOURCE_DIR} ${TD_API_JAVA_PACKAGE})
if (PHP_EXECUTABLE)
set(GENERATE_JAVA_CMD ${GENERATE_JAVA_CMD} &&
${PHP_EXECUTABLE} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH} ${TD_API_TL_PATH} ${TD_API_JAVA_PATH} androidx.annotation.Nullable @Nullable &&
${PHP_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/AddIntDef.php ${TD_API_JAVA_PATH})
endif()
file(MAKE_DIRECTORY ${TD_API_JAVA_PACKAGE})
add_custom_target(tl_generate_java
COMMAND ${GENERATE_JAVA_CMD}
COMMENT "Generate Java TL source files"
DEPENDS td_generate_java_api tl_generate_tlo ${TD_API_TLO_PATH} ${TD_API_TL_PATH}
)
endif()

22
example/android/README.md Normal file
View File

@ -0,0 +1,22 @@
# TDLib Android example
This is an example of building `TDLib` for Android.
You need a Bash shell on Linux, macOS, or Windows with some common tools, a C++ compiler, cmake, JDK, PHP, and gperf pre-installed.
## Building TDLib for Android
* Run the script `./check-environment.sh` to check that you have all required Unix tools and Java utilities. If the script exits with an error message, install the missing tool.
* Run the script `./fetch-sdk.sh` to download Android SDK to a local directory.
* Run the script `./build-openssl.sh` to download and build OpenSSL for Android.
* Run the script `./build-tdlib.sh` to build TDLib for Android.
* The built libraries are now located in the `tdlib/libs` directory, corresponding Java code is located in the `tdlib/java` directory, and standalone Java documentation can be found in the `tdlib/javadoc` directory. You can also use archives `tdlib/tdlib.zip` and `tdlib/tdlib-debug.zip`, which contain all aforementioned data.
If you already have installed Android SDK and NDK, you can skip the second step and specify existing Android SDK root path and Android NDK version as the first and the second parameters to the subsequent scripts. Make sure that the SDK includes android-33 platform and CMake 3.22.1.
If you already have prebuilt OpenSSL, you can skip the third step and specify path to the prebuild OpenSSL as the third parameter to the script `./build-tdlib.sh`.
If you want to update TDLib to a newer version, you need to run only the script `./build-tdlib.sh`.
You can specify different OpenSSL version as the fourth parameter to the script `./build-openssl.sh`. By default OpenSSL 1.1.1 is used because of much smaller binary footprint than newer OpenSSL versions.
You can build TDLib against shared standard C++ library by specifying "c++_shared" as the fourth parameter to the script `./build-tdlib.sh`. This can reduce total application size if you have a lot of other C++ code and want it to use the same shared library.

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
cd $(dirname $0)
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
OPENSSL_INSTALL_DIR=${3:-third-party/openssl}
OPENSSL_VERSION=${4:-OpenSSL_1_1_1q} # openssl-3.0.5
if [ ! -d "$ANDROID_SDK_ROOT" ] ; then
echo "Error: directory \"$ANDROID_SDK_ROOT\" doesn't exist. Run ./fetch-sdk.sh first, or provide a valid path to Android SDK."
exit 1
fi
if [ -e "$OPENSSL_INSTALL_DIR" ] ; then
echo "Error: file or directory \"$OPENSSL_INSTALL_DIR\" already exists. Delete it manually to proceed."
exit 1
fi
source ./check-environment.sh || exit 1
mkdir -p $OPENSSL_INSTALL_DIR || exit 1
ANDROID_SDK_ROOT="$(cd "$(dirname -- "$ANDROID_SDK_ROOT")" >/dev/null; pwd -P)/$(basename -- "$ANDROID_SDK_ROOT")"
OPENSSL_INSTALL_DIR="$(cd "$(dirname -- "$OPENSSL_INSTALL_DIR")" >/dev/null; pwd -P)/$(basename -- "$OPENSSL_INSTALL_DIR")"
echo "Downloading OpenSSL sources..."
rm -f $OPENSSL_VERSION.tar.gz || exit 1
$WGET https://github.com/openssl/openssl/archive/refs/tags/$OPENSSL_VERSION.tar.gz || exit 1
rm -rf ./openssl-$OPENSSL_VERSION || exit 1
tar xzf $OPENSSL_VERSION.tar.gz || exit 1
rm $OPENSSL_VERSION.tar.gz || exit 1
cd openssl-$OPENSSL_VERSION
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION # for OpenSSL 3.0
export ANDROID_NDK_HOME=$ANDROID_NDK_ROOT # for OpenSSL 1.1.1
PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin:$PATH
if ! clang --help >/dev/null 2>&1 ; then
echo "Error: failed to run clang from Android NDK."
if [[ "$OS_NAME" == "linux" ]] ; then
echo "Prebuilt Android NDK binaries are linked against glibc, so glibc must be installed."
fi
exit 1
fi
for ABI in arm64-v8a armeabi-v7a x86_64 x86 ; do
if [[ $ABI == "x86" ]]; then
./Configure android-x86 no-shared -U__ANDROID_API__ -D__ANDROID_API__=16 || exit 1
elif [[ $ABI == "x86_64" ]]; then
./Configure android-x86_64 no-shared -U__ANDROID_API__ -D__ANDROID_API__=21 || exit 1
elif [[ $ABI == "armeabi-v7a" ]]; then
./Configure android-arm no-shared -U__ANDROID_API__ -D__ANDROID_API__=16 -D__ARM_MAX_ARCH__=8 || exit 1
elif [[ $ABI == "arm64-v8a" ]]; then
./Configure android-arm64 no-shared -U__ANDROID_API__ -D__ANDROID_API__=21 || exit 1
fi
sed -i.bak 's/-O3/-O3 -ffunction-sections -fdata-sections/g' Makefile || exit 1
make depend -s || exit 1
make -j4 -s || exit 1
mkdir -p $OPENSSL_INSTALL_DIR/$ABI/lib/ || exit 1
cp libcrypto.a libssl.a $OPENSSL_INSTALL_DIR/$ABI/lib/ || exit 1
cp -r include $OPENSSL_INSTALL_DIR/$ABI/ || exit 1
make distclean || exit 1
done
cd ..
rm -rf ./openssl-$OPENSSL_VERSION || exit 1

86
example/android/build-tdlib.sh Executable file
View File

@ -0,0 +1,86 @@
#!/usr/bin/env bash
cd $(dirname $0)
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
OPENSSL_INSTALL_DIR=${3:-third-party/openssl}
ANDROID_STL=${4:-c++_static}
if [ "$ANDROID_STL" != "c++_static" ] && [ "$ANDROID_STL" != "c++_shared" ] ; then
echo 'Error: ANDROID_STL must be either "c++_static" or "c++_shared".'
exit 1
fi
source ./check-environment.sh || exit 1
if [ ! -d "$ANDROID_SDK_ROOT" ] ; then
echo "Error: directory \"$ANDROID_SDK_ROOT\" doesn't exist. Run ./fetch-sdk.sh first, or provide a valid path to Android SDK."
exit 1
fi
if [ ! -d "$OPENSSL_INSTALL_DIR" ] ; then
echo "Error: directory \"$OPENSSL_INSTALL_DIR\" doesn't exists. Run ./build-openssl.sh first."
exit 1
fi
ANDROID_SDK_ROOT="$(cd "$(dirname -- "$ANDROID_SDK_ROOT")" >/dev/null; pwd -P)/$(basename -- "$ANDROID_SDK_ROOT")"
ANDROID_NDK_ROOT="$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION"
OPENSSL_INSTALL_DIR="$(cd "$(dirname -- "$OPENSSL_INSTALL_DIR")" >/dev/null; pwd -P)/$(basename -- "$OPENSSL_INSTALL_DIR")"
PATH=$ANDROID_SDK_ROOT/cmake/3.22.1/bin:$PATH
echo "Downloading annotation Java package..."
rm -f android.jar annotation-1.4.0.jar || exit 1
$WGET https://maven.google.com/androidx/annotation/annotation/1.4.0/annotation-1.4.0.jar || exit 1
echo "Generating TDLib source files..."
mkdir -p build-native || exit 1
cd build-native
cmake .. || exit 1
cmake --build . --target prepare_cross_compiling || exit 1
cmake --build . --target tl_generate_java || exit 1
cd ..
php AddIntDef.php org/drinkless/tdlib/TdApi.java || exit 1
echo "Copying Java source files..."
rm -rf tdlib || exit 1
mkdir -p tdlib/java/org/drinkless/tdlib || exit 1
cp -p {../../example,tdlib}/java/org/drinkless/tdlib/Client.java || exit 1
mv {,tdlib/java/}org/drinkless/tdlib/TdApi.java || exit 1
rm -rf org || exit 1
echo "Generating Javadoc documentation..."
cp "$ANDROID_SDK_ROOT/platforms/android-33/android.jar" . || exit 1
JAVADOC_SEPARATOR=$([ "$OS_NAME" == "win" ] && echo ";" || echo ":")
javadoc -d tdlib/javadoc -encoding UTF-8 -charset UTF-8 -classpath "android.jar${JAVADOC_SEPARATOR}annotation-1.4.0.jar" -quiet -sourcepath tdlib/java org.drinkless.tdlib || exit 1
rm android.jar annotation-1.4.0.jar || exit 1
echo "Building TDLib..."
for ABI in arm64-v8a armeabi-v7a x86_64 x86 ; do
mkdir -p build-$ABI || exit 1
cd build-$ABI
cmake -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" -DOPENSSL_ROOT_DIR="$OPENSSL_INSTALL_DIR/$ABI" -DCMAKE_BUILD_TYPE=RelWithDebInfo -GNinja -DANDROID_ABI=$ABI -DANDROID_STL=$ANDROID_STL -DANDROID_PLATFORM=android-16 .. || exit 1
cmake --build . || exit 1
cd ..
mkdir -p tdlib/libs/$ABI/ || exit 1
cp -p build-$ABI/libtd*.so* tdlib/libs/$ABI/ || exit 1
if [[ "$ANDROID_STL" == "c++_shared" ]] ; then
FULL_ABI=$(case $ABI in
"arm64-v8a") echo "aarch64-linux-android" ;;
"armeabi-v7a") echo "arm-linux-androideabi" ;;
"x86_64") echo "x86_64-linux-android" ;;
"x86") echo "i686-linux-android" ;;
esac)
cp "$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/sysroot/usr/lib/$FULL_ABI/libc++_shared.so" tdlib/libs/$ABI/ || exit 1
"$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin/llvm-strip" tdlib/libs/$ABI/libc++_shared.so || exit 1
fi
done
echo "Compressing..."
rm -f tdlib.zip tdlib-debug.zip || exit 1
jar -cMf tdlib-debug.zip tdlib || exit 1
rm tdlib/libs/*/*.debug || exit 1
jar -cMf tdlib.zip tdlib || exit 1
mv tdlib.zip tdlib-debug.zip tdlib || exit 1
echo "Done."

View File

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# The script checks that all needed tools are installed and sets OS_NAME, HOST_ARCH, and WGET variables
if [[ "$OSTYPE" == "linux"* ]] ; then
OS_NAME="linux"
HOST_ARCH="linux-x86_64"
elif [[ "$OSTYPE" == "darwin"* ]] ; then
OS_NAME="mac"
HOST_ARCH="darwin-x86_64"
elif [[ "$OSTYPE" == "msys" ]] ; then
OS_NAME="win"
HOST_ARCH="windows-x86_64"
else
echo "Error: this script supports only Bash shell on Linux, macOS, or Windows."
exit 1
fi
if which wget >/dev/null 2>&1 ; then
WGET="wget -q"
elif which curl >/dev/null 2>&1 ; then
WGET="curl -sfLO"
else
echo "Error: this script requires either curl or wget tool installed."
exit 1
fi
for TOOL_NAME in gperf jar javadoc make perl php sed tar yes unzip ; do
if ! which "$TOOL_NAME" >/dev/null 2>&1 ; then
echo "Error: this script requires $TOOL_NAME tool installed."
exit 1
fi
done
if [[ $(which make) = *" "* ]] ; then
echo "Error: OpenSSL expects that full path to make tool doesn't contain spaces. Move it to some other place."
fi
if ! perl -MExtUtils::MakeMaker -MLocale::Maketext::Simple -MPod::Usage -e '' >/dev/null 2>&1 ; then
echo "Error: Perl installation is broken."
if [[ "$OSTYPE" == "msys" ]] ; then
echo "For Git Bash you need to manually copy ExtUtils, Locale and Pod modules to /usr/share/perl5/core_perl from any compatible Perl installation."
fi
exit 1
fi

31
example/android/fetch-sdk.sh Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
cd $(dirname $0)
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
if [ -e "$ANDROID_SDK_ROOT" ] ; then
echo "Error: file or directory \"$ANDROID_SDK_ROOT\" already exists. Delete it manually to proceed."
exit 1
fi
source ./check-environment.sh || exit 1
SDKMANAGER="./sdkmanager"
if [[ "$OS_NAME" == "win" ]] ; then
SDKMANAGER="./sdkmanager.bat"
fi
echo "Downloading SDK Manager..."
mkdir -p "$ANDROID_SDK_ROOT" || exit 1
cd "$ANDROID_SDK_ROOT" || exit 1
$WGET "https://dl.google.com/android/repository/commandlinetools-$OS_NAME-8512546_latest.zip" || exit 1
mkdir -p cmdline-tools || exit 1
unzip -qq "commandlinetools-$OS_NAME-8512546_latest.zip" -d cmdline-tools || exit 1
rm "commandlinetools-$OS_NAME-8512546_latest.zip" || exit 1
mv cmdline-tools/* cmdline-tools/latest/ || exit 1
echo "Installing required SDK tools..."
cd cmdline-tools/latest/bin/ || exit 1
yes | $SDKMANAGER --licenses >/dev/null || exit 1
$SDKMANAGER --install "ndk;$ANDROID_NDK_VERSION" "cmake;3.22.1" "build-tools;33.0.0" "platforms;android-33" > /dev/null || exit 1

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.4 REQUIRED)
find_package(Td 1.8.5 REQUIRED)
add_executable(tdjson_example tdjson_example.cpp)
target_link_libraries(tdjson_example PRIVATE Td::TdJson)

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.4" Language="en-US" Publisher="Telegram LLC" />
<Identity Id="Telegram.Td.UWP" Version="1.8.5" 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

@ -19,6 +19,8 @@ set(SQLITE_SOURCE
sqlite/sqlite3session.h
)
# all SQLite functions are moved to namespace tdsqlite3 by `sed -Ebi 's/sqlite3([^.]|$)/td&/g' *`
add_library(tdsqlite STATIC ${SQLITE_SOURCE})
target_include_directories(tdsqlite PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_include_directories(tdsqlite SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})

51387
sqlite/sqlite/sqlite3.c vendored

File diff suppressed because it is too large Load Diff

4806
sqlite/sqlite/sqlite3.h vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -121,6 +121,11 @@ messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
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;
// layer 144
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity;
---functions---
test.dummyFunction = Bool;

View File

@ -101,7 +101,7 @@ authorizationStateWaitOtherDeviceConfirmation link:string = AuthorizationState;
//@description The user is unregistered and need to accept terms of service and enter their first name and last name to finish registration @terms_of_service Telegram terms of service
authorizationStateWaitRegistration terms_of_service:termsOfService = AuthorizationState;
//@description The user has been authorized, but needs to enter a password to start using the application @password_hint Hint for the password; may be empty @has_recovery_email_address True, if a recovery email address has been set up
//@description The user has been authorized, but needs to enter a 2-step verification password to start using the application @password_hint Hint for the password; may be empty @has_recovery_email_address True, if a recovery email address has been set up
//@recovery_email_address_pattern Pattern of the email address to which the recovery email was sent; empty until a recovery email has been sent
authorizationStateWaitPassword password_hint:string has_recovery_email_address:Bool recovery_email_address_pattern:string = AuthorizationState;
@ -122,7 +122,7 @@ authorizationStateClosed = AuthorizationState;
//@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; may be empty
//@has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved
//@recovery_email_address_code_info Information about the recovery email address to which the confirmation email was sent; may be null
//@pending_reset_date If not 0, point in time (Unix timestamp) after which the password can be reset immediately using resetPassword
//@pending_reset_date If not 0, point in time (Unix timestamp) after which the 2-step verification password can be reset immediately using resetPassword
passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo pending_reset_date:int32 = PasswordState;
//@description Contains information about the current recovery email address @recovery_email_address Recovery email address
@ -240,19 +240,28 @@ maskPointChin = MaskPoint;
maskPosition point:MaskPoint x_shift:double y_shift:double scale:double = MaskPosition;
//@class StickerType @description Describes type of a sticker
//@class StickerFormat @description Describes format of a sticker
//@description The sticker is an image in WEBP format
stickerTypeStatic = StickerType;
stickerFormatWebp = StickerFormat;
//@description The sticker is an animation in TGS format
stickerTypeAnimated = StickerType;
stickerFormatTgs = StickerFormat;
//@description The sticker is a video in WEBM format
stickerTypeVideo = StickerType;
stickerFormatWebm = StickerFormat;
//@description The sticker is a mask in WEBP format to be placed on photos or videos @mask_position Position where the mask is placed; may be null
stickerTypeMask mask_position:maskPosition = StickerType;
//@class StickerType @description Describes type of a sticker
//@description The sticker is a regular sticker
stickerTypeRegular = StickerType;
//@description The sticker is a mask in WEBP format to be placed on photos or videos
stickerTypeMask = StickerType;
//@description The sticker is a custom emoji to be used inside message text and caption
stickerTypeCustomEmoji = StickerType;
//@description Represents a closed vector path. The path begins at the end point of the last command @commands List of vector path commands
@ -283,8 +292,9 @@ animation duration:int32 width:int32 height:int32 file_name:string mime_type:str
//@description Describes an audio file. Audio is usually in MP3 or M4A format @duration Duration of the audio, in seconds; as defined by the sender @title Title of the audio; as defined by the sender @performer Performer of the audio; as defined by the sender
//@file_name Original name of the file; as defined by the sender @mime_type The MIME type of the file; as defined by the sender @album_cover_minithumbnail The minithumbnail of the album cover; may be null
//@album_cover_thumbnail The thumbnail of the album cover in JPEG format; as defined by the sender. The full size thumbnail is supposed to be extracted from the downloaded file; may be null @audio File containing the audio
audio duration:int32 title:string performer:string file_name:string mime_type:string album_cover_minithumbnail:minithumbnail album_cover_thumbnail:thumbnail audio:file = Audio;
//@album_cover_thumbnail The thumbnail of the album cover in JPEG format; as defined by the sender. The full size thumbnail is supposed to be extracted from the downloaded audio file; may be null
//@external_album_covers Album cover variants to use if the downloaded audio file contains no album cover. Provided thumbnail dimensions are approximate @audio File containing the audio
audio duration:int32 title:string performer:string file_name:string mime_type:string album_cover_minithumbnail:minithumbnail album_cover_thumbnail:thumbnail external_album_covers:vector<thumbnail> audio:file = Audio;
//@description Describes a document of any type @file_name Original name of the file; as defined by the sender @mime_type MIME type of the file; as defined by the sender
//@minithumbnail Document minithumbnail; may be null @thumbnail Document thumbnail in JPEG or PNG format (PNG will be used only for background patterns); as defined by the sender; may be null @document File containing the document
@ -295,9 +305,11 @@ document file_name:string mime_type:string minithumbnail:minithumbnail thumbnail
photo has_stickers:Bool minithumbnail:minithumbnail sizes:vector<photoSize> = Photo;
//@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 @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;
//@emoji Emoji corresponding to the sticker @format Sticker format @type Sticker type @mask_position Position where the mask is placed; may be null even the sticker is a mask
//@custom_emoji_id Identifier of the emoji if the sticker is a custom emoji
//@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 @is_premium True, if only Premium users can use the sticker @premium_animation Premium animation of the sticker; may be null @sticker File containing the sticker
sticker set_id:int64 width:int32 height:int32 emoji:string format:StickerFormat type:StickerType mask_position:maskPosition custom_emoji_id:int64 outline:vector<closedVectorPath> thumbnail:thumbnail is_premium:Bool 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
@ -313,14 +325,13 @@ videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:thum
//@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;
//@mime_type MIME type of the file; as defined by the sender @speech_recognition_result Result of speech recognition in the voice note; may be null @voice File containing the voice note
voiceNote duration:int32 waveform:bytes mime_type:string speech_recognition_result:SpeechRecognitionResult voice:file = VoiceNote;
//@description Describes an animated representation of an emoji
//@sticker Animated sticker for the emoji
//@description Describes an animated or custom representation of an emoji
//@sticker Sticker for the emoji; may be null if yet unknown for a custom emoji. If the sticker is a custom emoji, it can have arbitrary format different from stickerFormatTgs
//@fitzpatrick_type Emoji modifier fitzpatrick type; 0-6; 0 if none
//@sound File containing the sound to be played when the animated emoji is clicked; may be null. The sound is encoded with the Opus codec, and stored inside an OGG container
//@sound File containing the sound to be played when the sticker is clicked; may be null. The sound is encoded with the Opus codec, and stored inside an OGG container
animatedEmoji sticker:sticker fitzpatrick_type:int32 sound:file = AnimatedEmoji;
//@description Describes a user contact @phone_number Phone number of the user @first_name First name of the user; 1-255 characters in length @last_name Last name of the user @vcard Additional data about the user in a form of vCard; 0-2048 bytes in length @user_id Identifier of the user, if known; otherwise 0
@ -469,6 +480,16 @@ chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_pol
chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatAdministratorRights;
//@description Describes an option for gifting Telegram Premium to a user
//@currency ISO 4217 currency code for Telegram Premium subscription payment
//@amount The amount to pay, in the smallest units of the currency
//@discount_percentage The discount associated with this gift option, as a percentage
//@month_count Number of month the Telegram Premium subscription will be active
//@store_product_id Identifier of the store product associated with the option
//@payment_link An internal link to be opened for gifting Telegram Premium to the user if store payment isn't possible; may be null if direct payment isn't available
premiumGiftOption currency:string amount:int53 discount_percentage:int32 month_count:int32 store_product_id:string payment_link:InternalLinkType = PremiumGiftOption;
//@description Represents a user
//@id User identifier
//@first_name First name of the user
@ -491,6 +512,7 @@ chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messa
//@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
@ -502,7 +524,6 @@ user id:int53 first_name:string last_name:string username:string phone_number:st
//@default_channel_administrator_rights Default administrator rights for adding the bot to channels; may be null
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
//@is_blocked True, if the user is blocked by the current user
@ -510,11 +531,13 @@ botInfo share_text:string description:string photo:photo animation:animation men
//@supports_video_calls True, if a video call can be created with the user
//@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
//@has_restricted_voice_and_video_note_messages True, if voice and video notes can't be sent or forwarded to the user
//@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; may be null for bots
//@premium_gift_options The list of available options for gifting Telegram Premium to the user
//@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:formattedText 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 has_restricted_voice_and_video_note_messages:Bool need_phone_number_privacy_exception:Bool bio:formattedText premium_gift_options:vector<premiumGiftOption> 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;
@ -758,7 +781,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, files bigger than 2000MB are supported if the layer >= 143
//@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, spoiler and custom emoji text entities are supported if the layer >= 144
secretChat id:int32 user_id:int53 state:SecretChatState is_outbound:Bool key_hash:bytes layer:int32 = SecretChat;
@ -862,7 +885,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
//@can_get_added_reactions True, if the list of added reactions is available through getMessageAddedReactions
//@can_get_statistics True, if the message statistics are available through getMessageStatistics
//@can_get_message_thread True, if information about the message thread is available through getMessageThread
//@can_get_message_thread True, if information about the message thread is available through getMessageThread and getMessageThreadHistory
//@can_get_viewers True, if chat members already viewed the message can be received through getMessageViewers
//@can_get_media_timestamp_links True, if media timestamp links can be generated for media timestamp entities in the message text, caption or web page description through getMessageLink
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
@ -1176,7 +1199,7 @@ 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
inlineKeyboardButtonTypeCallback data:bytes = InlineKeyboardButtonType;
//@description A button that asks for password of the current user and then sends a callback query to a bot @data Data to be sent to the bot via a callback query
//@description A button that asks for the 2-step verification password of the current user and then sends a callback query to a bot @data Data to be sent to the bot via a callback query
inlineKeyboardButtonTypeCallbackWithPassword data:bytes = InlineKeyboardButtonType;
//@description A button with a game that sends a callback query to a bot. This button must be in the first column and row of the keyboard and can be attached only to a message with content of the type messageGame
@ -1523,7 +1546,7 @@ orderInfo name:string phone_number:string email_address:string shipping_address:
//@description One shipping option @id Shipping option identifier @title Option title @price_parts A list of objects used to calculate the total shipping costs
shippingOption id:string title:string price_parts:vector<labeledPricePart> = ShippingOption;
//@description Contains information about saved card credentials @id Unique identifier of the saved credentials @title Title of the saved credentials
//@description Contains information about saved payment credentials @id Unique identifier of the saved credentials @title Title of the saved credentials
savedCredentials id:string title:string = SavedCredentials;
@ -1554,20 +1577,25 @@ paymentProviderStripe publishable_key:string need_country:Bool need_postal_code:
paymentProviderOther url:string = PaymentProvider;
//@description Describes an additional payment option @title Title for the payment option @url Payment form URL to be opened in a web view
paymentOption title:string url:string = PaymentOption;
//@description Contains information about an invoice payment form
//@id The payment form identifier
//@invoice Full information about the invoice
//@seller_bot_user_id User identifier of the seller bot
//@payment_provider_user_id User identifier of the payment provider bot
//@payment_provider Information about the payment provider
//@additional_payment_options The list of additional payment options
//@saved_order_info Saved server-side order information; may be null
//@saved_credentials Information about saved card credentials; may be null
//@saved_credentials The list of saved payment credentials
//@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
//@need_password True, if the user will be able to save credentials, if sets up a 2-step verification password
//@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;
paymentForm id:int64 invoice:invoice seller_bot_user_id:int53 payment_provider_user_id:int53 payment_provider:PaymentProvider additional_payment_options:vector<paymentOption> saved_order_info:orderInfo saved_credentials:vector<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;
@ -1995,6 +2023,10 @@ messagePaymentSuccessful invoice_chat_id:int53 invoice_message_id:int53 currency
//@telegram_payment_charge_id Telegram payment identifier @provider_payment_charge_id Provider payment identifier
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 Telegram Premium was gifted to the user @currency Currency for the paid amount @amount The paid amount, in the smallest units of the currency @month_count Number of month the Telegram Premium subscription will be active
//@sticker A sticker to be shown in the message; may be null if unknown
messageGiftedPremium currency:string amount:int53 month_count:int32 sticker:sticker = MessageContent;
//@description A contact has registered with Telegram
messageContactRegistered = MessageContent;
@ -2058,7 +2090,7 @@ textEntityTypeUnderline = TextEntityType;
//@description A strikethrough text
textEntityTypeStrikethrough = TextEntityType;
//@description A spoiler text. Not supported in secret chats
//@description A spoiler text
textEntityTypeSpoiler = TextEntityType;
//@description Text that must be formatted as if inside a code HTML tag
@ -2076,6 +2108,9 @@ textEntityTypeTextUrl url:string = TextEntityType;
//@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user
textEntityTypeMentionName user_id:int53 = TextEntityType;
//@description A custom emoji. The text behind a custom emoji must be an emoji. Only premium users can use premium custom emoji @custom_emoji_id Unique identifier of the custom emoji
textEntityTypeCustomEmoji custom_emoji_id:int64 = TextEntityType;
//@description A media timestamp @media_timestamp Timestamp from which a video/audio/video note/voice note playing must start, in seconds. The media can be in the content or the web page preview of the current message, or in the same places in the replied message
textEntityTypeMediaTimestamp media_timestamp:int32 = TextEntityType;
@ -2112,7 +2147,7 @@ messageCopyOptions send_copy:Bool replace_caption:Bool new_caption:formattedText
//@class InputMessageContent @description The content of a message to send
//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Spoiler, Code, Pre, PreCode, TextUrl and MentionName entities are allowed to be specified manually
//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Spoiler, CustomEmoji, Code, Pre, PreCode, TextUrl and MentionName entities are allowed to be specified manually
//@disable_web_page_preview True, if rich web page previews for URLs in the message text must be disabled @clear_draft True, if a chat message draft must be deleted
inputMessageText text:formattedText disable_web_page_preview:Bool clear_draft:Bool = InputMessageContent;
@ -2314,17 +2349,17 @@ emojis emojis:vector<string> = Emojis;
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP, TGS, or WEBM format with width and height 100; may be null. The file can be downloaded only before the thumbnail is changed
//@thumbnail_outline Sticker set thumbnail's outline represented as a list of closed vector paths; may be empty. The coordinate system origin is in the upper-left corner
//@is_installed True, if the sticker set has been installed by the current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously
//@is_official True, if the sticker set is official @sticker_type Type of the stickers in the set @is_viewed True for already viewed trending sticker sets
//@is_official True, if the sticker set is official @sticker_format Format of the stickers in the set @sticker_type Type of the stickers in the set @is_viewed True for already viewed trending sticker sets
//@stickers List of stickers in this set @emojis A list of emoji corresponding to the stickers in the same order. The list is only for informational purposes, because a sticker is always sent with a fixed emoji from the corresponding Sticker object
stickerSet id:int64 title:string name:string thumbnail:thumbnail thumbnail_outline:vector<closedVectorPath> is_installed:Bool is_archived:Bool is_official:Bool sticker_type:StickerType is_viewed:Bool stickers:vector<sticker> emojis:vector<emojis> = StickerSet;
stickerSet id:int64 title:string name:string thumbnail:thumbnail thumbnail_outline:vector<closedVectorPath> is_installed:Bool is_archived:Bool is_official:Bool sticker_format:StickerFormat sticker_type:StickerType is_viewed:Bool stickers:vector<sticker> emojis:vector<emojis> = StickerSet;
//@description Represents short information about a sticker set
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP, TGS, or WEBM format with width and height 100; may be null
//@thumbnail_outline Sticker set thumbnail's outline represented as a list of closed vector paths; may be empty. The coordinate system origin is in the upper-left corner
//@is_installed True, if the sticker set has been installed by the current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously
//@is_official True, if the sticker set is official @sticker_type Type of the stickers in the set @is_viewed True for already viewed trending sticker sets
//@is_official True, if the sticker set is official @sticker_format Format of the stickers in the set @sticker_type Type of the stickers in the set @is_viewed True for already viewed trending sticker sets
//@size Total number of stickers in the set @covers Up to the first 5 stickers from the set, depending on the context. If the application needs more stickers the full sticker set needs to be requested
stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail thumbnail_outline:vector<closedVectorPath> is_installed:Bool is_archived:Bool is_official:Bool sticker_type:StickerType is_viewed:Bool size:int32 covers:vector<sticker> = StickerSetInfo;
stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail thumbnail_outline:vector<closedVectorPath> is_installed:Bool is_archived:Bool is_official:Bool sticker_format:StickerFormat sticker_type:StickerType is_viewed:Bool size:int32 covers:vector<sticker> = StickerSetInfo;
//@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;
@ -2576,6 +2611,18 @@ diceStickersSlotMachine background:sticker lever:sticker left_reel:sticker cente
importedContacts user_ids:vector<int53> importer_count:vector<int32> = ImportedContacts;
//@class SpeechRecognitionResult @description Describes result of speech recognition in a voice note
//@description The speech recognition is ongoing @partial_text Partially recognized text
speechRecognitionResultPending partial_text:string = SpeechRecognitionResult;
//@description The speech recognition successfully finished @text Recognized text
speechRecognitionResultText text:string = SpeechRecognitionResult;
//@description The speech recognition failed @error Received error
speechRecognitionResultError error:error = SpeechRecognitionResult;
//@description Describes a color to highlight a bot added to attachment menu @light_color Color in the RGB24 format for light themes @dark_color Color in the RGB24 format for dark themes
attachmentMenuBotColor light_color:int32 dark_color:int32 = AttachmentMenuBotColor;
@ -2731,7 +2778,7 @@ inlineQueryResults inline_query_id:int64 next_offset:string results:vector<Inlin
//@description The payload for a general callback button @data Data that was attached to the callback button
callbackQueryPayloadData data:bytes = CallbackQueryPayload;
//@description The payload for a callback button requiring password @password The password for the current user @data Data that was attached to the callback button
//@description The payload for a callback button requiring password @password The 2-step verification password for the current user @data Data that was attached to the callback button
callbackQueryPayloadDataWithPassword password:string data:bytes = CallbackQueryPayload;
//@description The payload for a game callback button @game_short_name A short name of the game that was attached to the callback button
@ -2972,6 +3019,9 @@ premiumFeatureUniqueReactions = PremiumFeature;
//@description Allowed to use premium stickers with unique effects
premiumFeatureUniqueStickers = PremiumFeature;
//@description Allowed to use custom emoji stickers in message texts and captions
premiumFeatureCustomEmoji = 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;
@ -3019,6 +3069,15 @@ premiumFeaturePromotionAnimation feature:PremiumFeature animation:animation = Pr
premiumState state:formattedText currency:string monthly_amount:int53 animations:vector<premiumFeaturePromotionAnimation> = PremiumState;
//@class StorePaymentPurpose @description Describes a purpose of an in-store payment
//@description The user subscribed to Telegram Premium @is_restore Pass true if this is a restore of a Telegram Premium purchase; only for App Store
storePaymentPurposePremiumSubscription is_restore:Bool = StorePaymentPurpose;
//@description The user gifted Telegram Premium to another user @user_id Identifier of the user for which Premium was gifted @currency ISO 4217 currency code of the payment currency @amount Paid amount, in the smallest units of the currency
storePaymentPurposeGiftedPremium user_id:int53 currency:string amount:int53 = StorePaymentPurpose;
//@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
@ -3448,6 +3507,9 @@ userPrivacySettingAllowPeerToPeerCalls = UserPrivacySetting;
//@description A privacy setting for managing whether the user can be found by their phone number. Checked only if the phone number is not known to the other user. Can be set only to "Allow contacts" or "Allow all"
userPrivacySettingAllowFindingByPhoneNumber = UserPrivacySetting;
//@description A privacy setting for managing whether the user can receive voice and video messages in private chats
userPrivacySettingAllowPrivateVoiceAndVideoNoteMessages = UserPrivacySetting;
//@description Contains information about the period of inactivity after which the current user's account will automatically be deleted @days Number of days of inactivity before the account will be flagged for deletion; 30-366 days
accountTtl days:int32 = AccountTtl;
@ -3509,7 +3571,7 @@ sessionTypeXbox = SessionType;
//@description Contains information about one session in a Telegram application used by the current user. Sessions must be shown to the user in the returned order
//@id Session identifier @is_current True, if this session is the current session
//@is_password_pending True, if a password is needed to complete authorization of the session
//@is_password_pending True, if a 2-step verification password is needed to complete authorization of the session
//@can_accept_secret_chats True, if incoming secret chats can be accepted by the session
//@can_accept_calls True, if incoming calls can be accepted by the session
//@type Session type based on the system and application version, which can be used to display a corresponding icon
@ -3684,6 +3746,9 @@ 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 forces restore of App Store purchases when opened. For official iOS application only
internalLinkTypeRestorePurchases = InternalLinkType;
//@description The link is a link to application settings
internalLinkTypeSettings = InternalLinkType;
@ -3992,8 +4057,9 @@ proxies proxies:vector<proxy> = Proxies;
//@description A sticker to be added to a sticker set
//@sticker File with the sticker; must fit in a 512x512 square. For WEBP stickers and masks the file must be in PNG format, which will be converted to WEBP server-side. Otherwise, the file must be local or uploaded within a week. See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements
//@emojis Emojis corresponding to the sticker
//@type Sticker type
inputSticker sticker:InputFile emojis:string type:StickerType = InputSticker;
//@format Sticker format
//@mask_position Position where the mask is placed; pass null if not specified
inputSticker sticker:InputFile emojis:string format:StickerFormat mask_position:maskPosition = InputSticker;
//@description Represents a date range @start_date Point in time (Unix timestamp) at which the date range begins @end_date Point in time (Unix timestamp) at which the date range ends
@ -4376,11 +4442,11 @@ updateOption name:string value:OptionValue = Update;
//@description A sticker set has changed @sticker_set The sticker set
updateStickerSet sticker_set:stickerSet = Update;
//@description The list of installed sticker sets was updated @is_masks True, if the list of installed mask sticker sets was updated @sticker_set_ids The new list of installed ordinary sticker sets
updateInstalledStickerSets is_masks:Bool sticker_set_ids:vector<int64> = Update;
//@description The list of installed sticker sets was updated @sticker_type Type of the affected stickers @sticker_set_ids The new list of installed ordinary sticker sets
updateInstalledStickerSets sticker_type:StickerType 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:trendingStickerSets = Update;
//@description The list of trending sticker sets was updated or some of them were viewed @sticker_type Type of the affected stickers @sticker_sets The prefix of the list of trending sticker sets with the newest trending sticker sets
updateTrendingStickerSets sticker_type:StickerType 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;
@ -4552,17 +4618,17 @@ requestQrCodeAuthentication other_user_ids:vector<int53> = Ok;
//@first_name The first name of the user; 1-64 characters @last_name The last name of the user; 0-64 characters
registerUser first_name:string last_name:string = Ok;
//@description Checks the authentication password for correctness. Works only when the current authorization state is authorizationStateWaitPassword @password The password to check
//@description Checks the 2-step verification password for correctness. Works only when the current authorization state is authorizationStateWaitPassword @password The 2-step verification password to check
checkAuthenticationPassword password:string = Ok;
//@description Requests to send a password recovery code to an email address that was previously set up. Works only when the current authorization state is authorizationStateWaitPassword
//@description Requests to send a 2-step verification password recovery code to an email address that was previously set up. Works only when the current authorization state is authorizationStateWaitPassword
requestAuthenticationPasswordRecovery = Ok;
//@description Checks whether a password recovery code sent to an email address is valid. Works only when the current authorization state is authorizationStateWaitPassword @recovery_code Recovery code to check
//@description Checks whether a 2-step verification password recovery code sent to an email address is valid. Works only when the current authorization state is authorizationStateWaitPassword @recovery_code Recovery code to check
checkAuthenticationPasswordRecoveryCode recovery_code:string = Ok;
//@description Recovers the password with a password recovery code sent to an email address that was previously set up. Works only when the current authorization state is authorizationStateWaitPassword
//@recovery_code Recovery code to check @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty
//@description Recovers the 2-step verification password with a password recovery code sent to an email address that was previously set up. Works only when the current authorization state is authorizationStateWaitPassword
//@recovery_code Recovery code to check @new_password New 2-step verification password of the user; may be empty to remove the password @new_hint New password hint; may be empty
recoverAuthenticationPassword recovery_code:string new_password:string new_hint:string = Ok;
//@description Checks the authentication token of a bot; to log in as a bot. Works only when the current authorization state is authorizationStateWaitPhoneNumber. Can be used instead of setAuthenticationPhoneNumber and checkAuthenticationCode to log in @token The bot token
@ -4593,15 +4659,15 @@ setDatabaseEncryptionKey new_encryption_key:bytes = Ok;
//@description Returns the current state of 2-step verification
getPasswordState = PasswordState;
//@description Changes the password for the current user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed
//@old_password Previous password of the user @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true to change also the recovery email address @new_recovery_email_address New recovery email address; may be empty
//@description Changes the 2-step verification password for the current user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed
//@old_password Previous 2-step verification password of the user @new_password New 2-step verification password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true to change also the recovery email address @new_recovery_email_address New recovery email address; may be empty
setPassword old_password:string new_password:string new_hint:string set_recovery_email_address:Bool new_recovery_email_address:string = PasswordState;
//@description Returns a 2-step verification recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The password for the current user
//@description Returns a 2-step verification recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The 2-step verification password for the current user
getRecoveryEmailAddress password:string = RecoveryEmailAddress;
//@description Changes the 2-step verification recovery email address of the user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed.
//-If new_recovery_email_address is the same as the email address that is currently set up, this call succeeds immediately and aborts all other requests waiting for an email confirmation @password Password of the current user @new_recovery_email_address New recovery email address
//-If new_recovery_email_address is the same as the email address that is currently set up, this call succeeds immediately and aborts all other requests waiting for an email confirmation @password The 2-step verification password of the current user @new_recovery_email_address New recovery email address
setRecoveryEmailAddress password:string new_recovery_email_address:string = PasswordState;
//@description Checks the 2-step verification recovery email address verification code @code Verification code to check
@ -4617,7 +4683,7 @@ requestPasswordRecovery = EmailAddressAuthenticationCodeInfo;
checkPasswordRecoveryCode recovery_code:string = Ok;
//@description Recovers the 2-step verification password using a recovery code sent to an email address that was previously set up
//@recovery_code Recovery code to check @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty
//@recovery_code Recovery code to check @new_password New 2-step verification password of the user; may be empty to remove the password @new_hint New password hint; may be empty
recoverPassword recovery_code:string new_password:string new_hint:string = PasswordState;
//@description Removes 2-step verification password without previous password and access to recovery email address. The password can't be reset immediately and the request needs to be repeated after the specified time
@ -4626,7 +4692,7 @@ resetPassword = ResetPasswordResult;
//@description Cancels reset of 2-step verification password. The method can be called if passwordState.pending_reset_date > 0
cancelPasswordReset = Ok;
//@description Creates a new temporary password for processing payments @password Persistent user password @valid_for Time during which the temporary password will be valid, in seconds; must be between 60 and 86400
//@description Creates a new temporary password for processing payments @password The 2-step verification password of the current user @valid_for Time during which the temporary password will be valid, in seconds; must be between 60 and 86400
createTemporaryPassword password:string valid_for:int32 = TemporaryPasswordState;
//@description Returns information about the current temporary password
@ -5058,7 +5124,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. The method will return Premium reactions, even the current user has no Premium subscription
//@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
//@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;
@ -5082,7 +5148,7 @@ getMessageAddedReactions chat_id:int53 message_id:int53 reaction:string offset:s
//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) contained in the text. Can be called synchronously @text The text in which to look for entites
getTextEntities text:string = TextEntities;
//@description Parses Bold, Italic, Underline, Strikethrough, Spoiler, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
//@description Parses Bold, Italic, Underline, Strikethrough, Spoiler, CustomEmoji, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
parseTextEntities text:string parse_mode:TextParseMode = FormattedText;
//@description Parses Markdown entities in a human-friendly format, ignoring markup errors. Can be called synchronously
@ -5358,7 +5424,7 @@ setChatTheme chat_id:int53 theme_name:string = Ok;
setChatDraftMessage chat_id:int53 message_thread_id:int53 draft_message:draftMessage = Ok;
//@description Changes the notification settings of a chat. Notification settings of a chat with the current user (Saved Messages) can't be changed
//@chat_id Chat identifier @notification_settings New notification settings for the chat. If the chat is muted for more than 1 week, it is considered to be muted forever
//@chat_id Chat identifier @notification_settings New notification settings for the chat. If the chat is muted for more than 366 days, it is considered to be muted forever
setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok;
//@description Changes the ability of users to save, forward, or copy chat content. Supported only for basic groups, supergroups and channels. Requires owner privileges
@ -5433,7 +5499,7 @@ banChatMember chat_id:int53 member_id:MessageSender banned_until_date:int32 revo
canTransferOwnership = CanTransferOwnershipResult;
//@description Changes the owner of a chat. The current user must be a current owner of the chat. Use the method canTransferOwnership to check whether the ownership can be transferred from the current session. Available only for supergroups and channel chats
//@chat_id Chat identifier @user_id Identifier of the user to which transfer the ownership. The ownership can't be transferred to a bot or to a deleted user @password The password of the current user
//@chat_id Chat identifier @user_id Identifier of the user to which transfer the ownership. The ownership can't be transferred to a bot or to a deleted user @password The 2-step verification password of the current user
transferChatOwnership chat_id:int53 user_id:int53 password:string = Ok;
//@description Returns information about a single member of a chat @chat_id Chat identifier @member_id Member identifier
@ -5514,14 +5580,14 @@ cancelDownloadFile file_id:int32 only_if_pending:Bool = Ok;
//@description Returns suggested name for saving a file in a given directory @file_id Identifier of the file @directory Directory in which the file is supposed to be saved
getSuggestedFileName file_id:int32 directory:string = Text;
//@description Asynchronously uploads a file to the cloud without sending it in a message. updateFile will be used to notify about upload progress and successful completion of the upload. The file will not have a persistent remote identifier until it will be sent in a message
//@description Preliminary uploads a file to the cloud before sending it in a message, which can be useful for uploading of being recorded voice and video notes. Updates updateFile will be used to notify about upload progress and successful completion of the upload. The file will not have a persistent remote identifier until it will be sent in a message
//@file File to upload
//@file_type File type; pass null if unknown
//@priority Priority of the upload (1-32). The higher the priority, the earlier the file will be uploaded. If the priorities of two files are equal, then the first one for which uploadFile was called will be uploaded first
uploadFile file:InputFile file_type:FileType priority:int32 = File;
//@priority Priority of the upload (1-32). The higher the priority, the earlier the file will be uploaded. If the priorities of two files are equal, then the first one for which preliminaryUploadFile was called will be uploaded first
preliminaryUploadFile file:InputFile file_type:FileType priority:int32 = File;
//@description Stops the uploading of a file. Supported only for files uploaded by using uploadFile. For other files the behavior is undefined @file_id Identifier of the file to stop uploading
cancelUploadFile file_id:int32 = Ok;
//@description Stops the preliminary uploading of a file. Supported only for files uploaded by using preliminaryUploadFile. For other files the behavior is undefined @file_id Identifier of the file to stop uploading
cancelPreliminaryUploadFile 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
@ -5871,22 +5937,30 @@ sharePhoneNumber user_id:int53 = Ok;
getUserProfilePhotos user_id:int53 offset:int32 limit:int32 = ChatPhotos;
//@description Returns stickers from the installed sticker sets that correspond to a given emoji. If the emoji is non-empty, favorite and recently used stickers may also be returned @emoji String representation of emoji. If empty, returns all known installed stickers @limit The maximum number of stickers to be returned
getStickers emoji:string limit:int32 = Stickers;
//@description Returns stickers from the installed sticker sets that correspond to a given emoji. If the emoji is non-empty, then favorite, recently used or trending stickers may also be returned
//@sticker_type Type of the sticker sets to return
//@emoji String representation of emoji. If empty, returns all known installed stickers
//@limit The maximum number of stickers to be returned
//@chat_id Chat identifier for which to return stickers. Available custom emoji may be different for different chats
getStickers sticker_type:StickerType emoji:string limit:int32 chat_id:int53 = Stickers;
//@description Searches for stickers from public sticker sets that correspond to a given emoji @emoji String representation of emoji; must be non-empty @limit The maximum number of stickers to be returned
//@description Searches for stickers from public sticker sets that correspond to a given emoji @emoji String representation of emoji; must be non-empty @limit The maximum number of stickers to be returned; 0-100
searchStickers emoji:string limit:int32 = Stickers;
//@description Returns a list of installed sticker sets @is_masks Pass true to return mask sticker sets; pass false to return ordinary sticker sets
getInstalledStickerSets is_masks:Bool = StickerSets;
//@description Returns premium stickers from regular sticker sets @limit The maximum number of stickers to be returned; 0-100
getPremiumStickers limit:int32 = Stickers;
//@description Returns a list of archived sticker sets @is_masks Pass true to return mask stickers sets; pass false to return ordinary sticker sets @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return; up to 100
getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = StickerSets;
//@description Returns a list of installed sticker sets @sticker_type Type of the sticker sets to return
getInstalledStickerSets sticker_type:StickerType = StickerSets;
//@description Returns a list of archived sticker sets @sticker_type Type of the sticker sets to return @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return; up to 100
getArchivedStickerSets sticker_type:StickerType offset_sticker_set_id:int64 limit:int32 = StickerSets;
//@description Returns a list of trending sticker sets. For optimal performance, the number of returned sticker sets is chosen by TDLib
//@sticker_type Type of the sticker sets to return
//@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 = TrendingStickerSets;
getTrendingStickerSets sticker_type:StickerType 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;
@ -5897,8 +5971,8 @@ getStickerSet set_id:int64 = StickerSet;
//@description Searches for a sticker set by its name @name Name of the sticker set
searchStickerSet name:string = StickerSet;
//@description Searches for installed sticker sets by looking for specified query in their title and name @is_masks Pass true to return mask sticker sets; pass false to return ordinary sticker sets @query Query to search for @limit The maximum number of sticker sets to return
searchInstalledStickerSets is_masks:Bool query:string limit:int32 = StickerSets;
//@description Searches for installed sticker sets by looking for specified query in their title and name @sticker_type Type of the sticker sets to search for @query Query to search for @limit The maximum number of sticker sets to return
searchInstalledStickerSets sticker_type:StickerType query:string limit:int32 = StickerSets;
//@description Searches for ordinary sticker sets by looking for specified query in their title and name. Excludes installed sticker sets from the results @query Query to search for
searchStickerSets query:string = StickerSets;
@ -5909,13 +5983,13 @@ changeStickerSet set_id:int64 is_installed:Bool is_archived:Bool = Ok;
//@description Informs the server that some trending sticker sets have been viewed by the user @sticker_set_ids Identifiers of viewed trending sticker sets
viewTrendingStickerSets sticker_set_ids:vector<int64> = Ok;
//@description Changes the order of installed sticker sets @is_masks Pass true to change the order of mask sticker sets; pass false to change the order of ordinary sticker sets @sticker_set_ids Identifiers of installed sticker sets in the new correct order
reorderInstalledStickerSets is_masks:Bool sticker_set_ids:vector<int64> = Ok;
//@description Changes the order of installed sticker sets @sticker_type Type of the sticker sets to reorder @sticker_set_ids Identifiers of installed sticker sets in the new correct order
reorderInstalledStickerSets sticker_type:StickerType sticker_set_ids:vector<int64> = Ok;
//@description Returns a list of recently used stickers @is_attached Pass true to return stickers and masks that were recently attached to photos or video files; pass false to return recently sent stickers
getRecentStickers is_attached:Bool = Stickers;
//@description Manually adds a new sticker to the list of recently used stickers. The new sticker is added to the top of the list. If the sticker was already in the list, it is removed from the list first. Only stickers belonging to a sticker set can be added to this list
//@description Manually adds a new sticker to the list of recently used stickers. The new sticker is added to the top of the list. If the sticker was already in the list, it is removed from the list first. Only stickers belonging to a sticker set can be added to this list. Emoji stickers can't be added to recent stickers
//@is_attached Pass true to add the sticker to the list of stickers recently attached to photo or video files; pass false to add the sticker to the list of recently sent stickers @sticker Sticker file to add
addRecentSticker is_attached:Bool sticker:InputFile = Stickers;
@ -5928,7 +6002,7 @@ clearRecentStickers is_attached:Bool = Ok;
//@description Returns favorite stickers
getFavoriteStickers = Stickers;
//@description Adds a new sticker to the list of favorite stickers. The new sticker is added to the top of the list. If the sticker was already in the list, it is removed from the list first. Only stickers belonging to a sticker set can be added to this list
//@description Adds a new sticker to the list of favorite stickers. The new sticker is added to the top of the list. If the sticker was already in the list, it is removed from the list first. Only stickers belonging to a sticker set can be added to this list. Emoji stickers can't be added to favorite stickers
//@sticker Sticker file to add
addFavoriteSticker sticker:InputFile = Ok;
@ -5944,12 +6018,12 @@ 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;
//@description Returns list of custom emoji stickers by their identifiers. Stickers are returned in arbitrary order. Only found stickers are returned @custom_emoji_ids Identifiers of custom emoji stickers. At most 200 custom emoji stickers can be received simultaneously
getCustomEmojiStickers custom_emoji_ids:vector<int64> = Stickers;
//@description Returns saved animations
getSavedAnimations = Animations;
@ -6232,8 +6306,8 @@ setAccountTtl ttl:accountTtl = Ok;
//@description Returns the period of inactivity after which the account of the current user will automatically be deleted
getAccountTtl = AccountTtl;
//@description Deletes the account of the current user, deleting all information associated with the user from the server. The phone number of the account can be used to create a new account. Can be called before authorization when the current authorization state is authorizationStateWaitPassword @reason The reason why the account was deleted; optional
deleteAccount reason:string = Ok;
//@description Deletes the account of the current user, deleting all information associated with the user from the server. The phone number of the account can be used to create a new account. Can be called before authorization when the current authorization state is authorizationStateWaitPassword @reason The reason why the account was deleted; optional @password The 2-step verification password of the current user. If not specified, account deletion can be canceled within one week
deleteAccount reason:string password:string = Ok;
//@description Removes a chat action bar without any other action @chat_id Chat identifier
@ -6307,13 +6381,13 @@ setAutoDownloadSettings settings:autoDownloadSettings type:NetworkType = Ok;
getBankCardInfo bank_card_number:string = BankCardInfo;
//@description Returns one of the available Telegram Passport elements @type Telegram Passport element type @password Password of the current user
//@description Returns one of the available Telegram Passport elements @type Telegram Passport element type @password The 2-step verification password of the current user
getPassportElement type:PassportElementType password:string = PassportElement;
//@description Returns all available Telegram Passport elements @password Password of the current user
//@description Returns all available Telegram Passport elements @password The 2-step verification password of the current user
getAllPassportElements password:string = PassportElements;
//@description Adds an element to the user's Telegram Passport. May return an error with a message "PHONE_VERIFICATION_NEEDED" or "EMAIL_VERIFICATION_NEEDED" if the chosen phone number or the chosen email address must be verified first @element Input Telegram Passport element @password Password of the current user
//@description Adds an element to the user's Telegram Passport. May return an error with a message "PHONE_VERIFICATION_NEEDED" or "EMAIL_VERIFICATION_NEEDED" if the chosen phone number or the chosen email address must be verified first @element Input Telegram Passport element @password The 2-step verification password of the current user
setPassportElement element:InputPassportElement password:string = PassportElement;
//@description Deletes a Telegram Passport element @type Element type
@ -6351,7 +6425,7 @@ checkEmailAddressVerificationCode code:string = Ok;
//@description Returns a Telegram Passport authorization form for sharing data with a service @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
getPassportAuthorizationForm bot_user_id:int53 scope:string public_key:string nonce:string = PassportAuthorizationForm;
//@description Returns already available Telegram Passport elements suitable for completing a Telegram Passport authorization form. Result can be received only once for each authorization form @autorization_form_id Authorization form identifier @password Password of the current user
//@description Returns already available Telegram Passport elements suitable for completing a Telegram Passport authorization form. Result can be received only once for each authorization form @autorization_form_id Authorization form identifier @password The 2-step verification password of the current user
getPassportAuthorizationFormAvailableElements autorization_form_id:int32 password:string = PassportElementsWithErrors;
//@description Sends a Telegram Passport authorization form, effectively sharing data with the service. This method must be called after getPassportAuthorizationFormAvailableElements if some previously available elements are going to be reused
@ -6386,9 +6460,10 @@ checkStickerSetName name:string = CheckStickerSetNameResult;
//@user_id Sticker set owner; ignored for regular users
//@title Sticker set title; 1-64 characters
//@name Sticker set name. Can contain only English letters, digits and underscores. Must end with *"_by_<bot username>"* (*<bot_username>* is case insensitive) for bots; 1-64 characters
//@sticker_type Type of the stickers in the set
//@stickers List of stickers to be added to the set; must be non-empty. All stickers must have the same format. For TGS stickers, uploadStickerFile must be used before the sticker is shown
//@source Source of the sticker set; may be empty if unknown
createNewStickerSet user_id:int53 title:string name:string stickers:vector<inputSticker> source:string = StickerSet;
createNewStickerSet user_id:int53 title:string name:string sticker_type:StickerType stickers:vector<inputSticker> source:string = StickerSet;
//@description Adds a new sticker to a set; for bots only. Returns the sticker set
//@user_id Sticker set owner @name Sticker set name @sticker Sticker to add to the set
@ -6418,7 +6493,7 @@ getPremiumLimit limit_type:PremiumLimitType = PremiumLimit;
getPremiumFeatures source:PremiumSource = PremiumFeatures;
//@description Returns examples of premium stickers for demonstration purposes
getPremiumStickers = Stickers;
getPremiumStickerExamples = 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;
@ -6429,14 +6504,14 @@ clickPremiumSubscriptionButton = Ok;
//@description Returns state of Telegram Premium subscription and promotion videos for Premium features
getPremiumState = PremiumState;
//@description Checks whether Telegram Premium purchase is possible. Must be called before in-store Premium purchase
canPurchasePremium = Ok;
//@description Checks whether Telegram Premium purchase is possible. Must be called before in-store Premium purchase @purpose Transaction purpose
canPurchasePremium purpose:StorePaymentPurpose = Ok;
//@description Informs server about a Telegram Premium purchase through App Store. For official applications only @receipt App Store receipt @is_restore Pass true if this is a restore of a Telegram Premium purchase
assignAppStoreTransaction receipt:bytes is_restore:Bool = Ok;
//@description Informs server about a purchase through App Store. For official applications only @receipt App Store receipt @purpose Transaction purpose
assignAppStoreTransaction receipt:bytes purpose:StorePaymentPurpose = Ok;
//@description Informs server about a Telegram Premium purchase through Google Play. For official applications only @purchase_token Google Play purchase token
assignGooglePlayTransaction purchase_token:string = Ok;
//@description Informs server about a purchase through Google Play. For official applications only @package_name Application package name @store_product_id Identifier of the purchased store product @purchase_token Google Play purchase token @purpose Transaction purpose
assignGooglePlayTransaction package_name:string store_product_id:string purchase_token:string purpose:StorePaymentPurpose = Ok;
//@description Accepts Telegram terms of services @terms_of_service_id Terms of service identifier

View File

@ -182,6 +182,7 @@ messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -231,7 +232,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#8c72ea81 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights = UserFull;
userFull#c4b1fc3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@ -313,7 +314,7 @@ updateDeleteChannelMessages#c32d5b12 channel_id:long messages:Vector<int> pts:in
updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update;
updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update;
@ -382,6 +383,7 @@ 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;
updateReadFeaturedEmojiStickers#fb4c496c = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -484,6 +486,7 @@ inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey;
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey;
@ -493,6 +496,7 @@ privacyKeyForwards#69ec56a3 = PrivacyKey;
privacyKeyProfilePhoto#96151fed = PrivacyKey;
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
privacyKeyVoiceMessages#697f414 = PrivacyKey;
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
@ -523,6 +527,7 @@ documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_strea
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true alt:string stickerset:InputStickerSet = DocumentAttribute;
messages.stickersNotModified#f1749a22 = messages.Stickers;
messages.stickers#30a6ec7e hash:long stickers:Vector<Document> = messages.Stickers;
@ -566,8 +571,9 @@ inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
@ -619,6 +625,7 @@ messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
@ -745,6 +752,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = StickerSetCovered;
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
@ -833,10 +841,11 @@ inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
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.paymentForm#a0058751 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 additional_methods:flags.6?Vector<PaymentFormMethod> saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector<PaymentSavedCredentials> users:Vector<User> = payments.PaymentForm;
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
@ -1378,6 +1387,13 @@ messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id
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;
inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1421,7 +1437,7 @@ account.checkUsername#2714d86c username:string = Bool;
account.updateUsername#3e0bdd7c username:string = User;
account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules;
account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;
account.deleteAccount#418d4e0b reason:string = Bool;
account.deleteAccount#a2c0cf74 flags:# reason:string password:flags.0?InputCheckPasswordSRP = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
@ -1559,7 +1575,7 @@ messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool
messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = Bool;
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.reorderStickerSets#78337739 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Bool;
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;
@ -1579,7 +1595,7 @@ messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true offset_id:long limit:int = messages.ArchivedStickers;
messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true emojis:flags.1?true offset_id:long limit:int = messages.ArchivedStickers;
messages.getMaskStickers#640f82b8 hash:long = messages.AllStickers;
messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector<StickerSetCovered>;
messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates;
@ -1680,6 +1696,9 @@ messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInl
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;
messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;
messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers;
messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1783,9 +1802,9 @@ 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.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates;
payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates;
payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = 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;

View File

@ -57,13 +57,13 @@ bool TD_TL_writer::is_default_constructor_generated(const tl::tl_combinator *t,
bool TD_TL_writer::is_full_constructor_generated(const tl::tl_combinator *t, bool can_be_parsed,
bool can_be_stored) const {
return tl_name == "td_api" || tl_name == "TdApi" || can_be_stored || t->name == "phone.groupParticipants" ||
t->name == "user" || t->name == "userProfilePhoto" || t->name == "channelForbidden" ||
t->name == "user" || t->name == "userProfilePhoto" || t->name == "channelForbidden" || t->name == "message" ||
t->name == "photoSizeEmpty" || t->name == "photoSize" || t->name == "photoCachedSize" ||
t->name == "document" || t->name == "updateDeleteMessages" || t->name == "updateEditChannelMessage" ||
t->name == "encryptedChatWaiting" || t->name == "encryptedChatRequested" || t->name == "encryptedChat" ||
t->name == "langPackString" || t->name == "langPackStringPluralized" || t->name == "langPackStringDeleted" ||
t->name == "peerUser" || t->name == "peerChat" || t->name == "updateServiceNotification" ||
t->name == "updateNewMessage" || t->name == "message" || t->name == "updateChannelTooLong";
t->name == "updateNewMessage" || t->name == "updateChannelTooLong" || t->name == "messages.stickerSet";
}
int TD_TL_writer::get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const {

View File

@ -149,9 +149,9 @@ void AnimationsManager::tear_down() {
}
int32 AnimationsManager::get_animation_duration(FileId file_id) const {
auto it = animations_.find(file_id);
CHECK(it != animations_.end());
return it->second->duration;
const auto *animation = get_animation(file_id);
CHECK(animation != nullptr);
return animation->duration;
}
tl_object_ptr<td_api::animation> AnimationsManager::get_animation_object(FileId file_id) const {
@ -159,9 +159,7 @@ tl_object_ptr<td_api::animation> AnimationsManager::get_animation_object(FileId
return nullptr;
}
auto it = animations_.find(file_id);
CHECK(it != animations_.end());
auto animation = it->second.get();
auto animation = get_animation(file_id);
CHECK(animation != nullptr);
auto thumbnail =
animation->animated_thumbnail.file_id.is_valid()
@ -234,13 +232,7 @@ FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation,
}
const AnimationsManager::Animation *AnimationsManager::get_animation(FileId file_id) const {
auto animation = animations_.find(file_id);
if (animation == animations_.end()) {
return nullptr;
}
CHECK(animation->second->file_id == file_id);
return animation->second.get();
return animations_.get_pointer(file_id);
}
FileId AnimationsManager::get_animation_thumbnail_file_id(FileId file_id) const {
@ -276,7 +268,7 @@ FileId AnimationsManager::dup_animation(FileId new_id, FileId old_id) {
return new_id;
}
void AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_delete_old) {
void AnimationsManager::merge_animations(FileId new_id, FileId old_id) {
CHECK(old_id.is_valid() && new_id.is_valid());
CHECK(new_id != old_id);
@ -285,19 +277,10 @@ void AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_
CHECK(old_ != nullptr);
bool need_merge = true;
auto new_it = animations_.find(new_id);
if (new_it == animations_.end()) {
auto &old = animations_[old_id];
if (!can_delete_old) {
dup_animation(new_id, old_id);
} else {
old->file_id = new_id;
animations_.emplace(new_id, std::move(old));
}
const auto *new_ = get_animation(new_id);
if (new_ == nullptr) {
dup_animation(new_id, old_id);
} else {
Animation *new_ = new_it->second.get();
CHECK(new_ != nullptr);
if (old_->thumbnail != new_->thumbnail) {
// LOG_STATUS(td_->file_manager_->merge(new_->thumbnail.file_id, old_->thumbnail.file_id));
}
@ -308,9 +291,6 @@ void AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_
if (need_merge) {
LOG_STATUS(td_->file_manager_->merge(new_id, old_id));
}
if (can_delete_old) {
animations_.erase(old_id);
}
}
void AnimationsManager::create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail,

View File

@ -18,9 +18,9 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Promise.h"
#include "td/utils/Status.h"
#include "td/utils/WaitFreeHashMap.h"
namespace td {
@ -61,7 +61,7 @@ class AnimationsManager final : public Actor {
FileId dup_animation(FileId new_id, FileId old_id);
void merge_animations(FileId new_id, FileId old_id, bool can_delete_old);
void merge_animations(FileId new_id, FileId old_id);
void on_update_animation_search_emojis(string animation_search_emojis);
@ -147,7 +147,7 @@ class AnimationsManager final : public Actor {
Td *td_;
ActorShared<> parent_;
FlatHashMap<FileId, unique_ptr<Animation>, FileIdHash> animations_;
WaitFreeHashMap<FileId, unique_ptr<Animation>, FileIdHash> animations_;
int32 saved_animations_limit_ = 200;
vector<FileId> saved_animation_ids_;

View File

@ -21,9 +21,8 @@ namespace td {
template <class StorerT>
void AnimationsManager::store_animation(FileId file_id, StorerT &storer) const {
auto it = animations_.find(file_id);
CHECK(it != animations_.end());
const Animation *animation = it->second.get();
const Animation *animation = get_animation(file_id);
CHECK(animation != nullptr);
bool has_animated_thumbnail = animation->animated_thumbnail.file_id.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(animation->has_stickers);

View File

@ -12,6 +12,7 @@
#include "td/telegram/Dependencies.h"
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/Global.h"
@ -503,6 +504,21 @@ void AttachMenuManager::init() {
}
hash_ = is_cache_outdated ? 0 : attach_menu_bots_log_event.hash_;
attach_menu_bots_ = std::move(attach_menu_bots_log_event.attach_menu_bots_);
for (auto attach_menu_bot : attach_menu_bots_) {
auto file_source_id = get_attach_menu_bot_file_source_id(attach_menu_bot.user_id_);
auto register_file_source = [&](FileId file_id) {
if (file_id.is_valid()) {
td_->file_manager_->add_file_source(file_id, file_source_id);
}
};
register_file_source(attach_menu_bot.default_icon_file_id_);
register_file_source(attach_menu_bot.ios_static_icon_file_id_);
register_file_source(attach_menu_bot.ios_animated_icon_file_id_);
register_file_source(attach_menu_bot.android_icon_file_id_);
register_file_source(attach_menu_bot.macos_icon_file_id_);
register_file_source(attach_menu_bot.placeholder_file_id_);
}
} else {
LOG(ERROR) << "Ignore invalid attachment menu bots log event";
}
@ -649,12 +665,14 @@ void AttachMenuManager::close_web_view(int64 query_id, Promise<Unit> &&promise)
}
Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
tl_object_ptr<telegram_api::attachMenuBot> &&bot) const {
tl_object_ptr<telegram_api::attachMenuBot> &&bot) {
UserId user_id(bot->bot_id_);
if (!td_->contacts_manager_->have_user(user_id)) {
return Status::Error(PSLICE() << "Have no information about " << user_id);
}
auto file_source_id = get_attach_menu_bot_file_source_id(user_id);
AttachMenuBot attach_menu_bot;
attach_menu_bot.is_added_ = !bot->inactive_;
attach_menu_bot.user_id_ = user_id;
@ -704,6 +722,7 @@ Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
default:
UNREACHABLE();
}
td_->file_manager_->add_file_source(parsed_document.file_id, file_source_id);
if (expect_colors) {
if (icon->colors_.empty()) {
LOG(ERROR) << "Have no colors for attachment menu bot icon for " << user_id;
@ -882,6 +901,26 @@ void AttachMenuManager::get_attach_menu_bot(UserId user_id,
td_->create_handler<GetAttachMenuBotQuery>(std::move(query_promise))->send(std::move(input_user));
}
void AttachMenuManager::reload_attach_menu_bot(UserId user_id, Promise<Unit> &&promise) {
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
auto wrapped_promise = PromiseCreator::lambda(
[promise = std::move(promise)](Result<td_api::object_ptr<td_api::attachmentMenuBot>> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(Unit());
}
});
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), user_id, promise = std::move(wrapped_promise)](
Result<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&result) mutable {
send_closure(actor_id, &AttachMenuManager::on_get_attach_menu_bot, user_id, std::move(result),
std::move(promise));
});
td_->create_handler<GetAttachMenuBotQuery>(std::move(query_promise))->send(std::move(input_user));
}
void AttachMenuManager::on_get_attach_menu_bot(
UserId user_id, Result<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&result,
Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise) {
@ -921,6 +960,19 @@ void AttachMenuManager::on_get_attach_menu_bot(
promise.set_value(get_attachment_menu_bot_object(attach_menu_bot));
}
FileSourceId AttachMenuManager::get_attach_menu_bot_file_source_id(UserId user_id) {
if (!user_id.is_valid() || !is_active()) {
return FileSourceId();
}
auto &source_id = attach_menu_bot_file_source_ids_[user_id];
if (!source_id.is_valid()) {
source_id = td_->file_reference_manager_->create_attach_menu_bot_file_source(user_id);
}
VLOG(file_references) << "Return " << source_id << " for attach menu bot " << user_id;
return source_id;
}
void AttachMenuManager::toggle_bot_is_added_to_attach_menu(UserId user_id, bool is_added, Promise<Unit> &&promise) {
CHECK(is_active());

View File

@ -8,6 +8,7 @@
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -44,6 +45,10 @@ class AttachMenuManager final : public Actor {
void get_attach_menu_bot(UserId user_id, Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise);
void reload_attach_menu_bot(UserId user_id, Promise<Unit> &&promise);
FileSourceId get_attach_menu_bot_file_source_id(UserId user_id);
void toggle_bot_is_added_to_attach_menu(UserId user_id, bool is_added, Promise<Unit> &&promise);
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
@ -117,7 +122,7 @@ class AttachMenuManager final : public Actor {
void schedule_ping_web_view();
Result<AttachMenuBot> get_attach_menu_bot(tl_object_ptr<telegram_api::attachMenuBot> &&bot) const;
Result<AttachMenuBot> get_attach_menu_bot(tl_object_ptr<telegram_api::attachMenuBot> &&bot);
td_api::object_ptr<td_api::attachmentMenuBot> get_attachment_menu_bot_object(const AttachMenuBot &bot) const;
@ -144,6 +149,7 @@ class AttachMenuManager final : public Actor {
bool is_inited_ = false;
int64 hash_ = 0;
vector<AttachMenuBot> attach_menu_bots_;
FlatHashMap<UserId, FileSourceId, UserIdHash> attach_menu_bot_file_source_ids_;
struct OpenedWebView {
DialogId dialog_id_;

View File

@ -29,11 +29,11 @@ AudiosManager::~AudiosManager() {
}
int32 AudiosManager::get_audio_duration(FileId file_id) const {
auto it = audios_.find(file_id);
if (it == audios_.end()) {
const auto *audio = get_audio(file_id);
if (audio == nullptr) {
return 0;
}
return it->second->duration;
return audio->duration;
}
tl_object_ptr<td_api::audio> AudiosManager::get_audio_object(FileId file_id) const {
@ -41,14 +41,28 @@ tl_object_ptr<td_api::audio> AudiosManager::get_audio_object(FileId file_id) con
return nullptr;
}
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
auto audio = it->second.get();
auto audio = get_audio(file_id);
CHECK(audio != nullptr);
vector<td_api::object_ptr<td_api::thumbnail>> album_covers;
if (!td_->auth_manager_->is_bot()) {
auto add_album_cover = [&](bool is_small, int32 width, int32 height) {
auto r_file_id =
td_->file_manager_->get_audio_thumbnail_file_id(audio->title, audio->performer, is_small, DialogId());
if (r_file_id.is_ok()) {
album_covers.emplace_back(
td_api::make_object<td_api::thumbnail>(td_api::make_object<td_api::thumbnailFormatJpeg>(), width, height,
td_->file_manager_->get_file_object(r_file_id.move_as_ok())));
}
};
add_album_cover(true, 100, 100);
add_album_cover(false, 600, 600);
}
return make_tl_object<td_api::audio>(
audio->duration, audio->title, audio->performer, audio->file_name, audio->mime_type,
get_minithumbnail_object(audio->minithumbnail),
get_thumbnail_object(td_->file_manager_.get(), audio->thumbnail, PhotoFormat::Jpeg),
get_thumbnail_object(td_->file_manager_.get(), audio->thumbnail, PhotoFormat::Jpeg), std::move(album_covers),
td_->file_manager_->get_file_object(file_id));
}
@ -57,9 +71,7 @@ td_api::object_ptr<td_api::notificationSound> AudiosManager::get_notification_so
return nullptr;
}
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
auto audio = it->second.get();
auto audio = get_audio(file_id);
CHECK(audio != nullptr);
auto file_view = td_->file_manager_->get_file_view(file_id);
CHECK(!file_view.empty());
@ -118,13 +130,7 @@ FileId AudiosManager::on_get_audio(unique_ptr<Audio> new_audio, bool replace) {
}
const AudiosManager::Audio *AudiosManager::get_audio(FileId file_id) const {
auto audio = audios_.find(file_id);
if (audio == audios_.end()) {
return nullptr;
}
CHECK(audio->second->file_id == file_id);
return audio->second.get();
return audios_.get_pointer(file_id);
}
FileId AudiosManager::dup_audio(FileId new_id, FileId old_id) {
@ -138,7 +144,7 @@ FileId AudiosManager::dup_audio(FileId new_id, FileId old_id) {
return new_id;
}
void AudiosManager::merge_audios(FileId new_id, FileId old_id, bool can_delete_old) {
void AudiosManager::merge_audios(FileId new_id, FileId old_id) {
CHECK(old_id.is_valid() && new_id.is_valid());
CHECK(new_id != old_id);
@ -146,19 +152,10 @@ void AudiosManager::merge_audios(FileId new_id, FileId old_id, bool can_delete_o
const Audio *old_ = get_audio(old_id);
CHECK(old_ != nullptr);
auto new_it = audios_.find(new_id);
if (new_it == audios_.end()) {
auto &old = audios_[old_id];
if (!can_delete_old) {
dup_audio(new_id, old_id);
} else {
old->file_id = new_id;
audios_.emplace(new_id, std::move(old));
}
const auto *new_ = get_audio(new_id);
if (new_ == nullptr) {
dup_audio(new_id, old_id);
} else {
Audio *new_ = new_it->second.get();
CHECK(new_ != nullptr);
if (!old_->mime_type.empty() && old_->mime_type != new_->mime_type) {
LOG(INFO) << "Audio has changed: mime_type = (" << old_->mime_type << ", " << new_->mime_type << ")";
}
@ -168,9 +165,6 @@ void AudiosManager::merge_audios(FileId new_id, FileId old_id, bool can_delete_o
}
}
LOG_STATUS(td_->file_manager_->merge(new_id, old_id));
if (can_delete_old) {
audios_.erase(old_id);
}
}
string AudiosManager::get_audio_search_text(FileId file_id) const {
@ -185,6 +179,25 @@ FileId AudiosManager::get_audio_thumbnail_file_id(FileId file_id) const {
return audio->thumbnail.file_id;
}
void AudiosManager::append_audio_album_cover_file_ids(FileId file_id, vector<FileId> &file_ids) const {
if (td_->auth_manager_->is_bot()) {
return;
}
auto audio = get_audio(file_id);
CHECK(audio != nullptr);
auto append_album_cover = [&](bool is_small) {
auto r_file_id =
td_->file_manager_->get_audio_thumbnail_file_id(audio->title, audio->performer, is_small, DialogId());
if (r_file_id.is_ok()) {
file_ids.push_back(r_file_id.ok());
}
};
append_album_cover(true);
append_album_cover(false);
}
void AudiosManager::delete_audio_thumbnail(FileId file_id) {
auto &audio = audios_[file_id];
CHECK(audio != nullptr);

View File

@ -14,7 +14,7 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/WaitFreeHashMap.h"
namespace td {
@ -50,11 +50,13 @@ class AudiosManager {
FileId get_audio_thumbnail_file_id(FileId file_id) const;
void append_audio_album_cover_file_ids(FileId file_id, vector<FileId> &file_ids) const;
void delete_audio_thumbnail(FileId file_id);
FileId dup_audio(FileId new_id, FileId old_id);
void merge_audios(FileId new_id, FileId old_id, bool can_delete_old);
void merge_audios(FileId new_id, FileId old_id);
template <class StorerT>
void store_audio(FileId file_id, StorerT &storer) const;
@ -84,7 +86,7 @@ class AudiosManager {
FileId on_get_audio(unique_ptr<Audio> new_audio, bool replace);
Td *td_;
FlatHashMap<FileId, unique_ptr<Audio>, FileIdHash> audios_;
WaitFreeHashMap<FileId, unique_ptr<Audio>, FileIdHash> audios_;
};
} // namespace td

View File

@ -21,9 +21,8 @@ namespace td {
template <class StorerT>
void AudiosManager::store_audio(FileId file_id, StorerT &storer) const {
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
const Audio *audio = it->second.get();
const Audio *audio = get_audio(file_id);
CHECK(audio != nullptr);
bool has_file_name = !audio->file_name.empty();
bool has_mime_type = !audio->mime_type.empty();
bool has_duration = audio->duration != 0;

View File

@ -394,14 +394,37 @@ void AuthManager::send_log_out_query() {
start_net_query(NetQueryType::LogOut, std::move(query));
}
void AuthManager::delete_account(uint64 query_id, const string &reason) {
void AuthManager::delete_account(uint64 query_id, string reason, string password) {
if (state_ != State::Ok && state_ != State::WaitPassword) {
return on_query_error(query_id, Status::Error(400, "Need to log in first"));
}
if (password.empty() || state_ != State::Ok) {
on_new_query(query_id);
LOG(INFO) << "Deleting account";
start_net_query(NetQueryType::DeleteAccount,
G()->net_query_creator().create_unauth(telegram_api::account_deleteAccount(0, reason, nullptr)));
} else {
send_closure(G()->password_manager(), &PasswordManager::get_input_check_password_srp, password,
PromiseCreator::lambda(
[actor_id = actor_id(this), query_id, reason = std::move(reason)](
Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password) mutable {
send_closure(actor_id, &AuthManager::do_delete_account, query_id, std::move(reason),
std::move(r_input_password));
}));
}
}
void AuthManager::do_delete_account(uint64 query_id, string reason,
Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password) {
if (r_input_password.is_error()) {
return on_query_error(query_id, r_input_password.move_as_error());
}
on_new_query(query_id);
LOG(INFO) << "Deleting account";
start_net_query(NetQueryType::DeleteAccount,
G()->net_query_creator().create_unauth(telegram_api::account_deleteAccount(reason)));
LOG(INFO) << "Deleting account with password";
int32 flags = telegram_api::account_deleteAccount::PASSWORD_MASK;
start_net_query(NetQueryType::DeleteAccount, G()->net_query_creator().create(telegram_api::account_deleteAccount(
flags, reason, r_input_password.move_as_ok())));
}
void AuthManager::on_closing(bool destroy_flag) {

View File

@ -45,7 +45,7 @@ class AuthManager final : public NetActor {
void check_password_recovery_code(uint64 query_id, string code);
void recover_password(uint64 query_id, string code, string new_password, string new_hint);
void log_out(uint64 query_id);
void delete_account(uint64 query_id, const string &reason);
void delete_account(uint64 query_id, string reason, string password);
void on_update_login_token();
@ -224,6 +224,9 @@ class AuthManager final : public NetActor {
void send_export_login_token_query();
void set_login_token_expires_at(double login_token_expires_at);
void do_delete_account(uint64 query_id, string reason,
Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password);
void send_log_out_query();
void destroy_auth_keys();

View File

@ -352,10 +352,11 @@ void CallActor::on_save_debug_query_result(Result<NetQueryPtr> r_net_query) {
}
void CallActor::send_call_log(td_api::object_ptr<td_api::InputFile> log_file, Promise<Unit> promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
if (!call_state_.need_log) {
return promise.set_error(Status::Error(400, "Unexpected sendCallLog"));
}
TRY_STATUS_PROMISE(promise, G()->close_status());
auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
auto r_file_id = file_manager->get_input_file_id(FileType::CallLog, log_file, DialogId(), false, false);
@ -418,18 +419,19 @@ void CallActor::upload_log_file(FileId file_id, Promise<Unit> &&promise) {
void CallActor::on_upload_log_file(FileId file_id, Promise<Unit> &&promise,
tl_object_ptr<telegram_api::InputFile> input_file) {
LOG(INFO) << "Log file " << file_id << " has been uploaded";
TRY_STATUS_PROMISE(promise, G()->close_status());
LOG(INFO) << "Log file " << file_id << " has been uploaded";
do_upload_log_file(file_id, std::move(input_file), std::move(promise));
}
void CallActor::on_upload_log_file_error(FileId file_id, Promise<Unit> &&promise, Status status) {
TRY_STATUS_PROMISE(promise, G()->close_status());
LOG(WARNING) << "Log file " << file_id << " has upload error " << status;
CHECK(status.is_error());
TRY_STATUS_PROMISE(promise, G()->close_status());
promise.set_error(Status::Error(status.code() > 0 ? status.code() : 500,
status.message())); // TODO CHECK that status has always a code
}

View File

@ -781,6 +781,11 @@ class ConfigRecoverer final : public Actor {
if (close_flag_) {
return;
}
if (Session::is_high_loaded()) {
VLOG(config_recoverer) << "Skip config recoverer under high load";
set_timeout_in(Random::fast(200, 300));
return;
}
if (is_connecting_) {
VLOG(config_recoverer) << "Failed to connect for " << Time::now() - connecting_since_;
@ -1387,8 +1392,8 @@ void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
shared_config.set_option_integer("call_packet_timeout_ms", config->call_packet_timeout_ms_);
shared_config.set_option_integer("call_receive_timeout_ms", config->call_receive_timeout_ms_);
shared_config.set_option_integer("message_text_length_max", config->message_length_max_);
shared_config.set_option_integer("message_caption_length_max", config->caption_length_max_);
shared_config.set_option_integer("message_text_length_max", clamp(config->message_length_max_, 4096, 1000000));
shared_config.set_option_integer("message_caption_length_max", clamp(config->caption_length_max_, 1024, 1000000));
if (config->gif_search_username_.empty()) {
shared_config.set_option_empty("animation_search_bot_username");
@ -1489,9 +1494,10 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
Slice key = key_value->key_;
telegram_api::JSONValue *value = key_value->value_.get();
if (key == "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") {
if (key == "getfile_experimental_params" || 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") {

View File

@ -39,6 +39,7 @@
#include "td/telegram/PasswordManager.h"
#include "td/telegram/Photo.h"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PremiumGiftOption.hpp"
#include "td/telegram/SecretChatLayer.h"
#include "td/telegram/SecretChatsManager.h"
#include "td/telegram/ServerMessageId.h"
@ -3401,8 +3402,8 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent
ContactsManager::~ContactsManager() {
Scheduler::instance()->destroy_on_scheduler(
G()->get_gc_scheduler_id(), users_, users_full_, user_photos_, unknown_users_, pending_user_photos_,
user_profile_photo_file_source_ids_, my_photo_file_id_, chats_, chats_full_, unknown_chats_,
chat_full_file_source_ids_, min_channels_, channels_, channels_full_, unknown_channels_,
user_profile_photo_file_source_ids_, my_photo_file_id_, user_full_file_source_ids_, chats_, chats_full_,
unknown_chats_, chat_full_file_source_ids_, min_channels_, channels_, channels_full_, unknown_channels_,
invalidated_channels_full_, channel_full_file_source_ids_, secret_chats_, unknown_secret_chats_,
secret_chats_with_user_, invite_link_infos_, dialog_access_by_invite_link_, loaded_from_database_users_,
unavailable_user_fulls_, loaded_from_database_chats_, unavailable_chat_fulls_, loaded_from_database_channels_,
@ -3809,6 +3810,7 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
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();
bool has_premium_gift_options = !premium_gift_options.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_about);
STORE_FLAG(is_blocked);
@ -3826,6 +3828,8 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
STORE_FLAG(has_menu_button);
STORE_FLAG(has_description_photo);
STORE_FLAG(has_description_animation);
STORE_FLAG(has_premium_gift_options);
STORE_FLAG(voice_messages_forbidden);
END_STORE_FLAGS();
if (has_about) {
store(about, storer);
@ -3860,6 +3864,9 @@ void ContactsManager::UserFull::store(StorerT &storer) const {
storer.context()->td().get_actor_unsafe()->animations_manager_->store_animation(description_animation_file_id,
storer);
}
if (has_premium_gift_options) {
store(premium_gift_options, storer);
}
}
template <class ParserT>
@ -3875,6 +3882,7 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
bool has_menu_button;
bool has_description_photo;
bool has_description_animation;
bool has_premium_gift_options;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_about);
PARSE_FLAG(is_blocked);
@ -3892,6 +3900,8 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
PARSE_FLAG(has_menu_button);
PARSE_FLAG(has_description_photo);
PARSE_FLAG(has_description_animation);
PARSE_FLAG(has_premium_gift_options);
PARSE_FLAG(voice_messages_forbidden);
END_PARSE_FLAGS();
if (has_about) {
parse(about, parser);
@ -3926,6 +3936,9 @@ void ContactsManager::UserFull::parse(ParserT &parser) {
description_animation_file_id =
parser.context()->td().get_actor_unsafe()->animations_manager_->parse_animation(parser);
}
if (has_premium_gift_options) {
parse(premium_gift_options, parser);
}
}
template <class StorerT>
@ -4938,6 +4951,17 @@ string ContactsManager::get_user_private_forward_name(UserId user_id) {
return string();
}
bool ContactsManager::get_user_voice_messages_forbidden(UserId user_id) const {
if (!is_user_premium(user_id)) {
return false;
}
auto user_full = get_user_full(user_id);
if (user_full != nullptr) {
return user_full->voice_messages_forbidden;
}
return false;
}
string ContactsManager::get_dialog_about(DialogId dialog_id) {
switch (dialog_id.get_type()) {
case DialogType::User: {
@ -5787,9 +5811,7 @@ void ContactsManager::clear_imported_contacts(Promise<Unit> &&promise) {
void ContactsManager::on_update_contacts_reset() {
/*
UserId my_id = get_my_id();
for (auto &p : users_) {
UserId user_id = p.first;
User u = &p.second;
users_.foreach([&](const UserId &user_id, unique_ptr<User> &u) {
if (u->is_contact) {
LOG(INFO) << "Drop contact with " << user_id;
if (user_id != my_id) {
@ -5805,7 +5827,7 @@ void ContactsManager::on_update_contacts_reset() {
CHECK(!contacts_hints_.has_key(user_id.get()));
}
}
}
});
*/
saved_contact_count_ = 0;
@ -8036,12 +8058,13 @@ void ContactsManager::reload_created_public_dialogs(PublicDialogType type,
}
void ContactsManager::finish_get_created_public_dialogs(PublicDialogType type, Result<Unit> &&result) {
if (G()->close_flag()) {
result = Global::request_aborted_error();
}
auto index = static_cast<int32>(type);
auto promises = std::move(get_created_public_channels_queries_[index]);
reset_to_empty(get_created_public_channels_queries_[index]);
if (G()->close_flag()) {
result = G()->close_status();
}
if (result.is_error()) {
fail_promises(promises, result.move_as_error());
return;
@ -8373,9 +8396,8 @@ void ContactsManager::on_get_contacts(tl_object_ptr<telegram_api::contacts_Conta
on_get_users(std::move(contacts->users_), "on_get_contacts");
UserId my_id = get_my_id();
for (auto &p : users_) {
UserId user_id = p.first;
User *u = p.second.get();
users_.foreach([&](const UserId &user_id, unique_ptr<User> &user) {
User *u = user.get();
bool should_be_contact = contact_user_ids.count(user_id) == 1;
if (u->is_contact != should_be_contact) {
if (u->is_contact) {
@ -8397,7 +8419,7 @@ void ContactsManager::on_get_contacts(tl_object_ptr<telegram_api::contacts_Conta
LOG(ERROR) << "Receive non-contact " << user_id << " in the list of contacts";
}
}
}
});
saved_contact_count_ = contacts->saved_count_;
on_get_contacts_finished(std::numeric_limits<size_t>::max());
@ -8489,9 +8511,7 @@ void ContactsManager::on_update_online_status_privacy() {
void ContactsManager::on_update_phone_number_privacy() {
// all UserFull.need_phone_number_privacy_exception can be outdated now,
// so mark all of them as expired
for (auto &it : users_full_) {
it.second->expires_at = 0.0;
}
users_full_.foreach([&](const UserId &user_id, unique_ptr<UserFull> &user_full) { user_full->expires_at = 0.0; });
}
void ContactsManager::invalidate_user_full(UserId user_id) {
@ -8827,16 +8847,17 @@ void ContactsManager::on_binlog_user_event(BinlogEvent &&event) {
log_event_parse(log_event, event.data_).ensure();
auto user_id = log_event.user_id;
if (have_min_user(user_id)) {
if (have_min_user(user_id) || !user_id.is_valid()) {
LOG(ERROR) << "Skip adding already added " << user_id;
binlog_erase(G()->td_db()->get_binlog(), event.id_);
return;
}
LOG(INFO) << "Add " << user_id << " from binlog";
User *u = users_.emplace(user_id, std::move(log_event.u_out)).first->second.get();
CHECK(u != nullptr);
users_.set(user_id, std::move(log_event.u_out));
User *u = get_user(user_id);
CHECK(u != nullptr);
u->log_event_id = event.id_;
update_user(u, user_id, true, false);
@ -9135,16 +9156,17 @@ void ContactsManager::on_binlog_chat_event(BinlogEvent &&event) {
log_event_parse(log_event, event.data_).ensure();
auto chat_id = log_event.chat_id;
if (have_chat(chat_id)) {
if (have_chat(chat_id) || !chat_id.is_valid()) {
LOG(ERROR) << "Skip adding already added " << chat_id;
binlog_erase(G()->td_db()->get_binlog(), event.id_);
return;
}
LOG(INFO) << "Add " << chat_id << " from binlog";
Chat *c = chats_.emplace(chat_id, std::move(log_event.c_out)).first->second.get();
CHECK(c != nullptr);
chats_.set(chat_id, std::move(log_event.c_out));
Chat *c = get_chat(chat_id);
CHECK(c != nullptr);
c->log_event_id = event.id_;
update_chat(c, chat_id, true, false);
@ -9381,16 +9403,17 @@ void ContactsManager::on_binlog_channel_event(BinlogEvent &&event) {
log_event_parse(log_event, event.data_).ensure();
auto channel_id = log_event.channel_id;
if (have_channel(channel_id)) {
if (have_channel(channel_id) || !channel_id.is_valid()) {
LOG(ERROR) << "Skip adding already added " << channel_id;
binlog_erase(G()->td_db()->get_binlog(), event.id_);
return;
}
LOG(INFO) << "Add " << channel_id << " from binlog";
Channel *c = channels_.emplace(channel_id, std::move(log_event.c_out)).first->second.get();
CHECK(c != nullptr);
channels_.set(channel_id, std::move(log_event.c_out));
Channel *c = get_channel(channel_id);
CHECK(c != nullptr);
c->log_event_id = event.id_;
update_channel(c, channel_id, true, false);
@ -9635,16 +9658,17 @@ void ContactsManager::on_binlog_secret_chat_event(BinlogEvent &&event) {
log_event_parse(log_event, event.data_).ensure();
auto secret_chat_id = log_event.secret_chat_id;
if (have_secret_chat(secret_chat_id)) {
if (have_secret_chat(secret_chat_id) || !secret_chat_id.is_valid()) {
LOG(ERROR) << "Skip adding already added " << secret_chat_id;
binlog_erase(G()->td_db()->get_binlog(), event.id_);
return;
}
LOG(INFO) << "Add " << secret_chat_id << " from binlog";
SecretChat *c = secret_chats_.emplace(secret_chat_id, std::move(log_event.c_out)).first->second.get();
CHECK(c != nullptr);
secret_chats_.set(secret_chat_id, std::move(log_event.c_out));
SecretChat *c = get_secret_chat(secret_chat_id);
CHECK(c != nullptr);
c->log_event_id = event.id_;
update_secret_chat(c, secret_chat_id, true, false);
@ -10556,6 +10580,28 @@ void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, cons
td_->messages_manager_->drop_common_dialogs_cache(user_id);
user_full->is_common_chat_count_changed = false;
}
if (user_full->are_files_changed) {
auto file_ids = photo_get_file_ids(user_full->description_photo);
if (user_full->description_animation_file_id.is_valid()) {
file_ids.push_back(user_full->description_animation_file_id);
}
if (user_full->registered_file_ids != file_ids) {
auto &file_source_id = user_full->file_source_id;
if (!file_source_id.is_valid()) {
file_source_id = user_full_file_source_ids_.get(user_id);
if (file_source_id.is_valid()) {
VLOG(file_references) << "Move " << file_source_id << " inside of " << user_id;
user_full_file_source_ids_.erase(user_id);
} else {
VLOG(file_references) << "Need to create new file source for full " << user_id;
file_source_id = td_->file_reference_manager_->create_user_full_file_source(user_id);
}
}
td_->file_manager_->change_files_source(file_source_id, user_full->registered_file_ids, file_ids);
user_full->registered_file_ids = std::move(file_ids);
}
}
user_full->need_send_update |= user_full->is_changed;
user_full->need_save_to_database |= user_full->is_changed;
@ -10774,22 +10820,35 @@ void ContactsManager::on_get_user_full(tl_object_ptr<telegram_api::userFull> &&u
bool can_be_called = user->phone_calls_available_ && !user->phone_calls_private_;
bool supports_video_calls = user->video_calls_available_ && !user->phone_calls_private_;
bool has_private_calls = user->phone_calls_private_;
bool voice_messages_forbidden = u->is_premium ? user->voice_messages_forbidden_ : false;
auto premium_gift_options = transform(std::move(user->premium_gifts_), [](auto &&premium_gift_option) {
return PremiumGiftOption(std::move(premium_gift_option));
});
AdministratorRights group_administrator_rights(user->bot_group_admin_rights_, ChannelType::Megagroup);
AdministratorRights broadcast_administrator_rights(user->bot_broadcast_admin_rights_, ChannelType::Broadcast);
if (user_full->can_be_called != can_be_called || user_full->supports_video_calls != supports_video_calls ||
user_full->has_private_calls != has_private_calls ||
user_full->private_forward_name != user->private_forward_name_ ||
user_full->group_administrator_rights != group_administrator_rights ||
user_full->broadcast_administrator_rights != broadcast_administrator_rights) {
user_full->broadcast_administrator_rights != broadcast_administrator_rights ||
user_full->premium_gift_options != premium_gift_options ||
user_full->voice_messages_forbidden != voice_messages_forbidden) {
user_full->can_be_called = can_be_called;
user_full->supports_video_calls = supports_video_calls;
user_full->has_private_calls = has_private_calls;
user_full->private_forward_name = std::move(user->private_forward_name_);
user_full->group_administrator_rights = group_administrator_rights;
user_full->broadcast_administrator_rights = broadcast_administrator_rights;
user_full->premium_gift_options = std::move(premium_gift_options);
user_full->voice_messages_forbidden = voice_messages_forbidden;
user_full->is_changed = true;
}
if (user_full->private_forward_name != user->private_forward_name_) {
if (user_full->private_forward_name.empty() != user->private_forward_name_.empty()) {
user_full->is_changed = true;
}
user_full->private_forward_name = std::move(user->private_forward_name_);
user_full->need_save_to_database = true;
}
if (user_full->about != user->about_) {
user_full->about = std::move(user->about_);
user_full->is_changed = true;
@ -10819,11 +10878,15 @@ void ContactsManager::on_get_user_full(tl_object_ptr<telegram_api::userFull> &&u
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 || user_full->description_photo != description_photo ||
user_full->description_animation_file_id != description_animation_file_id) {
if (user_full->description != description) {
user_full->description = std::move(description);
user_full->is_changed = true;
}
if (user_full->description_photo != description_photo ||
user_full->description_animation_file_id != description_animation_file_id) {
user_full->description_photo = std::move(description_photo);
user_full->description_animation_file_id = description_animation_file_id;
user_full->are_files_changed = true;
user_full->is_changed = true;
}
@ -11604,12 +11667,10 @@ void ContactsManager::register_user_photo(User *u, UserId user_id, const Photo &
if (user_id == get_my_id()) {
my_photo_file_id_[photo_id] = first_file_id;
}
FileSourceId file_source_id;
auto it = user_profile_photo_file_source_ids_.find(std::make_pair(user_id, photo_id));
if (it != user_profile_photo_file_source_ids_.end()) {
VLOG(file_references) << "Move " << it->second << " inside of " << user_id;
file_source_id = it->second;
user_profile_photo_file_source_ids_.erase(it);
auto file_source_id = user_profile_photo_file_source_ids_.get(std::make_pair(user_id, photo_id));
if (file_source_id.is_valid()) {
VLOG(file_references) << "Move " << file_source_id << " inside of " << user_id;
user_profile_photo_file_source_ids_.erase(std::make_pair(user_id, photo_id));
} else {
VLOG(file_references) << "Need to create new file source for photo " << photo_id << " of " << user_id;
file_source_id = td_->file_reference_manager_->create_user_photo_file_source(user_id, photo_id);
@ -11902,6 +11963,15 @@ void ContactsManager::on_set_profile_photo(tl_object_ptr<telegram_api::photos_ph
add_profile_photo_to_cache(my_user_id,
get_photo(td_->file_manager_.get(), std::move(photo->photo_), DialogId(my_user_id)));
User *u = get_user(my_user_id);
if (u != nullptr) {
update_user(u, my_user_id);
}
auto *user_full = get_user_full(my_user_id);
if (user_full != nullptr) {
update_user_full(user_full, my_user_id, "on_set_profile_photo");
}
// if cache was correctly updated, this should produce no updates
on_get_users(std::move(photo->users_), "on_set_profile_photo");
}
@ -12047,33 +12117,30 @@ void ContactsManager::drop_user_photos(UserId user_id, bool is_empty, bool drop_
if (user_photos->count == new_count) {
CHECK(user_photos->photos.empty());
CHECK(user_photos->offset == user_photos->count);
return;
} else {
LOG(INFO) << "Drop photos of " << user_id << " to " << (is_empty ? "empty" : "unknown") << " from " << source;
user_photos->photos.clear();
user_photos->count = new_count;
user_photos->offset = user_photos->count;
}
LOG(INFO) << "Drop photos of " << user_id << " to " << (is_empty ? "empty" : "unknown") << " from " << source;
user_photos->photos.clear();
user_photos->count = new_count;
user_photos->offset = user_photos->count;
}
if (drop_user_full_photo) {
auto user_full = get_user_full(user_id); // must not load UserFull
if (user_full == nullptr) {
return;
}
if (!user_full->photo.is_empty()) {
user_full->photo = Photo();
user_full->is_changed = true;
}
if (!is_empty) {
if (user_full->expires_at > 0.0) {
user_full->expires_at = 0.0;
user_full->need_save_to_database = true;
if (user_full != nullptr) {
if (!user_full->photo.is_empty()) {
user_full->photo = Photo();
user_full->is_changed = true;
}
reload_user_full(user_id);
if (!is_empty) {
if (user_full->expires_at > 0.0) {
user_full->expires_at = 0.0;
user_full->need_save_to_database = true;
}
reload_user_full(user_id, Auto());
}
update_user_full(user_full, user_id, "drop_user_photos");
}
update_user_full(user_full, user_id, "drop_user_photos");
}
}
@ -12104,6 +12171,9 @@ void ContactsManager::drop_user_full(UserId user_id) {
user_full->private_forward_name.clear();
user_full->group_administrator_rights = {};
user_full->broadcast_administrator_rights = {};
user_full->premium_gift_options.clear();
user_full->voice_messages_forbidden = false;
user_full->are_files_changed = true;
user_full->is_changed = true;
update_user_full(user_full, user_id, "drop_user_full");
@ -12897,24 +12967,18 @@ void ContactsManager::on_update_chat_full_photo(ChatFull *chat_full, ChatId chat
auto &file_source_id = chat_full->file_source_id;
if (!file_source_id.is_valid()) {
auto it = chat_full_file_source_ids_.find(chat_id);
if (it != chat_full_file_source_ids_.end()) {
VLOG(file_references) << "Move " << it->second << " inside of " << chat_id;
file_source_id = it->second;
chat_full_file_source_ids_.erase(it);
file_source_id = chat_full_file_source_ids_.get(chat_id);
if (file_source_id.is_valid()) {
VLOG(file_references) << "Move " << file_source_id << " inside of " << chat_id;
chat_full_file_source_ids_.erase(chat_id);
} else {
VLOG(file_references) << "Need to create new file source for full " << chat_id;
file_source_id = td_->file_reference_manager_->create_chat_full_file_source(chat_id);
}
}
for (auto &file_id : chat_full->registered_photo_file_ids) {
td_->file_manager_->remove_file_source(file_id, file_source_id);
}
td_->file_manager_->change_files_source(file_source_id, chat_full->registered_photo_file_ids, photo_file_ids);
chat_full->registered_photo_file_ids = std::move(photo_file_ids);
for (auto &file_id : chat_full->registered_photo_file_ids) {
td_->file_manager_->add_file_source(file_id, file_source_id);
}
}
void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, ChannelId channel_id, Photo photo) {
@ -12931,24 +12995,18 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch
auto &file_source_id = channel_full->file_source_id;
if (!file_source_id.is_valid()) {
auto it = channel_full_file_source_ids_.find(channel_id);
if (it != channel_full_file_source_ids_.end()) {
VLOG(file_references) << "Move " << it->second << " inside of " << channel_id;
file_source_id = it->second;
channel_full_file_source_ids_.erase(it);
file_source_id = channel_full_file_source_ids_.get(channel_id);
if (file_source_id.is_valid()) {
VLOG(file_references) << "Move " << file_source_id << " inside of " << channel_id;
channel_full_file_source_ids_.erase(channel_id);
} else {
VLOG(file_references) << "Need to create new file source for full " << channel_id;
file_source_id = td_->file_reference_manager_->create_channel_full_file_source(channel_id);
}
}
for (auto &file_id : channel_full->registered_photo_file_ids) {
td_->file_manager_->remove_file_source(file_id, file_source_id);
}
td_->file_manager_->change_files_source(file_source_id, channel_full->registered_photo_file_ids, photo_file_ids);
channel_full->registered_photo_file_ids = std::move(photo_file_ids);
for (auto &file_id : channel_full->registered_photo_file_ids) {
td_->file_manager_->add_file_source(file_id, file_source_id);
}
}
void ContactsManager::on_get_permanent_dialog_invite_link(DialogId dialog_id, const DialogInviteLink &invite_link) {
@ -13001,10 +13059,9 @@ void ContactsManager::remove_linked_channel_id(ChannelId channel_id) {
return;
}
auto it = linked_channel_ids_.find(channel_id);
if (it != linked_channel_ids_.end()) {
auto linked_channel_id = it->second;
linked_channel_ids_.erase(it);
auto linked_channel_id = linked_channel_ids_.get(channel_id);
if (linked_channel_id.is_valid()) {
linked_channel_ids_.erase(channel_id);
linked_channel_ids_.erase(linked_channel_id);
}
}
@ -13015,12 +13072,7 @@ ChannelId ContactsManager::get_linked_channel_id(ChannelId channel_id) const {
return channel_full->linked_channel_id;
}
auto it = linked_channel_ids_.find(channel_id);
if (it != linked_channel_ids_.end()) {
return it->second;
}
return ChannelId();
return linked_channel_ids_.get(channel_id);
}
void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id,
@ -13039,8 +13091,8 @@ void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *chan
remove_linked_channel_id(channel_id);
remove_linked_channel_id(linked_channel_id);
if (channel_id.is_valid() && linked_channel_id.is_valid()) {
linked_channel_ids_[channel_id] = linked_channel_id;
linked_channel_ids_[linked_channel_id] = channel_id;
linked_channel_ids_.set(channel_id, linked_channel_id);
linked_channel_ids_.set(linked_channel_id, channel_id);
}
if (channel_full != nullptr && channel_full->linked_channel_id != linked_channel_id) {
@ -14269,6 +14321,12 @@ void ContactsManager::on_update_channel_participant(ChannelId channel_id, UserId
<< new_dialog_participant;
return;
}
if (new_dialog_participant.status_.is_administrator() && user_id == get_my_id() &&
!new_dialog_participant.status_.can_be_edited()) {
LOG(ERROR) << "Fix wrong can_be_edited in " << new_dialog_participant << " from " << channel_id << " changed from "
<< old_dialog_participant;
new_dialog_participant.status_.toggle_can_be_edited();
}
if (old_dialog_participant.dialog_id_ == DialogId(get_my_id()) && old_dialog_participant.status_.is_administrator() &&
!new_dialog_participant.status_.is_administrator()) {
@ -14368,12 +14426,10 @@ bool ContactsManager::is_user_bot(UserId user_id) const {
}
Result<ContactsManager::BotData> ContactsManager::get_bot_data(UserId user_id) const {
auto p = users_.find(user_id);
if (p == users_.end()) {
auto u = get_user(user_id);
if (u == nullptr) {
return Status::Error(400, "Bot not found");
}
auto u = p->second.get();
if (!u->is_bot) {
return Status::Error(400, "User is not a bot");
}
@ -14410,21 +14466,11 @@ bool ContactsManager::can_report_user(UserId user_id) const {
}
const ContactsManager::User *ContactsManager::get_user(UserId user_id) const {
auto p = users_.find(user_id);
if (p == users_.end()) {
return nullptr;
} else {
return p->second.get();
}
return users_.get_pointer(user_id);
}
ContactsManager::User *ContactsManager::get_user(UserId user_id) {
auto p = users_.find(user_id);
if (p == users_.end()) {
return nullptr;
} else {
return p->second.get();
}
return users_.get_pointer(user_id);
}
bool ContactsManager::is_dialog_info_received_from_server(DialogId dialog_id) const {
@ -14524,21 +14570,11 @@ ContactsManager::User *ContactsManager::add_user(UserId user_id, const char *sou
}
const ContactsManager::UserFull *ContactsManager::get_user_full(UserId user_id) const {
auto p = users_full_.find(user_id);
if (p == users_full_.end()) {
return nullptr;
} else {
return p->second.get();
}
return users_full_.get_pointer(user_id);
}
ContactsManager::UserFull *ContactsManager::get_user_full(UserId user_id) {
auto p = users_full_.find(user_id);
if (p == users_full_.end()) {
return nullptr;
} else {
return p->second.get();
}
return users_full_.get_pointer(user_id);
}
ContactsManager::UserFull *ContactsManager::add_user_full(UserId user_id) {
@ -14595,11 +14631,9 @@ void ContactsManager::load_user_full(UserId user_id, bool force, Promise<Unit> &
promise.set_value(Unit());
}
void ContactsManager::reload_user_full(UserId user_id) {
auto r_input_user = get_input_user(user_id);
if (r_input_user.is_ok()) {
send_get_user_full_query(user_id, r_input_user.move_as_ok(), Auto(), "reload_user_full");
}
void ContactsManager::reload_user_full(UserId user_id, Promise<Unit> &&promise) {
TRY_RESULT_PROMISE(promise, input_user, get_input_user(user_id));
send_get_user_full_query(user_id, std::move(input_user), std::move(promise), "reload_user_full");
}
void ContactsManager::send_get_user_full_query(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user,
@ -14724,6 +14758,25 @@ FileSourceId ContactsManager::get_user_profile_photo_file_source_id(UserId user_
return source_id;
}
FileSourceId ContactsManager::get_user_full_file_source_id(UserId user_id) {
if (!user_id.is_valid()) {
return FileSourceId();
}
if (get_user_full(user_id) != nullptr) {
VLOG(file_references) << "Don't need to create file source for full " << user_id;
// user full was already added, source ID was registered and shouldn't be needed
return FileSourceId();
}
auto &source_id = user_full_file_source_ids_[user_id];
if (!source_id.is_valid()) {
source_id = td_->file_reference_manager_->create_user_full_file_source(user_id);
}
VLOG(file_references) << "Return " << source_id << " for full " << user_id;
return source_id;
}
FileSourceId ContactsManager::get_chat_full_file_source_id(ChatId chat_id) {
if (!chat_id.is_valid()) {
return FileSourceId();
@ -14767,21 +14820,11 @@ bool ContactsManager::have_chat(ChatId chat_id) const {
}
const ContactsManager::Chat *ContactsManager::get_chat(ChatId chat_id) const {
auto p = chats_.find(chat_id);
if (p == chats_.end()) {
return nullptr;
} else {
return p->second.get();
}
return chats_.get_pointer(chat_id);
}
ContactsManager::Chat *ContactsManager::get_chat(ChatId chat_id) {
auto p = chats_.find(chat_id);
if (p == chats_.end()) {
return nullptr;
} else {
return p->second.get();
}
return chats_.get_pointer(chat_id);
}
ContactsManager::Chat *ContactsManager::add_chat(ChatId chat_id) {
@ -14829,21 +14872,11 @@ void ContactsManager::reload_chat(ChatId chat_id, Promise<Unit> &&promise) {
}
const ContactsManager::ChatFull *ContactsManager::get_chat_full(ChatId chat_id) const {
auto p = chats_full_.find(chat_id);
if (p == chats_full_.end()) {
return nullptr;
} else {
return p->second.get();
}
return chats_full_.get_pointer(chat_id);
}
ContactsManager::ChatFull *ContactsManager::get_chat_full(ChatId chat_id) {
auto p = chats_full_.find(chat_id);
if (p == chats_full_.end()) {
return nullptr;
} else {
return p->second.get();
}
return chats_full_.get_pointer(chat_id);
}
ContactsManager::ChatFull *ContactsManager::add_chat_full(ChatId chat_id) {
@ -14880,7 +14913,8 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha
return true;
}
LOG(DEBUG) << "Full " << chat_id << " is up-to-date with version " << chat_full->version;
LOG(DEBUG) << "Full " << chat_id << " is up-to-date with version " << chat_full->version << " and photos " << c->photo
<< '/' << chat_full->photo;
return false;
}
@ -15152,36 +15186,22 @@ bool ContactsManager::have_min_channel(ChannelId channel_id) const {
}
const MinChannel *ContactsManager::get_min_channel(ChannelId channel_id) const {
auto it = min_channels_.find(channel_id);
if (it == min_channels_.end()) {
return nullptr;
}
return it->second.get();
return min_channels_.get_pointer(channel_id);
}
void ContactsManager::add_min_channel(ChannelId channel_id, const MinChannel &min_channel) {
if (have_channel(channel_id) || have_min_channel(channel_id) || !channel_id.is_valid()) {
return;
}
min_channels_[channel_id] = td::make_unique<MinChannel>(min_channel);
min_channels_.set(channel_id, td::make_unique<MinChannel>(min_channel));
}
const ContactsManager::Channel *ContactsManager::get_channel(ChannelId channel_id) const {
auto p = channels_.find(channel_id);
if (p == channels_.end()) {
return nullptr;
} else {
return p->second.get();
}
return channels_.get_pointer(channel_id);
}
ContactsManager::Channel *ContactsManager::get_channel(ChannelId channel_id) {
auto p = channels_.find(channel_id);
if (p == channels_.end()) {
return nullptr;
} else {
return p->second.get();
}
return channels_.get_pointer(channel_id);
}
ContactsManager::Channel *ContactsManager::add_channel(ChannelId channel_id, const char *source) {
@ -15189,6 +15209,7 @@ ContactsManager::Channel *ContactsManager::add_channel(ChannelId channel_id, con
auto &channel_ptr = channels_[channel_id];
if (channel_ptr == nullptr) {
channel_ptr = make_unique<Channel>();
min_channels_.erase(channel_id);
}
return channel_ptr.get();
}
@ -15236,26 +15257,20 @@ void ContactsManager::reload_channel(ChannelId channel_id, Promise<Unit> &&promi
}
const ContactsManager::ChannelFull *ContactsManager::get_channel_full_const(ChannelId channel_id) const {
auto p = channels_full_.find(channel_id);
if (p == channels_full_.end()) {
return nullptr;
} else {
return p->second.get();
}
return channels_full_.get_pointer(channel_id);
}
const ContactsManager::ChannelFull *ContactsManager::get_channel_full(ChannelId channel_id) const {
return get_channel_full_const(channel_id);
return channels_full_.get_pointer(channel_id);
}
ContactsManager::ChannelFull *ContactsManager::get_channel_full(ChannelId channel_id, bool only_local,
const char *source) {
auto p = channels_full_.find(channel_id);
if (p == channels_full_.end()) {
auto channel_full = channels_full_.get_pointer(channel_id);
if (channel_full == nullptr) {
return nullptr;
}
auto channel_full = p->second.get();
if (!only_local && channel_full->is_expired() && !td_->auth_manager_->is_bot()) {
send_get_channel_full_query(channel_full, channel_id, Auto(), source);
}
@ -15350,19 +15365,11 @@ ContactsManager::SecretChat *ContactsManager::add_secret_chat(SecretChatId secre
}
const ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secret_chat_id) const {
auto it = secret_chats_.find(secret_chat_id);
if (it == secret_chats_.end()) {
return nullptr;
}
return it->second.get();
return secret_chats_.get_pointer(secret_chat_id);
}
ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secret_chat_id) {
auto it = secret_chats_.find(secret_chat_id);
if (it == secret_chats_.end()) {
return nullptr;
}
return it->second.get();
return secret_chats_.get_pointer(secret_chat_id);
}
bool ContactsManager::get_secret_chat(SecretChatId secret_chat_id, bool force, Promise<Unit> &&promise) {
@ -16143,6 +16150,8 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc
on_update_chat_title(c, chat_id, std::move(chat.title_));
if (!status.is_left()) {
on_update_chat_participant_count(c, chat_id, chat.participants_count_, chat.version_, debug_str);
} else {
chat.photo_ = nullptr;
}
if (c->date != chat.date_) {
LOG_IF(ERROR, c->date != 0) << "Chat creation date has changed from " << c->date << " to " << chat.date_
@ -16213,7 +16222,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
LOG(ERROR) << "Receive empty " << to_string(channel) << " from " << source << ", have "
<< to_string(get_supergroup_object(channel_id, c));
if (c == nullptr && !have_min_channel(channel_id)) {
min_channels_[channel_id] = td::make_unique<MinChannel>();
min_channels_.set(channel_id, td::make_unique<MinChannel>());
}
return;
}
@ -16338,7 +16347,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
min_channel->title_ = std::move(channel.title_);
min_channel->is_megagroup_ = is_megagroup;
min_channels_[channel_id] = std::move(min_channel);
min_channels_.set(channel_id, std::move(min_channel));
}
return;
}
@ -16353,9 +16362,6 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
}
Channel *c = add_channel(channel_id, "on_channel");
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) {
@ -16451,15 +16457,12 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
LOG(ERROR) << "Receive empty " << to_string(channel) << " from " << source << ", have "
<< to_string(get_supergroup_object(channel_id, c));
if (c == nullptr && !have_min_channel(channel_id)) {
min_channels_[channel_id] = td::make_unique<MinChannel>();
min_channels_.set(channel_id, td::make_unique<MinChannel>());
}
return;
}
Channel *c = add_channel(channel_id, "on_channel_forbidden");
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_) {
@ -16731,6 +16734,7 @@ 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);
bool is_premium = is_user_premium(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());
@ -16765,10 +16769,17 @@ tl_object_ptr<td_api::userFullInfo> ContactsManager::get_user_full_info_object(U
}
bio_object = get_formatted_text_object(bio, true, 0);
}
auto base_premium_gift_it =
std::min_element(user_full->premium_gift_options.begin(), user_full->premium_gift_options.end());
auto premium_gift_options = transform(user_full->premium_gift_options, [&base_premium_gift_it](const auto &option) {
return option.get_premium_gift_option_object(*base_premium_gift_it);
});
auto voice_messages_forbidden = is_premium ? user_full->voice_messages_forbidden : false;
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, std::move(bio_object),
!user_full->private_forward_name.empty(), voice_messages_forbidden,
user_full->need_phone_number_privacy_exception, std::move(bio_object), std::move(premium_gift_options),
user_full->common_chat_count, std::move(bot_info));
}
@ -17089,33 +17100,34 @@ void ContactsManager::get_current_state(vector<td_api::object_ptr<td_api::Update
}
}
for (const auto &it : users_) {
updates.push_back(td_api::make_object<td_api::updateUser>(get_user_object(it.first, it.second.get())));
}
for (const auto &it : channels_) {
updates.push_back(td_api::make_object<td_api::updateSupergroup>(get_supergroup_object(it.first, it.second.get())));
}
for (const auto &it : chats_) { // chat object can contain channel_id, so it must be sent after channels
users_.foreach([&](const UserId &user_id, const unique_ptr<User> &user) {
updates.push_back(td_api::make_object<td_api::updateUser>(get_user_object(user_id, user.get())));
});
channels_.foreach([&](const ChannelId &channel_id, const unique_ptr<Channel> &channel) {
updates.push_back(td_api::make_object<td_api::updateSupergroup>(get_supergroup_object(channel_id, channel.get())));
});
// chat objects can contain channel_id, so they must be sent after channels
chats_.foreach([&](const ChatId &chat_id, const unique_ptr<Chat> &chat) {
updates.push_back(td_api::make_object<td_api::updateBasicGroup>(get_basic_group_object_const(chat_id, chat.get())));
});
// secret chat objects contain user_id, so they must be sent after users
secret_chats_.foreach([&](const SecretChatId &secret_chat_id, const unique_ptr<SecretChat> &secret_chat) {
updates.push_back(
td_api::make_object<td_api::updateBasicGroup>(get_basic_group_object_const(it.first, it.second.get())));
}
for (const auto &it : secret_chats_) { // secret chat object contains user_id, so it must be sent after users
updates.push_back(
td_api::make_object<td_api::updateSecretChat>(get_secret_chat_object_const(it.first, it.second.get())));
}
td_api::make_object<td_api::updateSecretChat>(get_secret_chat_object_const(secret_chat_id, secret_chat.get())));
});
for (const auto &it : users_full_) {
users_full_.foreach([&](const UserId &user_id, const unique_ptr<UserFull> &user_full) {
updates.push_back(td_api::make_object<td_api::updateUserFullInfo>(
it.first.get(), get_user_full_info_object(it.first, it.second.get())));
}
for (const auto &it : channels_full_) {
user_id.get(), get_user_full_info_object(user_id, user_full.get())));
});
channels_full_.foreach([&](const ChannelId &channel_id, const unique_ptr<ChannelFull> &channel_full) {
updates.push_back(td_api::make_object<td_api::updateSupergroupFullInfo>(
it.first.get(), get_supergroup_full_info_object(it.second.get(), it.first)));
}
for (const auto &it : chats_full_) {
channel_id.get(), get_supergroup_full_info_object(channel_full.get(), channel_id)));
});
chats_full_.foreach([&](const ChatId &chat_id, const unique_ptr<ChatFull> &chat_full) {
updates.push_back(td_api::make_object<td_api::updateBasicGroupFullInfo>(
it.first.get(), get_basic_group_full_info_object(it.second.get())));
}
chat_id.get(), get_basic_group_full_info_object(chat_full.get())));
});
}
void ContactsManager::memory_stats(vector<string> &output) {

View File

@ -27,6 +27,7 @@
#include "td/telegram/MessageId.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PremiumGiftOption.h"
#include "td/telegram/PublicDialogType.h"
#include "td/telegram/QueryCombiner.h"
#include "td/telegram/RestrictionReason.h"
@ -49,6 +50,7 @@
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Time.h"
#include "td/utils/WaitFreeHashMap.h"
#include <functional>
#include <memory>
@ -120,6 +122,7 @@ class ContactsManager final : public Actor {
bool get_channel_has_protected_content(ChannelId channel_id) const;
string get_user_private_forward_name(UserId user_id);
bool get_user_voice_messages_forbidden(UserId user_id) const;
string get_dialog_about(DialogId dialog_id);
@ -491,7 +494,8 @@ class ContactsManager final : public Actor {
bool get_user(UserId user_id, int left_tries, Promise<Unit> &&promise);
void reload_user(UserId user_id, Promise<Unit> &&promise);
void load_user_full(UserId user_id, bool force, Promise<Unit> &&promise, const char *source);
void reload_user_full(UserId user_id);
FileSourceId get_user_full_file_source_id(UserId user_id);
void reload_user_full(UserId user_id, Promise<Unit> &&promise);
std::pair<int32, vector<const Photo *>> get_user_profile_photos(UserId user_id, int32 offset, int32 limit,
Promise<Unit> &&promise);
@ -716,6 +720,10 @@ class ContactsManager final : public Actor {
string description;
Photo description_photo;
FileId description_animation_file_id;
vector<FileId> registered_file_ids;
FileSourceId file_source_id;
vector<PremiumGiftOption> premium_gift_options;
unique_ptr<BotMenuButton> menu_button;
vector<BotCommand> commands;
@ -730,8 +738,10 @@ class ContactsManager final : public Actor {
bool has_private_calls = false;
bool can_pin_messages = true;
bool need_phone_number_privacy_exception = false;
bool voice_messages_forbidden = false;
bool is_common_chat_count_changed = true;
bool are_files_changed = true;
bool is_changed = true; // have new changes that need to be sent to the client and database
bool need_send_update = true; // have new changes that need only to be sent to the client
bool need_save_to_database = true; // have new changes that need only to be saved to the database
@ -1074,6 +1084,7 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FULL_FLAG_HAS_PRIVATE_FORWARD_NAME = 1 << 16;
static constexpr int32 USER_FULL_FLAG_HAS_GROUP_ADMINISTRATOR_RIGHTS = 1 << 17;
static constexpr int32 USER_FULL_FLAG_HAS_BROADCAST_ADMINISTRATOR_RIGHTS = 1 << 18;
static constexpr int32 USER_FULL_FLAG_HAS_VOICE_MESSAGES_FORBIDDEN = 1 << 20;
static constexpr int32 CHAT_FLAG_USER_IS_CREATOR = 1 << 0;
static constexpr int32 CHAT_FLAG_USER_HAS_LEFT = 1 << 2;
@ -1690,8 +1701,8 @@ class ContactsManager final : public Actor {
UserId support_user_id_;
int32 my_was_online_local_ = 0;
FlatHashMap<UserId, unique_ptr<User>, UserIdHash> users_;
FlatHashMap<UserId, unique_ptr<UserFull>, UserIdHash> users_full_;
WaitFreeHashMap<UserId, unique_ptr<User>, UserIdHash> users_;
WaitFreeHashMap<UserId, unique_ptr<UserFull>, UserIdHash> users_full_;
FlatHashMap<UserId, UserPhotos, UserIdHash> user_photos_;
mutable FlatHashSet<UserId, UserIdHash> unknown_users_;
FlatHashMap<UserId, tl_object_ptr<telegram_api::UserProfilePhoto>, UserIdHash> pending_user_photos_;
@ -1700,22 +1711,23 @@ class ContactsManager final : public Actor {
return UserIdHash()(pair.first) * 2023654985u + std::hash<int64>()(pair.second);
}
};
FlatHashMap<std::pair<UserId, int64>, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_;
WaitFreeHashMap<std::pair<UserId, int64>, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_;
FlatHashMap<int64, FileId> my_photo_file_id_;
WaitFreeHashMap<UserId, FileSourceId, UserIdHash> user_full_file_source_ids_;
FlatHashMap<ChatId, unique_ptr<Chat>, ChatIdHash> chats_;
FlatHashMap<ChatId, unique_ptr<ChatFull>, ChatIdHash> chats_full_;
WaitFreeHashMap<ChatId, unique_ptr<Chat>, ChatIdHash> chats_;
WaitFreeHashMap<ChatId, unique_ptr<ChatFull>, ChatIdHash> chats_full_;
mutable FlatHashSet<ChatId, ChatIdHash> unknown_chats_;
FlatHashMap<ChatId, FileSourceId, ChatIdHash> chat_full_file_source_ids_;
WaitFreeHashMap<ChatId, FileSourceId, ChatIdHash> chat_full_file_source_ids_;
FlatHashMap<ChannelId, unique_ptr<MinChannel>, ChannelIdHash> min_channels_;
FlatHashMap<ChannelId, unique_ptr<Channel>, ChannelIdHash> channels_;
FlatHashMap<ChannelId, unique_ptr<ChannelFull>, ChannelIdHash> channels_full_;
WaitFreeHashMap<ChannelId, unique_ptr<MinChannel>, ChannelIdHash> min_channels_;
WaitFreeHashMap<ChannelId, unique_ptr<Channel>, ChannelIdHash> channels_;
WaitFreeHashMap<ChannelId, unique_ptr<ChannelFull>, ChannelIdHash> channels_full_;
mutable FlatHashSet<ChannelId, ChannelIdHash> unknown_channels_;
FlatHashSet<ChannelId, ChannelIdHash> invalidated_channels_full_;
FlatHashMap<ChannelId, FileSourceId, ChannelIdHash> channel_full_file_source_ids_;
WaitFreeHashMap<ChannelId, FileSourceId, ChannelIdHash> channel_full_file_source_ids_;
FlatHashMap<SecretChatId, unique_ptr<SecretChat>, SecretChatIdHash> secret_chats_;
WaitFreeHashMap<SecretChatId, unique_ptr<SecretChat>, SecretChatIdHash> secret_chats_;
mutable FlatHashSet<SecretChatId, SecretChatIdHash> unknown_secret_chats_;
FlatHashMap<UserId, vector<SecretChatId>, UserIdHash> secret_chats_with_user_;
@ -1829,7 +1841,7 @@ class ContactsManager final : public Actor {
bool is_set_location_visibility_request_sent_ = false;
Location last_user_location_;
FlatHashMap<ChannelId, ChannelId, ChannelIdHash> linked_channel_ids_;
WaitFreeHashMap<ChannelId, ChannelId, ChannelIdHash> linked_channel_ids_;
FlatHashSet<UserId, UserIdHash> restricted_user_ids_;
FlatHashSet<ChannelId, ChannelIdHash> restricted_channel_ids_;

View File

@ -402,6 +402,7 @@ bool DialogAction::is_canceled_by_message_of_type(MessageContentType message_con
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return false;
default:
UNREACHABLE();

View File

@ -360,6 +360,10 @@ class DialogParticipantStatus {
return (flags_ & CAN_BE_EDITED) != 0;
}
void toggle_can_be_edited() {
flags_ ^= CAN_BE_EDITED;
}
bool can_send_messages() const {
return get_restricted_rights().can_send_messages();
}

View File

@ -73,6 +73,10 @@ void Document::append_file_ids(const Td *td, vector<FileId> &file_ids) const {
if (animated_thumbnail_file_id.is_valid()) {
file_ids.push_back(animated_thumbnail_file_id);
}
if (type == Type::Audio) {
td->audios_manager_->append_audio_album_cover_file_ids(file_id, file_ids);
}
}
bool operator==(const Document &lhs, const Document &rhs) {

View File

@ -21,6 +21,7 @@
#include "td/telegram/secret_api.h"
#include "td/telegram/StickerFormat.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -58,9 +59,7 @@ tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId fil
return nullptr;
}
auto it = documents_.find(file_id);
CHECK(it != documents_.end());
auto document = it->second.get();
auto document = get_document(file_id);
CHECK(document != nullptr);
return make_tl_object<td_api::document>(
document->file_name, document->mime_type, get_minithumbnail_object(document->minithumbnail),
@ -76,6 +75,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
tl_object_ptr<telegram_api::documentAttributeVideo> video;
tl_object_ptr<telegram_api::documentAttributeAudio> audio;
tl_object_ptr<telegram_api::documentAttributeSticker> sticker;
tl_object_ptr<telegram_api::documentAttributeCustomEmoji> custom_emoji;
Dimensions dimensions;
string file_name;
bool has_stickers = false;
@ -110,6 +110,10 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
case telegram_api::documentAttributeHasStickers::ID:
has_stickers = true;
break;
case telegram_api::documentAttributeCustomEmoji::ID:
custom_emoji = move_tl_object_as<telegram_api::documentAttributeCustomEmoji>(attribute);
type_attributes++;
break;
default:
UNREACHABLE();
}
@ -130,7 +134,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
if ((video->flags_ & telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK) != 0) {
// video note without sound
animated = nullptr;
} else if (sticker != nullptr) {
} else if (sticker != nullptr || custom_emoji != nullptr) {
// sticker
type_attributes--;
animated = nullptr;
@ -139,7 +143,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
// video animation
video = nullptr;
}
} else if (sticker != nullptr) {
} else if (sticker != nullptr || custom_emoji != nullptr) {
// some stickers uploaded before release
type_attributes--;
video = nullptr;
@ -155,6 +159,11 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
type_attributes--;
sticker = nullptr;
}
if (animated != nullptr && custom_emoji != nullptr) {
// just in case
type_attributes--;
custom_emoji = nullptr;
}
auto document_type = default_document_type;
FileType file_type = FileType::Document;
@ -183,7 +192,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
file_type = FileType::Audio;
default_extension = Slice("mp3");
}
} else if (sticker != nullptr || default_document_type == Document::Type::Sticker) {
} else if (sticker != nullptr || custom_emoji != nullptr || default_document_type == Document::Type::Sticker) {
document_type = Document::Type::Sticker;
file_type = FileType::Sticker;
sticker_format = StickerFormat::Webp;
@ -211,8 +220,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
}
} else if (type_attributes >= 2) {
LOG(WARNING) << "Receive document with more than 1 type attribute: animated = " << to_string(animated)
<< ", sticker = " << to_string(sticker) << ", video = " << to_string(video)
<< ", audio = " << to_string(audio) << ", file_name = " << file_name << ", dimensions = " << dimensions
<< ", sticker = " << to_string(sticker) << ", custom_emoji = " << to_string(custom_emoji)
<< ", video = " << to_string(video) << ", audio = " << to_string(audio)
<< ", file_name = " << file_name << ", dimensions = " << dimensions
<< ", has_stickers = " << has_stickers;
}
@ -411,7 +421,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
default_extension = Slice("webm");
}
if (file_type == FileType::Encrypted && document_type == Document::Type::Sticker &&
size > get_max_sticker_file_size(sticker_format, false)) {
size > get_max_sticker_file_size(sticker_format, StickerType::Regular, false)) {
document_type = Document::Type::General;
}
@ -490,8 +500,8 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
minithumbnail = string();
}
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);
std::move(thumbnail), dimensions, std::move(sticker),
std::move(custom_emoji), sticker_format, load_data_multipromise_ptr);
break;
case Document::Type::Video:
td_->videos_manager_->create_video(file_id, std::move(minithumbnail), std::move(thumbnail),
@ -569,13 +579,7 @@ void DocumentsManager::create_document(FileId file_id, string minithumbnail, Pho
}
const DocumentsManager::GeneralDocument *DocumentsManager::get_document(FileId file_id) const {
auto document = documents_.find(file_id);
if (document == documents_.end()) {
return nullptr;
}
CHECK(document->second->file_id == file_id);
return document->second.get();
return documents_.get_pointer(file_id);
}
bool DocumentsManager::has_input_media(FileId file_id, FileId thumbnail_file_id, bool is_secret) const {
@ -696,7 +700,7 @@ FileId DocumentsManager::dup_document(FileId new_id, FileId old_id) {
return new_id;
}
void DocumentsManager::merge_documents(FileId new_id, FileId old_id, bool can_delete_old) {
void DocumentsManager::merge_documents(FileId new_id, FileId old_id) {
CHECK(old_id.is_valid() && new_id.is_valid());
CHECK(new_id != old_id);
@ -704,27 +708,15 @@ void DocumentsManager::merge_documents(FileId new_id, FileId old_id, bool can_de
const GeneralDocument *old_ = get_document(old_id);
CHECK(old_ != nullptr);
auto new_it = documents_.find(new_id);
if (new_it == documents_.end()) {
auto &old = documents_[old_id];
if (!can_delete_old) {
dup_document(new_id, old_id);
} else {
old->file_id = new_id;
documents_.emplace(new_id, std::move(old));
}
const auto *new_ = get_document(new_id);
if (new_ == nullptr) {
dup_document(new_id, old_id);
} else {
GeneralDocument *new_ = new_it->second.get();
CHECK(new_ != nullptr);
if (old_->thumbnail != new_->thumbnail) {
// LOG_STATUS(td_->file_manager_->merge(new_->thumbnail.file_id, old_->thumbnail.file_id));
}
}
LOG_STATUS(td_->file_manager_->merge(new_id, old_id));
if (can_delete_old) {
documents_.erase(old_id);
}
}
void DocumentsManager::memory_stats(vector<string> &output) {

View File

@ -19,7 +19,7 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/WaitFreeHashMap.h"
#include <utility>
@ -108,7 +108,7 @@ class DocumentsManager {
FileId dup_document(FileId new_id, FileId old_id);
void merge_documents(FileId new_id, FileId old_id, bool can_delete_old);
void merge_documents(FileId new_id, FileId old_id);
template <class StorerT>
void store_document(FileId file_id, StorerT &storer) const;
@ -133,7 +133,7 @@ class DocumentsManager {
FileId on_get_document(unique_ptr<GeneralDocument> new_document, bool replace);
Td *td_;
FlatHashMap<FileId, unique_ptr<GeneralDocument>, FileIdHash> documents_; // file_id -> GeneralDocument
WaitFreeHashMap<FileId, unique_ptr<GeneralDocument>, FileIdHash> documents_; // file_id -> GeneralDocument
};
} // namespace td

View File

@ -21,10 +21,8 @@ namespace td {
template <class StorerT>
void DocumentsManager::store_document(FileId file_id, StorerT &storer) const {
LOG(DEBUG) << "Store document " << file_id;
auto it = documents_.find(file_id);
CHECK(it != documents_.end());
const GeneralDocument *document = it->second.get();
const GeneralDocument *document = get_document(file_id);
CHECK(document != nullptr);
store(document->file_name, storer);
store(document->mime_type, storer);
store(document->minithumbnail, storer);
@ -60,7 +58,6 @@ FileId DocumentsManager::parse_document(ParserT &parser) {
parse(document->thumbnail, parser);
parse(document->file_id, parser);
LOG(DEBUG) << "Parsed document " << document->file_id;
if (parser.get_error() != nullptr || !document->file_id.is_valid()) {
return FileId();
}

View File

@ -79,18 +79,18 @@ class DownloadManagerImpl final : public DownloadManager {
}
void after_get_difference() final {
load_database_files();
load_database_files("after_get_difference");
}
void toggle_is_paused(FileId file_id, bool is_paused, Promise<Unit> promise) final {
TRY_STATUS_PROMISE(promise, check_is_active());
TRY_STATUS_PROMISE(promise, check_is_active("toggle_is_paused"));
TRY_RESULT_PROMISE(promise, file_info_ptr, get_file_info(file_id));
toggle_is_paused(*file_info_ptr, is_paused);
promise.set_value(Unit());
}
void toggle_all_is_paused(bool is_paused, Promise<Unit> promise) final {
TRY_STATUS_PROMISE(promise, check_is_active());
TRY_STATUS_PROMISE(promise, check_is_active("toggle_all_is_paused"));
vector<FileId> to_toggle;
for (auto &it : files_) {
@ -118,7 +118,7 @@ class DownloadManagerImpl final : public DownloadManager {
}
void remove_all_files(bool only_active, bool only_completed, bool delete_from_cache, Promise<Unit> promise) final {
TRY_STATUS_PROMISE(promise, check_is_active());
TRY_STATUS_PROMISE(promise, check_is_active("remove_all_files"));
vector<FileId> to_remove;
for (auto &it : files_) {
FileInfo &file_info = *it.second;
@ -138,7 +138,7 @@ class DownloadManagerImpl final : public DownloadManager {
void add_file(FileId file_id, FileSourceId file_source_id, string search_text, int8 priority,
Promise<td_api::object_ptr<td_api::file>> promise) final {
TRY_STATUS_PROMISE(promise, check_is_active());
TRY_STATUS_PROMISE(promise, check_is_active("add_file"));
remove_file_impl(file_id, {}, false);
@ -163,7 +163,7 @@ class DownloadManagerImpl final : public DownloadManager {
return;
}
if (check_is_active().is_error()) {
if (check_is_active("change_search_text").is_error()) {
return;
}
auto r_file_info_ptr = get_file_info(file_id, file_source_id);
@ -192,7 +192,7 @@ class DownloadManagerImpl final : public DownloadManager {
void do_search(string query, bool only_active, bool only_completed, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundFileDownloads>> promise, Result<Unit>) {
TRY_STATUS_PROMISE(promise, G()->close_status());
TRY_STATUS_PROMISE(promise, check_is_active());
TRY_STATUS_PROMISE(promise, check_is_active("do_search"));
if (!is_search_inited_) {
Promise<Unit> lock;
@ -358,8 +358,8 @@ class DownloadManagerImpl final : public DownloadManager {
Counters counters_;
Counters sent_counters_;
FileCounters file_counters_;
const char *database_loading_source_ = nullptr;
bool is_inited_{false};
bool is_database_being_loaded_{false};
bool is_database_loaded_{false};
bool is_search_inited_{false};
int64 max_download_id_{0};
@ -462,7 +462,7 @@ class DownloadManagerImpl final : public DownloadManager {
add_file_info(std::move(file_info), "");
}
void load_database_files() {
void load_database_files(const char *source) {
if (is_database_loaded_) {
return;
}
@ -472,8 +472,8 @@ class DownloadManagerImpl final : public DownloadManager {
return;
}
CHECK(is_inited_);
CHECK(!is_database_being_loaded_);
is_database_being_loaded_ = true;
LOG_CHECK(database_loading_source_ == nullptr) << database_loading_source_ << ' ' << source;
database_loading_source_ = source;
LOG(INFO) << "Start Download Manager database loading";
@ -489,7 +489,7 @@ class DownloadManagerImpl final : public DownloadManager {
}
is_database_loaded_ = true;
is_database_being_loaded_ = false;
database_loading_source_ = nullptr;
update_counters();
check_completed_downloads_size();
@ -574,7 +574,7 @@ class DownloadManagerImpl final : public DownloadManager {
Status remove_file_impl(FileId file_id, FileSourceId file_source_id, bool delete_from_cache) {
LOG(INFO) << "Remove from downloads file " << file_id << " from " << file_source_id;
TRY_STATUS(check_is_active());
TRY_STATUS(check_is_active("remove_file_impl"));
TRY_RESULT(file_info_ptr, get_file_info(file_id, file_source_id));
auto &file_info = *file_info_ptr;
auto download_id = file_info.download_id;
@ -603,7 +603,7 @@ class DownloadManagerImpl final : public DownloadManager {
}
Status remove_file_if_finished_impl(FileId file_id) {
TRY_STATUS(check_is_active());
TRY_STATUS(check_is_active("remove_file_if_finished_impl"));
TRY_RESULT(file_info_ptr, get_file_info(file_id, {}));
if (!is_completed(*file_info_ptr)) {
return Status::Error("File is active");
@ -803,13 +803,13 @@ class DownloadManagerImpl final : public DownloadManager {
register_file_info(file_info);
}
Status check_is_active() {
Status check_is_active(const char *source) {
if (!callback_) {
LOG(ERROR) << "DownloadManager is closed";
LOG(ERROR) << "DownloadManager is closed in " << source;
return Status::Error(500, "DownloadManager is closed");
}
CHECK(is_inited_);
load_database_files();
load_database_files(source);
return Status::OK();
}
};

View File

@ -66,7 +66,7 @@ unique_ptr<DraftMessage> get_draft_message(ContactsManager *contacts_manager,
}
}
Result<unique_ptr<DraftMessage>> get_draft_message(ContactsManager *contacts_manager, DialogId dialog_id,
Result<unique_ptr<DraftMessage>> get_draft_message(Td *td, DialogId dialog_id,
td_api::object_ptr<td_api::draftMessage> &&draft_message) {
if (draft_message == nullptr) {
return nullptr;
@ -85,7 +85,7 @@ Result<unique_ptr<DraftMessage>> get_draft_message(ContactsManager *contacts_man
return Status::Error(400, "Input message content type must be InputMessageText");
}
TRY_RESULT(message_content,
process_input_message_text(contacts_manager, dialog_id, std::move(input_message_content), false, true));
process_input_message_text(td, dialog_id, std::move(input_message_content), false, true));
result->input_message_text = std::move(message_content);
}

View File

@ -18,6 +18,7 @@
namespace td {
class ContactsManager;
class Td;
class DraftMessage {
public:
@ -31,7 +32,7 @@ td_api::object_ptr<td_api::draftMessage> get_draft_message_object(const unique_p
unique_ptr<DraftMessage> get_draft_message(ContactsManager *contacts_manager,
telegram_api::object_ptr<telegram_api::DraftMessage> &&draft_message_ptr);
Result<unique_ptr<DraftMessage>> get_draft_message(ContactsManager *contacts_manager, DialogId dialog_id,
Result<unique_ptr<DraftMessage>> get_draft_message(Td *td, DialogId dialog_id,
td_api::object_ptr<td_api::draftMessage> &&draft_message);
} // namespace td

View File

@ -7,6 +7,7 @@
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/AttachMenuManager.h"
#include "td/telegram/BackgroundManager.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ContactsManager.h"
@ -33,10 +34,17 @@ namespace td {
int VERBOSITY_NAME(file_references) = VERBOSITY_NAME(INFO);
FileReferenceManager::FileReferenceManager(ActorShared<> parent) : parent_(std::move(parent)) {
}
FileReferenceManager::~FileReferenceManager() {
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_sources_);
}
void FileReferenceManager::tear_down() {
parent_.reset();
}
bool FileReferenceManager::is_file_reference_error(const Status &error) {
return error.is_error() && error.code() == 400 && begins_with(error.message(), "FILE_REFERENCE_");
}
@ -67,6 +75,8 @@ fileSourceBasicGroupFull basic_group_id:int32 = FileSource; // repa
fileSourceSupergroupFull supergroup_id:int32 = FileSource; // repaired with messages.getFullChannel
fileSourceAppConfig = FileSource; // repaired with help.getAppConfig, not reliable
fileSourceSavedRingtones = FileSource; // repaired with account.getSavedRingtones
fileSourceUserFull = FileSource; // repaired with users.getFullUser
fileSourceAttachmentMenuBot = FileSource; // repaired with messages.getAttachMenuBot
*/
FileSourceId FileReferenceManager::get_current_file_source_id() const {
@ -131,6 +141,21 @@ FileSourceId FileReferenceManager::create_app_config_file_source() {
return add_file_source_id(source, "app config");
}
FileSourceId FileReferenceManager::create_saved_ringtones_file_source() {
FileSourceSavedRingtones source;
return add_file_source_id(source, "saved notification sounds");
}
FileSourceId FileReferenceManager::create_user_full_file_source(UserId user_id) {
FileSourceUserFull source{user_id};
return add_file_source_id(source, PSLICE() << "full " << user_id);
}
FileSourceId FileReferenceManager::create_attach_menu_bot_file_source(UserId user_id) {
FileSourceAttachMenuBot source{user_id};
return add_file_source_id(source, PSLICE() << "attachment menu bot " << user_id);
}
bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_source_id) {
CHECK(node_id.is_valid());
bool is_added = nodes_[node_id].file_source_ids.add(file_source_id);
@ -138,11 +163,6 @@ bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_sou
return is_added;
}
FileSourceId FileReferenceManager::create_saved_ringtones_file_source() {
FileSourceSavedRingtones source;
return add_file_source_id(source, "saved notification sounds");
}
bool FileReferenceManager::remove_file_source(NodeId node_id, FileSourceId file_source_id) {
CHECK(node_id.is_valid());
bool is_removed = nodes_[node_id].file_source_ids.remove(file_source_id);
@ -321,6 +341,14 @@ void FileReferenceManager::send_query(Destination dest, FileSourceId file_source
[&](const FileSourceSavedRingtones &source) {
send_closure_later(G()->notification_settings_manager(), &NotificationSettingsManager::repair_saved_ringtones,
std::move(promise));
},
[&](const FileSourceUserFull &source) {
send_closure_later(G()->contacts_manager(), &ContactsManager::reload_user_full, source.user_id,
std::move(promise));
},
[&](const FileSourceAttachMenuBot &source) {
send_closure_later(G()->attach_menu_manager(), &AttachMenuManager::reload_attach_menu_bot, source.user_id,
std::move(promise));
}));
}

View File

@ -26,6 +26,7 @@
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/Variant.h"
#include "td/utils/WaitFreeVector.h"
namespace td {
@ -35,7 +36,7 @@ extern int VERBOSITY_NAME(file_references);
class FileReferenceManager final : public Actor {
public:
FileReferenceManager() = default;
explicit FileReferenceManager(ActorShared<> parent);
FileReferenceManager(const FileReferenceManager &) = delete;
FileReferenceManager &operator=(const FileReferenceManager &) = delete;
FileReferenceManager(FileReferenceManager &&) = delete;
@ -60,6 +61,8 @@ class FileReferenceManager final : public Actor {
FileSourceId create_channel_full_file_source(ChannelId channel_id);
FileSourceId create_app_config_file_source();
FileSourceId create_saved_ringtones_file_source();
FileSourceId create_user_full_file_source(UserId user_id);
FileSourceId create_attach_menu_bot_file_source(UserId user_id);
using NodeId = FileId;
void repair_file_reference(NodeId node_id, Promise<> promise);
@ -157,18 +160,27 @@ class FileReferenceManager final : public Actor {
struct FileSourceSavedRingtones {
// empty
};
struct FileSourceUserFull {
UserId user_id;
};
struct FileSourceAttachMenuBot {
UserId user_id;
};
// append only
using FileSource = Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto,
FileSourceWallpapers, FileSourceWebPage, FileSourceSavedAnimations,
FileSourceRecentStickers, FileSourceFavoriteStickers, FileSourceBackground,
FileSourceChatFull, FileSourceChannelFull, FileSourceAppConfig, FileSourceSavedRingtones>;
vector<FileSource> file_sources_;
using FileSource =
Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto, FileSourceWallpapers,
FileSourceWebPage, FileSourceSavedAnimations, FileSourceRecentStickers, FileSourceFavoriteStickers,
FileSourceBackground, FileSourceChatFull, FileSourceChannelFull, FileSourceAppConfig,
FileSourceSavedRingtones, FileSourceUserFull, FileSourceAttachMenuBot>;
WaitFreeVector<FileSource> file_sources_;
int64 query_generation_{0};
FlatHashMap<NodeId, Node, FileIdHash> nodes_;
ActorShared<> parent_;
void run_node(NodeId node);
void send_query(Destination dest, FileSourceId file_source_id);
Destination on_query_result(Destination dest, FileSourceId file_source_id, Status status, int32 sub = 0);
@ -177,6 +189,8 @@ class FileReferenceManager final : public Actor {
FileSourceId add_file_source_id(T source, Slice source_str);
FileSourceId get_current_file_source_id() const;
void tear_down() final;
};
} // namespace td

View File

@ -7,6 +7,7 @@
#pragma once
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/AttachMenuManager.h"
#include "td/telegram/BackgroundManager.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChatId.h"
@ -51,7 +52,9 @@ void FileReferenceManager::store_file_source(FileSourceId file_source_id, Storer
},
[&](const FileSourceChatFull &source) { td::store(source.chat_id, storer); },
[&](const FileSourceChannelFull &source) { td::store(source.channel_id, storer); },
[&](const FileSourceAppConfig &source) {}, [&](const FileSourceSavedRingtones &source) {}));
[&](const FileSourceAppConfig &source) {}, [&](const FileSourceSavedRingtones &source) {},
[&](const FileSourceUserFull &source) { td::store(source.user_id, storer); },
[&](const FileSourceAttachMenuBot &source) { td::store(source.user_id, storer); }));
}
template <class ParserT>
@ -117,6 +120,16 @@ FileSourceId FileReferenceManager::parse_file_source(Td *td, ParserT &parser) {
return td->stickers_manager_->get_app_config_file_source_id();
case 13:
return td->notification_settings_manager_->get_saved_ringtones_file_source_id();
case 14: {
UserId user_id;
td::parse(user_id, parser);
return td->contacts_manager_->get_user_full_file_source_id(user_id);
}
case 15: {
UserId user_id;
td::parse(user_id, parser);
return td->attach_menu_manager_->get_attach_menu_bot_file_source_id(user_id);
}
default:
parser.set_error("Invalid type in FileSource");
return FileSourceId();

View File

@ -32,6 +32,7 @@
namespace td {
class AnimationsManager;
class AttachMenuManager;
class BackgroundManager;
class CallManager;
class ConfigManager;
@ -233,6 +234,13 @@ class Global final : public ActorContext {
animations_manager_ = animations_manager;
}
ActorId<AttachMenuManager> attach_menu_manager() const {
return attach_menu_manager_;
}
void set_attach_menu_manager(ActorId<AttachMenuManager> attach_menu_manager) {
attach_menu_manager_ = attach_menu_manager;
}
ActorId<BackgroundManager> background_manager() const {
return background_manager_;
}
@ -512,6 +520,7 @@ class Global final : public ActorContext {
ActorId<Td> td_;
ActorId<AnimationsManager> animations_manager_;
ActorId<AttachMenuManager> attach_menu_manager_;
ActorId<BackgroundManager> background_manager_;
ActorId<CallManager> call_manager_;
ActorId<ConfigManager> config_manager_;

View File

@ -1453,16 +1453,16 @@ void GroupCallManager::reload_group_call(InputGroupCallId input_group_call_id,
void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_id,
Result<tl_object_ptr<telegram_api::phone_groupCall>> &&result) {
if (G()->close_flag()) {
result = Global::request_aborted_error();
}
auto it = load_group_call_queries_.find(input_group_call_id);
CHECK(it != load_group_call_queries_.end());
CHECK(!it->second.empty());
auto promises = std::move(it->second);
load_group_call_queries_.erase(it);
if (G()->close_flag()) {
result = Global::request_aborted_error();
}
if (result.is_ok()) {
td_->contacts_manager_->on_get_users(std::move(result.ok_ref()->users_), "finish_get_group_call");
td_->contacts_manager_->on_get_chats(std::move(result.ok_ref()->chats_), "finish_get_group_call");

View File

@ -354,8 +354,7 @@ Result<tl_object_ptr<telegram_api::InputBotInlineMessage>> InlineQueriesManager:
auto constructor_id = input_message_content->get_id();
if (constructor_id == td_api::inputMessageText::ID) {
TRY_RESULT(input_message_text, process_input_message_text(td_->contacts_manager_.get(), DialogId(),
std::move(input_message_content), true));
TRY_RESULT(input_message_text, process_input_message_text(td_, DialogId(), std::move(input_message_content), true));
int32 flags = 0;
if (input_reply_markup != nullptr) {
flags |= telegram_api::inputBotInlineMessageText::REPLY_MARKUP_MASK;
@ -1126,6 +1125,25 @@ tl_object_ptr<td_api::thumbnail> copy(const td_api::thumbnail &obj) {
return td_api::make_object<td_api::thumbnail>(std::move(format), obj.width_, obj.height_, copy(obj.file_));
}
static tl_object_ptr<td_api::thumbnail> copy_thumbnail(const tl_object_ptr<td_api::thumbnail> &obj) {
return copy(obj);
}
template <>
tl_object_ptr<td_api::StickerFormat> copy(const td_api::StickerFormat &obj) {
switch (obj.get_id()) {
case td_api::stickerFormatWebp::ID:
return td_api::make_object<td_api::stickerFormatWebp>();
case td_api::stickerFormatTgs::ID:
return td_api::make_object<td_api::stickerFormatTgs>();
case td_api::stickerFormatWebm::ID:
return td_api::make_object<td_api::stickerFormatWebm>();
default:
UNREACHABLE();
}
return nullptr;
}
template <>
tl_object_ptr<td_api::MaskPoint> copy(const td_api::MaskPoint &obj) {
switch (obj.get_id()) {
@ -1151,16 +1169,12 @@ tl_object_ptr<td_api::maskPosition> copy(const td_api::maskPosition &obj) {
template <>
tl_object_ptr<td_api::StickerType> copy(const td_api::StickerType &obj) {
switch (obj.get_id()) {
case td_api::stickerTypeStatic::ID:
return td_api::make_object<td_api::stickerTypeStatic>();
case td_api::stickerTypeAnimated::ID:
return td_api::make_object<td_api::stickerTypeAnimated>();
case td_api::stickerTypeVideo::ID:
return td_api::make_object<td_api::stickerTypeVideo>();
case td_api::stickerTypeMask::ID: {
auto &mask_position = static_cast<const td_api::stickerTypeMask &>(obj).mask_position_;
return td_api::make_object<td_api::stickerTypeMask>(copy(mask_position));
}
case td_api::stickerTypeRegular::ID:
return td_api::make_object<td_api::stickerTypeRegular>();
case td_api::stickerTypeMask::ID:
return td_api::make_object<td_api::stickerTypeMask>();
case td_api::stickerTypeCustomEmoji::ID:
return td_api::make_object<td_api::stickerTypeCustomEmoji>();
default:
UNREACHABLE();
}
@ -1205,6 +1219,26 @@ static tl_object_ptr<td_api::closedVectorPath> copy_closed_vector_path(
return copy(obj);
}
template <>
tl_object_ptr<td_api::SpeechRecognitionResult> copy(const td_api::SpeechRecognitionResult &obj) {
switch (obj.get_id()) {
case td_api::speechRecognitionResultPending::ID:
return td_api::make_object<td_api::speechRecognitionResultPending>(
static_cast<const td_api::speechRecognitionResultPending &>(obj).partial_text_);
case td_api::speechRecognitionResultText::ID:
return td_api::make_object<td_api::speechRecognitionResultText>(
static_cast<const td_api::speechRecognitionResultText &>(obj).text_);
case td_api::speechRecognitionResultError::ID: {
auto *error = static_cast<const td_api::speechRecognitionResultError &>(obj).error_.get();
return td_api::make_object<td_api::speechRecognitionResultError>(
td_api::make_object<td_api::error>(error->code_, error->message_));
}
default:
UNREACHABLE();
}
return nullptr;
}
template <>
tl_object_ptr<td_api::animation> copy(const td_api::animation &obj) {
return td_api::make_object<td_api::animation>(obj.duration_, obj.width_, obj.height_, obj.file_name_, obj.mime_type_,
@ -1216,7 +1250,7 @@ template <>
tl_object_ptr<td_api::audio> copy(const td_api::audio &obj) {
return td_api::make_object<td_api::audio>(obj.duration_, obj.title_, obj.performer_, obj.file_name_, obj.mime_type_,
copy(obj.album_cover_minithumbnail_), copy(obj.album_cover_thumbnail_),
copy(obj.audio_));
transform(obj.external_album_covers_, copy_thumbnail), copy(obj.audio_));
}
template <>
@ -1233,9 +1267,10 @@ tl_object_ptr<td_api::photo> copy(const td_api::photo &obj) {
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_),
return td_api::make_object<td_api::sticker>(obj.set_id_, obj.width_, obj.height_, obj.emoji_, copy(obj.format_),
copy(obj.type_), copy(obj.mask_position_), obj.custom_emoji_id_,
transform(obj.outline_, copy_closed_vector_path), copy(obj.thumbnail_),
copy(obj.premium_animation_), copy(obj.sticker_));
obj.is_premium_, copy(obj.premium_animation_), copy(obj.sticker_));
}
template <>
@ -1247,8 +1282,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_, obj.is_recognized_,
obj.recognized_text_, copy(obj.voice_));
return td_api::make_object<td_api::voiceNote>(obj.duration_, obj.waveform_, obj.mime_type_,
copy(obj.speech_recognition_result_), copy(obj.voice_));
}
template <>

View File

@ -9,6 +9,7 @@
#include "td/telegram/ConfigShared.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/Td.h"
#include "td/utils/common.h"
@ -23,7 +24,7 @@ bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs) {
return !(lhs == rhs);
}
Result<InputMessageText> process_input_message_text(const ContactsManager *contacts_manager, DialogId dialog_id,
Result<InputMessageText> process_input_message_text(const Td *td, DialogId dialog_id,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
bool is_bot, bool for_draft) {
CHECK(input_message_content != nullptr);
@ -38,8 +39,9 @@ Result<InputMessageText> process_input_message_text(const ContactsManager *conta
return Status::Error(400, "Message text can't be empty");
}
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_)));
auto need_skip_bot_commands = need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot);
TRY_RESULT(entities,
get_message_entities(td->contacts_manager_.get(), std::move(input_message_text->text_->entities_)));
auto need_skip_bot_commands = need_always_skip_bot_commands(td->contacts_manager_.get(), dialog_id, is_bot);
bool parse_markdown = G()->shared_config().get_option_boolean("always_parse_markdown");
TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, parse_markdown,
need_skip_bot_commands, is_bot || for_draft || parse_markdown, for_draft));
@ -51,6 +53,7 @@ Result<InputMessageText> process_input_message_text(const ContactsManager *conta
is_bot || for_draft, for_draft)
.ensure();
}
remove_unallowed_entities(td, result.text, dialog_id);
return std::move(result);
}

View File

@ -14,7 +14,7 @@
namespace td {
class ContactsManager;
class Td;
class InputMessageText {
public:
@ -30,7 +30,7 @@ class InputMessageText {
bool operator==(const InputMessageText &lhs, const InputMessageText &rhs);
bool operator!=(const InputMessageText &lhs, const InputMessageText &rhs);
Result<InputMessageText> process_input_message_text(const ContactsManager *contacts_manager, DialogId dialog_id,
Result<InputMessageText> process_input_message_text(const Td *td, DialogId dialog_id,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content,
bool is_bot, bool for_draft = false) TD_WARN_UNUSED_RESULT;

View File

@ -487,6 +487,12 @@ class LinkManager::InternalLinkQrCodeAuthentication final : public InternalLink
}
};
class LinkManager::InternalLinkRestorePurchases final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeRestorePurchases>();
}
};
class LinkManager::InternalLinkSettings final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeSettings>();
@ -1068,6 +1074,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
if (has_arg("token")) {
return td::make_unique<InternalLinkQrCodeAuthentication>();
}
} else if (path.size() == 1 && path[0] == "restore_purchases") {
// restore_purchases
return td::make_unique<InternalLinkRestorePurchases>();
} 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_);
@ -1107,8 +1116,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
<< url_encode(get_url_query_hash(true, url_query)));
}
} else if (path.size() == 1 && path[0] == "addstickers") {
} else if (path.size() == 1 && (path[0] == "addstickers" || path[0] == "addemoji")) {
// addstickers?set=<name>
// addemoji?set=<name>
if (has_arg("set")) {
return td::make_unique<InternalLinkStickerSet>(get_arg("set"));
}
@ -1246,9 +1256,10 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
<< url_encode(get_url_query_hash(false, url_query)));
}
}
} else if (path[0] == "addstickers") {
} else if (path[0] == "addstickers" || path[0] == "addemoji") {
if (path.size() >= 2 && !path[1].empty()) {
// /addstickers/<name>
// /addemoji/<name>
return td::make_unique<InternalLinkStickerSet>(path[1]);
}
} else if (path[0] == "setlanguage") {
@ -1577,7 +1588,7 @@ UserId LinkManager::get_link_user_id(Slice url) {
}
Slice host("user");
if (!begins_with(url, host)) {
if (!begins_with(url, host) || (url.size() > host.size() && Slice("/?#").find(url[host.size()]) == Slice::npos)) {
return UserId();
}
url.remove_prefix(host.size());
@ -1605,6 +1616,48 @@ UserId LinkManager::get_link_user_id(Slice url) {
return UserId();
}
Result<int64> LinkManager::get_link_custom_emoji_document_id(Slice url) {
string lower_cased_url = to_lower(url);
url = lower_cased_url;
Slice link_scheme("tg:");
if (!begins_with(url, link_scheme)) {
return Status::Error(400, "Custom emoji URL must have scheme tg");
}
url.remove_prefix(link_scheme.size());
if (begins_with(url, "//")) {
url.remove_prefix(2);
}
Slice host("emoji");
if (!begins_with(url, host) || (url.size() > host.size() && Slice("/?#").find(url[host.size()]) == Slice::npos)) {
return Status::Error(400, PSLICE() << "Custom emoji URL must have host \"" << host << '"');
}
url.remove_prefix(host.size());
if (begins_with(url, "/")) {
url.remove_prefix(1);
}
if (!begins_with(url, "?")) {
return Status::Error(400, "Custom emoji URL must have an emoji identifier");
}
url.remove_prefix(1);
url.truncate(url.find('#'));
for (auto parameter : full_split(url, '&')) {
Slice key;
Slice value;
std::tie(key, value) = split(parameter, '=');
if (key == Slice("id")) {
auto r_document_id = to_integer_safe<int64>(value);
if (r_document_id.is_error() || r_document_id.ok() == 0) {
return Status::Error(400, "Invalid custom emoji identifier specified");
}
return r_document_id.ok();
}
}
return Status::Error(400, "Custom emoji URL must have an emoji identifier");
}
Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
if (url.empty()) {
return Status::Error("URL must be non-empty");

View File

@ -80,6 +80,8 @@ class LinkManager final : public Actor {
static UserId get_link_user_id(Slice url);
static Result<int64> get_link_custom_emoji_document_id(Slice url);
static Result<MessageLinkInfo> get_message_link_info(Slice url);
private:
@ -110,6 +112,7 @@ class LinkManager final : public Actor {
class InternalLinkProxy;
class InternalLinkPublicDialog;
class InternalLinkQrCodeAuthentication;
class InternalLinkRestorePurchases;
class InternalLinkSettings;
class InternalLinkStickerSet;
class InternalLinkTheme;

View File

@ -60,6 +60,7 @@
#include "td/telegram/StickerFormat.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StickersManager.hpp"
#include "td/telegram/StickerType.h"
#include "td/telegram/Td.h"
#include "td/telegram/TopDialogManager.h"
#include "td/telegram/UserId.h"
@ -453,7 +454,7 @@ class MessageChatSetTtl final : public MessageContent {
class MessageUnsupported final : public MessageContent {
public:
static constexpr int32 CURRENT_VERSION = 12;
static constexpr int32 CURRENT_VERSION = 13;
int32 version = CURRENT_VERSION;
MessageUnsupported() = default;
@ -782,6 +783,22 @@ class MessageWebViewDataReceived final : public MessageContent {
}
};
class MessageGiftPremium final : public MessageContent {
public:
string currency;
int64 amount = 0;
int32 months = 0;
MessageGiftPremium() = default;
MessageGiftPremium(string &&currency, int64 amount, int32 months)
: currency(std::move(currency)), amount(amount), months(months) {
}
MessageContentType get_type() const final {
return MessageContentType::GiftPremium;
}
};
template <class StorerT>
static void store(const MessageContent *content, StorerT &storer) {
CHECK(content != nullptr);
@ -1101,6 +1118,15 @@ static void store(const MessageContent *content, StorerT &storer) {
store(m->data, storer);
break;
}
case MessageContentType::GiftPremium: {
const auto *m = static_cast<const MessageGiftPremium *>(content);
BEGIN_STORE_FLAGS();
END_STORE_FLAGS();
store(m->currency, storer);
store(m->amount, storer);
store(m->months, storer);
break;
}
default:
UNREACHABLE();
}
@ -1111,6 +1137,7 @@ static void parse_caption(FormattedText &caption, ParserT &parser) {
parse(caption.text, parser);
if (parser.version() >= static_cast<int32>(Version::AddCaptionEntities)) {
parse(caption.entities, parser);
remove_empty_entities(caption.entities);
} else {
if (!check_utf8(caption.text)) {
caption.text.clear();
@ -1547,6 +1574,16 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
content = std::move(m);
break;
}
case MessageContentType::GiftPremium: {
auto m = make_unique<MessageGiftPremium>();
BEGIN_PARSE_FLAGS();
END_PARSE_FLAGS();
parse(m->currency, parser);
parse(m->amount, parser);
parse(m->months, parser);
content = std::move(m);
break;
}
default:
LOG(FATAL) << "Have unknown message content type " << static_cast<int32>(content_type);
}
@ -1725,8 +1762,8 @@ static Result<InputMessageContent> create_input_message_content(
bool is_bot = td->auth_manager_->is_bot();
switch (input_message_content->get_id()) {
case td_api::inputMessageText::ID: {
TRY_RESULT(input_message_text, process_input_message_text(td->contacts_manager_.get(), dialog_id,
std::move(input_message_content), is_bot));
TRY_RESULT(input_message_text,
process_input_message_text(td, dialog_id, std::move(input_message_content), is_bot));
disable_web_page_preview = input_message_text.disable_web_page_preview;
clear_draft = input_message_text.clear_draft;
@ -1847,7 +1884,7 @@ static Result<InputMessageContent> create_input_message_content(
td->stickers_manager_->create_sticker(file_id, FileId(), string(), thumbnail,
get_dimensions(input_sticker->width_, input_sticker->height_, nullptr),
nullptr, StickerFormat::Unknown, nullptr);
nullptr, nullptr, StickerFormat::Unknown, nullptr);
content = make_unique<MessageSticker>(file_id, is_premium);
break;
@ -2165,6 +2202,7 @@ bool can_have_input_media(const Td *td, const MessageContent *content, bool is_s
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return false;
case MessageContentType::Animation:
case MessageContentType::Audio:
@ -2285,6 +2323,7 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
break;
default:
UNREACHABLE();
@ -2403,6 +2442,7 @@ static tl_object_ptr<telegram_api::InputMedia> get_input_media_impl(
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
break;
default:
UNREACHABLE();
@ -2563,6 +2603,7 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) {
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
break;
default:
UNREACHABLE();
@ -2675,6 +2716,9 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
if (!permissions.can_send_stickers()) {
return Status::Error(400, "Not enough rights to send stickers to the chat");
}
if (get_message_content_sticker_type(td, content) == StickerType::CustomEmoji) {
return Status::Error(400, "Can't send emoji stickers in messages");
}
break;
case MessageContentType::Text:
if (!permissions.can_send_messages()) {
@ -2695,11 +2739,19 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
if (!permissions.can_send_media()) {
return Status::Error(400, "Not enough rights to send video notes to the chat");
}
if (dialog_type == DialogType::User &&
td->contacts_manager_->get_user_voice_messages_forbidden(dialog_id.get_user_id())) {
return Status::Error(400, "User restricted receiving of voice messages");
}
break;
case MessageContentType::VoiceNote:
if (!permissions.can_send_media()) {
return Status::Error(400, "Not enough rights to send voice notes to the chat");
}
if (dialog_type == DialogType::User &&
td->contacts_manager_->get_user_voice_messages_forbidden(dialog_id.get_user_id())) {
return Status::Error(400, "User restricted receiving of video messages");
}
break;
case MessageContentType::None:
case MessageContentType::ChatCreate:
@ -2733,6 +2785,7 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
UNREACHABLE();
}
return Status::OK();
@ -2861,6 +2914,7 @@ static int32 get_message_content_media_index_mask(const MessageContent *content,
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return 0;
default:
UNREACHABLE();
@ -2873,6 +2927,11 @@ int32 get_message_content_index_mask(const MessageContent *content, const Td *td
return get_message_content_text_index_mask(content) | get_message_content_media_index_mask(content, td, is_outgoing);
}
StickerType get_message_content_sticker_type(const Td *td, const MessageContent *content) {
CHECK(content->get_type() == MessageContentType::Sticker);
return td->stickers_manager_->get_sticker_type(static_cast<const MessageSticker *>(content)->file_id);
}
MessageId get_message_content_pinned_message_id(const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::PinMessage:
@ -3016,14 +3075,14 @@ static void merge_location_access_hash(const Location &first, const Location &se
static bool need_message_text_changed_warning(const MessageText *old_content, const MessageText *new_content) {
if (new_content->text.text == "Unsupported characters" ||
new_content->text.text == "This channel is blocked because it was used to spread pornographic content.") {
// message contained unsupported characters, text is replaced
// message contained unsupported characters or is restricted, text is replaced
return false;
}
if (/* old_message->message_id.is_yet_unsent() && */ !old_content->text.entities.empty() &&
old_content->text.entities[0].offset == 0 &&
(new_content->text.entities.empty() || new_content->text.entities[0].offset != 0) &&
(new_content->text.entities.empty() || new_content->text.entities[0] != old_content->text.entities[0]) &&
old_content->text.text != new_content->text.text && ends_with(old_content->text.text, new_content->text.text)) {
// server has deleted first entity and ltrim the message
// server has deleted first entity and left-trimed the message
return false;
}
return true;
@ -3047,8 +3106,9 @@ static bool need_message_entities_changed_warning(const vector<MessageEntity> &o
continue;
}
if (old_pos < old_entities.size() && old_entities[old_pos].type == MessageEntity::Type::MentionName) {
// server could delete sime MentionName entities
if (old_pos < old_entities.size() && (old_entities[old_pos].type == MessageEntity::Type::MentionName ||
old_entities[old_pos].type == MessageEntity::Type::CustomEmoji)) {
// server can delete some MentionName and CustomEmoji entities
old_pos++;
continue;
}
@ -3105,7 +3165,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageAnimation *>(new_content);
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->animations_manager_->merge_animations(new_->file_id, old_->file_id, false);
td->animations_manager_->merge_animations(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3119,7 +3179,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageAudio *>(new_content);
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->audios_manager_->merge_audios(new_->file_id, old_->file_id, false);
td->audios_manager_->merge_audios(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3141,7 +3201,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageDocument *>(new_content);
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->documents_manager_->merge_documents(new_->file_id, old_->file_id, false);
td->documents_manager_->merge_documents(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3279,7 +3339,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageSticker *>(new_content);
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->stickers_manager_->merge_stickers(new_->file_id, old_->file_id, false);
td->stickers_manager_->merge_stickers(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3305,7 +3365,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageVideo *>(new_content);
if (old_->file_id != new_->file_id) {
if (need_merge_files) {
td->videos_manager_->merge_videos(new_->file_id, old_->file_id, false);
td->videos_manager_->merge_videos(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3319,7 +3379,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageVideoNote *>(new_content);
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);
td->video_notes_manager_->merge_video_notes(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3333,7 +3393,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
const auto *new_ = static_cast<const MessageVoiceNote *>(new_content);
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);
td->voice_notes_manager_->merge_voice_notes(new_->file_id, old_->file_id);
}
need_update = true;
}
@ -3582,6 +3642,14 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
}
break;
}
case MessageContentType::GiftPremium: {
const auto *old_ = static_cast<const MessageGiftPremium *>(old_content);
const auto *new_ = static_cast<const MessageGiftPremium *>(new_content);
if (old_->currency != new_->currency || old_->amount != new_->amount || old_->months != new_->months) {
need_update = true;
}
break;
}
case MessageContentType::Unsupported: {
const auto *old_ = static_cast<const MessageUnsupported *>(old_content);
const auto *new_ = static_cast<const MessageUnsupported *>(new_content);
@ -3607,7 +3675,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::Animation: {
auto content = static_cast<MessageAnimation *>(message_content);
if (new_file_id != content->file_id) {
td->animations_manager_->merge_animations(new_file_id, content->file_id, false);
td->animations_manager_->merge_animations(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3616,7 +3684,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::Audio: {
auto content = static_cast<MessageAudio *>(message_content);
if (new_file_id != content->file_id) {
td->audios_manager_->merge_audios(new_file_id, content->file_id, false);
td->audios_manager_->merge_audios(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3625,7 +3693,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::Document: {
auto content = static_cast<MessageDocument *>(message_content);
if (new_file_id != content->file_id) {
td->documents_manager_->merge_documents(new_file_id, content->file_id, false);
td->documents_manager_->merge_documents(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3647,7 +3715,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::Sticker: {
auto content = static_cast<MessageSticker *>(message_content);
if (new_file_id != content->file_id) {
td->stickers_manager_->merge_stickers(new_file_id, content->file_id, false);
td->stickers_manager_->merge_stickers(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3656,7 +3724,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::Video: {
auto content = static_cast<MessageVideo *>(message_content);
if (new_file_id != content->file_id) {
td->videos_manager_->merge_videos(new_file_id, content->file_id, false);
td->videos_manager_->merge_videos(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3665,7 +3733,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::VideoNote: {
auto content = static_cast<MessageVideoNote *>(message_content);
if (new_file_id != content->file_id) {
td->video_notes_manager_->merge_video_notes(new_file_id, content->file_id, false);
td->video_notes_manager_->merge_video_notes(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3674,7 +3742,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::VoiceNote: {
auto content = static_cast<MessageVoiceNote *>(message_content);
if (new_file_id != content->file_id) {
td->voice_notes_manager_->merge_voice_notes(new_file_id, content->file_id, false);
td->voice_notes_manager_->merge_voice_notes(new_file_id, content->file_id);
content->file_id = new_file_id;
return true;
}
@ -3720,6 +3788,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type;
break;
default:
@ -3730,7 +3799,22 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
}
static bool can_be_animated_emoji(const FormattedText &text) {
return text.entities.empty() && is_emoji(text.text);
if (!is_emoji(text.text)) {
return false;
}
if (text.entities.empty()) {
return true;
}
if (text.entities.size() == 1 && text.entities[0].type == MessageEntity::Type::CustomEmoji &&
text.entities[0].offset == 0 && static_cast<size_t>(text.entities[0].length) == utf8_utf16_length(text.text) &&
text.entities[0].document_id != 0) {
return true;
}
return false;
}
static int64 get_custom_emoji_id(const FormattedText &text) {
return text.entities.empty() ? 0 : text.entities[0].document_id;
}
void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id,
@ -3741,7 +3825,8 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage
if (text->web_page_id.is_valid()) {
td->web_pages_manager_->register_web_page(text->web_page_id, full_message_id, source);
} else if (can_be_animated_emoji(text->text)) {
td->stickers_manager_->register_emoji(text->text.text, full_message_id, source);
td->stickers_manager_->register_emoji(text->text.text, get_custom_emoji_id(text->text), full_message_id,
source);
}
return;
}
@ -3755,6 +3840,9 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage
auto dice = static_cast<const MessageDice *>(content);
return td->stickers_manager_->register_dice(dice->emoji, dice->dice_value, full_message_id, source);
}
case MessageContentType::GiftPremium:
return td->stickers_manager_->register_premium_gift(static_cast<const MessageGiftPremium *>(content)->months,
full_message_id, source);
default:
return;
}
@ -3796,6 +3884,12 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const
return;
}
break;
case MessageContentType::GiftPremium:
if (static_cast<const MessageGiftPremium *>(old_content)->months ==
static_cast<const MessageGiftPremium *>(new_content)->months) {
return;
}
break;
default:
return;
}
@ -3812,7 +3906,8 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa
if (text->web_page_id.is_valid()) {
td->web_pages_manager_->unregister_web_page(text->web_page_id, full_message_id, source);
} else if (can_be_animated_emoji(text->text)) {
td->stickers_manager_->unregister_emoji(text->text.text, full_message_id, source);
td->stickers_manager_->unregister_emoji(text->text.text, get_custom_emoji_id(text->text), full_message_id,
source);
}
return;
}
@ -3826,6 +3921,9 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa
auto dice = static_cast<const MessageDice *>(content);
return td->stickers_manager_->unregister_dice(dice->emoji, dice->dice_value, full_message_id, source);
}
case MessageContentType::GiftPremium:
return td->stickers_manager_->unregister_premium_gift(static_cast<const MessageGiftPremium *>(content)->months,
full_message_id, source);
default:
return;
}
@ -4068,7 +4166,7 @@ unique_ptr<MessageContent> get_secret_message_content(
message_text = message_text + "\n\n" + caption;
}
auto entities = get_message_entities(std::move(secret_entities));
auto entities = get_message_entities(td, std::move(secret_entities), is_premium, load_data_multipromise);
auto status = fix_formatted_text(message_text, entities, true, false, true, td->auth_manager_->is_bot(), false);
if (status.is_error()) {
LOG(WARNING) << "Receive error " << status << " while parsing secret message \"" << message_text
@ -4244,10 +4342,10 @@ unique_ptr<MessageContent> get_secret_message_content(
unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
tl_object_ptr<telegram_api::MessageMedia> &&media_ptr,
DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id,
int32 *ttl, bool *disable_web_page_preview) {
int32 *ttl, bool *disable_web_page_preview, const char *source) {
if (!td->auth_manager_->was_authorized() && !G()->close_flag() && media_ptr != nullptr &&
media_ptr->get_id() != telegram_api::messageMediaEmpty::ID) {
LOG(ERROR) << "Receive without authorization " << to_string(media_ptr);
LOG(ERROR) << "Receive without authorization from " << source << ": " << to_string(media_ptr);
media_ptr = nullptr;
}
if (disable_web_page_preview != nullptr) {
@ -4258,7 +4356,7 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
switch (constructor_id) {
case telegram_api::messageMediaEmpty::ID:
if (message.text.empty()) {
LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id;
LOG(ERROR) << "Receive empty message text and media from " << source;
}
if (disable_web_page_preview != nullptr) {
*disable_web_page_preview = true;
@ -4268,7 +4366,8 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
auto media = move_tl_object_as<telegram_api::messageMediaPhoto>(media_ptr);
if (media->photo_ == nullptr) {
if ((media->flags_ & telegram_api::messageMediaPhoto::TTL_SECONDS_MASK) == 0) {
LOG(ERROR) << "Receive messageMediaPhoto without photo and TTL: " << oneline(to_string(media));
LOG(ERROR) << "Receive messageMediaPhoto without photo and TTL from " << source << ": "
<< oneline(to_string(media));
break;
}
@ -4314,7 +4413,7 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
int32 period = media->period_;
if (period <= 0) {
LOG(ERROR) << "Receive wrong live location period = " << period;
LOG(ERROR) << "Receive wrong live location period = " << period << " from " << source;
return make_unique<MessageLocation>(std::move(location));
}
return make_unique<MessageLiveLocation>(std::move(location), period, media->heading_,
@ -4345,7 +4444,8 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
auto media = move_tl_object_as<telegram_api::messageMediaDocument>(media_ptr);
if (media->document_ == nullptr) {
if ((media->flags_ & telegram_api::messageMediaDocument::TTL_SECONDS_MASK) == 0) {
LOG(ERROR) << "Receive messageMediaDocument without document and TTL: " << oneline(to_string(media));
LOG(ERROR) << "Receive messageMediaDocument without document and TTL from " << source << ": "
<< oneline(to_string(media));
break;
}
@ -4387,8 +4487,8 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
}
case telegram_api::messageMediaPoll::ID: {
auto media = move_tl_object_as<telegram_api::messageMediaPoll>(media_ptr);
auto poll_id = td->poll_manager_->on_get_poll(PollId(), std::move(media->poll_), std::move(media->results_),
"messageMediaPoll");
auto poll_id =
td->poll_manager_->on_get_poll(PollId(), std::move(media->poll_), std::move(media->results_), source);
if (!poll_id.is_valid()) {
break;
}
@ -4582,8 +4682,13 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
CHECK(result->file_id.is_valid());
return std::move(result);
}
case MessageContentType::Text:
return make_unique<MessageText>(*static_cast<const MessageText *>(content));
case MessageContentType::Text: {
auto result = make_unique<MessageText>(*static_cast<const MessageText *>(content));
if (type == MessageContentDupType::Copy || type == MessageContentDupType::ServerCopy) {
remove_unallowed_entities(td, result->text, dialog_id);
}
return result;
}
case MessageContentType::Venue:
return make_unique<MessageVenue>(*static_cast<const MessageVenue *>(content));
case MessageContentType::Video: {
@ -4652,6 +4757,7 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return nullptr;
default:
UNREACHABLE();
@ -4809,6 +4915,10 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
reply_in_dialog_id = DialogId();
reply_to_message_id = MessageId();
}
if (action->total_amount_ <= 0 || !check_currency_amount(action->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << action->total_amount_;
action->total_amount_ = 0;
}
return td::make_unique<MessagePaymentSuccessful>(
reply_in_dialog_id, reply_to_message_id, std::move(action->currency_), action->total_amount_,
std::move(action->invoice_slug_), action->recurring_used_, action->recurring_init_);
@ -4819,6 +4929,10 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
break;
}
auto action = move_tl_object_as<telegram_api::messageActionPaymentSentMe>(action_ptr);
if (action->total_amount_ <= 0 || !check_currency_amount(action->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << action->total_amount_;
action->total_amount_ = 0;
}
auto result = td::make_unique<MessagePaymentSuccessful>(DialogId(), MessageId(), std::move(action->currency_),
action->total_amount_, action->payload_.as_slice().str(),
action->recurring_used_, action->recurring_init_);
@ -4933,6 +5047,14 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
auto action = move_tl_object_as<telegram_api::messageActionWebViewDataSentMe>(action_ptr);
return td::make_unique<MessageWebViewDataReceived>(std::move(action->text_), std::move(action->data_));
}
case telegram_api::messageActionGiftPremium::ID: {
auto action = move_tl_object_as<telegram_api::messageActionGiftPremium>(action_ptr);
if (action->amount_ <= 0 || !check_currency_amount(action->amount_)) {
LOG(ERROR) << "Receive invalid premium gift price " << action->amount_;
action->amount_ = 0;
}
return td::make_unique<MessageGiftPremium>(std::move(action->currency_), action->amount_, action->months_);
}
default:
UNREACHABLE();
}
@ -5009,7 +5131,8 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
case MessageContentType::Text: {
const auto *m = static_cast<const MessageText *>(content);
if (can_be_animated_emoji(m->text) && !m->web_page_id.is_valid()) {
auto animated_emoji = td->stickers_manager_->get_animated_emoji_object(m->text.text);
auto animated_emoji =
td->stickers_manager_->get_animated_emoji_object(m->text.text, get_custom_emoji_id(m->text));
if (animated_emoji != nullptr) {
return td_api::make_object<td_api::messageAnimatedEmoji>(std::move(animated_emoji), m->text.text);
}
@ -5203,6 +5326,11 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
const auto *m = static_cast<const MessageWebViewDataReceived *>(content);
return make_tl_object<td_api::messageWebAppDataReceived>(m->button_text, m->data);
}
case MessageContentType::GiftPremium: {
const auto *m = static_cast<const MessageGiftPremium *>(content);
return make_tl_object<td_api::messageGiftedPremium>(
m->currency, m->amount, m->months, td->stickers_manager_->get_premium_gift_sticker_object(m->months));
}
default:
UNREACHABLE();
return nullptr;
@ -5405,20 +5533,6 @@ FileId get_message_content_thumbnail_file_id(const MessageContent *content, cons
return FileId();
}
static FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td) {
switch (content->get_type()) {
case MessageContentType::Animation:
return td->animations_manager_->get_animation_animated_thumbnail_file_id(
static_cast<const MessageAnimation *>(content)->file_id);
case MessageContentType::Video:
return td->videos_manager_->get_video_animated_thumbnail_file_id(
static_cast<const MessageVideo *>(content)->file_id);
default:
break;
}
return FileId();
}
vector<FileId> get_message_content_file_ids(const MessageContent *content, const Td *td) {
switch (content->get_type()) {
case MessageContentType::Photo:
@ -5426,27 +5540,33 @@ vector<FileId> get_message_content_file_ids(const MessageContent *content, const
case MessageContentType::Animation:
case MessageContentType::Audio:
case MessageContentType::Document:
case MessageContentType::Sticker:
case MessageContentType::Video:
case MessageContentType::VideoNote:
case MessageContentType::VoiceNote: {
vector<FileId> result;
result.reserve(2);
FileId file_id = get_message_content_upload_file_id(content);
if (file_id.is_valid()) {
result.push_back(file_id);
}
FileId thumbnail_file_id = get_message_content_thumbnail_file_id(content, td);
if (thumbnail_file_id.is_valid()) {
result.push_back(thumbnail_file_id);
}
FileId animated_thumbnail_file_id = get_message_content_animated_thumbnail_file_id(content, td);
if (animated_thumbnail_file_id.is_valid()) {
result.push_back(animated_thumbnail_file_id);
}
return result;
auto document_type = [&] {
switch (content->get_type()) {
case MessageContentType::Animation:
return Document::Type::Animation;
case MessageContentType::Audio:
return Document::Type::Audio;
case MessageContentType::Document:
return Document::Type::General;
case MessageContentType::Sticker:
return Document::Type::Sticker;
case MessageContentType::Video:
return Document::Type::Video;
case MessageContentType::VideoNote:
return Document::Type::VideoNote;
case MessageContentType::VoiceNote:
return Document::Type::VoiceNote;
default:
UNREACHABLE();
return Document::Type::Unknown;
}
}();
return Document(document_type, get_message_content_upload_file_id(content)).get_file_ids(td);
}
case MessageContentType::Sticker:
return td->stickers_manager_->get_sticker_file_ids(static_cast<const MessageSticker *>(content)->file_id);
case MessageContentType::Game:
return static_cast<const MessageGame *>(content)->game.get_file_ids(td);
case MessageContentType::Invoice:
@ -5554,6 +5674,7 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return string();
default:
UNREACHABLE();
@ -5805,6 +5926,8 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::WebViewDataReceived:
break;
case MessageContentType::GiftPremium:
break;
default:
UNREACHABLE();
break;

View File

@ -20,6 +20,7 @@
#include "td/telegram/ReplyMarkup.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/TopDialogCategory.h"
@ -38,11 +39,10 @@ namespace td {
class Dependencies;
class DialogAction;
class Game;
class MultiPromiseActor;
struct Photo;
class Td;
class MultiPromiseActor;
// Do not forget to update merge_message_contents when one of the inheritors of this class changes
class MessageContent {
public:
@ -131,6 +131,8 @@ bool update_opened_message_content(MessageContent *content);
int32 get_message_content_index_mask(const MessageContent *content, const Td *td, bool is_outgoing);
StickerType get_message_content_sticker_type(const Td *td, const MessageContent *content);
MessageId get_message_content_pinned_message_id(const MessageContent *content);
string get_message_content_theme_name(const MessageContent *content);
@ -188,7 +190,7 @@ unique_ptr<MessageContent> get_secret_message_content(
unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message_text,
tl_object_ptr<telegram_api::MessageMedia> &&media_ptr,
DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id,
int32 *ttl, bool *disable_web_page_preview);
int32 *ttl, bool *disable_web_page_preview, const char *source);
enum class MessageContentDupType : int32 { Send, SendViaBot, Forward, Copy, ServerCopy };

View File

@ -108,6 +108,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType cont
return string_builder << "WebViewDataSent";
case MessageContentType::WebViewDataReceived:
return string_builder << "WebViewDataReceived";
case MessageContentType::GiftPremium:
return string_builder << "GiftPremium";
default:
UNREACHABLE();
return string_builder;
@ -165,6 +167,7 @@ bool is_allowed_media_group_content(MessageContentType content_type) {
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return false;
default:
UNREACHABLE();
@ -230,6 +233,7 @@ bool is_secret_message_content(int32 ttl, MessageContentType content_type) {
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return false;
default:
UNREACHABLE();
@ -288,6 +292,7 @@ bool is_service_message_content(MessageContentType content_type) {
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return true;
default:
UNREACHABLE();
@ -346,6 +351,7 @@ bool can_have_message_content_caption(MessageContentType content_type) {
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
return false;
default:
UNREACHABLE();

View File

@ -63,7 +63,8 @@ enum class MessageContentType : int32 {
InviteToGroupCall,
ChatSetTheme,
WebViewDataSent,
WebViewDataReceived
WebViewDataReceived,
GiftPremium
};
StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type);

View File

@ -6,11 +6,16 @@
//
#include "td/telegram/MessageEntity.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/SecretChatLayer.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/actor/MultiPromise.h"
#include "td/utils/algorithm.h"
#include "td/utils/format.h"
@ -29,12 +34,27 @@
namespace td {
int MessageEntity::get_type_priority(Type 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 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*/,
99 /*CustomEmoji*/};
static_assert(sizeof(priorities) / sizeof(priorities[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
return priorities[static_cast<int32>(type)];
}
@ -81,6 +101,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
return string_builder << "MediaTimestamp";
case MessageEntity::Type::Spoiler:
return string_builder << "Spoiler";
case MessageEntity::Type::CustomEmoji:
return string_builder << "CustomEmoji";
default:
UNREACHABLE();
return string_builder << "Impossible";
@ -99,6 +121,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &me
if (message_entity.user_id.is_valid()) {
string_builder << ", " << message_entity.user_id;
}
if (message_entity.document_id != 0) {
string_builder << ", emoji = " << message_entity.document_id;
}
string_builder << ']';
return string_builder;
}
@ -146,6 +171,8 @@ tl_object_ptr<td_api::TextEntityType> MessageEntity::get_text_entity_type_object
return make_tl_object<td_api::textEntityTypeMediaTimestamp>(media_timestamp);
case MessageEntity::Type::Spoiler:
return make_tl_object<td_api::textEntityTypeSpoiler>();
case MessageEntity::Type::CustomEmoji:
return make_tl_object<td_api::textEntityTypeCustomEmoji>(document_id);
default:
UNREACHABLE();
return nullptr;
@ -323,7 +350,7 @@ static vector<Slice> match_bot_commands(Slice str) {
static bool is_hashtag_letter(uint32 c, UnicodeSimpleCategory &category) {
category = get_unicode_simple_category(c);
if (c == '_' || c == 0x200c || c == 0xb7) {
if (c == '_' || c == 0x200c || c == 0xb7 || (0xd80 <= c && c <= 0xdff)) {
return true;
}
switch (category) {
@ -341,7 +368,7 @@ static vector<Slice> match_hashtags(Slice str) {
const unsigned char *end = str.uend();
const unsigned char *ptr = begin;
// '/(?<=^|[^\d_\pL\x{200c}])#([\d_\pL\x{200c}]{1,256})(?![\d_\pL\x{200c}]*#)/u'
// '/(?<=^|[^\d_\pL\x{200c}\x{0d80}-\x{0dff}])#([\d_\pL\x{200c}\x{0d80}-\x{0dff}]{1,256})(?![\d_\pL\x{200c}\x{0d80}-\x{0dff}]*#)/u'
// and at least one letter
UnicodeSimpleCategory category;
@ -404,7 +431,7 @@ static vector<Slice> match_cashtags(Slice str) {
const unsigned char *end = str.uend();
const unsigned char *ptr = begin;
// '/(?<=^|[^$\d_\pL\x{200c}])\$(1INCH|[A-Z]{1,8})(?![$\d_\pL\x{200c}])/u'
// '/(?<=^|[^$\d_\pL\x{200c}\x{0d80}-\x{0dff}])\$(1INCH|[A-Z]{1,8})(?![$\d_\pL\x{200c}\x{0d80}-\x{0dff}])/u'
UnicodeSimpleCategory category;
while (true) {
@ -1378,6 +1405,24 @@ vector<std::pair<Slice, int32>> find_media_timestamps(Slice str) {
return result;
}
void remove_empty_entities(vector<MessageEntity> &entities) {
td::remove_if(entities, [](const auto &entity) {
if (entity.length <= 0) {
return true;
}
switch (entity.type) {
case MessageEntity::Type::TextUrl:
return entity.argument.empty();
case MessageEntity::Type::MentionName:
return !entity.user_id.is_valid();
case MessageEntity::Type::CustomEmoji:
return entity.document_id == 0;
default:
return false;
}
});
}
static int32 text_length(Slice text) {
return narrow_cast<int32>(utf8_utf16_length(text));
}
@ -1423,7 +1468,8 @@ static constexpr int32 get_continuous_entities_mask() {
get_entity_type_mask(MessageEntity::Type::MentionName) | get_entity_type_mask(MessageEntity::Type::Cashtag) |
get_entity_type_mask(MessageEntity::Type::PhoneNumber) |
get_entity_type_mask(MessageEntity::Type::BankCardNumber) |
get_entity_type_mask(MessageEntity::Type::MediaTimestamp);
get_entity_type_mask(MessageEntity::Type::MediaTimestamp) |
get_entity_type_mask(MessageEntity::Type::CustomEmoji);
}
static constexpr int32 get_pre_entities_mask() {
@ -1434,7 +1480,7 @@ static constexpr int32 get_pre_entities_mask() {
static constexpr int32 get_user_entities_mask() {
return get_splittable_entities_mask() | get_blockquote_entities_mask() |
get_entity_type_mask(MessageEntity::Type::TextUrl) | get_entity_type_mask(MessageEntity::Type::MentionName) |
get_pre_entities_mask();
get_entity_type_mask(MessageEntity::Type::CustomEmoji) | get_pre_entities_mask();
}
static int32 is_splittable_entity(MessageEntity::Type type) {
@ -1782,6 +1828,8 @@ string get_first_url(Slice text, const vector<MessageEntity> &entities) {
break;
case MessageEntity::Type::Spoiler:
break;
case MessageEntity::Type::CustomEmoji:
break;
default:
UNREACHABLE();
}
@ -1982,6 +2030,8 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
return c == '~';
case MessageEntity::Type::Spoiler:
return c == '|' && text[i + 1] == '|';
case MessageEntity::Type::CustomEmoji:
return c == ']';
default:
UNREACHABLE();
return false;
@ -2048,6 +2098,15 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
type = MessageEntity::Type::Code;
}
break;
case '!':
if (text[i + 1] == '[') {
i++;
type = MessageEntity::Type::CustomEmoji;
} else {
return Status::Error(400, PSLICE() << "Character '" << text[i]
<< "' is reserved and must be escaped with the preceding '\\'");
}
break;
default:
return Status::Error(
400, PSLICE() << "Character '" << text[i] << "' is reserved and must be escaped with the preceding '\\'");
@ -2058,6 +2117,7 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
auto type = nested_entities.back().type;
auto argument = std::move(nested_entities.back().argument);
UserId user_id;
int64 document_id = 0;
bool skip_entity = utf16_offset == nested_entities.back().entity_offset;
switch (type) {
case MessageEntity::Type::Bold:
@ -2104,6 +2164,28 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
}
break;
}
case MessageEntity::Type::CustomEmoji: {
if (text[i + 1] != '(') {
return Status::Error(400, "Custom emoji entity must contain a tg://emoji URL");
}
i += 2;
string url;
auto url_begin_pos = i;
while (i < text.size() && text[i] != ')') {
if (text[i] == '\\' && text[i + 1] > 0 && text[i + 1] <= 126) {
url += text[i + 1];
i += 2;
continue;
}
url += text[i++];
}
if (text[i] != ')') {
return Status::Error(400, PSLICE()
<< "Can't find end of a custom emoji URL at byte offset " << url_begin_pos);
}
TRY_RESULT_ASSIGN(document_id, LinkManager::get_link_custom_emoji_document_id(url));
break;
}
default:
UNREACHABLE();
return false;
@ -2114,6 +2196,8 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
auto entity_length = utf16_offset - entity_offset;
if (user_id.is_valid()) {
entities.emplace_back(entity_offset, entity_length, user_id);
} else if (document_id != 0) {
entities.emplace_back(type, entity_offset, entity_length, document_id);
} else {
entities.emplace_back(type, entity_offset, entity_length, std::move(argument));
}
@ -2500,7 +2584,7 @@ static FormattedText parse_markdown_v3_without_pre(Slice text, vector<MessageEnt
CHECK(entity.offset + entity.length <= utf16_offset);
}
td::remove_if(entities, [](const auto &entity) { return entity.length == 0; });
remove_empty_entities(entities);
sort_entities(entities);
return {std::move(new_text), std::move(entities)};
@ -2946,7 +3030,8 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
string tag_name = to_lower(text.substr(begin_pos + 1, i - begin_pos - 1));
if (tag_name != "a" && tag_name != "b" && tag_name != "strong" && tag_name != "i" && tag_name != "em" &&
tag_name != "s" && tag_name != "strike" && tag_name != "del" && tag_name != "u" && tag_name != "ins" &&
tag_name != "tg-spoiler" && tag_name != "span" && tag_name != "pre" && tag_name != "code") {
tag_name != "tg-spoiler" && tag_name != "tg-emoji" && tag_name != "span" && tag_name != "pre" &&
tag_name != "code") {
return Status::Error(400, PSLICE()
<< "Unsupported start tag \"" << tag_name << "\" at byte offset " << begin_pos);
}
@ -3024,6 +3109,8 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
argument = attribute_value.substr(9);
} else if (tag_name == "span" && attribute_name == Slice("class") && begins_with(attribute_value, "tg-")) {
argument = attribute_value.substr(3);
} else if (tag_name == "tg-emoji" && attribute_name == Slice("emoji-id")) {
argument = std::move(attribute_value);
}
}
@ -3069,6 +3156,12 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
entities.emplace_back(MessageEntity::Type::Underline, entity_offset, entity_length);
} else if (tag_name == "tg-spoiler" || (tag_name == "span" && nested_entities.back().argument == "spoiler")) {
entities.emplace_back(MessageEntity::Type::Spoiler, entity_offset, entity_length);
} else if (tag_name == "tg-emoji") {
auto r_document_id = to_integer_safe<int64>(nested_entities.back().argument);
if (r_document_id.is_error() || r_document_id.ok() == 0) {
return Status::Error(400, "Invalid custom emoji identifier specified");
}
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity_offset, entity_length, r_document_id.ok());
} else if (tag_name == "a") {
auto url = std::move(nested_entities.back().argument);
if (url.empty()) {
@ -3200,6 +3293,15 @@ vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entiti
case MessageEntity::Type::MediaTimestamp:
break;
case MessageEntity::Type::Spoiler:
if (layer >= static_cast<int32>(SecretChatLayer::SpoilerAndCustomEmojiEntities)) {
result.push_back(make_tl_object<secret_api::messageEntitySpoiler>(entity.offset, entity.length));
}
break;
case MessageEntity::Type::CustomEmoji:
if (layer >= static_cast<int32>(SecretChatLayer::SpoilerAndCustomEmojiEntities)) {
result.push_back(
make_tl_object<secret_api::messageEntityCustomEmoji>(entity.offset, entity.length, entity.document_id));
}
break;
default:
UNREACHABLE();
@ -3312,6 +3414,11 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
case td_api::textEntityTypeSpoiler::ID:
entities.emplace_back(MessageEntity::Type::Spoiler, offset, length);
break;
case td_api::textEntityTypeCustomEmoji::ID: {
auto entity = static_cast<td_api::textEntityTypeCustomEmoji *>(input_entity->type_.get());
entities.emplace_back(MessageEntity::Type::CustomEmoji, offset, length, entity->custom_emoji_id_);
break;
}
default:
UNREACHABLE();
}
@ -3446,6 +3553,11 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
entities.emplace_back(entity->offset_, entity->length_, user_id);
break;
}
case telegram_api::messageEntityCustomEmoji::ID: {
auto entity = static_cast<const telegram_api::messageEntityCustomEmoji *>(server_entity.get());
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_, entity->document_id_);
break;
}
default:
UNREACHABLE();
}
@ -3453,9 +3565,13 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
return entities;
}
vector<MessageEntity> get_message_entities(vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities) {
vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities,
bool is_premium, MultiPromiseActor &load_data_multipromise) {
constexpr size_t MAX_SECRET_CHAT_ENTITIES = 1000;
constexpr size_t MAX_CUSTOM_EMOJI_ENTITIES = 100;
vector<MessageEntity> entities;
entities.reserve(secret_entities.size());
vector<int64> document_ids;
for (auto &secret_entity : secret_entities) {
switch (secret_entity->get_id()) {
case secret_api::messageEntityUnknown::ID:
@ -3551,10 +3667,41 @@ vector<MessageEntity> get_message_entities(vector<tl_object_ptr<secret_api::Mess
case secret_api::messageEntityMentionName::ID:
// skip all name mentions in secret chats
break;
case secret_api::messageEntitySpoiler::ID: {
auto entity = static_cast<const secret_api::messageEntitySpoiler *>(secret_entity.get());
entities.emplace_back(MessageEntity::Type::Spoiler, entity->offset_, entity->length_);
break;
}
case secret_api::messageEntityCustomEmoji::ID: {
auto entity = static_cast<const secret_api::messageEntityCustomEmoji *>(secret_entity.get());
if (is_premium || !td->stickers_manager_->is_premium_custom_emoji(entity->document_id_, false)) {
if (document_ids.size() < MAX_CUSTOM_EMOJI_ENTITIES) {
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_,
entity->document_id_);
document_ids.push_back(entity->document_id_);
}
}
break;
}
default:
UNREACHABLE();
}
if (entities.size() >= MAX_SECRET_CHAT_ENTITIES) {
break;
}
}
if (!document_ids.empty() && !is_premium) {
// preload custom emoji to check that they aren't premium
td->stickers_manager_->get_custom_emoji_stickers(
std::move(document_ids), true,
PromiseCreator::lambda(
[promise = load_data_multipromise.get_promise()](td_api::object_ptr<td_api::stickers> result) mutable {
promise.set_value(Unit());
}));
}
return entities;
}
@ -3730,7 +3877,7 @@ static std::pair<size_t, int32> remove_invalid_entities(const string &text, vect
int32 utf16_offset = 0;
int32 last_non_whitespace_utf16_offset = -1;
td::remove_if(entities, [](const auto &entity) { return entity.length == 0; });
remove_empty_entities(entities);
for (size_t pos = 0; pos <= text.size(); pos++) {
while (!nested_entities_stack.empty()) {
@ -3795,7 +3942,7 @@ static std::pair<size_t, int32> remove_invalid_entities(const string &text, vect
CHECK(nested_entities_stack.empty());
CHECK(current_entity == entities.size());
td::remove_if(entities, [](const auto &entity) { return entity.length == 0; });
remove_empty_entities(entities);
return {last_non_whitespace_pos, last_non_whitespace_utf16_offset};
}
@ -3985,7 +4132,7 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
return Status::Error(400, PSLICE() << "Receive an entity with incorrect length " << entity.length);
}
}
td::remove_if(entities, [](const MessageEntity &entity) { return entity.length == 0; });
remove_empty_entities(entities);
fix_entities(entities);
@ -4251,6 +4398,10 @@ vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(co
r_input_user.move_as_ok()));
break;
}
case MessageEntity::Type::CustomEmoji:
result.push_back(
make_tl_object<telegram_api::messageEntityCustomEmoji>(entity.offset, entity.length, entity.document_id));
break;
default:
UNREACHABLE();
}
@ -4290,4 +4441,42 @@ vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(co
return {};
}
void remove_premium_custom_emoji_entities(const Td *td, vector<MessageEntity> &entities, bool remove_unknown) {
td::remove_if(entities, [&](const MessageEntity &entity) {
return entity.type == MessageEntity::Type::CustomEmoji &&
td->stickers_manager_->is_premium_custom_emoji(entity.document_id, remove_unknown);
});
}
void remove_unallowed_entities(const Td *td, FormattedText &text, DialogId dialog_id) {
if (text.entities.empty()) {
return;
}
if (dialog_id.get_type() == DialogType::SecretChat) {
auto layer = td->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id());
td::remove_if(text.entities, [layer](const MessageEntity &entity) {
if (layer < static_cast<int32>(SecretChatLayer::NewEntities) &&
(entity.type == MessageEntity::Type::Underline || entity.type == MessageEntity::Type::Strikethrough ||
entity.type == MessageEntity::Type::BlockQuote)) {
return true;
}
if (layer < static_cast<int32>(SecretChatLayer::SpoilerAndCustomEmojiEntities) &&
(entity.type == MessageEntity::Type::Spoiler || entity.type == MessageEntity::Type::CustomEmoji)) {
return true;
}
return false;
});
if (layer < static_cast<int32>(SecretChatLayer::NewEntities)) {
sort_entities(text.entities);
remove_intersecting_entities(text.entities);
}
}
if (!G()->shared_config().get_option_boolean("is_premium") &&
dialog_id != DialogId(td->contacts_manager_->get_my_id())) {
remove_premium_custom_emoji_entities(td, text.entities, false);
}
}
} // namespace td

View File

@ -24,6 +24,8 @@ namespace td {
class ContactsManager;
class Dependencies;
class MultiPromiseActor;
class Td;
class MessageEntity {
public:
@ -49,6 +51,7 @@ class MessageEntity {
BankCardNumber,
MediaTimestamp,
Spoiler,
CustomEmoji,
Size
};
Type type = Type::Size;
@ -57,6 +60,7 @@ class MessageEntity {
int32 media_timestamp = -1;
string argument;
UserId user_id;
int64 document_id = 0;
MessageEntity() = default;
@ -70,12 +74,17 @@ class MessageEntity {
: type(type), offset(offset), length(length), media_timestamp(media_timestamp) {
CHECK(type == Type::MediaTimestamp);
}
MessageEntity(Type type, int32 offset, int32 length, int64 document_id)
: type(type), offset(offset), length(length), document_id(document_id) {
CHECK(type == Type::CustomEmoji);
}
tl_object_ptr<td_api::textEntity> get_text_entity_object() const;
bool operator==(const MessageEntity &other) const {
return offset == other.offset && length == other.length && type == other.type &&
media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id;
media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id &&
document_id == other.document_id;
}
bool operator<(const MessageEntity &other) const {
@ -143,6 +152,10 @@ 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);
void remove_premium_custom_emoji_entities(const Td *td, vector<MessageEntity> &entities, bool remove_unknown);
void remove_unallowed_entities(const Td *td, FormattedText &text, DialogId dialog_id);
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
vector<Slice> find_mentions(Slice str);
@ -155,6 +168,8 @@ bool is_email_address(Slice str);
vector<std::pair<Slice, bool>> find_urls(Slice str); // slice + is_email_address
vector<std::pair<Slice, int32>> find_media_timestamps(Slice str); // slice + media_timestamp
void remove_empty_entities(vector<MessageEntity> &entities);
string get_first_url(Slice text, const vector<MessageEntity> &entities);
Result<vector<MessageEntity>> parse_markdown(string &text);
@ -182,7 +197,8 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
const char *source);
vector<MessageEntity> get_message_entities(vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities);
vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_api::MessageEntity>> &&secret_entities,
bool is_premium, MultiPromiseActor &load_data_multipromise);
// like clean_input_string but also validates entities
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,

View File

@ -27,6 +27,9 @@ void MessageEntity::store(StorerT &storer) const {
if (type == Type::MediaTimestamp) {
store(media_timestamp, storer);
}
if (type == Type::CustomEmoji) {
store(document_id, storer);
}
}
template <class ParserT>
@ -44,6 +47,9 @@ void MessageEntity::parse(ParserT &parser) {
if (type == Type::MediaTimestamp) {
parse(media_timestamp, parser);
}
if (type == Type::CustomEmoji) {
parse(document_id, parser);
}
}
template <class StorerT>
@ -56,6 +62,7 @@ template <class ParserT>
void FormattedText::parse(ParserT &parser) {
td::parse(text, parser);
td::parse(entities, parser);
remove_empty_entities(entities);
}
} // namespace td

View File

@ -219,8 +219,9 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
void MessageReaction::add_recent_chooser_dialog_id(DialogId dialog_id) {
recent_chooser_dialog_ids_.insert(recent_chooser_dialog_ids_.begin(), dialog_id);
if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS) {
recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS);
if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS + 1) {
LOG(ERROR) << "Have " << recent_chooser_dialog_ids_.size() << " recent reaction choosers";
recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS + 1);
}
}
@ -228,6 +229,22 @@ bool MessageReaction::remove_recent_chooser_dialog_id(DialogId dialog_id) {
return td::remove(recent_chooser_dialog_ids_, dialog_id);
}
void MessageReaction::update_recent_chooser_dialog_ids(const MessageReaction &old_reaction) {
if (recent_chooser_dialog_ids_.size() != MAX_RECENT_CHOOSERS) {
return;
}
CHECK(is_chosen_ && old_reaction.is_chosen_);
CHECK(reaction_ == old_reaction.reaction_);
CHECK(old_reaction.recent_chooser_dialog_ids_.size() == MAX_RECENT_CHOOSERS + 1);
for (size_t i = 0; i < MAX_RECENT_CHOOSERS; i++) {
if (recent_chooser_dialog_ids_[i] != old_reaction.recent_chooser_dialog_ids_[i]) {
return;
}
}
recent_chooser_dialog_ids_ = old_reaction.recent_chooser_dialog_ids_;
recent_chooser_min_channels_ = old_reaction.recent_chooser_min_channels_;
}
void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool can_get_added_reactions) {
if (is_chosen_ == is_chosen) {
return;
@ -254,6 +271,9 @@ td_api::object_ptr<td_api::messageReaction> MessageReaction::get_message_reactio
auto recent_chooser = get_min_message_sender_object(td, dialog_id, "get_message_reaction_object");
if (recent_chooser != nullptr) {
recent_choosers.push_back(std::move(recent_chooser));
if (recent_choosers.size() == MAX_RECENT_CHOOSERS) {
break;
}
}
}
return td_api::make_object<td_api::messageReaction>(reaction_, choose_count_, is_chosen_, std::move(recent_choosers));
@ -398,6 +418,15 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
}
unread_reactions_ = old_reactions.unread_reactions_;
}
for (const auto &old_reaction : old_reactions.reactions_) {
if (old_reaction.is_chosen() &&
old_reaction.get_recent_chooser_dialog_ids().size() == MessageReaction::MAX_RECENT_CHOOSERS + 1) {
auto *reaction = get_reaction(old_reaction.get_reaction());
if (reaction != nullptr && reaction->is_chosen()) {
reaction->update_recent_chooser_dialog_ids(old_reaction);
}
}
}
}
void MessageReactions::sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) {

View File

@ -81,6 +81,8 @@ class MessageReaction {
bool remove_recent_chooser_dialog_id(DialogId dialog_id);
void update_recent_chooser_dialog_ids(const MessageReaction &old_reaction);
td_api::object_ptr<td_api::messageReaction> get_message_reaction_object(Td *td) const;
template <class StorerT>

File diff suppressed because it is too large Load Diff

View File

@ -1268,6 +1268,7 @@ class MessagesManager final : public Actor {
string theme_name;
int32 pending_join_request_count = 0;
vector<UserId> pending_join_request_user_ids;
int32 have_full_history_source = 0;
FolderId folder_id;
vector<DialogListId> dialog_list_ids; // TODO replace with mask
@ -1341,10 +1342,12 @@ class MessagesManager final : public Actor {
bool is_has_bots_inited = false;
bool is_theme_name_inited = false;
bool is_available_reactions_inited = false;
bool had_yet_unsent_message_id_overflow = false;
bool increment_view_counter = false;
bool is_update_new_chat_sent = false;
bool is_update_new_chat_being_sent = false;
bool has_unload_timeout = false;
bool is_channel_difference_finished = false;
@ -1787,6 +1790,7 @@ class MessagesManager final : public Actor {
static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 3 * 86400;
static constexpr int32 USERNAME_CACHE_EXPIRE_TIME_SHORT = 900;
static constexpr int32 AUTH_NOTIFICATION_ID_CACHE_TIME = 7 * 86400;
static constexpr size_t MAX_SAVED_AUTH_NOTIFICATION_IDS = 100;
static constexpr int32 ONLINE_MEMBER_COUNT_UPDATE_TIME = 5 * 60;
@ -1940,6 +1944,10 @@ class MessagesManager final : public Actor {
const vector<MessageId> &message_ids, bool drop_author, bool drop_media_captions,
uint64 log_event_id);
void send_forward_message_query(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id,
tl_object_ptr<telegram_api::InputPeer> as_input_peer, vector<MessageId> message_ids,
vector<int64> random_ids, int32 schedule_date, Promise<Unit> promise);
Result<td_api::object_ptr<td_api::message>> forward_message(DialogId to_dialog_id, DialogId from_dialog_id,
MessageId message_id,
tl_object_ptr<td_api::messageSendOptions> &&options,
@ -2017,12 +2025,12 @@ class MessagesManager final : public Actor {
static void save_send_bot_start_message_log_event(UserId bot_user_id, DialogId dialog_id, const string &parameter,
const Message *m);
void do_send_bot_start_message(UserId bot_user_id, DialogId dialog_id, const string &parameter, const Message *m);
void do_send_bot_start_message(UserId bot_user_id, DialogId dialog_id, MessageId message_id, const string &parameter);
static void save_send_inline_query_result_message_log_event(DialogId dialog_id, const Message *m, int64 query_id,
const string &result_id);
void do_send_inline_query_result_message(DialogId dialog_id, const Message *m, int64 query_id,
void do_send_inline_query_result_message(DialogId dialog_id, MessageId message_id, int64 query_id,
const string &result_id);
static uint64 save_send_screenshot_taken_notification_message_log_event(DialogId dialog_id, const Message *m);
@ -2692,7 +2700,7 @@ class MessagesManager final : public Actor {
Dialog *add_dialog(DialogId dialog_id, const char *source);
Dialog *add_new_dialog(unique_ptr<Dialog> &&d, bool is_loaded_from_database, const char *source);
Dialog *add_new_dialog(unique_ptr<Dialog> &&dialog, bool is_loaded_from_database, const char *source);
void fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_database_message, MessageId last_database_message_id,
int64 order, int32 last_clear_history_date, MessageId last_clear_history_message_id,
@ -2907,7 +2915,7 @@ class MessagesManager final : public Actor {
static bool is_forward_info_sender_hidden(const MessageForwardInfo *forward_info);
unique_ptr<MessageForwardInfo> get_message_forward_info(
tl_object_ptr<telegram_api::messageFwdHeader> &&forward_header);
tl_object_ptr<telegram_api::messageFwdHeader> &&forward_header, FullMessageId full_message_id);
td_api::object_ptr<td_api::messageForwardInfo> get_message_forward_info_object(
const unique_ptr<MessageForwardInfo> &forward_info) const;
@ -3449,7 +3457,7 @@ class MessagesManager final : public Actor {
bool running_get_difference_ = false; // true after before_get_difference and false after after_get_difference
FlatHashMap<DialogId, unique_ptr<Dialog>, DialogIdHash> dialogs_;
WaitFreeHashMap<DialogId, unique_ptr<Dialog>, DialogIdHash> dialogs_;
int64 added_message_count_ = 0;
FlatHashSet<DialogId, DialogIdHash> loaded_dialogs_; // dialogs loaded from database, but not added to dialogs_
@ -3653,7 +3661,7 @@ class MessagesManager final : public Actor {
FlatHashMap<DialogId, NetQueryRef, DialogIdHash> set_typing_query_;
FlatHashMap<FullMessageId, FileSourceId, FullMessageIdHash> full_message_id_to_file_source_id_;
WaitFreeHashMap<FullMessageId, FileSourceId, FullMessageIdHash> full_message_id_to_file_source_id_;
FlatHashMap<DialogId, int32, DialogIdHash> last_outgoing_forwarded_message_date_;

View File

@ -119,7 +119,7 @@ static int32 get_mute_until(int32 mute_for) {
return 0;
}
const int32 MAX_PRECISE_MUTE_FOR = 7 * 86400;
const int32 MAX_PRECISE_MUTE_FOR = 366 * 86400;
int32 current_time = G()->unix_time();
if (mute_for > MAX_PRECISE_MUTE_FOR || mute_for >= std::numeric_limits<int32>::max() - current_time) {
return std::numeric_limits<int32>::max();

View File

@ -449,7 +449,7 @@ td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_synchronously(
break;
case 'v':
if (name == "version") {
return td_api::make_object<td_api::optionValueString>("1.8.4");
return td_api::make_object<td_api::optionValueString>("1.8.5");
}
break;
}

View File

@ -160,16 +160,20 @@ class SetBotPreCheckoutAnswerQuery final : public Td::ResultHandler {
}
};
static tl_object_ptr<td_api::labeledPricePart> convert_labeled_price(
tl_object_ptr<telegram_api::labeledPrice> labeled_price) {
CHECK(labeled_price != nullptr);
if (!check_currency_amount(labeled_price->amount_)) {
LOG(ERROR) << "Receive invalid labeled price amount " << labeled_price->amount_;
labeled_price->amount_ = (labeled_price->amount_ < 0 ? -1 : 1) * (static_cast<int64>(1) << 40);
}
return make_tl_object<td_api::labeledPricePart>(std::move(labeled_price->label_), labeled_price->amount_);
}
static tl_object_ptr<td_api::invoice> convert_invoice(tl_object_ptr<telegram_api::invoice> invoice) {
CHECK(invoice != nullptr);
vector<tl_object_ptr<td_api::labeledPricePart>> labeled_prices;
labeled_prices.reserve(invoice->prices_.size());
for (auto &labeled_price : invoice->prices_) {
labeled_prices.push_back(
make_tl_object<td_api::labeledPricePart>(std::move(labeled_price->label_), labeled_price->amount_));
}
auto labeled_prices = transform(std::move(invoice->prices_), convert_labeled_price);
bool is_test = (invoice->flags_ & telegram_api::invoice::TEST_MASK) != 0;
bool need_name = (invoice->flags_ & telegram_api::invoice::NAME_REQUESTED_MASK) != 0;
bool need_phone_number = (invoice->flags_ & telegram_api::invoice::PHONE_REQUESTED_MASK) != 0;
@ -188,8 +192,18 @@ static tl_object_ptr<td_api::invoice> convert_invoice(tl_object_ptr<telegram_api
need_shipping_address = true;
}
if (invoice->max_tip_amount_ < 0 || !check_currency_amount(invoice->max_tip_amount_)) {
LOG(ERROR) << "Receive invalid maximum tip amount " << invoice->max_tip_amount_;
invoice->max_tip_amount_ = 0;
}
td::remove_if(invoice->suggested_tip_amounts_,
[](int64 amount) { return amount < 0 || !check_currency_amount(amount); });
if (invoice->suggested_tip_amounts_.size() > 4) {
invoice->suggested_tip_amounts_.resize(4);
}
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_),
invoice->max_tip_amount_, std::move(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);
@ -292,12 +306,6 @@ static tl_object_ptr<td_api::orderInfo> convert_order_info(
convert_address(std::move(order_info->shipping_address_)));
}
static tl_object_ptr<td_api::labeledPricePart> convert_labeled_price(
tl_object_ptr<telegram_api::labeledPrice> labeled_price) {
CHECK(labeled_price != nullptr);
return make_tl_object<td_api::labeledPricePart>(std::move(labeled_price->label_), labeled_price->amount_);
}
static tl_object_ptr<td_api::shippingOption> convert_shipping_option(
tl_object_ptr<telegram_api::shippingOption> shipping_option) {
if (shipping_option == nullptr) {
@ -331,13 +339,12 @@ static tl_object_ptr<telegram_api::paymentRequestedInfo> convert_order_info(
convert_address(std::move(order_info->shipping_address_)));
}
static tl_object_ptr<td_api::savedCredentials> convert_saved_credentials(
tl_object_ptr<telegram_api::paymentSavedCredentialsCard> saved_credentials) {
if (saved_credentials == nullptr) {
return nullptr;
}
return make_tl_object<td_api::savedCredentials>(std::move(saved_credentials->id_),
std::move(saved_credentials->title_));
static vector<tl_object_ptr<td_api::savedCredentials>> convert_saved_credentials(
vector<tl_object_ptr<telegram_api::paymentSavedCredentialsCard>> saved_credentials) {
return transform(
std::move(saved_credentials), [](tl_object_ptr<telegram_api::paymentSavedCredentialsCard> &&credentials) {
return make_tl_object<td_api::savedCredentials>(std::move(credentials->id_), std::move(credentials->title_));
});
}
class GetPaymentFormQuery final : public Td::ResultHandler {
@ -388,11 +395,16 @@ class GetPaymentFormQuery final : public Td::ResultHandler {
if (payment_provider == nullptr) {
payment_provider = td_api::make_object<td_api::paymentProviderOther>(std::move(payment_form->url_));
}
auto additional_payment_options = transform(
payment_form->additional_methods_, [](const telegram_api::object_ptr<telegram_api::paymentFormMethod> &method) {
return td_api::make_object<td_api::paymentOption>(method->title_, method->url_);
});
promise_.set_value(make_tl_object<td_api::paymentForm>(
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"),
std::move(payment_provider), convert_order_info(std::move(payment_form->saved_info_)),
std::move(payment_provider), std::move(additional_payment_options),
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)));
@ -556,6 +568,10 @@ class GetPaymentReceiptQuery final : public Td::ResultHandler {
return on_error(Status::Error(500, "Receive invalid seller identifier"));
}
auto photo = get_web_document_photo(td_->file_manager_.get(), std::move(payment_receipt->photo_), dialog_id_);
if (payment_receipt->tip_amount_ < 0 || !check_currency_amount(payment_receipt->tip_amount_)) {
LOG(ERROR) << "Receive invalid tip amount " << payment_receipt->tip_amount_;
payment_receipt->tip_amount_ = 0;
}
promise_.set_value(make_tl_object<td_api::paymentReceipt>(
payment_receipt->title_, get_product_description_object(payment_receipt->description_),
@ -760,6 +776,10 @@ InputInvoice get_input_invoice(tl_object_ptr<telegram_api::messageMediaInvoice>
// result.payload = string();
// result.provider_token = string();
// result.provider_data = string();
if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_;
message_invoice->total_amount_ = 0;
}
result.total_amount = message_invoice->total_amount_;
if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) {
result.receipt_message_id = MessageId(ServerMessageId(message_invoice->receipt_msg_id_));
@ -784,6 +804,10 @@ InputInvoice get_input_invoice(tl_object_ptr<telegram_api::botInlineMessageMedia
// result.payload = string();
// result.provider_token = string();
// result.provider_data = string();
if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_;
message_invoice->total_amount_ = 0;
}
result.total_amount = message_invoice->total_amount_;
// result.receipt_message_id = MessageId();
return result;
@ -852,26 +876,26 @@ Result<InputInvoice> process_input_message_invoice(
result.invoice.currency = std::move(input_invoice->invoice_->currency_);
result.invoice.price_parts.reserve(input_invoice->invoice_->price_parts_.size());
int64 total_amount = 0;
const int64 MAX_AMOUNT = 9999'9999'9999;
for (auto &price : input_invoice->invoice_->price_parts_) {
if (!clean_input_string(price->label_)) {
return Status::Error(400, "Invoice price label must be encoded in UTF-8");
}
result.invoice.price_parts.emplace_back(std::move(price->label_), price->amount_);
if (price->amount_ < -MAX_AMOUNT || price->amount_ > MAX_AMOUNT) {
if (!check_currency_amount(price->amount_)) {
return Status::Error(400, "Too big amount of the currency specified");
}
result.invoice.price_parts.emplace_back(std::move(price->label_), price->amount_);
total_amount += price->amount_;
}
if (total_amount <= 0) {
return Status::Error(400, "Total price must be positive");
}
if (total_amount > MAX_AMOUNT) {
if (!check_currency_amount(total_amount)) {
return Status::Error(400, "Total price is too big");
}
result.total_amount = total_amount;
if (input_invoice->invoice_->max_tip_amount_ < 0 || input_invoice->invoice_->max_tip_amount_ > MAX_AMOUNT) {
if (input_invoice->invoice_->max_tip_amount_ < 0 ||
!check_currency_amount(input_invoice->invoice_->max_tip_amount_)) {
return Status::Error(400, "Invalid max_tip_amount of the currency specified");
}
for (auto tip_amount : input_invoice->invoice_->suggested_tip_amounts_) {
@ -1217,6 +1241,11 @@ StringBuilder &operator<<(StringBuilder &string_builder, const ShippingOption &s
<< " with price parts " << format::as_array(shipping_option.price_parts) << "]";
}
bool check_currency_amount(int64 amount) {
constexpr int64 MAX_AMOUNT = 9999'9999'9999;
return -MAX_AMOUNT <= amount && amount <= MAX_AMOUNT;
}
void answer_shipping_query(Td *td, int64 shipping_query_id,
vector<tl_object_ptr<td_api::shippingOption>> &&shipping_options,
const string &error_message, Promise<Unit> &&promise) {
@ -1240,6 +1269,9 @@ void answer_shipping_query(Td *td, int64 shipping_query_id,
if (!clean_input_string(price_part->label_)) {
return promise.set_error(Status::Error(400, "Shipping option price part label must be encoded in UTF-8"));
}
if (!check_currency_amount(price_part->amount_)) {
return promise.set_error(Status::Error(400, "Too big amount of the currency specified"));
}
prices.push_back(make_tl_object<telegram_api::labeledPrice>(std::move(price_part->label_), price_part->amount_));
}

View File

@ -173,6 +173,8 @@ bool operator!=(const ShippingOption &lhs, const ShippingOption &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const ShippingOption &shipping_option);
bool check_currency_amount(int64 amount);
void answer_shipping_query(Td *td, int64 shipping_query_id,
vector<tl_object_ptr<td_api::shippingOption>> &&shipping_options,
const string &error_message, Promise<Unit> &&promise);

View File

@ -373,6 +373,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
case telegram_api::documentAttributeSticker::ID:
case telegram_api::documentAttributeVideo::ID:
case telegram_api::documentAttributeAudio::ID:
case telegram_api::documentAttributeCustomEmoji::ID:
LOG(ERROR) << "Unexpected web document attribute " << to_string(attribute);
break;
case telegram_api::documentAttributeFilename::ID:

View File

@ -11,14 +11,12 @@
#include "td/telegram/PhotoSize.h"
#include "td/telegram/Version.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void store(const PhotoSize &photo_size, StorerT &storer) {
LOG(DEBUG) << "Store photo size " << photo_size;
store(photo_size.type, storer);
store(photo_size.dimensions, storer);
store(photo_size.size, storer);
@ -41,7 +39,6 @@ void parse(PhotoSize &photo_size, ParserT &parser) {
parser.set_error("Wrong PhotoSize type");
return;
}
LOG(DEBUG) << "Parsed photo size " << photo_size;
}
template <class StorerT>

View File

@ -1048,7 +1048,7 @@ void PollManager::get_poll_voters(PollId poll_id, FullMessageId full_message_id,
for (int32 i = offset; i != cur_offset && i - offset < limit; i++) {
result.push_back(voters.voter_user_ids[i]);
}
return promise.set_value({poll->options[option_id].voter_count, std::move(result)});
return promise.set_value({max(poll->options[option_id].voter_count, cur_offset), std::move(result)});
}
if (poll->options[option_id].voter_count == 0 || (voters.next_offset.empty() && cur_offset > 0)) {
@ -1148,13 +1148,14 @@ void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, string off
if (static_cast<int32>(user_ids.size()) > limit) {
user_ids.resize(limit);
}
if (voters.next_offset.empty() && narrow_cast<int32>(voters.voter_user_ids.size()) != vote_list->count_) {
auto known_voter_count = narrow_cast<int32>(voters.voter_user_ids.size());
if (voters.next_offset.empty() && known_voter_count != vote_list->count_) {
// invalidate_poll_option_voters(poll, poll_id, option_id);
voters.was_invalidated = true;
}
for (auto &promise : promises) {
promise.set_value({vote_list->count_, vector<UserId>(user_ids)});
promise.set_value({max(vote_list->count_, known_voter_count), vector<UserId>(user_ids)});
}
}

View File

@ -14,13 +14,16 @@
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/JsonValue.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/Payments.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UpdatesManager.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/SliceBuilder.h"
@ -50,6 +53,9 @@ static td_api::object_ptr<td_api::PremiumFeature> get_premium_feature_object(Sli
if (premium_feature == "premium_stickers") {
return td_api::make_object<td_api::premiumFeatureUniqueStickers>();
}
if (premium_feature == "animated_emoji") {
return td_api::make_object<td_api::premiumFeatureCustomEmoji>();
}
if (premium_feature == "advanced_chat_management") {
return td_api::make_object<td_api::premiumFeatureAdvancedChatManagement>();
}
@ -65,6 +71,37 @@ static td_api::object_ptr<td_api::PremiumFeature> get_premium_feature_object(Sli
return nullptr;
}
static Result<tl_object_ptr<telegram_api::InputStorePaymentPurpose>> get_input_store_payment_purpose(
Td *td, const td_api::object_ptr<td_api::StorePaymentPurpose> &purpose) {
if (purpose == nullptr) {
return Status::Error(400, "Purchase purpose must be non-empty");
}
switch (purpose->get_id()) {
case td_api::storePaymentPurposePremiumSubscription::ID: {
auto p = static_cast<const td_api::storePaymentPurposePremiumSubscription *>(purpose.get());
int32 flags = 0;
if (p->is_restore_) {
flags |= telegram_api::inputStorePaymentPremiumSubscription::RESTORE_MASK;
}
return make_tl_object<telegram_api::inputStorePaymentPremiumSubscription>(flags, false /*ignored*/);
}
case td_api::storePaymentPurposeGiftedPremium::ID: {
auto p = static_cast<const td_api::storePaymentPurposeGiftedPremium *>(purpose.get());
UserId user_id(p->user_id_);
TRY_RESULT(input_user, td->contacts_manager_->get_input_user(user_id));
if (p->amount_ <= 0 || !check_currency_amount(p->amount_)) {
return Status::Error(400, "Invalid amount of the currency specified");
}
return make_tl_object<telegram_api::inputStorePaymentGiftPremium>(std::move(input_user), p->currency_,
p->amount_);
}
default:
UNREACHABLE();
return nullptr;
}
}
class GetPremiumPromoQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::premiumState>> promise_;
@ -95,7 +132,7 @@ class GetPremiumPromoQuery final : public Td::ResultHandler {
return on_error(Status::Error(500, "Receive wrong number of videos"));
}
if (promo->monthly_amount_ < 0 || promo->monthly_amount_ > 9999'9999'9999) {
if (promo->monthly_amount_ < 0 || !check_currency_amount(promo->monthly_amount_)) {
return on_error(Status::Error(500, "Receive invalid monthly amount"));
}
@ -146,8 +183,14 @@ class CanPurchasePremiumQuery final : public Td::ResultHandler {
explicit CanPurchasePremiumQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::payments_canPurchasePremium()));
void send(td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose) {
auto r_input_purpose = get_input_store_payment_purpose(td_, purpose);
if (r_input_purpose.is_error()) {
return on_error(r_input_purpose.move_as_error());
}
send_query(
G()->net_query_creator().create(telegram_api::payments_canPurchasePremium(r_input_purpose.move_as_ok())));
}
void on_result(BufferSlice packet) final {
@ -175,13 +218,14 @@ class AssignAppStoreTransactionQuery final : public Td::ResultHandler {
explicit AssignAppStoreTransactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &receipt, bool is_restore) {
int32 flags = 0;
if (is_restore) {
flags |= telegram_api::payments_assignAppStoreTransaction::RESTORE_MASK;
void send(const string &receipt, td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose) {
auto r_input_purpose = get_input_store_payment_purpose(td_, purpose);
if (r_input_purpose.is_error()) {
return on_error(r_input_purpose.move_as_error());
}
send_query(G()->net_query_creator().create(
telegram_api::payments_assignAppStoreTransaction(flags, false /*ignored*/, BufferSlice(receipt))));
telegram_api::payments_assignAppStoreTransaction(BufferSlice(receipt), r_input_purpose.move_as_ok())));
}
void on_result(BufferSlice packet) final {
@ -207,8 +251,20 @@ class AssignPlayMarketTransactionQuery final : public Td::ResultHandler {
explicit AssignPlayMarketTransactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &purchase_token) {
send_query(G()->net_query_creator().create(telegram_api::payments_assignPlayMarketTransaction(purchase_token)));
void send(const string &package_name, const string &store_product_id, const string &purchase_token,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose) {
auto r_input_purpose = get_input_store_payment_purpose(td_, purpose);
if (r_input_purpose.is_error()) {
return on_error(r_input_purpose.move_as_error());
}
auto receipt = make_tl_object<telegram_api::dataJSON>(string());
receipt->data_ = json_encode<string>(json_object([&](auto &o) {
o("packageName", package_name);
o("purchaseToken", purchase_token);
o("productId", store_product_id);
}));
send_query(G()->net_query_creator().create(
telegram_api::payments_assignPlayMarketTransaction(std::move(receipt), r_input_purpose.move_as_ok())));
}
void on_result(BufferSlice packet) final {
@ -298,6 +354,8 @@ static string get_premium_source(const td_api::PremiumFeature *feature) {
return "unique_reactions";
case td_api::premiumFeatureUniqueStickers::ID:
return "premium_stickers";
case td_api::premiumFeatureCustomEmoji::ID:
return "animated_emoji";
case td_api::premiumFeatureAdvancedChatManagement::ID:
return "advanced_chat_management";
case td_api::premiumFeatureProfileBadge::ID:
@ -400,7 +458,7 @@ void get_premium_features(Td *td, const td_api::object_ptr<td_api::PremiumSource
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"),
"animated_emoji,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) {
@ -466,16 +524,21 @@ void get_premium_state(Td *td, Promise<td_api::object_ptr<td_api::premiumState>>
td->create_handler<GetPremiumPromoQuery>(std::move(promise))->send();
}
void can_purchase_premium(Td *td, Promise<Unit> &&promise) {
td->create_handler<CanPurchasePremiumQuery>(std::move(promise))->send();
void can_purchase_premium(Td *td, td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise) {
td->create_handler<CanPurchasePremiumQuery>(std::move(promise))->send(std::move(purpose));
}
void assign_app_store_transaction(Td *td, const string &receipt, bool is_restore, Promise<Unit> &&promise) {
td->create_handler<AssignAppStoreTransactionQuery>(std::move(promise))->send(receipt, is_restore);
void assign_app_store_transaction(Td *td, const string &receipt,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise) {
td->create_handler<AssignAppStoreTransactionQuery>(std::move(promise))->send(receipt, std::move(purpose));
}
void assign_play_market_transaction(Td *td, const string &purchase_token, Promise<Unit> &&promise) {
td->create_handler<AssignPlayMarketTransactionQuery>(std::move(promise))->send(purchase_token);
void assign_play_market_transaction(Td *td, const string &package_name, const string &store_product_id,
const string &purchase_token,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose,
Promise<Unit> &&promise) {
td->create_handler<AssignPlayMarketTransactionQuery>(std::move(promise))
->send(package_name, store_product_id, purchase_token, std::move(purpose));
}
} // namespace td

View File

@ -30,10 +30,13 @@ 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);
void can_purchase_premium(Td *td, Promise<Unit> &&promise);
void can_purchase_premium(Td *td, td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
void assign_app_store_transaction(Td *td, const string &receipt, bool is_restore, Promise<Unit> &&promise);
void assign_app_store_transaction(Td *td, const string &receipt,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
void assign_play_market_transaction(Td *td, const string &purchase_token, Promise<Unit> &&promise);
void assign_play_market_transaction(Td *td, const string &package_name, const string &store_product_id,
const string &purchase_token,
td_api::object_ptr<td_api::StorePaymentPurpose> &&purpose, Promise<Unit> &&promise);
} // namespace td

View File

@ -0,0 +1,64 @@
//
// 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/PremiumGiftOption.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/Payments.h"
#include "td/utils/common.h"
#include <limits>
#include <tuple>
namespace td {
PremiumGiftOption::PremiumGiftOption(telegram_api::object_ptr<telegram_api::premiumGiftOption> &&option)
: months_(option->months_)
, currency_(std::move(option->currency_))
, amount_(option->amount_)
, bot_url_(std::move(option->bot_url_))
, store_product_(std::move(option->store_product_)) {
if (amount_ <= 0 || !check_currency_amount(amount_)) {
LOG(ERROR) << "Receive invalid premium gift option amount " << amount_;
amount_ = static_cast<int64>(1) << 40;
}
}
double PremiumGiftOption::get_monthly_price() const {
return static_cast<double>(amount_) / static_cast<double>(months_);
}
td_api::object_ptr<td_api::premiumGiftOption> PremiumGiftOption::get_premium_gift_option_object(
const PremiumGiftOption &base_option) const {
auto link_type = LinkManager::parse_internal_link(bot_url_, true);
int32 discount_percentage = 0;
if (base_option.months_ > 0 && months_ > 0 && base_option.amount_ > 0 && amount_ > 0) {
double relative_price = get_monthly_price() / base_option.get_monthly_price();
if (relative_price < 1.0) {
discount_percentage = static_cast<int32>(100 * (1.0 - relative_price));
}
}
return td_api::make_object<td_api::premiumGiftOption>(
currency_, amount_, discount_percentage, months_, store_product_,
link_type == nullptr ? nullptr : link_type->get_internal_link_type_object());
}
bool operator<(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs) {
return std::tie(lhs.months_, lhs.amount_, lhs.currency_, lhs.store_product_, lhs.bot_url_) <
std::tie(rhs.months_, rhs.amount_, rhs.currency_, rhs.store_product_, rhs.bot_url_);
}
bool operator==(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs) {
return lhs.months_ == rhs.months_ && lhs.currency_ == rhs.currency_ && lhs.amount_ == rhs.amount_ &&
lhs.bot_url_ == rhs.bot_url_ && lhs.store_product_ == rhs.store_product_;
}
bool operator!=(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs) {
return !(lhs == rhs);
}
} // namespace td

View File

@ -0,0 +1,46 @@
//
// 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/telegram/telegram_api.h"
#include "td/utils/common.h"
namespace td {
class PremiumGiftOption {
int32 months_ = 0;
string currency_;
int64 amount_ = 0;
string bot_url_;
string store_product_;
friend bool operator<(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
friend bool operator==(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
double get_monthly_price() const;
public:
PremiumGiftOption() = default;
explicit PremiumGiftOption(telegram_api::object_ptr<telegram_api::premiumGiftOption> &&option);
td_api::object_ptr<td_api::premiumGiftOption> get_premium_gift_option_object(
const PremiumGiftOption &base_option) const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
bool operator==(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
bool operator!=(const PremiumGiftOption &lhs, const PremiumGiftOption &rhs);
} // namespace td

View File

@ -0,0 +1,77 @@
//
// 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/PremiumGiftOption.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void PremiumGiftOption::store(StorerT &storer) const {
bool has_months = months_ != 0;
bool has_currency = !currency_.empty();
bool has_amount = amount_ != 0;
bool has_bot_url = !bot_url_.empty();
bool has_store_product = !store_product_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_months);
STORE_FLAG(has_currency);
STORE_FLAG(has_amount);
STORE_FLAG(has_bot_url);
STORE_FLAG(has_store_product);
END_STORE_FLAGS();
if (has_months) {
td::store(months_, storer);
}
if (has_currency) {
td::store(currency_, storer);
}
if (has_amount) {
td::store(amount_, storer);
}
if (has_bot_url) {
td::store(bot_url_, storer);
}
if (has_store_product) {
td::store(store_product_, storer);
}
}
template <class ParserT>
void PremiumGiftOption::parse(ParserT &parser) {
bool has_months;
bool has_currency;
bool has_amount;
bool has_bot_url;
bool has_store_product;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_months);
PARSE_FLAG(has_currency);
PARSE_FLAG(has_amount);
PARSE_FLAG(has_bot_url);
PARSE_FLAG(has_store_product);
END_PARSE_FLAGS();
if (has_months) {
td::parse(months_, parser);
}
if (has_currency) {
td::parse(currency_, parser);
}
if (has_amount) {
td::parse(amount_, parser);
}
if (has_bot_url) {
td::parse(bot_url_, parser);
}
if (has_store_product) {
td::parse(store_product_, parser);
}
}
} // namespace td

View File

@ -58,6 +58,9 @@ PrivacyManager::UserPrivacySetting::UserPrivacySetting(const telegram_api::Priva
case telegram_api::privacyKeyAddedByPhone::ID:
type_ = Type::FindByPhoneNumber;
break;
case telegram_api::privacyKeyVoiceMessages::ID:
type_ = Type::VoiceMessages;
break;
default:
UNREACHABLE();
type_ = Type::UserStatus;
@ -82,6 +85,8 @@ tl_object_ptr<td_api::UserPrivacySetting> PrivacyManager::UserPrivacySetting::ge
return make_tl_object<td_api::userPrivacySettingShowPhoneNumber>();
case Type::FindByPhoneNumber:
return make_tl_object<td_api::userPrivacySettingAllowFindingByPhoneNumber>();
case Type::VoiceMessages:
return make_tl_object<td_api::userPrivacySettingAllowPrivateVoiceAndVideoNoteMessages>();
default:
UNREACHABLE();
return nullptr;
@ -105,6 +110,8 @@ tl_object_ptr<telegram_api::InputPrivacyKey> PrivacyManager::UserPrivacySetting:
return make_tl_object<telegram_api::inputPrivacyKeyPhoneNumber>();
case Type::FindByPhoneNumber:
return make_tl_object<telegram_api::inputPrivacyKeyAddedByPhone>();
case Type::VoiceMessages:
return make_tl_object<telegram_api::inputPrivacyKeyVoiceMessages>();
default:
UNREACHABLE();
return nullptr;
@ -137,6 +144,9 @@ PrivacyManager::UserPrivacySetting::UserPrivacySetting(const td_api::UserPrivacy
case td_api::userPrivacySettingAllowFindingByPhoneNumber::ID:
type_ = Type::FindByPhoneNumber;
break;
case td_api::userPrivacySettingAllowPrivateVoiceAndVideoNoteMessages::ID:
type_ = Type::VoiceMessages;
break;
default:
UNREACHABLE();
type_ = Type::UserStatus;

View File

@ -47,6 +47,7 @@ class PrivacyManager final : public NetQueryCallback {
UserProfilePhoto,
UserPhoneNumber,
FindByPhoneNumber,
VoiceMessages,
Size
};

View File

@ -130,8 +130,7 @@ void RecentDialogList::on_load_dialogs(vector<string> &&found_dialogs) {
CHECK(!promises.empty());
if (G()->close_flag()) {
fail_promises(promises, Global::request_aborted_error());
return;
return fail_promises(promises, Global::request_aborted_error());
}
auto newly_found_dialogs = std::move(dialog_ids_);

View File

@ -922,9 +922,10 @@ static tl_object_ptr<td_api::inlineKeyboardButton> get_inline_keyboard_button_ob
type = make_tl_object<td_api::inlineKeyboardButtonTypeCallbackWithPassword>(keyboard_button.data);
break;
case InlineKeyboardButton::Type::User: {
auto user_id = contacts_manager == nullptr ? keyboard_button.user_id.get()
: contacts_manager->get_user_id_object(
keyboard_button.user_id, "get_inline_keyboard_button_object");
bool need_user = contacts_manager != nullptr && !contacts_manager->is_user_bot(contacts_manager->get_my_id());
auto user_id =
need_user ? contacts_manager->get_user_id_object(keyboard_button.user_id, "get_inline_keyboard_button_object")
: keyboard_button.user_id.get();
type = make_tl_object<td_api::inlineKeyboardButtonTypeUser>(user_id);
break;
}

View File

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

View File

@ -489,6 +489,7 @@ void SetSecureValue::load_secret() {
send_closure(actor_id, &SetSecureValue::on_secret, std::move(r_secret), true);
}));
}
void SetSecureValue::cancel_upload() {
upload_generation_++;
auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();

View File

@ -146,6 +146,7 @@ void SequenceDispatcher::on_result(NetQueryPtr query) {
data_[i].last_timeout_ = query->last_timeout_;
check_timeout(data_[i]);
}
query->last_timeout_ = 0;
}
if (query->is_error() && (query->error().code() == NetQuery::ResendInvokeAfter ||
@ -323,14 +324,18 @@ class MultiSequenceDispatcherImpl final : public MultiSequenceDispatcher {
if (net_query.empty() || net_query->is_ready()) {
return false;
}
net_query->total_timeout_ += node.total_timeout;
node.total_timeout = 0;
if (net_query->total_timeout_ > net_query->total_timeout_limit_) {
LOG(WARNING) << "Fail " << net_query << " to " << net_query->source_ << " because total_timeout "
<< net_query->total_timeout_ << " is greater than total_timeout_limit "
<< net_query->total_timeout_limit_;
net_query->set_error(Status::Error(429, PSLICE() << "Too Many Requests: retry after " << node.last_timeout));
return true;
if (node.total_timeout > 0) {
net_query->total_timeout_ += node.total_timeout;
LOG(INFO) << "Set total_timeout to " << net_query->total_timeout_ << " for " << net_query->id();
node.total_timeout = 0;
if (net_query->total_timeout_ > net_query->total_timeout_limit_) {
LOG(WARNING) << "Fail " << net_query << " to " << net_query->source_ << " because total_timeout "
<< net_query->total_timeout_ << " is greater than total_timeout_limit "
<< net_query->total_timeout_limit_;
net_query->set_error(Status::Error(429, PSLICE() << "Too Many Requests: retry after " << node.last_timeout));
return true;
}
}
return false;
}
@ -345,15 +350,17 @@ class MultiSequenceDispatcherImpl final : public MultiSequenceDispatcher {
auto tl_constructor = query->tl_constructor();
scheduler_.for_each_dependent(task_id, [&](TaskId child_task_id) {
auto &child_node = *scheduler_.get_task_extra(child_task_id);
if (child_node.net_query_ref->tl_constructor() == tl_constructor) {
if (child_node.net_query_ref->tl_constructor() == tl_constructor && child_task_id != task_id) {
child_node.total_timeout += query->last_timeout_;
child_node.last_timeout = query->last_timeout_;
to_check_timeout.push_back(child_task_id);
}
});
query->last_timeout_ = 0;
for (auto dependent_task_id : to_check_timeout) {
if (check_timeout(*scheduler_.get_task_extra(dependent_task_id))) {
auto &child_node = *scheduler_.get_task_extra(dependent_task_id);
if (check_timeout(child_node)) {
scheduler_.pause_task(dependent_task_id);
try_resend(dependent_task_id);
}

View File

@ -25,6 +25,10 @@ SpecialStickerSetType SpecialStickerSetType::animated_dice(const string &emoji)
return SpecialStickerSetType(PSTRING() << "animated_dice_sticker_set#" << emoji);
}
SpecialStickerSetType SpecialStickerSetType::premium_gifts() {
return SpecialStickerSetType("premium_gifts_sticker_set");
}
SpecialStickerSetType::SpecialStickerSetType(
const telegram_api::object_ptr<telegram_api::InputStickerSet> &input_sticker_set) {
CHECK(input_sticker_set != nullptr);
@ -38,6 +42,9 @@ SpecialStickerSetType::SpecialStickerSetType(
case telegram_api::inputStickerSetDice::ID:
*this = animated_dice(static_cast<const telegram_api::inputStickerSetDice *>(input_sticker_set.get())->emoticon_);
break;
case telegram_api::inputStickerSetPremiumGifts::ID:
*this = premium_gifts();
break;
default:
UNREACHABLE();
break;
@ -59,6 +66,9 @@ telegram_api::object_ptr<telegram_api::InputStickerSet> SpecialStickerSetType::g
if (*this == animated_emoji_click()) {
return telegram_api::make_object<telegram_api::inputStickerSetAnimatedEmojiAnimations>();
}
if (*this == premium_gifts()) {
return telegram_api::make_object<telegram_api::inputStickerSetPremiumGifts>();
}
auto emoji = get_dice_emoji();
if (!emoji.empty()) {
return telegram_api::make_object<telegram_api::inputStickerSetDice>(emoji);

View File

@ -29,6 +29,8 @@ class SpecialStickerSetType {
static SpecialStickerSetType animated_dice(const string &emoji);
static SpecialStickerSetType premium_gifts();
string get_dice_emoji() const;
bool is_empty() const {

View File

@ -234,6 +234,10 @@ void SponsoredMessageManager::get_dialog_sponsored_message(
void SponsoredMessageManager::on_get_dialog_sponsored_messages(
DialogId dialog_id, Result<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&result) {
if (result.is_ok() && G()->close_flag()) {
result = Global::request_aborted_error();
}
auto &messages = dialog_sponsored_messages_[dialog_id];
CHECK(messages != nullptr);
auto promises = std::move(messages->promises);
@ -241,9 +245,6 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages(
CHECK(messages->messages.empty());
CHECK(messages->message_random_ids.empty());
if (result.is_ok() && G()->close_flag()) {
result = Global::request_aborted_error();
}
if (result.is_error()) {
dialog_sponsored_messages_.erase(dialog_id);
fail_promises(promises, result.move_as_error());
@ -297,7 +298,7 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages(
int32 ttl = 0;
bool disable_web_page_preview = false;
auto content = get_message_content(td_, std::move(message_text), nullptr, sponsor_dialog_id, true, UserId(), &ttl,
&disable_web_page_preview);
&disable_web_page_preview, "on_get_dialog_sponsored_messages");
if (ttl != 0) {
LOG(ERROR) << "Receive sponsored message with TTL " << ttl;
continue;

View File

@ -10,17 +10,15 @@
namespace td {
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type) {
CHECK(type != nullptr);
switch (type->get_id()) {
case td_api::stickerTypeStatic::ID:
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerFormat> &format) {
CHECK(format != nullptr);
switch (format->get_id()) {
case td_api::stickerFormatWebp::ID:
return StickerFormat::Webp;
case td_api::stickerTypeAnimated::ID:
case td_api::stickerFormatTgs::ID:
return StickerFormat::Tgs;
case td_api::stickerTypeVideo::ID:
case td_api::stickerFormatWebm::ID:
return StickerFormat::Webm;
case td_api::stickerTypeMask::ID:
return StickerFormat::Webp;
default:
UNREACHABLE();
return StickerFormat::Unknown;
@ -53,21 +51,17 @@ StickerFormat get_sticker_format_by_extension(Slice extension) {
return StickerFormat::Unknown;
}
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(
StickerFormat sticker_format, bool is_masks, td_api::object_ptr<td_api::maskPosition> mask_position) {
td_api::object_ptr<td_api::StickerFormat> get_sticker_format_object(StickerFormat sticker_format) {
switch (sticker_format) {
case StickerFormat::Unknown:
LOG(ERROR) << "Have a sticker of unknown format";
return td_api::make_object<td_api::stickerTypeStatic>();
return td_api::make_object<td_api::stickerFormatWebp>();
case StickerFormat::Webp:
if (is_masks) {
return td_api::make_object<td_api::stickerTypeMask>(std::move(mask_position));
}
return td_api::make_object<td_api::stickerTypeStatic>();
return td_api::make_object<td_api::stickerFormatWebp>();
case StickerFormat::Tgs:
return td_api::make_object<td_api::stickerTypeAnimated>();
return td_api::make_object<td_api::stickerFormatTgs>();
case StickerFormat::Webm:
return td_api::make_object<td_api::stickerTypeVideo>();
return td_api::make_object<td_api::stickerFormatWebm>();
default:
UNREACHABLE();
return nullptr;
@ -152,15 +146,16 @@ bool is_sticker_format_vector(StickerFormat sticker_format) {
}
}
int64 get_max_sticker_file_size(StickerFormat sticker_format, bool for_thumbnail) {
int64 get_max_sticker_file_size(StickerFormat sticker_format, StickerType sticker_type, bool for_thumbnail) {
bool is_emoji = sticker_type == StickerType::CustomEmoji;
switch (sticker_format) {
case StickerFormat::Unknown:
case StickerFormat::Webp:
return for_thumbnail ? (1 << 17) : (1 << 19);
return for_thumbnail ? (1 << 17) : (is_emoji ? (1 << 17) : (1 << 19));
case StickerFormat::Tgs:
return for_thumbnail ? (1 << 15) : (1 << 16);
case StickerFormat::Webm:
return for_thumbnail ? (1 << 15) : (1 << 18);
return for_thumbnail ? (1 << 15) : (is_emoji ? (1 << 16) : (1 << 18));
default:
UNREACHABLE();
return 0;

View File

@ -7,6 +7,7 @@
#pragma once
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
@ -18,14 +19,13 @@ namespace td {
// update store_sticker/store_sticker_set when this type changes
enum class StickerFormat : int32 { Unknown, Webp, Tgs, Webm };
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type);
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerFormat> &format);
StickerFormat get_sticker_format_by_mime_type(Slice mime_type);
StickerFormat get_sticker_format_by_extension(Slice extension);
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(StickerFormat sticker_format, bool is_masks,
td_api::object_ptr<td_api::maskPosition> mask_position);
td_api::object_ptr<td_api::StickerFormat> get_sticker_format_object(StickerFormat sticker_format);
string get_sticker_format_mime_type(StickerFormat sticker_format);
@ -37,7 +37,7 @@ bool is_sticker_format_animated(StickerFormat sticker_format);
bool is_sticker_format_vector(StickerFormat sticker_format);
int64 get_max_sticker_file_size(StickerFormat sticker_format, bool for_thumbnail);
int64 get_max_sticker_file_size(StickerFormat sticker_format, StickerType sticker_type, bool for_thumbnail);
StringBuilder &operator<<(StringBuilder &string_builder, StickerFormat sticker_format);

View File

@ -0,0 +1,56 @@
//
// 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/StickerType.h"
namespace td {
StickerType get_sticker_type(const td_api::object_ptr<td_api::StickerType> &type) {
if (type == nullptr) {
return StickerType::Regular;
}
switch (type->get_id()) {
case td_api::stickerTypeRegular::ID:
return StickerType::Regular;
case td_api::stickerTypeMask::ID:
return StickerType::Mask;
case td_api::stickerTypeCustomEmoji::ID:
return StickerType::CustomEmoji;
default:
UNREACHABLE();
return StickerType::Regular;
}
}
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(StickerType sticker_type) {
switch (sticker_type) {
case StickerType::Regular:
return td_api::make_object<td_api::stickerTypeRegular>();
case StickerType::Mask:
return td_api::make_object<td_api::stickerTypeMask>();
case StickerType::CustomEmoji:
return td_api::make_object<td_api::stickerTypeCustomEmoji>();
default:
UNREACHABLE();
return nullptr;
}
}
StringBuilder &operator<<(StringBuilder &string_builder, StickerType sticker_type) {
switch (sticker_type) {
case StickerType::Regular:
return string_builder << "Regular";
case StickerType::Mask:
return string_builder << "Mask";
case StickerType::CustomEmoji:
return string_builder << "CustomEmoji";
default:
UNREACHABLE();
return string_builder;
}
}
} // namespace td

27
td/telegram/StickerType.h Normal file
View File

@ -0,0 +1,27 @@
//
// 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"
#include "td/utils/StringBuilder.h"
namespace td {
// update store_sticker/store_sticker_set when this type changes
enum class StickerType : int32 { Regular, Mask, CustomEmoji };
static constexpr int32 MAX_STICKER_TYPE = 3;
StickerType get_sticker_type(const td_api::object_ptr<td_api::StickerType> &type);
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(StickerType sticker_type);
StringBuilder &operator<<(StringBuilder &string_builder, StickerType sticker_type);
} // namespace td

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
#include "td/telegram/SpecialStickerSetType.h"
#include "td/telegram/StickerFormat.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -32,6 +33,7 @@
#include "td/utils/Promise.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/WaitFreeHashMap.h"
#include <memory>
#include <tuple>
@ -60,6 +62,10 @@ class StickersManager final : public Actor {
void init();
StickerType get_sticker_type(FileId file_id) const;
bool is_premium_custom_emoji(int64 custom_emoji_id, bool default_result) const;
tl_object_ptr<td_api::sticker> get_sticker_object(FileId file_id, bool for_animated_emoji = false,
bool for_clicked_animated_emoji = false) const;
@ -75,23 +81,35 @@ class StickersManager final : public Actor {
const vector<StickerSetId> &sticker_set_ids,
size_t covers_limit) const;
td_api::object_ptr<td_api::animatedEmoji> get_animated_emoji_object(const string &emoji);
td_api::object_ptr<td_api::sticker> get_premium_gift_sticker_object(int32 month_count);
td_api::object_ptr<td_api::animatedEmoji> get_animated_emoji_object(const string &emoji, int64 custom_emoji_id);
tl_object_ptr<telegram_api::InputStickerSet> get_input_sticker_set(StickerSetId sticker_set_id) const;
void register_premium_gift(int32 months, FullMessageId full_message_id, const char *source);
void unregister_premium_gift(int32 months, FullMessageId full_message_id, const char *source);
void register_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source);
void unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source);
void register_emoji(const string &emoji, FullMessageId full_message_id, const char *source);
void register_emoji(const string &emoji, int64 custom_emoji_id, FullMessageId full_message_id, const char *source);
void unregister_emoji(const string &emoji, FullMessageId full_message_id, const char *source);
void unregister_emoji(const string &emoji, int64 custom_emoji_id, FullMessageId full_message_id, const char *source);
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_custom_emoji_stickers(vector<int64> &&document_ids, bool use_database,
Promise<td_api::object_ptr<td_api::stickers>> &&promise);
void get_premium_gift_option_sticker(int32 month_count, bool is_recursive,
Promise<td_api::object_ptr<td_api::sticker>> &&promise);
void get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::sticker>> &&promise);
@ -105,6 +123,7 @@ class StickersManager final : public Actor {
void create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail, PhotoSize thumbnail,
Dimensions dimensions, tl_object_ptr<telegram_api::documentAttributeSticker> sticker,
tl_object_ptr<telegram_api::documentAttributeCustomEmoji> custom_emoji,
StickerFormat sticker_format, MultiPromiseActor *load_data_multipromise_ptr);
bool has_input_media(FileId sticker_file_id, bool is_secret) const;
@ -118,11 +137,14 @@ class StickersManager final : public Actor {
tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
BufferSlice thumbnail, int32 layer) const;
vector<FileId> get_stickers(string emoji, int32 limit, bool force, Promise<Unit> &&promise);
vector<FileId> get_stickers(StickerType sticker_type, string emoji, int32 limit, DialogId dialog_id, bool force,
Promise<Unit> &&promise);
vector<FileId> search_stickers(string emoji, int32 limit, Promise<Unit> &&promise);
void search_stickers(string emoji, int32 limit, Promise<td_api::object_ptr<td_api::stickers>> &&promise);
vector<StickerSetId> get_installed_sticker_sets(bool is_masks, Promise<Unit> &&promise);
void get_premium_stickers(int32 limit, Promise<td_api::object_ptr<td_api::stickers>> &&promise);
vector<StickerSetId> get_installed_sticker_sets(StickerType sticker_type, Promise<Unit> &&promise);
static bool has_webp_thumbnail(const vector<tl_object_ptr<telegram_api::PhotoSize>> &thumbnails);
@ -134,8 +156,8 @@ class StickersManager final : public Actor {
StickerSetId search_sticker_set(const string &short_name_to_search, Promise<Unit> &&promise);
std::pair<int32, vector<StickerSetId>> search_installed_sticker_sets(bool is_masks, const string &query, int32 limit,
Promise<Unit> &&promise);
std::pair<int32, vector<StickerSetId>> search_installed_sticker_sets(StickerType sticker_type, const string &query,
int32 limit, Promise<Unit> &&promise);
vector<StickerSetId> search_sticker_sets(const string &query, Promise<Unit> &&promise);
@ -149,9 +171,10 @@ class StickersManager final : public Actor {
void on_get_available_reactions(tl_object_ptr<telegram_api::messages_AvailableReactions> &&available_reactions_ptr);
void on_get_installed_sticker_sets(bool is_masks, tl_object_ptr<telegram_api::messages_AllStickers> &&stickers_ptr);
void on_get_installed_sticker_sets(StickerType sticker_type,
tl_object_ptr<telegram_api::messages_AllStickers> &&stickers_ptr);
void on_get_installed_sticker_sets_failed(bool is_masks, Status error);
void on_get_installed_sticker_sets_failed(StickerType sticker_type, Status error);
StickerSetId on_get_messages_sticker_set(StickerSetId sticker_set_id,
tl_object_ptr<telegram_api::messages_StickerSet> &&set_ptr, bool is_changed,
@ -185,29 +208,31 @@ class StickersManager final : public Actor {
void on_update_sticker_sets();
void on_update_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids);
void on_update_sticker_sets_order(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids);
std::pair<int32, vector<StickerSetId>> get_archived_sticker_sets(bool is_masks, StickerSetId offset_sticker_set_id,
int32 limit, bool force, Promise<Unit> &&promise);
std::pair<int32, vector<StickerSetId>> get_archived_sticker_sets(StickerType sticker_type,
StickerSetId offset_sticker_set_id, int32 limit,
bool force, Promise<Unit> &&promise);
void on_get_archived_sticker_sets(bool is_masks, StickerSetId offset_sticker_set_id,
void on_get_archived_sticker_sets(StickerType sticker_type, StickerSetId offset_sticker_set_id,
vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets,
int32 total_count);
td_api::object_ptr<td_api::trendingStickerSets> get_featured_sticker_sets(int32 offset, int32 limit,
Promise<Unit> &&promise);
td_api::object_ptr<td_api::trendingStickerSets> get_featured_sticker_sets(StickerType sticker_type, int32 offset,
int32 limit, Promise<Unit> &&promise);
void on_get_featured_sticker_sets(int32 offset, int32 limit, uint32 generation,
void on_get_featured_sticker_sets(StickerType sticker_type, int32 offset, int32 limit, uint32 generation,
tl_object_ptr<telegram_api::messages_FeaturedStickers> &&sticker_sets_ptr);
void on_get_featured_sticker_sets_failed(int32 offset, int32 limit, uint32 generation, Status error);
void on_get_featured_sticker_sets_failed(StickerType sticker_type, int32 offset, int32 limit, uint32 generation,
Status error);
vector<StickerSetId> get_attached_sticker_sets(FileId file_id, Promise<Unit> &&promise);
void on_get_attached_sticker_sets(FileId file_id,
vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets);
void reorder_installed_sticker_sets(bool is_masks, const vector<StickerSetId> &sticker_set_ids,
void reorder_installed_sticker_sets(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids,
Promise<Unit> &&promise);
FileId upload_sticker_file(UserId user_id, tl_object_ptr<td_api::inputSticker> &&sticker, Promise<Unit> &&promise);
@ -220,15 +245,15 @@ class StickersManager final : public Actor {
static td_api::object_ptr<td_api::CheckStickerSetNameResult> get_check_sticker_set_name_result_object(
CheckStickerSetNameResult result);
void create_new_sticker_set(UserId user_id, string &title, string &short_name,
vector<tl_object_ptr<td_api::inputSticker>> &&stickers, string software,
Promise<Unit> &&promise);
void create_new_sticker_set(UserId user_id, string title, string short_name, StickerType sticker_type,
vector<td_api::object_ptr<td_api::inputSticker>> &&stickers, string software,
Promise<td_api::object_ptr<td_api::stickerSet>> &&promise);
void add_sticker_to_set(UserId user_id, string &short_name, tl_object_ptr<td_api::inputSticker> &&sticker,
Promise<Unit> &&promise);
void add_sticker_to_set(UserId user_id, string short_name, tl_object_ptr<td_api::inputSticker> &&sticker,
Promise<td_api::object_ptr<td_api::stickerSet>> &&promise);
void set_sticker_set_thumbnail(UserId user_id, string &short_name, tl_object_ptr<td_api::InputFile> &&thumbnail,
Promise<Unit> &&promise);
void set_sticker_set_thumbnail(UserId user_id, string short_name, tl_object_ptr<td_api::InputFile> &&thumbnail,
Promise<td_api::object_ptr<td_api::stickerSet>> &&promise);
void set_sticker_position_in_set(const tl_object_ptr<td_api::InputFile> &sticker, int32 position,
Promise<Unit> &&promise);
@ -296,9 +321,9 @@ class StickersManager final : public Actor {
void reload_sticker_set(StickerSetId sticker_set_id, int64 access_hash, Promise<Unit> &&promise);
void reload_installed_sticker_sets(bool is_masks, bool force);
void reload_installed_sticker_sets(StickerType sticker_type, bool force);
void reload_featured_sticker_sets(bool force);
void reload_featured_sticker_sets(StickerType sticker_type, bool force);
void reload_recent_stickers(bool is_attached, bool force);
@ -312,7 +337,7 @@ class StickersManager final : public Actor {
FileId dup_sticker(FileId new_id, FileId old_id);
void merge_stickers(FileId new_id, FileId old_id, bool can_delete_old);
void merge_stickers(FileId new_id, FileId old_id);
template <class StorerT>
void store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer, const char *source) const;
@ -349,88 +374,96 @@ class StickersManager final : public Actor {
static constexpr int32 MAX_FOUND_STICKERS = 100; // server side limit
static constexpr size_t MAX_STICKER_SET_TITLE_LENGTH = 64; // server side limit
static constexpr size_t MAX_STICKER_SET_SHORT_NAME_LENGTH = 64; // server side limit
static constexpr size_t MAX_GET_CUSTOM_EMOJI_STICKERS = 200; // server-side limit
static constexpr int32 EMOJI_KEYWORDS_UPDATE_DELAY = 3600;
static constexpr double MIN_ANIMATED_EMOJI_CLICK_DELAY = 0.2;
class Sticker {
public:
StickerSetId set_id;
string alt;
Dimensions dimensions;
string minithumbnail;
PhotoSize s_thumbnail;
PhotoSize m_thumbnail;
FileId premium_animation_file_id;
FileId file_id;
StickerFormat format = StickerFormat::Unknown;
bool is_mask = false;
int32 point = -1;
double x_shift = 0;
double y_shift = 0;
double scale = 0;
StickerSetId set_id_;
string alt_;
Dimensions dimensions_;
string minithumbnail_;
PhotoSize s_thumbnail_;
PhotoSize m_thumbnail_;
FileId premium_animation_file_id_;
FileId file_id_;
StickerFormat format_ = StickerFormat::Unknown;
StickerType type_ = StickerType::Regular;
bool is_premium_ = false;
bool is_from_database_ = false;
bool is_being_reloaded_ = false;
int32 point_ = -1;
double x_shift_ = 0;
double y_shift_ = 0;
double scale_ = 0;
int32 emoji_receive_date_ = 0;
};
class StickerSet {
public:
bool is_inited = false; // basic information about the set
bool was_loaded = false;
bool is_loaded = false;
bool is_inited_ = false; // basic information about the set
bool was_loaded_ = false;
bool is_loaded_ = false;
StickerSetId id;
int64 access_hash = 0;
string title;
string short_name;
StickerFormat sticker_format = StickerFormat::Unknown;
int32 sticker_count = 0;
int32 hash = 0;
int32 expires_at = 0;
StickerSetId id_;
int64 access_hash_ = 0;
string title_;
string short_name_;
StickerFormat sticker_format_ = StickerFormat::Unknown;
StickerType sticker_type_ = StickerType::Regular;
int32 sticker_count_ = 0;
int32 hash_ = 0;
int32 expires_at_ = 0;
string minithumbnail;
PhotoSize thumbnail;
string minithumbnail_;
PhotoSize thumbnail_;
int64 thumbnail_document_id_ = 0;
vector<FileId> sticker_ids;
vector<FileId> sticker_ids_;
vector<int32> premium_sticker_positions_;
FlatHashMap<string, vector<FileId>> emoji_stickers_map_; // emoji -> stickers
FlatHashMap<FileId, vector<string>, FileIdHash> sticker_emojis_map_; // sticker -> emojis
bool is_installed = false;
bool is_archived = false;
bool is_official = false;
bool is_masks = false;
bool is_viewed = true;
bool is_thumbnail_reloaded = false;
bool are_legacy_sticker_thumbnails_reloaded = false;
mutable bool was_update_sent = false; // does the sticker set is known to the client
bool is_changed = true; // have new changes that need to be sent to the client and database
bool need_save_to_database = true; // have new changes that need only to be saved to the database
bool is_installed_ = false;
bool is_archived_ = false;
bool is_official_ = false;
bool is_viewed_ = true;
bool is_thumbnail_reloaded_ = false;
bool are_legacy_sticker_thumbnails_reloaded_ = false;
mutable bool was_update_sent_ = false; // does the sticker set is known to the client
bool is_changed_ = true; // have new changes that need to be sent to the client and database
bool need_save_to_database_ = true; // have new changes that need only to be saved to the database
vector<uint32> load_requests;
vector<uint32> load_without_stickers_requests;
vector<uint32> load_requests_;
vector<uint32> load_without_stickers_requests_;
};
struct PendingNewStickerSet {
MultiPromiseActor upload_files_multipromise{"UploadNewStickerSetFilesMultiPromiseActor"};
UserId user_id;
string title;
string short_name;
StickerFormat sticker_format = StickerFormat::Unknown;
vector<FileId> file_ids;
vector<tl_object_ptr<td_api::inputSticker>> stickers;
string software;
Promise<> promise;
MultiPromiseActor upload_files_multipromise_{"UploadNewStickerSetFilesMultiPromiseActor"};
UserId user_id_;
string title_;
string short_name_;
StickerType sticker_type_ = StickerType::Regular;
StickerFormat sticker_format_ = StickerFormat::Unknown;
vector<FileId> file_ids_;
vector<tl_object_ptr<td_api::inputSticker>> stickers_;
string software_;
Promise<td_api::object_ptr<td_api::stickerSet>> promise_;
};
struct PendingAddStickerToSet {
string short_name;
FileId file_id;
tl_object_ptr<td_api::inputSticker> sticker;
Promise<> promise;
string short_name_;
FileId file_id_;
tl_object_ptr<td_api::inputSticker> sticker_;
Promise<td_api::object_ptr<td_api::stickerSet>> promise_;
};
struct PendingSetStickerSetThumbnail {
string short_name;
FileId file_id;
Promise<> promise;
string short_name_;
FileId file_id_;
Promise<td_api::object_ptr<td_api::stickerSet>> promise_;
};
struct PendingGetAnimatedEmojiClickSticker {
@ -455,6 +488,18 @@ class StickersManager final : public Actor {
bool is_being_reloaded_ = false;
};
struct FoundStickers {
vector<FileId> sticker_ids_;
int32 cache_time_ = 300;
double next_reload_time_ = 0;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
struct Reaction {
string reaction_;
string title_;
@ -492,11 +537,14 @@ class StickersManager final : public Actor {
void parse(ParserT &parser);
};
class CustomEmojiLogEvent;
class StickerListLogEvent;
class StickerSetListLogEvent;
class UploadStickerFileCallback;
int64 get_custom_emoji_id(FileId sticker_id) const;
static vector<td_api::object_ptr<td_api::closedVectorPath>> get_sticker_minithumbnail(CSlice path,
StickerSetId sticker_set_id,
int64 document_id, double zoom);
@ -513,6 +561,18 @@ class StickersManager final : public Actor {
Sticker *get_sticker(FileId file_id);
const Sticker *get_sticker(FileId file_id) const;
static string get_found_stickers_database_key(const string &emoji);
void on_load_found_stickers_from_database(string emoji, string value);
void on_search_stickers_finished(const string &emoji, const FoundStickers &found_stickers);
static string get_custom_emoji_database_key(int64 custom_emoji_id);
void load_custom_emoji_sticker_from_database(int64 custom_emoji_id, Promise<Unit> &&promise);
void on_load_custom_emoji_from_database(int64 custom_emoji_id, string value);
FileId on_get_sticker(unique_ptr<Sticker> new_sticker, bool replace);
StickerSet *get_sticker_set(StickerSetId sticker_set_id);
@ -534,7 +594,7 @@ class StickersManager final : public Actor {
void on_resolve_sticker_set_short_name(FileId sticker_file_id, const string &short_name);
int apply_installed_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids);
int apply_installed_sticker_sets_order(StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids);
void on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived, bool is_changed,
bool from_database = false);
@ -561,34 +621,38 @@ class StickersManager final : public Actor {
tl_object_ptr<telegram_api::InputStickerSet> &&input_sticker_set, int32 hash,
Promise<Unit> &&promise) const;
void do_get_premium_stickers(int32 limit, Promise<td_api::object_ptr<td_api::stickers>> &&promise);
static void read_featured_sticker_sets(void *td_void);
int64 get_sticker_sets_hash(const vector<StickerSetId> &sticker_set_ids) const;
int64 get_featured_sticker_sets_hash() const;
int64 get_featured_sticker_sets_hash(StickerType sticker_type) const;
int64 get_recent_stickers_hash(const vector<FileId> &sticker_ids) const;
void load_installed_sticker_sets(bool is_masks, Promise<Unit> &&promise);
void load_installed_sticker_sets(StickerType sticker_type, Promise<Unit> &&promise);
void load_featured_sticker_sets(Promise<Unit> &&promise);
void load_featured_sticker_sets(StickerType sticker_type, Promise<Unit> &&promise);
void load_old_featured_sticker_sets(Promise<Unit> &&promise);
void load_old_featured_sticker_sets(StickerType sticker_type, Promise<Unit> &&promise);
void load_recent_stickers(bool is_attached, Promise<Unit> &&promise);
void on_load_installed_sticker_sets_from_database(bool is_masks, string value);
void on_load_installed_sticker_sets_from_database(StickerType sticker_type, string value);
void on_load_installed_sticker_sets_finished(bool is_masks, vector<StickerSetId> &&installed_sticker_set_ids,
void on_load_installed_sticker_sets_finished(StickerType sticker_type,
vector<StickerSetId> &&installed_sticker_set_ids,
bool from_database = false);
void on_load_featured_sticker_sets_from_database(string value);
void on_load_featured_sticker_sets_from_database(StickerType sticker_type, string value);
void on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids, bool is_premium);
void on_load_featured_sticker_sets_finished(StickerType sticker_type, vector<StickerSetId> &&featured_sticker_set_ids,
bool is_premium);
void on_load_old_featured_sticker_sets_from_database(uint32 generation, string value);
void on_load_old_featured_sticker_sets_from_database(StickerType sticker_type, uint32 generation, string value);
void on_load_old_featured_sticker_sets_finished(uint32 generation,
void on_load_old_featured_sticker_sets_finished(StickerType sticker_type, uint32 generation,
vector<StickerSetId> &&old_featured_sticker_set_ids);
void on_load_recent_stickers_from_database(bool is_attached, string value);
@ -596,28 +660,34 @@ class StickersManager final : public Actor {
void on_load_recent_stickers_finished(bool is_attached, vector<FileId> &&recent_sticker_ids,
bool from_database = false);
td_api::object_ptr<td_api::updateInstalledStickerSets> get_update_installed_sticker_sets_object(int is_mask) const;
td_api::object_ptr<td_api::updateInstalledStickerSets> get_update_installed_sticker_sets_object(
StickerType sticker_type) const;
void send_update_installed_sticker_sets(bool from_database = false);
void reload_old_featured_sticker_sets(uint32 generation = 0);
void reload_old_featured_sticker_sets(StickerType sticker_type, uint32 generation = 0);
void on_old_featured_sticker_sets_invalidated();
void on_old_featured_sticker_sets_invalidated(StickerType sticker_type);
void invalidate_old_featured_sticker_sets();
void invalidate_old_featured_sticker_sets(StickerType sticker_type);
void set_old_featured_sticker_set_count(int32 count);
void set_old_featured_sticker_set_count(StickerType sticker_type, int32 count);
// must be called after every call to set_old_featured_sticker_set_count or
// any change of old_featured_sticker_set_ids_ size
void fix_old_featured_sticker_set_count();
void fix_old_featured_sticker_set_count(StickerType sticker_type);
static size_t get_max_featured_sticker_count(StickerType sticker_type);
static Slice get_featured_sticker_suffix(StickerType sticker_type);
td_api::object_ptr<td_api::trendingStickerSets> get_trending_sticker_sets_object(
const vector<StickerSetId> &sticker_set_ids) const;
StickerType sticker_type, const vector<StickerSetId> &sticker_set_ids) const;
td_api::object_ptr<td_api::updateTrendingStickerSets> get_update_trending_sticker_sets_object() const;
td_api::object_ptr<td_api::updateTrendingStickerSets> get_update_trending_sticker_sets_object(
StickerType sticker_type) const;
void send_update_featured_sticker_sets();
void send_update_featured_sticker_sets(StickerType sticker_type);
td_api::object_ptr<td_api::updateRecentStickers> get_update_recent_stickers_object(int is_attached) const;
@ -651,10 +721,13 @@ class StickersManager final : public Actor {
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);
std::pair<vector<FileId>, vector<FileId>> split_stickers_by_premium(const StickerSet *sticker_set) const;
Result<std::tuple<FileId, bool, bool, StickerFormat>> prepare_input_sticker(td_api::inputSticker *sticker);
Result<std::tuple<FileId, bool, bool, StickerFormat>> prepare_input_file(
const tl_object_ptr<td_api::InputFile> &input_file, StickerFormat format, StickerType type, bool for_thumbnail);
Result<std::tuple<FileId, bool, bool, StickerFormat>> prepare_input_sticker(td_api::inputSticker *sticker,
StickerType sticker_type);
tl_object_ptr<telegram_api::inputStickerSetItem> get_input_sticker(const td_api::inputSticker *sticker,
FileId file_id) const;
@ -672,13 +745,24 @@ class StickersManager final : public Actor {
void on_added_sticker_uploaded(int64 random_id, Result<Unit> result);
void do_add_sticker_to_set(UserId user_id, string short_name, tl_object_ptr<td_api::inputSticker> &&sticker,
Promise<td_api::object_ptr<td_api::stickerSet>> &&promise);
void on_sticker_set_thumbnail_uploaded(int64 random_id, Result<Unit> result);
void do_set_sticker_set_thumbnail(UserId user_id, string short_name, tl_object_ptr<td_api::InputFile> &&thumbnail,
Promise<Unit> &&promise);
Promise<td_api::object_ptr<td_api::stickerSet>> &&promise);
bool update_sticker_set_cache(const StickerSet *sticker_set, Promise<Unit> &promise);
const StickerSet *get_premium_gift_sticker_set();
static FileId get_premium_gift_option_sticker_id(const StickerSet *sticker_set, int32 month_count);
FileId get_premium_gift_option_sticker_id(int32 month_count);
void try_update_premium_gift_messages();
const StickerSet *get_animated_emoji_sticker_set();
static std::pair<FileId, int> get_animated_emoji_sticker(const StickerSet *sticker_set, const string &emoji);
@ -687,11 +771,15 @@ class StickersManager final : public Actor {
FileId get_animated_emoji_sound_file_id(const string &emoji) const;
FileId get_custom_animated_emoji_sticker_id(int64 custom_emoji_id) const;
td_api::object_ptr<td_api::animatedEmoji> get_animated_emoji_object(std::pair<FileId, int> animated_sticker,
FileId sound_file_id) const;
void try_update_animated_emoji_messages();
void try_update_custom_emoji_messages(int64 custom_emoji_id);
static int get_emoji_number(Slice emoji);
vector<FileId> get_animated_emoji_click_stickers(const StickerSet *sticker_set, Slice emoji) const;
@ -743,6 +831,12 @@ class StickersManager final : public Actor {
static void add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail);
td_api::object_ptr<td_api::stickers> get_custom_emoji_stickers_object(const vector<int64> &document_ids);
void on_get_custom_emoji_documents(Result<vector<telegram_api::object_ptr<telegram_api::Document>>> &&r_documents,
vector<int64> &&document_ids,
Promise<td_api::object_ptr<td_api::stickers>> &&promise);
static string get_emoji_language_code_version_database_key(const string &language_code);
static string get_emoji_language_code_last_difference_time_database_key(const string &language_code);
@ -785,41 +879,41 @@ class StickersManager final : public Actor {
bool is_inited_ = false;
FlatHashMap<FileId, unique_ptr<Sticker>, FileIdHash> stickers_; // file_id -> Sticker
FlatHashMap<StickerSetId, unique_ptr<StickerSet>, StickerSetIdHash> sticker_sets_; // id -> StickerSet
FlatHashMap<string, StickerSetId> short_name_to_sticker_set_id_;
WaitFreeHashMap<FileId, unique_ptr<Sticker>, FileIdHash> stickers_; // file_id -> Sticker
WaitFreeHashMap<StickerSetId, unique_ptr<StickerSet>, StickerSetIdHash> sticker_sets_; // id -> StickerSet
WaitFreeHashMap<string, StickerSetId> short_name_to_sticker_set_id_;
vector<StickerSetId> installed_sticker_set_ids_[2];
vector<StickerSetId> featured_sticker_set_ids_;
vector<StickerSetId> old_featured_sticker_set_ids_;
vector<StickerSetId> installed_sticker_set_ids_[MAX_STICKER_TYPE];
vector<StickerSetId> featured_sticker_set_ids_[MAX_STICKER_TYPE];
vector<StickerSetId> old_featured_sticker_set_ids_[MAX_STICKER_TYPE];
vector<FileId> recent_sticker_ids_[2];
vector<FileId> favorite_sticker_ids_;
double next_installed_sticker_sets_load_time_[2] = {0, 0};
double next_featured_sticker_sets_load_time_ = 0;
double next_installed_sticker_sets_load_time_[MAX_STICKER_TYPE] = {0, 0, 0};
double next_featured_sticker_sets_load_time_[MAX_STICKER_TYPE] = {0, 0, 0};
double next_recent_stickers_load_time_[2] = {0, 0};
double next_favorite_stickers_load_time_ = 0;
int64 installed_sticker_sets_hash_[2] = {0, 0};
int64 featured_sticker_sets_hash_ = 0;
int64 installed_sticker_sets_hash_[MAX_STICKER_TYPE] = {0, 0, 0};
int64 featured_sticker_sets_hash_[MAX_STICKER_TYPE] = {0, 0, 0};
int64 recent_stickers_hash_[2] = {0, 0};
int32 old_featured_sticker_set_count_ = -1;
uint32 old_featured_sticker_set_generation_ = 1;
int32 old_featured_sticker_set_count_[MAX_STICKER_TYPE] = {-1, 0, 0};
uint32 old_featured_sticker_set_generation_[MAX_STICKER_TYPE] = {1, 0, 0};
bool need_update_installed_sticker_sets_[2] = {false, false};
bool need_update_featured_sticker_sets_ = false;
bool need_update_installed_sticker_sets_[MAX_STICKER_TYPE] = {false, false, false};
bool need_update_featured_sticker_sets_[MAX_STICKER_TYPE] = {false, false, false};
bool are_installed_sticker_sets_loaded_[2] = {false, false};
bool are_featured_sticker_sets_loaded_ = false;
bool are_installed_sticker_sets_loaded_[MAX_STICKER_TYPE] = {false, false, false};
bool are_featured_sticker_sets_loaded_[MAX_STICKER_TYPE] = {false, true, false};
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;
bool are_featured_sticker_sets_premium_[MAX_STICKER_TYPE] = {false, false, false};
bool are_old_featured_sticker_sets_invalidated_[MAX_STICKER_TYPE] = {false, false, false};
vector<Promise<Unit>> load_installed_sticker_sets_queries_[2];
vector<Promise<Unit>> load_featured_sticker_sets_queries_;
vector<Promise<Unit>> load_installed_sticker_sets_queries_[MAX_STICKER_TYPE];
vector<Promise<Unit>> load_featured_sticker_sets_queries_[MAX_STICKER_TYPE];
vector<Promise<Unit>> load_old_featured_sticker_sets_queries_;
vector<Promise<Unit>> load_recent_stickers_queries_[2];
vector<Promise<Unit>> repair_recent_stickers_queries_[2];
@ -833,20 +927,15 @@ class StickersManager final : public Actor {
FileSourceId app_config_file_source_id_;
vector<StickerSetId> archived_sticker_set_ids_[2];
int32 total_archived_sticker_set_count_[2] = {-1, -1};
vector<StickerSetId> archived_sticker_set_ids_[MAX_STICKER_TYPE];
int32 total_archived_sticker_set_count_[MAX_STICKER_TYPE] = {-1, -1, -1};
FlatHashMap<FileId, vector<StickerSetId>, FileIdHash> attached_sticker_sets_;
Hints installed_sticker_sets_hints_[2]; // search installed sticker sets by their title and name
Hints installed_sticker_sets_hints_[MAX_STICKER_TYPE]; // search installed sticker sets by their title and name
struct FoundStickers {
vector<FileId> sticker_ids_;
int32 cache_time_ = 300;
double next_reload_time_ = 0;
};
FlatHashMap<string, FoundStickers> found_stickers_;
FlatHashMap<string, vector<Promise<Unit>>> search_stickers_queries_;
FlatHashMap<string, vector<std::pair<int32, Promise<td_api::object_ptr<td_api::stickers>>>>> search_stickers_queries_;
std::unordered_map<string, vector<StickerSetId>> found_sticker_sets_;
std::unordered_map<string, vector<Promise<Unit>>> search_sticker_sets_queries_;
@ -860,14 +949,16 @@ class StickersManager final : public Actor {
FlatHashMap<SpecialStickerSetType, unique_ptr<SpecialStickerSet>, SpecialStickerSetTypeHash> special_sticker_sets_;
struct StickerSetLoadRequest {
Promise<Unit> promise;
Status error;
size_t left_queries = 0;
Promise<Unit> promise_;
Status error_;
size_t left_queries_ = 0;
};
FlatHashMap<uint32, StickerSetLoadRequest> sticker_set_load_requests_;
uint32 current_sticker_set_load_request_ = 0;
FlatHashMap<int64, vector<Promise<Unit>>> custom_emoji_load_queries_;
FlatHashMap<int64, unique_ptr<PendingNewStickerSet>> pending_new_sticker_sets_;
FlatHashMap<int64, unique_ptr<PendingAddStickerToSet>> pending_add_sticker_to_sets_;
@ -875,6 +966,7 @@ class StickersManager final : public Actor {
FlatHashMap<int64, unique_ptr<PendingSetStickerSetThumbnail>> pending_set_sticker_set_thumbnails_;
vector<Promise<Unit>> pending_get_animated_emoji_queries_;
vector<Promise<Unit>> pending_get_premium_gift_option_sticker_queries_;
double next_click_animated_emoji_message_time_ = 0;
double next_update_animated_emoji_clicked_time_ = 0;
@ -886,9 +978,9 @@ class StickersManager final : public Actor {
std::vector<std::pair<int, double>> pending_animated_emoji_clicks_;
struct SentAnimatedEmojiClicks {
double send_time = 0.0;
DialogId dialog_id;
string emoji;
double send_time_ = 0.0;
DialogId dialog_id_;
string emoji_;
};
std::vector<SentAnimatedEmojiClicks> sent_animated_emoji_clicks_;
@ -906,15 +998,27 @@ class StickersManager final : public Actor {
FlatHashMap<string, vector<Promise<Unit>>> load_language_codes_queries_;
FlatHashMap<int64, string> emoji_suggestions_urls_;
struct GiftPremiumMessages {
FlatHashSet<FullMessageId, FullMessageIdHash> full_message_ids_;
FileId sticker_id_;
};
FlatHashMap<int32, unique_ptr<GiftPremiumMessages>> premium_gift_messages_;
FlatHashMap<string, FlatHashSet<FullMessageId, FullMessageIdHash>> dice_messages_;
struct EmojiMessages {
FlatHashSet<FullMessageId, FullMessageIdHash> full_message_ids;
std::pair<FileId, int> animated_emoji_sticker;
FileId sound_file_id;
FlatHashSet<FullMessageId, FullMessageIdHash> full_message_ids_;
std::pair<FileId, int> animated_emoji_sticker_;
FileId sound_file_id_;
};
FlatHashMap<string, unique_ptr<EmojiMessages>> emoji_messages_;
struct CustomEmojiMessages {
FlatHashSet<FullMessageId, FullMessageIdHash> full_message_ids_;
FileId sticker_id_;
};
FlatHashMap<int64, unique_ptr<CustomEmojiMessages>> custom_emoji_messages_;
string dice_emojis_str_;
vector<string> dice_emojis_;
@ -924,6 +1028,8 @@ class StickersManager final : public Actor {
string emoji_sounds_str_;
FlatHashMap<string, FileId> emoji_sounds_;
WaitFreeHashMap<int64, FileId> custom_emoji_to_sticker_id_;
double animated_emoji_zoom_ = 0.0;
bool disable_animated_emojis_ = false;

View File

@ -26,47 +26,55 @@ namespace td {
template <class StorerT>
void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer, const char *source) const {
auto it = stickers_.find(file_id);
LOG_CHECK(it != stickers_.end()) << file_id << ' ' << in_sticker_set << ' ' << source;
const Sticker *sticker = it->second.get();
bool has_sticker_set_access_hash = sticker->set_id.is_valid() && !in_sticker_set;
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();
const Sticker *sticker = get_sticker(file_id);
LOG_CHECK(sticker != nullptr) << file_id << ' ' << in_sticker_set << ' ' << source;
bool has_sticker_set_access_hash = sticker->set_id_.is_valid() && !in_sticker_set;
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();
bool is_mask = sticker->type_ == StickerType::Mask;
bool is_emoji = sticker->type_ == StickerType::CustomEmoji;
bool has_emoji_receive_date = is_emoji && sticker->emoji_receive_date_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(sticker->is_mask);
STORE_FLAG(is_mask);
STORE_FLAG(has_sticker_set_access_hash);
STORE_FLAG(in_sticker_set);
STORE_FLAG(is_tgs);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(is_webm);
STORE_FLAG(has_premium_animation);
STORE_FLAG(is_emoji);
STORE_FLAG(sticker->is_premium_);
STORE_FLAG(has_emoji_receive_date);
END_STORE_FLAGS();
if (!in_sticker_set) {
store(sticker->set_id.get(), storer);
store(sticker->set_id_.get(), storer);
if (has_sticker_set_access_hash) {
auto sticker_set = get_sticker_set(sticker->set_id);
auto sticker_set = get_sticker_set(sticker->set_id_);
CHECK(sticker_set != nullptr);
store(sticker_set->access_hash, storer);
}
store(sticker_set->access_hash_, storer);
}
store(sticker->alt, storer);
store(sticker->dimensions, storer);
store(sticker->s_thumbnail, storer);
store(sticker->m_thumbnail, storer);
}
store(sticker->alt_, storer);
store(sticker->dimensions_, storer);
store(sticker->s_thumbnail_, storer);
store(sticker->m_thumbnail_, storer);
store(file_id, storer);
if (sticker->is_mask) {
store(sticker->point, storer);
store(sticker->x_shift, storer);
store(sticker->y_shift, storer);
store(sticker->scale, storer);
if (is_mask) {
store(sticker->point_, storer);
store(sticker->x_shift_, storer);
store(sticker->y_shift_, storer);
store(sticker->scale_, storer);
}
if (has_minithumbnail) {
store(sticker->minithumbnail, storer);
store(sticker->minithumbnail_, storer);
}
if (has_premium_animation) {
store(sticker->premium_animation_file_id, storer);
store(sticker->premium_animation_file_id_, storer);
}
if (has_emoji_receive_date) {
store(sticker->emoji_receive_date_, storer);
}
}
@ -83,21 +91,34 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
bool is_tgs;
bool is_webm;
bool has_premium_animation;
bool is_mask;
bool is_emoji;
bool has_emoji_receive_date;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(sticker->is_mask);
PARSE_FLAG(is_mask);
PARSE_FLAG(has_sticker_set_access_hash);
PARSE_FLAG(in_sticker_set_stored);
PARSE_FLAG(is_tgs);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(is_webm);
PARSE_FLAG(has_premium_animation);
PARSE_FLAG(is_emoji);
PARSE_FLAG(sticker->is_premium_);
PARSE_FLAG(has_emoji_receive_date);
END_PARSE_FLAGS();
if (is_webm) {
sticker->format = StickerFormat::Webm;
sticker->format_ = StickerFormat::Webm;
} else if (is_tgs) {
sticker->format = StickerFormat::Tgs;
sticker->format_ = StickerFormat::Tgs;
} else {
sticker->format = StickerFormat::Webp;
sticker->format_ = StickerFormat::Webp;
}
if (is_emoji) {
sticker->type_ = StickerType::CustomEmoji;
} else if (is_mask) {
sticker->type_ = StickerType::Mask;
} else {
sticker->type_ = StickerType::Regular;
}
if (in_sticker_set_stored != in_sticker_set) {
Slice data = parser.template fetch_string_raw<Slice>(parser.get_left_len());
@ -113,92 +134,107 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
if (!in_sticker_set) {
int64 set_id;
parse(set_id, parser);
sticker->set_id = StickerSetId(set_id);
sticker->set_id_ = StickerSetId(set_id);
if (has_sticker_set_access_hash) {
int64 sticker_set_access_hash;
parse(sticker_set_access_hash, parser);
add_sticker_set(sticker->set_id, sticker_set_access_hash);
add_sticker_set(sticker->set_id_, sticker_set_access_hash);
} else {
// backward compatibility
sticker->set_id = StickerSetId();
sticker->set_id_ = StickerSetId();
}
}
parse(sticker->alt, parser);
parse(sticker->dimensions, parser);
parse(sticker->alt_, parser);
parse(sticker->dimensions_, parser);
PhotoSize thumbnail;
parse(thumbnail, parser);
add_sticker_thumbnail(sticker.get(), thumbnail);
parse(thumbnail, parser);
add_sticker_thumbnail(sticker.get(), thumbnail);
parse(sticker->file_id, parser);
if (sticker->is_mask) {
parse(sticker->point, parser);
parse(sticker->x_shift, parser);
parse(sticker->y_shift, parser);
parse(sticker->scale, parser);
parse(sticker->file_id_, parser);
if (is_mask) {
parse(sticker->point_, parser);
parse(sticker->x_shift_, parser);
parse(sticker->y_shift_, parser);
parse(sticker->scale_, parser);
}
if (has_minithumbnail) {
parse(sticker->minithumbnail, parser);
parse(sticker->minithumbnail_, parser);
}
if (has_premium_animation) {
parse(sticker->premium_animation_file_id, parser);
sticker->is_premium_ = true;
parse(sticker->premium_animation_file_id_, parser);
}
if (parser.get_error() != nullptr || !sticker->file_id.is_valid()) {
if (has_emoji_receive_date) {
parse(sticker->emoji_receive_date_, parser);
}
if (parser.get_error() != nullptr || !sticker->file_id_.is_valid()) {
return FileId();
}
sticker->is_from_database_ = true;
return on_get_sticker(std::move(sticker), false); // data in the database is always outdated
}
template <class StorerT>
void StickersManager::store_sticker_set(const StickerSet *sticker_set, bool with_stickers, StorerT &storer,
const char *source) const {
size_t stickers_limit = with_stickers ? sticker_set->sticker_ids.size() : 5;
bool is_full = sticker_set->sticker_ids.size() <= stickers_limit;
bool was_loaded = sticker_set->was_loaded && is_full;
bool is_loaded = sticker_set->is_loaded && is_full;
bool has_expires_at = !sticker_set->is_installed && sticker_set->expires_at != 0;
bool has_thumbnail = sticker_set->thumbnail.file_id.is_valid();
bool has_minithumbnail = !sticker_set->minithumbnail.empty();
bool is_tgs = sticker_set->sticker_format == StickerFormat::Tgs;
bool is_webm = sticker_set->sticker_format == StickerFormat::Webm;
size_t stickers_limit =
with_stickers ? sticker_set->sticker_ids_.size() : get_max_featured_sticker_count(sticker_set->sticker_type_);
bool is_full = sticker_set->sticker_ids_.size() <= stickers_limit;
bool was_loaded = sticker_set->was_loaded_ && is_full;
bool is_loaded = sticker_set->is_loaded_ && is_full;
bool has_expires_at = !sticker_set->is_installed_ && sticker_set->expires_at_ != 0;
bool has_thumbnail = sticker_set->thumbnail_.file_id.is_valid();
bool has_minithumbnail = !sticker_set->minithumbnail_.empty();
bool is_tgs = sticker_set->sticker_format_ == StickerFormat::Tgs;
bool is_webm = sticker_set->sticker_format_ == StickerFormat::Webm;
bool is_masks = sticker_set->sticker_type_ == StickerType::Mask;
bool is_emojis = sticker_set->sticker_type_ == StickerType::CustomEmoji;
bool has_thumbnail_document_id = sticker_set->thumbnail_document_id_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(sticker_set->is_inited);
STORE_FLAG(sticker_set->is_inited_);
STORE_FLAG(was_loaded);
STORE_FLAG(is_loaded);
STORE_FLAG(sticker_set->is_installed);
STORE_FLAG(sticker_set->is_archived);
STORE_FLAG(sticker_set->is_official);
STORE_FLAG(sticker_set->is_masks);
STORE_FLAG(sticker_set->is_viewed);
STORE_FLAG(sticker_set->is_installed_);
STORE_FLAG(sticker_set->is_archived_);
STORE_FLAG(sticker_set->is_official_);
STORE_FLAG(is_masks);
STORE_FLAG(sticker_set->is_viewed_);
STORE_FLAG(has_expires_at);
STORE_FLAG(has_thumbnail);
STORE_FLAG(sticker_set->is_thumbnail_reloaded);
STORE_FLAG(sticker_set->is_thumbnail_reloaded_);
STORE_FLAG(is_tgs);
STORE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded);
STORE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded_);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(is_webm);
STORE_FLAG(is_emojis);
STORE_FLAG(has_thumbnail_document_id);
END_STORE_FLAGS();
store(sticker_set->id.get(), storer);
store(sticker_set->access_hash, storer);
if (sticker_set->is_inited) {
store(sticker_set->title, storer);
store(sticker_set->short_name, storer);
store(sticker_set->sticker_count, storer);
store(sticker_set->hash, storer);
store(sticker_set->id_.get(), storer);
store(sticker_set->access_hash_, storer);
if (sticker_set->is_inited_) {
store(sticker_set->title_, storer);
store(sticker_set->short_name_, storer);
store(sticker_set->sticker_count_, storer);
store(sticker_set->hash_, storer);
if (has_expires_at) {
store(sticker_set->expires_at, storer);
store(sticker_set->expires_at_, storer);
}
if (has_thumbnail) {
store(sticker_set->thumbnail, storer);
store(sticker_set->thumbnail_, storer);
}
if (has_minithumbnail) {
store(sticker_set->minithumbnail, storer);
store(sticker_set->minithumbnail_, storer);
}
if (has_thumbnail_document_id) {
store(sticker_set->thumbnail_document_id_, storer);
}
auto stored_sticker_count = narrow_cast<uint32>(is_full ? sticker_set->sticker_ids.size() : stickers_limit);
auto stored_sticker_count = narrow_cast<uint32>(is_full ? sticker_set->sticker_ids_.size() : stickers_limit);
store(stored_sticker_count, storer);
for (uint32 i = 0; i < stored_sticker_count; i++) {
auto sticker_id = sticker_set->sticker_ids[i];
auto sticker_id = sticker_set->sticker_ids_[i];
store_sticker(sticker_id, true, storer, source);
if (was_loaded) {
@ -216,8 +252,8 @@ void StickersManager::store_sticker_set(const StickerSet *sticker_set, bool with
template <class ParserT>
void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser) {
CHECK(sticker_set != nullptr);
CHECK(!sticker_set->was_loaded);
bool was_inited = sticker_set->is_inited;
CHECK(!sticker_set->was_loaded_);
bool was_inited = sticker_set->is_inited_;
bool is_installed;
bool is_archived;
bool is_official;
@ -227,32 +263,33 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
bool is_tgs;
bool has_minithumbnail;
bool is_webm;
bool is_emojis;
bool has_thumbnail_document_id;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(sticker_set->is_inited);
PARSE_FLAG(sticker_set->was_loaded);
PARSE_FLAG(sticker_set->is_loaded);
PARSE_FLAG(sticker_set->is_inited_);
PARSE_FLAG(sticker_set->was_loaded_);
PARSE_FLAG(sticker_set->is_loaded_);
PARSE_FLAG(is_installed);
PARSE_FLAG(is_archived);
PARSE_FLAG(is_official);
PARSE_FLAG(is_masks);
PARSE_FLAG(sticker_set->is_viewed);
PARSE_FLAG(sticker_set->is_viewed_);
PARSE_FLAG(has_expires_at);
PARSE_FLAG(has_thumbnail);
PARSE_FLAG(sticker_set->is_thumbnail_reloaded);
PARSE_FLAG(sticker_set->is_thumbnail_reloaded_);
PARSE_FLAG(is_tgs);
PARSE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded);
PARSE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded_);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(is_webm);
PARSE_FLAG(is_emojis);
PARSE_FLAG(has_thumbnail_document_id);
END_PARSE_FLAGS();
int64 sticker_set_id;
int64 access_hash;
parse(sticker_set_id, parser);
parse(access_hash, parser);
CHECK(sticker_set->id.get() == sticker_set_id);
if (sticker_set->access_hash != access_hash) {
LOG(ERROR) << "Access hash of " << sticker_set->id << " has changed from " << access_hash << " to "
<< sticker_set->access_hash;
}
CHECK(sticker_set->id_.get() == sticker_set_id);
(void)access_hash; // unused, because only known sticker sets with access hash can be loaded from database
StickerFormat sticker_format = StickerFormat::Unknown;
if (is_webm) {
@ -262,12 +299,19 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
} else {
sticker_format = StickerFormat::Webp;
}
StickerType sticker_type = StickerType::Regular;
if (is_emojis) {
sticker_type = StickerType::CustomEmoji;
} else if (is_masks) {
sticker_type = StickerType::Mask;
}
if (sticker_set->is_inited) {
if (sticker_set->is_inited_) {
string title;
string short_name;
string minithumbnail;
PhotoSize thumbnail;
int64 thumbnail_document_id = 0;
int32 sticker_count;
int32 hash;
int32 expires_at = 0;
@ -284,49 +328,54 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
if (has_minithumbnail) {
parse(minithumbnail, parser);
}
if (has_thumbnail_document_id) {
parse(thumbnail_document_id, parser);
}
if (!was_inited) {
sticker_set->title = std::move(title);
sticker_set->short_name = std::move(short_name);
sticker_set->minithumbnail = std::move(minithumbnail);
sticker_set->thumbnail = std::move(thumbnail);
sticker_set->sticker_count = sticker_count;
sticker_set->hash = hash;
sticker_set->expires_at = expires_at;
sticker_set->is_official = is_official;
sticker_set->is_masks = is_masks;
sticker_set->sticker_format = sticker_format;
sticker_set->title_ = std::move(title);
sticker_set->short_name_ = std::move(short_name);
sticker_set->minithumbnail_ = std::move(minithumbnail);
sticker_set->thumbnail_ = std::move(thumbnail);
sticker_set->thumbnail_document_id_ = thumbnail_document_id;
sticker_set->sticker_count_ = sticker_count;
sticker_set->hash_ = hash;
sticker_set->expires_at_ = expires_at;
sticker_set->is_official_ = is_official;
sticker_set->sticker_type_ = sticker_type;
sticker_set->sticker_format_ = sticker_format;
auto cleaned_username = clean_username(sticker_set->short_name);
auto cleaned_username = clean_username(sticker_set->short_name_);
if (!cleaned_username.empty()) {
short_name_to_sticker_set_id_.emplace(cleaned_username, sticker_set->id);
short_name_to_sticker_set_id_.set(cleaned_username, sticker_set->id_);
}
on_update_sticker_set(sticker_set, is_installed, is_archived, false, true);
} else {
if (sticker_set->title != title) {
LOG(INFO) << "Title of " << sticker_set->id << " has changed";
if (sticker_set->title_ != title) {
LOG(INFO) << "Title of " << sticker_set->id_ << " has changed";
}
if (sticker_set->short_name != short_name) {
LOG(ERROR) << "Short name of " << sticker_set->id << " has changed from \"" << short_name << "\" to \""
<< sticker_set->short_name << "\"";
if (sticker_set->short_name_ != short_name) {
LOG(ERROR) << "Short name of " << sticker_set->id_ << " has changed from \"" << short_name << "\" to \""
<< sticker_set->short_name_ << "\"";
}
if (sticker_set->sticker_count != sticker_count || sticker_set->hash != hash) {
sticker_set->is_loaded = false;
if (sticker_set->sticker_count_ != sticker_count || sticker_set->hash_ != hash) {
sticker_set->is_loaded_ = false;
}
if (sticker_set->sticker_format != sticker_format) {
LOG(ERROR) << "Sticker format of " << sticker_set->id << " has changed from \"" << sticker_format << "\" to \""
<< sticker_set->sticker_format << "\"";
if (sticker_set->sticker_format_ != sticker_format) {
LOG(ERROR) << "Sticker format of " << sticker_set->id_ << " has changed from \"" << sticker_format << "\" to \""
<< sticker_set->sticker_format_ << "\"";
}
if (sticker_set->is_masks != is_masks) {
LOG(ERROR) << "Is masks of " << sticker_set->id << " has changed from \"" << is_masks << "\" to \""
<< sticker_set->is_masks << "\"";
if (sticker_set->sticker_type_ != sticker_type) {
LOG(ERROR) << "Type of " << sticker_set->id_ << " has changed from \"" << sticker_type << "\" to \""
<< sticker_set->sticker_type_ << "\"";
}
}
uint32 stored_sticker_count;
parse(stored_sticker_count, parser);
sticker_set->sticker_ids.clear();
if (sticker_set->was_loaded) {
sticker_set->sticker_ids_.clear();
sticker_set->premium_sticker_positions_.clear();
if (sticker_set->was_loaded_) {
sticker_set->emoji_stickers_map_.clear();
sticker_set->sticker_emojis_map_.clear();
}
@ -338,16 +387,19 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
if (!sticker_id.is_valid()) {
return parser.set_error("Receive invalid sticker in a sticker set");
}
sticker_set->sticker_ids.push_back(sticker_id);
sticker_set->sticker_ids_.push_back(sticker_id);
Sticker *sticker = get_sticker(sticker_id);
CHECK(sticker != nullptr);
if (sticker->set_id != sticker_set->id) {
LOG_IF(ERROR, sticker->set_id.is_valid()) << "Sticker " << sticker_id << " set_id has changed";
sticker->set_id = sticker_set->id;
if (sticker->set_id_ != sticker_set->id_) {
LOG_IF(ERROR, sticker->set_id_.is_valid()) << "Sticker " << sticker_id << " set_id has changed";
sticker->set_id_ = sticker_set->id_;
}
if (sticker->is_premium_) {
sticker_set->premium_sticker_positions_.push_back(static_cast<int32>(sticker_set->sticker_ids_.size() - 1));
}
if (sticker_set->was_loaded) {
if (sticker_set->was_loaded_) {
vector<string> emojis;
parse(emojis, parser);
for (auto &emoji : emojis) {
@ -358,21 +410,21 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser
sticker_ids.push_back(sticker_id);
}
} else {
LOG(INFO) << "Sticker " << sticker_id << " in " << sticker_set_id << '/' << sticker_set->short_name
LOG(INFO) << "Sticker " << sticker_id << " in " << sticker_set_id << '/' << sticker_set->short_name_
<< " has an empty emoji";
}
}
sticker_set->sticker_emojis_map_[sticker_id] = std::move(emojis);
}
}
if (expires_at > sticker_set->expires_at) {
sticker_set->expires_at = expires_at;
if (expires_at > sticker_set->expires_at_) {
sticker_set->expires_at_ = expires_at;
}
if (!check_utf8(sticker_set->title)) {
if (!check_utf8(sticker_set->title_)) {
return parser.set_error("Have invalid sticker set title");
}
if (!check_utf8(sticker_set->short_name)) {
if (!check_utf8(sticker_set->short_name_)) {
return parser.set_error("Have invalid sticker set name");
}
}
@ -384,7 +436,7 @@ void StickersManager::store_sticker_set_id(StickerSetId sticker_set_id, StorerT
const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
store(sticker_set_id.get(), storer);
store(sticker_set->access_hash, storer);
store(sticker_set->access_hash_, storer);
}
template <class ParserT>

View File

@ -108,6 +108,7 @@
#include "td/telegram/StateManager.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/StorageManager.h"
#include "td/telegram/MemoryManager.h"
#include "td/telegram/SuggestedAction.h"
@ -1983,13 +1984,16 @@ class GetScopeNotificationSettingsRequest final : public RequestActor<> {
};
class GetStickersRequest final : public RequestActor<> {
StickerType sticker_type_;
string emoji_;
int32 limit_;
DialogId dialog_id_;
vector<FileId> sticker_ids_;
void do_run(Promise<Unit> &&promise) final {
sticker_ids_ = td_->stickers_manager_->get_stickers(emoji_, limit_, get_tries() < 2, std::move(promise));
sticker_ids_ = td_->stickers_manager_->get_stickers(sticker_type_, emoji_, limit_, dialog_id_, get_tries() < 2,
std::move(promise));
}
void do_send_result() final {
@ -1997,39 +2001,24 @@ class GetStickersRequest final : public RequestActor<> {
}
public:
GetStickersRequest(ActorShared<Td> td, uint64 request_id, string &&emoji, int32 limit)
: RequestActor(std::move(td), request_id), emoji_(std::move(emoji)), limit_(limit) {
set_tries(5);
}
};
class SearchStickersRequest final : public RequestActor<> {
string emoji_;
int32 limit_;
vector<FileId> sticker_ids_;
void do_run(Promise<Unit> &&promise) final {
sticker_ids_ = td_->stickers_manager_->search_stickers(emoji_, limit_, std::move(promise));
}
void do_send_result() final {
send_result(td_->stickers_manager_->get_stickers_object(sticker_ids_));
}
public:
SearchStickersRequest(ActorShared<Td> td, uint64 request_id, string &&emoji, int32 limit)
: RequestActor(std::move(td), request_id), emoji_(std::move(emoji)), limit_(limit) {
GetStickersRequest(ActorShared<Td> td, uint64 request_id, StickerType sticker_type, string &&emoji, int32 limit,
int64 dialog_id)
: RequestActor(std::move(td), request_id)
, sticker_type_(sticker_type)
, emoji_(std::move(emoji))
, limit_(limit)
, dialog_id_(dialog_id) {
set_tries(4);
}
};
class GetInstalledStickerSetsRequest final : public RequestActor<> {
bool is_masks_;
StickerType sticker_type_;
vector<StickerSetId> sticker_set_ids_;
void do_run(Promise<Unit> &&promise) final {
sticker_set_ids_ = td_->stickers_manager_->get_installed_sticker_sets(is_masks_, std::move(promise));
sticker_set_ids_ = td_->stickers_manager_->get_installed_sticker_sets(sticker_type_, std::move(promise));
}
void do_send_result() final {
@ -2037,13 +2026,13 @@ class GetInstalledStickerSetsRequest final : public RequestActor<> {
}
public:
GetInstalledStickerSetsRequest(ActorShared<Td> td, uint64 request_id, bool is_masks)
: RequestActor(std::move(td), request_id), is_masks_(is_masks) {
GetInstalledStickerSetsRequest(ActorShared<Td> td, uint64 request_id, StickerType sticker_type)
: RequestActor(std::move(td), request_id), sticker_type_(sticker_type) {
}
};
class GetArchivedStickerSetsRequest final : public RequestActor<> {
bool is_masks_;
StickerType sticker_type_;
StickerSetId offset_sticker_set_id_;
int32 limit_;
@ -2052,7 +2041,7 @@ class GetArchivedStickerSetsRequest final : public RequestActor<> {
void do_run(Promise<Unit> &&promise) final {
std::tie(total_count_, sticker_set_ids_) = td_->stickers_manager_->get_archived_sticker_sets(
is_masks_, offset_sticker_set_id_, limit_, get_tries() < 2, std::move(promise));
sticker_type_, offset_sticker_set_id_, limit_, get_tries() < 2, std::move(promise));
}
void do_send_result() final {
@ -2060,10 +2049,10 @@ class GetArchivedStickerSetsRequest final : public RequestActor<> {
}
public:
GetArchivedStickerSetsRequest(ActorShared<Td> td, uint64 request_id, bool is_masks, int64 offset_sticker_set_id,
int32 limit)
GetArchivedStickerSetsRequest(ActorShared<Td> td, uint64 request_id, StickerType sticker_type,
int64 offset_sticker_set_id, int32 limit)
: RequestActor(std::move(td), request_id)
, is_masks_(is_masks)
, sticker_type_(sticker_type)
, offset_sticker_set_id_(offset_sticker_set_id)
, limit_(limit) {
}
@ -2071,11 +2060,12 @@ class GetArchivedStickerSetsRequest final : public RequestActor<> {
class GetTrendingStickerSetsRequest final : public RequestActor<> {
td_api::object_ptr<td_api::trendingStickerSets> result_;
StickerType sticker_type_;
int32 offset_;
int32 limit_;
void do_run(Promise<Unit> &&promise) final {
result_ = td_->stickers_manager_->get_featured_sticker_sets(offset_, limit_, std::move(promise));
result_ = td_->stickers_manager_->get_featured_sticker_sets(sticker_type_, offset_, limit_, std::move(promise));
}
void do_send_result() final {
@ -2083,8 +2073,9 @@ class GetTrendingStickerSetsRequest final : public RequestActor<> {
}
public:
GetTrendingStickerSetsRequest(ActorShared<Td> td, uint64 request_id, int32 offset, int32 limit)
: RequestActor(std::move(td), request_id), offset_(offset), limit_(limit) {
GetTrendingStickerSetsRequest(ActorShared<Td> td, uint64 request_id, StickerType sticker_type, int32 offset,
int32 limit)
: RequestActor(std::move(td), request_id), sticker_type_(sticker_type), offset_(offset), limit_(limit) {
set_tries(3);
}
};
@ -2149,7 +2140,7 @@ class SearchStickerSetRequest final : public RequestActor<> {
};
class SearchInstalledStickerSetsRequest final : public RequestActor<> {
bool is_masks_;
StickerType sticker_type_;
string query_;
int32 limit_;
@ -2157,7 +2148,7 @@ class SearchInstalledStickerSetsRequest final : public RequestActor<> {
void do_run(Promise<Unit> &&promise) final {
sticker_set_ids_ =
td_->stickers_manager_->search_installed_sticker_sets(is_masks_, query_, limit_, std::move(promise));
td_->stickers_manager_->search_installed_sticker_sets(sticker_type_, query_, limit_, std::move(promise));
}
void do_send_result() final {
@ -2165,8 +2156,9 @@ class SearchInstalledStickerSetsRequest final : public RequestActor<> {
}
public:
SearchInstalledStickerSetsRequest(ActorShared<Td> td, uint64 request_id, bool is_masks, string &&query, int32 limit)
: RequestActor(std::move(td), request_id), is_masks_(is_masks), query_(std::move(query)), limit_(limit) {
SearchInstalledStickerSetsRequest(ActorShared<Td> td, uint64 request_id, StickerType sticker_type, string &&query,
int32 limit)
: RequestActor(std::move(td), request_id), sticker_type_(sticker_type), query_(std::move(query)), limit_(limit) {
}
};
@ -2229,92 +2221,6 @@ class UploadStickerFileRequest final : public RequestOnceActor {
}
};
class CreateNewStickerSetRequest final : public RequestOnceActor {
UserId user_id_;
string title_;
string name_;
vector<tl_object_ptr<td_api::inputSticker>> stickers_;
string software_;
void do_run(Promise<Unit> &&promise) final {
td_->stickers_manager_->create_new_sticker_set(user_id_, title_, name_, std::move(stickers_), std::move(software_),
std::move(promise));
}
void do_send_result() final {
auto set_id = td_->stickers_manager_->search_sticker_set(name_, Auto());
if (!set_id.is_valid()) {
return send_error(Status::Error(500, "Created sticker set not found"));
}
send_result(td_->stickers_manager_->get_sticker_set_object(set_id));
}
public:
CreateNewStickerSetRequest(ActorShared<Td> td, uint64 request_id, int64 user_id, string &&title, string &&name,
vector<tl_object_ptr<td_api::inputSticker>> &&stickers, string &&software)
: RequestOnceActor(std::move(td), request_id)
, user_id_(user_id)
, title_(std::move(title))
, name_(std::move(name))
, stickers_(std::move(stickers))
, software_(std::move(software)) {
}
};
class AddStickerToSetRequest final : public RequestOnceActor {
UserId user_id_;
string name_;
tl_object_ptr<td_api::inputSticker> sticker_;
void do_run(Promise<Unit> &&promise) final {
td_->stickers_manager_->add_sticker_to_set(user_id_, name_, std::move(sticker_), std::move(promise));
}
void do_send_result() final {
auto set_id = td_->stickers_manager_->search_sticker_set(name_, Auto());
if (!set_id.is_valid()) {
return send_error(Status::Error(500, "Sticker set not found"));
}
send_result(td_->stickers_manager_->get_sticker_set_object(set_id));
}
public:
AddStickerToSetRequest(ActorShared<Td> td, uint64 request_id, int64 user_id, string &&name,
tl_object_ptr<td_api::inputSticker> &&sticker)
: RequestOnceActor(std::move(td), request_id)
, user_id_(user_id)
, name_(std::move(name))
, sticker_(std::move(sticker)) {
}
};
class SetStickerSetThumbnailRequest final : public RequestOnceActor {
UserId user_id_;
string name_;
tl_object_ptr<td_api::InputFile> thumbnail_;
void do_run(Promise<Unit> &&promise) final {
td_->stickers_manager_->set_sticker_set_thumbnail(user_id_, name_, std::move(thumbnail_), std::move(promise));
}
void do_send_result() final {
auto set_id = td_->stickers_manager_->search_sticker_set(name_, Auto());
if (!set_id.is_valid()) {
return send_error(Status::Error(500, "Sticker set not found"));
}
send_result(td_->stickers_manager_->get_sticker_set_object(set_id));
}
public:
SetStickerSetThumbnailRequest(ActorShared<Td> td, uint64 request_id, int64 user_id, string &&name,
tl_object_ptr<td_api::InputFile> &&thumbnail)
: RequestOnceActor(std::move(td), request_id)
, user_id_(user_id)
, name_(std::move(name))
, thumbnail_(std::move(thumbnail)) {
}
};
class GetRecentStickersRequest final : public RequestActor<> {
bool is_attached_;
@ -3253,11 +3159,11 @@ void Td::on_result(NetQueryPtr query) {
}
void Td::on_connection_state_changed(ConnectionState new_state) {
if (new_state == connection_state_) {
LOG(ERROR) << "State manager sends update about unchanged state " << static_cast<int32>(new_state);
if (G()->close_flag()) {
return;
}
if (G()->close_flag()) {
if (new_state == connection_state_) {
LOG(ERROR) << "State manager sends update about unchanged state " << static_cast<int32>(new_state);
return;
}
connection_state_ = new_state;
@ -4054,7 +3960,7 @@ void Td::init_file_manager() {
file_manager_->init_actor();
G()->set_file_manager(file_manager_actor_.get());
file_reference_manager_ = make_unique<FileReferenceManager>();
file_reference_manager_ = make_unique<FileReferenceManager>(create_reference());
file_reference_manager_actor_ = register_actor("FileReferenceManager", file_reference_manager_.get());
G()->set_file_reference_manager(file_reference_manager_actor_.get());
}
@ -4072,6 +3978,7 @@ void Td::init_managers() {
G()->set_animations_manager(animations_manager_actor_.get());
attach_menu_manager_ = make_unique<AttachMenuManager>(this, create_reference());
attach_menu_manager_actor_ = register_actor("AttachMenuManager", attach_menu_manager_.get());
G()->set_attach_menu_manager(attach_menu_manager_actor_.get());
background_manager_ = make_unique<BackgroundManager>(this, create_reference());
background_manager_actor_ = register_actor("BackgroundManager", background_manager_.get());
G()->set_background_manager(background_manager_actor_.get());
@ -4169,8 +4076,10 @@ void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
VLOG(td_requests) << "Sending update: " << oneline(to_string(object));
break;
case td_api::updateTrendingStickerSets::ID: {
auto sticker_sets = static_cast<const td_api::updateTrendingStickerSets *>(object.get())->sticker_sets_.get();
VLOG(td_requests) << "Sending update: updateTrendingStickerSets { total_count = " << sticker_sets->total_count_
auto update = static_cast<const td_api::updateTrendingStickerSets *>(object.get());
auto sticker_sets = update->sticker_sets_.get();
VLOG(td_requests) << "Sending update: updateTrendingStickerSets { " << oneline(to_string(update->sticker_type_))
<< ", total_count = " << sticker_sets->total_count_
<< ", count = " << sticker_sets->sets_.size() << " }";
break;
}
@ -4666,7 +4575,7 @@ void Td::on_request(uint64 id, const td_api::setAccountTtl &request) {
void Td::on_request(uint64 id, td_api::deleteAccount &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.reason_);
send_closure(auth_manager_actor_, &AuthManager::delete_account, id, request.reason_);
send_closure(auth_manager_actor_, &AuthManager::delete_account, id, request.reason_, request.password_);
}
void Td::on_request(uint64 id, td_api::changePhoneNumber &request) {
@ -5235,7 +5144,7 @@ void Td::on_request(uint64 id, const td_api::clickAnimatedEmojiMessage &request)
}
void Td::on_request(uint64 id, const td_api::getInternalLinkType &request) {
auto type = link_manager_->parse_internal_link(request.link_);
auto type = LinkManager::parse_internal_link(request.link_);
send_closure(actor_id(this), &Td::send_result, id, type == nullptr ? nullptr : type->get_internal_link_type_object());
}
@ -6621,7 +6530,7 @@ void Td::on_request(uint64 id, const td_api::getSuggestedFileName &request) {
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::text>(r_file_name.ok()));
}
void Td::on_request(uint64 id, td_api::uploadFile &request) {
void Td::on_request(uint64 id, td_api::preliminaryUploadFile &request) {
auto priority = request.priority_;
if (!(1 <= priority && priority <= 32)) {
return send_error_raw(id, 400, "Upload priority must be between 1 and 32");
@ -6643,7 +6552,7 @@ void Td::on_request(uint64 id, td_api::uploadFile &request) {
send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(upload_file_id, false));
}
void Td::on_request(uint64 id, const td_api::cancelUploadFile &request) {
void Td::on_request(uint64 id, const td_api::cancelPreliminaryUploadFile &request) {
file_manager_->cancel_upload(FileId(request.file_id_, 0));
send_closure(actor_id(this), &Td::send_result, id, make_tl_object<td_api::ok>());
@ -7017,28 +6926,38 @@ void Td::on_request(uint64 id, td_api::closeSecretChat &request) {
void Td::on_request(uint64 id, td_api::getStickers &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.emoji_);
CREATE_REQUEST(GetStickersRequest, std::move(request.emoji_), request.limit_);
CREATE_REQUEST(GetStickersRequest, get_sticker_type(request.sticker_type_), std::move(request.emoji_), request.limit_,
request.chat_id_);
}
void Td::on_request(uint64 id, td_api::searchStickers &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.emoji_);
CREATE_REQUEST(SearchStickersRequest, std::move(request.emoji_), request.limit_);
CREATE_REQUEST_PROMISE();
stickers_manager_->search_stickers(std::move(request.emoji_), request.limit_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumStickers &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
stickers_manager_->get_premium_stickers(request.limit_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getInstalledStickerSets &request) {
CHECK_IS_USER();
CREATE_REQUEST(GetInstalledStickerSetsRequest, request.is_masks_);
CREATE_REQUEST(GetInstalledStickerSetsRequest, get_sticker_type(request.sticker_type_));
}
void Td::on_request(uint64 id, const td_api::getArchivedStickerSets &request) {
CHECK_IS_USER();
CREATE_REQUEST(GetArchivedStickerSetsRequest, request.is_masks_, request.offset_sticker_set_id_, request.limit_);
CREATE_REQUEST(GetArchivedStickerSetsRequest, get_sticker_type(request.sticker_type_), request.offset_sticker_set_id_,
request.limit_);
}
void Td::on_request(uint64 id, const td_api::getTrendingStickerSets &request) {
CHECK_IS_USER();
CREATE_REQUEST(GetTrendingStickerSetsRequest, request.offset_, request.limit_);
CREATE_REQUEST(GetTrendingStickerSetsRequest, get_sticker_type(request.sticker_type_), request.offset_,
request.limit_);
}
void Td::on_request(uint64 id, const td_api::getAttachedStickerSets &request) {
@ -7057,7 +6976,8 @@ void Td::on_request(uint64 id, td_api::searchStickerSet &request) {
void Td::on_request(uint64 id, td_api::searchInstalledStickerSets &request) {
CLEAN_INPUT_STRING(request.query_);
CREATE_REQUEST(SearchInstalledStickerSetsRequest, request.is_masks_, std::move(request.query_), request.limit_);
CREATE_REQUEST(SearchInstalledStickerSetsRequest, get_sticker_type(request.sticker_type_), std::move(request.query_),
request.limit_);
}
void Td::on_request(uint64 id, td_api::searchStickerSets &request) {
@ -7079,8 +6999,9 @@ void Td::on_request(uint64 id, const td_api::viewTrendingStickerSets &request) {
void Td::on_request(uint64 id, td_api::reorderInstalledStickerSets &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
stickers_manager_->reorder_installed_sticker_sets(
request.is_masks_, StickersManager::convert_sticker_set_ids(request.sticker_set_ids_), std::move(promise));
stickers_manager_->reorder_installed_sticker_sets(get_sticker_type(request.sticker_type_),
StickersManager::convert_sticker_set_ids(request.sticker_set_ids_),
std::move(promise));
}
void Td::on_request(uint64 id, td_api::uploadStickerFile &request) {
@ -7118,21 +7039,27 @@ void Td::on_request(uint64 id, td_api::createNewStickerSet &request) {
CLEAN_INPUT_STRING(request.title_);
CLEAN_INPUT_STRING(request.name_);
CLEAN_INPUT_STRING(request.source_);
CREATE_REQUEST(CreateNewStickerSetRequest, request.user_id_, std::move(request.title_), std::move(request.name_),
std::move(request.stickers_), std::move(request.source_));
CREATE_REQUEST_PROMISE();
stickers_manager_->create_new_sticker_set(UserId(request.user_id_), std::move(request.title_),
std::move(request.name_), get_sticker_type(request.sticker_type_),
std::move(request.stickers_), std::move(request.source_),
std::move(promise));
}
void Td::on_request(uint64 id, td_api::addStickerToSet &request) {
CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.name_);
CREATE_REQUEST(AddStickerToSetRequest, request.user_id_, std::move(request.name_), std::move(request.sticker_));
CREATE_REQUEST_PROMISE();
stickers_manager_->add_sticker_to_set(UserId(request.user_id_), std::move(request.name_), std::move(request.sticker_),
std::move(promise));
}
void Td::on_request(uint64 id, td_api::setStickerSetThumbnail &request) {
CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.name_);
CREATE_REQUEST(SetStickerSetThumbnailRequest, request.user_id_, std::move(request.name_),
std::move(request.thumbnail_));
CREATE_REQUEST_PROMISE();
stickers_manager_->set_sticker_set_thumbnail(UserId(request.user_id_), std::move(request.name_),
std::move(request.thumbnail_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::setStickerPositionInSet &request) {
@ -7204,18 +7131,17 @@ 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_);
CREATE_REQUEST(GetEmojiSuggestionsUrlRequest, std::move(request.language_code_));
}
void Td::on_request(uint64 id, td_api::getCustomEmojiStickers &request) {
CREATE_REQUEST_PROMISE();
stickers_manager_->get_custom_emoji_stickers(std::move(request.custom_emoji_ids_), true, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getSavedAnimations &request) {
CHECK_IS_USER();
CREATE_NO_ARGS_REQUEST(GetSavedAnimationsRequest);
@ -7928,9 +7854,10 @@ void Td::on_request(uint64 id, const td_api::getPremiumFeatures &request) {
get_premium_features(this, request.source_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getPremiumStickers &request) {
void Td::on_request(uint64 id, const td_api::getPremiumStickerExamples &request) {
CHECK_IS_USER();
CREATE_REQUEST(SearchStickersRequest, "⭐️⭐️", 100);
CREATE_REQUEST_PROMISE();
stickers_manager_->search_stickers("⭐️⭐️", 100, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::viewPremiumFeature &request) {
@ -7951,23 +7878,26 @@ void Td::on_request(uint64 id, const td_api::getPremiumState &request) {
get_premium_state(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::canPurchasePremium &request) {
void Td::on_request(uint64 id, td_api::canPurchasePremium &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
can_purchase_premium(this, std::move(promise));
can_purchase_premium(this, std::move(request.purpose_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::assignAppStoreTransaction &request) {
void Td::on_request(uint64 id, td_api::assignAppStoreTransaction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
assign_app_store_transaction(this, request.receipt_, request.is_restore_, std::move(promise));
assign_app_store_transaction(this, request.receipt_, std::move(request.purpose_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::assignGooglePlayTransaction &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.package_name_);
CLEAN_INPUT_STRING(request.store_product_id_);
CLEAN_INPUT_STRING(request.purchase_token_);
CREATE_OK_REQUEST_PROMISE();
assign_play_market_transaction(this, request.purchase_token_, std::move(promise));
assign_play_market_transaction(this, request.package_name_, request.store_product_id_, request.purchase_token_,
std::move(request.purpose_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::acceptTermsOfService &request) {

View File

@ -958,9 +958,9 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::getSuggestedFileName &request);
void on_request(uint64 id, td_api::uploadFile &request);
void on_request(uint64 id, td_api::preliminaryUploadFile &request);
void on_request(uint64 id, const td_api::cancelUploadFile &request);
void on_request(uint64 id, const td_api::cancelPreliminaryUploadFile &request);
void on_request(uint64 id, td_api::writeGeneratedFilePart &request);
@ -1068,6 +1068,8 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::searchStickers &request);
void on_request(uint64 id, const td_api::getPremiumStickers &request);
void on_request(uint64 id, const td_api::getInstalledStickerSets &request);
void on_request(uint64 id, const td_api::getArchivedStickerSets &request);
@ -1126,10 +1128,10 @@ 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, td_api::getCustomEmojiStickers &request);
void on_request(uint64 id, const td_api::getFavoriteStickers &request);
void on_request(uint64 id, td_api::addFavoriteSticker &request);
@ -1310,7 +1312,7 @@ class Td final : public Actor {
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::getPremiumStickerExamples &request);
void on_request(uint64 id, const td_api::viewPremiumFeature &request);
@ -1318,9 +1320,9 @@ class Td final : public Actor {
void on_request(uint64 id, const td_api::getPremiumState &request);
void on_request(uint64 id, const td_api::canPurchasePremium &request);
void on_request(uint64 id, td_api::canPurchasePremium &request);
void on_request(uint64 id, const td_api::assignAppStoreTransaction &request);
void on_request(uint64 id, td_api::assignAppStoreTransaction &request);
void on_request(uint64 id, td_api::assignGooglePlayTransaction &request);

View File

@ -247,7 +247,7 @@ static auto get_color_json(int32 color);
template <>
auto get_color_json<false>(int32 color) {
return static_cast<int64>(static_cast<uint32>(color) | 0x000000FF);
return static_cast<int64>(static_cast<uint32>(color) | 0xFF000000);
}
template <>

View File

@ -48,6 +48,7 @@
#include "td/telegram/StateManager.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StickerType.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/telegram/TdDb.h"
@ -230,11 +231,11 @@ ActorShared<UpdatesManager> UpdatesManager::create_reference() {
}
void UpdatesManager::fill_pts_gap(void *td) {
CHECK(td != nullptr);
if (G()->close_flag()) {
return;
}
CHECK(td != nullptr);
auto updates_manager = static_cast<Td *>(td)->updates_manager_.get();
auto min_pts = std::numeric_limits<int32>::max();
auto max_pts = 0;
@ -251,11 +252,11 @@ void UpdatesManager::fill_pts_gap(void *td) {
}
void UpdatesManager::fill_seq_gap(void *td) {
CHECK(td != nullptr);
if (G()->close_flag()) {
return;
}
CHECK(td != nullptr);
auto updates_manager = static_cast<Td *>(td)->updates_manager_.get();
auto min_seq = std::numeric_limits<int32>::max();
auto max_seq = 0;
@ -268,11 +269,11 @@ void UpdatesManager::fill_seq_gap(void *td) {
}
void UpdatesManager::fill_qts_gap(void *td) {
CHECK(td != nullptr);
if (G()->close_flag()) {
return;
}
CHECK(td != nullptr);
auto updates_manager = static_cast<Td *>(td)->updates_manager_.get();
auto min_qts = std::numeric_limits<int32>::max();
auto max_qts = 0;
@ -289,8 +290,11 @@ void UpdatesManager::fill_get_difference_gap(void *td) {
}
void UpdatesManager::fill_gap(void *td, const char *source) {
if (G()->close_flag()) {
return;
}
CHECK(td != nullptr);
if (G()->close_flag() || !static_cast<Td *>(td)->auth_manager_->is_authorized()) {
if (!static_cast<Td *>(td)->auth_manager_->is_authorized()) {
return;
}
auto updates_manager = static_cast<Td *>(td)->updates_manager_.get();
@ -753,6 +757,7 @@ bool UpdatesManager::is_acceptable_message(const telegram_api::Message *message_
case telegram_api::messageActionChatJoinedByRequest::ID:
case telegram_api::messageActionWebViewDataSentMe::ID:
case telegram_api::messageActionWebViewDataSent::ID:
case telegram_api::messageActionGiftPremium::ID:
break;
case telegram_api::messageActionChatCreate::ID: {
auto chat_create = static_cast<const telegram_api::messageActionChatCreate *>(action);
@ -867,14 +872,15 @@ bool UpdatesManager::is_acceptable_update(const telegram_api::Update *update) co
}
void UpdatesManager::on_get_updates(tl_object_ptr<telegram_api::Updates> &&updates_ptr, Promise<Unit> &&promise) {
promise = PromiseCreator::lambda([promise = std::move(promise)](Result<Unit> result) mutable {
if (!G()->close_flag() && result.is_error()) {
LOG(ERROR) << "Failed to process updates: " << result.error();
}
promise.set_value(Unit());
});
CHECK(updates_ptr != nullptr);
promise = PromiseCreator::lambda(
[promise = std::move(promise), update_ids = get_update_ids(updates_ptr.get())](Result<Unit> result) mutable {
if (!G()->close_flag() && result.is_error()) {
LOG(ERROR) << "Failed to process updates " << update_ids << ": " << result.error();
}
promise.set_value(Unit());
});
auto updates_type = updates_ptr->get_id();
if (updates_type != telegram_api::updateShort::ID) {
LOG(INFO) << "Receive " << to_string(updates_ptr);
@ -1195,6 +1201,30 @@ vector<DialogId> UpdatesManager::get_update_notify_settings_dialog_ids(const tel
return dialog_ids;
}
vector<int32> UpdatesManager::get_update_ids(const telegram_api::Updates *updates_ptr) {
const vector<tl_object_ptr<telegram_api::Update>> *updates = nullptr;
auto updates_type = updates_ptr->get_id();
switch (updates_type) {
case telegram_api::updatesTooLong::ID:
case telegram_api::updateShortMessage::ID:
case telegram_api::updateShortChatMessage::ID:
case telegram_api::updateShortSentMessage::ID:
return {updates_type};
case telegram_api::updateShort::ID:
return {static_cast<const telegram_api::updateShort *>(updates_ptr)->update_->get_id()};
case telegram_api::updatesCombined::ID:
updates = &static_cast<const telegram_api::updatesCombined *>(updates_ptr)->updates_;
break;
case telegram_api::updates::ID:
updates = &static_cast<const telegram_api::updates *>(updates_ptr)->updates_;
break;
default:
UNREACHABLE();
}
return transform(*updates, [](const tl_object_ptr<telegram_api::Update> &update) { return update->get_id(); });
}
vector<DialogId> UpdatesManager::get_chat_dialog_ids(const telegram_api::Updates *updates_ptr) {
const vector<tl_object_ptr<telegram_api::Chat>> *chats = nullptr;
switch (updates_ptr->get_id()) {
@ -1205,14 +1235,12 @@ vector<DialogId> UpdatesManager::get_chat_dialog_ids(const telegram_api::Updates
case telegram_api::updateShortSentMessage::ID:
LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates";
break;
case telegram_api::updatesCombined::ID: {
case telegram_api::updatesCombined::ID:
chats = &static_cast<const telegram_api::updatesCombined *>(updates_ptr)->chats_;
break;
}
case telegram_api::updates::ID: {
case telegram_api::updates::ID:
chats = &static_cast<const telegram_api::updates *>(updates_ptr)->chats_;
break;
}
default:
UNREACHABLE();
}
@ -1525,7 +1553,9 @@ void UpdatesManager::on_get_difference(tl_object_ptr<telegram_api::updates_Diffe
break;
}
case telegram_api::updates_differenceTooLong::ID: {
LOG(ERROR) << "Receive differenceTooLong";
if (G()->shared_config().get_option_integer("session_count") <= 1) {
LOG(ERROR) << "Receive differenceTooLong";
}
// TODO
auto difference = move_tl_object_as<telegram_api::updates_differenceTooLong>(difference_ptr);
set_pts(difference->pts_, "differenceTooLong").set_value(Unit());
@ -1604,7 +1634,7 @@ void UpdatesManager::after_get_difference() {
for (auto &postponed_update : postponed_updates) {
auto &update = postponed_update.second;
add_pending_pts_update(std::move(update.update), update.pts, update.pts_count, update.receive_time,
std::move(update.promise), "after get difference");
std::move(update.promise), AFTER_GET_DIFFERENCE_SOURCE);
CHECK(!running_get_difference_);
}
VLOG(get_difference) << "After applying postponed pts updates have pts = " << get_pts()
@ -1640,11 +1670,11 @@ void UpdatesManager::schedule_data_reload() {
}
void UpdatesManager::try_reload_data_static(void *td) {
CHECK(td != nullptr);
if (G()->close_flag()) {
return;
}
CHECK(td != nullptr);
static_cast<Td *>(td)->updates_manager_->try_reload_data();
}
@ -1673,14 +1703,17 @@ void UpdatesManager::try_reload_data() {
td_->notification_settings_manager_->send_get_scope_notification_settings_query(NotificationSettingsScope::Channel,
Auto());
td_->stickers_manager_->reload_reactions();
td_->stickers_manager_->get_installed_sticker_sets(false, Auto());
td_->stickers_manager_->get_installed_sticker_sets(true, Auto());
td_->stickers_manager_->get_featured_sticker_sets(0, 1000, Auto());
for (int32 type = 0; type < MAX_STICKER_TYPE; type++) {
auto sticker_type = static_cast<StickerType>(type);
td_->stickers_manager_->get_installed_sticker_sets(sticker_type, Auto());
td_->stickers_manager_->get_featured_sticker_sets(sticker_type, 0, 1000, Auto());
}
td_->stickers_manager_->get_recent_stickers(false, Auto());
td_->stickers_manager_->get_recent_stickers(true, Auto());
td_->stickers_manager_->get_favorite_stickers(Auto());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji_click());
td_->stickers_manager_->reload_special_sticker_set_by_type(SpecialStickerSetType::premium_gifts());
schedule_data_reload();
}
@ -2186,7 +2219,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
}
int32 old_pts = get_pts();
if (new_pts < old_pts - 99 && Slice(source) != "after get difference") {
if (new_pts < old_pts - 99 && source != AFTER_GET_DIFFERENCE_SOURCE) {
bool need_restore_pts = new_pts < old_pts - 19999;
auto now = Time::now();
if (now > last_pts_jump_warning_time_ + 1 && (need_restore_pts || now < last_pts_jump_warning_time_ + 5)) {
@ -2219,7 +2252,7 @@ void UpdatesManager::add_pending_pts_update(tl_object_ptr<telegram_api::Update>
}
// is_acceptable_update check was skipped for postponed pts updates
if (Slice(source) == "after get difference" && !is_acceptable_update(update.get())) {
if (source == AFTER_GET_DIFFERENCE_SOURCE && !is_acceptable_update(update.get())) {
LOG(INFO) << "Postpone again unacceptable pending update";
postpone_pts_update(std::move(update), new_pts, pts_count, receive_time, std::move(promise));
set_pts_gap_timeout(0.001);
@ -3313,14 +3346,26 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateStickerSets> up
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateStickerSetsOrder> update, Promise<Unit> &&promise) {
td_->stickers_manager_->on_update_sticker_sets_order(update->masks_,
StickerType sticker_type = StickerType::Regular;
if (update->emojis_) {
sticker_type = StickerType::CustomEmoji;
} else if (update->masks_) {
sticker_type = StickerType::Mask;
}
td_->stickers_manager_->on_update_sticker_sets_order(sticker_type,
StickersManager::convert_sticker_set_ids(update->order_));
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadFeaturedStickers> update,
Promise<Unit> &&promise) {
td_->stickers_manager_->reload_featured_sticker_sets(true);
td_->stickers_manager_->reload_featured_sticker_sets(StickerType::Regular, true);
promise.set_value(Unit());
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateReadFeaturedEmojiStickers> update,
Promise<Unit> &&promise) {
td_->stickers_manager_->reload_featured_sticker_sets(StickerType::CustomEmoji, true);
promise.set_value(Unit());
}
@ -3351,6 +3396,8 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateBotPrecheckoutQ
UserId user_id(update->user_id_);
if (!user_id.is_valid()) {
LOG(ERROR) << "Receive pre-checkout query from invalid " << user_id;
} else if (update->total_amount_ <= 0 || !check_currency_amount(update->total_amount_)) {
LOG(ERROR) << "Receive pre-checkout query with invalid total amount " << update->total_amount_;
} else {
send_closure(
G()->td(), &Td::send_update,

View File

@ -132,6 +132,7 @@ class UpdatesManager final : public Actor {
static const double MAX_UNFILLED_GAP_TIME;
static const double MAX_PTS_SAVE_DELAY;
static constexpr bool DROP_PTS_UPDATES = false;
static constexpr const char *AFTER_GET_DIFFERENCE_SOURCE = "after get difference";
friend class OnUpdate;
@ -343,6 +344,8 @@ class UpdatesManager final : public Actor {
void try_reload_data();
static vector<int32> get_update_ids(const telegram_api::Updates *updates_ptr);
static bool have_update_pts_changed(const vector<tl_object_ptr<telegram_api::Update>> &updates);
static bool check_pts_update_dialog_id(DialogId dialog_id);
@ -487,6 +490,7 @@ class UpdatesManager final : public Actor {
void on_update(tl_object_ptr<telegram_api::updateStickerSets> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateStickerSetsOrder> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateReadFeaturedStickers> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateReadFeaturedEmojiStickers> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateRecentStickers> update, Promise<Unit> &&promise);
void on_update(tl_object_ptr<telegram_api::updateBotShippingQuery> update, Promise<Unit> &&promise);

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