Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2021-09-10 15:49:57 +02:00
commit c06a3d9dcf
38 changed files with 1074 additions and 892 deletions

View File

@ -60,15 +60,15 @@ if (POLICY CMP0069)
# set_property(DIRECTORY PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) do not work?
string(REPLACE ";" " " CXX_FLAGS_IPO "${CMAKE_CXX_COMPILE_OPTIONS_IPO}")
message(STATUS "Use link time optimization CXX options: ${CXX_FLAGS_IPO}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CXX_FLAGS_IPO}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_FLAGS_IPO}")
string(REPLACE ";" " " C_FLAGS_IPO "${CMAKE_C_COMPILE_OPTIONS_IPO}")
message(STATUS "Use link time optimization C options: ${C_FLAGS_IPO}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${C_FLAGS_IPO}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS_IPO}")
string(REPLACE ";" " " LINK_FLAGS_IPO "${CMAKE_CXX_LINK_OPTIONS_IPO}")
message(STATUS "Use link time optimization linker options: ${LINK_FLAGS_IPO}")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINK_FLAGS_IPO}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINK_FLAGS_IPO}")
endif()
endif()
endif()
@ -335,6 +335,7 @@ set(TDLIB_SOURCE
td/telegram/files/PartsManager.cpp
td/telegram/files/ResourceManager.cpp
td/telegram/Game.cpp
td/telegram/GameManager.cpp
td/telegram/Global.cpp
td/telegram/GroupCallManager.cpp
td/telegram/GroupCallParticipant.cpp
@ -403,7 +404,7 @@ set(TDLIB_SOURCE
td/telegram/SendCodeHelper.cpp
td/telegram/SequenceDispatcher.cpp
td/telegram/SpecialStickerSetType.cpp
td/telegram/SponsoredMessages.cpp
td/telegram/SponsoredMessageManager.cpp
td/telegram/StateManager.cpp
td/telegram/StickersManager.cpp
td/telegram/StorageManager.cpp
@ -524,6 +525,7 @@ set(TDLIB_SOURCE
td/telegram/FolderId.h
td/telegram/FullMessageId.h
td/telegram/Game.h
td/telegram/GameManager.h
td/telegram/Global.h
td/telegram/GroupCallId.h
td/telegram/GroupCallManager.h
@ -618,7 +620,7 @@ set(TDLIB_SOURCE
td/telegram/ServerMessageId.h
td/telegram/SetWithPosition.h
td/telegram/SpecialStickerSetType.h
td/telegram/SponsoredMessages.h
td/telegram/SponsoredMessageManager.h
td/telegram/StateManager.h
td/telegram/StickerSetId.h
td/telegram/StickersManager.h

View File

@ -80,7 +80,6 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele
- [Examples and documentation](#usage)
- [Dependencies](#dependencies)
- [Building](#building)
- [Installing dependencies](#installing-dependencies)
- [Using in CMake C++ projects](#using-cxx)
- [Using in Java projects](#using-java)
- [Using in .NET projects](#using-dotnet)
@ -132,8 +131,7 @@ for a list of all available `TDLib` [methods](https://core.telegram.org/tdlib/do
The simplest way to build `TDLight` is to use our [TDLight build instructions generator](https://tdlight-team.github.io/tdlight/build.html).
You need only to choose your programming language and target operating system to receive complete build instructions.
In general, you need to install all `TDLib` [dependencies](#dependencies) as described in [Installing dependencies](#installing-dependencies).
Then enter directory containing `TDLib` sources and compile them using CMake:
In general, you need to install all `TDLib` [dependencies](#dependencies), enter directory containing `TDLib` sources and compile them using CMake:
```
mkdir build
@ -159,44 +157,6 @@ php SplitSource.php --undo
```
In our tests clang 6.0 with libc++ required less than 500 MB of RAM per file and GCC 4.9/6.3 used less than 1 GB of RAM per file.
<a name="installing-dependencies"></a>
### Installing dependencies
<a name="macos"></a>
#### macOS
* Install the latest Xcode command line tools, for example, via `xcode-select --install`.
* Install other [dependencies](#dependencies), for example, using [Homebrew](https://brew.sh):
```
brew install gperf cmake openssl
```
* Build `TDLib` with CMake as explained in [building](#building). You will likely need to manually specify path to the installed OpenSSL to CMake, e.g.,
```
cmake -DCMAKE_BUILD_TYPE=Release -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/ ..
```
<a name="windows"></a>
#### Windows
* Download and install Microsoft Visual Studio 2015 or later.
* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable.
* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start).
* Run the following commands to install `TDLib` dependencies using vcpkg:
```
cd <path to vcpkg>
.\vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows
```
* Download and install [CMake](https://cmake.org/download/); choose "Add CMake to the system PATH" option while installing.
* Build `TDLib` with CMake as explained in [building](#building), but instead of `cmake -DCMAKE_BUILD_TYPE=Release ..` use
```
cmake -DCMAKE_TOOLCHAIN_FILE=<path to vcpkg>/scripts/buildsystems/vcpkg.cmake ..
```
To build 32-bit/64-bit `TDLib` using MSVC, you will need to additionally specify parameter `-A Win32`/`-A x64` to CMake.
To build `TDLib` in Release mode using MSVC, you will need to additionally specify parameter `--config Release` to the `cmake --build .` command.
<a name="linux"></a>
#### Linux
* Install all [dependencies](#dependencies) using your package manager.
<a name="using-cxx"></a>
## Using in CMake C++ projects
For C++ projects that use CMake, the best approach is to build `TDLib` as part of your project or to install it system-wide.

View File

@ -286,6 +286,7 @@ function split_file($file, $chunks, $undo) {
'file_reference_manager[_(-][^.]|FileReferenceManager|file_references[)]' => 'FileReferenceManager',
'file_manager[_(-][^.]|FileManager([^ ;.]| [^*])|update_file[)]' => 'files/FileManager',
'G[(][)]|Global[^A-Za-z]' => 'Global',
'game_manager[_(-][^.]|GameManager' => 'GameManager',
'group_call_manager[_(-][^.]|GroupCallManager' => 'GroupCallManager',
'HashtagHints' => 'HashtagHints',
'inline_queries_manager[_(-][^.]|InlineQueriesManager' => 'InlineQueriesManager',
@ -300,6 +301,7 @@ function split_file($file, $chunks, $undo) {
'PublicDialogType|get_public_dialog_type' => 'PublicDialogType',
'SecretChatActor' => 'SecretChatActor',
'secret_chats_manager[_(-][^.]|SecretChatsManager' => 'SecretChatsManager',
'sponsored_message_manager[_(-][^.]|SponsoredMessageManager' => 'SponsoredMessageManager',
'stickers_manager[_(-][^.]|StickersManager' => 'StickersManager',
'[>](td_db[(][)]|get_td_db_impl[(])|TdDb[^A-Za-z]' => 'TdDb',
'theme_manager[_(-][^.]|ThemeManager' => "ThemeManager",

View File

@ -542,6 +542,10 @@ function onOptionsChanged() {
if (os_netbsd) {
pre_text.push('Note that the following instruction is for NetBSD 8.0 and default SH shell.');
}
if (os_mac) {
pre_text.push('Note that the following instruction will build TDLib only for the current architecture (x64 or Apple silicon).');
pre_text.push('If you want to create a universal XCFramework, take a look at our <a href="https://github.com/tdlib/td/tree/master/example/ios">example</a> instead.');
}
var terminal_name = (function () {
if (os_windows) {

View File

@ -136,10 +136,11 @@ See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbou
TDLib can be used from the Swift programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
See [example/ios](https://github.com/tdlib/td/tree/master/example/ios) for an example of building TDLib for iOS, watchOS, tvOS, and macOS.
See [TDLib-iOS](https://github.com/leoMehlig/TDLib-iOS) or [tdlib-swift](https://github.com/modestman/tdlib-swift), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
See [example/swift](https://github.com/tdlib/td/tree/master/example/swift) for an example of a macOS Swift application.
See [example/ios](https://github.com/tdlib/td/tree/master/example/ios) for an example of building TDLib for iOS, watchOS, tvOS, and macOS.
<a name="objective-c"></a>
## Using TDLib in Objective-C projects

View File

@ -6,16 +6,12 @@ index 695be54..4efe5e5 100644
# - iOS - build everything for iOS
# - tvOS - build everything for tvOS
# - watchOS - build everything for watchOS
-# - OpenSSL-macOS - build OpenSSL for macOS
-# - OpenSSL-iOS - build OpenSSL for iOS
-# - OpenSSL-tvOS - build OpenSSL for tvOS
-# - OpenSSL-watchOS - build OpenSSL for watchOS
+# - OpenSSL-macOS - build OpenSSL for macOS
+# - OpenSSL-iOS - build OpenSSL for iOS
# - OpenSSL-macOS - build OpenSSL for macOS
# - OpenSSL-iOS - build OpenSSL for iOS
+# - OpenSSL-iOS-simulator - build OpenSSL for iOS-simulator
+# - OpenSSL-tvOS - build OpenSSL for tvOS
# - OpenSSL-tvOS - build OpenSSL for tvOS
+# - OpenSSL-tvOS-simulator - build OpenSSL for tvOS-simulator
+# - OpenSSL-watchOS - build OpenSSL for watchOS
# - OpenSSL-watchOS - build OpenSSL for watchOS
+# - OpenSSL-watchOS-simulator - build OpenSSL for watchOS-simulator
# - BZip2-macOS - build BZip2 for macOS
# - BZip2-iOS - build BZip2 for iOS

View File

@ -1,8 +1,8 @@
# Build for iOS
# Universal XCFramework build example
Below are instructions for building TDLib for iOS, watchOS, tvOS, and also macOS.
If you need only a macOS build, take a look at our build instructions for [macOS](https://github.com/tdlib/td#macos).
If you need only a macOS build for the current architecture, take a look at [TDLib build instructions generator](https://tdlib.github.io/td/build.html).
For example of usage take a look at our [Swift example](https://github.com/tdlib/td/tree/master/example/swift).
@ -21,24 +21,26 @@ cd native-build
cmake ..
cmake --build . --target prepare_cross_compiling
```
* Build OpenSSL for iOS, watchOS, tvOS and macOS:
* Build OpenSSL for iOS, watchOS, tvOS, and macOS:
```
cd <path to TDLib sources>/example/ios
./build-openssl.sh
```
Here we use scripts from [Python Apple support](https://github.com/beeware/Python-Apple-support), but any other OpenSSL builds should work too.
Here we use scripts from [Python Apple support](https://github.com/beeware/Python-Apple-support), but any other OpenSSL build should work too.
[Python Apple support](https://github.com/beeware/Python-Apple-support) has known problems with spaces in the path to the current directory, so
you need to ensure that there is no spaces in the path.
Built libraries should be stored in `third_party/openssl/<platform>`, because the next script will rely on this location.
* Build TDLib for iOS, watchOS, tvOS and macOS:
you need to ensure that there are no spaces in the path.
Built OpenSSL libraries should be stored in the directory `third_party/openssl/<platform>`, because the next script will rely on this location.
* Build TDLib for iOS, watchOS, tvOS, and macOS:
```
cd <path to TDLib sources>/example/ios
./build.sh
```
This may take a while, because TDLib will be built about 10 times.
Resulting library for iOS will work on any architecture (armv7, armv7s, arm64) and even on a simulator (Intel, Apple Silicon).
This may take a while, because TDLib will be built about 16 times.
Resulting XCFramework will work on any architecture and even on a simulator (x64, Apple silicon).
We use [CMake/iOS.cmake](https://github.com/tdlib/td/blob/master/CMake/iOS.cmake) toolchain, other toolchains may work too.
Built libraries will be stored in `tdjson` directory.
Built libraries and XCFramework will be stored in `tdjson` directory.
Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs.
If you receive an "error: SDK "appletvsimulator" cannot be located", you need to run the command "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" before running ./build.sh.

View File

@ -26,6 +26,37 @@ for platform in $platforms;
do
echo "Platform = ${platform}"
if [[ $platform = "macOS" ]]; then
simulators="0"
else
simulators="0 1"
fi
for simulator in $simulators;
do
if [[ $platform = "macOS" ]]; then
other_options="-DCMAKE_OSX_ARCHITECTURES='x86_64;arm64'"
else
watchos=""
if [[ $platform = "watchOS" ]]; then
ios_platform="WATCH"
watchos="-DTD_EXPERIMENTAL_WATCH_OS=ON"
elif [[ $platform = "tvOS" ]]; then
ios_platform="TV"
else
ios_platform=""
fi
if [[ $simulator = "1" ]]; then
platform="${platform}-simulator"
ios_platform="${ios_platform}SIMULATOR"
else
ios_platform="${ios_platform}OS"
fi
echo "iOS platform = ${ios_platform}"
other_options="${watchos} -DIOS_PLATFORM=${ios_platform} -DCMAKE_TOOLCHAIN_FILE=${td_path}/CMake/iOS.cmake"
fi
set_cmake_options $platform
build="build-${platform}"
install="install-${platform}"
@ -33,62 +64,15 @@ do
mkdir -p $build
mkdir -p $install
cd $build
cmake $td_path $options -DCMAKE_INSTALL_PREFIX=../${install} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cmake $td_path $options $other_options -DCMAKE_INSTALL_PREFIX=../${install}
make -j3 install || exit
cd ..
mkdir -p $platform
cp $build/libtdjson.dylib $platform/libtdjson.dylib
install_name_tool -id @rpath/libtdjson.dylib $platform/libtdjson.dylib
install_name_tool -id @rpath/libtdjson.dylib ${install}/lib/libtdjson.dylib
mkdir -p ../tdjson/${platform}/include
rsync --recursive ${install}/include/ ../tdjson/${platform}/include/
mkdir -p ../tdjson/${platform}/lib
cp ${platform}/libtdjson.dylib ../tdjson/${platform}/lib/
else
simulators="0 1"
for simulator in $simulators;
do
build="build-${platform}"
install="install-${platform}"
if [[ $simulator = "1" ]]; then
build="${build}-simulator"
install="${install}-simulator"
platform_path="${platform}-simulator"
ios_platform="SIMULATOR"
lib="${install}/lib/libtdjson.dylib"
set_cmake_options ${platform_path}
else
platform_path=${platform}
ios_platform="OS"
lib="${install}/lib/libtdjson.dylib"
set_cmake_options ${platform_path}
fi
watchos=""
if [[ $platform = "watchOS" ]]; then
ios_platform="WATCH${ios_platform}"
watchos="-DTD_EXPERIMENTAL_WATCH_OS=ON"
fi
if [[ $platform = "tvOS" ]]; then
ios_platform="TV${ios_platform}"
fi
echo $ios_platform
rm -rf $build
mkdir -p $build
mkdir -p $install
cd $build
cmake $td_path $options $watchos -DIOS_PLATFORM=${ios_platform} -DCMAKE_TOOLCHAIN_FILE=${td_path}/CMake/iOS.cmake -DCMAKE_INSTALL_PREFIX=../${install}
make -j3 install || exit
cd ..
install_name_tool -id @rpath/libtdjson.dylib $lib
mkdir -p ../tdjson/${platform_path}/include
rsync --recursive ${install}/include/ ../tdjson/${platform_path}/include/
mkdir -p ../tdjson/${platform_path}/lib
cp ${lib} ../tdjson/${platform_path}/lib/
done
fi
cp ${install}/lib/libtdjson.dylib ../tdjson/${platform}/lib/
done
done
produced_dylibs=(install-*/lib/libtdjson.dylib)
@ -104,4 +88,4 @@ xcodebuild -create-xcframework \
"${xcodebuild_frameworks[@]}" \
-output "libtdjson.xcframework"
rsync --recursive libtdjson.xcframework ../tdjson/
rsync --recursive libtdjson.xcframework ../tdjson/

View File

@ -32,8 +32,8 @@ cd <path to TDLib sources>/example/java/bin
java '-Djava.library.path=.' org/drinkless/tdlib/example/Example
```
If you get "Could NOT find JNI ..." error from CMake, you need to specify to CMake path to the installed JDK, for example, "-DJAVA_HOME=/usr/lib/jvm/java-8-oracle/".
If you receive "Could NOT find JNI ..." error from CMake, you need to specify to CMake path to the installed JDK, for example, "-DJAVA_HOME=/usr/lib/jvm/java-8-oracle/".
If you get java.lang.UnsatisfiedLinkError with "Can't find dependent libraries", you may also need to copy some dependent shared OpenSSL and zlib libraries to `bin/`.
If you receive java.lang.UnsatisfiedLinkError with "Can't find dependent libraries", you may also need to copy some dependent shared OpenSSL and zlib libraries to `bin/`.
In case you compiled the example as 32-bit version, you may need to give -d32 parameter to Java.

View File

@ -7,7 +7,7 @@ You need a Unix shell with `sed`, `tar` and `wget` utilities to run the provided
## Building tdweb NPM package
* Install the 2.0.6 [emsdk](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html), which is known to work. Do not use the system-provided `emscripten` package, because it contains a too old emsdk version.
* Install all `TDLib` build dependencies as described in [Building](https://github.com/tdlib/td#building).
* Install all `TDLib` build dependencies described in [Building](https://github.com/tdlib/td#building).
* Run `source ./emsdk_env.sh` from `emsdk` directory to set up the correct build environment.
* On `macOS`, install the `coreutils` [Homebrew](https://brew.sh) package and replace `realpath` in scripts with `grealpath`:
```

View File

@ -137,8 +137,8 @@ temporaryPasswordState has_password:Bool valid_for:int32 = TemporaryPasswordStat
//@is_downloading_active True, if the file is currently being downloaded (or a local copy is being generated by some other means)
//@is_downloading_completed True, if the local copy is fully available
//@download_offset Download will be started from this offset. downloaded_prefix_size is calculated from this offset
//@downloaded_prefix_size If is_downloading_completed is false, then only some prefix of the file starting from download_offset is ready to be read. downloaded_prefix_size is the size of that prefix
//@downloaded_size Total downloaded file bytes. Should be used only for calculating download progress. The actual file size may be bigger, and some parts of it may contain garbage
//@downloaded_prefix_size If is_downloading_completed is false, then only some prefix of the file starting from download_offset is ready to be read. downloaded_prefix_size is the size of that prefix in bytes
//@downloaded_size Total downloaded file size, in bytes. Should be used only for calculating download progress. The actual file size may be bigger, and some parts of it may contain garbage
localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_active:Bool is_downloading_completed:Bool download_offset:int32 downloaded_prefix_size:int32 downloaded_size:int32 = LocalFile;
//@description Represents a remote file
@ -148,13 +148,13 @@ localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_
//@unique_id Unique file identifier; may be empty if unknown. The unique file identifier which is the same for the same file even for different users and is persistent over time
//@is_uploading_active True, if the file is currently being uploaded (or a remote copy is being generated by some other means)
//@is_uploading_completed True, if a remote copy is fully available
//@uploaded_size Size of the remote available part of the file; 0 if unknown
//@uploaded_size Size of the remote available part of the file, in bytes; 0 if unknown
remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_completed:Bool uploaded_size:int32 = RemoteFile;
//@description Represents a file
//@id Unique file identifier
//@size File size; 0 if unknown
//@expected_size Expected file size in case the exact file size is unknown, but an approximate size is known. Can be used to show download/upload progress
//@size File size, in bytes; 0 if unknown
//@expected_size Approximate file size in bytes in case the exact file size is unknown. Can be used to show download/upload progress
//@local Information about the local copy of the file
//@remote Information about the remote copy of the file
file id:int32 size:int32 expected_size:int32 local:localFile remote:remoteFile = File;
@ -175,13 +175,13 @@ inputFileLocal path:string = InputFile;
//@description A file generated by the application @original_path Local path to a file from which the file is generated; may be empty if there is no such file
//@conversion String specifying the conversion applied to the original file; should be persistent across application restarts. Conversions beginning with '#' are reserved for internal TDLib usage
//@expected_size Expected size of the generated file; 0 if unknown
//@expected_size Expected size of the generated file, in bytes; 0 if unknown
inputFileGenerated original_path:string conversion:string expected_size:int32 = InputFile;
//@description Describes an image in JPEG format @type Image type (see https://core.telegram.org/constructor/photoSize)
//@photo Information about the image file @width Image width @height Image height
//@progressive_sizes Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image
//@progressive_sizes Sizes of progressive JPEG file prefixes, which can be used to preliminarily show the image; in bytes
photoSize type:string photo:file width:int32 height:int32 progressive_sizes:vector<int32> = PhotoSize;
//@description Thumbnail image of a very poor quality and low resolution @width Thumbnail width, usually doesn't exceed 40 @height Thumbnail height, usually doesn't exceed 40 @data The thumbnail in JPEG format
@ -1790,7 +1790,7 @@ messagePinMessage message_id:int53 = MessageContent;
//@description A screenshot of a message in the chat has been taken
messageScreenshotTaken = MessageContent;
//@description A theme in the chat has been changed @theme_name If non-empty, name of the new theme set for the chat. Otherwise theme was deleted in the chat
//@description A theme in the chat has been changed @theme_name If non-empty, name of the new theme set for the chat. Otherwise chat theme was reset to the default one
messageChatSetTheme theme_name:string = MessageContent;
//@description The TTL (Time To Live) setting for messages in the chat has been changed @ttl New message TTL setting
@ -2532,7 +2532,7 @@ chatEventUsernameChanged old_username:string new_username:string = ChatEventActi
//@description The chat photo was changed @old_photo Previous chat photo value; may be null @new_photo New chat photo value; may be null
chatEventPhotoChanged old_photo:chatPhoto new_photo:chatPhoto = ChatEventAction;
//@description The chat theme was changed @old_theme_name Previous chat theme name; empty if none @new_theme_name New chat theme name; empty if none
//@description The chat theme was changed @old_theme_name Previous chat theme name; empty if default one @new_theme_name New chat theme name; empty if default one
chatEventThemeChanged old_theme_name:string new_theme_name:string = ChatEventAction;
//@description The can_invite_users permission of a supergroup chat was toggled @can_invite_users New value of can_invite_users permission
@ -2893,7 +2893,7 @@ pushMessageContentChatChangePhoto = PushMessageContent;
//@description A chat title was edited @title New chat title
pushMessageContentChatChangeTitle title:string = PushMessageContent;
//@description A chat theme was edited @theme_name Name of the new chat theme
//@description A chat theme was edited @theme_name If non-empty, name of the new theme set for the chat. Otherwise chat theme was reset to the default one
pushMessageContentChatChangeTheme theme_name:string = PushMessageContent;
//@description A chat member was deleted @member_name Name of the deleted member @is_current_user True, if the current user was deleted from the group
@ -3194,7 +3194,8 @@ internalLinkTypeUnknownDeepLink link:string = InternalLinkType;
//@description The link is a link to a voice chat. Call searchPublicChat with the given chat username, and then joinGoupCall with the given invite hash to process the link
//@chat_username Username of the chat with the voice chat @invite_hash If non-empty, invite hash to be used to join the voice chat without being muted by administrators
internalLinkTypeVoiceChat chat_username:string invite_hash:string = InternalLinkType;
//@is_live_stream True, if the voice chat is expected to be a live stream in a channel or a broadcast group
internalLinkTypeVoiceChat chat_username:string invite_hash:string is_live_stream:Bool = InternalLinkType;
//@description Contains an HTTPS link to a message in a supergroup or channel @link Message link @is_public True, if the link will work for non-members of the chat
@ -3265,16 +3266,16 @@ fileTypeVoiceNote = FileType;
fileTypeWallpaper = FileType;
//@description Contains the storage usage statistics for a specific file type @file_type File type @size Total size of the files @count Total number of files
//@description Contains the storage usage statistics for a specific file type @file_type File type @size Total size of the files, in bytes @count Total number of files
storageStatisticsByFileType file_type:FileType size:int53 count:int32 = StorageStatisticsByFileType;
//@description Contains the storage usage statistics for a specific chat @chat_id Chat identifier; 0 if none @size Total size of the files in the chat @count Total number of files in the chat @by_file_type Statistics split by file types
//@description Contains the storage usage statistics for a specific chat @chat_id Chat identifier; 0 if none @size Total size of the files in the chat, in bytes @count Total number of files in the chat @by_file_type Statistics split by file types
storageStatisticsByChat chat_id:int53 size:int53 count:int32 by_file_type:vector<storageStatisticsByFileType> = StorageStatisticsByChat;
//@description Contains the exact storage usage statistics split by chats and file type @size Total size of files @count Total number of files @by_chat Statistics split by chats
//@description Contains the exact storage usage statistics split by chats and file type @size Total size of files, in bytes @count Total number of files @by_chat Statistics split by chats
storageStatistics size:int53 count:int32 by_chat:vector<storageStatisticsByChat> = StorageStatistics;
//@description Contains approximate storage usage statistics, excluding files of unknown file type @files_size Approximate total size of files @file_count Approximate number of files
//@description Contains approximate storage usage statistics, excluding files of unknown file type @files_size Approximate total size of files, in bytes @file_count Approximate number of files
//@database_size Size of the database @language_pack_database_size Size of the language pack database @log_size Size of the TDLib internal log
storageStatisticsFast files_size:int53 file_count:int32 database_size:int53 language_pack_database_size:int53 log_size:int53 = StorageStatisticsFast;
@ -3321,9 +3322,9 @@ networkStatistics since_date:int32 entries:vector<NetworkStatisticsEntry> = Netw
//@description Contains auto-download settings
//@is_auto_download_enabled True, if the auto-download is enabled
//@max_photo_file_size The maximum size of a photo file to be auto-downloaded
//@max_video_file_size The maximum size of a video file to be auto-downloaded
//@max_other_file_size The maximum size of other file types to be auto-downloaded
//@max_photo_file_size The maximum size of a photo file to be auto-downloaded, in bytes
//@max_video_file_size The maximum size of a video file to be auto-downloaded, in bytes
//@max_other_file_size The maximum size of other file types to be auto-downloaded, in bytes
//@video_upload_bitrate The maximum suggested bitrate for uploaded videos, in kbit/s
//@preload_large_videos True, if the beginning of video files needs to be preloaded for instant playback
//@preload_next_audio True, if the next audio track needs to be preloaded while the user is listening to an audio file
@ -3914,7 +3915,7 @@ logStreamDefault = LogStream;
//@description The log is written to a file
//@path Path to the file to where the internal TDLib log will be written
//@max_file_size The maximum size of the file to where the internal TDLib log is written before the file will be auto-rotated
//@max_file_size The maximum size of the file to where the internal TDLib log is written before the file will be auto-rotated, in bytes
//@redirect_stderr Pass true to additionally redirect stderr to the log file. Ignored on Windows
logStreamFile path:string max_file_size:int53 redirect_stderr:Bool = LogStream;
@ -4745,7 +4746,7 @@ setPinnedChats chat_list:ChatList chat_ids:vector<int53> = Ok;
//-the download has succeeded, has failed, has been canceled or a new downloadFile request with different offset/limit parameters was sent
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 synchronous:Bool = File;
//@description Returns file downloaded prefix size from a given offset @file_id Identifier of the file @offset Offset from which downloaded prefix size should be calculated
//@description Returns file downloaded prefix size from a given offset, in bytes @file_id Identifier of the file @offset Offset from which downloaded prefix size should be calculated
getFileDownloadedPrefixSize file_id:int32 offset:int32 = Count;
//@description Stops the downloading of a file. If a file has already been downloaded, does nothing @file_id Identifier of a file to stop downloading @only_if_pending Pass true to stop downloading only if it hasn't been started, i.e. request hasn't been sent to server
@ -5403,7 +5404,7 @@ getMemoryStatistics full:Bool = MemoryStatistics;
optimizeMemory full:Bool = Ok;
//@description Optimizes storage usage, i.e. deletes some files and returns new storage usage statistics. Secret thumbnails can't be deleted
//@size Limit on the total size of files after deletion. Pass -1 to use the default limit
//@size Limit on the total size of files after deletion, in bytes. Pass -1 to use the default limit
//@ttl Limit on the time that has passed since the last time a file was accessed (or creation time for some filesystems). Pass -1 to use the default limit
//@count Limit on the total count of files after deletion. Pass -1 to use the default limit
//@immunity_delay The amount of time after the creation of a file during which it can't be deleted, in seconds. Pass -1 to use the default value

View File

@ -6080,7 +6080,7 @@ void ContactsManager::on_update_bot_commands(DialogId dialog_id, UserId bot_user
auto user_full = get_user_full(user_id);
if (user_full != nullptr) {
on_update_user_full_commands(user_full, user_id, std::move(bot_commands));
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "on_update_bot_commands");
}
break;
}
@ -6105,7 +6105,7 @@ void ContactsManager::on_update_bot_commands(DialogId dialog_id, UserId bot_user
chat_full->is_changed = true;
}
}
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_bot_commands");
}
break;
}
@ -6130,7 +6130,7 @@ void ContactsManager::on_update_bot_commands(DialogId dialog_id, UserId bot_user
channel_full->is_changed = true;
}
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_bot_commands");
}
break;
}
@ -6302,7 +6302,7 @@ void ContactsManager::on_update_profile_success(int32 flags, const string &first
if (user_full != nullptr) {
user_full->about = about;
user_full->is_changed = true;
update_user_full(user_full, my_user_id);
update_user_full(user_full, my_user_id, "on_update_profile_success");
td_->group_call_manager_->on_update_dialog_about(DialogId(my_user_id), user_full->about, true);
}
}
@ -8021,7 +8021,7 @@ void ContactsManager::invalidate_user_full(UserId user_id) {
user_full->expires_at = 0.0;
user_full->need_save_to_database = true;
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "invalidate_user_full");
}
}
@ -9375,7 +9375,8 @@ void ContactsManager::on_load_user_full_from_database(UserId user_id, string val
td_->group_call_manager_->on_update_dialog_about(DialogId(user_id), user_full->about, false);
update_user_full(user_full, user_id, true);
user_full->is_update_user_full_sent = true;
update_user_full(user_full, user_id, "on_load_user_full_from_database", true);
if (is_user_deleted(user_id)) {
drop_user_full(user_id);
@ -9488,7 +9489,8 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val
on_update_chat_full_photo(chat_full, chat_id, std::move(chat_full->photo));
update_chat_full(chat_full, chat_id, true);
chat_full->is_update_chat_full_sent = true;
update_chat_full(chat_full, chat_id, "on_load_chat_full_from_database", true);
}
ContactsManager::ChatFull *ContactsManager::get_chat_full_force(ChatId chat_id, const char *source) {
@ -9620,7 +9622,8 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s
send_closure_later(G()->messages_manager(), &MessagesManager::on_dialog_bots_updated, DialogId(channel_id),
channel_full->bot_user_ids, true);
update_channel_full(channel_full, channel_id, true);
channel_full->is_update_channel_full_sent = true;
update_channel_full(channel_full, channel_id, "on_load_channel_full_from_database", true);
if (channel_full->expires_at == 0.0) {
load_channel_full(channel_id, true, Auto(), "on_load_channel_full_from_database");
@ -9670,7 +9673,7 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
auto user_full = get_user_full(user_id);
if (user_full != nullptr && user_full->need_phone_number_privacy_exception) {
on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, false);
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "update_user");
}
}
}
@ -9962,7 +9965,7 @@ void ContactsManager::update_secret_chat(SecretChat *c, SecretChatId secret_chat
}
}
void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, bool from_database) {
void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, const char *source, bool from_database) {
CHECK(user_full != nullptr);
unavailable_user_fulls_.erase(user_id); // don't needed anymore
if (user_full->is_common_chat_count_changed) {
@ -9973,11 +9976,18 @@ void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, bool
user_full->need_send_update |= user_full->is_changed;
user_full->need_save_to_database |= user_full->is_changed;
user_full->is_changed = false;
if (user_full->need_send_update || user_full->need_save_to_database) {
LOG(INFO) << "Update full " << user_id << " from " << source;
}
if (user_full->need_send_update) {
{
auto u = get_user(user_id);
CHECK(u == nullptr || u->is_update_user_sent);
}
if (!user_full->is_update_user_full_sent) {
LOG(ERROR) << "Send partial updateUserFullInfo for " << user_id << " from " << source;
user_full->is_update_user_full_sent = true;
}
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserFullInfo>(get_user_id_object(user_id, "updateUserFullInfo"),
get_user_full_info_object(user_id, user_full)));
@ -9991,13 +10001,16 @@ void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, bool
}
}
void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool from_database) {
void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, const char *source, bool from_database) {
CHECK(chat_full != nullptr);
unavailable_chat_fulls_.erase(chat_id); // don't needed anymore
chat_full->need_send_update |= chat_full->is_changed;
chat_full->need_save_to_database |= chat_full->is_changed;
chat_full->is_changed = false;
if (chat_full->need_send_update || chat_full->need_save_to_database) {
LOG(INFO) << "Update full " << chat_id << " from " << source;
}
if (chat_full->need_send_update) {
vector<DialogAdministrator> administrators;
vector<UserId> bot_user_ids;
@ -10026,6 +10039,10 @@ void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool
Chat *c = get_chat(chat_id);
CHECK(c == nullptr || c->is_update_basic_group_sent);
}
if (!chat_full->is_update_chat_full_sent) {
LOG(ERROR) << "Send partial updateBasicGroupFullInfo for " << chat_id << " from " << source;
chat_full->is_update_chat_full_sent = true;
}
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateBasicGroupFullInfo>(get_basic_group_id_object(chat_id, "update_chat_full"),
@ -10040,7 +10057,8 @@ void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool
}
}
void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool from_database) {
void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId channel_id, const char *source,
bool from_database) {
if (channel_full == nullptr) {
return;
}
@ -10076,6 +10094,9 @@ void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId c
channel_full->need_send_update |= channel_full->is_changed;
channel_full->need_save_to_database |= channel_full->is_changed;
channel_full->is_changed = false;
if (channel_full->need_send_update || channel_full->need_save_to_database) {
LOG(INFO) << "Update full " << channel_id << " from " << source;
}
if (channel_full->need_send_update) {
if (channel_full->linked_channel_id.is_valid()) {
td_->messages_manager_->force_create_dialog(DialogId(channel_full->linked_channel_id), "update_channel_full",
@ -10088,6 +10109,10 @@ void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId c
return;
}
}
if (!channel_full->is_update_channel_full_sent) {
LOG(ERROR) << "Send partial updateSupergroupFullInfo for " << channel_id << " from " << source;
channel_full->is_update_channel_full_sent = true;
}
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateSupergroupFullInfo>(get_supergroup_id_object(channel_id, "update_channel_full"),
@ -10207,7 +10232,8 @@ void ContactsManager::on_get_user_full(tl_object_ptr<telegram_api::userFull> &&u
register_user_photo(u, user_id, user_full->photo);
}
update_user_full(user_full, user_id);
user_full->is_update_user_full_sent = true;
update_user_full(user_full, user_id, "on_get_user_full");
// update peer settings after UserFull is created and updated to not update twice need_phone_number_privacy_exception
td_->messages_manager_->on_get_peer_settings(DialogId(user_id), std::move(user->settings_));
@ -10450,7 +10476,8 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
chat_full->is_changed = true;
}
update_chat_full(chat_full, chat_id);
chat_full->is_update_chat_full_sent = true;
update_chat_full(chat_full, chat_id, "on_get_chat_full");
} else {
CHECK(chat_full_ptr->get_id() == telegram_api::channelFull::ID);
auto channel = move_tl_object_as<telegram_api::channelFull>(chat_full_ptr);
@ -10698,13 +10725,14 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
channel_full->is_changed = true;
}
update_channel_full(channel_full, channel_id);
channel_full->is_update_channel_full_sent = true;
update_channel_full(channel_full, channel_id, "on_get_channel_full");
if (linked_channel_id.is_valid()) {
auto linked_channel_full = get_channel_full_force(linked_channel_id, true, "on_get_chat_full");
on_update_channel_full_linked_channel_id(linked_channel_full, linked_channel_id, channel_id);
if (linked_channel_full != nullptr) {
update_channel_full(linked_channel_full, linked_channel_id);
update_channel_full(linked_channel_full, linked_channel_id, "on_get_channel_full 2");
}
}
@ -11078,11 +11106,11 @@ void ContactsManager::on_update_user_is_blocked(UserId user_id, bool is_blocked)
}
UserFull *user_full = get_user_full_force(user_id);
if (user_full == nullptr) {
if (user_full == nullptr || user_full->is_blocked == is_blocked) {
return;
}
on_update_user_full_is_blocked(user_full, user_id, is_blocked);
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "on_update_user_is_blocked");
}
void ContactsManager::on_update_user_full_is_blocked(UserFull *user_full, UserId user_id, bool is_blocked) {
@ -11106,7 +11134,7 @@ void ContactsManager::on_update_user_common_chat_count(UserId user_id, int32 com
return;
}
on_update_user_full_common_chat_count(user_full, user_id, common_chat_count);
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "on_update_user_common_chat_count");
}
void ContactsManager::on_update_user_full_common_chat_count(UserFull *user_full, UserId user_id,
@ -11148,7 +11176,7 @@ void ContactsManager::on_update_user_need_phone_number_privacy_exception(UserId
return;
}
on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, need_phone_number_privacy_exception);
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "on_update_user_need_phone_number_privacy_exception");
}
void ContactsManager::on_update_user_full_need_phone_number_privacy_exception(
@ -11240,7 +11268,7 @@ void ContactsManager::add_profile_photo_to_cache(UserId user_id, Photo &&photo)
user_full->photo = photo;
user_full->is_changed = true;
}
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "add_profile_photo_to_cache");
}
// update ProfilePhoto in User
@ -11300,7 +11328,7 @@ bool ContactsManager::delete_profile_photo_from_cache(UserId user_id, int64 prof
load_user_full(user_id, true, Auto(), "delete_profile_photo_from_cache");
}
if (send_updates) {
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "delete_profile_photo_from_cache");
}
}
@ -11359,7 +11387,7 @@ void ContactsManager::drop_user_photos(UserId user_id, bool is_empty, bool drop_
}
load_user_full(user_id, true, Auto(), "drop_user_photos");
}
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "drop_user_photos");
}
}
@ -11386,7 +11414,7 @@ void ContactsManager::drop_user_full(UserId user_id) {
user_full->common_chat_count = 0;
user_full->is_changed = true;
update_user_full(user_full, user_id);
update_user_full(user_full, user_id, "drop_user_full");
td_->group_call_manager_->on_update_dialog_about(DialogId(user_id), user_full->about, true);
}
@ -11555,7 +11583,7 @@ void ContactsManager::on_get_chat_participants(tl_object_ptr<telegram_api::ChatP
on_update_chat_full_participants(chat_full, chat_id, std::move(new_participants), participants->version_,
from_update);
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_get_chat_participants");
break;
}
default:
@ -11793,7 +11821,7 @@ void ContactsManager::on_get_channel_participants(
channel_full->administrator_count = administrator_count;
channel_full->is_changed = true;
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_get_channel_participants");
}
if (participant_count != -1) {
auto c = get_channel(channel_id);
@ -11922,7 +11950,7 @@ void ContactsManager::speculative_add_channel_participants(ChannelId channel_id,
update_channel_online_member_count(channel_id, false);
}
if (channel_full != nullptr) {
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "speculative_add_channel_participants");
}
if (delta_participant_count == 0) {
return;
@ -11952,7 +11980,7 @@ void ContactsManager::speculative_delete_channel_participant(ChannelId channel_i
auto channel_full = get_channel_full_force(channel_id, true, "speculative_delete_channel_participant");
if (channel_full != nullptr && td::remove(channel_full->bot_user_ids, deleted_user_id)) {
channel_full->need_save_to_database = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "speculative_delete_channel_participant");
send_closure_later(G()->messages_manager(), &MessagesManager::on_dialog_bots_updated, DialogId(channel_id),
channel_full->bot_user_ids, false);
@ -11991,7 +12019,7 @@ void ContactsManager::speculative_add_channel_participant_count(ChannelId channe
channel_full->speculative_version++;
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "speculative_add_channel_participant_count");
}
void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId user_id,
@ -12107,7 +12135,7 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId
}
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "speculative_add_channel_user");
}
void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo,
@ -12126,7 +12154,7 @@ void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, b
}
send_get_channel_full_query(channel_full, channel_id, Auto(), "drop_channel_photos");
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "drop_channel_photos");
}
}
@ -12135,7 +12163,7 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_dr
auto channel_full = get_channel_full(channel_id, true, "invalidate_channel_full"); // must not load ChannelFull
if (channel_full != nullptr) {
do_invalidate_channel_full(channel_full, need_drop_slow_mode_delay);
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "invalidate_channel_full");
} else {
invalidated_channels_full_.insert(channel_id);
}
@ -12236,7 +12264,7 @@ void ContactsManager::on_get_permanent_dialog_invite_link(DialogId dialog_id, co
auto chat_full = get_chat_full_force(chat_id, "on_get_permanent_dialog_invite_link");
if (chat_full != nullptr && update_permanent_invite_link(chat_full->invite_link, invite_link)) {
chat_full->is_changed = true;
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_get_permanent_dialog_invite_link");
}
break;
}
@ -12245,7 +12273,7 @@ void ContactsManager::on_get_permanent_dialog_invite_link(DialogId dialog_id, co
auto channel_full = get_channel_full_force(channel_id, true, "on_get_permanent_dialog_invite_link");
if (channel_full != nullptr && update_permanent_invite_link(channel_full->invite_link, invite_link)) {
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_get_permanent_dialog_invite_link");
}
break;
}
@ -12335,7 +12363,8 @@ void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *chan
if (linked_channel_full != nullptr && linked_channel_full->linked_channel_id == channel_id) {
linked_channel_full->linked_channel_id = ChannelId();
linked_channel_full->is_changed = true;
update_channel_full(linked_channel_full, channel_full->linked_channel_id);
update_channel_full(linked_channel_full, channel_full->linked_channel_id,
"on_update_channel_full_linked_channel_id 3");
}
}
@ -12356,7 +12385,8 @@ void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *chan
if (linked_channel_full != nullptr && linked_channel_full->linked_channel_id != channel_id) {
linked_channel_full->linked_channel_id = channel_id;
linked_channel_full->is_changed = true;
update_channel_full(linked_channel_full, channel_full->linked_channel_id);
update_channel_full(linked_channel_full, channel_full->linked_channel_id,
"on_update_channel_full_linked_channel_id 4");
}
}
}
@ -12655,7 +12685,7 @@ void ContactsManager::on_update_chat_add_user(ChatId chat_id, UserId inviter_use
: DialogParticipantStatus::Member()});
update_chat_online_member_count(chat_full, chat_id, false);
chat_full->is_changed = true;
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_chat_add_user");
// Chat is already updated
if (chat_full->version == c->version &&
@ -12727,7 +12757,7 @@ void ContactsManager::on_update_chat_edit_administrator(ChatId chat_id, UserId u
if (participant.dialog_id == DialogId(user_id)) {
participant.status = std::move(status);
chat_full->is_changed = true;
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_chat_edit_administrator");
return;
}
}
@ -12780,7 +12810,7 @@ void ContactsManager::on_update_chat_delete_user(ChatId chat_id, UserId user_id,
chat_full->participants.resize(chat_full->participants.size() - 1);
chat_full->is_changed = true;
update_chat_online_member_count(chat_full, chat_id, false);
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_chat_delete_user");
if (static_cast<int32>(chat_full->participants.size()) != c->participant_count) {
repair_chat_participants(chat_id);
@ -12812,7 +12842,7 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti
ChatFull *chat_full = get_chat_full_force(chat_id, "on_update_chat_status");
if (chat_full != nullptr) {
on_update_chat_full_invite_link(chat_full, nullptr);
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_chat_status");
}
}
if (need_reload_group_call) {
@ -13019,7 +13049,7 @@ void ContactsManager::on_update_chat_description(ChatId chat_id, string &&descri
if (chat_full->description != description) {
chat_full->description = std::move(description);
chat_full->is_changed = true;
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "on_update_chat_description");
td_->group_call_manager_->on_update_dialog_about(DialogId(chat_id), chat_full->description, true);
}
}
@ -13084,7 +13114,7 @@ void ContactsManager::drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_
if (!is_empty) {
reload_chat_full(chat_id, Auto());
}
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "drop_chat_photos");
}
}
@ -13104,7 +13134,7 @@ void ContactsManager::drop_chat_full(ChatId chat_id) {
on_update_chat_full_invite_link(chat_full, nullptr);
update_chat_online_member_count(chat_full, chat_id, true);
chat_full->is_changed = true;
update_chat_full(chat_full, chat_id);
update_chat_full(chat_full, chat_id, "drop_chat_full");
}
void ContactsManager::on_update_channel_photo(Channel *c, ChannelId channel_id,
@ -13154,7 +13184,7 @@ void ContactsManager::on_channel_status_changed(const Channel *c, ChannelId chan
if (channel_full != nullptr) { // otherwise invite_link will be dropped when the channel is loaded
on_update_channel_full_invite_link(channel_full, nullptr);
do_invalidate_channel_full(channel_full, !c->is_slow_mode_enabled);
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_channel_status_changed");
}
} else {
invalidate_channel_full(channel_id, !c->is_slow_mode_enabled);
@ -13255,7 +13285,7 @@ void ContactsManager::on_update_channel_description(ChannelId channel_id, string
if (channel_full->description != description) {
channel_full->description = std::move(description);
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_description");
td_->group_call_manager_->on_update_dialog_about(DialogId(channel_id), channel_full->description, true);
}
}
@ -13269,7 +13299,7 @@ void ContactsManager::on_update_channel_sticker_set(ChannelId channel_id, Sticke
if (channel_full->sticker_set_id != sticker_set_id) {
channel_full->sticker_set_id = sticker_set_id;
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_sticker_set");
}
}
@ -13278,14 +13308,14 @@ void ContactsManager::on_update_channel_linked_channel_id(ChannelId channel_id,
auto channel_full = get_channel_full_force(channel_id, true, "on_update_channel_linked_channel_id 1");
on_update_channel_full_linked_channel_id(channel_full, channel_id, group_channel_id);
if (channel_full != nullptr) {
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_linked_channel_id 3");
}
}
if (group_channel_id.is_valid()) {
auto channel_full = get_channel_full_force(group_channel_id, true, "on_update_channel_linked_channel_id 2");
on_update_channel_full_linked_channel_id(channel_full, group_channel_id, channel_id);
if (channel_full != nullptr) {
update_channel_full(channel_full, group_channel_id);
update_channel_full(channel_full, group_channel_id, "on_update_channel_linked_channel_id 4");
}
}
}
@ -13294,7 +13324,7 @@ void ContactsManager::on_update_channel_location(ChannelId channel_id, const Dia
auto channel_full = get_channel_full_force(channel_id, true, "on_update_channel_location");
if (channel_full != nullptr) {
on_update_channel_full_location(channel_full, channel_id, location);
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_location");
}
}
@ -13306,7 +13336,7 @@ void ContactsManager::on_update_channel_slow_mode_delay(ChannelId channel_id, in
auto channel_full = get_channel_full_force(channel_id, true, "on_update_channel_slow_mode_delay");
if (channel_full != nullptr) {
on_update_channel_full_slow_mode_delay(channel_full, channel_id, slow_mode_delay, 0);
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_slow_mode_delay");
}
promise.set_value(Unit());
}
@ -13315,7 +13345,7 @@ void ContactsManager::on_update_channel_slow_mode_next_send_date(ChannelId chann
auto channel_full = get_channel_full_force(channel_id, true, "on_update_channel_slow_mode_next_send_date");
if (channel_full != nullptr) {
on_update_channel_full_slow_mode_next_send_date(channel_full, slow_mode_next_send_date);
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_slow_mode_next_send_date");
}
}
@ -13333,7 +13363,7 @@ void ContactsManager::on_update_channel_bot_user_ids(ChannelId channel_id, vecto
return;
}
on_update_channel_full_bot_user_ids(channel_full, channel_id, std::move(bot_user_ids));
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_bot_user_ids");
}
void ContactsManager::on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id,
@ -13357,7 +13387,7 @@ void ContactsManager::on_update_channel_is_all_history_available(ChannelId chann
if (channel_full != nullptr && channel_full->is_all_history_available != is_all_history_available) {
channel_full->is_all_history_available = is_all_history_available;
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_is_all_history_available");
}
promise.set_value(Unit());
}
@ -15240,7 +15270,7 @@ void ContactsManager::on_update_channel_administrator_count(ChannelId channel_id
}
}
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_update_channel_administrator_count");
}
}
@ -15624,7 +15654,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char
if (channel_full != nullptr && channel_full->participant_count != participant_count) {
channel_full->participant_count = participant_count;
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_chat_update");
}
}
@ -15734,7 +15764,7 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co
channel_full->participant_count = 0;
channel_full->administrator_count = 0;
channel_full->is_changed = true;
update_channel_full(channel_full, channel_id);
update_channel_full(channel_full, channel_id, "on_chat_update 2");
}
}
if (need_invalidate_channel_full) {

View File

@ -692,6 +692,7 @@ class ContactsManager final : public Actor {
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
bool is_update_user_full_sent = false;
double expires_at = 0.0;
@ -769,6 +770,7 @@ class ContactsManager final : public Actor {
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
bool is_update_chat_full_sent = false;
template <class StorerT>
void store(StorerT &storer) const;
@ -876,6 +878,7 @@ class ContactsManager final : public Actor {
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
bool is_update_channel_full_sent = false;
double expires_at = 0.0;
@ -1327,9 +1330,10 @@ class ContactsManager final : public Actor {
void update_secret_chat(SecretChat *c, SecretChatId secret_chat_id, bool from_binlog = false,
bool from_database = false);
void update_user_full(UserFull *user_full, UserId user_id, bool from_database = false);
void update_chat_full(ChatFull *chat_full, ChatId chat_id, bool from_database = false);
void update_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool from_database = false);
void update_user_full(UserFull *user_full, UserId user_id, const char *source, bool from_database = false);
void update_chat_full(ChatFull *chat_full, ChatId chat_id, const char *source, bool from_database = false);
void update_channel_full(ChannelFull *channel_full, ChannelId channel_id, const char *source,
bool from_database = false);
bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id);

328
td/telegram/GameManager.cpp Normal file
View File

@ -0,0 +1,328 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/GameManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/Global.h"
#include "td/telegram/InlineQueriesManager.h"
#include "td/telegram/MessageContentType.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/SequenceDispatcher.h"
#include "td/telegram/Td.h"
#include "td/telegram/UpdatesManager.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
namespace td {
class SetGameScoreActor final : public NetActorOnce {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit SetGameScoreActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, bool edit_message,
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force, uint64 sequence_dispatcher_id) {
int32 flags = 0;
if (edit_message) {
flags |= telegram_api::messages_setGameScore::EDIT_MESSAGE_MASK;
}
if (force) {
flags |= telegram_api::messages_setGameScore::FORCE_MASK;
}
dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Can't access the chat"));
stop();
return;
}
CHECK(input_user != nullptr);
auto query = G()->net_query_creator().create(
telegram_api::messages_setGameScore(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
message_id.get_server_message_id().get(), std::move(input_user), score));
LOG(INFO) << "Set game score to " << score;
query->debug("send to MultiSequenceDispatcher");
send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_dispatcher_id);
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_setGameScore>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for SetGameScore: " << to_string(ptr);
td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for SetGameScore: " << status;
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreActor");
promise_.set_error(std::move(status));
}
};
class SetInlineGameScoreQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetInlineGameScoreQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id, bool edit_message,
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force) {
CHECK(input_bot_inline_message_id != nullptr);
CHECK(input_user != nullptr);
int32 flags = 0;
if (edit_message) {
flags |= telegram_api::messages_setInlineGameScore::EDIT_MESSAGE_MASK;
}
if (force) {
flags |= telegram_api::messages_setInlineGameScore::FORCE_MASK;
}
auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_);
send_query(G()->net_query_creator().create(
telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/,
std::move(input_bot_inline_message_id), std::move(input_user), score),
dc_id));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_setInlineGameScore>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
LOG_IF(ERROR, !result_ptr.ok()) << "Receive false in result of setInlineGameScore";
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for SetInlineGameScoreQuery: " << status;
promise_.set_error(std::move(status));
}
};
class GetGameHighScoresQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::gameHighScores>> promise_;
DialogId dialog_id_;
public:
explicit GetGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, tl_object_ptr<telegram_api::InputUser> input_user) {
dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
CHECK(input_peer != nullptr);
CHECK(input_user != nullptr);
send_query(G()->net_query_creator().create(telegram_api::messages_getGameHighScores(
std::move(input_peer), message_id.get_server_message_id().get(), std::move(input_user))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getGameHighScores>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(td->game_manager_->get_game_high_scores_object(result_ptr.move_as_ok()));
}
void on_error(uint64 id, Status status) final {
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetGameHighScoresQuery");
promise_.set_error(std::move(status));
}
};
class GetInlineGameHighScoresQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::gameHighScores>> promise_;
public:
explicit GetInlineGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise)
: promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id,
tl_object_ptr<telegram_api::InputUser> input_user) {
CHECK(input_bot_inline_message_id != nullptr);
CHECK(input_user != nullptr);
auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_);
send_query(G()->net_query_creator().create(
telegram_api::messages_getInlineGameHighScores(std::move(input_bot_inline_message_id), std::move(input_user)),
dc_id));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getInlineGameHighScores>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(td->game_manager_->get_game_high_scores_object(result_ptr.move_as_ok()));
}
void on_error(uint64 id, Status status) final {
promise_.set_error(std::move(status));
}
};
GameManager::GameManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
GameManager::~GameManager() = default;
void GameManager::tear_down() {
parent_.reset();
}
void GameManager::set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score,
bool force, Promise<td_api::object_ptr<td_api::message>> &&promise) {
CHECK(td_->auth_manager_->is_bot());
if (!td_->messages_manager_->have_message_force(full_message_id, "set_game_score")) {
return promise.set_error(Status::Error(5, "Message not found"));
}
auto dialog_id = full_message_id.get_dialog_id();
if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Edit)) {
return promise.set_error(Status::Error(5, "Can't access the chat"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Invalid user identifier specified"));
}
if (!td_->messages_manager_->can_set_game_score(full_message_id)) {
return promise.set_error(Status::Error(5, "Game score can't be set"));
}
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), full_message_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
send_closure(actor_id, &GameManager::on_set_game_score, full_message_id, std::move(promise));
});
send_closure(td_->create_net_actor<SetGameScoreActor>(std::move(query_promise)), &SetGameScoreActor::send, dialog_id,
full_message_id.get_message_id(), edit_message, std::move(input_user), score, force,
MessagesManager::get_sequence_dispatcher_id(dialog_id, MessageContentType::None));
}
void GameManager::on_set_game_score(FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::message>> &&promise) {
promise.set_value(td_->messages_manager_->get_message_object(full_message_id));
}
void GameManager::set_inline_game_score(const string &inline_message_id, bool edit_message, UserId user_id, int32 score,
bool force, Promise<Unit> &&promise) {
CHECK(td_->auth_manager_->is_bot());
auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
if (input_bot_inline_message_id == nullptr) {
return promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
}
td_->create_handler<SetInlineGameScoreQuery>(std::move(promise))
->send(std::move(input_bot_inline_message_id), edit_message, std::move(input_user), score, force);
}
void GameManager::get_game_high_scores(FullMessageId full_message_id, UserId user_id,
Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise) {
CHECK(td_->auth_manager_->is_bot());
if (!td_->messages_manager_->have_message_force(full_message_id, "get_game_high_scores")) {
return promise.set_error(Status::Error(5, "Message not found"));
}
auto dialog_id = full_message_id.get_dialog_id();
if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
return promise.set_error(Status::Error(5, "Can't access the chat"));
}
auto message_id = full_message_id.get_message_id();
if (message_id.is_scheduled() || !message_id.is_server()) {
return promise.set_error(Status::Error(5, "Wrong message identifier specified"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
}
td_->create_handler<GetGameHighScoresQuery>(std::move(promise))->send(dialog_id, message_id, std::move(input_user));
}
void GameManager::get_inline_game_high_scores(const string &inline_message_id, UserId user_id,
Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise) {
CHECK(td_->auth_manager_->is_bot());
auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
if (input_bot_inline_message_id == nullptr) {
return promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
}
td_->create_handler<GetInlineGameHighScoresQuery>(std::move(promise))
->send(std::move(input_bot_inline_message_id), std::move(input_user));
}
td_api::object_ptr<td_api::gameHighScores> GameManager::get_game_high_scores_object(
telegram_api::object_ptr<telegram_api::messages_highScores> &&high_scores) {
td_->contacts_manager_->on_get_users(std::move(high_scores->users_), "get_game_high_scores_object");
auto result = td_api::make_object<td_api::gameHighScores>();
for (const auto &high_score : high_scores->scores_) {
int32 position = high_score->pos_;
UserId user_id(high_score->user_id_);
int32 score = high_score->score_;
if (position <= 0 || !user_id.is_valid() || score < 0) {
LOG(ERROR) << "Receive wrong " << to_string(high_score);
continue;
}
result->scores_.push_back(make_tl_object<td_api::gameHighScore>(
position, td_->contacts_manager_->get_user_id_object(user_id, "get_game_high_scores_object"), score));
}
return result;
}
} // namespace td

56
td/telegram/GameManager.h Normal file
View File

@ -0,0 +1,56 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/FullMessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
namespace td {
class Td;
class GameManager final : public Actor {
public:
GameManager(Td *td, ActorShared<> parent);
GameManager(const GameManager &) = delete;
GameManager &operator=(const GameManager &) = delete;
GameManager(GameManager &&) = delete;
GameManager &operator=(GameManager &&) = delete;
~GameManager() final;
void set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score, bool force,
Promise<td_api::object_ptr<td_api::message>> &&promise);
void set_inline_game_score(const string &inline_message_id, bool edit_message, UserId user_id, int32 score,
bool force, Promise<Unit> &&promise);
void get_game_high_scores(FullMessageId full_message_id, UserId user_id,
Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise);
void get_inline_game_high_scores(const string &inline_message_id, UserId user_id,
Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise);
td_api::object_ptr<td_api::gameHighScores> get_game_high_scores_object(
telegram_api::object_ptr<telegram_api::messages_highScores> &&high_scores);
private:
void tear_down() final;
void on_set_game_score(FullMessageId full_message_id, Promise<td_api::object_ptr<td_api::message>> &&promise);
Td *td_;
ActorShared<> parent_;
};
} // namespace td

View File

@ -38,6 +38,7 @@ class ConnectionCreator;
class ContactsManager;
class FileManager;
class FileReferenceManager;
class GameManager;
class GroupCallManager;
class LanguagePackManager;
class LinkManager;
@ -47,6 +48,7 @@ class NetQueryDispatcher;
class NotificationManager;
class PasswordManager;
class SecretChatsManager;
class SponsoredMessageManager;
class StateManager;
class StickersManager;
class StorageManager;
@ -219,6 +221,13 @@ class Global final : public ActorContext {
file_reference_manager_ = std::move(file_reference_manager);
}
ActorId<GameManager> game_manager() const {
return game_manager_;
}
void set_game_manager(ActorId<GameManager> game_manager) {
game_manager_ = game_manager;
}
ActorId<GroupCallManager> group_call_manager() const {
return group_call_manager_;
}
@ -268,6 +277,13 @@ class Global final : public ActorContext {
secret_chats_manager_ = secret_chats_manager;
}
ActorId<SponsoredMessageManager> sponsored_message_manager() const {
return sponsored_message_manager_;
}
void set_sponsored_message_manager(ActorId<SponsoredMessageManager> sponsored_message_manager) {
sponsored_message_manager_ = sponsored_message_manager;
}
ActorId<StickersManager> stickers_manager() const {
return stickers_manager_;
}
@ -417,6 +433,7 @@ class Global final : public ActorContext {
ActorId<ContactsManager> contacts_manager_;
ActorId<FileManager> file_manager_;
ActorId<FileReferenceManager> file_reference_manager_;
ActorId<GameManager> game_manager_;
ActorId<GroupCallManager> group_call_manager_;
ActorId<LanguagePackManager> language_pack_manager_;
ActorId<LinkManager> link_manager_;
@ -424,6 +441,7 @@ class Global final : public ActorContext {
ActorId<NotificationManager> notification_manager_;
ActorId<PasswordManager> password_manager_;
ActorId<SecretChatsManager> secret_chats_manager_;
ActorId<SponsoredMessageManager> sponsored_message_manager_;
ActorId<StickersManager> stickers_manager_;
ActorId<StorageManager> storage_manager_;
ActorId<MemoryManager> memory_manager_;

View File

@ -1074,7 +1074,7 @@ tl_object_ptr<td_api::venue> copy(const td_api::venue &obj) {
template <>
tl_object_ptr<td_api::formattedText> copy(const td_api::formattedText &obj) {
// there is no entities in the game text
// there are no entities in the game text
return make_tl_object<td_api::formattedText>(obj.text_, vector<tl_object_ptr<td_api::textEntity>>());
}

View File

@ -357,14 +357,17 @@ class LinkManager::InternalLinkUnknownDeepLink final : public InternalLink {
class LinkManager::InternalLinkVoiceChat final : public InternalLink {
string dialog_username_;
string invite_hash_;
bool is_live_stream_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeVoiceChat>(dialog_username_, invite_hash_);
return td_api::make_object<td_api::internalLinkTypeVoiceChat>(dialog_username_, invite_hash_, is_live_stream_);
}
public:
InternalLinkVoiceChat(string dialog_username, string invite_hash)
: dialog_username_(std::move(dialog_username)), invite_hash_(std::move(invite_hash)) {
InternalLinkVoiceChat(string dialog_username, string invite_hash, bool is_live_stream)
: dialog_username_(std::move(dialog_username))
, invite_hash_(std::move(invite_hash))
, is_live_stream_(is_live_stream) {
}
};
@ -768,13 +771,13 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
}
auto username = get_arg("domain");
for (auto &arg : url_query.args_) {
if (arg.first == "voicechat") {
// resolve?domain=<username>&voicechat
// resolve?domain=<username>&voicechat=<invite_hash>
if (arg.first == "voicechat" || arg.first == "videochat" || arg.first == "livestream") {
// resolve?domain=<username>&videochat
// resolve?domain=<username>&videochat=<invite_hash>
if (Scheduler::context() != nullptr) {
send_closure(G()->messages_manager(), &MessagesManager::reload_voice_chat_on_search, username);
}
return td::make_unique<InternalLinkVoiceChat>(std::move(username), arg.second);
return td::make_unique<InternalLinkVoiceChat>(std::move(username), arg.second, arg.first == "livestream");
}
if (arg.first == "start" && is_valid_start_parameter(arg.second)) {
// resolve?domain=<bot_username>?start=<parameter>
@ -1017,13 +1020,13 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
}
auto username = path[0];
for (auto &arg : url_query.args_) {
if (arg.first == "voicechat") {
// /<username>?voicechat
// /<username>?voicechat=<invite_hash>
if (arg.first == "voicechat" || arg.first == "videochat" || arg.first == "livestream") {
// /<username>?videochat
// /<username>?videochat=<invite_hash>
if (Scheduler::context() != nullptr) {
send_closure(G()->messages_manager(), &MessagesManager::reload_voice_chat_on_search, username);
}
return td::make_unique<InternalLinkVoiceChat>(std::move(username), arg.second);
return td::make_unique<InternalLinkVoiceChat>(std::move(username), arg.second, arg.first == "livestream");
}
if (arg.first == "start" && is_valid_start_parameter(arg.second)) {
// /<bot_username>?start=<parameter>

View File

@ -4364,7 +4364,12 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
return std::move(result);
}
case MessageContentType::Poll:
return make_unique<MessagePoll>(*static_cast<const MessagePoll *>(content));
if (type == MessageContentDupType::Copy) {
return make_unique<MessagePoll>(
td->poll_manager_->dup_poll(static_cast<const MessagePoll *>(content)->poll_id));
} else {
return make_unique<MessagePoll>(*static_cast<const MessagePoll *>(content));
}
case MessageContentType::Sticker: {
auto result = make_unique<MessageSticker>(*static_cast<const MessageSticker *>(content));
if (td->stickers_manager_->has_input_media(result->file_id, to_secret)) {

View File

@ -3542,188 +3542,6 @@ class EditInlineMessageQuery final : public Td::ResultHandler {
}
};
class SetGameScoreActor final : public NetActorOnce {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit SetGameScoreActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, bool edit_message,
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force, uint64 sequence_dispatcher_id) {
int32 flags = 0;
if (edit_message) {
flags |= telegram_api::messages_setGameScore::EDIT_MESSAGE_MASK;
}
if (force) {
flags |= telegram_api::messages_setGameScore::FORCE_MASK;
}
dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
if (input_peer == nullptr) {
on_error(0, Status::Error(400, "Can't access the chat"));
stop();
return;
}
CHECK(input_user != nullptr);
auto query = G()->net_query_creator().create(
telegram_api::messages_setGameScore(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
message_id.get_server_message_id().get(), std::move(input_user), score));
LOG(INFO) << "Set game score to " << score;
query->debug("send to MessagesManager::MultiSequenceDispatcher");
send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_dispatcher_id);
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_setGameScore>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for SetGameScore: " << to_string(ptr);
td->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for SetGameScore: " << status;
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreActor");
promise_.set_error(std::move(status));
}
};
class SetInlineGameScoreQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetInlineGameScoreQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id, bool edit_message,
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force) {
CHECK(input_bot_inline_message_id != nullptr);
CHECK(input_user != nullptr);
int32 flags = 0;
if (edit_message) {
flags |= telegram_api::messages_setInlineGameScore::EDIT_MESSAGE_MASK;
}
if (force) {
flags |= telegram_api::messages_setInlineGameScore::FORCE_MASK;
}
LOG(INFO) << "Set inline game score to " << score;
auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_);
send_query(G()->net_query_creator().create(
telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/,
std::move(input_bot_inline_message_id), std::move(input_user), score),
dc_id));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_setInlineGameScore>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
LOG_IF(ERROR, !result_ptr.ok()) << "Receive false in result of setInlineGameScore";
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for SetInlineGameScoreQuery: " << status;
promise_.set_error(std::move(status));
}
};
class GetGameHighScoresQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
int64 random_id_;
public:
explicit GetGameHighScoresQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, tl_object_ptr<telegram_api::InputUser> input_user,
int64 random_id) {
dialog_id_ = dialog_id;
random_id_ = random_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
return;
}
CHECK(input_user != nullptr);
send_query(G()->net_query_creator().create(telegram_api::messages_getGameHighScores(
std::move(input_peer), message_id.get_server_message_id().get(), std::move(input_user))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getGameHighScores>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
td->messages_manager_->on_get_game_high_scores(random_id_, result_ptr.move_as_ok());
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for GetGameHighScoresQuery: " << status;
td->messages_manager_->on_get_game_high_scores(random_id_, nullptr);
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetGameHighScoresQuery");
promise_.set_error(std::move(status));
}
};
class GetInlineGameHighScoresQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
int64 random_id_;
public:
explicit GetInlineGameHighScoresQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputBotInlineMessageID> input_bot_inline_message_id,
tl_object_ptr<telegram_api::InputUser> input_user, int64 random_id) {
CHECK(input_bot_inline_message_id != nullptr);
CHECK(input_user != nullptr);
random_id_ = random_id;
auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_);
send_query(G()->net_query_creator().create(
telegram_api::messages_getInlineGameHighScores(std::move(input_bot_inline_message_id), std::move(input_user)),
dc_id));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getInlineGameHighScores>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
td->messages_manager_->on_get_game_high_scores(random_id_, result_ptr.move_as_ok());
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
LOG(INFO) << "Receive error for GetInlineGameHighScoresQuery: " << status;
td->messages_manager_->on_get_game_high_scores(random_id_, nullptr);
promise_.set_error(std::move(status));
}
};
class ForwardMessagesActor final : public NetActorOnce {
Promise<Unit> promise_;
vector<int64> random_ids_;
@ -8808,7 +8626,7 @@ void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string
return promise.set_error(Status::Error(3, "Can't access the chat"));
}
if (dialog_id.get_type() == DialogType::SecretChat) {
return promise.set_error(Status::Error(500, "There is no statistics for secret chats"));
return promise.set_error(Status::Error(500, "There are no statistics for secret chats"));
}
td_->create_handler<GetStatsUrlQuery>(std::move(promise))->send(dialog_id, parameters, is_dark);
@ -11646,7 +11464,7 @@ void MessagesManager::repair_channel_server_unread_count(Dialog *d) {
return;
}
if (!need_unread_counter(d->order)) {
// there is no unread count in left channels
// there are no unread counters in left channels
return;
}
if (!d->need_repair_channel_server_unread_count) {
@ -19836,7 +19654,7 @@ Status MessagesManager::view_messages(DialogId dialog_id, MessageId top_thread_m
return Status::Error(400, "Invalid message thread ID specified");
}
if (dialog_id.get_type() != DialogType::Channel || is_broadcast_channel(dialog_id)) {
return Status::Error(400, "There is no message threads in the chat");
return Status::Error(400, "There are no message threads in the chat");
}
}
@ -26193,10 +26011,17 @@ int32 MessagesManager::get_message_flags(const Message *m) {
return flags;
}
bool MessagesManager::can_set_game_score(FullMessageId full_message_id) const {
return can_set_game_score(full_message_id.get_dialog_id(), get_message(full_message_id));
}
bool MessagesManager::can_set_game_score(DialogId dialog_id, const Message *m) const {
if (m == nullptr) {
return false;
}
if (m->content->get_type() != MessageContentType::Game) {
return false;
}
if (m->message_id.is_scheduled()) {
return false;
}
@ -26255,181 +26080,7 @@ bool MessagesManager::can_set_game_score(DialogId dialog_id, const Message *m) c
return false;
}
return m->content->get_type() == MessageContentType::Game;
}
void MessagesManager::set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score,
bool force, Promise<Unit> &&promise) {
if (!td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(3, "Method is available only for bots"));
}
LOG(INFO) << "Begin to set game score of " << user_id << " in " << full_message_id;
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog_force(dialog_id, "set_game_score");
if (d == nullptr) {
return promise.set_error(Status::Error(5, "Chat not found"));
}
if (!have_input_peer(dialog_id, AccessRights::Edit)) {
return promise.set_error(Status::Error(5, "Can't access the chat"));
}
const Message *m = get_message_force(d, full_message_id.get_message_id(), "set_game_score");
if (m == nullptr) {
return promise.set_error(Status::Error(5, "Message not found"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Invalid user identifier specified"));
}
if (!can_set_game_score(dialog_id, m)) {
return promise.set_error(Status::Error(5, "Game score can't be set"));
}
send_closure(td_->create_net_actor<SetGameScoreActor>(std::move(promise)), &SetGameScoreActor::send, dialog_id,
m->message_id, edit_message, std::move(input_user), score, force,
get_sequence_dispatcher_id(dialog_id, MessageContentType::None));
}
void MessagesManager::set_inline_game_score(const string &inline_message_id, bool edit_message, UserId user_id,
int32 score, bool force, Promise<Unit> &&promise) {
if (!td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(3, "Method is available only for bots"));
}
auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
if (input_bot_inline_message_id == nullptr) {
return promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
}
td_->create_handler<SetInlineGameScoreQuery>(std::move(promise))
->send(std::move(input_bot_inline_message_id), edit_message, std::move(input_user), score, force);
}
int64 MessagesManager::get_game_high_scores(FullMessageId full_message_id, UserId user_id, Promise<Unit> &&promise) {
if (!td_->auth_manager_->is_bot()) {
promise.set_error(Status::Error(3, "Method is available only for bots"));
return 0;
}
LOG(INFO) << "Begin to get game high scores of " << user_id << " in " << full_message_id;
auto dialog_id = full_message_id.get_dialog_id();
Dialog *d = get_dialog_force(dialog_id, "get_game_high_scores");
if (d == nullptr) {
promise.set_error(Status::Error(5, "Chat not found"));
return 0;
}
if (!have_input_peer(dialog_id, AccessRights::Read)) {
promise.set_error(Status::Error(5, "Can't access the chat"));
return 0;
}
const Message *m = get_message_force(d, full_message_id.get_message_id(), "get_game_high_scores");
if (m == nullptr) {
promise.set_error(Status::Error(5, "Message not found"));
return 0;
}
if (m->message_id.is_scheduled() || !m->message_id.is_server()) {
promise.set_error(Status::Error(5, "Wrong message identifier specified"));
return 0;
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
promise.set_error(Status::Error(400, "Wrong user identifier specified"));
return 0;
}
int64 random_id = 0;
do {
random_id = Random::secure_int64();
} while (random_id == 0 || game_high_scores_.find(random_id) != game_high_scores_.end());
game_high_scores_[random_id]; // reserve place for result
td_->create_handler<GetGameHighScoresQuery>(std::move(promise))
->send(dialog_id, m->message_id, std::move(input_user), random_id);
return random_id;
}
int64 MessagesManager::get_inline_game_high_scores(const string &inline_message_id, UserId user_id,
Promise<Unit> &&promise) {
if (!td_->auth_manager_->is_bot()) {
promise.set_error(Status::Error(3, "Method is available only for bots"));
return 0;
}
auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
if (input_bot_inline_message_id == nullptr) {
promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
return 0;
}
auto input_user = td_->contacts_manager_->get_input_user(user_id);
if (input_user == nullptr) {
promise.set_error(Status::Error(400, "Wrong user identifier specified"));
return 0;
}
int64 random_id = 0;
do {
random_id = Random::secure_int64();
} while (random_id == 0 || game_high_scores_.find(random_id) != game_high_scores_.end());
game_high_scores_[random_id]; // reserve place for result
td_->create_handler<GetInlineGameHighScoresQuery>(std::move(promise))
->send(std::move(input_bot_inline_message_id), std::move(input_user), random_id);
return random_id;
}
void MessagesManager::on_get_game_high_scores(int64 random_id,
tl_object_ptr<telegram_api::messages_highScores> &&high_scores) {
auto it = game_high_scores_.find(random_id);
CHECK(it != game_high_scores_.end());
auto &result = it->second;
CHECK(result == nullptr);
if (high_scores == nullptr) {
game_high_scores_.erase(it);
return;
}
td_->contacts_manager_->on_get_users(std::move(high_scores->users_), "on_get_game_high_scores");
result = make_tl_object<td_api::gameHighScores>();
for (auto &high_score : high_scores->scores_) {
int32 position = high_score->pos_;
if (position <= 0) {
LOG(ERROR) << "Receive wrong position = " << position;
continue;
}
UserId user_id(high_score->user_id_);
LOG_IF(WARNING, !td_->contacts_manager_->have_user(user_id)) << "Have no info about " << user_id;
int32 score = high_score->score_;
if (score < 0) {
LOG(ERROR) << "Receive wrong score = " << score;
continue;
}
result->scores_.push_back(make_tl_object<td_api::gameHighScore>(
position, td_->contacts_manager_->get_user_id_object(user_id, "gameHighScore"), score));
}
}
tl_object_ptr<td_api::gameHighScores> MessagesManager::get_game_high_scores_object(int64 random_id) {
auto it = game_high_scores_.find(random_id);
CHECK(it != game_high_scores_.end());
auto result = std::move(it->second);
game_high_scores_.erase(it);
return result;
return true;
}
bool MessagesManager::is_forward_info_sender_hidden(const MessageForwardInfo *forward_info) {

View File

@ -467,20 +467,6 @@ class MessagesManager final : public Actor {
td_api::object_ptr<td_api::MessageSchedulingState> &&scheduling_state,
Promise<Unit> &&promise);
void set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score, bool force,
Promise<Unit> &&promise);
void set_inline_game_score(const string &inline_message_id, bool edit_message, UserId user_id, int32 score,
bool force, Promise<Unit> &&promise);
int64 get_game_high_scores(FullMessageId full_message_id, UserId user_id, Promise<Unit> &&promise);
int64 get_inline_game_high_scores(const string &inline_message_id, UserId user_id, Promise<Unit> &&promise);
void on_get_game_high_scores(int64 random_id, tl_object_ptr<telegram_api::messages_highScores> &&high_scores);
tl_object_ptr<td_api::gameHighScores> get_game_high_scores_object(int64 random_id);
void send_dialog_action(DialogId dialog_id, MessageId top_thread_message_id, DialogAction action,
Promise<Unit> &&promise);
@ -917,10 +903,14 @@ class MessagesManager final : public Actor {
Result<ServerMessageId> get_payment_successful_message_id(FullMessageId full_message_id);
bool can_set_game_score(FullMessageId full_message_id) const;
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
ActorOwn<MultiSequenceDispatcher> sequence_dispatcher_;
static uint64 get_sequence_dispatcher_id(DialogId dialog_id, MessageContentType message_content_type);
private:
class PendingPtsUpdate {
public:
@ -1149,10 +1139,10 @@ class MessagesManager final : public Actor {
MessageId last_new_message_id; // identifier of the last known server message received from update, there should be
// no server messages after it
MessageId last_message_id; // identifier of the message after which currently there is no any message, i.e. a
MessageId last_message_id; // identifier of the message after which currently there are no messages, i.e. a
// message without a gap after it, memory only
MessageId first_database_message_id; // identifier of the first message in the database, needed
// until there is no gaps in the database
// until there are no gaps in the database
MessageId last_database_message_id; // identifier of the last local or server message, if last_database_message_id
// is known and last_message_id is known, then last_database_message_id <=
// last_message_id
@ -2985,8 +2975,6 @@ class MessagesManager final : public Actor {
void set_sponsored_dialog(DialogId dialog_id, DialogSource source);
static uint64 get_sequence_dispatcher_id(DialogId dialog_id, MessageContentType message_content_type);
Dialog *get_service_notifications_dialog();
void save_auth_notification_ids();
@ -3321,8 +3309,6 @@ class MessagesManager final : public Actor {
std::unordered_map<int64, tl_object_ptr<td_api::chatEvents>> chat_events_; // random_id -> chat events
std::unordered_map<int64, tl_object_ptr<td_api::gameHighScores>> game_high_scores_; // random_id -> high scores
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_notification_settings_queries_;
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_queries_;

View File

@ -1063,7 +1063,7 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
// all edits of notifications from edited_notification_ids
// deletions of a notification can be removed, only if the addition of the notification has already been deleted
// deletions of all unkept notifications can be moved to the first updateNotificationGroup
// after that at every moment there is no more active notifications than in the last moment,
// after that at every moment there are no more active notifications than in the last moment,
// so left deletions after add/edit can be safely removed and following additions can be treated as edits
// we still need to keep deletions coming first, because we can't have 2 consequent additions
// from all additions of the same notification, we need to preserve the first, because it can be with sound,
@ -2809,6 +2809,9 @@ string NotificationManager::convert_loc_key(const string &loc_key) {
if (loc_key == "MESSAGE_NOTEXT") {
return "MESSAGE";
}
if (loc_key == "MESSAGE_NOTHEME") {
return "MESSAGE_CHAT_CHANGE_THEME";
}
if (loc_key == "PINNED_INVOICE") {
return "PINNED_MESSAGE_INVOICE";
}

View File

@ -593,6 +593,10 @@ td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id, co
open_period = close_date - now;
}
}
if (poll->is_closed) {
open_period = 0;
close_date = 0;
}
return td_api::make_object<td_api::poll>(
poll_id.get(), poll->question, std::move(poll_options), total_voter_count,
td_->contacts_manager_->get_user_ids_object(poll->recent_voter_user_ids, "get_poll_object"), poll->is_anonymous,
@ -1283,6 +1287,18 @@ void PollManager::on_online() {
}
}
PollId PollManager::dup_poll(PollId poll_id) {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
auto question = poll->question;
auto options = transform(poll->options, [](auto &option) { return option.text; });
auto explanation = poll->explanation;
return create_poll(std::move(question), std::move(options), poll->is_anonymous, poll->allow_multiple_answers,
poll->is_quiz, poll->correct_option_id, std::move(explanation), poll->open_period,
poll->open_period == 0 ? 0 : G()->unix_time(), false);
}
bool PollManager::has_input_media(PollId poll_id) const {
auto poll = get_poll(poll_id);
if (!(poll != nullptr)) return false;
@ -1383,6 +1399,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
auto poll = get_poll_force(poll_id);
bool is_changed = false;
bool need_save_to_database = false;
if (poll == nullptr) {
if (poll_server == nullptr) {
LOG(INFO) << "Ignore " << poll_id << ", because have no data about it";
@ -1465,13 +1482,16 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
}
if (open_period != poll->open_period) {
poll->open_period = open_period;
is_changed = true;
if (!poll->is_closed) {
is_changed = true;
} else {
need_save_to_database = true;
}
}
if (close_date != poll->close_date) {
poll->close_date = close_date;
is_changed = true;
if (!poll->is_closed) {
is_changed = true;
if (close_date != 0) {
if (close_date <= G()->server_time()) {
poll->is_closed = true;
@ -1481,6 +1501,8 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
} else {
close_poll_timeout_.cancel_timeout(poll_id.get());
}
} else {
need_save_to_database = true;
}
}
bool is_anonymous = (poll_server->flags_ & telegram_api::poll::PUBLIC_VOTERS_MASK) == 0;
@ -1645,6 +1667,8 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
}
if (is_changed) {
notify_on_poll_update(poll_id);
}
if (is_changed || need_save_to_database) {
save_poll(poll, poll_id);
}
if (need_update_poll && (is_changed || (poll->is_closed && being_closed_polls_.erase(poll_id) != 0))) {

View File

@ -74,6 +74,8 @@ class PollManager final : public Actor {
void stop_local_poll(PollId poll_id);
PollId dup_poll(PollId poll_id);
bool has_input_media(PollId poll_id) const;
tl_object_ptr<telegram_api::InputMedia> get_input_media(PollId poll_id) const;

View File

@ -0,0 +1,256 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/SponsoredMessageManager.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
namespace td {
class GetSponsoredMessagesQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> promise_;
ChannelId channel_id_;
public:
explicit GetSponsoredMessagesQuery(
Promise<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&promise)
: promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td->contacts_manager_->get_input_channel(channel_id);
if (input_channel == nullptr) {
return promise_.set_error(Status::Error(3, "Chat info not found"));
}
send_query(G()->net_query_creator().create(telegram_api::channels_getSponsoredMessages(std::move(input_channel))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_getSponsoredMessages>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(uint64 id, Status status) final {
td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetSponsoredMessagesQuery");
promise_.set_error(std::move(status));
}
};
class ViewSponsoredMessageQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit ViewSponsoredMessageQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, const string &message_id) {
channel_id_ = channel_id;
auto input_channel = td->contacts_manager_->get_input_channel(channel_id);
if (input_channel == nullptr) {
return promise_.set_error(Status::Error(3, "Chat info not found"));
}
send_query(G()->net_query_creator().create(
telegram_api::channels_viewSponsoredMessage(std::move(input_channel), BufferSlice(message_id))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_viewSponsoredMessage>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
td->contacts_manager_->on_get_channel_error(channel_id_, status, "ViewSponsoredMessageQuery");
promise_.set_error(std::move(status));
}
};
struct SponsoredMessageManager::SponsoredMessage {
string random_id;
DialogId sponsor_dialog_id;
string start_param;
unique_ptr<MessageContent> content;
SponsoredMessage() = default;
SponsoredMessage(string random_id, DialogId sponsor_dialog_id, string start_param, unique_ptr<MessageContent> content)
: random_id(std::move(random_id))
, sponsor_dialog_id(sponsor_dialog_id)
, start_param(std::move(start_param))
, content(std::move(content)) {
}
};
struct SponsoredMessageManager::DialogSponsoredMessages {
vector<Promise<td_api::object_ptr<td_api::sponsoredMessages>>> promises;
vector<SponsoredMessage> messages;
};
SponsoredMessageManager::SponsoredMessageManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
delete_cached_sponsored_messages_timeout_.set_callback(on_delete_cached_sponsored_messages_timeout_callback);
delete_cached_sponsored_messages_timeout_.set_callback_data(static_cast<void *>(this));
}
SponsoredMessageManager::~SponsoredMessageManager() = default;
void SponsoredMessageManager::tear_down() {
parent_.reset();
}
void SponsoredMessageManager::on_delete_cached_sponsored_messages_timeout_callback(void *sponsored_message_manager_ptr,
int64 dialog_id_int) {
if (G()->close_flag()) {
return;
}
auto sponsored_message_manager = static_cast<SponsoredMessageManager *>(sponsored_message_manager_ptr);
send_closure_later(sponsored_message_manager->actor_id(sponsored_message_manager),
&SponsoredMessageManager::delete_cached_sponsored_messages, DialogId(dialog_id_int));
}
void SponsoredMessageManager::delete_cached_sponsored_messages(DialogId dialog_id) {
if (G()->close_flag()) {
return;
}
auto it = dialog_sponsored_messages_.find(dialog_id);
CHECK(it != dialog_sponsored_messages_.end());
CHECK(it->second->promises.empty());
dialog_sponsored_messages_.erase(it);
}
td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponsored_message_object(
DialogId dialog_id, const SponsoredMessage &sponsored_message) const {
return td_api::make_object<td_api::sponsoredMessage>(
sponsored_message.random_id, sponsored_message.sponsor_dialog_id.get(), sponsored_message.start_param,
get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
}
td_api::object_ptr<td_api::sponsoredMessages> SponsoredMessageManager::get_sponsored_messages_object(
DialogId dialog_id, const DialogSponsoredMessages &sponsored_messages) const {
return td_api::make_object<td_api::sponsoredMessages>(
transform(sponsored_messages.messages, [this, dialog_id](const SponsoredMessage &sponsored_message) {
return get_sponsored_message_object(dialog_id, sponsored_message);
}));
}
void SponsoredMessageManager::get_dialog_sponsored_messages(
DialogId dialog_id, Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise) {
if (!td_->messages_manager_->have_dialog_force(dialog_id, "get_sponsored_messages")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (dialog_id.get_type() != DialogType::Channel ||
td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) != ContactsManager::ChannelType::Broadcast) {
return promise.set_value(td_api::make_object<td_api::sponsoredMessages>());
}
auto &messages = dialog_sponsored_messages_[dialog_id];
if (messages != nullptr && messages->promises.empty()) {
return promise.set_value(get_sponsored_messages_object(dialog_id, *messages));
}
if (messages == nullptr) {
messages = make_unique<DialogSponsoredMessages>();
}
messages->promises.push_back(std::move(promise));
if (messages->promises.size() == 1) {
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this),
dialog_id](Result<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&result) mutable {
send_closure(actor_id, &SponsoredMessageManager::on_get_dialog_sponsored_messages, dialog_id,
std::move(result));
});
td_->create_handler<GetSponsoredMessagesQuery>(std::move(query_promise))->send(dialog_id.get_channel_id());
}
}
void SponsoredMessageManager::on_get_dialog_sponsored_messages(
DialogId dialog_id, Result<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&result) {
auto &messages = dialog_sponsored_messages_[dialog_id];
CHECK(messages != nullptr);
auto promises = std::move(messages->promises);
reset_to_empty(messages->promises);
if (result.is_ok() && G()->close_flag()) {
result = Status::Error(500, "Request aborted");
}
if (result.is_error()) {
dialog_sponsored_messages_.erase(dialog_id);
for (auto &promise : promises) {
promise.set_error(result.error().clone());
}
return;
}
auto sponsored_messages = result.move_as_ok();
td_->contacts_manager_->on_get_users(std::move(sponsored_messages->users_), "on_get_dialog_sponsored_messages");
td_->contacts_manager_->on_get_chats(std::move(sponsored_messages->chats_), "on_get_dialog_sponsored_messages");
reset_to_empty(messages->messages);
for (auto &sponsored_message : sponsored_messages->messages_) {
DialogId sponsor_dialog_id(sponsored_message->from_id_);
if (!sponsor_dialog_id.is_valid() || !td_->messages_manager_->have_dialog_info_force(sponsor_dialog_id)) {
LOG(ERROR) << "Receive unknown sponsor " << sponsor_dialog_id;
continue;
}
td_->messages_manager_->force_create_dialog(sponsor_dialog_id, "on_get_dialog_sponsored_messages");
auto message_text = get_message_text(td_->contacts_manager_.get(), std::move(sponsored_message->message_),
std::move(sponsored_message->entities_), true, true, 0, false,
"on_get_dialog_sponsored_messages");
int32 ttl = 0;
auto content = get_message_content(td_, std::move(message_text), nullptr, sponsor_dialog_id, true, UserId(), &ttl);
if (ttl != 0) {
LOG(ERROR) << "Receive sponsored message with TTL " << ttl;
continue;
}
messages->messages.emplace_back(sponsored_message->random_id_.as_slice().str(), sponsor_dialog_id,
std::move(sponsored_message->start_param_), std::move(content));
}
for (auto &promise : promises) {
promise.set_value(get_sponsored_messages_object(dialog_id, *messages));
}
delete_cached_sponsored_messages_timeout_.set_timeout_in(dialog_id.get(), 300.0);
}
void SponsoredMessageManager::view_sponsored_message(DialogId dialog_id, const string &message_id,
Promise<Unit> &&promise) {
if (!td_->messages_manager_->have_dialog_force(dialog_id, "view_sponsored_message")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (dialog_id.get_type() != DialogType::Channel ||
td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) != ContactsManager::ChannelType::Broadcast ||
message_id.empty()) {
return promise.set_error(Status::Error(400, "Message not found"));
}
td_->create_handler<ViewSponsoredMessageQuery>(std::move(promise))->send(dialog_id.get_channel_id(), message_id);
}
} // namespace td

View File

@ -0,0 +1,68 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
#include <unordered_map>
namespace td {
class Td;
class SponsoredMessageManager final : public Actor {
public:
SponsoredMessageManager(Td *td, ActorShared<> parent);
SponsoredMessageManager(const SponsoredMessageManager &) = delete;
SponsoredMessageManager &operator=(const SponsoredMessageManager &) = delete;
SponsoredMessageManager(SponsoredMessageManager &&) = delete;
SponsoredMessageManager &operator=(SponsoredMessageManager &&) = delete;
~SponsoredMessageManager() final;
void get_dialog_sponsored_messages(DialogId dialog_id,
Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise);
void view_sponsored_message(DialogId dialog_id, const string &message_id, Promise<Unit> &&promise);
private:
struct SponsoredMessage;
struct DialogSponsoredMessages;
void tear_down() final;
static void on_delete_cached_sponsored_messages_timeout_callback(void *sponsored_message_manager_ptr,
int64 dialog_id_int);
void delete_cached_sponsored_messages(DialogId dialog_id);
td_api::object_ptr<td_api::sponsoredMessage> get_sponsored_message_object(
DialogId dialog_id, const SponsoredMessage &sponsored_message) const;
td_api::object_ptr<td_api::sponsoredMessages> get_sponsored_messages_object(
DialogId dialog_id, const DialogSponsoredMessages &sponsored_messages) const;
void on_get_dialog_sponsored_messages(
DialogId dialog_id, Result<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&result);
std::unordered_map<DialogId, unique_ptr<DialogSponsoredMessages>, DialogIdHash> dialog_sponsored_messages_;
MultiTimeout delete_cached_sponsored_messages_timeout_{"DeleteCachedSponsoredMessagesTimeout"};
Td *td_;
ActorShared<> parent_;
};
} // namespace td

View File

@ -1,145 +0,0 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/SponsoredMessages.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
namespace td {
class GetSponsoredMessagesQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::sponsoredMessages>> promise_;
ChannelId channel_id_;
public:
explicit GetSponsoredMessagesQuery(Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise)
: promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td->contacts_manager_->get_input_channel(channel_id);
if (input_channel == nullptr) {
return promise_.set_error(Status::Error(3, "Chat info not found"));
}
send_query(G()->net_query_creator().create(telegram_api::channels_getSponsoredMessages(std::move(input_channel))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_getSponsoredMessages>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto sponsored_messages = result_ptr.move_as_ok();
td->contacts_manager_->on_get_users(std::move(sponsored_messages->users_), "GetSponsoredMessagesQuery");
td->contacts_manager_->on_get_chats(std::move(sponsored_messages->chats_), "GetSponsoredMessagesQuery");
vector<td_api::object_ptr<td_api::sponsoredMessage>> messages;
for (auto &sponsored_message : sponsored_messages->messages_) {
DialogId dialog_id(sponsored_message->from_id_);
if (!dialog_id.is_valid() || !td->messages_manager_->have_dialog_info_force(dialog_id)) {
LOG(ERROR) << "Receive unknown sponsor " << dialog_id;
continue;
}
td->messages_manager_->force_create_dialog(dialog_id, "GetSponsoredMessagesQuery");
auto message_text =
get_message_text(td->contacts_manager_.get(), std::move(sponsored_message->message_),
std::move(sponsored_message->entities_), true, true, 0, false, "GetSponsoredMessagesQuery");
int32 ttl = 0;
auto content = get_message_content(td, std::move(message_text), nullptr, dialog_id, true, UserId(), &ttl);
if (ttl != 0) {
LOG(ERROR) << "Receive sponsored message with TTL " << ttl;
continue;
}
messages.push_back(td_api::make_object<td_api::sponsoredMessage>(
sponsored_message->random_id_.as_slice().str(), dialog_id.get(), sponsored_message->start_param_,
get_message_content_object(content.get(), td, DialogId(channel_id_), 0, false, true, -1)));
}
promise_.set_value(td_api::make_object<td_api::sponsoredMessages>(std::move(messages)));
}
void on_error(uint64 id, Status status) final {
td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetSponsoredMessagesQuery");
promise_.set_error(std::move(status));
}
};
class ViewSponsoredMessageQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
public:
explicit ViewSponsoredMessageQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, const string &message_id) {
channel_id_ = channel_id;
auto input_channel = td->contacts_manager_->get_input_channel(channel_id);
if (input_channel == nullptr) {
return promise_.set_error(Status::Error(3, "Chat info not found"));
}
send_query(G()->net_query_creator().create(
telegram_api::channels_viewSponsoredMessage(std::move(input_channel), BufferSlice(message_id))));
}
void on_result(uint64 id, BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_viewSponsoredMessage>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) final {
td->contacts_manager_->on_get_channel_error(channel_id_, status, "ViewSponsoredMessageQuery");
promise_.set_error(std::move(status));
}
};
void get_dialog_sponsored_messages(Td *td, DialogId dialog_id,
Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise) {
if (!td->messages_manager_->have_dialog_force(dialog_id, "get_sponsored_messages")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (dialog_id.get_type() != DialogType::Channel ||
td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) != ContactsManager::ChannelType::Broadcast) {
return promise.set_value(td_api::make_object<td_api::sponsoredMessages>());
}
td->create_handler<GetSponsoredMessagesQuery>(std::move(promise))->send(dialog_id.get_channel_id());
}
void view_sponsored_message(Td *td, DialogId dialog_id, const string &message_id, Promise<Unit> &&promise) {
if (!td->messages_manager_->have_dialog_force(dialog_id, "view_sponsored_message")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (dialog_id.get_type() != DialogType::Channel ||
td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) != ContactsManager::ChannelType::Broadcast ||
message_id.empty()) {
return promise.set_error(Status::Error(400, "Message not found"));
}
td->create_handler<ViewSponsoredMessageQuery>(std::move(promise))->send(dialog_id.get_channel_id(), message_id);
}
} // namespace td

View File

@ -1,25 +0,0 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/td_api.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
namespace td {
class Td;
void get_dialog_sponsored_messages(Td *td, DialogId dialog_id,
Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise);
void view_sponsored_message(Td *td, DialogId dialog_id, const string &message_id, Promise<Unit> &&promise);
} // namespace td

View File

@ -4071,8 +4071,8 @@ void StickersManager::on_get_archived_sticker_sets(
LOG(ERROR) << "Receive " << total_count << " as total count of archived sticker sets";
}
// if 0 sticker sets are received, then set offset_sticker_set_id was found and there is no stickers after it
// or it wasn't found and there is no archived sets at all
// if 0 sticker sets are received, then set offset_sticker_set_id was found and there are no stickers after it
// or it wasn't found and there are no archived sets at all
bool is_last =
sticker_sets.empty() && (!offset_sticker_set_id.is_valid() ||
(!sticker_set_ids.empty() && offset_sticker_set_id == sticker_set_ids.back()));

View File

@ -43,6 +43,7 @@
#include "td/telegram/files/FileType.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/GameManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/GroupCallId.h"
#include "td/telegram/GroupCallManager.h"
@ -90,7 +91,7 @@
#include "td/telegram/SecretChatsManager.h"
#include "td/telegram/SecureManager.h"
#include "td/telegram/SecureValue.h"
#include "td/telegram/SponsoredMessages.h"
#include "td/telegram/SponsoredMessageManager.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
@ -1268,82 +1269,6 @@ class EditMessageReplyMarkupRequest final : public RequestOnceActor {
}
};
class SetGameScoreRequest final : public RequestOnceActor {
FullMessageId full_message_id_;
bool edit_message_;
UserId user_id_;
int32 score_;
bool force_;
void do_run(Promise<Unit> &&promise) final {
td->messages_manager_->set_game_score(full_message_id_, edit_message_, user_id_, score_, force_,
std::move(promise));
}
void do_send_result() final {
send_result(td->messages_manager_->get_message_object(full_message_id_));
}
public:
SetGameScoreRequest(ActorShared<Td> td, uint64 request_id, int64 dialog_id, int64 message_id, bool edit_message,
int32 user_id, int32 score, bool force)
: RequestOnceActor(std::move(td), request_id)
, full_message_id_(DialogId(dialog_id), MessageId(message_id))
, edit_message_(edit_message)
, user_id_(user_id)
, score_(score)
, force_(force) {
}
};
class GetGameHighScoresRequest final : public RequestOnceActor {
FullMessageId full_message_id_;
UserId user_id_;
int64 random_id_;
void do_run(Promise<Unit> &&promise) final {
random_id_ = td->messages_manager_->get_game_high_scores(full_message_id_, user_id_, std::move(promise));
}
void do_send_result() final {
CHECK(random_id_ != 0);
send_result(td->messages_manager_->get_game_high_scores_object(random_id_));
}
public:
GetGameHighScoresRequest(ActorShared<Td> td, uint64 request_id, int64 dialog_id, int64 message_id, int32 user_id)
: RequestOnceActor(std::move(td), request_id)
, full_message_id_(DialogId(dialog_id), MessageId(message_id))
, user_id_(user_id)
, random_id_(0) {
}
};
class GetInlineGameHighScoresRequest final : public RequestOnceActor {
string inline_message_id_;
UserId user_id_;
int64 random_id_;
void do_run(Promise<Unit> &&promise) final {
random_id_ = td->messages_manager_->get_inline_game_high_scores(inline_message_id_, user_id_, std::move(promise));
}
void do_send_result() final {
CHECK(random_id_ != 0);
send_result(td->messages_manager_->get_game_high_scores_object(random_id_));
}
public:
GetInlineGameHighScoresRequest(ActorShared<Td> td, uint64 request_id, string inline_message_id, int32 user_id)
: RequestOnceActor(std::move(td), request_id)
, inline_message_id_(std::move(inline_message_id))
, user_id_(user_id)
, random_id_(0) {
}
};
class GetChatHistoryRequest final : public RequestActor<> {
DialogId dialog_id_;
MessageId from_message_id_;
@ -3706,6 +3631,8 @@ void Td::dec_actor_refcnt() {
LOG(DEBUG) << "FileManager was cleared" << timer;
file_reference_manager_.reset();
LOG(DEBUG) << "FileReferenceManager was cleared" << timer;
game_manager_.reset();
LOG(DEBUG) << "GameManager was cleared" << timer;
group_call_manager_.reset();
LOG(DEBUG) << "GroupCallManager was cleared" << timer;
inline_queries_manager_.reset();
@ -3718,6 +3645,8 @@ void Td::dec_actor_refcnt() {
LOG(DEBUG) << "NotificationManager was cleared" << timer;
poll_manager_.reset();
LOG(DEBUG) << "PollManager was cleared" << timer;
sponsored_message_manager_.reset();
LOG(DEBUG) << "SponsoredMessageManager was cleared" << timer;
stickers_manager_.reset();
LOG(DEBUG) << "StickersManager was cleared" << timer;
memory_manager_.reset();
@ -3909,6 +3838,8 @@ void Td::clear() {
LOG(DEBUG) << "FileManager actor was cleared" << timer;
file_reference_manager_actor_.reset();
LOG(DEBUG) << "FileReferenceManager actor was cleared" << timer;
game_manager_actor_.reset();
LOG(DEBUG) << "GameManager actor was cleared" << timer;
group_call_manager_actor_.reset();
LOG(DEBUG) << "GroupCallManager actor was cleared" << timer;
inline_queries_manager_actor_.reset();
@ -3921,6 +3852,8 @@ void Td::clear() {
LOG(DEBUG) << "NotificationManager actor was cleared" << timer;
poll_manager_actor_.reset();
LOG(DEBUG) << "PollManager actor was cleared" << timer;
sponsored_message_manager_actor_.reset();
LOG(DEBUG) << "SponsoredMessageManager actor was cleared" << timer;
stickers_manager_actor_.reset();
LOG(DEBUG) << "StickersManager actor was cleared" << timer;
memory_manager_actor_.reset();
@ -4371,6 +4304,9 @@ void Td::init_managers() {
G()->set_contacts_manager(contacts_manager_actor_.get());
country_info_manager_ = make_unique<CountryInfoManager>(this, create_reference());
country_info_manager_actor_ = register_actor("CountryInfoManager", country_info_manager_.get());
game_manager_ = make_unique<GameManager>(this, create_reference());
game_manager_actor_ = register_actor("GameManager", game_manager_.get());
G()->set_game_manager(game_manager_actor_.get());
group_call_manager_ = make_unique<GroupCallManager>(this, create_reference());
group_call_manager_actor_ = register_actor("GroupCallManager", group_call_manager_.get());
G()->set_group_call_manager(group_call_manager_actor_.get());
@ -4387,6 +4323,9 @@ void Td::init_managers() {
poll_manager_ = make_unique<PollManager>(this, create_reference());
poll_manager_actor_ = register_actor("PollManager", poll_manager_.get());
G()->set_notification_manager(notification_manager_actor_.get());
sponsored_message_manager_ = make_unique<SponsoredMessageManager>(this, create_reference());
sponsored_message_manager_actor_ = register_actor("SponsoredMessageManager", sponsored_message_manager_.get());
G()->set_sponsored_message_manager(sponsored_message_manager_actor_.get());
stickers_manager_ = make_unique<StickersManager>(this, create_reference());
stickers_manager_actor_ = register_actor("StickersManager", stickers_manager_.get());
G()->set_stickers_manager(stickers_manager_actor_.get());
@ -5097,13 +5036,14 @@ void Td::on_request(uint64 id, const td_api::getMessages &request) {
void Td::on_request(uint64 id, const td_api::getChatSponsoredMessages &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
get_dialog_sponsored_messages(this, DialogId(request.chat_id_), std::move(promise));
sponsored_message_manager_->get_dialog_sponsored_messages(DialogId(request.chat_id_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::viewSponsoredMessage &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
view_sponsored_message(this, DialogId(request.chat_id_), request.message_id_, std::move(promise));
sponsored_message_manager_->view_sponsored_message(DialogId(request.chat_id_), request.message_id_,
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getMessageThread &request) {
@ -5831,28 +5771,31 @@ void Td::on_request(uint64 id, td_api::editMessageSchedulingState &request) {
void Td::on_request(uint64 id, td_api::setGameScore &request) {
CHECK_IS_BOT();
CREATE_REQUEST(SetGameScoreRequest, request.chat_id_, request.message_id_, request.edit_message_, request.user_id_,
request.score_, request.force_);
CREATE_REQUEST_PROMISE();
game_manager_->set_game_score({DialogId(request.chat_id_), MessageId(request.message_id_)}, request.edit_message_,
UserId(request.user_id_), request.score_, request.force_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::setInlineGameScore &request) {
CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.inline_message_id_);
CREATE_OK_REQUEST_PROMISE();
messages_manager_->set_inline_game_score(std::move(request.inline_message_id_), request.edit_message_,
UserId(request.user_id_), request.score_, request.force_,
std::move(promise));
game_manager_->set_inline_game_score(std::move(request.inline_message_id_), request.edit_message_,
UserId(request.user_id_), request.score_, request.force_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::getGameHighScores &request) {
CHECK_IS_BOT();
CREATE_REQUEST(GetGameHighScoresRequest, request.chat_id_, request.message_id_, request.user_id_);
CREATE_REQUEST_PROMISE();
game_manager_->get_game_high_scores({DialogId(request.chat_id_), MessageId(request.message_id_)},
UserId(request.user_id_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::getInlineGameHighScores &request) {
CHECK_IS_BOT();
CLEAN_INPUT_STRING(request.inline_message_id_);
CREATE_REQUEST(GetInlineGameHighScoresRequest, std::move(request.inline_message_id_), request.user_id_);
CREATE_REQUEST_PROMISE();
game_manager_->get_inline_game_high_scores(request.inline_message_id_, UserId(request.user_id_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::deleteChatReplyMarkup &request) {

View File

@ -57,6 +57,7 @@ class DeviceTokenManager;
class DocumentsManager;
class FileManager;
class FileReferenceManager;
class GameManager;
class GroupCallManager;
class InlineQueriesManager;
class HashtagHints;
@ -71,6 +72,7 @@ class PollManager;
class PrivacyManager;
class SecureManager;
class SecretChatsManager;
class SponsoredMessageManager;
class StickersManager;
class StorageManager;
class MemoryManager;
@ -168,6 +170,8 @@ class Td final : public NetQueryCallback {
ActorOwn<FileManager> file_manager_actor_;
unique_ptr<FileReferenceManager> file_reference_manager_;
ActorOwn<FileReferenceManager> file_reference_manager_actor_;
unique_ptr<GameManager> game_manager_;
ActorOwn<GameManager> game_manager_actor_;
unique_ptr<GroupCallManager> group_call_manager_;
ActorOwn<GroupCallManager> group_call_manager_actor_;
unique_ptr<InlineQueriesManager> inline_queries_manager_;
@ -180,6 +184,8 @@ class Td final : public NetQueryCallback {
ActorOwn<NotificationManager> notification_manager_actor_;
unique_ptr<PollManager> poll_manager_;
ActorOwn<PollManager> poll_manager_actor_;
unique_ptr<SponsoredMessageManager> sponsored_message_manager_;
ActorOwn<SponsoredMessageManager> sponsored_message_manager_actor_;
unique_ptr<StickersManager> stickers_manager_;
ActorOwn<StickersManager> stickers_manager_actor_;
unique_ptr<ThemeManager> theme_manager_;

View File

@ -14,6 +14,7 @@
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Time.h"
namespace td {
@ -44,6 +45,7 @@ class GetChatThemesQuery final : public Td::ResultHandler {
};
ThemeManager::ThemeManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
chat_themes_.next_reload_time = Time::now();
}
void ThemeManager::tear_down() {
@ -51,7 +53,16 @@ void ThemeManager::tear_down() {
}
void ThemeManager::get_chat_themes(Promise<td_api::object_ptr<td_api::chatThemes>> &&promise) {
pending_get_chat_themes_queries_.push_back(std::move(promise));
if (Time::now() < chat_themes_.next_reload_time) {
return promise.set_value(get_chat_themes_object());
}
if (!chat_themes_.themes.empty()) {
promise.set_value(get_chat_themes_object());
pending_get_chat_themes_queries_.push_back(Promise<td_api::object_ptr<td_api::chatThemes>>());
} else {
pending_get_chat_themes_queries_.push_back(std::move(promise));
}
if (pending_get_chat_themes_queries_.size() == 1) {
auto request_promise = PromiseCreator::lambda(
[actor_id = actor_id(this)](Result<telegram_api::object_ptr<telegram_api::account_ChatThemes>> result) {
@ -106,34 +117,33 @@ void ThemeManager::on_get_chat_themes(Result<telegram_api::object_ptr<telegram_a
return;
}
chat_themes_.next_reload_time = Time::now() + THEME_CACHE_TIME;
auto chat_themes_ptr = result.move_as_ok();
LOG(DEBUG) << "Receive " << to_string(chat_themes_ptr);
if (chat_themes_ptr->get_id() == telegram_api::account_chatThemesNotModified::ID) {
for (auto &promise : promises) {
promise.set_value(get_chat_themes_object());
}
return;
}
if (chat_themes_ptr->get_id() != telegram_api::account_chatThemesNotModified::ID) {
CHECK(chat_themes_ptr->get_id() == telegram_api::account_chatThemes::ID);
auto chat_themes = telegram_api::move_object_as<telegram_api::account_chatThemes>(chat_themes_ptr);
chat_themes_.hash = chat_themes->hash_;
chat_themes_.themes.clear();
for (auto &chat_theme : chat_themes->themes_) {
if (chat_theme->emoticon_.empty()) {
LOG(ERROR) << "Receive " << to_string(chat_theme);
continue;
}
auto chat_themes = telegram_api::move_object_as<telegram_api::account_chatThemes>(chat_themes_ptr);
LOG(INFO) << "Receive " << to_string(chat_themes);
chat_themes_.hash = chat_themes->hash_;
chat_themes_.themes.clear();
for (auto &chat_theme : chat_themes->themes_) {
if (chat_theme->emoticon_.empty()) {
LOG(ERROR) << "Receive " << to_string(chat_theme);
continue;
ChatTheme theme;
theme.emoji = std::move(chat_theme->emoticon_);
theme.light_theme = get_chat_theme_settings(std::move(chat_theme->theme_->settings_));
theme.dark_theme = get_chat_theme_settings(std::move(chat_theme->dark_theme_->settings_));
chat_themes_.themes.push_back(std::move(theme));
}
ChatTheme theme;
theme.emoji = std::move(chat_theme->emoticon_);
theme.light_theme = get_chat_theme_settings(std::move(chat_theme->theme_->settings_));
theme.dark_theme = get_chat_theme_settings(std::move(chat_theme->dark_theme_->settings_));
chat_themes_.themes.push_back(std::move(theme));
}
for (auto &promise : promises) {
promise.set_value(get_chat_themes_object());
if (promise) {
promise.set_value(get_chat_themes_object());
}
}
}

View File

@ -30,6 +30,8 @@ class ThemeManager final : public Actor {
private:
enum class BaseTheme : int32 { Classic, Day, Night, Tinted, Arctic };
static constexpr int32 THEME_CACHE_TIME = 3600;
struct ThemeSettings {
int32 accent_color = 0;
BackgroundId background_id;
@ -47,6 +49,7 @@ class ThemeManager final : public Actor {
struct ChatThemes {
int32 hash = 0;
double next_reload_time = 0;
vector<ChatTheme> themes;
};

View File

@ -622,7 +622,7 @@ void WebPagesManager::update_web_page_instant_view(WebPageId web_page_id, WebPag
auto previous_queries =
load_web_page_instant_view_queries.partial.size() + load_web_page_instant_view_queries.full.size();
if (previous_queries == 0) {
// try to load it only if there is no pending load queries
// try to load it only if there are no pending load queries
load_web_page_instant_view(web_page_id, false, Auto());
return;
}

View File

@ -4191,8 +4191,7 @@ class CliClient final : public Actor {
string chat_id;
string message_id;
string user_id;
int32 score;
get_args(args, chat_id, message_id, user_id, score);
get_args(args, chat_id, message_id, user_id);
send_request(td_api::make_object<td_api::getGameHighScores>(as_chat_id(chat_id), as_message_id(message_id),
as_user_id(user_id)));
} else if (op == "gmst") {

View File

@ -196,6 +196,7 @@ void HttpConnectionBase::on_start_migrate(int32 sched_id) {
void HttpConnectionBase::on_finish_migrate() {
Scheduler::subscribe(fd_.get_poll_info().extract_pollable_fd(this));
live_event();
}
} // namespace detail

View File

@ -223,7 +223,7 @@ Result<size_t> HttpReader::read_next(HttpQuery *query, bool can_be_slow) {
return need_size;
}
case State::ReadMultipartFormData: {
if (!content_->empty()) {
if (!content_->empty() || flow_sink_.is_ready()) {
TRY_RESULT(result, parse_multipart_form_data(can_be_slow));
if (result) {
break;

View File

@ -170,8 +170,8 @@ TEST(Link, parse_internal_link) {
auto unknown_deep_link = [](td::string link) {
return td::td_api::make_object<td::td_api::internalLinkTypeUnknownDeepLink>(link);
};
auto voice_chat = [](td::string chat_username, td::string invite_hash) {
return td::td_api::make_object<td::td_api::internalLinkTypeVoiceChat>(chat_username, invite_hash);
auto voice_chat = [](td::string chat_username, td::string invite_hash, bool is_live_stream) {
return td::td_api::make_object<td::td_api::internalLinkTypeVoiceChat>(chat_username, invite_hash, is_live_stream);
};
parse_internal_link("t.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
@ -523,23 +523,27 @@ TEST(Link, parse_internal_link) {
parse_internal_link("tg:socks?server=google.com&port=8%30&user=&pass=2", proxy_socks("google.com", 80, "", "2"));
parse_internal_link("tg:socks?server=google.com&port=80&user=1&pass=2", proxy_socks("google.com", 80, "1", "2"));
parse_internal_link("tg:resolve?domain=username&voice%63hat=aasdasd", voice_chat("username", "aasdasd"));
parse_internal_link("TG://resolve?domain=username&voicechat=", voice_chat("username", ""));
parse_internal_link("tg:resolve?domain=username&voice%63hat=aasdasd", voice_chat("username", "aasdasd", false));
parse_internal_link("tg:resolve?domain=username&video%63hat=aasdasd", voice_chat("username", "aasdasd", false));
parse_internal_link("tg:resolve?domain=username&livestream=aasdasd", voice_chat("username", "aasdasd", true));
parse_internal_link("TG://resolve?domain=username&voicechat=", voice_chat("username", "", false));
parse_internal_link("TG://test@resolve?domain=username&voicechat=", nullptr);
parse_internal_link("tg:resolve:80?domain=username&voicechat=", nullptr);
parse_internal_link("tg:http://resolve?domain=username&voicechat=", nullptr);
parse_internal_link("tg:https://resolve?domain=username&voicechat=", nullptr);
parse_internal_link("tg:resolve?domain=&voicechat=", unknown_deep_link("tg://resolve?domain=&voicechat="));
parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", voice_chat("telegram", "0"));
parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", voice_chat("telegram", "0", false));
parse_internal_link("t.me/username/0/a//s/as?voicechat=", voice_chat("username", ""));
parse_internal_link("t.me/username/aasdas?test=1&voicechat=#12312", voice_chat("username", ""));
parse_internal_link("t.me/username/0?voicechat=", voice_chat("username", ""));
parse_internal_link("t.me/username/-1?voicechat=asdasd", voice_chat("username", "asdasd"));
parse_internal_link("t.me/username?voicechat=", voice_chat("username", ""));
parse_internal_link("t.me/username/0/a//s/as?voicechat=", voice_chat("username", "", false));
parse_internal_link("t.me/username/0/a//s/as?videochat=2", voice_chat("username", "2", false));
parse_internal_link("t.me/username/0/a//s/as?livestream=3", voice_chat("username", "3", true));
parse_internal_link("t.me/username/aasdas?test=1&voicechat=#12312", voice_chat("username", "", false));
parse_internal_link("t.me/username/0?voicechat=", voice_chat("username", "", false));
parse_internal_link("t.me/username/-1?voicechat=asdasd", voice_chat("username", "asdasd", false));
parse_internal_link("t.me/username?voicechat=", voice_chat("username", "", false));
parse_internal_link("t.me/username#voicechat=asdas", public_chat("username"));
parse_internal_link("t.me//username?voicechat=", nullptr);
parse_internal_link("https://telegram.dog/tele%63ram?voi%63e%63hat=t%63st", voice_chat("telecram", "tcst"));
parse_internal_link("https://telegram.dog/tele%63ram?voi%63e%63hat=t%63st", voice_chat("telecram", "tcst", false));
parse_internal_link("tg:resolve?domain=username&start=aasdasd", bot_start("username", "aasdasd"));
parse_internal_link("TG://resolve?domain=username&start=", bot_start("username", ""));