diff --git a/CHANGELOG.md b/CHANGELOG.md index 66220220..ff83a201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -206,7 +206,7 @@ Changes in 1.5.0: instead of `can_send_media_messages`. - Allowed administrators in basic groups to use the method `generateChatInviteLink`. * Added out of the box `OpenBSD` and `NetBSD` operating systems support. -* Added possibility to use `LibreSSL` instead of `OpenSSL` to build TDLib. +* Added possibility to use `LibreSSL` >= 2.7.0 instead of `OpenSSL` to build TDLib. * Added instructions for building TDLib on `Debian 10`, `OpenBSD` and `NetBSD` to the [TDLib build instructions generator](https://tdlib.github.io/td/build.html). * Added support for Backgrounds 2.0: diff --git a/CMakeLists.txt b/CMakeLists.txt index a26dae4d..29046e4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) -project(TDLib VERSION 1.5.5 LANGUAGES CXX C) +project(TDLib VERSION 1.6.2 LANGUAGES CXX C) if (NOT DEFINED CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR "lib") @@ -189,7 +189,7 @@ elseif (INTEL) endif() if (WIN32) - add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DNOMINMAX -DUNICODE -D_UNICODE) + add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DNOMINMAX -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN) endif() if (CYGWIN) add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096) @@ -243,6 +243,10 @@ if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)) # see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579 add_cxx_compiler_flag("-Wno-redundant-move") endif() +if (CLANG AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)) + # https://stackoverflow.com/questions/26744556/warning-returning-a-captured-reference-from-a-lambda + add_cxx_compiler_flag("-Wno-return-stack-address") +endif() #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") @@ -262,9 +266,9 @@ if (NOT CMAKE_CROSSCOMPILING) if (TD_ENABLE_DOTNET) add_custom_target(remove_cpp_documentation WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND remove_documentation ${TL_TD_AUTO} td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h + COMMAND remove_documentation ${TL_TD_API_AUTO_SOURCE} td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h COMMENT "Remove C++ documentation from sources" - DEPENDS remove_documentation tl_generate_common generate_dotnet_api ${TL_TD_AUTO} td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h + DEPENDS remove_documentation tl_generate_common generate_dotnet_api ${TL_TD_API_AUTO_SOURCE} td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h ) add_dependencies(prepare_cross_compiling generate_dotnet_api remove_cpp_documentation) @@ -301,46 +305,52 @@ endif() get_directory_property(HAS_PARENT PARENT_DIRECTORY) if (HAS_PARENT) - set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) - set(TL_TD_JSON_AUTO ${TL_TD_JSON_AUTO} PARENT_SCOPE) - set(TD_TEST_SOURCE ${TD_TEST_SOURCE} PARENT_SCOPE) + set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) # was used in standalone binding generators + set(TL_TD_JSON_AUTO ${TL_TD_JSON_AUTO_SOURCE} PARENT_SCOPE) # used in tdbot + set(TD_TEST_SOURCE ${TD_TEST_SOURCE} PARENT_SCOPE) # used to build tests endif() #SOURCE SETS -set_source_files_properties(${TL_TD_AUTO} PROPERTIES GENERATED TRUE) +set_source_files_properties(${TL_TD_API_AUTO_SOURCE} PROPERTIES GENERATED TRUE) if (TD_ENABLE_JNI OR ANDROID) - set(TL_JNI_OBJECT + set(TL_JNI_OBJECT_SOURCE td/tl/tl_jni_object.cpp td/tl/tl_jni_object.h ) else() - set(TL_JNI_OBJECT) + set(TL_JNI_OBJECT_SOURCE) endif() +set(TL_TD_API_SOURCE + ${TL_TD_API_AUTO_SOURCE} + ${TL_JNI_OBJECT_SOURCE} + td/tl/TlObject.h +) + +set_source_files_properties(${TL_TD_AUTO_SOURCE} PROPERTIES GENERATED TRUE) set(TL_TD_SCHEME_SOURCE - ${TL_TD_AUTO} - ${TL_JNI_OBJECT} + ${TL_TD_AUTO_SOURCE} td/tl/TlObject.h td/tl/tl_object_parse.h td/tl/tl_object_store.h ) -set_source_files_properties(${TL_TD_JSON_AUTO} PROPERTIES GENERATED TRUE) -set(TL_TD_JSON - ${TL_TD_JSON_AUTO} +set_source_files_properties(${TL_TD_JSON_AUTO_SOURCE} PROPERTIES GENERATED TRUE) +set(TL_TD_JSON_SOURCE + ${TL_TD_JSON_AUTO_SOURCE} td/tl/tl_json.h ) -set_source_files_properties(${TL_C_AUTO} PROPERTIES GENERATED TRUE) +set_source_files_properties(${TL_C_AUTO_SOURCE} PROPERTIES GENERATED TRUE) set(TL_C_SCHEME_SOURCE - ${TL_C_AUTO} + ${TL_C_AUTO_SOURCE} ) -set_source_files_properties(${TL_DOTNET_AUTO} PROPERTIES GENERATED TRUE) +set_source_files_properties(${TL_DOTNET_AUTO_SOURCE} PROPERTIES GENERATED TRUE) set(TL_DOTNET_SCHEME_SOURCE - ${TL_DOTNET_AUTO} + ${TL_DOTNET_AUTO_SOURCE} td/tl/tl_dotnet_object.h ) @@ -741,21 +751,29 @@ if (MEMPROF) endif() -# tdcore - mostly internal TDLib interface. One should use tdactor for interactions with it. -add_library(tdcore STATIC ${TDLIB_SOURCE}) -target_include_directories(tdcore PUBLIC $ $) -target_include_directories(tdcore SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) -target_link_libraries(tdcore PUBLIC tdactor tdutils tdnet tddb PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES}) +add_library(tdapi STATIC ${TL_TD_API_SOURCE}) +target_include_directories(tdapi PUBLIC $ INTERFACE $) +target_link_libraries(tdapi PRIVATE tdutils) if (TD_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android if (NOT JNI_FOUND) find_package(JNI REQUIRED) endif() message(STATUS "Found JNI: ${JNI_INCLUDE_DIRS} ${JNI_LIBRARIES}") - target_include_directories(tdcore PUBLIC ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) - target_link_libraries(tdcore PUBLIC ${JAVA_JVM_LIBRARY}) + target_include_directories(tdapi PUBLIC ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) + target_link_libraries(tdapi PUBLIC ${JAVA_JVM_LIBRARY}) endif() +if (NOT CMAKE_CROSSCOMPILING) + add_dependencies(tdapi tl_generate_common) +endif() + +# tdcore - mostly internal TDLib interface. One should use tdactor for interactions with it. +add_library(tdcore STATIC ${TDLIB_SOURCE}) +target_include_directories(tdcore PUBLIC $ $) +target_include_directories(tdcore SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) +target_link_libraries(tdcore PUBLIC tdapi tdactor tdutils tdnet tddb PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES}) + if (NOT CMAKE_CROSSCOMPILING) add_dependencies(tdcore tl_generate_common) if (TD_ENABLE_JNI) @@ -769,15 +787,8 @@ endif() add_library(tdclient td/telegram/Client.cpp td/telegram/Client.h td/telegram/Log.cpp td/telegram/Log.h) target_include_directories(tdclient PUBLIC $ - $ ) - -target_link_libraries(tdclient PRIVATE tdcore) - -if (TD_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android - target_include_directories(tdclient PUBLIC ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) - target_link_libraries(tdclient PUBLIC ${JAVA_JVM_LIBRARY}) -endif() +target_link_libraries(tdclient PUBLIC tdapi PRIVATE tdcore) if (TD_ENABLE_DOTNET) add_library(tddotnet SHARED @@ -815,7 +826,7 @@ if (NOT CMAKE_CROSSCOMPILING) add_dependencies(tdc tl_generate_c) endif() -add_library(tdjson_private STATIC ${TL_TD_JSON} td/telegram/ClientJson.cpp td/telegram/ClientJson.h) +add_library(tdjson_private STATIC ${TL_TD_JSON_SOURCE} td/telegram/ClientJson.cpp td/telegram/ClientJson.h) target_include_directories(tdjson_private PUBLIC $ $) @@ -858,6 +869,7 @@ if (WIN32 OR CYGWIN) endif() endif() if (BIGOBJ) + target_compile_options(tdapi PUBLIC ${BIGOBJ}) target_compile_options(tdc PUBLIC ${BIGOBJ}) target_compile_options(tdcore PUBLIC ${BIGOBJ}) target_compile_options(tdclient PUBLIC ${BIGOBJ}) @@ -877,7 +889,7 @@ endif() #EXECUTABLES if (NOT CMAKE_CROSSCOMPILING) - add_executable(tg_cli td/telegram/cli.cpp ${TL_TD_JSON}) + add_executable(tg_cli td/telegram/cli.cpp ${TL_TD_JSON_SOURCE}) if (NOT READLINE_FOUND) find_package(Readline) @@ -904,7 +916,7 @@ if (NOT CMAKE_CROSSCOMPILING) target_compile_definitions(tg_cli PRIVATE -DUSE_READLINE=1) endif() endif() - target_link_libraries(tg_cli PRIVATE memprof tdclient tdcore tdtl) + target_link_libraries(tg_cli PRIVATE memprof tdclient tdcore) add_dependencies(tg_cli tl_generate_json) endif() @@ -922,7 +934,7 @@ add_library(Td::TdStatic ALIAS TdStatic) add_library(Td::TdJson ALIAS TdJson) add_library(Td::TdJsonStatic ALIAS TdJsonStatic) -install(TARGETS tdjson TdJson tdjson_static TdJsonStatic tdjson_private tdclient tdcore TdStatic EXPORT TdTargets +install(TARGETS tdjson TdJson tdjson_static TdJsonStatic tdjson_private tdclient tdcore tdapi TdStatic EXPORT TdTargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" @@ -935,8 +947,11 @@ install(EXPORT TdTargets DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Td" ) +# Install tdjson/tdjson_static: install(FILES ${TD_JSON_HEADERS} "${CMAKE_CURRENT_BINARY_DIR}/td/telegram/tdjson_export.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/telegram") +# Install tdclient: install(FILES td/telegram/Client.h td/telegram/Log.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/telegram") +# Install tdapi: install(FILES td/tl/TlObject.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/tl") install(FILES "${TL_TD_AUTO_INCLUDE_DIR}/td/telegram/td_api.h" "${TL_TD_AUTO_INCLUDE_DIR}/td/telegram/td_api.hpp" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/telegram") if (TD_ENABLE_JNI) diff --git a/README.md b/README.md index d716a3d5..b44a16b7 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele ## Examples and documentation -Take a look at our [examples](https://github.com/tdlib/td/blob/master/example/README.md#tdlib-usage-and-build-examples). - See our [Getting Started](https://core.telegram.org/tdlib/getting-started) tutorial for a description of basic TDLib concepts. +Take a look at our [examples](https://github.com/tdlib/td/blob/master/example/README.md#tdlib-usage-and-build-examples). + See a [TDLib build instructions generator](https://tdlib.github.io/td/build.html) for detailed instructions on how to build TDLib. See description of our [JSON](#using-json), [C++](#using-cxx), [Java](#using-java) and [.NET](#using-dotnet) interfaces. @@ -115,7 +115,7 @@ cd * 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=\scripts\buildsystems\vcpkg.cmake .. +cmake -DCMAKE_TOOLCHAIN_FILE=/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. @@ -146,7 +146,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic) Or you could install `TDLib` and then reference it in your CMakeLists.txt like this: ``` -find_package(Td 1.5.5 REQUIRED) +find_package(Td 1.6.2 REQUIRED) target_link_libraries(YourTarget PRIVATE Td::TdStatic) ``` See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt). @@ -160,7 +160,7 @@ See [example/java](https://github.com/tdlib/td/tree/master/example/java) for exa ## Using in .NET projects `TDLib` provides native .NET interface through `C++/CLI` and `C++/CX`. To enable it, specify option `-DTD_ENABLE_DOTNET=ON` to CMake. -.NET Core doesn't support `C++/CLI`, so if .NET Core is used, then `TDLib` JSON interface should be used through P/Invoke instead. +.NET Core supports `C++/CLI` only since version 3.1 and only on Windows, so if older .NET Core is used or portability is needed, then `TDLib` JSON interface should be used through P/Invoke instead. See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions. See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform". diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 5167bb07..9b7d5c05 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -7,7 +7,7 @@ endif() #TODO: all benchmarks in one file add_executable(bench_crypto bench_crypto.cpp) -target_link_libraries(bench_crypto PRIVATE tdcore tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES}) +target_link_libraries(bench_crypto PRIVATE tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES}) target_include_directories(bench_crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) add_executable(bench_actor bench_actor.cpp) @@ -41,7 +41,7 @@ add_executable(bench_misc bench_misc.cpp) target_link_libraries(bench_misc PRIVATE tdcore tdutils) add_executable(check_proxy check_proxy.cpp) -target_link_libraries(check_proxy PRIVATE tdclient tdcore) +target_link_libraries(check_proxy PRIVATE tdclient tdutils) add_executable(check_tls check_tls.cpp) target_link_libraries(check_tls PRIVATE tdutils) diff --git a/benchmark/bench_crypto.cpp b/benchmark/bench_crypto.cpp index edc74af0..39493025 100644 --- a/benchmark/bench_crypto.cpp +++ b/benchmark/bench_crypto.cpp @@ -30,7 +30,7 @@ class SHA1Bench : public td::Benchmark { alignas(64) unsigned char data[DATA_SIZE]; std::string get_description() const override { - return PSTRING() << "SHA1 OpenSSL [" << (DATA_SIZE >> 10) << "kB]"; + return PSTRING() << "SHA1 OpenSSL [" << (DATA_SIZE >> 10) << "KB]"; } void start_up() override { @@ -55,7 +55,7 @@ class AESBench : public td::Benchmark { td::UInt256 iv; std::string get_description() const override { - return PSTRING() << "AES OpenSSL [" << (DATA_SIZE >> 10) << "kB]"; + return PSTRING() << "AES OpenSSL [" << (DATA_SIZE >> 10) << "KB]"; } void start_up() override { @@ -152,7 +152,7 @@ class Crc32Bench : public td::Benchmark { alignas(64) unsigned char data[DATA_SIZE]; std::string get_description() const override { - return PSTRING() << "Crc32 zlib [" << (DATA_SIZE >> 10) << "kB]"; + return PSTRING() << "Crc32 zlib [" << (DATA_SIZE >> 10) << "KB]"; } void start_up() override { @@ -176,7 +176,7 @@ class Crc64Bench : public td::Benchmark { alignas(64) unsigned char data[DATA_SIZE]; std::string get_description() const override { - return PSTRING() << "Crc64 Anton [" << (DATA_SIZE >> 10) << "kB]"; + return PSTRING() << "Crc64 Anton [" << (DATA_SIZE >> 10) << "KB]"; } void start_up() override { diff --git a/benchmark/bench_queue.cpp b/benchmark/bench_queue.cpp index 7dbe02b8..d22b1247 100644 --- a/benchmark/bench_queue.cpp +++ b/benchmark/bench_queue.cpp @@ -43,7 +43,6 @@ //} //} -// TODO: warnings and asserts. There should be no warnings or debug output in production. using qvalue_t = int; // Just for testing, not production diff --git a/benchmark/check_proxy.cpp b/benchmark/check_proxy.cpp index efe081ce..a493b1c4 100644 --- a/benchmark/check_proxy.cpp +++ b/benchmark/check_proxy.cpp @@ -7,11 +7,13 @@ #include "td/telegram/Client.h" #include "td/telegram/td_api.h" +#include "td/utils/base64.h" #include "td/utils/common.h" #include "td/utils/filesystem.h" #include "td/utils/logging.h" #include "td/utils/misc.h" +#include #include #include #include @@ -34,11 +36,36 @@ int main(int argc, char **argv) { td::vector>> requests; - auto add_proxy = [&requests](const td::string &arg) { + auto add_proxy = [&requests](td::string arg) { if (arg.empty()) { return; } + std::size_t offset = 0; + if (arg[0] == '[') { + auto end_ipv6_pos = arg.find(']'); + if (end_ipv6_pos == td::string::npos) { + td::TsCerr() << "Error: failed to find end of IPv6 address in \"" << arg << "\"\n"; + usage(); + } + offset = end_ipv6_pos; + } + if (std::count(arg.begin() + offset, arg.end(), ':') == 3) { + auto secret_domain_pos = arg.find(':', arg.find(':', offset) + 1) + 1; + auto domain_pos = arg.find(':', secret_domain_pos); + auto secret = arg.substr(secret_domain_pos, domain_pos - secret_domain_pos); + auto domain = arg.substr(domain_pos + 1); + auto r_decoded_secret = td::hex_decode(secret); + if (r_decoded_secret.is_error()) { + r_decoded_secret = td::base64url_decode(secret); + if (r_decoded_secret.is_error()) { + td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n"; + usage(); + } + } + arg = arg.substr(0, secret_domain_pos) + td::base64url_encode(r_decoded_secret.ok() + domain); + } + auto secret_pos = arg.rfind(':'); if (secret_pos == td::string::npos) { td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n"; @@ -57,6 +84,9 @@ int main(int argc, char **argv) { } auto port = r_port.move_as_ok(); auto server = arg.substr(0, port_pos); + if (server[0] == '[' && server.back() == ']') { + server = server.substr(1, server.size() - 2); + } if (server.empty() || port <= 0 || port > 65536 || secret.empty()) { td::TsCerr() << "Error: proxy address to check is in wrong format: \"" << arg << "\"\n"; diff --git a/build.html b/build.html index 4208e5f8..0c458fcd 100644 --- a/build.html +++ b/build.html @@ -668,14 +668,14 @@ function onOptionsChanged() { commands.push('git clone https://github.com/tdlib/td.git'); commands.push('cd td'); - commands.push('git checkout v1.5.0'); + commands.push('git checkout v1.6.0'); if (use_vcpkg) { commands.push('git clone https://github.com/Microsoft/vcpkg.git'); commands.push('cd vcpkg'); commands.push(local + 'bootstrap-vcpkg.bat'); if (target === 'C++/CX') { - commands.push(local + 'vcpkg.exe install openssl:arm-uwp openssl:x64-uwp openssl:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp'); + commands.push(local + 'vcpkg.exe install openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp'); } else { if (build_64bit) { commands.push(local + 'vcpkg.exe install openssl:x64-windows zlib:x64-windows'); diff --git a/example/README.md b/example/README.md index 1fdabe5a..5a9dbdee 100644 --- a/example/README.md +++ b/example/README.md @@ -164,7 +164,7 @@ See [erl-tdlib](https://github.com/lattenwald/erl-tdlib) for an example of TDLib TDLib can be used from the PHP programming language by wrapping its functionality in a PHP extension. -See [phptdlib](https://github.com/yaroslavche/phptdlib) or [PIF-TDPony](https://github.com/danog/pif-tdpony) for examples of such extensions which provide access to TDLib from PHP. +See [phptdlib](https://github.com/yaroslavche/phptdlib), [tdlib](https://github.com/aurimasniekis/php-ext-tdlib) or [PIF-TDPony](https://github.com/danog/pif-tdpony) for examples of such extensions which provide access to TDLib from PHP. See [tdlib-bundle](https://github.com/yaroslavche/tdlib-bundle) โ€“ a Symfony bundle based on [phptdlib](https://github.com/yaroslavche/phptdlib). diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index 73f8e391..ff2ea9bd 100644 --- a/example/cpp/CMakeLists.txt +++ b/example/cpp/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(TdExample VERSION 1.0 LANGUAGES CXX) -find_package(Td 1.5.5 REQUIRED) +find_package(Td 1.6.2 REQUIRED) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/example/csharp/README.md b/example/csharp/README.md index b243861e..2542748f 100644 --- a/example/csharp/README.md +++ b/example/csharp/README.md @@ -19,7 +19,13 @@ cd cd /example/csharp mkdir build cd build -cmake -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=\scripts\buildsystems\vcpkg.cmake ../../.. +cmake -A Win32 -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake ../../.. +cmake --build . --config Release +cmake --build . --config Debug +cd .. +mkdir build64 +cd build64 +cmake -A x64 -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake ../../.. cmake --build . --config Release cmake --build . --config Debug ``` diff --git a/example/csharp/TdExample.csproj b/example/csharp/TdExample.csproj index ea2ad565..6a511d8f 100644 --- a/example/csharp/TdExample.csproj +++ b/example/csharp/TdExample.csproj @@ -33,6 +33,24 @@ TdExample + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + @@ -43,41 +61,57 @@ False - build\Debug\Telegram.Td.dll - build\Release\Telegram.Td.dll + build\$(Configuration)\Telegram.Td.dll + build64\$(Configuration)\Telegram.Td.dll - - - LIBEAY32.dll - PreserveNewest - - - SSLEAY32.dll - PreserveNewest - + zlibd1.dll PreserveNewest - - - LIBEAY32.dll - PreserveNewest - - - SSLEAY32.dll - PreserveNewest - + zlib1.dll PreserveNewest + + + zlibd1.dll + PreserveNewest + + + + + zlib1.dll + PreserveNewest + + + + + libcrypto-1_1.dll + PreserveNewest + + + libssl-1_1.dll + PreserveNewest + + + + + libcrypto-1_1-x64.dll + PreserveNewest + + + libssl-1_1-x64.dll + PreserveNewest + + diff --git a/example/csharp/TdExample.sln b/example/csharp/TdExample.sln index 7ebc7302..204063df 100644 --- a/example/csharp/TdExample.sln +++ b/example/csharp/TdExample.sln @@ -1,22 +1,31 @@ ๏ปฟ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2046 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TdExample", "TdExample.csproj", "{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x64.ActiveCfg = Debug|x64 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x64.Build.0 = Debug|x64 {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.ActiveCfg = Debug|x86 {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.Build.0 = Debug|x86 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x64.ActiveCfg = Release|x64 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x64.Build.0 = Release|x64 {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.ActiveCfg = Release|x86 {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {865856BA-F733-45DF-B35F-12607605B023} + EndGlobalSection EndGlobal diff --git a/example/java/CMakeLists.txt b/example/java/CMakeLists.txt index 79b1139a..edc8246a 100644 --- a/example/java/CMakeLists.txt +++ b/example/java/CMakeLists.txt @@ -21,6 +21,9 @@ message(STATUS "Found Java: ${Java_JAVAC_EXECUTABLE} ${Java_JAVADOC_EXECUTABLE}" # Generating TdApi.java find_program(PHP_EXECUTABLE php) +if ((CMAKE_SYSTEM_NAME MATCHES "FreeBSD") AND (CMAKE_SYSTEM_VERSION MATCHES "HBSD")) + set(PHP_EXECUTABLE "PHP_EXECUTABLE-NOTFOUND") +endif() set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib") set(TD_API_JAVA_PATH ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/example/python/tdjson_example.py b/example/python/tdjson_example.py index 884ab547..ac034800 100644 --- a/example/python/tdjson_example.py +++ b/example/python/tdjson_example.py @@ -112,7 +112,7 @@ while True: # set an encryption key for database to let know TDLib how to open the database if auth_state['@type'] == 'authorizationStateWaitEncryptionKey': - td_send({'@type': 'checkDatabaseEncryptionKey', 'key': 'my_key'}) + td_send({'@type': 'checkDatabaseEncryptionKey', 'encryption_key': ''}) # enter phone number to log in if auth_state['@type'] == 'authorizationStateWaitPhoneNumber': diff --git a/example/swift/src/main.swift b/example/swift/src/main.swift index 6060981b..06eaa9ce 100644 --- a/example/swift/src/main.swift +++ b/example/swift/src/main.swift @@ -124,7 +124,7 @@ func updateAuthorizationState(authorizationState: Dictionary) { ]); case "authorizationStateWaitEncryptionKey": - client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "key":"cucumber"]) + client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "encryption_key":""]) case "authorizationStateWaitPhoneNumber": print("Enter your phone number: ") diff --git a/example/uwp/README.md b/example/uwp/README.md index 866cc2d3..65952d63 100644 --- a/example/uwp/README.md +++ b/example/uwp/README.md @@ -10,7 +10,7 @@ This is an example of building TDLib SDK for Universal Windows Platform and an e * Install `zlib` and `openssl` for all UWP architectures using `vcpkg`: ``` cd -.\vcpkg.exe install openssl:arm-uwp openssl:x64-uwp openssl:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp +.\vcpkg.exe install openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp ``` * (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download#php-7.2). Add the path to php.exe to the PATH environment variable. * 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. diff --git a/example/uwp/build.ps1 b/example/uwp/build.ps1 index d24e5ece..0d84bfc8 100644 --- a/example/uwp/build.ps1 +++ b/example/uwp/build.ps1 @@ -35,7 +35,7 @@ function prepare { cd build-native - cmake $td_root -DCMAKE_TOOLCHAIN_FILE="$vcpkg_cmake" -DTD_ENABLE_DOTNET=ON + cmake "$td_root" -A Win32 -DCMAKE_TOOLCHAIN_FILE="$vcpkg_cmake" -DTD_ENABLE_DOTNET=ON CheckLastExitCode cmake --build . --target prepare_cross_compiling CheckLastExitCode @@ -92,8 +92,8 @@ function export { New-Item -ItemType Directory -Force -Path vsix/Redist/Retail/${arch} New-Item -ItemType Directory -Force -Path vsix/References/CommonConfiguration/${arch} - cp ${arch}/Debug/* -include "LIBEAY*","SSLEAY*","zlib*" vsix/Redist/Debug/${arch}/ - cp ${arch}/Release/* -include "LIBEAY*","SSLEAY*","zlib*" vsix/Redist/Retail/${arch}/ + cp ${arch}/Debug/* -include "SSLEAY*","LIBEAY*","libcrypto*","libssl*","zlib*" vsix/Redist/Debug/${arch}/ + cp ${arch}/Release/* -include "SSLEAY*","LIBEAY*","libcrypto*","libssl*","zlib*" vsix/Redist/Retail/${arch}/ cp ${arch}/Debug/* -filter "Telegram.Td.*" -include "*.lib" vsix/DesignTime/Debug/${arch}/ cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.lib" vsix/DesignTime/Retail/${arch}/ diff --git a/example/uwp/extension.vsixmanifest b/example/uwp/extension.vsixmanifest index a016a5e8..9237b3dc 100644 --- a/example/uwp/extension.vsixmanifest +++ b/example/uwp/extension.vsixmanifest @@ -1,6 +1,6 @@ - + TDLib for Universal Windows Platform TDLib is a library for building Telegram clients https://core.telegram.org/tdlib diff --git a/example/web/tdweb/package-lock.json b/example/web/tdweb/package-lock.json index 0a8b3cbb..40be006b 100644 --- a/example/web/tdweb/package-lock.json +++ b/example/web/tdweb/package-lock.json @@ -1,6 +1,6 @@ { "name": "tdweb", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1162,6 +1162,12 @@ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", "dev": true }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -1485,9 +1491,9 @@ "dev": true }, "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "bn.js": { @@ -1686,41 +1692,41 @@ "dev": true }, "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "bluebird": "^3.5.3", + "bluebird": "^3.5.5", "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", + "glob": "^7.1.4", "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", + "rimraf": "^2.6.3", "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" }, "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "yallist": "^3.0.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true } } }, @@ -1818,9 +1824,9 @@ } }, "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "chrome-trace-event": { @@ -2225,9 +2231,9 @@ } }, "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, "damerau-levenshtein": { @@ -2769,10 +2775,21 @@ } }, "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + } + } }, "eslint-visitor-keys": { "version": "1.0.0", @@ -4433,6 +4450,12 @@ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4794,6 +4817,12 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -5378,6 +5407,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -5997,12 +6035,12 @@ "dev": true }, "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "dev": true, "requires": { - "cyclist": "~0.2.2", + "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" } @@ -6676,9 +6714,9 @@ "dev": true }, "serialize-javascript": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz", - "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "set-blocking": { @@ -6934,9 +6972,9 @@ } }, "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -7074,9 +7112,9 @@ } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "string-argv": { @@ -7224,16 +7262,22 @@ "dev": true }, "terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", + "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", "dev": true, "requires": { - "commander": "^2.19.0", + "commander": "^2.20.0", "source-map": "~0.6.1", - "source-map-support": "~0.5.10" + "source-map-support": "~0.5.12" }, "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7243,51 +7287,22 @@ } }, "terser-webpack-plugin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", - "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" }, "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -7304,6 +7319,16 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } } } }, @@ -7499,9 +7524,9 @@ } }, "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -7860,9 +7885,9 @@ "dev": true }, "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, "requires": { "errno": "~0.1.7" @@ -7936,6 +7961,12 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "yargs": { "version": "12.0.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", diff --git a/example/web/tdweb/package.json b/example/web/tdweb/package.json index 584dcee8..ad2610da 100644 --- a/example/web/tdweb/package.json +++ b/example/web/tdweb/package.json @@ -1,6 +1,6 @@ { "name": "tdweb", - "version": "1.5.0", + "version": "1.6.0", "description": "Javascript interface for TDLib (telegram library)", "main": "dist/tdweb.js", "repository": { diff --git a/example/web/tdweb/src/index.js b/example/web/tdweb/src/index.js index 34a02928..b132cd0d 100644 --- a/example/web/tdweb/src/index.js +++ b/example/web/tdweb/src/index.js @@ -589,7 +589,7 @@ class FileManager { '@type': 'readFilePart', path: info.file.local.path, offset: offset, - size: size + count: size }); res.data = new Blob([res.data]); res.transaction_id = -2; @@ -632,7 +632,7 @@ class FileManager { this.lru.onUsed(info.node); } query.offset = query.offset || 0; - query.size = query.size || 0; + query.size = query.count || query.size || 0; const response = await this.doLoad(info, query.offset, query.size); return { '@type': 'filePart', diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index a3f52b6c..adbaa266 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -11,13 +11,10 @@ set(TL_TD_AUTO_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/auto PARENT_SCOPE) set(TD_AUTO_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/auto/td) -set(TL_TD_AUTO +set(TL_TD_AUTO_SOURCE ${TD_AUTO_INCLUDE_DIR}/mtproto/mtproto_api.cpp ${TD_AUTO_INCLUDE_DIR}/mtproto/mtproto_api.h ${TD_AUTO_INCLUDE_DIR}/mtproto/mtproto_api.hpp - ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.cpp - ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.h - ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.hpp ${TD_AUTO_INCLUDE_DIR}/telegram/telegram_api.cpp ${TD_AUTO_INCLUDE_DIR}/telegram/telegram_api.h ${TD_AUTO_INCLUDE_DIR}/telegram/telegram_api.hpp @@ -27,7 +24,14 @@ set(TL_TD_AUTO PARENT_SCOPE ) -set(TL_TD_JSON_AUTO +set(TL_TD_API_AUTO_SOURCE + ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.cpp + ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.h + ${TD_AUTO_INCLUDE_DIR}/telegram/td_api.hpp + PARENT_SCOPE +) + +set(TL_TD_JSON_AUTO_SOURCE ${TD_AUTO_INCLUDE_DIR}/telegram/td_api_json.cpp ${TD_AUTO_INCLUDE_DIR}/telegram/td_api_json.h PARENT_SCOPE @@ -36,14 +40,14 @@ set(TL_TD_JSON_AUTO set(TL_TD_API_TLO ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tlo) set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE) -set(TL_C_AUTO +set(TL_C_AUTO_SOURCE ${TD_AUTO_INCLUDE_DIR}/telegram/td_tdc_api.cpp ${TD_AUTO_INCLUDE_DIR}/telegram/td_tdc_api.h ${TD_AUTO_INCLUDE_DIR}/telegram/td_tdc_api_inner.h PARENT_SCOPE ) -set(TL_DOTNET_AUTO +set(TL_DOTNET_AUTO_SOURCE ${TD_AUTO_INCLUDE_DIR}/telegram/TdDotNetApi.cpp ${TD_AUTO_INCLUDE_DIR}/telegram/TdDotNetApi.h PARENT_SCOPE @@ -92,6 +96,10 @@ set(TL_GENERATE_JSON_SOURCE if (NOT CMAKE_CROSSCOMPILING) find_program(PHP_EXECUTABLE php) + if ((CMAKE_SYSTEM_NAME MATCHES "FreeBSD") AND (CMAKE_SYSTEM_VERSION MATCHES "HBSD")) + set(PHP_EXECUTABLE "PHP_EXECUTABLE-NOTFOUND") + endif() + if (PHP_EXECUTABLE AND NOT TD_ENABLE_DOTNET) set(GENERATE_COMMON_CMD generate_common && ${PHP_EXECUTABLE} DoxygenTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/td_api.h) else() diff --git a/td/generate/DotnetTlDocumentationGenerator.php b/td/generate/DotnetTlDocumentationGenerator.php index d32492c8..674cbe13 100644 --- a/td/generate/DotnetTlDocumentationGenerator.php +++ b/td/generate/DotnetTlDocumentationGenerator.php @@ -54,7 +54,7 @@ class DotnetTlDocumentationGenerator extends TlDocumentationGenerator case 'string': return 'String^'; case 'bytes': - return 'Array^'; + return 'Array^'; case 'bool': case 'int': case 'long': @@ -210,7 +210,7 @@ EOT while (substr($field_type, $pos, 6) === 'Array<') { $pos += 6; } - if (substr($field_type, $pos, 6) !== 'String' && ucfirst(substr($field_type, $pos)) === substr($field_type, $pos)) { + if (substr($field_type, $pos, 4) !== 'BYTE' && substr($field_type, $pos, 6) !== 'String' && ucfirst(substr($field_type, $pos)) === substr($field_type, $pos)) { $field_type = substr($field_type, 0, $pos).'::Telegram::Td::Api::'.substr($field_type, $pos); } $full_constructor .= $colon.$field_type.' '.$this->getParameterName($name, $class_name); diff --git a/td/generate/DoxygenTlDocumentationGenerator.php b/td/generate/DoxygenTlDocumentationGenerator.php index afe94559..641c1a12 100644 --- a/td/generate/DoxygenTlDocumentationGenerator.php +++ b/td/generate/DoxygenTlDocumentationGenerator.php @@ -10,15 +10,17 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator case 'Bool': return 'bool '; case 'int32': - return 'std::int32_t '; + return 'int32 '; case 'int53': + return 'int53 '; case 'int64': - return 'std::int64_t '; + return 'int64 '; case 'double': return 'double '; case 'string': + return 'string const &'; case 'bytes': - return 'std::string const &'; + return 'bytes const &'; default: if (substr($type, 0, 6) === 'vector') { @@ -67,15 +69,17 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator case 'Bool': return 'bool'; case 'int32': - return 'std::int32_t'; + return 'int32'; case 'int53': + return 'int53'; case 'int64': - return 'std::int64_t'; + return 'int64'; case 'double': return 'double'; case 'string': + return 'string'; case 'bytes': - return 'std::string'; + return 'bytes'; case 'bool': case 'int': case 'long': @@ -163,6 +167,41 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator * Contains declarations of all functions and types which represent a public TDLib interface. */ EOT +); + + $this->addDocumentation('using int32 = std::int32_t;', <<addDocumentation('using int53 = std::int64_t;', <<addDocumentation('using int64 = std::int64_t;', <<addDocumentation('using string = std::string;', <<addDocumentation('using bytes = std::string;', <<addDocumentation('using BaseObject', << is_public:Bool = ChatInviteLinkInfo; @@ -843,8 +846,14 @@ richTextPhoneNumber text:RichText phone_number:string = RichText; //@height Height of a bounding box in which the image should be shown; 0 if unknown richTextIcon document:document width:int32 height:int32 = RichText; -//@description A rich text anchor @text Text @name Anchor name -richTextAnchor text:RichText name:string = RichText; +//@description A rich text reference of a text on the same web page @text The text @reference_text The text to show on click @url An HTTP URL, opening the reference +richTextReference text:RichText reference_text:RichText url:string = RichText; + +//@description An anchor @name Anchor name +richTextAnchor name:string = RichText; + +//@description A link to an anchor on the same web page @text The link text @name The anchor name. If the name is empty, the link should bring back to top @url An HTTP URL, opening the anchor +richTextAnchorLink text:RichText name:string url:string = RichText; //@description A concatenation of rich texts @texts Texts richTexts texts:vector = RichText; @@ -978,12 +987,13 @@ pageBlockRelatedArticles header:RichText articles:vector version:int32 url:string is_rtl:Bool is_full:Bool = WebPageInstantView; +webPageInstantView page_blocks:vector view_count:int32 version:int32 is_rtl:Bool is_full:Bool = WebPageInstantView; //@description Describes a web page preview @@ -1008,7 +1018,14 @@ webPageInstantView page_blocks:vector version:int32 url:string is_rtl //@video_note Preview of the content as a video note, if available; may be null //@voice_note Preview of the content as a voice note, if available; may be null //@instant_view_version Version of instant view, available for the web page (currently can be 1 or 2), 0 if none -webPage url:string display_url:string type:string site_name:string title:string description:string photo:photo embed_url:string embed_type:string embed_width:int32 embed_height:int32 duration:int32 author:string animation:animation audio:audio document:document sticker:sticker video:video video_note:videoNote voice_note:voiceNote instant_view_version:int32 = WebPage; +webPage url:string display_url:string type:string site_name:string title:string description:formattedText photo:photo embed_url:string embed_type:string embed_width:int32 embed_height:int32 duration:int32 author:string animation:animation audio:audio document:document sticker:sticker video:video video_note:videoNote voice_note:voiceNote instant_view_version:int32 = WebPage; + + +//@description Describes an action associated with a bank card number @text Action text @url The URL to be opened +bankCardActionOpenUrl text:string url:string = BankCardActionOpenUrl; + +//@description Information about a bank card @title Title of the bank card description @actions Actions that can be done with the bank card number +bankCardInfo title:string actions:vector = BankCardInfo; //@description Describes an address @country_code A two-letter ISO 3166-1 alpha-2 country code @state State, if applicable @city City @street_line1 First line of the address @street_line2 Second line of the address @postal_code Address postal code @@ -1360,6 +1377,9 @@ messageVenue venue:venue = MessageContent; //@description A message with a user contact @contact The contact description messageContact contact:contact = MessageContent; +//@description A dice message. The dice value is randomly generated by the server @value The dice value; 0-6. If the value is 0, the dice must roll infinitely +messageDice value:int32 = MessageContent; + //@description A message with a game @game The game description messageGame game:game = MessageContent; @@ -1466,6 +1486,9 @@ textEntityTypeEmailAddress = TextEntityType; //@description A phone number textEntityTypePhoneNumber = TextEntityType; +//@description A bank card number. The getBankCardInfo method can be used to get information about the bank card +textEntityTypeBankCardNumber = TextEntityType; + //@description A bold text textEntityTypeBold = TextEntityType; @@ -1494,7 +1517,7 @@ textEntityTypeTextUrl url:string = TextEntityType; textEntityTypeMentionName user_id:int32 = TextEntityType; -//@description A thumbnail to be sent along with a file; should be in JPEG or WEBP format for stickers, and less than 200 kB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported +//@description A thumbnail to be sent along with a file; should be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported //@width Thumbnail width, usually shouldn't exceed 320. Use 0 if unknown @height Thumbnail height, usually shouldn't exceed 320. Use 0 if unknown inputThumbnail thumbnail:InputFile width:int32 height:int32 = InputThumbnail; @@ -1558,6 +1581,9 @@ inputMessageVenue venue:venue = InputMessageContent; //@description A message containing a user contact @contact Contact to send inputMessageContact contact:contact = InputMessageContent; +//@description A dice message +inputMessageDice = InputMessageContent; + //@description A message with a game; not supported for channels or secret chats @bot_user_id User identifier of the bot that owns the game @game_short_name Short name of the game inputMessageGame bot_user_id:int32 game_short_name:string = InputMessageContent; @@ -1720,8 +1746,13 @@ callDiscardReasonDisconnected = CallDiscardReason; callDiscardReasonHungUp = CallDiscardReason; -//@description Specifies the supported call protocols @udp_p2p True, if UDP peer-to-peer connections are supported @udp_reflector True, if connection through UDP reflectors is supported @min_layer The minimum supported API layer; use 65 @max_layer The maximum supported API layer; use 65 -callProtocol udp_p2p:Bool udp_reflector:Bool min_layer:int32 max_layer:int32 = CallProtocol; +//@description Specifies the supported call protocols +//@udp_p2p True, if UDP peer-to-peer connections are supported +//@udp_reflector True, if connection through UDP reflectors is supported +//@min_layer The minimum supported API layer; use 65 +//@max_layer The maximum supported API layer; use 65 +//@library_versions List of supported libtgvoip versions +callProtocol udp_p2p:Bool udp_reflector:Bool min_layer:int32 max_layer:int32 library_versions:vector = CallProtocol; //@description Describes the address of UDP reflectors @id Reflector identifier @ip IPv4 reflector address @ipv6 IPv6 reflector address @port Reflector port number @peer_tag Connection peer tag callConnection id:int64 ip:string ipv6:string port:int32 peer_tag:bytes = CallConnection; @@ -2291,8 +2322,11 @@ notificationTypeNewCall call_id:int32 = NotificationType; //@description New message was received through a push notification //@message_id The message identifier. The message will not be available in the chat history, but the ID can be used in viewMessages and as reply_to_message_id -//@sender_user_id Sender of the message. Corresponding user may be inaccessible @content Push message content -notificationTypeNewPushMessage message_id:int53 sender_user_id:int32 content:PushMessageContent = NotificationType; +//@sender_user_id Sender of the message; 0 if unknown. Corresponding user may be inaccessible +//@sender_name Name of the sender; can be different from the name of the sender user +//@is_outgoing True, if the message is outgoing +//@content Push message content +notificationTypeNewPushMessage message_id:int53 sender_user_id:int32 sender_name:string is_outgoing:Bool content:PushMessageContent = NotificationType; //@class NotificationGroupType @description Describes the type of notifications in a notification group @@ -2687,11 +2721,11 @@ deepLinkInfo text:formattedText need_update_application:Bool = DeepLinkInfo; //@class TextParseMode @description Describes the way the text should be parsed for TextEntities -//@description The text should be parsed in markdown-style -//@version Version of the parser: 0 or 1 - Bot API Markdown parse mode, 2 - Bot API MarkdownV2 parse mode +//@description The text uses Markdown-style formatting +//@version Version of the parser: 0 or 1 - Telegram Bot API "Markdown" parse mode, 2 - Telegram Bot API "MarkdownV2" parse mode textParseModeMarkdown version:int32 = TextParseMode; -//@description The text should be parsed in HTML-style +//@description The text uses HTML-style formatting. The same as Telegram Bot API "HTML" parse mode textParseModeHTML = TextParseMode; @@ -2700,7 +2734,7 @@ textParseModeHTML = TextParseMode; //@description A SOCKS5 proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty proxyTypeSocks5 username:string password:string = ProxyType; -//@description A HTTP transparent proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty @http_only Pass true, if the proxy supports only HTTP requests and doesn't support transparent TCP connections via HTTP CONNECT method +//@description A HTTP transparent proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty @http_only Pass true if the proxy supports only HTTP requests and doesn't support transparent TCP connections via HTTP CONNECT method proxyTypeHttp username:string password:string http_only:Bool = ProxyType; //@description An MTProto proxy server @secret The proxy's secret in hexadecimal encoding @@ -2714,8 +2748,64 @@ proxy id:int32 server:string port:int32 last_used_date:int32 is_enabled:Bool typ proxies proxies:vector = Proxies; -//@description Describes a sticker that should be added to a sticker set @png_sticker PNG image with the sticker; must be up to 512 kB in size and fit in a 512x512 square @emojis Emoji corresponding to the sticker @mask_position For masks, position where the mask should be placed; may be null -inputSticker png_sticker:InputFile emojis:string mask_position:maskPosition = InputSticker; +//@class InputSticker @description Describes a sticker that needs to be added to a sticker set + +//@description A static sticker in PNG format, which will be converted to WEBP server-side +//@sticker PNG image with the sticker; must be up to 512 KB in size and fit in a 512x512 square +//@emojis Emojis corresponding to the sticker +//@mask_position For masks, position where the mask should be placed; may be null +inputStickerStatic sticker:InputFile emojis:string mask_position:maskPosition = InputSticker; + +//@description An animated sticker in TGS format +//@sticker File with the animated sticker. Only local or uploaded within a week files are supported. See https://core.telegram.org/animated_stickers#technical-requirements for technical requirements +//@emojis Emojis corresponding to the sticker +inputStickerAnimated sticker:InputFile emojis:string = InputSticker; + + +//@description Represents a date range @start_date Point in time (Unix timestamp) at which the date range begins @end_date Point in time (Unix timestamp) at which the date range ends +dateRange start_date:int32 end_date:int32 = DateRange; + + +//@description A statistics value @value The value @previous_value The value for the previous day @growth_rate_percentage The growth rate of the value, as a percentage +statisticsValue value:double previous_value:double growth_rate_percentage:double = StatisticsValue; + + +//@class StatisticsGraph @description Descrbes a statistics graph + +//@description A graph data @json_data Graph data in JSON format @zoom_token If non-empty, a token which can be used to receive a zoomed in graph +statisticsGraphData json_data:string zoom_token:string = StatisticsGraph; + +//@description The graph data to be asynchronously loaded through getChatStatisticsGraph @token The token to use for data loading +statisticsGraphAsync token:string = StatisticsGraph; + +//@description An error message to be shown to the user instead of the graph @error_message The error message +statisticsGraphError error_message:string = StatisticsGraph; + + +//@description Contains statistics about interactions with a message +//@message_id Message identifier +//@view_count Number of times the message was viewed +//@forward_count Number of times the message was forwarded +chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionCounters; + + +//@description A detailed statistics about a chat +//@period A period to which the statistics applies +//@member_count Number of members in the chat +//@mean_view_count Mean number of times the recently sent messages was viewed +//@mean_share_count Mean number of times the recently sent messages was shared +//@enabled_notifications_percentage A percentage of users with enabled notifications for the chat +//@member_count_graph A graph containing number of members in the chat +//@join_graph A graph containing number of members joined and left the chat +//@mute_graph A graph containing number of members muted and unmuted the chat +//@view_count_by_hour_graph A graph containing number of message views in a given hour in the last two weeks +//@view_count_by_source_graph A graph containing number of message views per source +//@join_by_source_graph A graph containing number of new member joins per source +//@language_graph A graph containing number of users viewed chat messages per language +//@message_interaction_graph A graph containing number of chat message views and shares +//@instant_view_interaction_graph A graph containing number of views of associated with the chat instant views +//@recent_message_interactions Detailed statistics about number of views and shares of recently sent messages +chatStatistics period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector = ChatStatistics; //@class Update @description Contains notifications about data changes @@ -3400,12 +3490,19 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup = editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok; -//@description Returns all entities (mentions, hashtags, cashtags, bot commands, URLs, and email addresses) contained in the text. This is an offline method. Can be called before authorization. Can be called synchronously @text The text in which to look for entites +//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) contained in the text. This is an offline method. Can be called before authorization. Can be called synchronously @text The text in which to look for entites getTextEntities text:string = TextEntities; -//@description Parses Bold, Italic, Underline, Strikethrough, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. This is an offline method. Can be called before authorization. Can be called synchronously @text The text which should be parsed @parse_mode Text parse mode +//@description Parses Bold, Italic, Underline, Strikethrough, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. This is an offline method. Can be called before authorization. Can be called synchronously @text The text to parse @parse_mode Text parse mode parseTextEntities text:string parse_mode:TextParseMode = FormattedText; +//@description Parses Markdown entities in a human-friendly format, ignoring mark up errors. This is an offline method. Can be called before authorization. Can be called synchronously +//@text The text to parse. For example, "__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) __italic**bold italic__bold**" +parseMarkdown text:formattedText = FormattedText; + +//@description Replaces text entities with Markdown formatting in a human-friendly format. Entities that can't be represented in Markdown unambiguously are kept as is. This is an offline method. Can be called before authorization. Can be called synchronously @text The text +getMarkdownText text:formattedText = FormattedText; + //@description Returns the MIME type of a file, guessed by its extension. Returns an empty string on failure. This is an offline method. Can be called before authorization. Can be called synchronously @file_name The name of the file or path to the file getFileMimeType file_name:string = Text; @@ -3545,7 +3642,7 @@ createNewSecretChat user_id:int32 = Chat; upgradeBasicGroupChatToSupergroupChat chat_id:int53 = Chat; -//@description Moves a chat to a different chat list. Current chat list of the chat must ne non-null @chat_id Chat identifier @chat_list New chat list of the chat +//@description Moves a chat to a different chat list. Current chat list of the chat must ne non-null @chat_id Chat identifier @chat_list New chat list of the chat. The chat with the current user (Saved Messages) and the chat 777000 (Telegram) can't be moved to the Archive chat list setChatChatList chat_id:int53 chat_list:ChatList = Ok; //@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info rights. The title will not be changed until the request to the server has been completed @@ -3891,6 +3988,9 @@ setBio bio:string = Ok; //@description Changes the username of the current user. If something changes, updateUser will be sent @username The new value of the username. Use an empty string to remove the username setUsername username:string = Ok; +//@description Changes the location of the current user. Needs to be called if GetOption("is_location_visible") is true and location changes for more than 1 kilometer @location The new location of the user +setLocation location:location = Ok; + //@description Changes the phone number of the user and sends an authentication code to the user's new phone number. On success, returns information about the sent code //@phone_number The new phone number of the user in international format @settings Settings for the authentication of the user's phone number changePhoneNumber phone_number:string settings:phoneNumberAuthenticationSettings = AuthenticationCodeInfo; @@ -3901,6 +4001,9 @@ resendChangePhoneNumberCode = AuthenticationCodeInfo; //@description Checks the authentication code sent to confirm a new phone number of the user @code Verification code received by SMS, phone call or flash call checkChangePhoneNumberCode code:string = Ok; +//@description Sets the list of commands supported by the bot; for bots only @commands List of the bot's commands +setCommands commands:vector = Ok; + //@description Returns all active sessions of the current user getActiveSessions = Sessions; @@ -4076,13 +4179,19 @@ deleteAccount reason:string = Ok; //@description Removes a chat action bar without any other action @chat_id Chat identifier removeChatActionBar chat_id:int53 = Ok; -//@description Reports a chat to the Telegram moderators. Supported only for supergroups, channels, or private chats with bots, since other chats can't be checked by moderators, or when the report is done from the chat action bar @chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any +//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if this is a private chats with a bot, a private chat with a user sharing their location, a supergroup, or a channel, since other chats can't be checked by moderators @chat_id Chat identifier @reason The reason for reporting the chat @message_ids Identifiers of reported messages, if any reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; -//@description Returns an HTTP URL with the chat statistics. Currently this method can be used only for channels. Can be used only if SupergroupFullInfo.can_view_statistics == true @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned +//@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics is disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl; +//@description Returns detailed statistics about a chat. Currently this method can be used only for channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app +getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics; + +//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifer @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise +getChatStatisticsGraph chat_id:int53 token:string x:int53 = StatisticsGraph; + //@description Returns storage usage statistics. Can be called before authorization @chat_limit The maximum number of chats with the largest storage usage for which separate statistics should be returned. All other chats will be grouped in entries with chat_id == 0. If the chat info database is not used, the chat_limit is ignored and is always set to 0 getStorageStatistics chat_limit:int32 = StorageStatistics; @@ -4101,8 +4210,9 @@ getDatabaseStatistics = DatabaseStatistics; //@file_types If not empty, only files with the given type(s) are considered. By default, all types except thumbnails, profile photos, stickers and wallpapers are deleted //@chat_ids If not empty, only files from the given chats are considered. Use 0 as chat identifier to delete files not belonging to any chat (e.g., profile photos) //@exclude_chat_ids If not empty, files from the given chats are excluded. Use 0 as chat identifier to exclude all files not belonging to any chat (e.g., profile photos) +//@return_deleted_file_statistics Pass true if deleted file statistics needs to be returned instead of the whole storage usage statistics. Affects only returned statistics //@chat_limit Same as in getStorageStatistics. Affects only returned statistics -optimizeStorage size:int53 ttl:int32 count:int32 immunity_delay:int32 file_types:vector chat_ids:vector exclude_chat_ids:vector chat_limit:int32 = StorageStatistics; +optimizeStorage size:int53 ttl:int32 count:int32 immunity_delay:int32 file_types:vector chat_ids:vector exclude_chat_ids:vector return_deleted_file_statistics:Bool chat_limit:int32 = StorageStatistics; //@description Sets the current network type. Can be called before authorization. Calling this method forces all network connections to reopen, mitigating the delay in switching between different networks, so it should be called whenever the network is changed, even if the network type remains the same. @@ -4125,6 +4235,10 @@ getAutoDownloadSettingsPresets = AutoDownloadSettingsPresets; setAutoDownloadSettings settings:autoDownloadSettings type:NetworkType = Ok; +//@description Returns information about a bank card @bank_card_number The bank card number +getBankCardInfo bank_card_number:string = BankCardInfo; + + //@description Returns one of the available Telegram Passport elements @type Telegram Passport element type @password Password of the current user getPassportElement type:PassportElementType password:string = PassportElement; @@ -4192,17 +4306,29 @@ checkPhoneNumberConfirmationCode code:string = Ok; setBotUpdatesStatus pending_update_count:int32 error_message:string = Ok; -//@description Uploads a PNG image with a sticker; for bots only; returns the uploaded file @user_id Sticker file owner @png_sticker PNG image with the sticker; must be up to 512 kB in size and fit in 512x512 square +//@description Uploads a PNG image with a sticker; for bots only; returns the uploaded file +//@user_id Sticker file owner @png_sticker PNG image with the sticker; must be up to 512 KB in size and fit in 512x512 square uploadStickerFile user_id:int32 png_sticker:InputFile = File; -//@description Creates a new sticker set; for bots only. Returns the newly created sticker set @user_id Sticker set owner @title Sticker set title; 1-64 characters @name Sticker set name. Can contain only English letters, digits and underscores. Must end with *"_by_"* (** is case insensitive); 1-64 characters -//@is_masks True, if stickers are masks @stickers List of stickers to be added to the set -createNewStickerSet user_id:int32 title:string name:string is_masks:Bool stickers:vector = StickerSet; +//@description Creates a new sticker set; for bots only. Returns the newly created sticker set +//@user_id Sticker set owner +//@title Sticker set title; 1-64 characters +//@name Sticker set name. Can contain only English letters, digits and underscores. Must end with *"_by_"* (** is case insensitive); 1-64 characters +//@is_masks True, if stickers are masks. Animated stickers can't be masks +//@stickers List of stickers to be added to the set; must be non-empty. All stickers must be of the same type +createNewStickerSet user_id:int32 title:string name:string is_masks:Bool stickers:vector = StickerSet; -//@description Adds a new sticker to a set; for bots only. Returns the sticker set @user_id Sticker set owner @name Sticker set name @sticker Sticker to add to the set -addStickerToSet user_id:int32 name:string sticker:inputSticker = StickerSet; +//@description Adds a new sticker to a set; for bots only. Returns the sticker set +//@user_id Sticker set owner @name Sticker set name @sticker Sticker to add to the set +addStickerToSet user_id:int32 name:string sticker:InputSticker = StickerSet; -//@description Changes the position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot @sticker Sticker @position New position of the sticker in the set, zero-based +//@description Sets a sticker set thumbnail; for bots only. Returns the sticker set +//@user_id Sticker set owner @name Sticker set name +//@thumbnail Thumbnail to set in PNG or TGS format. Animated thumbnail must be set for animated sticker sets and only for them. You can use a zero InputFileId to delete the thumbnail +setStickerSetThumbnail user_id:int32 name:string thumbnail:InputFile = StickerSet; + +//@description Changes the position of a sticker in the set to which it belongs; for bots only. The sticker set must have been created by the bot +//@sticker Sticker @position New position of the sticker in the set, zero-based setStickerPositionInSet sticker:InputFile position:int32 = Ok; //@description Removes a sticker from the set to which it belongs; for bots only. The sticker set must have been created by the bot @sticker Sticker diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 3f14d9d0..fcd68ea2 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 27512a50..5318ee34 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -59,6 +59,7 @@ inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia; inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector = InputMedia; +inputMediaDice#aeffa807 = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto; @@ -115,7 +116,7 @@ channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags. channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull; -channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull; +channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -143,6 +144,7 @@ messageMediaGame#fdb19008 game:Game = MessageMedia; messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia; messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia; messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; +messageMediaDice#638fe46b value:int = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction; @@ -339,6 +341,9 @@ updateTheme#8216fba3 theme:Theme = Update; updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update; updateLoginToken#564fe691 = Update; updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector = Update; +updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update; +updateDialogFilterOrder#a5d72105 order:Vector = Update; +updateDialogFilters#3504914f = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -489,7 +494,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess webPageEmpty#eb1477e8 id:long = WebPage; webPagePending#c586da1c id:long date:int = WebPage; webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector = WebPage; -webPageNotModified#85849473 = WebPage; +webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage; authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; @@ -515,6 +520,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet; +inputStickerSetDice#79e21a53 = InputStickerSet; stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet; @@ -561,6 +567,7 @@ messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity; messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity; messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity; messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity; +messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity; inputChannelEmpty#ee8c1e86 = InputChannel; inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; @@ -809,7 +816,7 @@ phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3? phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; -phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int = PhoneCallProtocol; +phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector = PhoneCallProtocol; phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector = phone.PhoneCall; @@ -995,7 +1002,7 @@ pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector = PageLis pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle; -page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector = Page; +page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector views:flags.3?int = Page; help.supportName#8c05f1c9 name:string = help.SupportName; @@ -1060,6 +1067,7 @@ channelLocationEmpty#bfb5ad8b = ChannelLocation; channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation; peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated; +peerSelfLocated#f8ec284b expires:int = PeerLocated; restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason; @@ -1097,6 +1105,28 @@ messageUserVoteMultiple#e8fe0de user_id:int options:Vector date:int = Mes messages.votesList#823f649 flags:# count:int votes:Vector users:Vector next_offset:flags.0?string = messages.VotesList; +bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl; + +payments.bankCardData#3e24e573 title:string open_urls:Vector = payments.BankCardData; + +dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector include_peers:Vector exclude_peers:Vector = DialogFilter; + +dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested; + +statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays; + +statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev; + +statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue; + +statsGraphAsync#4a27eb2d token:string = StatsGraph; +statsGraphError#bedc9822 error:string = StatsGraph; +statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph; + +messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters; + +stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector = stats.BroadcastStats; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1214,7 +1244,7 @@ contacts.getSaved#82f1e39f = Vector; contacts.toggleTopPeers#8514bdda enabled:Bool = Bool; contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates; contacts.acceptContact#f831a20f id:InputUser = Updates; -contacts.getLocated#a356056 geo_point:InputGeoPoint = Updates; +contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates; messages.getMessages#63c66506 id:Vector = messages.Messages; messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs; @@ -1335,6 +1365,10 @@ messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates; messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates; messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList; messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector = Bool; +messages.getDialogFilters#f19ed96d = Vector; +messages.getSuggestedDialogFilters#a29cd42c = Vector; +messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool; +messages.updateDialogFiltersOrder#c563c1e4 order:Vector = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1412,6 +1446,7 @@ channels.getInactiveChannels#11e831ee = messages.InactiveChats; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; +bots.setBotCommands#805d46f6 commands:Vector = Bool; payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm; payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt; @@ -1419,11 +1454,13 @@ payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int inf payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult; payments.getSavedInfo#227d824b = payments.SavedInfo; payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool; +payments.getBankCardData#2e79d779 number:string = payments.BankCardData; -stickers.createStickerSet#9bd86e6a flags:# masks:flags.0?true user_id:InputUser title:string short_name:string stickers:Vector = messages.StickerSet; +stickers.createStickerSet#f1036780 flags:# masks:flags.0?true animated:flags.1?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet; stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet; +stickers.setStickerSetThumb#9a364e30 stickerset:InputStickerSet thumb:InputDocument = messages.StickerSet; phone.getCallConfig#55451fa9 = DataJSON; phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; @@ -1442,3 +1479,6 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates; folders.deleteFolder#1c295881 folder_id:int = Updates; + +stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats; +stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 0160f9a6..89a597f6 100644 Binary files a/td/generate/scheme/telegram_api.tlo and b/td/generate/scheme/telegram_api.tlo differ diff --git a/td/generate/tl_writer_cpp.cpp b/td/generate/tl_writer_cpp.cpp index 2d77c0d0..bfaeb2eb 100644 --- a/td/generate/tl_writer_cpp.cpp +++ b/td/generate/tl_writer_cpp.cpp @@ -147,10 +147,10 @@ std::string TD_TL_writer_cpp::gen_fetch_class_name(const tl::tl_tree_type *tree_ return "TlFetch" + name; } if (name == "String") { - return "TlFetchString<" + string_type + ">"; + return "TlFetchString"; } if (name == "Bytes") { - return "TlFetchBytes<" + bytes_type + ">"; + return "TlFetchBytes"; } if (name == "Vector") { @@ -662,7 +662,7 @@ std::string TD_TL_writer_cpp::gen_constructor_field_init(int field_num, const st } std::string move_begin; std::string move_end; - if ((field_type == bytes_type || field_type.compare(0, 11, "std::vector") == 0 || + if ((field_type == "bytes" || field_type.compare(0, 11, "std::vector") == 0 || field_type.compare(0, 10, "object_ptr") == 0) && !is_default) { move_begin = "std::move("; diff --git a/td/generate/tl_writer_dotnet.h b/td/generate/tl_writer_dotnet.h index 276f4ba3..01295805 100644 --- a/td/generate/tl_writer_dotnet.h +++ b/td/generate/tl_writer_dotnet.h @@ -158,7 +158,7 @@ class TlWriterDotNet : public TL_writer { return "String^"; } if (name == "Bytes") { - return "Array^"; + return "Array^"; } if (name == "Vector") { @@ -288,7 +288,8 @@ class TlWriterDotNet : public TL_writer { while (field_type.substr(pos, 6) == "Array<") { pos += 6; } - if (field_type.substr(pos, 6) != "String" && to_upper(field_type[pos]) == field_type[pos]) { + if (field_type.substr(pos, 4) != "BYTE" && field_type.substr(pos, 6) != "String" && + to_upper(field_type[pos]) == field_type[pos]) { field_type = field_type.substr(0, pos) + "::Telegram::Td::Api::" + field_type.substr(pos); } ss << field_type << " " << to_camelCase(a.name); @@ -383,7 +384,7 @@ class TlWriterDotNet : public TL_writer { } else { ss << ", "; } - bool need_bytes = gen_field_type(it) == "Array^" || gen_field_type(it) == "Array^>^"; + bool need_bytes = gen_field_type(it) == "Array^" || gen_field_type(it) == "Array^>^"; ss << (need_bytes ? "Bytes" : "") << "FromUnmanaged(from." << gen_native_field_name(it.name) << ")"; } ss << ");\n}\n"; diff --git a/td/generate/tl_writer_h.cpp b/td/generate/tl_writer_h.cpp index 82880695..2404d7f6 100644 --- a/td/generate/tl_writer_h.cpp +++ b/td/generate/tl_writer_h.cpp @@ -56,6 +56,18 @@ std::string TD_TL_writer_h::gen_output_begin() const { ext_forward_declaration + "namespace " + tl_name + " {\n\n" + "using int32 = std::int32_t;\n" + "using int53 = std::int64_t;\n" + "using int64 = std::int64_t;\n\n" + + "using string = " + + string_type + + ";\n\n" + + "using bytes = " + + bytes_type + + ";\n\n" + "using BaseObject = ::td::TlObject;\n\n" "template \n" diff --git a/td/generate/tl_writer_jni_cpp.cpp b/td/generate/tl_writer_jni_cpp.cpp index cc7bee25..645903a6 100644 --- a/td/generate/tl_writer_jni_cpp.cpp +++ b/td/generate/tl_writer_jni_cpp.cpp @@ -83,10 +83,10 @@ std::string TD_TL_writer_jni_cpp::gen_vector_fetch(std::string field_name, const std::string fetch_object = "jni::fetch_object(env, p, " + field_name + "fieldID)"; std::string array_type; - if (vector_type == "std::int32_t") { + if (vector_type == "int32") { array_type = "jintArray"; } - if (vector_type == "std::int64_t") { + if (vector_type == "int53" || vector_type == "int64") { array_type = "jlongArray"; } if (vector_type == "double") { @@ -94,12 +94,12 @@ std::string TD_TL_writer_jni_cpp::gen_vector_fetch(std::string field_name, const } if (!array_type.empty()) { - return "jni::fetch_vector(env, (" + array_type + ")" + fetch_object + ");"; + return "jni::fetch_vector(env, (" + array_type + ")" + fetch_object + ")"; } std::string template_type; - if (vector_type == string_type) { - template_type = "std::string"; + if (vector_type == "string") { + template_type = "string"; } else if (vector_type.compare(0, 11, "std::vector") == 0) { const tl::tl_tree_type *child = static_cast(t->children[0]); template_type = gen_type_name(child); @@ -107,14 +107,13 @@ std::string TD_TL_writer_jni_cpp::gen_vector_fetch(std::string field_name, const template_type = gen_main_class_name(child->type); } template_type = "std::vector<" + template_type + ">"; - } else if (vector_type == bytes_type) { - std::fprintf(stderr, "Vector of Bytes is not supported\n"); - assert(false); + } else if (vector_type == "bytes") { + template_type = "jbyteArray"; } else { assert(vector_type.compare(0, 10, "object_ptr") == 0); template_type = gen_main_class_name(t->type); } - return "jni::FetchVector<" + template_type + ">::fetch(env, (jobjectArray)" + fetch_object + ");"; + return "jni::FetchVector<" + template_type + ">::fetch(env, (jobjectArray)" + fetch_object + ")"; } std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type, @@ -179,7 +178,7 @@ std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name, return gen_main_class_name(tree_type->type) + "::fetch(env, p)"; } res = "jni::fetch_tl_object<" + gen_main_class_name(tree_type->type) + ">(env, jni::fetch_object(env, p, " + - field_name + "fieldID));"; + field_name + "fieldID))"; } return res_begin + res; } @@ -248,8 +247,8 @@ std::string TD_TL_writer_jni_cpp::gen_vector_store(const std::string &field_name if (vector_type == "bool") { assert(false); // TODO } - if (vector_type == "std::int32_t" || vector_type == "std::int64_t" || vector_type == "double" || - vector_type == string_type || vector_type.compare(0, 11, "std::vector") == 0 || + if (vector_type == "int32" || vector_type == "int53" || vector_type == "int64" || vector_type == "double" || + vector_type == "string" || vector_type.compare(0, 11, "std::vector") == 0 || vector_type.compare(0, 10, "object_ptr") == 0) { return "{ " "auto arr_tmp_ = jni::store_vector(env, " + @@ -262,7 +261,7 @@ std::string TD_TL_writer_jni_cpp::gen_vector_store(const std::string &field_name "env->DeleteLocalRef(arr_tmp_); " "} }"; } - if (vector_type == bytes_type) { + if (vector_type == "bytes") { std::fprintf(stderr, "Vector of Bytes is not supported\n"); assert(false); } diff --git a/td/generate/tl_writer_jni_h.cpp b/td/generate/tl_writer_jni_h.cpp index 0a56d298..d050edae 100644 --- a/td/generate/tl_writer_jni_h.cpp +++ b/td/generate/tl_writer_jni_h.cpp @@ -79,6 +79,18 @@ std::string TD_TL_writer_jni_h::gen_output_begin() const { tl_name + " {\n\n" + "using int32 = std::int32_t;\n" + "using int53 = std::int64_t;\n" + "using int64 = std::int64_t;\n\n" + + "using string = " + + string_type + + ";\n\n" + + "using bytes = " + + bytes_type + + ";\n\n" + "class " + gen_base_tl_class_name() + ";\n" diff --git a/td/generate/tl_writer_td.cpp b/td/generate/tl_writer_td.cpp index 0b2f64ff..1a9cdb43 100644 --- a/td/generate/tl_writer_td.cpp +++ b/td/generate/tl_writer_td.cpp @@ -121,7 +121,7 @@ std::string TD_TL_writer::gen_class_name(std::string name) const { assert(false); } if (name == "#") { - return "std::int32_t"; + return "int32"; } for (std::size_t i = 0; i < name.size(); i++) { if (!is_alnum(name[i])) { @@ -161,7 +161,7 @@ std::string TD_TL_writer::gen_type_name(const tl::tl_tree_type *tree_type) const const std::string &name = t->name; if (name == "#") { - return "std::int32_t"; + return "int32"; } if (name == "True") { return "bool"; @@ -170,16 +170,19 @@ std::string TD_TL_writer::gen_type_name(const tl::tl_tree_type *tree_type) const return "bool"; } if (name == "Int" || name == "Int32") { - return "std::int32_t"; + return "int32"; } - if (name == "Long" || name == "Int53" || name == "Int64") { - return "std::int64_t"; + if (name == "Int53") { + return "int53"; + } + if (name == "Long" || name == "Int64") { + return "int64"; } if (name == "Double") { return "double"; } if (name == "String") { - return string_type; + return "string"; } if (name == "Int128") { return "UInt128"; @@ -188,7 +191,7 @@ std::string TD_TL_writer::gen_type_name(const tl::tl_tree_type *tree_type) const return "UInt256"; } if (name == "Bytes") { - return bytes_type; + return "bytes"; } if (name == "Vector") { @@ -239,12 +242,13 @@ std::string TD_TL_writer::gen_constructor_parameter(int field_num, const std::st } std::string res = (field_num == 0 ? "" : ", "); - if (field_type == "bool " || field_type == "std::int32_t " || field_type == "std::int64_t " || + if (field_type == "bool " || field_type == "int32 " || field_type == "int53 " || field_type == "int64 " || field_type == "double ") { res += field_type; - } else if (field_type == "UInt128 " || field_type == "UInt256 " || field_type == string_type + " ") { + } else if (field_type == "UInt128 " || field_type == "UInt256 " || field_type == "string " || + (string_type == bytes_type && field_type == "bytes ")) { res += field_type + "const &"; - } else if (field_type.compare(0, 11, "std::vector") == 0 || field_type == bytes_type + " ") { + } else if (field_type.compare(0, 11, "std::vector") == 0 || field_type == "bytes ") { res += field_type + "&&"; } else if (field_type.compare(0, 10, "object_ptr") == 0) { res += field_type + "&&"; diff --git a/td/mtproto/Handshake.cpp b/td/mtproto/Handshake.cpp index 2c8c2f66..eb56e1b7 100644 --- a/td/mtproto/Handshake.cpp +++ b/td/mtproto/Handshake.cpp @@ -27,6 +27,23 @@ namespace td { namespace mtproto { +template +static Result fetch_result(Slice message, bool check_end = true) { + TlParser parser(message); + auto result = T::fetch_result(parser); + + if (check_end) { + parser.fetch_end(); + } + const char *error = parser.get_error(); + if (error != nullptr) { + LOG(ERROR) << "Can't parse: " << format::as_hex_dump<4>(message); + return Status::Error(500, Slice(error)); + } + + return std::move(result); +} + void AuthKeyHandshake::clear() { last_query_ = BufferSlice(); state_ = Start; @@ -109,7 +126,8 @@ Status AuthKeyHandshake::on_res_pq(Slice message, Callback *connection, PublicRs // encrypted_data := RSA (data_with_hash, server_public_key); a 255-byte long number (big endian) // is raised to the requisite power over the requisite modulus, and the result is stored as a 256-byte number. string encrypted_data(256, 0); - rsa.encrypt(data_with_hash, size, reinterpret_cast(&encrypted_data[0])); + rsa.encrypt(data_with_hash, size, sizeof(data_with_hash), reinterpret_cast(&encrypted_data[0]), + encrypted_data.size()); // req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long // encrypted_data:string = Server_DH_Params diff --git a/td/mtproto/Handshake.h b/td/mtproto/Handshake.h index 952c4ef8..46f0c69c 100644 --- a/td/mtproto/Handshake.h +++ b/td/mtproto/Handshake.h @@ -79,6 +79,10 @@ class AuthKeyHandshake { void clear(); + const AuthKey &get_auth_key() const { + return auth_key_; + } + AuthKey release_auth_key() { return std::move(auth_key_); } diff --git a/td/mtproto/crypto.cpp b/td/mtproto/crypto.cpp index 596a9263..905228cf 100644 --- a/td/mtproto/crypto.cpp +++ b/td/mtproto/crypto.cpp @@ -27,14 +27,13 @@ namespace td { /*** RSA ***/ RSA::RSA(BigNum n, BigNum e) : n_(std::move(n)), e_(std::move(e)) { - e_.ensure_const_time(); } RSA RSA::clone() const { return RSA(n_.clone(), e_.clone()); } -Result RSA::from_pem(Slice pem) { +Result RSA::from_pem_public_key(Slice pem) { init_crypto(); auto *bio = @@ -91,17 +90,19 @@ int64 RSA::get_fingerprint() const { } size_t RSA::size() const { - // Checked in RSA::from_pem step + // Checked in RSA::from_pem_public_key step return 256; } -size_t RSA::encrypt(unsigned char *from, size_t from_len, unsigned char *to) const { +size_t RSA::encrypt(unsigned char *from, size_t from_len, size_t max_from_len, unsigned char *to, size_t to_len) const { CHECK(from_len > 0 && from_len <= 2550); size_t pad = (25500 - from_len - 32) % 255 + 32; size_t chunks = (from_len + pad) / 255; int bits = n_.get_num_bits(); CHECK(bits >= 2041 && bits <= 2048); CHECK(chunks * 255 == from_len + pad); + CHECK(from_len + pad <= max_from_len); + CHECK(chunks * 256 <= to_len); Random::secure_bytes(from + from_len, pad); BigNumContext ctx; @@ -115,7 +116,7 @@ size_t RSA::encrypt(unsigned char *from, size_t from_len, unsigned char *to) con return chunks * 256; } -void RSA::decrypt(Slice from, MutableSlice to) const { +void RSA::decrypt_signature(Slice from, MutableSlice to) const { CHECK(from.size() == 256); BigNumContext ctx; BigNum x = BigNum::from_binary(from); diff --git a/td/mtproto/crypto.h b/td/mtproto/crypto.h index 119815ab..7d3f35d9 100644 --- a/td/mtproto/crypto.h +++ b/td/mtproto/crypto.h @@ -21,11 +21,11 @@ class RSA { RSA clone() const; int64 get_fingerprint() const; size_t size() const; - size_t encrypt(unsigned char *from, size_t from_len, unsigned char *to) const; + size_t encrypt(unsigned char *from, size_t from_len, size_t max_from_len, unsigned char *to, size_t to_len) const; - void decrypt(Slice from, MutableSlice to) const; + void decrypt_signature(Slice from, MutableSlice to) const; - static Result from_pem(Slice pem); + static Result from_pem_public_key(Slice pem); private: RSA(BigNum n, BigNum e); diff --git a/td/mtproto/utils.cpp b/td/mtproto/utils.cpp index 40274d5b..13e981ae 100644 --- a/td/mtproto/utils.cpp +++ b/td/mtproto/utils.cpp @@ -7,9 +7,6 @@ #include "td/mtproto/utils.h" #include "td/mtproto/mtproto_api.h" -#include "td/telegram/telegram_api.h" - -#include "td/utils/logging.h" namespace td { @@ -17,17 +14,8 @@ TLStorer create_storer(const mtproto_api::Function &funct return TLStorer(function); } -TLStorer create_storer(const telegram_api::Function &function) { - LOG(DEBUG) << "Create storer for " << to_string(function); - return TLStorer(function); -} - TLObjectStorer create_storer(const mtproto_api::Object &object) { return TLObjectStorer(object); } -TLObjectStorer create_storer(const telegram_api::Object &object) { - return TLObjectStorer(object); -} - } // namespace td diff --git a/td/mtproto/utils.h b/td/mtproto/utils.h index d7c6cbbc..4e5e3ae7 100644 --- a/td/mtproto/utils.h +++ b/td/mtproto/utils.h @@ -6,54 +6,14 @@ // #pragma once -#include "td/utils/buffer.h" -#include "td/utils/format.h" -#include "td/utils/logging.h" -#include "td/utils/Slice.h" -#include "td/utils/Status.h" #include "td/utils/Storer.h" #include "td/utils/StorerBase.h" -#include "td/utils/tl_parsers.h" #include "td/utils/tl_storers.h" #include namespace td { -template -Result fetch_result(Slice message, bool check_end = true) { - TlParser parser(message); - auto result = T::fetch_result(parser); - - if (check_end) { - parser.fetch_end(); - } - const char *error = parser.get_error(); - if (error != nullptr) { - LOG(ERROR) << "Can't parse: " << format::as_hex_dump<4>(message); - return Status::Error(500, Slice(error)); - } - - return std::move(result); -} - -template -Result fetch_result(const BufferSlice &message, bool check_end = true) { - TlBufferParser parser(&message); - auto result = T::fetch_result(parser); - - if (check_end) { - parser.fetch_end(); - } - const char *error = parser.get_error(); - if (error != nullptr) { - LOG(ERROR) << "Can't parse: " << format::as_hex_dump<4>(message.as_slice()); - return Status::Error(500, Slice(error)); - } - - return std::move(result); -} - template using TLStorer = DefaultStorer; @@ -88,17 +48,8 @@ class Object; class Function; } // namespace mtproto_api -namespace telegram_api { -class Object; -class Function; -} // namespace telegram_api - TLStorer create_storer(const mtproto_api::Function &function); -TLStorer create_storer(const telegram_api::Function &function); - TLObjectStorer create_storer(const mtproto_api::Object &object); -TLObjectStorer create_storer(const telegram_api::Object &object); - } // namespace td diff --git a/td/telegram/AnimationsManager.cpp b/td/telegram/AnimationsManager.cpp index 39b6e1fb..aa19b174 100644 --- a/td/telegram/AnimationsManager.cpp +++ b/td/telegram/AnimationsManager.cpp @@ -45,7 +45,7 @@ class GetSavedGifsQuery : public Td::ResultHandler { void send(bool is_repair, int32 hash) { is_repair_ = is_repair; LOG(INFO) << "Send get saved animations request with hash = " << hash; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getSavedGifs(hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getSavedGifs(hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -59,7 +59,7 @@ class GetSavedGifsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for get saved animations: " << status; } td->animations_manager_->on_get_saved_animations_failed(is_repair_, std::move(status)); @@ -83,8 +83,7 @@ class SaveGifQuery : public Td::ResultHandler { file_id_ = file_id; file_reference_ = input_document->file_reference_.as_slice().str(); unsave_ = unsave; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_saveGif(std::move(input_document), unsave)))); + send_query(G()->net_query_creator().create(telegram_api::messages_saveGif(std::move(input_document), unsave))); } void on_result(uint64 id, BufferSlice packet) override { @@ -119,7 +118,7 @@ class SaveGifQuery : public Td::ResultHandler { return; } - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for save GIF: " << status; } td->animations_manager_->reload_saved_animations(true); @@ -606,7 +605,10 @@ int32 AnimationsManager::get_saved_animations_hash(const char *source) const { CHECK(animation != nullptr); auto file_view = td_->file_manager_->get_file_view(animation_id); CHECK(file_view.has_remote_location()); - LOG_CHECK(file_view.remote_location().is_document()) << source << " " << file_view.remote_location(); + if (!file_view.remote_location().is_document()) { + LOG(ERROR) << "Saved animation remote location is not document: " << source << " " << file_view.remote_location(); + continue; + } auto id = static_cast(file_view.remote_location().get_id()); numbers.push_back(static_cast(id >> 32)); numbers.push_back(static_cast(id & 0xFFFFFFFF)); diff --git a/td/telegram/AuthManager.cpp b/td/telegram/AuthManager.cpp index 64ede0c1..9720e258 100644 --- a/td/telegram/AuthManager.cpp +++ b/td/telegram/AuthManager.cpp @@ -69,7 +69,7 @@ AuthManager::AuthManager(int32 api_id, const string &api_hash, ActorShared<> par void AuthManager::start_up() { if (state_ == State::LoggingOut) { - start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(create_storer(telegram_api::auth_logOut()))); + start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(telegram_api::auth_logOut())); } else if (state_ == State::DestroyingKeys) { destroy_auth_keys(); } @@ -175,9 +175,8 @@ void AuthManager::check_bot_token(uint64 query_id, string bot_token) { bot_token_ = bot_token; was_check_bot_token_ = true; start_net_query(NetQueryType::BotAuthentication, - G()->net_query_creator().create( - create_storer(telegram_api::auth_importBotAuthorization(0, api_id_, api_hash_, bot_token_)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth( + telegram_api::auth_importBotAuthorization(0, api_id_, api_hash_, bot_token_))); } void AuthManager::request_qr_code_authentication(uint64 query_id, vector other_user_ids) { @@ -215,9 +214,8 @@ void AuthManager::request_qr_code_authentication(uint64 query_id, vector void AuthManager::send_export_login_token_query() { poll_export_login_code_timeout_.cancel_timeout(); start_net_query(NetQueryType::RequestQrCode, - G()->net_query_creator().create(create_storer(telegram_api::auth_exportLoginToken( - api_id_, api_hash_, vector(other_user_ids_))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth( + telegram_api::auth_exportLoginToken(api_id_, api_hash_, vector(other_user_ids_)))); } void AuthManager::set_login_token_expires_at(double login_token_expires_at) { @@ -271,10 +269,8 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number, on_new_query(query_id); - start_net_query(NetQueryType::SendCode, - G()->net_query_creator().create( - create_storer(send_code_helper_.send_code(phone_number, settings, api_id_, api_hash_)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth( + send_code_helper_.send_code(phone_number, settings, api_id_, api_hash_))); } void AuthManager::resend_authentication_code(uint64 query_id) { @@ -289,9 +285,7 @@ void AuthManager::resend_authentication_code(uint64 query_id) { on_new_query(query_id); - start_net_query(NetQueryType::SendCode, - G()->net_query_creator().create(create_storer(r_resend_code.move_as_ok()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth(r_resend_code.move_as_ok())); } void AuthManager::check_code(uint64 query_id, string code) { @@ -302,10 +296,8 @@ void AuthManager::check_code(uint64 query_id, string code) { code_ = std::move(code); on_new_query(query_id); start_net_query(NetQueryType::SignIn, - G()->net_query_creator().create( - create_storer(telegram_api::auth_signIn(send_code_helper_.phone_number().str(), - send_code_helper_.phone_code_hash().str(), code_)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::auth_signIn( + send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_))); } void AuthManager::register_user(uint64 query_id, string first_name, string last_name) { @@ -320,12 +312,9 @@ void AuthManager::register_user(uint64 query_id, string first_name, string last_ } last_name = clean_name(last_name, MAX_NAME_LENGTH); - start_net_query( - NetQueryType::SignUp, - G()->net_query_creator().create( - create_storer(telegram_api::auth_signUp(send_code_helper_.phone_number().str(), - send_code_helper_.phone_code_hash().str(), first_name, last_name)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + start_net_query(NetQueryType::SignUp, G()->net_query_creator().create_unauth(telegram_api::auth_signUp( + send_code_helper_.phone_number().str(), + send_code_helper_.phone_code_hash().str(), first_name, last_name))); } void AuthManager::check_password(uint64 query_id, string password) { @@ -337,8 +326,7 @@ void AuthManager::check_password(uint64 query_id, string password) { on_new_query(query_id); password_ = std::move(password); start_net_query(NetQueryType::GetPassword, - G()->net_query_creator().create(create_storer(telegram_api::account_getPassword()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::account_getPassword())); } void AuthManager::request_password_recovery(uint64 query_id) { @@ -348,8 +336,7 @@ void AuthManager::request_password_recovery(uint64 query_id) { on_new_query(query_id); start_net_query(NetQueryType::RequestPasswordRecovery, - G()->net_query_creator().create(create_storer(telegram_api::auth_requestPasswordRecovery()), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::auth_requestPasswordRecovery())); } void AuthManager::recover_password(uint64 query_id, string code) { @@ -359,8 +346,7 @@ void AuthManager::recover_password(uint64 query_id, string code) { on_new_query(query_id); start_net_query(NetQueryType::RecoverPassword, - G()->net_query_creator().create(create_storer(telegram_api::auth_recoverPassword(code)), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::auth_recoverPassword(code))); } void AuthManager::logout(uint64 query_id) { @@ -380,7 +366,7 @@ void AuthManager::logout(uint64 query_id) { LOG(INFO) << "Logging out"; G()->td_db()->get_binlog_pmc()->set("auth", "logout"); update_state(State::LoggingOut); - start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(create_storer(telegram_api::auth_logOut()))); + start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(telegram_api::auth_logOut())); } } @@ -391,8 +377,7 @@ void AuthManager::delete_account(uint64 query_id, const string &reason) { on_new_query(query_id); LOG(INFO) << "Deleting account"; start_net_query(NetQueryType::DeleteAccount, - G()->net_query_creator().create(create_storer(telegram_api::account_deleteAccount(reason)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::account_deleteAccount(reason))); } void AuthManager::on_closing(bool destroy_flag) { @@ -524,10 +509,9 @@ void AuthManager::on_get_login_token(tl_object_ptrdc_id_; - start_net_query(NetQueryType::ImportQrCode, - G()->net_query_creator().create( - create_storer(telegram_api::auth_importLoginToken(std::move(token->token_))), - DcId::internal(token->dc_id_), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + start_net_query(NetQueryType::ImportQrCode, G()->net_query_creator().create_unauth( + telegram_api::auth_importLoginToken(std::move(token->token_)), + DcId::internal(token->dc_id_))); break; } case telegram_api::auth_loginTokenSuccess::ID: { @@ -582,10 +566,8 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { return; } else { start_net_query(NetQueryType::SignIn, - G()->net_query_creator().create( - create_storer(telegram_api::auth_signIn(send_code_helper_.phone_number().str(), - send_code_helper_.phone_code_hash().str(), code_)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::auth_signIn( + send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_))); return; } @@ -602,8 +584,7 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { wait_password_state_.srp_B_, wait_password_state_.srp_id_); start_net_query(NetQueryType::CheckPassword, - G()->net_query_creator().create(create_storer(telegram_api::auth_checkPassword(std::move(hash))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::auth_checkPassword(std::move(hash)))); } else { update_state(State::WaitPassword); if (query_id_ != 0) { @@ -779,8 +760,7 @@ void AuthManager::on_result(NetQueryPtr result) { dc_id = DcId::internal(imported_dc_id_); } start_net_query(NetQueryType::GetPassword, - G()->net_query_creator().create(create_storer(telegram_api::account_getPassword()), dc_id, - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + G()->net_query_creator().create_unauth(telegram_api::account_getPassword(), dc_id)); return; } if (result->error().message() == CSlice("PHONE_NUMBER_BANNED")) { diff --git a/td/telegram/AutoDownloadSettings.cpp b/td/telegram/AutoDownloadSettings.cpp index de2bd5f9..eca917a8 100644 --- a/td/telegram/AutoDownloadSettings.cpp +++ b/td/telegram/AutoDownloadSettings.cpp @@ -39,7 +39,7 @@ class GetAutoDownloadSettingsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getAutoDownloadSettings()))); + send_query(G()->net_query_creator().create(telegram_api::account_getAutoDownloadSettings())); } void on_result(uint64 id, BufferSlice packet) override { @@ -94,8 +94,8 @@ class SaveAutoDownloadSettingsQuery : public Td::ResultHandler { if (type == NetType::WiFi) { flags |= telegram_api::account_saveAutoDownloadSettings::HIGH_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_saveAutoDownloadSettings( - flags, false /*ignored*/, false /*ignored*/, get_input_auto_download_settings(settings))))); + send_query(G()->net_query_creator().create(telegram_api::account_saveAutoDownloadSettings( + flags, false /*ignored*/, false /*ignored*/, get_input_auto_download_settings(settings)))); } void on_result(uint64 id, BufferSlice packet) override { diff --git a/td/telegram/BackgroundManager.cpp b/td/telegram/BackgroundManager.cpp index a313d56b..69300f8e 100644 --- a/td/telegram/BackgroundManager.cpp +++ b/td/telegram/BackgroundManager.cpp @@ -51,8 +51,7 @@ class GetBackgroundQuery : public Td::ResultHandler { background_id_ = background_id; background_name_ = background_name; LOG(INFO) << "Load " << background_id_ << "/" << background_name_ << " from server: " << to_string(input_wallpaper); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::account_getWallPaper(std::move(input_wallpaper))))); + send_query(G()->net_query_creator().create(telegram_api::account_getWallPaper(std::move(input_wallpaper)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -81,7 +80,7 @@ class GetBackgroundsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getWallPapers(0)))); + send_query(G()->net_query_creator().create(telegram_api::account_getWallPapers(0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -106,9 +105,9 @@ class InstallBackgroundQuery : public Td::ResultHandler { } void send(BackgroundId background_id, int64 access_hash, const BackgroundType &type) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_installWallPaper( + send_query(G()->net_query_creator().create(telegram_api::account_installWallPaper( telegram_api::make_object(background_id.get(), access_hash), - get_input_wallpaper_settings(type))))); + get_input_wallpaper_settings(type)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -143,8 +142,8 @@ class UploadBackgroundQuery : public Td::ResultHandler { type_ = type; for_dark_theme_ = for_dark_theme; string mime_type = type.type == BackgroundType::Type::Pattern ? "image/png" : "image/jpeg"; - send_query(G()->net_query_creator().create(create_storer( - telegram_api::account_uploadWallPaper(std::move(input_file), mime_type, get_input_wallpaper_settings(type))))); + send_query(G()->net_query_creator().create( + telegram_api::account_uploadWallPaper(std::move(input_file), mime_type, get_input_wallpaper_settings(type)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -181,9 +180,9 @@ class SaveBackgroundQuery : public Td::ResultHandler { } void send(BackgroundId background_id, int64 access_hash, const BackgroundType &type, bool unsave) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_saveWallPaper( + send_query(G()->net_query_creator().create(telegram_api::account_saveWallPaper( telegram_api::make_object(background_id.get(), access_hash), unsave, - get_input_wallpaper_settings(type))))); + get_input_wallpaper_settings(type)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -198,7 +197,7 @@ class SaveBackgroundQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for save background: " << status; } promise_.set_error(std::move(status)); @@ -213,7 +212,7 @@ class ResetBackgroundsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_resetWallPapers()))); + send_query(G()->net_query_creator().create(telegram_api::account_resetWallPapers())); } void on_result(uint64 id, BufferSlice packet) override { @@ -228,7 +227,7 @@ class ResetBackgroundsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for reset backgrounds: " << status; } promise_.set_error(std::move(status)); diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp index 3ef23874..1d82b449 100644 --- a/td/telegram/CallActor.cpp +++ b/td/telegram/CallActor.cpp @@ -22,6 +22,8 @@ #include "td/telegram/Td.h" #include "td/telegram/UpdatesManager.h" +#include "td/utils/as.h" +#include "td/utils/bits.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/crypto.h" @@ -39,6 +41,7 @@ CallProtocol CallProtocol::from_telegram_api(const telegram_api::phoneCallProtoc res.udp_reflector = protocol.udp_reflector_; res.min_layer = protocol.min_layer_; res.max_layer = protocol.max_layer_; + res.library_versions = protocol.library_versions_; return res; } @@ -50,7 +53,8 @@ tl_object_ptr CallProtocol::as_telegram_api() c if (udp_reflector) { flags |= telegram_api::phoneCallProtocol::UDP_REFLECTOR_MASK; } - return make_tl_object(flags, udp_p2p, udp_reflector, min_layer, max_layer); + return make_tl_object(flags, udp_p2p, udp_reflector, min_layer, max_layer, + vector(library_versions)); } CallProtocol CallProtocol::from_td_api(const td_api::callProtocol &protocol) { @@ -59,10 +63,13 @@ CallProtocol CallProtocol::from_td_api(const td_api::callProtocol &protocol) { res.udp_reflector = protocol.udp_reflector_; res.min_layer = protocol.min_layer_; res.max_layer = protocol.max_layer_; + res.library_versions = protocol.library_versions_; return res; } + tl_object_ptr CallProtocol::as_td_api() const { - return make_tl_object(udp_p2p, udp_reflector, min_layer, max_layer); + return make_tl_object(udp_p2p, udp_reflector, min_layer, max_layer, + vector(library_versions)); } CallConnection CallConnection::from_telegram_api(const telegram_api::phoneConnection &connection) { @@ -235,7 +242,7 @@ void CallActor::rate_call(int32 rating, string comment, vectornet_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_set_rating_query_result, std::move(net_query)); })); @@ -258,7 +265,7 @@ void CallActor::send_call_debug_information(string data, Promise<> promise) { promise.set_value(Unit()); auto tl_query = telegram_api::phone_saveCallDebug(get_input_phone_call("send_call_debug_information"), make_tl_object(std::move(data))); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_set_debug_query_result, std::move(net_query)); })); @@ -508,7 +515,7 @@ void CallActor::do_load_dh_config(Promise> promise) { } int random_length = 0; telegram_api::messages_getDhConfig tl_query(version, random_length); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), old_dh_config = std::move(dh_config), promise = std::move(promise)](Result result_query) mutable { @@ -538,7 +545,7 @@ void CallActor::do_load_dh_config(Promise> promise) { void CallActor::send_received_query() { auto tl_query = telegram_api::phone_receivedCall(get_input_phone_call("send_received_query")); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_received_query_result, std::move(net_query)); })); @@ -565,7 +572,7 @@ void CallActor::try_send_request_query() { auto tl_query = telegram_api::phone_requestCall(flags, false /*ignored*/, std::move(input_user_), Random::secure_int32(), BufferSlice(dh_handshake_.get_g_b_hash()), call_state_.protocol.as_telegram_api()); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitRequestResult; int32 call_receive_timeout_ms = G()->shared_config().get_option_integer("call_receive_timeout_ms", 20000); double timeout = call_receive_timeout_ms * 0.001; @@ -600,7 +607,7 @@ void CallActor::try_send_accept_query() { auto tl_query = telegram_api::phone_acceptCall(get_input_phone_call("try_send_accept_query"), BufferSlice(dh_handshake_.get_g_b()), call_state_.protocol.as_telegram_api()); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitAcceptResult; send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_accept_query_result, std::move(net_query)); @@ -624,7 +631,7 @@ void CallActor::try_send_confirm_query() { auto tl_query = telegram_api::phone_confirmCall(get_input_phone_call("try_send_confirm_query"), BufferSlice(dh_handshake_.get_g_b()), call_state_.key_fingerprint, call_state_.protocol.as_telegram_api()); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitConfirmResult; send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_confirm_query_result, std::move(net_query)); @@ -654,7 +661,7 @@ void CallActor::try_send_discard_query() { auto tl_query = telegram_api::phone_discardCall( flags, false /*ignored*/, get_input_phone_call("try_send_discard_query"), duration_, get_input_phone_call_discard_reason(call_state_.discard_reason), connection_id_); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); state_ = State::WaitDiscardResult; send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_discard_query_result, std::move(net_query)); @@ -703,7 +710,7 @@ void CallActor::flush_call_state() { void CallActor::start_up() { auto tl_query = telegram_api::phone_getCallConfig(); - auto query = G()->net_query_creator().create(create_storer(tl_query)); + auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) { send_closure(actor_id, &CallActor::on_get_call_config_result, std::move(net_query)); })); @@ -779,11 +786,7 @@ vector CallActor::get_emojis_fingerprint(const string &key, const string vector result; result.reserve(4); for (int i = 0; i < 4; i++) { - uint64 num = - (static_cast(sha256_buf[8 * i + 0]) << 56) | (static_cast(sha256_buf[8 * i + 1]) << 48) | - (static_cast(sha256_buf[8 * i + 2]) << 40) | (static_cast(sha256_buf[8 * i + 3]) << 32) | - (static_cast(sha256_buf[8 * i + 4]) << 24) | (static_cast(sha256_buf[8 * i + 5]) << 16) | - (static_cast(sha256_buf[8 * i + 6]) << 8) | (static_cast(sha256_buf[8 * i + 7])); + uint64 num = bswap64(as(sha256_buf + 8 * i)); result.push_back(get_emoji_fingerprint(num)); } return result; diff --git a/td/telegram/CallActor.h b/td/telegram/CallActor.h index c13e9c2c..6adc9b98 100644 --- a/td/telegram/CallActor.h +++ b/td/telegram/CallActor.h @@ -32,6 +32,7 @@ struct CallProtocol { bool udp_reflector{true}; int32 min_layer{65}; int32 max_layer{65}; + vector library_versions; static CallProtocol from_telegram_api(const telegram_api::phoneCallProtocol &protocol); tl_object_ptr as_telegram_api() const; diff --git a/td/telegram/CallDiscardReason.cpp b/td/telegram/CallDiscardReason.cpp index 9b5e1b96..f5ce35aa 100644 --- a/td/telegram/CallDiscardReason.cpp +++ b/td/telegram/CallDiscardReason.cpp @@ -6,9 +6,6 @@ // #include "td/telegram/CallDiscardReason.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - #include "td/utils/common.h" namespace td { diff --git a/td/telegram/CallDiscardReason.h b/td/telegram/CallDiscardReason.h index d83a9e11..8f738031 100644 --- a/td/telegram/CallDiscardReason.h +++ b/td/telegram/CallDiscardReason.h @@ -6,20 +6,13 @@ // #pragma once -#include "td/tl/TlObject.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" #include "td/utils/int_types.h" namespace td { -namespace td_api { -class CallDiscardReason; -} // namespace td_api - -namespace telegram_api { -class PhoneCallDiscardReason; -} // namespace telegram_api - enum class CallDiscardReason : int32 { Empty, Missed, Disconnected, HungUp, Declined }; CallDiscardReason get_call_discard_reason(const tl_object_ptr &reason); diff --git a/td/telegram/CallbackQueriesManager.cpp b/td/telegram/CallbackQueriesManager.cpp index 7455c712..11061e5f 100644 --- a/td/telegram/CallbackQueriesManager.cpp +++ b/td/telegram/CallbackQueriesManager.cpp @@ -61,8 +61,8 @@ class GetBotCallbackAnswerQuery : public Td::ResultHandler { UNREACHABLE(); } - auto net_query = G()->net_query_creator().create(create_storer(telegram_api::messages_getBotCallbackAnswer( - flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), std::move(data)))); + auto net_query = G()->net_query_creator().create(telegram_api::messages_getBotCallbackAnswer( + flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), std::move(data))); net_query->need_resend_on_503 = false; send_query(std::move(net_query)); } @@ -95,8 +95,8 @@ class SetBotCallbackAnswerQuery : public Td::ResultHandler { } void send(int32 flags, int64 callback_query_id, const string &text, const string &url, int32 cache_time) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_setBotCallbackAnswer( - flags, false /*ignored*/, callback_query_id, text, url, cache_time)))); + send_query(G()->net_query_creator().create(telegram_api::messages_setBotCallbackAnswer( + flags, false /*ignored*/, callback_query_id, text, url, cache_time))); } void on_result(uint64 id, BufferSlice packet) override { @@ -226,7 +226,7 @@ int64 CallbackQueriesManager::send_callback_query(FullMessageId full_message_id, } if (payload == nullptr) { - promise.set_error(Status::Error(5, "Payload should not be empty")); + promise.set_error(Status::Error(5, "Payload must be non-empty")); return 0; } diff --git a/td/telegram/ClientActor.cpp b/td/telegram/ClientActor.cpp index d570c8c9..a53fd7e6 100644 --- a/td/telegram/ClientActor.cpp +++ b/td/telegram/ClientActor.cpp @@ -12,6 +12,7 @@ #include "td/telegram/Td.h" namespace td { + ClientActor::ClientActor(unique_ptr callback) { td_ = create_actor("Td", std::move(callback)); } diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index a3ef41c9..4f987aab 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -141,7 +141,7 @@ Result HttpDate::parse_http_date(std::string slice) { } Result decode_config(Slice input) { - static auto rsa = RSA::from_pem( + static auto rsa = RSA::from_pem_public_key( "-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAyr+18Rex2ohtVy8sroGP\n" "BwXD3DOoKCSpjDqYoXgCqB7ioln4eDCFfOBUlfXUEvM/fnKCpF46VkAftlb4VuPD\n" @@ -167,7 +167,7 @@ Result decode_config(Slice input) { } MutableSlice data_rsa_slice(data_rsa); - rsa.decrypt(data_rsa_slice, data_rsa_slice); + rsa.decrypt_signature(data_rsa_slice, data_rsa_slice); MutableSlice data_cbc = data_rsa_slice.substr(32); UInt256 key; @@ -254,6 +254,7 @@ static ActorOwn<> get_simple_config_dns(Slice address, Slice host, Promise Result { + VLOG(config_recoverer) << "Receive DNS response " << http_query.content_; TRY_RESULT(json, json_decode(http_query.content_)); if (json.type() != JsonValue::Type::Object) { return Status::Error("Expected JSON object"); @@ -282,7 +283,7 @@ static ActorOwn<> get_simple_config_dns(Slice address, Slice host, Promise get_full_config(DcOption option, Promise promise, ActorSh int_dc_id, false /*is_main*/, true /*use_pfs*/, false /*is_cdn*/, false /*need_destroy_auth_key*/, mtproto::AuthKey(), std::vector()); - auto query = G()->net_query_creator().create(create_storer(telegram_api::help_getConfig()), DcId::empty(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off, - NetQuery::GzipFlag::On, 60 * 60 * 24); + auto query = G()->net_query_creator().create_unauth(telegram_api::help_getConfig(), DcId::empty()); + query->total_timeout_limit = 60 * 60 * 24; query->set_callback(actor_shared(this)); query->dispatch_ttl = 0; send_closure(session_, &Session::send, std::move(query)); @@ -790,7 +790,7 @@ class ConfigRecoverer : public Actor { PromiseCreator::lambda([actor_id = actor_shared(this)](Result r_simple_config) { send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false); }); - auto get_simple_config = [&]() { + auto get_simple_config = [&] { switch (simple_config_turn_ % 4) { case 2: return get_simple_config_azure; @@ -931,11 +931,9 @@ void ConfigManager::get_app_config(Promise get_app_config_queries_.push_back(std::move(promise)); if (get_app_config_queries_.size() == 1) { - G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(create_storer(telegram_api::help_getAppConfig()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off, NetQuery::GzipFlag::On, - 60 * 60 * 24), - actor_shared(this, 1)); + auto query = G()->net_query_creator().create_unauth(telegram_api::help_getAppConfig()); + query->total_timeout_limit = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 1)); } } @@ -952,8 +950,7 @@ void ConfigManager::get_content_settings(Promise &&promise) { get_content_settings_queries_.push_back(std::move(promise)); if (get_content_settings_queries_.size() == 1) { G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(create_storer(telegram_api::account_getContentSettings())), - actor_shared(this, 2)); + G()->net_query_creator().create(telegram_api::account_getContentSettings()), actor_shared(this, 2)); } } @@ -972,8 +969,7 @@ void ConfigManager::set_content_settings(bool ignore_sensitive_content_restricti flags |= telegram_api::account_setContentSettings::SENSITIVE_ENABLED_MASK; } G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create( - create_storer(telegram_api::account_setContentSettings(flags, false /*ignored*/))), + G()->net_query_creator().create(telegram_api::account_setContentSettings(flags, false /*ignored*/)), actor_shared(this, 3 + static_cast(ignore_sensitive_content_restrictions))); } } @@ -991,10 +987,9 @@ void ConfigManager::on_dc_options_update(DcOptions dc_options) { void ConfigManager::request_config_from_dc_impl(DcId dc_id) { config_sent_cnt_++; - G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(create_storer(telegram_api::help_getConfig()), dc_id, NetQuery::Type::Common, - NetQuery::AuthFlag::Off, NetQuery::GzipFlag::On, 60 * 60 * 24), - actor_shared(this, 0)); + auto query = G()->net_query_creator().create_unauth(telegram_api::help_getConfig(), dc_id); + query->total_timeout_limit = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 0)); } void ConfigManager::set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions) { @@ -1098,7 +1093,7 @@ void ConfigManager::on_result(NetQueryPtr res) { auto r_config = fetch_result(std::move(res)); if (r_config.is_error()) { if (!G()->close_flag()) { - LOG(ERROR) << "TODO: getConfig failed: " << r_config.error(); + LOG(ERROR) << "getConfig failed: " << r_config.error(); expire_time_ = Timestamp::in(60.0); // try again in a minute set_timeout_in(expire_time_.in()); } diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index e518bdfd..36191fb4 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -74,7 +74,7 @@ class SetAccountTtlQuery : public Td::ResultHandler { void send(int32 account_ttl) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_setAccountTTL(make_tl_object(account_ttl))))); + telegram_api::account_setAccountTTL(make_tl_object(account_ttl)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -104,7 +104,7 @@ class GetAccountTtlQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getAccountTTL()))); + send_query(G()->net_query_creator().create(telegram_api::account_getAccountTTL())); } void on_result(uint64 id, BufferSlice packet) override { @@ -133,8 +133,7 @@ class AcceptLoginTokenQuery : public Td::ResultHandler { } void send(const string &login_token) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::auth_acceptLoginToken(BufferSlice(login_token))))); + send_query(G()->net_query_creator().create(telegram_api::auth_acceptLoginToken(BufferSlice(login_token)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -160,7 +159,7 @@ class GetAuthorizationsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getAuthorizations()))); + send_query(G()->net_query_creator().create(telegram_api::account_getAuthorizations())); } void on_result(uint64 id, BufferSlice packet) override { @@ -201,8 +200,7 @@ class ResetAuthorizationQuery : public Td::ResultHandler { } void send(int64 authorization_id) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::account_resetAuthorization(authorization_id)))); + send_query(G()->net_query_creator().create(telegram_api::account_resetAuthorization(authorization_id))); } void on_result(uint64 id, BufferSlice packet) override { @@ -229,7 +227,7 @@ class ResetAuthorizationsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::auth_resetAuthorizations()))); + send_query(G()->net_query_creator().create(telegram_api::auth_resetAuthorizations())); } void on_result(uint64 id, BufferSlice packet) override { @@ -258,7 +256,7 @@ class GetWebAuthorizationsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getWebAuthorizations()))); + send_query(G()->net_query_creator().create(telegram_api::account_getWebAuthorizations())); } void on_result(uint64 id, BufferSlice packet) override { @@ -305,7 +303,7 @@ class ResetWebAuthorizationQuery : public Td::ResultHandler { } void send(int64 hash) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_resetWebAuthorization(hash)))); + send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorization(hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -332,7 +330,7 @@ class ResetWebAuthorizationsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_resetWebAuthorizations()))); + send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorizations())); } void on_result(uint64 id, BufferSlice packet) override { @@ -362,9 +360,9 @@ class SetUserIsBlockedQuery : public Td::ResultHandler { void send(UserId user_id, tl_object_ptr &&input_user, bool is_blocked) { user_id_ = user_id; if (is_blocked) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_block(std::move(input_user))))); + send_query(G()->net_query_creator().create(telegram_api::contacts_block(std::move(input_user)))); } else { - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_unblock(std::move(input_user))))); + send_query(G()->net_query_creator().create(telegram_api::contacts_unblock(std::move(input_user)))); } } @@ -402,7 +400,7 @@ class GetBlockedUsersQuery : public Td::ResultHandler { limit_ = limit; random_id_ = random_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_getBlocked(offset, limit)))); + send_query(G()->net_query_creator().create(telegram_api::contacts_getBlocked(offset, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -450,7 +448,7 @@ class GetContactsQuery : public Td::ResultHandler { public: void send(int32 hash) { LOG(INFO) << "Reload contacts with hash " << hash; - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_getContacts(hash)))); + send_query(G()->net_query_creator().create(telegram_api::contacts_getContacts(hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -474,7 +472,7 @@ class GetContactsStatusesQuery : public Td::ResultHandler { public: void send() { LOG(INFO) << "Reload contacts statuses"; - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_getStatuses()))); + send_query(G()->net_query_creator().create(telegram_api::contacts_getStatuses())); } void on_result(uint64 id, BufferSlice packet) override { @@ -487,7 +485,7 @@ class GetContactsStatusesQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for getContactsStatuses: " << status; } } @@ -508,8 +506,8 @@ class AddContactQuery : public Td::ResultHandler { if (share_phone_number) { flags |= telegram_api::contacts_addContact::ADD_PHONE_PRIVACY_EXCEPTION_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_addContact( - flags, false /*ignored*/, std::move(input_user), first_name, last_name, phone_number)))); + send_query(G()->net_query_creator().create(telegram_api::contacts_addContact( + flags, false /*ignored*/, std::move(input_user), first_name, last_name, phone_number))); } void on_result(uint64 id, BufferSlice packet) override { @@ -528,7 +526,7 @@ class AddContactQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { promise_.set_error(std::move(status)); td->contacts_manager_->reload_contacts(true); - td->messages_manager_->repair_dialog_action_bar(DialogId(user_id_), "AddContactQuery"); + td->messages_manager_->reget_dialog_action_bar(DialogId(user_id_), "AddContactQuery"); } }; @@ -542,8 +540,7 @@ class AcceptContactQuery : public Td::ResultHandler { void send(UserId user_id, tl_object_ptr &&input_user) { user_id_ = user_id; - send_query( - G()->net_query_creator().create(create_storer(telegram_api::contacts_acceptContact(std::move(input_user))))); + send_query(G()->net_query_creator().create(telegram_api::contacts_acceptContact(std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -562,7 +559,7 @@ class AcceptContactQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { promise_.set_error(std::move(status)); td->contacts_manager_->reload_contacts(true); - td->messages_manager_->repair_dialog_action_bar(DialogId(user_id_), "AcceptContactQuery"); + td->messages_manager_->reget_dialog_action_bar(DialogId(user_id_), "AcceptContactQuery"); } }; @@ -572,6 +569,7 @@ class ImportContactsQuery : public Td::ResultHandler { vector imported_user_ids_; vector unimported_contact_invites_; int64 random_id_; + size_t sent_size_ = 0; public: explicit ImportContactsQuery(Promise &&promise) : promise_(std::move(promise)) { @@ -598,8 +596,8 @@ class ImportContactsQuery : public Td::ResultHandler { contacts.push_back(input_contacts_[i].get_input_phone_contact(static_cast(i))); } - send_query( - G()->net_query_creator().create(create_storer(telegram_api::contacts_importContacts(std::move(contacts))))); + sent_size_ = size; + send_query(G()->net_query_creator().create(telegram_api::contacts_importContacts(std::move(contacts)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -636,6 +634,10 @@ class ImportContactsQuery : public Td::ResultHandler { } if (!ptr->retry_contacts_.empty()) { + if (sent_size_ == ptr->retry_contacts_.size()) { + return promise_.set_error(Status::Error(429, "Too Many Requests: retry after 3600")); + } + int64 total_size = static_cast(input_contacts_.size()); vector> contacts; contacts.reserve(ptr->retry_contacts_.size()); @@ -648,8 +650,8 @@ class ImportContactsQuery : public Td::ResultHandler { contacts.push_back(input_contacts_[i].get_input_phone_contact(client_id)); } - send_query( - G()->net_query_creator().create(create_storer(telegram_api::contacts_importContacts(std::move(contacts))))); + sent_size_ = contacts.size(); + send_query(G()->net_query_creator().create(telegram_api::contacts_importContacts(std::move(contacts)))); return; } @@ -672,8 +674,7 @@ class DeleteContactsQuery : public Td::ResultHandler { } void send(vector> &&input_users) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::contacts_deleteContacts(std::move(input_users))))); + send_query(G()->net_query_creator().create(telegram_api::contacts_deleteContacts(std::move(input_users)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -704,9 +705,11 @@ class DeleteContactsByPhoneNumberQuery : public Td::ResultHandler { } void send(vector &&user_phone_numbers, vector &&user_ids) { + if (user_phone_numbers.empty()) { + return promise_.set_value(Unit()); + } user_ids_ = std::move(user_ids); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::contacts_deleteByPhones(std::move(user_phone_numbers))))); + send_query(G()->net_query_creator().create(telegram_api::contacts_deleteByPhones(std::move(user_phone_numbers)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -738,7 +741,7 @@ class ResetContactsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_resetSaved()))); + send_query(G()->net_query_creator().create(telegram_api::contacts_resetSaved())); } void on_result(uint64 id, BufferSlice packet) override { @@ -772,9 +775,16 @@ class SearchDialogsNearbyQuery : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(const Location &location) { + void send(const Location &location, bool from_background, int32 expire_date) { + int32 flags = 0; + if (from_background) { + flags |= telegram_api::contacts_getLocated::BACKGROUND_MASK; + } + if (expire_date != -1) { + flags |= telegram_api::contacts_getLocated::SELF_EXPIRES_MASK; + } send_query(G()->net_query_creator().create( - create_storer(telegram_api::contacts_getLocated(location.get_input_geo_point())))); + telegram_api::contacts_getLocated(flags, false /*ignored*/, location.get_input_geo_point(), expire_date))); } void on_result(uint64 id, BufferSlice packet) override { @@ -805,8 +815,7 @@ class UploadProfilePhotoQuery : public Td::ResultHandler { file_id_ = file_id; - send_query( - G()->net_query_creator().create(create_storer(telegram_api::photos_uploadProfilePhoto(std::move(input_file))))); + send_query(G()->net_query_creator().create(telegram_api::photos_uploadProfilePhoto(std::move(input_file)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -845,8 +854,7 @@ class UpdateProfilePhotoQuery : public Td::ResultHandler { CHECK(input_photo != nullptr); file_id_ = file_id; file_reference_ = FileManager::extract_file_reference(input_photo); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::photos_updateProfilePhoto(std::move(input_photo))))); + send_query(G()->net_query_creator().create(telegram_api::photos_updateProfilePhoto(std::move(input_photo)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -889,8 +897,7 @@ class DeleteProfilePhotoQuery : public Td::ResultHandler { profile_photo_id_ = profile_photo_id; vector> input_photo_ids; input_photo_ids.push_back(make_tl_object(profile_photo_id, 0, BufferSlice())); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::photos_deletePhotos(std::move(input_photo_ids))))); + send_query(G()->net_query_creator().create(telegram_api::photos_deletePhotos(std::move(input_photo_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -930,8 +937,8 @@ class UpdateProfileQuery : public Td::ResultHandler { first_name_ = first_name; last_name_ = last_name; about_ = about; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_updateProfile(flags, first_name, last_name, about)))); + send_query( + G()->net_query_creator().create(telegram_api::account_updateProfile(flags, first_name, last_name, about))); } void on_result(uint64 id, BufferSlice packet) override { @@ -960,7 +967,7 @@ class CheckUsernameQuery : public Td::ResultHandler { } void send(const string &username) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_checkUsername(username)))); + send_query(G()->net_query_creator().create(telegram_api::account_checkUsername(username))); } void on_result(uint64 id, BufferSlice packet) override { @@ -985,7 +992,7 @@ class UpdateUsernameQuery : public Td::ResultHandler { } void send(const string &username) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_updateUsername(username)))); + send_query(G()->net_query_creator().create(telegram_api::account_updateUsername(username))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1008,6 +1015,42 @@ class UpdateUsernameQuery : public Td::ResultHandler { } }; +class SetBotCommandsQuery : public Td::ResultHandler { + Promise promise_; + vector> commands_; + + public: + explicit SetBotCommandsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(vector> &&commands) { + commands_ = std::move(commands); + send_query( + G()->net_query_creator().create(telegram_api::bots_setBotCommands(transform(commands_, [](const auto &command) { + return make_tl_object(command.first, command.second); + })))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + bool result = result_ptr.ok(); + if (result) { + td->contacts_manager_->on_set_bot_commands_success(std::move(commands_)); + } else { + LOG(ERROR) << "Set bot commands request failed"; + } + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class CheckChannelUsernameQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -1026,8 +1069,8 @@ class CheckChannelUsernameQuery : public Td::ResultHandler { input_channel = make_tl_object(); } CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_checkUsername(std::move(input_channel), username)))); + send_query( + G()->net_query_creator().create(telegram_api::channels_checkUsername(std::move(input_channel), username))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1061,8 +1104,8 @@ class UpdateChannelUsernameQuery : public Td::ResultHandler { username_ = username; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_updateUsername(std::move(input_channel), username)))); + send_query( + G()->net_query_creator().create(telegram_api::channels_updateUsername(std::move(input_channel), username))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1111,7 +1154,7 @@ class SetChannelStickerSetQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_setStickers(std::move(input_channel), std::move(input_sticker_set))))); + telegram_api::channels_setStickers(std::move(input_channel), std::move(input_sticker_set)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1157,7 +1200,7 @@ class ToggleChannelSignaturesQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_toggleSignatures(std::move(input_channel), sign_messages)))); + telegram_api::channels_toggleSignatures(std::move(input_channel), sign_messages))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1201,8 +1244,8 @@ class ToggleChannelIsAllHistoryAvailableQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer( - telegram_api::channels_togglePreHistoryHidden(std::move(input_channel), !is_all_history_available)))); + send_query(G()->net_query_creator().create( + telegram_api::channels_togglePreHistoryHidden(std::move(input_channel), !is_all_history_available))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1261,8 +1304,7 @@ class EditChatAboutQuery : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_editChatAbout(std::move(input_peer), about)))); + send_query(G()->net_query_creator().create(telegram_api::messages_editChatAbout(std::move(input_peer), about))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1309,8 +1351,8 @@ class SetDiscussionGroupQuery : public Td::ResultHandler { telegram_api::object_ptr group_input_channel) { broadcast_channel_id_ = broadcast_channel_id; group_channel_id_ = group_channel_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_setDiscussionGroup( - std::move(broadcast_input_channel), std::move(group_input_channel))))); + send_query(G()->net_query_creator().create( + telegram_api::channels_setDiscussionGroup(std::move(broadcast_input_channel), std::move(group_input_channel)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1350,8 +1392,8 @@ class EditLocationQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editLocation( - std::move(input_channel), location_.get_input_geo_point(), location_.get_address())))); + send_query(G()->net_query_creator().create(telegram_api::channels_editLocation( + std::move(input_channel), location_.get_input_geo_point(), location_.get_address()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1390,7 +1432,7 @@ class ToggleSlowModeQuery : public Td::ResultHandler { CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_toggleSlowMode(std::move(input_channel), slow_mode_delay)))); + telegram_api::channels_toggleSlowMode(std::move(input_channel), slow_mode_delay))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1440,8 +1482,8 @@ class ReportChannelSpamQuery : public Td::ResultHandler { auto input_user = td->contacts_manager_->get_input_user(user_id); CHECK(input_user != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_reportSpam( - std::move(input_channel), std::move(input_user), MessagesManager::get_server_message_ids(message_ids))))); + send_query(G()->net_query_creator().create(telegram_api::channels_reportSpam( + std::move(input_channel), std::move(input_user), MessagesManager::get_server_message_ids(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1474,8 +1516,7 @@ class DeleteChannelQuery : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::channels_deleteChannel(std::move(input_channel))))); + send_query(G()->net_query_creator().create(telegram_api::channels_deleteChannel(std::move(input_channel)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1506,7 +1547,7 @@ class AddChatUserQuery : public Td::ResultHandler { void send(ChatId chat_id, tl_object_ptr &&input_user, int32 forward_limit) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_addChatUser(chat_id.get(), std::move(input_user), forward_limit)))); + telegram_api::messages_addChatUser(chat_id.get(), std::move(input_user), forward_limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1539,7 +1580,7 @@ class EditChatAdminQuery : public Td::ResultHandler { void send(ChatId chat_id, tl_object_ptr &&input_user, bool is_administrator) { chat_id_ = chat_id; send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_editChatAdmin(chat_id.get(), std::move(input_user), is_administrator)))); + telegram_api::messages_editChatAdmin(chat_id.get(), std::move(input_user), is_administrator))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1578,8 +1619,7 @@ class ExportChatInviteLinkQuery : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_exportChatInvite(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1615,8 +1655,7 @@ class ExportChannelInviteLinkQuery : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_exportChatInvite(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_exportChatInvite(std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1649,8 +1688,8 @@ class CheckDialogInviteLinkQuery : public Td::ResultHandler { void send(const string &invite_link) { invite_link_ = invite_link; - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_checkChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link_).str())))); + send_query(G()->net_query_creator().create( + telegram_api::messages_checkChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link_).str()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1682,8 +1721,8 @@ class ImportDialogInviteLinkQuery : public Td::ResultHandler { void send(const string &invite_link) { invite_link_ = invite_link; - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_importChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link).str())))); + send_query(G()->net_query_creator().create( + telegram_api::messages_importChatInvite(ContactsManager::get_dialog_invite_link_hash(invite_link).str()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1720,8 +1759,8 @@ class DeleteChatUserQuery : public Td::ResultHandler { } void send(ChatId chat_id, tl_object_ptr &&input_user) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_deleteChatUser(chat_id.get(), std::move(input_user))))); + send_query( + G()->net_query_creator().create(telegram_api::messages_deleteChatUser(chat_id.get(), std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1755,8 +1794,7 @@ class JoinChannelQuery : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::channels_joinChannel(std::move(input_channel))))); + send_query(G()->net_query_creator().create(telegram_api::channels_joinChannel(std::move(input_channel)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1792,7 +1830,7 @@ class InviteToChannelQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_inviteToChannel(std::move(input_channel), std::move(input_users))))); + telegram_api::channels_inviteToChannel(std::move(input_channel), std::move(input_users)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1828,8 +1866,8 @@ class EditChannelAdminQuery : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editAdmin( - std::move(input_channel), std::move(input_user), status.get_chat_admin_rights(), status.get_rank())))); + send_query(G()->net_query_creator().create(telegram_api::channels_editAdmin( + std::move(input_channel), std::move(input_user), status.get_chat_admin_rights(), status.get_rank()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1865,8 +1903,8 @@ class EditChannelBannedQuery : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editBanned( - std::move(input_channel), std::move(input_user), status.get_chat_banned_rights())))); + send_query(G()->net_query_creator().create(telegram_api::channels_editBanned( + std::move(input_channel), std::move(input_user), status.get_chat_banned_rights()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1902,8 +1940,7 @@ class LeaveChannelQuery : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::channels_leaveChannel(std::move(input_channel))))); + send_query(G()->net_query_creator().create(telegram_api::channels_leaveChannel(std::move(input_channel)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1936,9 +1973,9 @@ class CanEditChannelCreatorQuery : public Td::ResultHandler { void send() { auto input_user = td->contacts_manager_->get_input_user(td->contacts_manager_->get_my_id()); CHECK(input_user != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editCreator( + send_query(G()->net_query_creator().create(telegram_api::channels_editCreator( telegram_api::make_object(), std::move(input_user), - make_tl_object())))); + make_tl_object()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1976,8 +2013,8 @@ class EditChannelCreatorQuery : public Td::ResultHandler { if (input_user == nullptr) { return promise_.set_error(Status::Error(400, "Have no access to the user")); } - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_editCreator( - std::move(input_channel), std::move(input_user), std::move(input_check_password))))); + send_query(G()->net_query_creator().create(telegram_api::channels_editCreator( + std::move(input_channel), std::move(input_user), std::move(input_check_password)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2009,7 +2046,7 @@ class MigrateChatQuery : public Td::ResultHandler { } void send(ChatId chat_id) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_migrateChat(chat_id.get())))); + send_query(G()->net_query_creator().create(telegram_api::messages_migrateChat(chat_id.get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2049,7 +2086,7 @@ class GetCreatedPublicChannelsQuery : public Td::ResultHandler { flags |= telegram_api::channels_getAdminedPublicChannels::CHECK_LIMIT_MASK; } send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_getAdminedPublicChannels(flags, false /*ignored*/, false /*ignored*/)))); + telegram_api::channels_getAdminedPublicChannels(flags, false /*ignored*/, false /*ignored*/))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2093,7 +2130,7 @@ class GetGroupsForDiscussionQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getGroupsForDiscussion()))); + send_query(G()->net_query_creator().create(telegram_api::channels_getGroupsForDiscussion())); } void on_result(uint64 id, BufferSlice packet) override { @@ -2137,7 +2174,7 @@ class GetInactiveChannelsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getInactiveChannels()))); + send_query(G()->net_query_creator().create(telegram_api::channels_getInactiveChannels())); } void on_result(uint64 id, BufferSlice packet) override { @@ -2168,7 +2205,7 @@ class GetUsersQuery : public Td::ResultHandler { } void send(vector> &&input_users) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::users_getUsers(std::move(input_users))))); + send_query(G()->net_query_creator().create(telegram_api::users_getUsers(std::move(input_users)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2195,7 +2232,7 @@ class GetFullUserQuery : public Td::ResultHandler { } void send(tl_object_ptr &&input_user) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::users_getFullUser(std::move(input_user))))); + send_query(G()->net_query_creator().create(telegram_api::users_getFullUser(std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2232,7 +2269,7 @@ class GetUserPhotosQuery : public Td::ResultHandler { LOG(INFO) << "Get " << user_id << " profile photos with offset " << offset << " and limit " << limit << " from photo " << photo_id; send_query(G()->net_query_creator().create( - create_storer(telegram_api::photos_getUserPhotos(std::move(input_user), offset, photo_id, limit)))); + telegram_api::photos_getUserPhotos(std::move(input_user), offset, photo_id, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2275,7 +2312,7 @@ class GetChatsQuery : public Td::ResultHandler { } void send(vector &&chat_ids) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getChats(std::move(chat_ids))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getChats(std::move(chat_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2319,7 +2356,7 @@ class GetFullChatQuery : public Td::ResultHandler { void send(ChatId chat_id) { LOG(INFO) << "Send getFullChat query to get " << chat_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getFullChat(chat_id.get())))); + send_query(G()->net_query_creator().create(telegram_api::messages_getFullChat(chat_id.get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2355,8 +2392,7 @@ class GetChannelsQuery : public Td::ResultHandler { vector> input_channels; input_channels.push_back(std::move(input_channel)); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::channels_getChannels(std::move(input_channels))))); + send_query(G()->net_query_creator().create(telegram_api::channels_getChannels(std::move(input_channels)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2403,8 +2439,7 @@ class GetFullChannelQuery : public Td::ResultHandler { void send(ChannelId channel_id, tl_object_ptr &&input_channel) { channel_id_ = channel_id; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_getFullChannel(std::move(input_channel))))); + send_query(G()->net_query_creator().create(telegram_api::channels_getFullChannel(std::move(input_channel)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2445,7 +2480,7 @@ class GetChannelParticipantQuery : public Td::ResultHandler { channel_id_ = channel_id; user_id_ = user_id; send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_getParticipant(std::move(input_channel), std::move(input_user))))); + telegram_api::channels_getParticipant(std::move(input_channel), std::move(input_user)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2496,8 +2531,8 @@ class GetChannelParticipantsQuery : public Td::ResultHandler { offset_ = offset; limit_ = limit; random_id_ = random_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getParticipants( - std::move(input_channel), filter_.get_input_channel_participants_filter(), offset, limit, 0)))); + send_query(G()->net_query_creator().create(telegram_api::channels_getParticipants( + std::move(input_channel), filter_.get_input_channel_participants_filter(), offset, limit, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2553,9 +2588,9 @@ class GetChannelAdministratorsQuery : public Td::ResultHandler { hash = 0; // to load even only ranks or creator changed channel_id_ = channel_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getParticipants( + send_query(G()->net_query_creator().create(telegram_api::channels_getParticipants( std::move(input_channel), telegram_api::make_object(), 0, - std::numeric_limits::max(), hash)))); + std::numeric_limits::max(), hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2613,7 +2648,7 @@ class GetSupportUserQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getSupport()))); + send_query(G()->net_query_creator().create(telegram_api::help_getSupport())); } void on_result(uint64 id, BufferSlice packet) override { @@ -2635,6 +2670,140 @@ class GetSupportUserQuery : public Td::ResultHandler { } }; +tl_object_ptr ContactsManager::convert_stats_graph( + tl_object_ptr obj) { + CHECK(obj != nullptr); + + switch (obj->get_id()) { + case telegram_api::statsGraphAsync::ID: { + auto graph = move_tl_object_as(obj); + return make_tl_object(std::move(graph->token_)); + } + case telegram_api::statsGraphError::ID: { + auto graph = move_tl_object_as(obj); + return make_tl_object(std::move(graph->error_)); + } + case telegram_api::statsGraph::ID: { + auto graph = move_tl_object_as(obj); + return make_tl_object(std::move(graph->json_->data_), std::move(graph->zoom_token_)); + } + default: + UNREACHABLE(); + return nullptr; + } +} + +double ContactsManager::get_percentage_value(double part, double total) { + if (total < 1e-6 && total > -1e-6) { + if (part < 1e-6 && part > -1e-6) { + return 0.0; + } + return 100.0; + } + if (part > 1e20) { + return 100.0; + } + return part / total * 100; +} + +tl_object_ptr ContactsManager::convert_stats_absolute_value( + const tl_object_ptr &obj) { + return make_tl_object(obj->current_, obj->previous_, + get_percentage_value(obj->current_ - obj->previous_, obj->previous_)); +} + +tl_object_ptr ContactsManager::convert_broadcast_stats( + tl_object_ptr obj) { + CHECK(obj != nullptr); + + auto recent_message_interactions = transform(std::move(obj->recent_message_interactions_), [](auto &&interaction) { + return make_tl_object( + MessageId(ServerMessageId(interaction->msg_id_)).get(), interaction->views_, interaction->forwards_); + }); + + return make_tl_object( + make_tl_object(obj->period_->min_date_, obj->period_->max_date_), + convert_stats_absolute_value(obj->followers_), convert_stats_absolute_value(obj->views_per_post_), + convert_stats_absolute_value(obj->shares_per_post_), + get_percentage_value(obj->enabled_notifications_->part_, obj->enabled_notifications_->total_), + convert_stats_graph(std::move(obj->growth_graph_)), convert_stats_graph(std::move(obj->followers_graph_)), + convert_stats_graph(std::move(obj->mute_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), + convert_stats_graph(std::move(obj->views_by_source_graph_)), + convert_stats_graph(std::move(obj->new_followers_by_source_graph_)), + convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->interactions_graph_)), + convert_stats_graph(std::move(obj->iv_interactions_graph_)), std::move(recent_message_interactions)); +} + +class GetBroadcastStatsQuery : public Td::ResultHandler { + Promise> promise_; + ChannelId channel_id_; + + public: + explicit GetBroadcastStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, bool is_dark, DcId dc_id) { + channel_id_ = channel_id; + + auto input_channel = td->contacts_manager_->get_input_channel(channel_id); + CHECK(input_channel != nullptr); + + int32 flags = 0; + if (is_dark) { + flags |= telegram_api::stats_getBroadcastStats::DARK_MASK; + } + send_query(G()->net_query_creator().create( + telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), dc_id)); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + promise_.set_value(ContactsManager::convert_broadcast_stats(std::move(result))); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetBroadcastStatsQuery"); + promise_.set_error(std::move(status)); + } +}; + +class LoadAsyncGraphQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit LoadAsyncGraphQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &token, int64 x, DcId dc_id) { + int32 flags = 0; + if (x != 0) { + flags |= telegram_api::stats_loadAsyncGraph::X_MASK; + } + send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), dc_id)); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + promise_.set_value(ContactsManager::convert_stats_graph(std::move(result))); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + bool ContactsManager::UserFull::is_expired() const { return expires_at < Time::now(); } @@ -2690,6 +2859,22 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent was_online_local_ = G()->unix_time_cached() - 1; } + location_visibility_expire_date_ = + to_integer(G()->td_db()->get_binlog_pmc()->get("location_visibility_expire_date")); + if (location_visibility_expire_date_ != 0 && location_visibility_expire_date_ <= G()->unix_time()) { + location_visibility_expire_date_ = 0; + G()->td_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); + } + auto pending_location_visibility_expire_date_string = + G()->td_db()->get_binlog_pmc()->get("pending_location_visibility_expire_date"); + if (!pending_location_visibility_expire_date_string.empty()) { + pending_location_visibility_expire_date_ = to_integer(pending_location_visibility_expire_date_string); + try_send_set_location_visibility_query(); + } + update_is_location_visible(); + LOG(INFO) << "Loaded location_visibility_expire_date = " << location_visibility_expire_date_ + << " and pending_location_visibility_expire_date = " << pending_location_visibility_expire_date_; + user_online_timeout_.set_callback(on_user_online_timeout_callback); user_online_timeout_.set_callback_data(static_cast(this)); @@ -3378,6 +3563,7 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { bool has_bot_user_ids = !bot_user_ids.empty(); bool is_slow_mode_enabled = slow_mode_delay != 0; bool is_slow_mode_delay_active = slow_mode_next_send_date != 0; + bool has_stats_dc_id = stats_dc_id.is_exact(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_description); STORE_FLAG(has_administrator_count); @@ -3391,13 +3577,14 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { STORE_FLAG(can_get_participants); STORE_FLAG(can_set_username); STORE_FLAG(can_set_sticker_set); - STORE_FLAG(can_view_statistics); + STORE_FLAG(false); // legacy_can_view_statistics STORE_FLAG(is_all_history_available); STORE_FLAG(can_set_location); STORE_FLAG(has_location); STORE_FLAG(has_bot_user_ids); STORE_FLAG(is_slow_mode_enabled); STORE_FLAG(is_slow_mode_delay_active); + STORE_FLAG(has_stats_dc_id); END_STORE_FLAGS(); if (has_description) { store(description, storer); @@ -3440,6 +3627,9 @@ void ContactsManager::ChannelFull::store(StorerT &storer) const { store(slow_mode_next_send_date, storer); } store_time(expires_at, storer); + if (has_stats_dc_id) { + store(stats_dc_id.get_raw_id(), storer); + } } template @@ -3454,10 +3644,12 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { bool has_linked_channel_id; bool has_migrated_from_max_message_id; bool has_migrated_from_chat_id; + bool legacy_can_view_statistics; bool has_location; bool has_bot_user_ids; bool is_slow_mode_enabled; bool is_slow_mode_delay_active; + bool has_stats_dc_id; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_description); PARSE_FLAG(has_administrator_count); @@ -3471,13 +3663,14 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { PARSE_FLAG(can_get_participants); PARSE_FLAG(can_set_username); PARSE_FLAG(can_set_sticker_set); - PARSE_FLAG(can_view_statistics); + PARSE_FLAG(legacy_can_view_statistics); PARSE_FLAG(is_all_history_available); PARSE_FLAG(can_set_location); PARSE_FLAG(has_location); PARSE_FLAG(has_bot_user_ids); PARSE_FLAG(is_slow_mode_enabled); PARSE_FLAG(is_slow_mode_delay_active); + PARSE_FLAG(has_stats_dc_id); END_PARSE_FLAGS(); if (has_description) { parse(description, parser); @@ -3520,6 +3713,12 @@ void ContactsManager::ChannelFull::parse(ParserT &parser) { parse(slow_mode_next_send_date, parser); } parse_time(expires_at, parser); + if (has_stats_dc_id) { + stats_dc_id = DcId::create(parser.fetch_int()); + } + if (legacy_can_view_statistics) { + LOG(DEBUG) << "Ignore legacy can view statistics flag"; + } } template @@ -3752,11 +3951,13 @@ const DialogPhoto *ContactsManager::get_user_dialog_photo(UserId user_id) { return nullptr; } - auto it = pending_user_photos_.find(user_id); - if (it != pending_user_photos_.end()) { - do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo"); - pending_user_photos_.erase(it); - update_user(u, user_id); + if (!u->is_photo_inited) { + auto it = pending_user_photos_.find(user_id); + if (it != pending_user_photos_.end()) { + do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo"); + pending_user_photos_.erase(it); + update_user(u, user_id); + } } return &u->photo; } @@ -4204,7 +4405,7 @@ void ContactsManager::on_set_user_is_blocked_failed(UserId user_id, bool is_bloc LOG(WARNING) << "Receive error for SetUserIsBlockedQuery: " << error; on_update_user_is_blocked(user_id, !is_blocked); reload_user_full(user_id); - td_->messages_manager_->repair_dialog_action_bar(DialogId(user_id), "on_set_user_is_blocked_failed"); + td_->messages_manager_->reget_dialog_action_bar(DialogId(user_id), "on_set_user_is_blocked_failed"); } bool ContactsManager::is_valid_username(const string &username) { @@ -4610,7 +4811,7 @@ std::pair, vector> ContactsManager::change_imported_contac for (auto &contact : contacts) { if (contact == nullptr) { - promise.set_error(Status::Error(400, "Contacts should not be empty")); + promise.set_error(Status::Error(400, "Contacts must be non-empty")); return {}; } } @@ -4724,6 +4925,9 @@ void ContactsManager::on_update_contacts_reset() { CHECK(contacts_hints_.has_key(user_id.get())); } on_update_user_is_contact(u, user_id, false, false); + CHECK(u->is_is_contact_changed); + u->cache_version = 0; + u->is_repaired = false; update_user(u, user_id); CHECK(!u->is_contact); if (user_id != my_id) { @@ -4819,12 +5023,28 @@ void ContactsManager::search_dialogs_nearby(const Location &location, if (location.empty()) { return promise.set_error(Status::Error(400, "Invalid location specified")); } + last_user_location_ = location; + try_send_set_location_visibility_query(); auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( Result> result) mutable { send_closure(actor_id, &ContactsManager::on_get_dialogs_nearby, std::move(result), std::move(promise)); }); - td_->create_handler(std::move(query_promise))->send(location); + td_->create_handler(std::move(query_promise))->send(location, false, -1); +} + +void ContactsManager::set_location(const Location &location, Promise &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid location specified")); + } + last_user_location_ = location; + try_send_set_location_visibility_query(); + + auto query_promise = PromiseCreator::lambda( + [promise = std::move(promise)](Result> result) mutable { + promise.set_value(Unit()); + }); + td_->create_handler(std::move(query_promise))->send(location, true, -1); } vector> ContactsManager::get_chats_nearby_object( @@ -4863,13 +5083,22 @@ void ContactsManager::on_get_dialogs_nearby(Resultupdates_) { if (update_ptr->get_id() != telegram_api::updatePeerLocated::ID) { LOG(ERROR) << "Receive unexpected " << to_string(update); continue; } - on_update_peer_located(std::move(static_cast(update_ptr.get())->peers_), false); + auto expire_date = on_update_peer_located( + std::move(static_cast(update_ptr.get())->peers_), false); + if (expire_date != -1) { + location_visibility_expire_date = expire_date; + } + } + if (location_visibility_expire_date != location_visibility_expire_date_) { + set_location_visibility_expire_date(location_visibility_expire_date); + update_is_location_visible(); } std::sort(users_nearby_.begin(), users_nearby_.end()); @@ -4880,11 +5109,89 @@ void ContactsManager::on_get_dialogs_nearby(Result> &&peers, - bool from_update) { +void ContactsManager::set_location_visibility() { + bool is_location_visible = G()->shared_config().get_option_boolean("is_location_visible"); + auto pending_location_visibility_expire_date = is_location_visible ? std::numeric_limits::max() : 0; + if (pending_location_visibility_expire_date_ == -1 && + pending_location_visibility_expire_date == location_visibility_expire_date_) { + return; + } + if (pending_location_visibility_expire_date_ != pending_location_visibility_expire_date) { + pending_location_visibility_expire_date_ = pending_location_visibility_expire_date; + G()->td_db()->get_binlog_pmc()->set("pending_location_visibility_expire_date", + to_string(pending_location_visibility_expire_date)); + update_is_location_visible(); + } + try_send_set_location_visibility_query(); +} + +void ContactsManager::try_send_set_location_visibility_query() { + if (G()->close_flag()) { + return; + } + if (pending_location_visibility_expire_date_ == -1) { + return; + } + + if (is_set_location_visibility_request_sent_) { + return; + } + if (pending_location_visibility_expire_date_ != 0 && last_user_location_.empty()) { + return; + } + + is_set_location_visibility_request_sent_ = true; + auto query_promise = + PromiseCreator::lambda([actor_id = actor_id(this), set_expire_date = pending_location_visibility_expire_date_]( + Result> result) { + send_closure(actor_id, &ContactsManager::on_set_location_visibility_expire_date, set_expire_date, + result.is_ok() ? 0 : result.error().code()); + }); + td_->create_handler(std::move(query_promise)) + ->send(last_user_location_, true, pending_location_visibility_expire_date_); +} + +void ContactsManager::on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code) { + bool success = error_code == 0; + is_set_location_visibility_request_sent_ = false; + + if (set_expire_date != pending_location_visibility_expire_date_) { + try_send_set_location_visibility_query(); + return; + } + + if (success) { + set_location_visibility_expire_date(pending_location_visibility_expire_date_); + } else { + if (G()->close_flag()) { + // request will be re-sent after restart + return; + } + if (error_code != 406) { + LOG(ERROR) << "Failed to set location visibility expire date to " << pending_location_visibility_expire_date_; + } + } + G()->td_db()->get_binlog_pmc()->erase("pending_location_visibility_expire_date"); + pending_location_visibility_expire_date_ = -1; + update_is_location_visible(); +} + +int32 ContactsManager::on_update_peer_located(vector> &&peers, + bool from_update) { auto now = G()->unix_time(); bool need_update = false; - for (auto &peer_located : peers) { + int32 location_visibility_expire_date = -1; + for (auto &peer_located_ptr : peers) { + if (peer_located_ptr->get_id() == telegram_api::peerSelfLocated::ID) { + auto peer_self_located = telegram_api::move_object_as(peer_located_ptr); + if (peer_self_located->expires_ == 0 || peer_self_located->expires_ > G()->unix_time()) { + location_visibility_expire_date = peer_self_located->expires_; + } + continue; + } + + CHECK(peer_located_ptr->get_id() == telegram_api::peerLocated::ID); + auto peer_located = telegram_api::move_object_as(peer_located_ptr); DialogId dialog_id(peer_located->peer_); int32 expires_at = peer_located->expires_; int32 distance = peer_located->distance_; @@ -4928,6 +5235,7 @@ void ContactsManager::on_update_peer_located(vectormessages_manager_->force_create_dialog(dialog_id, "on_update_peer_located"); if (from_update) { + CHECK(dialog_type == DialogType::User); bool is_found = false; for (auto &dialog_nearby : users_nearby_) { if (dialog_nearby.dialog_id == dialog_id) { @@ -4941,17 +5249,43 @@ void ContactsManager::on_update_peer_located(vectortd_db()->get_binlog_pmc()->erase("location_visibility_expire_date"); + } else { + G()->td_db()->get_binlog_pmc()->set("location_visibility_expire_date", to_string(expire_date)); + } +} + +void ContactsManager::update_is_location_visible() { + auto expire_date = pending_location_visibility_expire_date_ != -1 ? pending_location_visibility_expire_date_ + : location_visibility_expire_date_; + G()->shared_config().set_option_boolean("is_location_visible", expire_date != 0); } void ContactsManager::set_profile_photo(const tl_object_ptr &input_photo, Promise &&promise) { @@ -5072,6 +5406,66 @@ void ContactsManager::set_username(const string &username, Promise &&promi td_->create_handler(std::move(promise))->send(username); } +void ContactsManager::set_commands(vector> &&commands, Promise &&promise) { + vector> new_commands; + for (auto &command : commands) { + if (command == nullptr) { + return promise.set_error(Status::Error(400, "Command must be non-empty")); + } + if (!clean_input_string(command->command_)) { + return promise.set_error(Status::Error(400, "Command must be encoded in UTF-8")); + } + if (!clean_input_string(command->description_)) { + return promise.set_error(Status::Error(400, "Command description must be encoded in UTF-8")); + } + + const size_t MAX_COMMAND_TEXT_LENGTH = 32; + command->command_ = trim(command->command_); + if (command->command_[0] == '/') { + command->command_ = command->command_.substr(1); + } + if (command->command_.empty()) { + return promise.set_error(Status::Error(400, "Command must be non-empty")); + } + if (utf8_length(command->command_) > MAX_COMMAND_TEXT_LENGTH) { + return promise.set_error( + Status::Error(400, PSLICE() << "Command length must not exceed " << MAX_COMMAND_TEXT_LENGTH)); + } + + const size_t MIN_COMMAND_DESCRIPTION_LENGTH = 3; + const size_t MAX_COMMAND_DESCRIPTION_LENGTH = 256; + command->description_ = trim(command->description_); + auto description_length = utf8_length(command->description_); + if (description_length < MIN_COMMAND_DESCRIPTION_LENGTH) { + return promise.set_error(Status::Error( + 400, PSLICE() << "Command description length must be at least " << MIN_COMMAND_DESCRIPTION_LENGTH)); + } + if (description_length > MAX_COMMAND_DESCRIPTION_LENGTH) { + return promise.set_error(Status::Error( + 400, PSLICE() << "Command description length must not exceed " << MAX_COMMAND_DESCRIPTION_LENGTH)); + } + + new_commands.emplace_back(std::move(command->command_), std::move(command->description_)); + } + + td_->create_handler(std::move(promise))->send(std::move(new_commands)); +} + +void ContactsManager::on_set_bot_commands_success(vector> &&commands) { + auto user_id = get_my_id(); + BotInfo *bot_info = get_bot_info_force(user_id); + if (bot_info == nullptr) { + return; + } + if (bot_info->commands == commands) { + return; + } + bot_info->commands = std::move(commands); + bot_info->is_changed = true; + + update_bot_info(bot_info, user_id, true, false); +} + void ContactsManager::set_chat_description(ChatId chat_id, const string &description, Promise &&promise) { auto new_description = strip_empty_characters(description, MAX_DESCRIPTION_LENGTH); auto c = get_chat(chat_id); @@ -5267,7 +5661,7 @@ void ContactsManager::set_channel_location(DialogId dialog_id, const DialogLocat } if (!dialog_id.is_valid()) { - return promise.set_error(Status::Error(400, "Invalid chat specified")); + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); } if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -5299,7 +5693,7 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow } if (!dialog_id.is_valid()) { - return promise.set_error(Status::Error(400, "Invalid chat specified")); + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); } if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -5324,6 +5718,105 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow td_->create_handler(std::move(promise))->send(channel_id, slow_mode_delay); } +void ContactsManager::get_channel_statistics_dc_id(DialogId dialog_id, Promise &&promise) { + if (!dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); + } + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(400, "Chat not found")); + } + + if (dialog_id.get_type() != DialogType::Channel) { + return promise.set_error(Status::Error(400, "Chat is not a channel")); + } + + auto channel_id = dialog_id.get_channel_id(); + const Channel *c = get_channel(channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Chat info not found")); + } + if (c->is_megagroup) { + return promise.set_error(Status::Error(400, "Chat is not a channel")); + } + + auto channel_full = get_channel_full_force(channel_id); + if (channel_full == nullptr || !channel_full->stats_dc_id.is_exact()) { + auto input_channel = get_input_channel(channel_id); + CHECK(input_channel != nullptr); + auto query_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), channel_id, promise = std::move(promise)](Result result) mutable { + send_closure(actor_id, &ContactsManager::get_channel_statistics_dc_id_impl, channel_id, std::move(promise)); + }); + + send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), std::move(query_promise), + "get_channel_statistics_dc_id"); + return; + } + + promise.set_value(DcId(channel_full->stats_dc_id)); +} + +void ContactsManager::get_channel_statistics_dc_id_impl(ChannelId channel_id, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + auto channel_full = get_channel_full(channel_id, "get_channel_statistics_dc_id_impl"); + if (channel_full == nullptr) { + return promise.set_error(Status::Error(400, "Chat full info not found")); + } + + if (!channel_full->stats_dc_id.is_exact()) { + return promise.set_error(Status::Error(400, "Chat statistics is not available")); + } + + promise.set_value(DcId(channel_full->stats_dc_id)); +} + +void ContactsManager::get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), dialog_id, is_dark, promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &ContactsManager::send_get_broadcast_stats_query, r_dc_id.move_as_ok(), + dialog_id.get_channel_id(), is_dark, std::move(promise)); + }); + get_channel_statistics_dc_id(dialog_id, std::move(dc_id_promise)); +} + +void ContactsManager::send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); +} + +void ContactsManager::load_statistics_graph(DialogId dialog_id, const string &token, int64 x, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), token, x, promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &ContactsManager::send_load_async_graph_query, r_dc_id.move_as_ok(), std::move(token), x, + std::move(promise)); + }); + get_channel_statistics_dc_id(dialog_id, std::move(dc_id_promise)); +} + +void ContactsManager::send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + td_->create_handler(std::move(promise))->send(token, x, dc_id); +} + void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, Promise &&promise) { auto c = get_channel(channel_id); @@ -6162,10 +6655,17 @@ void ContactsManager::on_deleted_contacts(const vector &deleted_contact_ LOG(INFO) << "Contacts deletion has finished for " << deleted_contact_user_ids; for (auto user_id : deleted_contact_user_ids) { - LOG(INFO) << "Drop contact with " << user_id; auto u = get_user(user_id); CHECK(u != nullptr); + if (!u->is_contact) { + continue; + } + + LOG(INFO) << "Drop contact with " << user_id; on_update_user_is_contact(u, user_id, false, false); + CHECK(u->is_is_contact_changed); + u->cache_version = 0; + u->is_repaired = false; update_user(u, user_id); CHECK(!u->is_contact); CHECK(!contacts_hints_.has_key(user_id.get())); @@ -6217,6 +6717,9 @@ void ContactsManager::on_get_contacts(tl_object_ptris_is_contact_changed); + u->cache_version = 0; + u->is_repaired = false; update_user(u, user_id); CHECK(!u->is_contact); if (user_id != my_id) { @@ -7220,6 +7723,15 @@ void ContactsManager::on_load_channel_from_database(ChannelId channel_id, string } else { CHECK(!c->is_saved); // channel can't be saved before load completes CHECK(!c->is_being_saved); + if (c->participant_count == 0 && !value.empty()) { + Channel temp_c; + log_event_parse(temp_c, value).ensure(); + if (temp_c.participant_count != 0) { + c->participant_count = temp_c.participant_count; + send_closure(G()->td(), &Td::send_update, + make_tl_object(get_supergroup_object(channel_id, c))); + } + } auto new_value = get_channel_database_value(c); if (value != new_value) { save_channel_to_database_impl(c, channel_id, std::move(new_value)); @@ -7549,7 +8061,7 @@ void ContactsManager::on_load_user_full_from_database(UserId user_id, string val } ContactsManager::UserFull *ContactsManager::get_user_full_force(UserId user_id) { - if (!user_id.is_valid()) { + if (!have_user_force(user_id)) { return nullptr; } @@ -7714,7 +8226,7 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val } ContactsManager::ChatFull *ContactsManager::get_chat_full_force(ChatId chat_id) { - if (!chat_id.is_valid()) { + if (!have_chat_force(chat_id)) { return nullptr; } @@ -7790,7 +8302,7 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s } ContactsManager::ChannelFull *ContactsManager::get_channel_full_force(ChannelId channel_id) { - if (!channel_id.is_valid()) { + if (!have_channel_force(channel_id)) { return nullptr; } @@ -7827,7 +8339,7 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo } if (u->is_is_contact_changed) { td_->messages_manager_->on_dialog_user_is_contact_updated(DialogId(user_id), u->is_contact); - if (u->is_contact) { + if (is_user_contact(u, user_id)) { 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); @@ -7860,7 +8372,7 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo add_user_photo_id(u, user_id, u->photo.id, dialog_photo_get_file_ids(u->photo)); - drop_user_photos(user_id, u->photo.id <= 0); + drop_user_photos(user_id, u->photo.id <= 0, "update_user"); } if (u->is_status_changed && user_id != get_my_id()) { auto left_time = get_user_was_online(u, user_id) - G()->server_time_cached(); @@ -8307,7 +8819,7 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u Photo photo = get_photo(td_->file_manager_.get(), std::move(user_full->profile_photo_), DialogId()); if (photo.id == -2) { - drop_user_photos(user_id, true); + drop_user_photos(user_id, true, "on_get_user_full"); } if (user_full->bot_info_ != nullptr) { if (on_update_bot_info(std::move(user_full->bot_info_), false)) { @@ -8359,6 +8871,8 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim return; } + LOG(INFO) << "Receive " << photo_count << " photos of " << user_id << " out of " << total_count << " with offset " + << offset << " and limit " << limit; UserPhotos *user_photos = &user_photos_[user_id]; user_photos->count = total_count; CHECK(user_photos->getting_now); @@ -8379,9 +8893,11 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim for (auto &photo : photos) { auto user_photo = get_photo(td_->file_manager_.get(), std::move(photo), DialogId()); if (user_photo.id == -2) { - LOG(ERROR) << "Have got empty profile photo in getUserPhotos request for " << user_id << " with offset " << offset + LOG(ERROR) << "Receive empty profile photo in getUserPhotos request for " << user_id << " with offset " << offset << " and limit " << limit << ". Receive " << photo_count << " photos out of " << total_count << " photos"; + user_photos->count--; + CHECK(user_photos->count >= 0); continue; } @@ -8479,6 +8995,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c Chat *c = get_chat(chat_id); if (c == nullptr) { LOG(ERROR) << "Can't find " << chat_id; + return promise.set_value(Unit()); } else if (c->version >= c->pinned_message_version) { LOG(INFO) << "Receive pinned " << pinned_message_id << " in " << chat_id << " with version " << c->version << ". Current version is " << c->pinned_message_version; @@ -8575,13 +9092,16 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c auto can_set_username = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_USERNAME) != 0; auto can_set_sticker_set = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_STICKER_SET) != 0; auto can_set_location = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_SET_LOCATION) != 0; - auto can_view_statistics = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS) != 0; auto is_all_history_available = (channel_full->flags_ & CHANNEL_FULL_FLAG_IS_ALL_HISTORY_HIDDEN) == 0; StickerSetId sticker_set_id; if (channel_full->stickerset_ != nullptr) { sticker_set_id = td_->stickers_manager_->on_get_sticker_set(std::move(channel_full->stickerset_), true, "on_get_channel_full"); } + DcId stats_dc_id; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS) != 0) { + stats_dc_id = DcId::create(channel_full->stats_dc_); + } ChannelFull *channel = add_channel_full(channel_id); channel->repair_request_version = 0; @@ -8590,7 +9110,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c channel->administrator_count != administrator_count || channel->restricted_count != restricted_count || channel->banned_count != banned_count || channel->can_get_participants != can_get_participants || channel->can_set_username != can_set_username || channel->can_set_sticker_set != can_set_sticker_set || - channel->can_set_location != can_set_location || channel->can_view_statistics != can_view_statistics || + channel->can_set_location != can_set_location || channel->stats_dc_id != stats_dc_id || channel->sticker_set_id != sticker_set_id || channel->is_all_history_available != is_all_history_available) { channel->description = std::move(channel_full->about_); channel->participant_count = participant_count; @@ -8601,7 +9121,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c channel->can_set_username = can_set_username; channel->can_set_sticker_set = can_set_sticker_set; channel->can_set_location = can_set_location; - channel->can_view_statistics = can_view_statistics; + channel->stats_dc_id = stats_dc_id; channel->is_all_history_available = is_all_history_available; channel->sticker_set_id = sticker_set_id; @@ -8798,7 +9318,7 @@ void ContactsManager::on_update_user_photo(User *u, UserId user_id, bool is_empty = photo == nullptr || photo->get_id() == telegram_api::userProfilePhotoEmpty::ID; pending_user_photos_[user_id] = std::move(photo); - drop_user_photos(user_id, is_empty); + drop_user_photos(user_id, is_empty, "on_update_user_photo"); return; } @@ -8813,6 +9333,8 @@ void ContactsManager::do_update_user_photo(User *u, UserId user_id, ProfilePhoto new_photo = get_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, std::move(photo)); if (new_photo != u->photo) { + LOG_IF(ERROR, u->access_hash == -1 && new_photo.small_file_id.is_valid()) + << "Update profile photo of " << user_id << " without access hash from " << source; u->photo = new_photo; u->is_photo_changed = true; LOG(DEBUG) << "Photo has changed for " << user_id; @@ -8851,7 +9373,9 @@ void ContactsManager::on_update_user_is_contact(User *u, UserId user_id, bool is if (u->is_contact != is_contact || u->is_mutual_contact != is_mutual_contact) { LOG(DEBUG) << "Update " << user_id << " is_contact from (" << u->is_contact << ", " << u->is_mutual_contact << ") to (" << is_contact << ", " << is_mutual_contact << ")"; - u->is_is_contact_changed |= (u->is_contact != is_contact); + if (u->is_contact != is_contact) { + u->is_is_contact_changed = true; + } u->is_contact = is_contact; u->is_mutual_contact = is_mutual_contact; u->is_changed = true; @@ -9056,6 +9580,12 @@ void ContactsManager::on_update_user_need_phone_number_privacy_exception(UserId void ContactsManager::on_update_user_full_need_phone_number_privacy_exception( UserFull *user_full, UserId user_id, bool need_phone_number_privacy_exception) { CHECK(user_full != nullptr); + if (need_phone_number_privacy_exception) { + const User *u = get_user(user_id); + if (u == nullptr || u->is_contact || user_id == get_my_id()) { + need_phone_number_privacy_exception = false; + } + } if (user_full->need_phone_number_privacy_exception != need_phone_number_privacy_exception) { user_full->need_phone_number_privacy_exception = need_phone_number_privacy_exception; user_full->is_changed = true; @@ -9077,7 +9607,7 @@ void ContactsManager::on_ignored_restriction_reasons_changed() { void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promise promise) { UserId my_id = get_my_id(); - drop_user_photos(my_id, false); + drop_user_photos(my_id, false, "on_delete_profile_photo"); if (G()->close_flag()) { return promise.set_value(Unit()); @@ -9086,22 +9616,26 @@ void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promisesecond; - user_photos->photos.clear(); - if (is_empty) { - user_photos->count = 0; - } else { - user_photos->count = -1; + int32 new_count = is_empty ? 0 : -1; + if (user_photos->count == new_count) { + CHECK(user_photos->photos.empty()); + CHECK(user_photos->offset == user_photos->count); + return; } + + LOG(INFO) << "Drop photos of " << user_id << " to " << (is_empty ? "empty" : "unknown") << " from " << source; + user_photos->photos.clear(); + user_photos->count = new_count; user_photos->offset = user_photos->count; } } void ContactsManager::drop_user_full(UserId user_id) { - drop_user_photos(user_id, false); + drop_user_photos(user_id, false, "drop_user_full"); bot_infos_.erase(user_id); if (G()->parameters().use_chat_info_db) { @@ -9379,19 +9913,11 @@ tl_object_ptr ContactsManager::get_chat_member_object( bool ContactsManager::on_get_channel_error(ChannelId channel_id, const Status &status, const string &source) { LOG(INFO) << "Receive " << status << " in " << channel_id << " from " << source; - if (status.code() == 401) { - // authorization is lost - return true; - } - if (status.code() == 420 || status.code() == 429) { - // flood wait - return true; - } if (status.message() == CSlice("BOT_METHOD_INVALID")) { LOG(ERROR) << "Receive BOT_METHOD_INVALID from " << source; return true; } - if (G()->close_flag()) { + if (G()->is_expected_error(status)) { return true; } if (status.message() == "CHANNEL_PRIVATE" || status.message() == "CHANNEL_PUBLIC_GROUP_NA") { @@ -9413,7 +9939,7 @@ bool ContactsManager::on_get_channel_error(ChannelId channel_id, const Status &s auto debug_channel_object = oneline(to_string(get_supergroup_object(channel_id, c))); if (c->status.is_member()) { LOG(INFO) << "Emulate leaving " << channel_id; - // TODO we also may try to write to public channel + // TODO we also may try to write to a public channel int32 flags = 0; if (c->is_megagroup) { flags |= CHANNEL_FLAG_IS_MEGAGROUP; @@ -9469,6 +9995,7 @@ void ContactsManager::on_get_channel_participants_success( vector result; for (auto &participant_ptr : participants) { + auto debug_participant = to_string(participant_ptr); result.push_back(get_dialog_participant(channel_id, std::move(participant_ptr))); if ((filter.is_bots() && !is_user_bot(result.back().user_id)) || (filter.is_administrators() && !result.back().status.is_administrator()) || @@ -9480,7 +10007,7 @@ void ContactsManager::on_get_channel_participants_success( (filter.is_contacts() && result.back().user_id == get_my_id()); if (!skip_error) { LOG(ERROR) << "Receive " << result.back() << ", while searching for " << filter << " in " << channel_id - << " with offset " << offset << " and limit " << limit; + << " with offset " << offset << " and limit " << limit << ": " << oneline(debug_participant); } result.pop_back(); total_count--; @@ -10270,8 +10797,8 @@ void ContactsManager::on_update_chat_edit_administrator(ChatId chat_id, UserId u : DialogParticipantStatus::Member(); if (version > c->version) { if (version != c->version + 1) { - LOG(ERROR) << "Administrators of " << chat_id << " with version " << c->version - << " has changed but new version is " << version; + LOG(INFO) << "Administrators of " << chat_id << " with version " << c->version + << " has changed, but new version is " << version; repair_chat_participants(chat_id); return; } @@ -10348,7 +10875,7 @@ void ContactsManager::on_update_chat_delete_user(ChatId chat_id, UserId user_id, update_chat_online_member_count(chat_full, chat_id, false); update_chat_full(chat_full, chat_id); - if (static_cast(chat_full->participants.size()) != c->participant_count) { + if (static_cast(chat_full->participants.size()) != c->participant_count) { repair_chat_participants(chat_id); } return; @@ -10417,8 +10944,8 @@ void ContactsManager::on_update_chat_default_permissions(ChatId chat_id, Restric // this should be unreachable, because version and default permissions must be already updated from // the chat object in on_chat_update if (version != c->version + 1) { - LOG(WARNING) << "Default permissions of " << chat_id << " with version " << c->version - << " has changed but new version is " << version; + LOG(INFO) << "Default permissions of " << chat_id << " with version " << c->version + << " has changed, but new version is " << version; repair_chat_participants(chat_id); return; } @@ -10473,8 +11000,8 @@ void ContactsManager::on_update_chat_pinned_message(ChatId chat_id, MessageId pi if (version >= c->pinned_message_version) { if (version != c->version + 1 && version != c->version) { - LOG(WARNING) << "Pinned message of " << chat_id << " with version " << c->version - << " has changed but new version is " << version; + LOG(INFO) << "Pinned message of " << chat_id << " with version " << c->version + << " has changed, but new version is " << version; repair_chat_participants(chat_id); } else if (version == c->version + 1) { c->version = version; @@ -10494,13 +11021,13 @@ void ContactsManager::on_update_chat_pinned_message(ChatId chat_id, MessageId pi void ContactsManager::on_update_chat_participant_count(Chat *c, ChatId chat_id, int32 participant_count, int32 version, const string &debug_str) { if (version <= -1) { - LOG(ERROR) << "Receive wrong version " << version << " in " << chat_id << " from " << debug_str; + LOG(ERROR) << "Receive wrong version " << version << " in " << chat_id << debug_str; return; } if (version < c->version) { // some outdated data - LOG(INFO) << "Receive member count of " << chat_id << " with version " << version << " from " << debug_str + LOG(INFO) << "Receive member count of " << chat_id << " with version " << version << debug_str << ", but current version is " << c->version; return; } @@ -10510,7 +11037,7 @@ void ContactsManager::on_update_chat_participant_count(Chat *c, ChatId chat_id, // version is not changed when deleted user is removed from the chat LOG_IF(ERROR, c->participant_count != participant_count + 1) << "Member count of " << chat_id << " has changed from " << c->participant_count << " to " - << participant_count << ", but version " << c->version << " remains unchanged in " << debug_str; + << participant_count << ", but version " << c->version << " remains unchanged" << debug_str; repair_chat_participants(chat_id); } @@ -10601,8 +11128,8 @@ bool ContactsManager::on_update_chat_full_participants_short(ChatFull *chat_full return true; } - LOG(ERROR) << "Member count of " << chat_id << " with version " << chat_full->version - << " has changed but new version is " << version; + LOG(INFO) << "Member count of " << chat_id << " with version " << chat_full->version + << " has changed, but new version is " << version; repair_chat_participants(chat_id); return false; } @@ -10965,7 +11492,7 @@ bool ContactsManager::is_user_status_exact(UserId user_id) const { bool ContactsManager::can_report_user(UserId user_id) const { auto u = get_user(user_id); - return u != nullptr && !u->is_deleted && u->is_bot && !u->is_support; + return u != nullptr && !u->is_deleted && !u->is_support && (u->is_bot || all_users_nearby_.count(user_id) != 0); } const ContactsManager::User *ContactsManager::get_user(UserId user_id) const { @@ -11104,7 +11631,7 @@ void ContactsManager::reload_user(UserId user_id, Promise &&promise) { td_->create_handler(std::move(promise))->send(std::move(users)); } -bool ContactsManager::get_user_full(UserId user_id, Promise &&promise) { +bool ContactsManager::get_user_full(UserId user_id, bool force, Promise &&promise) { auto u = get_user(user_id); if (u == nullptr) { promise.set_error(Status::Error(6, "User not found")); @@ -11125,7 +11652,7 @@ bool ContactsManager::get_user_full(UserId user_id, Promise &&promise) { if (user_full->is_expired() || is_bot_info_expired(user_id, u->bot_info_version)) { auto input_user = get_input_user(user_id); CHECK(input_user != nullptr); - if (td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot() && !force) { send_get_user_full_query(user_id, std::move(input_user), std::move(promise), "get expired user_full"); return false; } else { @@ -11206,6 +11733,8 @@ std::pair> ContactsManager::get_user_profile_photos return result; } + get_user_dialog_photo(user_id); // apply pending user photo + auto user_photos = &user_photos_[user_id]; if (user_photos->getting_now) { promise.set_error(Status::Error(400, "Request for new profile photos has already been sent")); @@ -11405,7 +11934,7 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha return false; } -bool ContactsManager::get_chat_full(ChatId chat_id, Promise &&promise) { +bool ContactsManager::get_chat_full(ChatId chat_id, bool force, Promise &&promise) { auto c = get_chat(chat_id); if (c == nullptr) { promise.set_error(Status::Error(6, "Group not found")); @@ -11421,7 +11950,7 @@ bool ContactsManager::get_chat_full(ChatId chat_id, Promise &&promise) { if (is_chat_full_outdated(chat_full, c, chat_id)) { LOG(INFO) << "Have outdated full " << chat_id; - if (td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot() && !force) { send_get_chat_full_query(chat_id, std::move(promise), "get expired chat_full"); return false; } else { @@ -11714,7 +12243,7 @@ ContactsManager::ChannelFull *ContactsManager::add_channel_full(ChannelId channe return channel_full_ptr.get(); } -bool ContactsManager::get_channel_full(ChannelId channel_id, Promise &&promise) { +bool ContactsManager::get_channel_full(ChannelId channel_id, bool force, Promise &&promise) { auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { auto input_channel = get_input_channel(channel_id); @@ -11727,7 +12256,7 @@ bool ContactsManager::get_channel_full(ChannelId channel_id, Promise &&pro return false; } if (channel_full->is_expired()) { - if (td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot() && !force) { auto input_channel = get_input_channel(channel_id); CHECK(input_channel != nullptr); send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), std::move(promise), @@ -11890,7 +12419,7 @@ DialogParticipant ContactsManager::get_chat_participant(ChatId chat_id, UserId u LOG(INFO) << "Trying to get " << user_id << " as member of " << chat_id; if (force) { promise.set_value(Unit()); - } else if (!get_chat_full(chat_id, std::move(promise))) { + } else if (!get_chat_full(chat_id, force, std::move(promise))) { return DialogParticipant(); } // promise is already set @@ -11915,7 +12444,7 @@ std::pair> ContactsManager::search_chat_partici if (force) { promise.set_value(Unit()); - } else if (!get_chat_full(chat_id, std::move(promise))) { + } else if (!get_chat_full(chat_id, force, std::move(promise))) { return {}; } // promise is already set @@ -12081,7 +12610,7 @@ std::pair> ContactsManager::get_channel_partici } if (channel_full != nullptr && !channel_full->is_expired() && !channel_full->can_get_participants) { - promise.set_error(Status::Error(3, "Supergroup members are unavailable")); + promise.set_error(Status::Error(3, "Member list is inaccessible")); return result; } @@ -12233,7 +12762,7 @@ void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector void ContactsManager::reload_dialog_administrators(DialogId dialog_id, int32 hash, Promise &&promise) { switch (dialog_id.get_type()) { case DialogType::Chat: - get_chat_full(dialog_id.get_chat_id(), std::move(promise)); + get_chat_full(dialog_id.get_chat_id(), false, std::move(promise)); break; case DialogType::Channel: td_->create_handler(std::move(promise))->send(dialog_id.get_channel_id(), hash); @@ -12263,7 +12792,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc return; } - DialogParticipantStatus status = [&]() { + DialogParticipantStatus status = [&] { bool is_creator = 0 != (chat.flags_ & CHAT_FLAG_USER_IS_CREATOR); bool has_left = 0 != (chat.flags_ & CHAT_FLAG_USER_HAS_LEFT); bool was_kicked = 0 != (chat.flags_ & CHAT_FLAG_USER_WAS_KICKED); @@ -12427,7 +12956,7 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char is_slow_mode_enabled = false; } - DialogParticipantStatus status = [&]() { + DialogParticipantStatus status = [&] { bool has_left = (channel.flags_ & CHANNEL_FLAG_USER_HAS_LEFT) != 0; bool is_creator = (channel.flags_ & CHANNEL_FLAG_USER_IS_CREATOR) != 0; @@ -12830,7 +13359,7 @@ tl_object_ptr ContactsManager::get_supergroup_full_i channel_full->restricted_count, channel_full->banned_count, DialogId(channel_full->linked_channel_id).get(), channel_full->slow_mode_delay, slow_mode_delay_expires_in, channel_full->can_get_participants, channel_full->can_set_username, channel_full->can_set_sticker_set, channel_full->can_set_location, - channel_full->can_view_statistics, channel_full->is_all_history_available, channel_full->sticker_set_id.get(), + channel_full->stats_dc_id.is_exact(), channel_full->is_all_history_available, channel_full->sticker_set_id.get(), channel_full->location.get_chat_location_object(), channel_full->invite_link, get_basic_group_id_object(channel_full->migrated_from_chat_id, "get_supergroup_full_info_object"), channel_full->migrated_from_max_message_id.get()); diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index d91844db..ffe34550 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -21,6 +21,7 @@ #include "td/telegram/files/FileSourceId.h" #include "td/telegram/Location.h" #include "td/telegram/MessageId.h" +#include "td/telegram/net/DcId.h" #include "td/telegram/Photo.h" #include "td/telegram/PublicDialogType.h" #include "td/telegram/QueryCombiner.h" @@ -163,6 +164,7 @@ class ContactsManager : public Actor { void on_get_chat_full(tl_object_ptr &&chat_full, Promise &&promise); void on_update_profile_success(int32 flags, const string &first_name, const string &last_name, const string &about); + void on_set_bot_commands_success(vector> &&commands); void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username); void on_update_user_phone_number(UserId user_id, string &&phone_number); @@ -196,7 +198,7 @@ class ContactsManager : public Actor { void on_update_channel_default_permissions(ChannelId channel_id, RestrictedRights default_permissions); void on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count); - void on_update_peer_located(vector> &&peers, bool from_update); + int32 on_update_peer_located(vector> &&peers, bool from_update); void on_update_dialog_administrators(DialogId dialog_id, vector &&administrators, bool have_access); @@ -313,6 +315,10 @@ class ContactsManager : public Actor { void search_dialogs_nearby(const Location &location, Promise> &&promise); + void set_location(const Location &location, Promise &&promise); + + void set_location_visibility(); + void set_profile_photo(const tl_object_ptr &input_photo, Promise &&promise); void delete_profile_photo(int64 profile_photo_id, Promise &&promise); @@ -325,6 +331,8 @@ class ContactsManager : public Actor { void set_username(const string &username, Promise &&promise); + void set_commands(vector> &&commands, Promise &&promise); + void set_chat_description(ChatId chat_id, const string &description, Promise &&promise); void set_channel_username(ChannelId channel_id, const string &username, Promise &&promise); @@ -349,6 +357,12 @@ class ContactsManager : public Actor { void delete_channel(ChannelId channel_id, Promise &&promise); + void get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise); + + void load_statistics_graph(DialogId dialog_id, const string &token, int64 x, + Promise> &&promise); + void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise); void add_channel_participant(ChannelId channel_id, UserId user_id, Promise &&promise, @@ -414,7 +428,7 @@ class ContactsManager : public Actor { UserId get_me(Promise &&promise); bool get_user(UserId user_id, int left_tries, Promise &&promise); void reload_user(UserId user_id, Promise &&promise); - bool get_user_full(UserId user_id, Promise &&promise); + bool get_user_full(UserId user_id, bool force, Promise &&promise); void reload_user_full(UserId user_id); std::pair> get_user_profile_photos(UserId user_id, int32 offset, int32 limit, @@ -426,7 +440,7 @@ class ContactsManager : public Actor { bool have_chat_force(ChatId chat_id); bool get_chat(ChatId chat_id, int left_tries, Promise &&promise); void reload_chat(ChatId chat_id, Promise &&promise); - bool get_chat_full(ChatId chat_id, Promise &&promise); + bool get_chat_full(ChatId chat_id, bool force, Promise &&promise); bool get_chat_is_active(ChatId chat_id) const; DialogParticipantStatus get_chat_status(ChatId chat_id) const; @@ -439,7 +453,7 @@ class ContactsManager : public Actor { bool have_channel_force(ChannelId channel_id); bool get_channel(ChannelId channel_id, int left_tries, Promise &&promise); void reload_channel(ChannelId chnanel_id, Promise &&promise); - bool get_channel_full(ChannelId channel_id, Promise &&promise); + bool get_channel_full(ChannelId channel_id, bool force, Promise &&promise); bool is_channel_public(ChannelId channel_id) const; @@ -525,6 +539,16 @@ class ContactsManager : public Actor { void get_current_state(vector> &updates) const; + static tl_object_ptr convert_stats_graph(tl_object_ptr obj); + + static double get_percentage_value(double new_value, double old_value); + + static tl_object_ptr convert_stats_absolute_value( + const tl_object_ptr &obj); + + static tl_object_ptr convert_broadcast_stats( + tl_object_ptr obj); + private: struct User { string first_name; @@ -767,6 +791,8 @@ class ContactsManager : public Actor { DialogLocation location; + DcId stats_dc_id; + int32 slow_mode_delay = 0; int32 slow_mode_next_send_date = 0; @@ -779,7 +805,6 @@ class ContactsManager : public Actor { bool can_set_username = false; bool can_set_sticker_set = false; bool can_set_location = false; - bool can_view_statistics = false; bool is_all_history_available = true; bool is_slow_mode_next_send_date_changed = true; @@ -1065,7 +1090,7 @@ class ContactsManager : public Actor { void on_update_user_full_common_chat_count(UserFull *user_full, UserId user_id, int32 common_chat_count); void on_update_user_full_need_phone_number_privacy_exception(UserFull *user_full, UserId user_id, bool need_phone_number_privacy_exception); - void drop_user_photos(UserId user_id, bool is_empty); + void drop_user_photos(UserId user_id, bool is_empty, const char *source); void drop_user_full(UserId user_id); void on_set_user_is_blocked_failed(UserId user_id, bool is_blocked, Status error); @@ -1238,6 +1263,14 @@ class ContactsManager : public Actor { void on_get_dialogs_nearby(Result> result, Promise> &&promise); + void try_send_set_location_visibility_query(); + + void on_set_location_visibility_expire_date(int32 set_expire_date, int32 error_code); + + void set_location_visibility_expire_date(int32 expire_date); + + void update_is_location_visible(); + static bool is_channel_public(const Channel *c); static bool is_valid_invite_link(const string &invite_link); @@ -1305,6 +1338,16 @@ class ContactsManager : public Actor { tl_object_ptr input_check_password, Promise &&promise); + void get_channel_statistics_dc_id(DialogId dialog_id, Promise &&promise); + + void get_channel_statistics_dc_id_impl(ChannelId channel_id, Promise &&promise); + + void send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise); + + void send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise); + static void on_user_online_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); static void on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long); @@ -1425,6 +1468,12 @@ class ContactsManager : public Actor { vector users_nearby_; vector channels_nearby_; + std::unordered_set all_users_nearby_; + + int32 location_visibility_expire_date_ = 0; + int32 pending_location_visibility_expire_date_ = -1; + bool is_set_location_visibility_request_sent_ = false; + Location last_user_location_; std::unordered_map linked_channel_ids_; diff --git a/td/telegram/DeviceTokenManager.cpp b/td/telegram/DeviceTokenManager.cpp index 4806ff19..04422328 100644 --- a/td/telegram/DeviceTokenManager.cpp +++ b/td/telegram/DeviceTokenManager.cpp @@ -373,12 +373,12 @@ void DeviceTokenManager::loop() { auto other_user_ids = info.other_user_ids; if (info.state == TokenInfo::State::Unregister) { net_query = G()->net_query_creator().create( - create_storer(telegram_api::account_unregisterDevice(token_type, info.token, std::move(other_user_ids)))); + telegram_api::account_unregisterDevice(token_type, info.token, std::move(other_user_ids))); } else { int32 flags = telegram_api::account_registerDevice::NO_MUTED_MASK; - net_query = G()->net_query_creator().create(create_storer( + net_query = G()->net_query_creator().create( telegram_api::account_registerDevice(flags, false /*ignored*/, token_type, info.token, info.is_app_sandbox, - BufferSlice(info.encryption_key), std::move(other_user_ids)))); + BufferSlice(info.encryption_key), std::move(other_user_ids))); } info.net_query_id = net_query->id(); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, token_type)); @@ -418,7 +418,7 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { info.state = TokenInfo::State::Sync; } else { if (r_flag.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(r_flag.error())) { LOG(ERROR) << "Failed to " << info.state << " device: " << r_flag.error(); } info.promise.set_error(r_flag.move_as_error()); diff --git a/td/telegram/DialogDb.cpp b/td/telegram/DialogDb.cpp index b5c7d4ac..a37a8e07 100644 --- a/td/telegram/DialogDb.cpp +++ b/td/telegram/DialogDb.cpp @@ -38,19 +38,19 @@ Status init_dialog_db(SqliteDb &db, int32 version, bool &was_created) { version = 0; } - auto create_notification_group_table = [&db]() { + auto create_notification_group_table = [&db] { return db.exec( "CREATE TABLE IF NOT EXISTS notification_groups (notification_group_id INT4 PRIMARY KEY, dialog_id " "INT8, last_notification_date INT4)"); }; - auto create_last_notification_date_index = [&db]() { + auto create_last_notification_date_index = [&db] { return db.exec( "CREATE INDEX IF NOT EXISTS notification_group_by_last_notification_date ON notification_groups " "(last_notification_date, dialog_id, notification_group_id) WHERE last_notification_date IS NOT NULL"); }; - auto add_dialogs_in_folder_index = [&db]() { + auto add_dialogs_in_folder_index = [&db] { return db.exec( "CREATE INDEX IF NOT EXISTS dialog_in_folder_by_dialog_order ON dialogs (folder_id, dialog_order, dialog_id) " "WHERE folder_id IS NOT NULL"); @@ -75,7 +75,7 @@ Status init_dialog_db(SqliteDb &db, int32 version, bool &was_created) { TRY_STATUS(db.exec("DROP INDEX IF EXISTS dialog_by_dialog_order")); TRY_STATUS(db.exec("ALTER TABLE dialogs ADD COLUMN folder_id INT4")); TRY_STATUS(add_dialogs_in_folder_index()); - TRY_STATUS(db.exec("UPDATE dialogs SET folder_id = 0 WHERE dialog_id < -1500000000000 AND dialog_order != 0")); + TRY_STATUS(db.exec("UPDATE dialogs SET folder_id = 0 WHERE dialog_id < -1500000000000 AND dialog_order > 0")); } return Status::OK(); @@ -129,7 +129,7 @@ class DialogDbImpl : public DialogDbSyncInterface { TRY_RESULT_ASSIGN( get_secret_chat_count_stmt_, db_.get_statement( - "SELECT COUNT(*) FROM dialogs WHERE folder_id = ?1 AND dialog_order != 0 AND dialog_id < -1500000000000")); + "SELECT COUNT(*) FROM dialogs WHERE folder_id = ?1 AND dialog_order > 0 AND dialog_id < -1500000000000")); // LOG(ERROR) << get_dialog_stmt_.explain().ok(); // LOG(ERROR) << get_dialogs_stmt_.explain().ok(); diff --git a/td/telegram/DraftMessage.cpp b/td/telegram/DraftMessage.cpp index badc2a77..c166ac29 100644 --- a/td/telegram/DraftMessage.cpp +++ b/td/telegram/DraftMessage.cpp @@ -18,7 +18,7 @@ td_api::object_ptr get_draft_message_object(const unique_p if (draft_message == nullptr) { return nullptr; } - return td_api::make_object(draft_message->reply_to_message_id.get(), + return td_api::make_object(draft_message->reply_to_message_id.get(), draft_message->date, get_input_message_text_object(draft_message->input_message_text)); } @@ -51,7 +51,7 @@ unique_ptr get_draft_message(ContactsManager *contacts_manager, if (!clean_input_string(draft->message_)) { draft->message_.clear(); } - entities.clear(); + entities = find_entities(draft->message_, false); } result->input_message_text.text = FormattedText{std::move(draft->message_), std::move(entities)}; result->input_message_text.disable_web_page_preview = (flags & telegram_api::draftMessage::NO_WEBPAGE_MASK) != 0; diff --git a/td/telegram/Global.cpp b/td/telegram/Global.cpp index ea10f23e..804d4c9b 100644 --- a/td/telegram/Global.cpp +++ b/td/telegram/Global.cpp @@ -102,6 +102,9 @@ Status Global::init(const TdParameters ¶meters, ActorId td, unique_ptr system_time) { double time_backwards_fix = saved_diff.system_time - system_time; @@ -118,6 +121,8 @@ Status Global::init(const TdParameters ¶meters, ActorId td, unique_ptr= 1500000000 && system_time >= 1500000000) { // only for saved_diff.system_time == 0 + diff = default_time_difference; } LOG(DEBUG) << "LOAD: " << tag("server_time_difference", diff); server_time_difference_ = diff; @@ -129,8 +134,10 @@ Status Global::init(const TdParameters ¶meters, ActorId td, unique_ptr(server_time); } @@ -250,4 +257,8 @@ void Global::add_location_access_hash(double latitude, double longitude, int64 a location_access_hashes_[get_location_key(latitude, longitude)] = access_hash; } +double get_server_time() { + return G()->server_time(); +} + } // namespace td diff --git a/td/telegram/Global.h b/td/telegram/Global.h index 1092da8e..1dfd3348 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -343,6 +343,19 @@ class Global : public ActorContext { return close_flag_.load(); } + bool is_expected_error(const Status &error) const { + CHECK(error.is_error()); + if (error.code() == 401) { + // authorization is lost + return true; + } + if (error.code() == 420 || error.code() == 429) { + // flood wait + return true; + } + return close_flag(); + } + const std::vector> &get_net_stats_file_callbacks() { return net_stats_file_callbacks_; } @@ -398,6 +411,8 @@ class Global : public ActorContext { std::atomic dns_time_difference_was_updated_{false}; std::atomic close_flag_{false}; std::atomic system_time_saved_at_{-1e10}; + double saved_diff_ = 0.0; + double saved_system_time_ = 0.0; #if !TD_HAVE_ATOMIC_SHARED_PTR std::mutex dh_config_mutex_; @@ -418,7 +433,7 @@ class Global : public ActorContext { std::unordered_map location_access_hashes_; - static int32 to_unix_time(double server_time); + int32 to_unix_time(double server_time) const; void do_save_server_time_difference(); @@ -434,4 +449,6 @@ inline Global *G_impl(const char *file, int line) { return static_cast(context); } +double get_server_time(); + } // namespace td diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index a3e9723c..ebfffa7b 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -80,9 +80,9 @@ class GetInlineBotResultsQuery : public Td::ResultHandler { input_peer = make_tl_object(); } - auto net_query = G()->net_query_creator().create(create_storer(telegram_api::messages_getInlineBotResults( + auto net_query = G()->net_query_creator().create(telegram_api::messages_getInlineBotResults( flags, std::move(bot_input_user), std::move(input_peer), - user_location.empty() ? nullptr : user_location.get_input_geo_point(), query, offset))); + user_location.empty() ? nullptr : user_location.get_input_geo_point(), query, offset)); auto result = net_query.get_weak(); net_query->need_resend_on_503 = false; send_query(std::move(net_query)); @@ -135,9 +135,9 @@ class SetInlineBotResultsQuery : public Td::ResultHandler { flags |= telegram_api::messages_setInlineBotResults::SWITCH_PM_MASK; inline_bot_switch_pm = make_tl_object(switch_pm_text, switch_pm_parameter); } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_setInlineBotResults( + send_query(G()->net_query_creator().create(telegram_api::messages_setInlineBotResults( flags, false /*ignored*/, false /*ignored*/, inline_query_id, std::move(results), cache_time, next_offset, - std::move(inline_bot_switch_pm))))); + std::move(inline_bot_switch_pm)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -450,7 +450,7 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe return promise.set_error(Status::Error(400, "Field \"phone_number\" must contain a valid phone number")); } if (first_name.empty()) { - return promise.set_error(Status::Error(400, "Field \"first_name\" should be non-empty")); + return promise.set_error(Status::Error(400, "Field \"first_name\" must be non-empty")); } title = last_name.empty() ? first_name : first_name + " " + last_name; description = std::move(phone_number); @@ -633,7 +633,7 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe } auto inline_message = r_inline_message.move_as_ok(); if (inline_message->get_id() == telegram_api::inputBotInlineMessageMediaAuto::ID && file_type == FileType::Temp) { - return promise.set_error(Status::Error(400, "Sent message content should be explicitly specified")); + return promise.set_error(Status::Error(400, "Sent message content must be explicitly specified")); } if (duration < 0) { diff --git a/td/telegram/InputMessageText.cpp b/td/telegram/InputMessageText.cpp index a6f12085..ac847ecc 100644 --- a/td/telegram/InputMessageText.cpp +++ b/td/telegram/InputMessageText.cpp @@ -6,6 +6,8 @@ // #include "td/telegram/InputMessageText.h" +#include "td/telegram/ConfigShared.h" +#include "td/telegram/Global.h" #include "td/telegram/MessageEntity.h" #include "td/utils/common.h" @@ -37,10 +39,18 @@ Result process_input_message_text(const ContactsManager *conta } TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_))); - TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, false, - need_skip_bot_commands(contacts_manager, dialog_id, is_bot), for_draft)); - return InputMessageText{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)}, + auto need_skip_commands = need_skip_bot_commands(contacts_manager, dialog_id, is_bot); + bool parse_markdown = G()->shared_config().get_option_boolean("always_parse_markdown"); + TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, parse_markdown, + need_skip_commands, for_draft)); + InputMessageText result{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)}, input_message_text->disable_web_page_preview_, input_message_text->clear_draft_}; + if (G()->shared_config().get_option_boolean("always_parse_markdown")) { + result.text = parse_markdown_v3(std::move(result.text)); + fix_formatted_text(result.text.text, result.text.entities, for_draft, false, need_skip_commands, for_draft) + .ensure(); + } + return std::move(result); } td_api::object_ptr get_input_message_text_object(const InputMessageText &input_message_text) { diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index 25375701..0fff0d78 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -10,7 +10,6 @@ #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/misc.h" -#include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/Td.h" @@ -383,9 +382,8 @@ void LanguagePackManager::send_language_get_difference_query(Language *language, std::move(language_code), result->version_, true, vector(), std::move(result->strings_), Promise>()); }); - send_with_promise(G()->net_query_creator().create( - create_storer(telegram_api::langpack_getDifference(language_pack_, language_code, version)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), + send_with_promise(G()->net_query_creator().create_unauth( + telegram_api::langpack_getDifference(language_pack_, language_code, version)), std::move(request_promise)); } @@ -798,8 +796,7 @@ void LanguagePackManager::get_languages(bool only_local, send_closure(actor_id, &LanguagePackManager::on_get_languages, r_result.move_as_ok(), std::move(language_pack), false, std::move(promise)); }); - send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::langpack_getLanguages(language_pack_)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), + send_with_promise(G()->net_query_creator().create_unauth(telegram_api::langpack_getLanguages(language_pack_)), std::move(request_promise)); } @@ -821,8 +818,7 @@ void LanguagePackManager::search_language_info(string language_code, std::move(language_code), std::move(promise)); }); send_with_promise( - G()->net_query_creator().create(create_storer(telegram_api::langpack_getLanguage(language_pack_, language_code)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), + G()->net_query_creator().create_unauth(telegram_api::langpack_getLanguage(language_pack_, language_code)), std::move(request_promise)); } @@ -1082,10 +1078,9 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector std::move(language_code), result->version_, false, vector(), std::move(result->strings_), std::move(promise)); }); - send_with_promise(G()->net_query_creator().create( - create_storer(telegram_api::langpack_getLangPack(language_pack_, language_code)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), - std::move(request_promise)); + send_with_promise( + G()->net_query_creator().create_unauth(telegram_api::langpack_getLangPack(language_pack_, language_code)), + std::move(request_promise)); } else { auto request_promise = PromiseCreator::lambda([actor_id = actor_id(this), language_pack = language_pack_, language_code, keys, @@ -1098,9 +1093,8 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector send_closure(actor_id, &LanguagePackManager::on_get_language_pack_strings, std::move(language_pack), std::move(language_code), -1, false, std::move(keys), r_result.move_as_ok(), std::move(promise)); }); - send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::langpack_getStrings( - language_pack_, language_code, std::move(keys))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off), + send_with_promise(G()->net_query_creator().create_unauth( + telegram_api::langpack_getStrings(language_pack_, language_code, std::move(keys))), std::move(request_promise)); } } diff --git a/td/telegram/Logging.cpp b/td/telegram/Logging.cpp index 41040be8..ec9d2fed 100644 --- a/td/telegram/Logging.cpp +++ b/td/telegram/Logging.cpp @@ -59,7 +59,7 @@ Status Logging::set_current_stream(td_api::object_ptr stream) auto file_stream = td_api::move_object_as(stream); auto max_log_file_size = file_stream->max_file_size_; if (max_log_file_size <= 0) { - return Status::Error("Max log file size should be positive"); + return Status::Error("Max log file size must be positive"); } TRY_STATUS(file_log.init(file_stream->path_, max_log_file_size)); diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index 330eb25e..4b291875 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -643,6 +643,19 @@ class MessagePoll : public MessageContent { } }; +class MessageDice : public MessageContent { + public: + int32 dice_value = 0; + + MessageDice() = default; + explicit MessageDice(int32 dice_value) : dice_value(dice_value) { + } + + MessageContentType get_type() const override { + return MessageContentType::Dice; + } +}; + template static void store(const MessageContent *content, StorerT &storer) { CHECK(content != nullptr); @@ -900,6 +913,11 @@ static void store(const MessageContent *content, StorerT &storer) { store(m->poll_id, storer); break; } + case MessageContentType::Dice: { + auto m = static_cast(content); + store(m->dice_value, storer); + break; + } default: UNREACHABLE(); } @@ -1242,6 +1260,13 @@ static void parse(unique_ptr &content, ParserT &parser) { content = std::move(m); break; } + case MessageContentType::Dice: { + auto m = make_unique(); + parse(m->dice_value, parser); + is_bad = m->dice_value < 0 || m->dice_value > 6; + content = std::move(m); + break; + } default: LOG(FATAL) << "Have unknown message content type " << static_cast(content_type); } @@ -1330,9 +1355,9 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id, } case telegram_api::botInlineMessageMediaAuto::ID: { auto input_message_media_auto = move_tl_object_as(inline_message); - auto caption = - get_message_text(td->contacts_manager_.get(), input_message_media_auto->message_, - std::move(input_message_media_auto->entities_), true, 0, "register_inline_message_content"); + auto caption = get_message_text(td->contacts_manager_.get(), input_message_media_auto->message_, + std::move(input_message_media_auto->entities_), true, 0, false, + "register_inline_message_content"); if (allowed_media_content_id == td_api::inputMessageAnimation::ID) { result.message_content = make_unique(file_id, std::move(caption)); } else if (allowed_media_content_id == td_api::inputMessageAudio::ID) { @@ -1450,6 +1475,9 @@ static Result create_input_message_content( content = make_unique(file_id, std::move(caption)); break; } + case td_api::inputMessageDice::ID: + content = make_unique(); + break; case td_api::inputMessageDocument::ID: td->documents_manager_->create_document(file_id, string(), thumbnail, std::move(file_name), std::move(mime_type), false); @@ -1903,6 +1931,7 @@ bool can_have_input_media(const Td *td, const MessageContent *content) { case MessageContentType::Animation: case MessageContentType::Audio: case MessageContentType::Contact: + case MessageContentType::Dice: case MessageContentType::Document: case MessageContentType::Invoice: case MessageContentType::LiveLocation: @@ -1981,6 +2010,7 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td, return td->voice_notes_manager_->get_secret_input_media(m->file_id, std::move(input_file), m->caption.text); } case MessageContentType::Call: + case MessageContentType::Dice: case MessageContentType::Game: case MessageContentType::Invoice: case MessageContentType::LiveLocation: @@ -2113,6 +2143,8 @@ static tl_object_ptr get_input_media_impl( auto m = static_cast(content); return m->contact.get_input_media_contact(); } + case MessageContentType::Dice: + return make_tl_object(); case MessageContentType::Document: { auto m = static_cast(content); return td->documents_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail)); @@ -2268,6 +2300,7 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) { return td->video_notes_manager_->delete_video_note_thumbnail(m->file_id); } case MessageContentType::Contact: + case MessageContentType::Dice: case MessageContentType::Game: case MessageContentType::Invoice: case MessageContentType::LiveLocation: @@ -2436,6 +2469,7 @@ static int32 get_message_content_media_index_mask(const MessageContent *content, case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: return 0; default: UNREACHABLE(); @@ -2512,11 +2546,16 @@ bool get_message_content_poll_is_anonymous(const Td *td, const MessageContent *c } } +bool has_message_content_web_page(const MessageContent *content) { + if (content->get_type() == MessageContentType::Text) { + return static_cast(content)->web_page_id.is_valid(); + } + return false; +} + void remove_message_content_web_page(MessageContent *content) { CHECK(content->get_type() == MessageContentType::Text); - auto &web_page_id = static_cast(content)->web_page_id; - CHECK(web_page_id.is_valid()); - web_page_id = WebPageId(); + static_cast(content)->web_page_id = WebPageId(); } void set_message_content_poll_answer(Td *td, const MessageContent *content, FullMessageId full_message_id, @@ -3034,6 +3073,14 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo } break; } + case MessageContentType::Dice: { + auto old_ = static_cast(old_content); + auto new_ = static_cast(new_content); + if (old_->dice_value != new_->dice_value) { + need_update = true; + } + break; + } case MessageContentType::Unsupported: { auto old_ = static_cast(old_content); auto new_ = static_cast(new_content); @@ -3165,6 +3212,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type; break; default: @@ -3174,20 +3222,22 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File return false; } -void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id) { +void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, + const char *source) { switch (content->get_type()) { case MessageContentType::Text: return td->web_pages_manager_->register_web_page(static_cast(content)->web_page_id, - full_message_id); + full_message_id, source); case MessageContentType::Poll: - return td->poll_manager_->register_poll(static_cast(content)->poll_id, full_message_id); + return td->poll_manager_->register_poll(static_cast(content)->poll_id, full_message_id, + source); default: return; } } void reregister_message_content(Td *td, const MessageContent *old_content, const MessageContent *new_content, - FullMessageId full_message_id) { + FullMessageId full_message_id, const char *source) { auto old_content_type = old_content->get_type(); auto new_content_type = new_content->get_type(); if (old_content_type == new_content_type) { @@ -3208,41 +3258,24 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const return; } } - unregister_message_content(td, old_content, full_message_id); - register_message_content(td, new_content, full_message_id); + unregister_message_content(td, old_content, full_message_id, source); + register_message_content(td, new_content, full_message_id, source); } -void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id) { +void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, + const char *source) { switch (content->get_type()) { case MessageContentType::Text: return td->web_pages_manager_->unregister_web_page(static_cast(content)->web_page_id, - full_message_id); + full_message_id, source); case MessageContentType::Poll: - return td->poll_manager_->unregister_poll(static_cast(content)->poll_id, full_message_id); + return td->poll_manager_->unregister_poll(static_cast(content)->poll_id, full_message_id, + source); default: return; } } -static FormattedText get_secret_media_caption(string &&message_text, string &&message_caption) { - // message_text was already cleaned - if (!clean_input_string(message_caption)) { - message_caption.clear(); - } - - FormattedText caption; - if (message_text.empty()) { - caption.text = std::move(message_caption); - } else if (message_caption.empty()) { - caption.text = std::move(message_text); - } else { - caption.text = message_text + "\n\n" + message_caption; - } - - caption.entities = find_entities(caption.text, false); - return caption; -} - template static tl_object_ptr secret_to_telegram(FromT &from); @@ -3444,17 +3477,6 @@ static unique_ptr get_document_message_content(Document &&parsed } } -static unique_ptr get_secret_document_message_content( - Td *td, tl_object_ptr file, - tl_object_ptr &&document, - vector> &&attributes, DialogId owner_dialog_id, - FormattedText &&caption, bool is_opened) { - return get_document_message_content( - td->documents_manager_->on_get_document({std::move(file), std::move(document), std::move(attributes)}, - owner_dialog_id), - std::move(caption), is_opened); -} - static unique_ptr get_document_message_content(Td *td, tl_object_ptr &&document, DialogId owner_dialog_id, FormattedText &&caption, bool is_opened, @@ -3469,6 +3491,35 @@ unique_ptr get_secret_message_content( tl_object_ptr &&media, vector> &&secret_entities, DialogId owner_dialog_id, MultiPromiseActor &load_data_multipromise) { + int32 constructor_id = media == nullptr ? secret_api::decryptedMessageMediaEmpty::ID : media->get_id(); + auto caption = [&] { + switch (constructor_id) { + case secret_api::decryptedMessageMediaVideo::ID: { + auto video = static_cast(media.get()); + return std::move(video->caption_); + } + case secret_api::decryptedMessageMediaPhoto::ID: { + auto photo = static_cast(media.get()); + return std::move(photo->caption_); + } + case secret_api::decryptedMessageMediaDocument::ID: { + auto document = static_cast(media.get()); + return std::move(document->caption_); + } + default: + return string(); + } + }(); + if (!clean_input_string(caption)) { + caption.clear(); + } + + if (message_text.empty()) { + message_text = std::move(caption); + } else if (!caption.empty()) { + message_text = message_text + "\n\n" + caption; + } + auto entities = get_message_entities(std::move(secret_entities)); auto status = fix_formatted_text(message_text, entities, true, false, true, false); if (status.is_error()) { @@ -3477,20 +3528,7 @@ unique_ptr get_secret_message_content( if (!clean_input_string(message_text)) { message_text.clear(); } - entities.clear(); - } - - if (media == nullptr) { - return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); - } - - int32 constructor_id = media->get_id(); - if (message_text.size()) { - if (constructor_id != secret_api::decryptedMessageMediaEmpty::ID) { - LOG(INFO) << "Receive non-empty message text and media"; - } else { - return create_text_message_content(std::move(message_text), std::move(entities), WebPageId()); - } + entities = find_entities(message_text, true); } // support of old layer and old constructions @@ -3502,7 +3540,7 @@ unique_ptr get_secret_message_content( make_tl_object(video->duration_, video->w_, video->h_)); media = make_tl_object( std::move(video->thumb_), video->thumb_w_, video->thumb_h_, video->mime_type_, video->size_, - std::move(video->key_), std::move(video->iv_), std::move(attributes), std::move(video->caption_)); + std::move(video->key_), std::move(video->iv_), std::move(attributes), string()); constructor_id = secret_api::decryptedMessageMediaDocument::ID; break; @@ -3512,7 +3550,9 @@ unique_ptr get_secret_message_content( bool is_media_empty = false; switch (constructor_id) { case secret_api::decryptedMessageMediaEmpty::ID: - LOG(ERROR) << "Receive empty message text and media"; + if (message_text.empty()) { + LOG(ERROR) << "Receive empty message text and media"; + } is_media_empty = true; break; case secret_api::decryptedMessageMediaGeoPoint::ID: { @@ -3612,7 +3652,7 @@ unique_ptr get_secret_message_content( auto message_photo = move_tl_object_as(media); return make_unique( get_encrypted_file_photo(td->file_manager_.get(), std::move(file), std::move(message_photo), owner_dialog_id), - get_secret_media_caption(std::move(message_text), std::move(message_photo->caption_))); + FormattedText{std::move(message_text), std::move(entities)}); } case secret_api::decryptedMessageMediaDocument::ID: { auto message_document = move_tl_object_as(media); @@ -3621,9 +3661,9 @@ unique_ptr get_secret_message_content( } auto attributes = secret_to_telegram(message_document->attributes_); message_document->attributes_.clear(); - return get_secret_document_message_content( - td, std::move(file), std::move(message_document), std::move(attributes), owner_dialog_id, - get_secret_media_caption(std::move(message_text), std::move(message_document->caption_)), false); + auto document = td->documents_manager_->on_get_document( + {std::move(file), std::move(message_document), std::move(attributes)}, owner_dialog_id); + return get_document_message_content(std::move(document), {std::move(message_text), std::move(entities)}, false); } default: LOG(ERROR) << "Unsupported: " << to_string(media); @@ -3635,21 +3675,17 @@ unique_ptr get_message_content(Td *td, FormattedText message, tl_object_ptr &&media, DialogId owner_dialog_id, bool is_content_read, UserId via_bot_user_id, int32 *ttl) { - if (media == nullptr) { - return make_unique(std::move(message), WebPageId()); + if (!td->auth_manager_->is_authorized() && !G()->close_flag() && media != nullptr) { + LOG(ERROR) << "Receive without authorization " << to_string(media); + media = nullptr; } - int32 constructor_id = media->get_id(); - if (message.text.size()) { - if (constructor_id != telegram_api::messageMediaEmpty::ID) { - LOG(INFO) << "Receive non-empty message text and media for message from " << owner_dialog_id; - } else { - return make_unique(std::move(message), WebPageId()); - } - } + int32 constructor_id = media == nullptr ? telegram_api::messageMediaEmpty::ID : media->get_id(); switch (constructor_id) { case telegram_api::messageMediaEmpty::ID: - LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id; + if (message.text.empty()) { + LOG(ERROR) << "Receive empty message text and media for message from " << owner_dialog_id; + } return make_unique(std::move(message), WebPageId()); case telegram_api::messageMediaPhoto::ID: { auto message_photo = move_tl_object_as(media); @@ -3672,6 +3708,16 @@ unique_ptr get_message_content(Td *td, FormattedText message, } return make_unique(std::move(photo), std::move(message)); } + case telegram_api::messageMediaDice::ID: { + auto message_dice = move_tl_object_as(media); + + auto m = make_unique(message_dice->value_); + if (m->dice_value < 0 || m->dice_value > 6) { + break; + } + + return std::move(m); + } case telegram_api::messageMediaGeo::ID: { auto message_geo_point = move_tl_object_as(media); @@ -3854,6 +3900,12 @@ unique_ptr dup_message_content(Td *td, DialogId dialog_id, const } case MessageContentType::Contact: return make_unique(*static_cast(content)); + case MessageContentType::Dice: + if (type != MessageContentDupType::Forward) { + return make_unique(); + } else { + return make_unique(*static_cast(content)); + } case MessageContentType::Document: { auto result = make_unique(*static_cast(content)); if (remove_caption) { @@ -4383,6 +4435,10 @@ tl_object_ptr get_message_content_object(const MessageCo const MessagePoll *m = static_cast(content); return make_tl_object(td->poll_manager_->get_poll_object(m->poll_id)); } + case MessageContentType::Dice: { + const MessageDice *m = static_cast(content); + return make_tl_object(m->dice_value); + } default: UNREACHABLE(); return nullptr; @@ -4493,7 +4549,7 @@ void update_message_content_file_id_remote(MessageContent *content, FileId file_ if (file_id.get_remote() == 0) { return; } - FileId *old_file_id = [&]() { + FileId *old_file_id = [&] { switch (content->get_type()) { case MessageContentType::Animation: return &static_cast(content)->file_id; @@ -4675,6 +4731,7 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte case MessageContentType::WebsiteConnected: case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: + case MessageContentType::Dice: return string(); default: UNREACHABLE(); @@ -4866,6 +4923,8 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC case MessageContentType::Poll: // no need to add poll dependencies, because they are forcely loaded with the poll break; + case MessageContentType::Dice: + break; default: UNREACHABLE(); break; diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index 87b40875..3b4ef1bb 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -135,6 +135,8 @@ bool get_message_content_poll_is_closed(const Td *td, const MessageContent *cont bool get_message_content_poll_is_anonymous(const Td *td, const MessageContent *content); +bool has_message_content_web_page(const MessageContent *content); + void remove_message_content_web_page(MessageContent *content); void set_message_content_poll_answer(Td *td, const MessageContent *content, FullMessageId full_message_id, @@ -153,12 +155,13 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo bool merge_message_content_file_id(Td *td, MessageContent *message_content, FileId new_file_id); -void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id); +void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, const char *source); void reregister_message_content(Td *td, const MessageContent *old_content, const MessageContent *new_content, - FullMessageId full_message_id); + FullMessageId full_message_id, const char *source); -void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id); +void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id, + const char *source); unique_ptr get_secret_message_content( Td *td, string message_text, tl_object_ptr file, diff --git a/td/telegram/MessageContentType.cpp b/td/telegram/MessageContentType.cpp index 8d58addb..a0e0808a 100644 --- a/td/telegram/MessageContentType.cpp +++ b/td/telegram/MessageContentType.cpp @@ -94,6 +94,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType cont return string_builder << "PassportDataReceived"; case MessageContentType::Poll: return string_builder << "Poll"; + case MessageContentType::Dice: + return string_builder << "Dice"; default: UNREACHABLE(); return string_builder; @@ -144,6 +146,7 @@ bool is_allowed_media_group_content(MessageContentType content_type) { case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: return false; default: UNREACHABLE(); @@ -198,6 +201,7 @@ bool is_secret_message_content(int32 ttl, MessageContentType content_type) { case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: return false; default: UNREACHABLE(); @@ -226,6 +230,7 @@ bool is_service_message_content(MessageContentType content_type) { case MessageContentType::ExpiredPhoto: case MessageContentType::ExpiredVideo: case MessageContentType::Poll: + case MessageContentType::Dice: return false; case MessageContentType::ChatCreate: case MessageContentType::ChatChangeTitle: @@ -300,6 +305,7 @@ bool can_have_message_content_caption(MessageContentType content_type) { case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: return false; default: UNREACHABLE(); diff --git a/td/telegram/MessageContentType.h b/td/telegram/MessageContentType.h index 240b58b9..b62c5b13 100644 --- a/td/telegram/MessageContentType.h +++ b/td/telegram/MessageContentType.h @@ -53,7 +53,8 @@ enum class MessageContentType : int32 { WebsiteConnected, PassportDataSent, PassportDataReceived, - Poll + Poll, + Dice }; StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type); diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index 2a7c2b59..4524c8ce 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -64,6 +65,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty return string_builder << "Cashtag"; case MessageEntity::Type::PhoneNumber: return string_builder << "PhoneNumber"; + case MessageEntity::Type::BankCardNumber: + return string_builder << "BankCardNumber"; default: UNREACHABLE(); return string_builder << "Impossible"; @@ -114,11 +117,14 @@ tl_object_ptr MessageEntity::get_text_entity_type_object case MessageEntity::Type::TextUrl: return make_tl_object(argument); case MessageEntity::Type::MentionName: + // can't use contacts_manager, because can be called from a static request return make_tl_object(user_id.get()); case MessageEntity::Type::Cashtag: return make_tl_object(); case MessageEntity::Type::PhoneNumber: return make_tl_object(); + case MessageEntity::Type::BankCardNumber: + return make_tl_object(); default: UNREACHABLE(); return nullptr; @@ -143,6 +149,14 @@ vector> get_text_entities_object(const vector< return result; } +StringBuilder &operator<<(StringBuilder &string_builder, const FormattedText &text) { + return string_builder << '"' << text.text << "\" with entities " << text.entities; +} + +td_api::object_ptr get_formatted_text_object(const FormattedText &text) { + return td_api::make_object(text.text, get_text_entities_object(text.entities)); +} + static bool is_word_character(uint32 code) { switch (get_unicode_simple_category(code)) { case UnicodeSimpleCategory::Letter: @@ -154,10 +168,6 @@ static bool is_word_character(uint32 code) { } } -td_api::object_ptr get_formatted_text_object(const FormattedText &text) { - return td_api::make_object(text.text, get_text_entities_object(text.entities)); -} - /* static bool is_word_boundary(uint32 a, uint32 b) { return is_word_character(a) ^ is_word_character(b); @@ -406,6 +416,51 @@ static vector match_cashtags(Slice str) { return result; } +static vector match_bank_card_numbers(Slice str) { + vector result; + const unsigned char *begin = str.ubegin(); + const unsigned char *end = str.uend(); + const unsigned char *ptr = begin; + + // '/[\d- ]{13,}/' + + while (true) { + while (ptr != end && !is_digit(*ptr)) { + ptr++; + } + if (ptr == end) { + break; + } + + auto card_number_begin = ptr; + size_t digit_count = 0; + while (ptr != end && (is_digit(*ptr) || *ptr == ' ' || *ptr == '-')) { + if (*ptr == ' ' && digit_count >= 16 && digit_count <= 19 && + digit_count == static_cast(ptr - card_number_begin)) { + // continuous card number + break; + } + digit_count += static_cast(is_digit(*ptr)); + ptr++; + } + if (digit_count < 13 || digit_count > 19) { + continue; + } + + auto card_number_end = ptr; + while (!is_digit(card_number_end[-1])) { + card_number_end--; + } + auto card_number_size = static_cast(card_number_end - card_number_begin); + if (card_number_size > 2 * digit_count - 1) { + continue; + } + + result.emplace_back(card_number_begin, card_number_end); + } + return result; +} + static vector match_urls(Slice str) { vector result; const unsigned char *begin = str.ubegin(); @@ -477,9 +532,15 @@ static vector match_urls(Slice str) { while (true) { auto dot_pos = str.find('.'); - if (dot_pos > str.size()) { + if (dot_pos > str.size() || dot_pos + 1 == str.size()) { break; } + if (str[dot_pos + 1] == ' ') { + // fast path + str = str.substr(dot_pos + 2); + begin = str.ubegin(); + continue; + } const unsigned char *last_at_ptr = nullptr; const unsigned char *domain_end_ptr = begin + dot_pos; @@ -637,6 +698,60 @@ static vector match_urls(Slice str) { return result; } +static bool is_valid_bank_card(Slice str) { + const size_t MIN_CARD_LENGTH = 13; + const size_t MAX_CARD_LENGTH = 19; + char digits[MAX_CARD_LENGTH]; + size_t digit_count = 0; + for (auto c : str) { + if (is_digit(c)) { + CHECK(digit_count < MAX_CARD_LENGTH); + digits[digit_count++] = c; + } + } + CHECK(digit_count >= MIN_CARD_LENGTH); + + // Luhn algorithm + int32 sum = 0; + for (size_t i = digit_count; i > 0; i--) { + int32 digit = digits[i - 1] - '0'; + if ((digit_count - i) % 2 == 0) { + sum += digit; + } else { + sum += (digit < 5 ? 2 * digit : 2 * digit - 9); + } + } + if (sum % 10 != 0) { + return false; + } + + int32 prefix1 = (digits[0] - '0'); + int32 prefix2 = prefix1 * 10 + (digits[1] - '0'); + int32 prefix3 = prefix2 * 10 + (digits[2] - '0'); + int32 prefix4 = prefix3 * 10 + (digits[3] - '0'); + if (prefix1 == 4) { + // Visa + return digit_count == 13 || digit_count == 16 || digit_count == 18 || digit_count == 19; + } + if ((51 <= prefix2 && prefix2 <= 55) || (2221 <= prefix4 && prefix4 <= 2720)) { + // mastercard + return digit_count == 16; + } + if (prefix2 == 34 || prefix2 == 37) { + // American Express + return digit_count == 15; + } + if (prefix2 == 62 || prefix2 == 81) { + // UnionPay + return digit_count >= 16; + } + if (2200 <= prefix4 && prefix4 <= 2204) { + // MIR + return digit_count == 16; + } + return true; // skip length check +} + bool is_email_address(Slice str) { // /^([a-z0-9_-]{0,26}[.+]){0,10}[a-z0-9_-]{1,35}@(([a-z0-9][a-z0-9_-]{0,28})?[a-z0-9][.]){1,6}[a-z]{2,6}$/i Slice userdata; @@ -1010,6 +1125,16 @@ vector find_cashtags(Slice str) { return match_cashtags(str); } +vector find_bank_card_numbers(Slice str) { + vector result; + for (auto bank_card : match_bank_card_numbers(str)) { + if (is_valid_bank_card(bank_card)) { + result.emplace_back(bank_card); + } + } + return result; +} + vector> find_urls(Slice str) { vector> result; for (auto url : match_urls(str)) { @@ -1027,54 +1152,162 @@ vector> find_urls(Slice str) { return result; } -// keeps nested, but removes mutually intersecting and empty entities -// entities must be pre-sorted -static void remove_unallowed_entities(vector &entities) { - vector nested_entities_stack; - size_t left_entities = 0; - for (size_t i = 0; i < entities.size(); i++) { - if (entities[i].offset < 0 || entities[i].length <= 0 || entities[i].offset > 1000000 || - entities[i].length > 1000000) { - continue; - } +static int32 text_length(Slice text) { + return narrow_cast(utf8_utf16_length(text)); +} +static void sort_entities(vector &entities) { + if (std::is_sorted(entities.begin(), entities.end())) { + return; + } + + std::sort(entities.begin(), entities.end()); +} + +#define check_is_sorted(entities) check_is_sorted_impl(entities, __LINE__) +static void check_is_sorted_impl(const vector &entities, int line) { + LOG_CHECK(std::is_sorted(entities.begin(), entities.end())) << line << " " << entities; +} + +#define check_non_intersecting(entities) check_non_intersecting_impl(entities, __LINE__) +static void check_non_intersecting_impl(const vector &entities, int line) { + for (size_t i = 0; i + 1 < entities.size(); i++) { + LOG_CHECK(entities[i].offset + entities[i].length <= entities[i + 1].offset) << line << " " << entities; + } +} + +static constexpr int32 get_entity_type_mask(MessageEntity::Type type) { + return 1 << static_cast(type); +} + +static constexpr int32 get_splittable_entities_mask() { + return get_entity_type_mask(MessageEntity::Type::Bold) | get_entity_type_mask(MessageEntity::Type::Italic) | + get_entity_type_mask(MessageEntity::Type::Underline) | + get_entity_type_mask(MessageEntity::Type::Strikethrough); +} + +static constexpr int32 get_blockquote_entities_mask() { + return get_entity_type_mask(MessageEntity::Type::BlockQuote); +} + +static constexpr int32 get_continuous_entities_mask() { + return get_entity_type_mask(MessageEntity::Type::Mention) | get_entity_type_mask(MessageEntity::Type::Hashtag) | + get_entity_type_mask(MessageEntity::Type::BotCommand) | get_entity_type_mask(MessageEntity::Type::Url) | + get_entity_type_mask(MessageEntity::Type::EmailAddress) | get_entity_type_mask(MessageEntity::Type::TextUrl) | + get_entity_type_mask(MessageEntity::Type::MentionName) | get_entity_type_mask(MessageEntity::Type::Cashtag) | + get_entity_type_mask(MessageEntity::Type::PhoneNumber) | + get_entity_type_mask(MessageEntity::Type::BankCardNumber); +} + +static constexpr int32 get_pre_entities_mask() { + return get_entity_type_mask(MessageEntity::Type::Pre) | get_entity_type_mask(MessageEntity::Type::Code) | + get_entity_type_mask(MessageEntity::Type::PreCode); +} + +static constexpr int32 get_user_entities_mask() { + return get_splittable_entities_mask() | get_blockquote_entities_mask() | + get_entity_type_mask(MessageEntity::Type::TextUrl) | get_entity_type_mask(MessageEntity::Type::MentionName) | + get_pre_entities_mask(); +} + +static int32 is_splittable_entity(MessageEntity::Type type) { + return (get_entity_type_mask(type) & get_splittable_entities_mask()) != 0; +} + +static int32 is_blockquote_entity(MessageEntity::Type type) { + return type == MessageEntity::Type::BlockQuote; +} + +static int32 is_continuous_entity(MessageEntity::Type type) { + return (get_entity_type_mask(type) & get_continuous_entities_mask()) != 0; +} + +static int32 is_pre_entity(MessageEntity::Type type) { + return (get_entity_type_mask(type) & get_pre_entities_mask()) != 0; +} + +static int32 is_user_entity(MessageEntity::Type type) { + return (get_entity_type_mask(type) & get_user_entities_mask()) != 0; +} + +static constexpr size_t SPLITTABLE_ENTITY_TYPE_COUNT = 4; + +static size_t get_splittable_entity_type_index(MessageEntity::Type type) { + if (static_cast(type) <= static_cast(MessageEntity::Type::Bold) + 1) { + // Bold or Italic + return static_cast(type) - static_cast(MessageEntity::Type::Bold); + } else { + // Underline or Strikethrough + return static_cast(type) - static_cast(MessageEntity::Type::Underline) + 2; + } +} + +static bool are_entities_valid(const vector &entities) { + if (entities.empty()) { + return true; + } + check_is_sorted(entities); + + int32 end_pos[SPLITTABLE_ENTITY_TYPE_COUNT]; + std::fill_n(end_pos, SPLITTABLE_ENTITY_TYPE_COUNT, -1); + vector nested_entities_stack; + int32 nested_entity_type_mask = 0; + for (auto &entity : entities) { while (!nested_entities_stack.empty() && - entities[i].offset >= nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { + entity.offset >= nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { // remove non-intersecting entities from the stack + nested_entity_type_mask -= get_entity_type_mask(nested_entities_stack.back()->type); nested_entities_stack.pop_back(); } if (!nested_entities_stack.empty()) { - // entity intersects some previous entity - if (entities[i].offset + entities[i].length > - nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { - // it must be nested - continue; + if (entity.offset + entity.length > nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { + // entity intersects some previous entity + return false; + } + if ((nested_entity_type_mask & get_entity_type_mask(entity.type)) != 0) { + // entity has the same type as one of the previous nested + return false; } auto parent_type = nested_entities_stack.back()->type; - if (entities[i].type == parent_type) { - // the type must be different - continue; - } - if (parent_type == MessageEntity::Type::Code || parent_type == MessageEntity::Type::Pre || - parent_type == MessageEntity::Type::PreCode) { + if (is_pre_entity(parent_type)) { // Pre and Code can't contain nested entities - continue; + return false; + } + // parents are not pre after this point + if (is_pre_entity(entity.type) && (nested_entity_type_mask & ~get_blockquote_entities_mask()) != 0) { + // Pre and Code can't be contained in other entities, except blockquote + return false; + } + if ((is_continuous_entity(entity.type) || is_blockquote_entity(entity.type)) && + (nested_entity_type_mask & get_continuous_entities_mask()) != 0) { + // continuous and blockquote can't be contained in continuous + return false; + } + if ((nested_entity_type_mask & get_splittable_entities_mask()) != 0) { + // the previous nested entity may be needed to splitted for consistency + // alternatively, better entity merging needs to be implemented + return false; } } - if (i != left_entities) { - entities[left_entities] = std::move(entities[i]); + if (is_splittable_entity(entity.type)) { + auto index = get_splittable_entity_type_index(entity.type); + if (end_pos[index] >= entity.offset) { + // the entities can be merged + return false; + } + end_pos[index] = entity.offset + entity.length; } - nested_entities_stack.push_back(&entities[left_entities++]); + nested_entities_stack.push_back(&entity); + nested_entity_type_mask += get_entity_type_mask(entity.type); } - - entities.erase(entities.begin() + left_entities, entities.end()); + return true; } // removes all intersecting entities, including nested -// entities must be pre-sorted and pre-validated static void remove_intersecting_entities(vector &entities) { + check_is_sorted(entities); int32 last_entity_end = 0; size_t left_entities = 0; for (size_t i = 0; i < entities.size(); i++) { @@ -1090,48 +1323,58 @@ static void remove_intersecting_entities(vector &entities) { entities.erase(entities.begin() + left_entities, entities.end()); } -static void fix_entities(vector &entities) { - if (entities.empty()) { +// continuous_entities and blockquote_entities must be pre-sorted and non-overlapping +static void remove_entities_intersecting_blockquote(vector &entities, + const vector &blockquote_entities) { + check_non_intersecting(entities); + check_non_intersecting(blockquote_entities); + if (blockquote_entities.empty()) { // fast path return; } - std::sort(entities.begin(), entities.end()); - - remove_unallowed_entities(entities); + auto blockquote_it = blockquote_entities.begin(); + size_t left_entities = 0; + for (size_t i = 0; i < entities.size(); i++) { + while (blockquote_it != blockquote_entities.end() && + (blockquote_it->type != MessageEntity::Type::BlockQuote || + blockquote_it->offset + blockquote_it->length <= entities[i].offset)) { + blockquote_it++; + } + if (blockquote_it != blockquote_entities.end() && + (blockquote_it->offset + blockquote_it->length < entities[i].offset + entities[i].length || + (entities[i].offset < blockquote_it->offset && + blockquote_it->offset < entities[i].offset + entities[i].length))) { + continue; + } + if (i != left_entities) { + entities[left_entities] = std::move(entities[i]); + } + left_entities++; + } + entities.erase(entities.begin() + left_entities, entities.end()); } vector find_entities(Slice text, bool skip_bot_commands, bool only_urls) { vector entities; if (!only_urls) { - auto mentions = find_mentions(text); - for (auto &mention : mentions) { - entities.emplace_back(MessageEntity::Type::Mention, narrow_cast(mention.begin() - text.begin()), - narrow_cast(mention.size())); - } - - if (!skip_bot_commands) { - auto bot_commands = find_bot_commands(text); - for (auto &bot_command : bot_commands) { - entities.emplace_back(MessageEntity::Type::BotCommand, narrow_cast(bot_command.begin() - text.begin()), - narrow_cast(bot_command.size())); + auto add_entities = [&entities, &text](MessageEntity::Type type, vector (*find_entities_f)(Slice)) mutable { + auto new_entities = find_entities_f(text); + for (auto &entity : new_entities) { + auto offset = narrow_cast(entity.begin() - text.begin()); + auto length = narrow_cast(entity.size()); + entities.emplace_back(type, offset, length); } + }; + add_entities(MessageEntity::Type::Mention, find_mentions); + if (!skip_bot_commands) { + add_entities(MessageEntity::Type::BotCommand, find_bot_commands); } - - auto hashtags = find_hashtags(text); - for (auto &hashtag : hashtags) { - entities.emplace_back(MessageEntity::Type::Hashtag, narrow_cast(hashtag.begin() - text.begin()), - narrow_cast(hashtag.size())); - } - - auto cashtags = find_cashtags(text); - for (auto &cashtag : cashtags) { - entities.emplace_back(MessageEntity::Type::Cashtag, narrow_cast(cashtag.begin() - text.begin()), - narrow_cast(cashtag.size())); - } - + add_entities(MessageEntity::Type::Hashtag, find_hashtags); + add_entities(MessageEntity::Type::Cashtag, find_cashtags); // TODO find_phone_numbers + add_entities(MessageEntity::Type::BankCardNumber, find_bank_card_numbers); } auto urls = find_urls(text); @@ -1149,7 +1392,7 @@ vector find_entities(Slice text, bool skip_bot_commands, bool onl return entities; } - std::sort(entities.begin(), entities.end()); + sort_entities(entities); remove_intersecting_entities(entities); @@ -1271,6 +1514,8 @@ string get_first_url(Slice text, const vector &entities) { break; case MessageEntity::Type::PhoneNumber: break; + case MessageEntity::Type::BankCardNumber: + break; default: UNREACHABLE(); } @@ -1645,7 +1890,7 @@ static Result> do_parse_markdown_v2(CSlice text, string &r << " entity at byte offset " << nested_entities.back().entity_byte_offset); } - std::sort(entities.begin(), entities.end()); + sort_entities(entities); return entities; } @@ -1657,6 +1902,686 @@ Result> parse_markdown_v2(string &text) { return entities; } +static vector find_text_url_entities_v3(Slice text) { + vector result; + size_t size = text.size(); + for (size_t i = 0; i < size; i++) { + if (text[i] != '[') { + continue; + } + + auto text_begin = i; + auto text_end = text_begin + 1; + while (text_end < size && text[text_end] != ']') { + text_end++; + } + + i = text_end; // prevent quadratic asymptotic + + if (text_end == size || text_end == text_begin + 1) { + continue; + } + + auto url_begin = text_end + 1; + if (url_begin == size || text[url_begin] != '(') { + continue; + } + + size_t url_end = url_begin + 1; + while (url_end < size && text[url_end] != ')') { + url_end++; + } + + i = url_end; // prevent quadratic asymptotic, disallows [a](b[c](t.me) + + if (url_end < size) { + Slice url = text.substr(url_begin + 1, url_end - url_begin - 1); + if (check_url(url).is_ok()) { + result.push_back(text.substr(text_begin, text_end - text_begin + 1)); + result.push_back(text.substr(url_begin, url_end - url_begin + 1)); + } + } + } + return result; +} + +// entities must be valid for the text +static FormattedText parse_text_url_entities_v3(Slice text, vector entities) { + // continuous entities can't intersect TextUrl entities, + // so try to find new TextUrl entities only between the predetermined continuous entities + + FormattedText result; + int32 result_text_utf16_length = 0; + vector part_entities; + vector part_splittable_entities[SPLITTABLE_ENTITY_TYPE_COUNT]; + int32 part_begin = 0; + int32 max_end = 0; + int32 skipped_length = 0; + auto add_part = [&](int32 part_end) { + // we have [part_begin, max_end) kept part and [max_end, part_end) part to parse text_url entities + + if (max_end != part_begin) { + // add all entities from the kept part + auto kept_part_text = utf8_utf16_substr(text, 0, max_end - part_begin); + text = text.substr(kept_part_text.size()); + + result.text.append(kept_part_text.begin(), kept_part_text.size()); + append(result.entities, std::move(part_entities)); + part_entities.clear(); + result_text_utf16_length += max_end - part_begin; + } + + size_t splittable_entity_pos[SPLITTABLE_ENTITY_TYPE_COUNT] = {}; + for (size_t index = 0; index < SPLITTABLE_ENTITY_TYPE_COUNT; index++) { + check_non_intersecting(part_splittable_entities[index]); + } + if (part_end != max_end) { + // try to find text_url entities in the left part + auto parsed_part_text = utf8_utf16_substr(text, 0, part_end - max_end); + text = text.substr(parsed_part_text.size()); + + vector text_urls = find_text_url_entities_v3(parsed_part_text); + + int32 text_utf16_offset = max_end; + size_t prev_pos = 0; + for (size_t i = 0; i < text_urls.size(); i += 2) { + auto text_begin_pos = static_cast(text_urls[i].begin() - parsed_part_text.begin()); + auto text_end_pos = text_begin_pos + text_urls[i].size() - 1; + auto url_begin_pos = static_cast(text_urls[i + 1].begin() - parsed_part_text.begin()); + auto url_end_pos = url_begin_pos + text_urls[i + 1].size() - 1; + CHECK(parsed_part_text[text_begin_pos] == '['); + CHECK(parsed_part_text[text_end_pos] == ']'); + CHECK(url_begin_pos == text_end_pos + 1); + CHECK(parsed_part_text[url_begin_pos] == '('); + CHECK(parsed_part_text[url_end_pos] == ')'); + + Slice before_text_url = parsed_part_text.substr(prev_pos, text_begin_pos - prev_pos); + auto before_text_url_utf16_length = text_length(before_text_url); + result_text_utf16_length += before_text_url_utf16_length; + result.text.append(before_text_url.begin(), before_text_url.size()); + text_utf16_offset += before_text_url_utf16_length; + + Slice text_url = parsed_part_text.substr(text_begin_pos + 1, text_end_pos - text_begin_pos - 1); + auto text_url_utf16_length = text_length(text_url); + Slice url = parsed_part_text.substr(url_begin_pos + 1, url_end_pos - url_begin_pos - 1); + auto url_utf16_length = text_length(url); + result.entities.emplace_back(MessageEntity::Type::TextUrl, result_text_utf16_length, text_url_utf16_length, + check_url(url).move_as_ok()); + result.text.append(text_url.begin(), text_url.size()); + result_text_utf16_length += text_url_utf16_length; + + auto initial_utf16_length = 1 + text_url_utf16_length + 1 + 1 + url_utf16_length + 1; + + // adjust splittable entities, removing deleted parts from them + // in the segment [text_utf16_offset, text_utf16_offset + initial_utf16_length) + // the first character and the last (url_utf16_length + 3) characters are deleted + for (size_t index = 0; index < SPLITTABLE_ENTITY_TYPE_COUNT; index++) { + auto &pos = splittable_entity_pos[index]; + auto &splittable_entities = part_splittable_entities[index]; + while (pos < splittable_entities.size() && + splittable_entities[pos].offset < text_utf16_offset + initial_utf16_length) { + auto offset = splittable_entities[pos].offset; + auto length = splittable_entities[pos].length; + if (offset + length > text_utf16_offset + 1 + text_url_utf16_length) { + // ends after last removed part; truncate length + length = text_utf16_offset + 1 + text_url_utf16_length - offset; + } + if (offset >= text_utf16_offset + 1) { + offset--; + } else if (offset + length >= text_utf16_offset + 1) { + length--; + } + if (length > 0) { + CHECK(offset >= skipped_length); + CHECK(offset - skipped_length + length <= result_text_utf16_length); + if (offset < text_utf16_offset && offset + length > text_utf16_offset) { + // entity intersects start on the new text_url entity; split it + result.entities.emplace_back(splittable_entities[pos].type, offset - skipped_length, + text_utf16_offset - offset); + length -= text_utf16_offset - offset; + offset = text_utf16_offset; + } + result.entities.emplace_back(splittable_entities[pos].type, offset - skipped_length, length); + } + if (splittable_entities[pos].offset + splittable_entities[pos].length > + text_utf16_offset + initial_utf16_length) { + // begins before end of the segment, but ends after it + // need to keep the entity for future segments, so split the entity + splittable_entities[pos].length = splittable_entities[pos].offset + splittable_entities[pos].length - + (text_utf16_offset + initial_utf16_length); + splittable_entities[pos].offset = text_utf16_offset + initial_utf16_length; + } else { + pos++; + } + } + } + text_utf16_offset += initial_utf16_length; + + skipped_length += 2 + 2 + url_utf16_length; + prev_pos = url_end_pos + 1; + } + + result.text.append(parsed_part_text.begin() + prev_pos, parsed_part_text.size() - prev_pos); + result_text_utf16_length += part_end - text_utf16_offset; + } + + // now add all left splittable entities from [part_begin, part_end) + for (size_t index = 0; index < SPLITTABLE_ENTITY_TYPE_COUNT; index++) { + auto &pos = splittable_entity_pos[index]; + auto &splittable_entities = part_splittable_entities[index]; + while (pos < splittable_entities.size() && splittable_entities[pos].offset < part_end) { + if (splittable_entities[pos].offset + splittable_entities[pos].length > part_end) { + // begins before end of the segment, but ends after it + // need to keep the entity for future segments, so split the entity + // entities don't intersect each other, so there can be at most one such entity + result.entities.emplace_back(splittable_entities[pos].type, splittable_entities[pos].offset - skipped_length, + part_end - splittable_entities[pos].offset); + + splittable_entities[pos].length = + splittable_entities[pos].offset + splittable_entities[pos].length - part_end; + splittable_entities[pos].offset = part_end; + } else { + result.entities.emplace_back(splittable_entities[pos].type, splittable_entities[pos].offset - skipped_length, + splittable_entities[pos].length); + pos++; + } + } + if (pos == splittable_entities.size()) { + splittable_entities.clear(); + } else { + CHECK(pos == splittable_entities.size() - 1); + CHECK(!text.empty()); + splittable_entities[0] = std::move(splittable_entities.back()); + splittable_entities.resize(1); + } + } + + part_begin = part_end; + }; + + for (auto &entity : entities) { + if (is_splittable_entity(entity.type)) { + auto index = get_splittable_entity_type_index(entity.type); + part_splittable_entities[index].push_back(entity); + continue; + } + CHECK(is_continuous_entity(entity.type)); + + if (entity.offset > max_end) { + // found a gap from max_end to entity.offset between predetermined entities + add_part(entity.offset); + } else { + CHECK(entity.offset == max_end); + } + + max_end = entity.offset + entity.length; + part_entities.push_back(entity); + part_entities.back().offset -= skipped_length; + } + add_part(part_begin + text_length(text)); + + return result; +} + +static vector find_splittable_entities_v3(Slice text, const vector &entities) { + std::unordered_set unallowed_boundaries; + for (auto &entity : entities) { + unallowed_boundaries.insert(entity.offset); + unallowed_boundaries.insert(entity.offset + entity.length); + if (entity.type == MessageEntity::Type::Mention || entity.type == MessageEntity::Type::Hashtag || + entity.type == MessageEntity::Type::BotCommand || entity.type == MessageEntity::Type::Cashtag || + entity.type == MessageEntity::Type::PhoneNumber || entity.type == MessageEntity::Type::BankCardNumber) { + for (int32 i = 1; i < entity.length; i++) { + unallowed_boundaries.insert(entity.offset + i); + } + } + } + + auto found_entities = find_entities(text, false, false); + td::remove_if(found_entities, [](const auto &entity) { + return entity.type == MessageEntity::Type::EmailAddress || entity.type == MessageEntity::Type::Url; + }); + for (auto &entity : found_entities) { + for (int32 i = 0; i <= entity.length; i++) { + unallowed_boundaries.insert(entity.offset + i); + } + } + + vector result; + size_t splittable_entity_offset[SPLITTABLE_ENTITY_TYPE_COUNT] = {}; + int32 utf16_offset = 0; + for (size_t i = 0; i + 1 < text.size(); i++) { + auto c = static_cast(text[i]); + if (is_utf8_character_first_code_unit(c)) { + utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + } + if ((c == '_' || c == '*' || c == '~') && text[i] == text[i + 1] && unallowed_boundaries.count(utf16_offset) == 0) { + auto j = i + 2; + while (j != text.size() && text[j] == text[i] && unallowed_boundaries.count(utf16_offset + j - i - 1) == 0) { + j++; + } + if (j == i + 2) { + auto type = c == '_' ? MessageEntity::Type::Italic + : (c == '*' ? MessageEntity::Type::Bold : MessageEntity::Type::Strikethrough); + auto index = get_splittable_entity_type_index(type); + if (splittable_entity_offset[index] != 0) { + auto length = utf16_offset - splittable_entity_offset[index] - 1; + if (length > 0) { + result.emplace_back(type, splittable_entity_offset[index], length); + } + splittable_entity_offset[index] = 0; + } else { + splittable_entity_offset[index] = utf16_offset + 1; + } + } + utf16_offset += narrow_cast(j - i - 1); + i = j - 1; + } + } + return result; +} + +// entities must be valid and can contain only splittable and continuous entities +// __italic__ ~~strikethrough~~ **bold** and [text_url](telegram.org) entities are left to be parsed +static FormattedText parse_markdown_v3_without_pre(Slice text, vector entities) { + check_is_sorted(entities); + + FormattedText parsed_text_url_text; + if (text.find('[') != string::npos) { + parsed_text_url_text = parse_text_url_entities_v3(text, std::move(entities)); + text = parsed_text_url_text.text; + entities = std::move(parsed_text_url_text.entities); + } + // splittable entities are sorted only within a fixed type now + + bool have_splittable_entities = false; + for (size_t i = 0; i + 1 < text.size(); i++) { + if ((text[i] == '_' || text[i] == '*' || text[i] == '~') && text[i] == text[i + 1]) { + have_splittable_entities = true; + break; + } + } + if (!have_splittable_entities) { + // fast path + sort_entities(entities); + return {text.str(), std::move(entities)}; + } + + auto found_splittable_entities = find_splittable_entities_v3(text, entities); + vector removed_pos; + for (auto &entity : found_splittable_entities) { + removed_pos.push_back(entity.offset - 1); + removed_pos.push_back(entity.offset + entity.length + 1); + } + std::sort(removed_pos.begin(), removed_pos.end()); + + string new_text; + CHECK(text.size() >= 2 * removed_pos.size()); + new_text.reserve(text.size() - 2 * removed_pos.size()); + size_t j = 0; + int32 utf16_offset = 0; + for (size_t i = 0; i < text.size(); i++) { + auto c = static_cast(text[i]); + if (is_utf8_character_first_code_unit(c)) { + utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + } + if (j < removed_pos.size() && utf16_offset == removed_pos[j]) { + i++; + utf16_offset++; + CHECK(j + 1 == removed_pos.size() || removed_pos[j + 1] >= removed_pos[j] + 2); + j++; + } else { + new_text += text[i]; + } + } + CHECK(j == removed_pos.size()); + combine(entities, std::move(found_splittable_entities)); + for (auto &entity : entities) { + auto removed_before_begin = narrow_cast( + std::upper_bound(removed_pos.begin(), removed_pos.end(), entity.offset) - removed_pos.begin()); + auto removed_before_end = narrow_cast( + std::upper_bound(removed_pos.begin(), removed_pos.end(), entity.offset + entity.length) - removed_pos.begin()); + entity.length -= 2 * (removed_before_end - removed_before_begin); + entity.offset -= 2 * removed_before_begin; + CHECK(entity.offset >= 0); + CHECK(entity.length >= 0); + CHECK(entity.offset + entity.length <= utf16_offset); + } + + td::remove_if(entities, [](const auto &entity) { return entity.length == 0; }); + + sort_entities(entities); + return {std::move(new_text), std::move(entities)}; +} + +static FormattedText parse_pre_entities_v3(Slice text) { + string result; + vector entities; + size_t size = text.size(); + int32 utf16_offset = 0; + for (size_t i = 0; i < size; i++) { + auto c = static_cast(text[i]); + if (c != '`') { + if (is_utf8_character_first_code_unit(c)) { + utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + } + result.push_back(text[i]); + continue; + } + + size_t j = i + 1; + while (j < size && text[j] == '`') { + j++; + } + + if (j - i == 1 || j - i == 3) { + // trying to find end of the entity + int32 entity_length = 0; + bool is_found = false; + for (size_t end_tag_begin = j; end_tag_begin < size; end_tag_begin++) { + auto cur_c = static_cast(text[end_tag_begin]); + if (cur_c == '`') { + // possible end tag + size_t end_tag_end = end_tag_begin + 1; + while (end_tag_end < size && text[end_tag_end] == '`') { + end_tag_end++; + } + if (end_tag_end - end_tag_begin == j - i) { + // end tag found + CHECK(entity_length > 0); + entities.emplace_back(j - i == 3 ? MessageEntity::Type::Pre : MessageEntity::Type::Code, utf16_offset, + entity_length); + result.append(text.begin() + j, end_tag_begin - j); + utf16_offset += entity_length; + i = end_tag_end - 1; + is_found = true; + break; + } else { + // not an end tag, skip + entity_length += narrow_cast(end_tag_end - end_tag_begin); + end_tag_begin = end_tag_end - 1; + } + } else if (is_utf8_character_first_code_unit(cur_c)) { + entity_length += 1 + (cur_c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + } + } + if (is_found) { + continue; + } + } + + result.append(text.begin() + i, j - i); + utf16_offset += narrow_cast(j - i); + i = j - 1; + } + return {std::move(result), std::move(entities)}; +} + +// entities must be valid for the text +static FormattedText parse_pre_entities_v3(Slice text, vector entities) { + // nothing can intersect pre entities, so ignore all '`' inside the predetermined entities + // and try to find new pre entities only between the predetermined entities + + FormattedText result; + int32 result_text_utf16_length = 0; + int32 part_begin = 0; + int32 max_end = 0; + int32 skipped_length = 0; + + auto add_part = [&](int32 part_end) { + // we have [part_begin, max_end) kept part and [max_end, part_end) part to parse pre entities + CHECK(part_begin == result_text_utf16_length + skipped_length); + + if (max_end != part_begin) { + // add the kept part + auto kept_part_text = utf8_utf16_substr(text, 0, max_end - part_begin); + text = text.substr(kept_part_text.size()); + + result.text.append(kept_part_text.begin(), kept_part_text.size()); + result_text_utf16_length += max_end - part_begin; + } + + if (part_end != max_end) { + // try to find pre entities in the left part + auto parsed_part_text = utf8_utf16_substr(text, 0, part_end - max_end); + text = text.substr(parsed_part_text.size()); + + if (parsed_part_text.find('`') == string::npos) { + // fast path, no pre entities; just append the text + result.text.append(parsed_part_text.begin(), parsed_part_text.size()); + result_text_utf16_length += part_end - max_end; + } else { + FormattedText parsed_text = parse_pre_entities_v3(parsed_part_text); + int32 new_skipped_length = 0; + for (auto &entity : parsed_text.entities) { + new_skipped_length += (entity.type == MessageEntity::Type::Pre ? 6 : 2); + } + CHECK(new_skipped_length < part_end - max_end); + result.text += parsed_text.text; + for (auto &entity : parsed_text.entities) { + entity.offset += result_text_utf16_length; + } + append(result.entities, std::move(parsed_text.entities)); + result_text_utf16_length += part_end - max_end - new_skipped_length; + skipped_length += new_skipped_length; + } + } + + part_begin = part_end; + }; + + for (auto &entity : entities) { + if (entity.offset > max_end) { + // found a gap from max_end to entity.offset between predetermined entities + add_part(entity.offset); + } + + max_end = td::max(max_end, entity.offset + entity.length); + result.entities.push_back(std::move(entity)); + result.entities.back().offset -= skipped_length; + } + add_part(part_begin + text_length(text)); + + return result; +} + +// text entities must be valid +// returned entities must be resplitted and fixed +FormattedText parse_markdown_v3(FormattedText text) { + if (text.text.find('`') != string::npos) { + text = parse_pre_entities_v3(text.text, std::move(text.entities)); + check_is_sorted(text.entities); + } + + bool have_pre = false; + for (auto &entity : text.entities) { + if (is_pre_entity(entity.type)) { + have_pre = true; + break; + } + } + if (!have_pre) { + // fast path + return parse_markdown_v3_without_pre(text.text, std::move(text.entities)); + } + + FormattedText result; + int32 result_text_utf16_length = 0; + vector part_entities; + int32 part_begin = 0; + int32 max_end = 0; + Slice left_text = text.text; + + auto add_part = [&](int32 part_end) { + auto part_text = utf8_utf16_substr(left_text, 0, part_end - part_begin); + left_text = left_text.substr(part_text.size()); + + FormattedText part = parse_markdown_v3_without_pre(part_text, std::move(part_entities)); + part_entities.clear(); + + result.text += part.text; + for (auto &entity : part.entities) { + entity.offset += result_text_utf16_length; + } + append(result.entities, std::move(part.entities)); + result_text_utf16_length += text_length(part.text); + part_begin = part_end; + }; + + for (size_t i = 0; i < text.entities.size(); i++) { + auto &entity = text.entities[i]; + CHECK(is_splittable_entity(entity.type) || is_pre_entity(entity.type) || is_continuous_entity(entity.type)); + if (is_pre_entity(entity.type)) { + CHECK(entity.offset >= max_end); + CHECK(i + 1 == text.entities.size() || text.entities[i + 1].offset >= entity.offset + entity.length); + + add_part(entity.offset); + + auto part_text = utf8_utf16_substr(left_text, 0, entity.length); + left_text = left_text.substr(part_text.size()); + + result.text.append(part_text.begin(), part_text.size()); + result.entities.push_back(entity); + result.entities.back().offset = result_text_utf16_length; + result_text_utf16_length += entity.length; + part_begin = entity.offset + entity.length; + } else { + part_entities.push_back(entity); + part_entities.back().offset -= part_begin; + } + + max_end = td::max(max_end, entity.offset + entity.length); + } + add_part(part_begin + text_length(left_text)); + + return result; +} + +// text entities must be valid +FormattedText get_markdown_v3(FormattedText text) { + if (text.entities.empty()) { + return text; + } + + check_is_sorted(text.entities); + for (auto &entity : text.entities) { + if (!is_user_entity(entity.type)) { + return text; + } + } + + FormattedText result; + struct EntityInfo { + const MessageEntity *entity; + int32 utf16_added_before; + + EntityInfo(MessageEntity *entity, int32 utf16_added_before) + : entity(entity), utf16_added_before(utf16_added_before) { + } + }; + vector nested_entities_stack; + size_t current_entity = 0; + + int32 utf16_offset = 0; + int32 utf16_added = 0; + + for (size_t pos = 0; pos <= text.text.size(); pos++) { + auto c = static_cast(text.text[pos]); + if (is_utf8_character_first_code_unit(c)) { + while (!nested_entities_stack.empty()) { + const auto *entity = nested_entities_stack.back().entity; + auto entity_end = entity->offset + entity->length; + if (utf16_offset < entity_end) { + break; + } + + CHECK(utf16_offset == entity_end); + + switch (entity->type) { + case MessageEntity::Type::Italic: + result.text += "__"; + utf16_added += 2; + break; + case MessageEntity::Type::Bold: + result.text += "**"; + utf16_added += 2; + break; + case MessageEntity::Type::Strikethrough: + result.text += "~~"; + utf16_added += 2; + break; + case MessageEntity::Type::TextUrl: + result.text += "]("; + result.text += entity->argument; + result.text += ')'; + utf16_added += narrow_cast(3 + entity->argument.size()); + break; + case MessageEntity::Type::Code: + result.text += '`'; + utf16_added++; + break; + case MessageEntity::Type::Pre: + result.text += "```"; + utf16_added += 3; + break; + default: + result.entities.push_back(*entity); + result.entities.back().offset += nested_entities_stack.back().utf16_added_before; + result.entities.back().length += utf16_added - nested_entities_stack.back().utf16_added_before; + break; + } + nested_entities_stack.pop_back(); + } + + while (current_entity < text.entities.size() && utf16_offset >= text.entities[current_entity].offset) { + CHECK(utf16_offset == text.entities[current_entity].offset); + switch (text.entities[current_entity].type) { + case MessageEntity::Type::Italic: + result.text += "__"; + utf16_added += 2; + break; + case MessageEntity::Type::Bold: + result.text += "**"; + utf16_added += 2; + break; + case MessageEntity::Type::Strikethrough: + result.text += "~~"; + utf16_added += 2; + break; + case MessageEntity::Type::TextUrl: + result.text += '['; + utf16_added++; + break; + case MessageEntity::Type::Code: + result.text += '`'; + utf16_added++; + break; + case MessageEntity::Type::Pre: + result.text += "```"; + utf16_added += 3; + break; + default: + // keep as is + break; + } + nested_entities_stack.emplace_back(&text.entities[current_entity++], utf16_added); + } + utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + } + if (pos == text.text.size()) { + break; + } + + result.text.push_back(text.text[pos]); + } + + sort_entities(result.entities); + if (parse_markdown_v3(result) != text) { + return text; + } + return result; +} + static uint32 decode_html_entity(CSlice text, size_t &pos) { auto c = static_cast(text[pos]); if (c != '&') { @@ -1925,7 +2850,7 @@ static Result> do_parse_html(CSlice text, string &result) } } - std::sort(entities.begin(), entities.end()); + sort_entities(entities); return entities; } @@ -1947,15 +2872,10 @@ vector> get_input_message_entities(co const char *source) { vector> result; for (auto &entity : entities) { + if (!is_user_entity(entity.type)) { + continue; + } switch (entity.type) { - case MessageEntity::Type::Mention: - case MessageEntity::Type::Hashtag: - case MessageEntity::Type::BotCommand: - case MessageEntity::Type::Url: - case MessageEntity::Type::EmailAddress: - case MessageEntity::Type::Cashtag: - case MessageEntity::Type::PhoneNumber: - continue; case MessageEntity::Type::Bold: result.push_back(make_tl_object(entity.offset, entity.length)); break; @@ -1991,6 +2911,14 @@ vector> get_input_message_entities(co std::move(input_user))); break; } + case MessageEntity::Type::Mention: + case MessageEntity::Type::Hashtag: + case MessageEntity::Type::BotCommand: + case MessageEntity::Type::Url: + case MessageEntity::Type::EmailAddress: + case MessageEntity::Type::Cashtag: + case MessageEntity::Type::PhoneNumber: + case MessageEntity::Type::BankCardNumber: default: UNREACHABLE(); } @@ -2023,6 +2951,10 @@ vector> get_input_secret_message_entiti break; case MessageEntity::Type::BotCommand: break; + case MessageEntity::Type::PhoneNumber: + break; + case MessageEntity::Type::BankCardNumber: + break; case MessageEntity::Type::Url: result.push_back(make_tl_object(entity.offset, entity.length)); break; @@ -2065,8 +2997,6 @@ vector> get_input_secret_message_entiti break; case MessageEntity::Type::MentionName: break; - case MessageEntity::Type::PhoneNumber: - break; default: UNREACHABLE(); } @@ -2076,7 +3006,8 @@ vector> get_input_secret_message_entiti } Result> get_message_entities(const ContactsManager *contacts_manager, - vector> &&input_entities) { + vector> &&input_entities, + bool allow_all) { vector entities; for (auto &entity : input_entities) { if (entity == nullptr || entity->type_ == nullptr) { @@ -2085,12 +3016,28 @@ Result> get_message_entities(const ContactsManager *contac switch (entity->type_->get_id()) { case td_api::textEntityTypeMention::ID: + entities.emplace_back(MessageEntity::Type::Mention, entity->offset_, entity->length_); + break; case td_api::textEntityTypeHashtag::ID: + entities.emplace_back(MessageEntity::Type::Hashtag, entity->offset_, entity->length_); + break; case td_api::textEntityTypeBotCommand::ID: + entities.emplace_back(MessageEntity::Type::BotCommand, entity->offset_, entity->length_); + break; case td_api::textEntityTypeUrl::ID: + entities.emplace_back(MessageEntity::Type::Url, entity->offset_, entity->length_); + break; case td_api::textEntityTypeEmailAddress::ID: + entities.emplace_back(MessageEntity::Type::EmailAddress, entity->offset_, entity->length_); + break; case td_api::textEntityTypeCashtag::ID: + entities.emplace_back(MessageEntity::Type::Cashtag, entity->offset_, entity->length_); + break; case td_api::textEntityTypePhoneNumber::ID: + entities.emplace_back(MessageEntity::Type::PhoneNumber, entity->offset_, entity->length_); + break; + case td_api::textEntityTypeBankCardNumber::ID: + entities.emplace_back(MessageEntity::Type::BankCardNumber, entity->offset_, entity->length_); break; case td_api::textEntityTypeBold::ID: entities.emplace_back(MessageEntity::Type::Bold, entity->offset_, entity->length_); @@ -2134,7 +3081,7 @@ Result> get_message_entities(const ContactsManager *contac case td_api::textEntityTypeMentionName::ID: { auto entity_mention_name = static_cast(entity->type_.get()); UserId user_id(entity_mention_name->user_id_); - if (!contacts_manager->have_input_user(user_id)) { + if (contacts_manager != nullptr && !contacts_manager->have_input_user(user_id)) { return Status::Error(7, "Have no access to the user"); } entities.emplace_back(entity->offset_, entity->length_, user_id); @@ -2143,6 +3090,10 @@ Result> get_message_entities(const ContactsManager *contac default: UNREACHABLE(); } + CHECK(!entities.empty()); + if (!allow_all && !is_user_entity(entities.back().type)) { + entities.pop_back(); + } } return entities; } @@ -2182,6 +3133,12 @@ vector get_message_entities(const ContactsManager *contacts_manag entity_bot_command->length_); break; } + case telegram_api::messageEntityBankCard::ID: { + auto entity_bank_card = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::BankCardNumber, entity_bank_card->offset_, + entity_bank_card->length_); + break; + } case telegram_api::messageEntityUrl::ID: { auto entity_url = static_cast(entity.get()); entities.emplace_back(MessageEntity::Type::Url, entity_url->offset_, entity_url->length_); @@ -2291,6 +3248,9 @@ vector get_message_entities(vector(entity.get()); // TODO skip URL when find_urls will be better @@ -2375,6 +3335,8 @@ vector get_message_entities(vector clean_input_string_with_entities(const string &text, vector &entities) { + check_is_sorted(entities); + struct EntityInfo { MessageEntity *entity; int32 utf16_skipped_before; @@ -2511,11 +3473,16 @@ static Result clean_input_string_with_entities(const string &text, vecto << entity->offset + entity->length); } + replace_offending_characters(result); + return result; } // removes entities containing whitespaces only +// entities must be sorted by offset and length, but not necessary by type +// returns {last_non_whitespace_pos, last_non_whitespace_utf16_offset} static std::pair remove_invalid_entities(const string &text, vector &entities) { + // check_is_sorted(entities); vector nested_entities_stack; size_t current_entity = 0; @@ -2537,8 +3504,8 @@ static std::pair remove_invalid_entities(const string &text, vect break; } - auto have_hidden_data = - entity->type == MessageEntity::Type::TextUrl || entity->type == MessageEntity::Type::MentionName; + auto have_hidden_data = entity->type == MessageEntity::Type::TextUrl || + entity->type == MessageEntity::Type::MentionName || is_pre_entity(entity->type); if (last_non_whitespace_utf16_offset >= entity->offset || (last_space_utf16_offset >= entity->offset && have_hidden_data)) { // TODO check entity for validness, for example, that mentions, hashtags, cashtags and URLs are valid @@ -2584,12 +3551,185 @@ static std::pair remove_invalid_entities(const string &text, vect return {last_non_whitespace_pos, last_non_whitespace_utf16_offset}; } +// enitities must contain only splittable entities +void split_entities(vector &entities, const vector &other_entities) { + check_is_sorted(entities); + check_is_sorted(other_entities); + + int32 begin_pos[SPLITTABLE_ENTITY_TYPE_COUNT] = {}; + int32 end_pos[SPLITTABLE_ENTITY_TYPE_COUNT] = {}; + auto it = entities.begin(); + vector result; + auto add_entities = [&](int32 end_offset) { + auto flush_entities = [&](int32 offset) { + for (auto type : {MessageEntity::Type::Bold, MessageEntity::Type::Italic, MessageEntity::Type::Underline, + MessageEntity::Type::Strikethrough}) { + auto index = get_splittable_entity_type_index(type); + if (end_pos[index] != 0 && begin_pos[index] < offset) { + if (end_pos[index] <= offset) { + result.emplace_back(type, begin_pos[index], end_pos[index] - begin_pos[index]); + begin_pos[index] = 0; + end_pos[index] = 0; + } else { + result.emplace_back(type, begin_pos[index], offset - begin_pos[index]); + begin_pos[index] = offset; + } + } + } + }; + + while (it != entities.end()) { + if (it->offset >= end_offset) { + break; + } + CHECK(is_splittable_entity(it->type)); + auto index = get_splittable_entity_type_index(it->type); + if (it->offset <= end_pos[index] && end_pos[index] != 0) { + if (it->offset + it->length > end_pos[index]) { + end_pos[index] = it->offset + it->length; + } + } else { + flush_entities(it->offset); + begin_pos[index] = it->offset; + end_pos[index] = it->offset + it->length; + } + ++it; + } + flush_entities(end_offset); + }; + + vector nested_entities_stack; + auto add_offset = [&](int32 offset) { + while (!nested_entities_stack.empty() && + offset >= nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { + // remove non-intersecting entities from the stack + auto old_size = result.size(); + add_entities(nested_entities_stack.back()->offset + nested_entities_stack.back()->length); + if (is_pre_entity(nested_entities_stack.back()->type)) { + result.resize(old_size); + } + nested_entities_stack.pop_back(); + } + + add_entities(offset); + }; + for (auto &other_entity : other_entities) { + add_offset(other_entity.offset); + nested_entities_stack.push_back(&other_entity); + } + add_offset(std::numeric_limits::max()); + + entities = std::move(result); + + // entities are sorted only by offset now, re-sort if needed + sort_entities(entities); +} + +static vector resplit_entities(vector &&splittable_entities, + vector &&entities) { + if (!splittable_entities.empty()) { + split_entities(splittable_entities, entities); // can merge some entities + + if (entities.empty()) { + return std::move(splittable_entities); + } + + combine(entities, std::move(splittable_entities)); + sort_entities(entities); + } + return std::move(entities); +} + +static void fix_entities(vector &entities) { + sort_entities(entities); + + if (are_entities_valid(entities)) { + // fast path + return; + } + + vector continuous_entities; + vector blockquote_entities; + vector splittable_entities; + for (auto &entity : entities) { + if (is_splittable_entity(entity.type)) { + splittable_entities.push_back(std::move(entity)); + } else if (is_blockquote_entity(entity.type)) { + blockquote_entities.push_back(std::move(entity)); + } else { + continuous_entities.push_back(std::move(entity)); + } + } + remove_intersecting_entities(continuous_entities); // continuous entities can't intersect each other + + if (!blockquote_entities.empty()) { + remove_intersecting_entities(blockquote_entities); // blockquote entities can't intersect each other + + // blockquote entities can contain continuous entities, but can't intersect them in the other ways + remove_entities_intersecting_blockquote(continuous_entities, blockquote_entities); + + combine(continuous_entities, std::move(blockquote_entities)); + sort_entities(continuous_entities); + } + + // must be called once to not merge some adjacent entities + entities = resplit_entities(std::move(splittable_entities), std::move(continuous_entities)); + check_is_sorted(entities); +} + +static void merge_new_entities(vector &entities, vector new_entities) { + check_is_sorted(entities); + if (new_entities.empty()) { + // fast path + return; + } + + check_non_intersecting(new_entities); + + vector continuous_entities; + vector blockquote_entities; + vector splittable_entities; + for (auto &entity : entities) { + if (is_splittable_entity(entity.type)) { + splittable_entities.push_back(std::move(entity)); + } else if (is_blockquote_entity(entity.type)) { + blockquote_entities.push_back(std::move(entity)); + } else { + continuous_entities.push_back(std::move(entity)); + } + } + + remove_entities_intersecting_blockquote(new_entities, blockquote_entities); + + // merge before combining with blockquote entities + continuous_entities = merge_entities(std::move(continuous_entities), std::move(new_entities)); + + if (!blockquote_entities.empty()) { + combine(continuous_entities, std::move(blockquote_entities)); + sort_entities(continuous_entities); + } + + // must be called once to not merge some adjacent entities + entities = resplit_entities(std::move(splittable_entities), std::move(continuous_entities)); + check_is_sorted(entities); +} + Status fix_formatted_text(string &text, vector &entities, bool allow_empty, bool skip_new_entities, bool skip_bot_commands, bool for_draft) { if (!check_utf8(text)) { return Status::Error(400, "Strings must be encoded in UTF-8"); } + for (auto &entity : entities) { + if (entity.offset < 0 || entity.offset > 1000000) { + return Status::Error(400, PSLICE() << "Receive an entity with incorrect offset " << entity.offset); + } + if (entity.length < 0 || entity.length > 1000000) { + return Status::Error(400, PSLICE() << "Receive an entity with incorrect length " << entity.length); + } + } + td::remove_if(entities, [](const MessageEntity &entity) { return entity.length == 0; }); + fix_entities(entities); TRY_RESULT(result, clean_input_string_with_entities(text, entities)); @@ -2609,9 +3749,10 @@ Status fix_formatted_text(string &text, vector &entities, bool al return Status::Error(3, "Message must be non-empty"); } - if (!std::is_sorted(entities.begin(), entities.end())) { - std::sort(entities.begin(), entities.end()); // re-sort entities if needed after removal of some characters - } + // re-fix entities if needed after removal of some characters + // the sort order can be incorrect by type + // some splittable entities may be needed to be concatenated + fix_entities(entities); if (for_draft) { text = std::move(result); @@ -2621,15 +3762,20 @@ Status fix_formatted_text(string &text, vector &entities, bool al result.resize(last_non_whitespace_pos + 1); while (!entities.empty() && entities.back().offset > last_non_whitespace_utf16_offset) { CHECK(entities.back().type == MessageEntity::Type::TextUrl || - entities.back().type == MessageEntity::Type::MentionName); + entities.back().type == MessageEntity::Type::MentionName || is_pre_entity(entities.back().type)); entities.pop_back(); } + bool need_sort = false; for (auto &entity : entities) { if (entity.offset + entity.length > last_non_whitespace_utf16_offset + 1) { entity.length = last_non_whitespace_utf16_offset + 1 - entity.offset; + need_sort = true; CHECK(entity.length > 0); } } + if (need_sort) { + sort_entities(entities); + } // ltrim size_t first_non_whitespaces_pos = 0; @@ -2663,13 +3809,13 @@ Status fix_formatted_text(string &text, vector &entities, bool al } text.resize(new_size); - td::remove_if(entities, [text_utf16_length = narrow_cast(utf8_utf16_length(text))](const auto &entity) { + td::remove_if(entities, [text_utf16_length = text_length(text)](const auto &entity) { return entity.offset + entity.length > text_utf16_length; }); } if (!skip_new_entities) { - entities = merge_entities(std::move(entities), find_entities(text, skip_bot_commands)); + merge_new_entities(entities, find_entities(text, skip_bot_commands)); } // TODO MAX_MESSAGE_LENGTH and MAX_CAPTION_LENGTH @@ -2679,13 +3825,13 @@ Status fix_formatted_text(string &text, vector &entities, bool al FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text, vector> &&server_entities, - bool skip_new_entities, int32 send_date, const char *source) { + bool skip_new_entities, int32 send_date, bool from_album, const char *source) { auto entities = get_message_entities(contacts_manager, std::move(server_entities), source); auto debug_message_text = message_text; auto debug_entities = entities; auto status = fix_formatted_text(message_text, entities, true, skip_new_entities, true, false); if (status.is_error()) { - if (send_date == 0 || send_date > 1579219200) { // approximate fix date + if (!from_album && (send_date == 0 || send_date > 1579219200)) { // approximate fix date LOG(ERROR) << "Receive error " << status << " while parsing message text from " << source << " with content \"" << debug_message_text << "\" -> \"" << message_text << "\" sent at " << send_date << " with entities " << format::as_array(debug_entities) << " -> " << format::as_array(entities); @@ -2693,7 +3839,7 @@ FormattedText get_message_text(const ContactsManager *contacts_manager, string m if (!clean_input_string(message_text)) { message_text.clear(); } - entities.clear(); + entities = find_entities(message_text, false); } return FormattedText{std::move(message_text), std::move(entities)}; } diff --git a/td/telegram/MessageEntity.h b/td/telegram/MessageEntity.h index 666302c7..ea922950 100644 --- a/td/telegram/MessageEntity.h +++ b/td/telegram/MessageEntity.h @@ -45,7 +45,8 @@ class MessageEntity { PhoneNumber, Underline, Strikethrough, - BlockQuote + BlockQuote, + BankCardNumber }; Type type; int32 offset; @@ -112,6 +113,8 @@ struct FormattedText { void parse(ParserT &parser); }; +StringBuilder &operator<<(StringBuilder &string_builder, const FormattedText &text); + inline bool operator==(const FormattedText &lhs, const FormattedText &rhs) { return lhs.text == rhs.text && lhs.entities == rhs.entities; } @@ -123,7 +126,8 @@ inline bool operator!=(const FormattedText &lhs, const FormattedText &rhs) { const std::unordered_set &get_valid_short_usernames(); Result> get_message_entities(const ContactsManager *contacts_manager, - vector> &&input_entities); + vector> &&input_entities, + bool allow_all = false); vector> get_text_entities_object(const vector &entities); @@ -135,6 +139,7 @@ vector find_mentions(Slice str); vector find_bot_commands(Slice str); vector find_hashtags(Slice str); vector find_cashtags(Slice str); +vector find_bank_card_numbers(Slice str); bool is_email_address(Slice str); vector> find_urls(Slice str); // slice + is_email_address @@ -144,6 +149,10 @@ Result> parse_markdown(string &text); Result> parse_markdown_v2(string &text); +FormattedText parse_markdown_v3(FormattedText text); + +FormattedText get_markdown_v3(FormattedText text); + Result> parse_html(string &text); vector> get_input_message_entities(const ContactsManager *contacts_manager, @@ -169,7 +178,7 @@ Status fix_formatted_text(string &text, vector &entities, bool al FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text, vector> &&server_entities, - bool skip_new_entities, int32 send_date, const char *source); + bool skip_new_entities, int32 send_date, bool from_album, const char *source); td_api::object_ptr extract_input_caption( tl_object_ptr &input_message_content); diff --git a/td/telegram/MessageId.h b/td/telegram/MessageId.h index 1d4392e7..355b1691 100644 --- a/td/telegram/MessageId.h +++ b/td/telegram/MessageId.h @@ -150,15 +150,18 @@ class MessageId { } friend bool operator>(const MessageId &lhs, const MessageId &rhs) { - return rhs < lhs; + CHECK(lhs.is_scheduled() == rhs.is_scheduled()); + return lhs.id > rhs.id; } friend bool operator<=(const MessageId &lhs, const MessageId &rhs) { - return !(rhs < lhs); + CHECK(lhs.is_scheduled() == rhs.is_scheduled()); + return lhs.id <= rhs.id; } friend bool operator>=(const MessageId &lhs, const MessageId &rhs) { - return !(lhs < rhs); + CHECK(lhs.is_scheduled() == rhs.is_scheduled()); + return lhs.id >= rhs.id; } template diff --git a/td/telegram/MessagesDb.cpp b/td/telegram/MessagesDb.cpp index 2cbfc5a3..ae04130c 100644 --- a/td/telegram/MessagesDb.cpp +++ b/td/telegram/MessagesDb.cpp @@ -93,12 +93,12 @@ Status init_messages_db(SqliteDb &db, int32 version) { // } return Status::OK(); }; - auto add_notification_id_index = [&db]() { + auto add_notification_id_index = [&db] { return db.exec( "CREATE INDEX IF NOT EXISTS message_by_notification_id ON messages (dialog_id, notification_id) WHERE " "notification_id IS NOT NULL"); }; - auto add_scheduled_messages_table = [&db]() { + auto add_scheduled_messages_table = [&db] { TRY_STATUS( db.exec("CREATE TABLE IF NOT EXISTS scheduled_messages (dialog_id INT8, message_id INT8, " "server_message_id INT4, data BLOB, PRIMARY KEY (dialog_id, message_id))")); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 2cbc9aad..c5aaeaac 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -26,6 +26,7 @@ #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessageContent.h" +#include "td/telegram/MessageEntity.h" #include "td/telegram/MessageEntity.hpp" #include "td/telegram/MessagesDb.h" #include "td/telegram/misc.h" @@ -70,7 +71,6 @@ #include #include #include -#include #include #include #include @@ -96,8 +96,7 @@ class GetOnlinesQuery : public Td::ResultHandler { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_getOnlines(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getOnlines(std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -119,7 +118,7 @@ class GetOnlinesQuery : public Td::ResultHandler { class GetAllDraftsQuery : public Td::ResultHandler { public: void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getAllDrafts()))); + send_query(G()->net_query_creator().create(telegram_api::messages_getAllDrafts())); } void on_result(uint64 id, BufferSlice packet) override { @@ -134,7 +133,9 @@ class GetAllDraftsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - LOG(ERROR) << "Receive error for GetAllDraftsQuery: " << status; + if (!G()->is_expected_error(status)) { + LOG(ERROR) << "Receive error for GetAllDraftsQuery: " << status; + } status.ignore(); } }; @@ -145,8 +146,8 @@ class GetDialogQuery : public Td::ResultHandler { public: void send(DialogId dialog_id) { dialog_id_ = dialog_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getPeerDialogs( - td->messages_manager_->get_input_dialog_peers({dialog_id}, AccessRights::Read))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getPeerDialogs( + td->messages_manager_->get_input_dialog_peers({dialog_id}, AccessRights::Read)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -191,8 +192,7 @@ class GetPinnedDialogsActor : public NetActorOnce { NetQueryRef send(FolderId folder_id, uint64 sequence_id) { folder_id_ = folder_id; - auto query = - G()->net_query_creator().create(create_storer(telegram_api::messages_getPinnedDialogs(folder_id.get()))); + auto query = G()->net_query_creator().create(telegram_api::messages_getPinnedDialogs(folder_id.get())); auto result = query.get_weak(); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); @@ -223,7 +223,7 @@ class GetPinnedDialogsActor : public NetActorOnce { class GetDialogUnreadMarksQuery : public Td::ResultHandler { public: void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getDialogUnreadMarks()))); + send_query(G()->net_query_creator().create(telegram_api::messages_getDialogUnreadMarks())); } void on_result(uint64 id, BufferSlice packet) override { @@ -241,7 +241,7 @@ class GetDialogUnreadMarksQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for GetDialogUnreadMarksQuery: " << status; } status.ignore(); @@ -256,8 +256,7 @@ class GetMessagesQuery : public Td::ResultHandler { } void send(vector> &&message_ids) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_getMessages(std::move(message_ids))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getMessages(std::move(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -296,7 +295,7 @@ class GetChannelMessagesQuery : public Td::ResultHandler { channel_id_ = channel_id; CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_getMessages(std::move(input_channel), std::move(message_ids))))); + telegram_api::channels_getMessages(std::move(input_channel), std::move(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -335,7 +334,7 @@ class GetScheduledMessagesQuery : public Td::ResultHandler { dialog_id_ = dialog_id; CHECK(input_peer != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getScheduledMessages(std::move(input_peer), std::move(message_ids))))); + telegram_api::messages_getScheduledMessages(std::move(input_peer), std::move(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -386,8 +385,8 @@ class UpdateDialogPinnedMessageQuery : public Td::ResultHandler { flags |= telegram_api::messages_updatePinnedMessage::SILENT_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_updatePinnedMessage( - flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get())))); + send_query(G()->net_query_creator().create(telegram_api::messages_updatePinnedMessage( + flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -435,8 +434,8 @@ class ExportChannelMessageLinkQuery : public Td::ResultHandler { ignore_result_ = ignore_result; auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_exportMessageLink( - std::move(input_channel), message_id.get_server_message_id().get(), for_group)))); + send_query(G()->net_query_creator().create(telegram_api::channels_exportMessageLink( + std::move(input_channel), message_id.get_server_message_id().get(), for_group))); } void on_result(uint64 id, BufferSlice packet) override { @@ -482,8 +481,8 @@ class GetDialogListActor : public NetActorOnce { int32 flags = telegram_api::messages_getDialogs::EXCLUDE_PINNED_MASK | telegram_api::messages_getDialogs::FOLDER_ID_MASK; auto query = G()->net_query_creator().create( - create_storer(telegram_api::messages_getDialogs(flags, false /*ignored*/, folder_id.get(), offset_date, - offset_message_id.get(), std::move(input_peer), limit, 0))); + telegram_api::messages_getDialogs(flags, false /*ignored*/, folder_id.get(), offset_date, + offset_message_id.get(), std::move(input_peer), limit, 0)); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); } @@ -533,8 +532,7 @@ class SearchPublicDialogsQuery : public Td::ResultHandler { public: void send(const string &query) { query_ = query; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::contacts_search(query, 3 /* ignored server-side */)))); + send_query(G()->net_query_creator().create(telegram_api::contacts_search(query, 3 /* ignored server-side */))); } void on_result(uint64 id, BufferSlice packet) override { @@ -552,7 +550,7 @@ class SearchPublicDialogsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for SearchPublicDialogsQuery: " << status; } td->messages_manager_->on_failed_public_dialogs_search(query_, std::move(status)); @@ -577,7 +575,7 @@ class GetCommonDialogsQuery : public Td::ResultHandler { CHECK(input_user != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getCommonChats(std::move(input_user), offset_chat_id, limit)))); + telegram_api::messages_getCommonChats(std::move(input_user), offset_chat_id, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -623,8 +621,7 @@ class CreateChatQuery : public Td::ResultHandler { void send(vector> &&input_users, const string &title, int64 random_id) { random_id_ = random_id; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_createChat(std::move(input_users), title)))); + send_query(G()->net_query_creator().create(telegram_api::messages_createChat(std::move(input_users), title))); } void on_result(uint64 id, BufferSlice packet) override { @@ -666,8 +663,8 @@ class CreateChannelQuery : public Td::ResultHandler { random_id_ = random_id; send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_createChannel(flags, false /*ignored*/, false /*ignored*/, title, about, - location.get_input_geo_point(), location.get_address())))); + telegram_api::channels_createChannel(flags, false /*ignored*/, false /*ignored*/, title, about, + location.get_input_geo_point(), location.get_address()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -707,15 +704,15 @@ class EditDialogPhotoQuery : public Td::ResultHandler { switch (dialog_id.get_type()) { case DialogType::Chat: - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_editChatPhoto(dialog_id.get_chat_id().get(), std::move(input_chat_photo))))); + send_query(G()->net_query_creator().create( + telegram_api::messages_editChatPhoto(dialog_id.get_chat_id().get(), std::move(input_chat_photo)))); break; case DialogType::Channel: { auto channel_id = dialog_id.get_channel_id(); auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_editPhoto(std::move(input_channel), std::move(input_chat_photo))))); + telegram_api::channels_editPhoto(std::move(input_channel), std::move(input_chat_photo)))); break; } default: @@ -785,14 +782,13 @@ class EditDialogTitleQuery : public Td::ResultHandler { switch (dialog_id.get_type()) { case DialogType::Chat: send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_editChatTitle(dialog_id.get_chat_id().get(), title)))); + telegram_api::messages_editChatTitle(dialog_id.get_chat_id().get(), title))); break; case DialogType::Channel: { auto channel_id = dialog_id.get_channel_id(); auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_editTitle(std::move(input_channel), title)))); + send_query(G()->net_query_creator().create(telegram_api::channels_editTitle(std::move(input_channel), title))); break; } default: @@ -843,8 +839,8 @@ class EditDialogDefaultBannedRightsQuery : public Td::ResultHandler { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); CHECK(input_peer != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_editChatDefaultBannedRights( - std::move(input_peer), permissions.get_chat_banned_rights())))); + send_query(G()->net_query_creator().create(telegram_api::messages_editChatDefaultBannedRights( + std::move(input_peer), permissions.get_chat_banned_rights()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -906,13 +902,13 @@ class SaveDraftMessageQuery : public Td::ResultHandler { } } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_saveDraft( + send_query(G()->net_query_creator().create(telegram_api::messages_saveDraft( flags, false /*ignored*/, reply_to_message_id.get(), std::move(input_peer), draft_message == nullptr ? "" : draft_message->input_message_text.text.text, draft_message == nullptr ? vector>() : get_input_message_entities(td->contacts_manager_.get(), draft_message->input_message_text.text.entities, - "SaveDraftMessageQuery"))))); + "SaveDraftMessageQuery")))); } void on_result(uint64 id, BufferSlice packet) override { @@ -945,7 +941,7 @@ class ClearAllDraftsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_clearAllDrafts()))); + send_query(G()->net_query_creator().create(telegram_api::messages_clearAllDrafts())); } void on_result(uint64 id, BufferSlice packet) override { @@ -965,7 +961,7 @@ class ClearAllDraftsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for ClearAllDraftsQuery: " << status; } promise_.set_error(std::move(status)); @@ -995,7 +991,7 @@ class ToggleDialogPinQuery : public Td::ResultHandler { flags |= telegram_api::messages_toggleDialogPin::PINNED_MASK; } send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_toggleDialogPin(flags, false /*ignored*/, std::move(input_peer))))); + telegram_api::messages_toggleDialogPin(flags, false /*ignored*/, std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1032,9 +1028,9 @@ class ReorderPinnedDialogsQuery : public Td::ResultHandler { void send(FolderId folder_id, const vector &dialog_ids) { folder_id_ = folder_id; int32 flags = telegram_api::messages_reorderPinnedDialogs::FORCE_MASK; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_reorderPinnedDialogs( + send_query(G()->net_query_creator().create(telegram_api::messages_reorderPinnedDialogs( flags, true /*ignored*/, folder_id.get(), - td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read))))); + td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1053,7 +1049,7 @@ class ReorderPinnedDialogsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for ReorderPinnedDialogsQuery: " << status; } td->messages_manager_->on_update_pinned_dialogs(folder_id_); @@ -1084,7 +1080,7 @@ class ToggleDialogUnreadMarkQuery : public Td::ResultHandler { flags |= telegram_api::messages_markDialogUnread::UNREAD_MASK; } send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_markDialogUnread(flags, false /*ignored*/, std::move(input_peer))))); + telegram_api::messages_markDialogUnread(flags, false /*ignored*/, std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1127,8 +1123,8 @@ class GetMessagesViewsQuery : public Td::ResultHandler { LOG(INFO) << "View " << message_ids_.size() << " messages in " << dialog_id << ", increment = " << increment_view_counter; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getMessagesViews( - std::move(input_peer), MessagesManager::get_server_message_ids(message_ids_), increment_view_counter)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getMessagesViews( + std::move(input_peer), MessagesManager::get_server_message_ids(message_ids_), increment_view_counter))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1165,8 +1161,8 @@ class ReadMessagesContentsQuery : public Td::ResultHandler { void send(vector &&message_ids) { LOG(INFO) << "Receive ReadMessagesContentsQuery for messages " << format::as_array(message_ids); - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_readMessageContents(MessagesManager::get_server_message_ids(message_ids))))); + send_query(G()->net_query_creator().create( + telegram_api::messages_readMessageContents(MessagesManager::get_server_message_ids(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1187,7 +1183,7 @@ class ReadMessagesContentsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for read message contents: " << status; } promise_.set_error(std::move(status)); @@ -1214,8 +1210,8 @@ class ReadChannelMessagesContentsQuery : public Td::ResultHandler { LOG(INFO) << "Receive ReadChannelMessagesContentsQuery for messages " << format::as_array(message_ids) << " in " << channel_id; - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_readMessageContents( - std::move(input_channel), MessagesManager::get_server_message_ids(message_ids))))); + send_query(G()->net_query_creator().create(telegram_api::channels_readMessageContents( + std::move(input_channel), MessagesManager::get_server_message_ids(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1259,7 +1255,7 @@ class GetDialogMessageByDateQuery : public Td::ResultHandler { random_id_ = random_id; send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getHistory(std::move(input_peer), 0, date, -3, 5, 0, 0, 0)))); + telegram_api::messages_getHistory(std::move(input_peer), 0, date, -3, 5, 0, 0, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1308,8 +1304,8 @@ class GetHistoryQuery : public Td::ResultHandler { offset_ = offset; limit_ = limit; from_the_end_ = false; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getHistory( - std::move(input_peer), from_message_id.get_server_message_id().get(), 0, offset, limit, 0, 0, 0)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getHistory( + std::move(input_peer), from_message_id.get_server_message_id().get(), 0, offset, limit, 0, 0, 0))); } void send_get_from_the_end(DialogId dialog_id, int32 limit) { @@ -1324,7 +1320,7 @@ class GetHistoryQuery : public Td::ResultHandler { limit_ = limit; from_the_end_ = true; send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getHistory(std::move(input_peer), 0, 0, 0, limit, 0, 0, 0)))); + telegram_api::messages_getHistory(std::move(input_peer), 0, 0, 0, limit, 0, 0, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1359,9 +1355,9 @@ class ReadHistoryQuery : public Td::ResultHandler { void send(DialogId dialog_id, MessageId max_message_id) { dialog_id_ = dialog_id; - send_query(G()->net_query_creator().create(create_storer( + send_query(G()->net_query_creator().create( telegram_api::messages_readHistory(td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read), - max_message_id.get_server_message_id().get())))); + max_message_id.get_server_message_id().get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1403,8 +1399,8 @@ class ReadChannelHistoryQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer( - telegram_api::channels_readHistory(std::move(input_channel), max_message_id.get_server_message_id().get())))); + send_query(G()->net_query_creator().create( + telegram_api::channels_readHistory(std::move(input_channel), max_message_id.get_server_message_id().get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1459,19 +1455,19 @@ class SearchMessagesQuery : public Td::ResultHandler { random_id_ = random_id; if (filter == SearchMessagesFilter::UnreadMention) { - send_query(G()->net_query_creator().create(create_storer( + send_query(G()->net_query_creator().create( telegram_api::messages_getUnreadMentions(std::move(input_peer), from_message_id.get_server_message_id().get(), - offset, limit, std::numeric_limits::max(), 0)))); + offset, limit, std::numeric_limits::max(), 0))); } else { int32 flags = 0; if (sender_input_user != nullptr) { flags |= telegram_api::messages_search::FROM_ID_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_search( + send_query(G()->net_query_creator().create(telegram_api::messages_search( flags, std::move(input_peer), query, std::move(sender_input_user), MessagesManager::get_input_messages_filter(filter), 0, std::numeric_limits::max(), - from_message_id.get_server_message_id().get(), offset, limit, std::numeric_limits::max(), 0, 0)))); + from_message_id.get_server_message_id().get(), offset, limit, std::numeric_limits::max(), 0, 0))); } } @@ -1530,9 +1526,9 @@ class SearchMessagesGlobalQuery : public Td::ResultHandler { if (!ignore_folder_id) { flags |= telegram_api::messages_searchGlobal::FOLDER_ID_MASK; } - send_query(G()->net_query_creator().create(create_storer( + send_query(G()->net_query_creator().create( telegram_api::messages_searchGlobal(flags, folder_id.get(), query, offset_date_, std::move(input_peer), - offset_message_id.get_server_message_id().get(), limit)))); + offset_message_id.get_server_message_id().get(), limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1571,8 +1567,8 @@ class GetAllScheduledMessagesQuery : public Td::ResultHandler { dialog_id_ = dialog_id; generation_ = generation; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getScheduledHistory(std::move(input_peer), hash)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getScheduledHistory(std::move(input_peer), hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1618,8 +1614,8 @@ class GetRecentLocationsQuery : public Td::ResultHandler { limit_ = limit; random_id_ = random_id; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getRecentLocations(std::move(input_peer), limit, 0)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getRecentLocations(std::move(input_peer), limit, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1664,9 +1660,9 @@ class DeleteHistoryQuery : public Td::ResultHandler { } LOG(INFO) << "Delete " << dialog_id_ << " history up to " << max_message_id_ << " with flags " << flags; - send_query(G()->net_query_creator().create(create_storer( + send_query(G()->net_query_creator().create( telegram_api::messages_deleteHistory(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), - max_message_id_.get_server_message_id().get())))); + max_message_id_.get_server_message_id().get()))); } public: @@ -1727,8 +1723,8 @@ class DeleteChannelHistoryQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_query(G()->net_query_creator().create(create_storer( - telegram_api::channels_deleteHistory(std::move(input_channel), max_message_id.get_server_message_id().get())))); + send_query(G()->net_query_creator().create( + telegram_api::channels_deleteHistory(std::move(input_channel), max_message_id.get_server_message_id().get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1770,7 +1766,7 @@ class DeleteUserHistoryQuery : public Td::ResultHandler { LOG(INFO) << "Delete all messages from " << user_id_ << " in " << channel_id_; send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_deleteUserHistory(std::move(input_channel), std::move(input_user))))); + telegram_api::channels_deleteUserHistory(std::move(input_channel), std::move(input_user)))); } public: @@ -1825,8 +1821,7 @@ class ReadAllMentionsQuery : public Td::ResultHandler { LOG(INFO) << "Read all mentions in " << dialog_id_; - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_readMentions(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_readMentions(std::move(input_peer)))); } public: @@ -1943,10 +1938,10 @@ class SendMessageActor : public NetActorOnce { flags |= MessagesManager::SEND_MESSAGE_FLAG_HAS_ENTITIES; } - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_sendMessage( + auto query = G()->net_query_creator().create(telegram_api::messages_sendMessage( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), reply_to_message_id.get_server_message_id().get(), text, random_id, std::move(reply_markup), - std::move(entities), schedule_date))); + std::move(entities), schedule_date)); if (G()->shared_config().get_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_id](Unit) { @@ -2014,8 +2009,8 @@ class StartBotQuery : public Td::ResultHandler { random_id_ = random_id; dialog_id_ = dialog_id; - auto query = G()->net_query_creator().create(create_storer( - telegram_api::messages_startBot(std::move(bot_input_user), std::move(input_peer), random_id, parameter))); + auto query = G()->net_query_creator().create( + telegram_api::messages_startBot(std::move(bot_input_user), std::move(input_peer), random_id, parameter)); if (G()->shared_config().get_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_id](Unit) { @@ -2065,9 +2060,9 @@ class SendInlineBotResultQuery : public Td::ResultHandler { auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); CHECK(input_peer != nullptr); - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_sendInlineBotResult( + auto query = G()->net_query_creator().create(telegram_api::messages_sendInlineBotResult( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), - reply_to_message_id.get_server_message_id().get(), random_id, query_id, result_id, schedule_date))); + reply_to_message_id.get_server_message_id().get(), random_id, query_id, result_id, schedule_date)); auto send_query_ref = query.get_weak(); send_query(std::move(query)); return send_query_ref; @@ -2122,9 +2117,9 @@ class SendMultiMediaActor : public NetActorOnce { return; } - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_sendMultiMedia( + auto query = G()->net_query_creator().create(telegram_api::messages_sendMultiMedia( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), - reply_to_message_id.get_server_message_id().get(), std::move(input_single_media), schedule_date))); + reply_to_message_id.get_server_message_id().get(), std::move(input_single_media), schedule_date)); // no quick ack, because file reference errors are very likely to happen query->debug("send to MessagesManager::MultiSequenceDispatcher"); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, @@ -2240,7 +2235,7 @@ class SendMediaActor : public NetActorOnce { std::move(input_media), text, random_id, std::move(reply_markup), std::move(entities), schedule_date); LOG(INFO) << "Send media: " << to_string(request); - auto query = G()->net_query_creator().create(create_storer(request)); + auto query = G()->net_query_creator().create(request); if (G()->shared_config().get_option_boolean("use_quick_ack") && was_uploaded_) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_id](Unit) { @@ -2340,7 +2335,7 @@ class UploadMediaQuery : public Td::ResultHandler { } send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_uploadMedia(std::move(input_peer), std::move(input_media))))); + telegram_api::messages_uploadMedia(std::move(input_peer), std::move(input_media)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -2413,7 +2408,7 @@ class SendScheduledMessageActor : public NetActorOnce { int32 server_message_id = message_id.get_scheduled_server_message_id().get(); auto query = G()->net_query_creator().create( - create_storer(telegram_api::messages_sendScheduledMessages(std::move(input_peer), {server_message_id}))); + telegram_api::messages_sendScheduledMessages(std::move(input_peer), {server_message_id})); query->debug("send to MessagesManager::MultiSequenceDispatcher"); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, @@ -2487,9 +2482,9 @@ class EditMessageActor : public NetActorOnce { int32 server_message_id = schedule_date != 0 ? message_id.get_scheduled_server_message_id().get() : message_id.get_server_message_id().get(); - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_editMessage( + auto query = G()->net_query_creator().create(telegram_api::messages_editMessage( flags, false /*ignored*/, std::move(input_peer), server_message_id, text, std::move(input_media), - std::move(reply_markup), std::move(entities), schedule_date))); + std::move(reply_markup), std::move(entities), schedule_date)); query->debug("send to MessagesManager::MultiSequenceDispatcher"); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, @@ -2551,11 +2546,11 @@ class EditInlineMessageQuery : public Td::ResultHandler { LOG(DEBUG) << "Edit inline message with flags " << flags; auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_editInlineBotMessage( - flags, false /*ignored*/, std::move(input_bot_inline_message_id), text, - std::move(input_media), std::move(reply_markup), std::move(entities))), - dc_id)); + send_query(G()->net_query_creator().create( + telegram_api::messages_editInlineBotMessage(flags, false /*ignored*/, std::move(input_bot_inline_message_id), + text, std::move(input_media), std::move(reply_markup), + std::move(entities)), + dc_id)); } void on_result(uint64 id, BufferSlice packet) override { @@ -2603,9 +2598,9 @@ class SetGameScoreActor : public NetActorOnce { } CHECK(input_user != nullptr); - auto query = G()->net_query_creator().create(create_storer( + 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))); + message_id.get_server_message_id().get(), std::move(input_user), score)); LOG(INFO) << "Set game score to " << score; @@ -2657,9 +2652,8 @@ class SetInlineGameScoreQuery : public Td::ResultHandler { 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( - create_storer(telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/, - std::move(input_bot_inline_message_id), - std::move(input_user), score)), + telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/, + std::move(input_bot_inline_message_id), std::move(input_user), score), dc_id)); } @@ -2698,8 +2692,8 @@ class GetGameHighScoresQuery : public Td::ResultHandler { CHECK(input_peer != nullptr); CHECK(input_user != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getGameHighScores( - std::move(input_peer), message_id.get_server_message_id().get(), std::move(input_user))))); + 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) override { @@ -2736,9 +2730,9 @@ class GetInlineGameHighScoresQuery : public Td::ResultHandler { random_id_ = random_id; auto dc_id = DcId::internal(input_bot_inline_message_id->dc_id_); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getInlineGameHighScores( - std::move(input_bot_inline_message_id), std::move(input_user))), - 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) override { @@ -2788,10 +2782,10 @@ class ForwardMessagesActor : public NetActorOnce { return; } - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_forwardMessages( + auto query = G()->net_query_creator().create(telegram_api::messages_forwardMessages( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(from_input_peer), MessagesManager::get_server_message_ids(message_ids), std::move(random_ids), std::move(to_input_peer), - schedule_date))); + schedule_date)); if (G()->shared_config().get_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_ids = random_ids_](Unit) { @@ -2882,7 +2876,7 @@ class SendScreenshotNotificationQuery : public Td::ResultHandler { CHECK(input_peer != nullptr); auto query = G()->net_query_creator().create( - create_storer(telegram_api::messages_sendScreenshotNotification(std::move(input_peer), 0, random_id))); + telegram_api::messages_sendScreenshotNotification(std::move(input_peer), 0, random_id)); send_query(std::move(query)); } @@ -2925,8 +2919,8 @@ class SetTypingQuery : public Td::ResultHandler { auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); CHECK(input_peer != nullptr); - auto net_query = G()->net_query_creator().create( - create_storer(telegram_api::messages_setTyping(std::move(input_peer), std::move(action)))); + auto net_query = + G()->net_query_creator().create(telegram_api::messages_setTyping(std::move(input_peer), std::move(action))); auto result = net_query.get_weak(); send_query(std::move(net_query)); return result; @@ -2957,14 +2951,16 @@ class SetTypingQuery : public Td::ResultHandler { class DeleteMessagesQuery : public Td::ResultHandler { Promise promise_; + DialogId dialog_id_; int32 query_count_; public: explicit DeleteMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(vector &&message_ids, bool revoke) { - LOG(INFO) << "Send deleteMessagesQuery to delete " << format::as_array(message_ids); + void send(DialogId dialog_id, vector &&message_ids, bool revoke) { + LOG(INFO) << "Send deleteMessagesQuery to delete " << format::as_array(message_ids) << " in " << dialog_id; + dialog_id_ = dialog_id; int32 flags = 0; if (revoke) { flags |= telegram_api::messages_deleteMessages::REVOKE_MASK; @@ -2980,7 +2976,7 @@ class DeleteMessagesQuery : public Td::ResultHandler { query_count_++; send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_deleteMessages(flags, false /*ignored*/, std::move(slice))))); + telegram_api::messages_deleteMessages(flags, false /*ignored*/, std::move(slice)))); } } @@ -3003,7 +2999,8 @@ class DeleteMessagesQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status) && + (dialog_id_.get_type() == DialogType::User || status.message() != "MESSAGE_DELETE_FORBIDDEN")) { LOG(ERROR) << "Receive error for delete messages: " << status; } promise_.set_error(std::move(status)); @@ -3036,7 +3033,7 @@ class DeleteChannelMessagesQuery : public Td::ResultHandler { auto input_channel = td->contacts_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); send_query(G()->net_query_creator().create( - create_storer(telegram_api::channels_deleteMessages(std::move(input_channel), std::move(slice))))); + telegram_api::channels_deleteMessages(std::move(input_channel), std::move(slice)))); } } @@ -3061,7 +3058,8 @@ class DeleteChannelMessagesQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!td->contacts_manager_->on_get_channel_error(channel_id_, status, "DeleteChannelMessagesQuery")) { + if (!td->contacts_manager_->on_get_channel_error(channel_id_, status, "DeleteChannelMessagesQuery") && + status.message() != "MESSAGE_DELETE_FORBIDDEN") { LOG(ERROR) << "Receive error for delete channel messages: " << status; } promise_.set_error(std::move(status)); @@ -3084,8 +3082,8 @@ class DeleteScheduledMessagesQuery : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(0, Status::Error(400, "Can't access the chat")); } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_deleteScheduledMessages( - std::move(input_peer), MessagesManager::get_scheduled_server_message_ids(message_ids))))); + send_query(G()->net_query_creator().create(telegram_api::messages_deleteScheduledMessages( + std::move(input_peer), MessagesManager::get_scheduled_server_message_ids(message_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3117,8 +3115,7 @@ class GetDialogNotifySettingsQuery : public Td::ResultHandler { dialog_id_ = dialog_id; auto input_notify_peer = td->messages_manager_->get_input_notify_peer(dialog_id); CHECK(input_notify_peer != nullptr); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_getNotifySettings(std::move(input_notify_peer))))); + send_query(G()->net_query_creator().create(telegram_api::account_getNotifySettings(std::move(input_notify_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3155,8 +3152,8 @@ class GetNotifySettingsExceptionsQuery : public Td::ResultHandler { if (compare_sound) { flags |= telegram_api::account_getNotifyExceptions::COMPARE_SOUND_MASK; } - send_query(G()->net_query_creator().create(create_storer( - telegram_api::account_getNotifyExceptions(flags, false /* ignored */, std::move(input_notify_peer))))); + send_query(G()->net_query_creator().create( + telegram_api::account_getNotifyExceptions(flags, false /* ignored */, std::move(input_notify_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3214,8 +3211,7 @@ class GetScopeNotifySettingsQuery : public Td::ResultHandler { scope_ = scope; auto input_notify_peer = get_input_notify_peer(scope); CHECK(input_notify_peer != nullptr); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_getNotifySettings(std::move(input_notify_peer))))); + send_query(G()->net_query_creator().create(telegram_api::account_getNotifySettings(std::move(input_notify_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3264,10 +3260,10 @@ class UpdateDialogNotifySettingsQuery : public Td::ResultHandler { if (new_settings.silent_send_message) { flags |= telegram_api::inputPeerNotifySettings::SILENT_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_updateNotifySettings( + send_query(G()->net_query_creator().create(telegram_api::account_updateNotifySettings( std::move(input_notify_peer), make_tl_object( flags, new_settings.show_preview, new_settings.silent_send_message, - new_settings.mute_until, new_settings.sound))))); + new_settings.mute_until, new_settings.sound)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3312,10 +3308,10 @@ class UpdateScopeNotifySettingsQuery : public Td::ResultHandler { int32 flags = telegram_api::inputPeerNotifySettings::MUTE_UNTIL_MASK | telegram_api::inputPeerNotifySettings::SOUND_MASK | telegram_api::inputPeerNotifySettings::SHOW_PREVIEWS_MASK; - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_updateNotifySettings( + send_query(G()->net_query_creator().create(telegram_api::account_updateNotifySettings( std::move(input_notify_peer), make_tl_object(flags, new_settings.show_preview, false, - new_settings.mute_until, new_settings.sound))))); + new_settings.mute_until, new_settings.sound)))); scope_ = scope; } @@ -3353,7 +3349,7 @@ class ResetNotifySettingsQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_resetNotifySettings()))); + send_query(G()->net_query_creator().create(telegram_api::account_resetNotifySettings())); } void on_result(uint64 id, BufferSlice packet) override { @@ -3371,7 +3367,7 @@ class ResetNotifySettingsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for reset notification settings: " << status; } promise_.set_error(std::move(status)); @@ -3388,8 +3384,7 @@ class GetPeerSettingsQuery : public Td::ResultHandler { auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_getPeerSettings(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getPeerSettings(std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3424,11 +3419,9 @@ class UpdatePeerSettingsQuery : public Td::ResultHandler { } if (is_spam_dialog) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_reportSpam(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_reportSpam(std::move(input_peer)))); } else { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_hidePeerSettingsBar(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_hidePeerSettingsBar(std::move(input_peer)))); } } @@ -3453,7 +3446,7 @@ class UpdatePeerSettingsQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { LOG(INFO) << "Receive error for update peer settings: " << status; td->messages_manager_->on_get_dialog_error(dialog_id_, status, "UpdatePeerSettingsQuery"); - td->messages_manager_->repair_dialog_action_bar(dialog_id_, "UpdatePeerSettingsQuery"); + td->messages_manager_->reget_dialog_action_bar(dialog_id_, "UpdatePeerSettingsQuery"); promise_.set_error(std::move(status)); } }; @@ -3473,8 +3466,7 @@ class ReportEncryptedSpamQuery : public Td::ResultHandler { CHECK(input_peer != nullptr); LOG(INFO) << "Report spam in " << to_string(input_peer); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_reportEncryptedSpam(std::move(input_peer))))); + send_query(G()->net_query_creator().create(telegram_api::messages_reportEncryptedSpam(std::move(input_peer)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3495,7 +3487,7 @@ class ReportEncryptedSpamQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { LOG(INFO) << "Receive error for report encrypted spam: " << status; td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReportEncryptedSpamQuery"); - td->messages_manager_->repair_dialog_action_bar( + td->messages_manager_->reget_dialog_action_bar( DialogId(td->contacts_manager_->get_secret_chat_user_id(dialog_id_.get_secret_chat_id())), "ReportEncryptedSpamQuery"); promise_.set_error(std::move(status)); @@ -3519,10 +3511,10 @@ class ReportPeerQuery : public Td::ResultHandler { if (message_ids.empty()) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason))))); + telegram_api::account_reportPeer(std::move(input_peer), std::move(report_reason)))); } else { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_report( - std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), std::move(report_reason))))); + send_query(G()->net_query_creator().create(telegram_api::messages_report( + std::move(input_peer), MessagesManager::get_server_message_ids(message_ids), std::move(report_reason)))); } } @@ -3546,7 +3538,7 @@ class ReportPeerQuery : public Td::ResultHandler { void on_error(uint64 id, Status status) override { LOG(INFO) << "Receive error for report peer: " << status; td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ReportPeerQuery"); - td->messages_manager_->repair_dialog_action_bar(dialog_id_, "ReportPeerQuery"); + td->messages_manager_->reget_dialog_action_bar(dialog_id_, "ReportPeerQuery"); promise_.set_error(std::move(status)); } }; @@ -3568,8 +3560,7 @@ class EditPeerFoldersQuery : public Td::ResultHandler { vector> input_folder_peers; input_folder_peers.push_back( telegram_api::make_object(std::move(input_peer), folder_id.get())); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::folders_editPeerFolders(std::move(input_folder_peers))))); + send_query(G()->net_query_creator().create(telegram_api::folders_editPeerFolders(std::move(input_folder_peers)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3612,8 +3603,8 @@ class GetStatsUrlQuery : public Td::ResultHandler { if (is_dark) { flags |= telegram_api::messages_getStatsURL::DARK_MASK; } - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_getStatsURL(flags, false /*ignored*/, std::move(input_peer), parameters)))); + send_query(G()->net_query_creator().create( + telegram_api::messages_getStatsURL(flags, false /*ignored*/, std::move(input_peer), parameters))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3647,8 +3638,8 @@ class RequestUrlAuthQuery : public Td::ResultHandler { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_requestUrlAuth( - std::move(input_peer), message_id.get_server_message_id().get(), button_id)))); + send_query(G()->net_query_creator().create(telegram_api::messages_requestUrlAuth( + std::move(input_peer), message_id.get_server_message_id().get(), button_id))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3711,8 +3702,8 @@ class AcceptUrlAuthQuery : public Td::ResultHandler { if (allow_write_access) { flags |= telegram_api::messages_acceptUrlAuth::WRITE_ALLOWED_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_acceptUrlAuth( - flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), button_id)))); + send_query(G()->net_query_creator().create(telegram_api::messages_acceptUrlAuth( + flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), button_id))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3764,9 +3755,9 @@ class GetChannelDifferenceQuery : public Td::ResultHandler { if (force) { flags |= telegram_api::updates_getChannelDifference::FORCE_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::updates_getChannelDifference( + send_query(G()->net_query_creator().create(telegram_api::updates_getChannelDifference( flags, false /*ignored*/, std::move(input_channel), make_tl_object(), - pts, limit)))); + pts, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3799,7 +3790,7 @@ class ResolveUsernameQuery : public Td::ResultHandler { username_ = username; LOG(INFO) << "Send ResolveUsernameQuery with username = " << username; - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_resolveUsername(username)))); + send_query(G()->net_query_creator().create(telegram_api::contacts_resolveUsername(username))); } void on_result(uint64 id, BufferSlice packet) override { @@ -3852,8 +3843,8 @@ class GetChannelAdminLogQuery : public Td::ResultHandler { flags |= telegram_api::channels_getAdminLog::ADMINS_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getAdminLog( - flags, std::move(input_channel), query, std::move(filter), std::move(input_users), from_event_id, 0, limit)))); + send_query(G()->net_query_creator().create(telegram_api::channels_getAdminLog( + flags, std::move(input_channel), query, std::move(filter), std::move(input_users), from_event_id, 0, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -4239,8 +4230,12 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_reply_markup) { parse(reply_markup, parser); } + is_content_secret |= is_secret_message_content(ttl, content->get_type()); // repair is_content_secret for old messages + if (hide_edit_date && content->get_type() == MessageContentType::LiveLocation) { + hide_edit_date = false; + } } template @@ -4346,6 +4341,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const { STORE_FLAG(can_report_location); STORE_FLAG(has_scheduled_server_messages); STORE_FLAG(has_scheduled_database_messages); + STORE_FLAG(need_repair_channel_server_unread_count); END_STORE_FLAGS(); } @@ -4506,6 +4502,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) { PARSE_FLAG(can_report_location); PARSE_FLAG(has_scheduled_server_messages); PARSE_FLAG(has_scheduled_database_messages); + PARSE_FLAG(need_repair_channel_server_unread_count); END_PARSE_FLAGS(); } else { is_folder_id_inited = false; @@ -5437,28 +5434,66 @@ MessagesManager::Dialog *MessagesManager::get_service_notifications_dialog() { return get_dialog(service_notifications_dialog_id); } +void MessagesManager::save_auth_notification_ids() { + auto min_date = G()->unix_time() - AUTH_NOTIFICATION_ID_CACHE_TIME; + vector ids; + for (auto &it : auth_notification_id_date_) { + auto date = it.second; + if (date < min_date) { + continue; + } + ids.push_back(it.first); + ids.push_back(to_string(date)); + } + + if (ids.empty()) { + G()->td_db()->get_binlog_pmc()->erase("auth_notification_ids"); + return; + } + + G()->td_db()->get_binlog_pmc()->set("auth_notification_ids", implode(ids, ',')); +} + void MessagesManager::on_update_service_notification(tl_object_ptr &&update, bool skip_new_entities, Promise &&promise) { int32 ttl = 0; bool has_date = (update->flags_ & telegram_api::updateServiceNotification::INBOX_DATE_MASK) != 0; auto date = has_date ? update->inbox_date_ : G()->unix_time(); - auto message_text = - get_message_text(td_->contacts_manager_.get(), std::move(update->message_), std::move(update->entities_), - skip_new_entities, date, "on_update_service_notification"); - auto content = get_message_content( - td_, std::move(message_text), std::move(update->media_), - td_->auth_manager_->is_bot() ? DialogId() : get_service_notifications_dialog()->dialog_id, false, UserId(), &ttl); + if (date <= 0) { + LOG(ERROR) << "Receive message date " << date << " in " << to_string(update); + return; + } + bool is_auth_notification = begins_with(update->type_, "auth"); + if (is_auth_notification) { + auto &old_date = auth_notification_id_date_[update->type_.substr(4)]; + if (date <= old_date) { + LOG(INFO) << "Skip already applied " << to_string(update); + return; + } + old_date = date; + } + + bool is_authorized = td_->auth_manager_->is_authorized(); + bool is_user = is_authorized && !td_->auth_manager_->is_bot(); + auto contacts_manager = is_authorized ? td_->contacts_manager_.get() : nullptr; + auto message_text = get_message_text(contacts_manager, std::move(update->message_), std::move(update->entities_), + skip_new_entities, date, false, "on_update_service_notification"); + DialogId owner_dialog_id = is_user ? get_service_notifications_dialog()->dialog_id : DialogId(); + auto content = get_message_content(td_, std::move(message_text), std::move(update->media_), owner_dialog_id, false, + UserId(), &ttl); bool is_content_secret = is_secret_message_content(ttl, content->get_type()); + if ((update->flags_ & telegram_api::updateServiceNotification::POPUP_MASK) != 0) { send_closure(G()->td(), &Td::send_update, td_api::make_object( update->type_, get_message_content_object(content.get(), td_, date, is_content_secret))); } - if (has_date && !td_->auth_manager_->is_bot()) { + if (has_date && is_user) { Dialog *d = get_service_notifications_dialog(); CHECK(d != nullptr); auto dialog_id = d->dialog_id; CHECK(dialog_id.get_type() == DialogType::User); + auto new_message = make_unique(); set_message_id(new_message, get_next_local_message_id(d)); new_message->sender_user_id = dialog_id.get_user_id(); @@ -5483,6 +5518,10 @@ void MessagesManager::on_update_service_notification(tl_object_ptr &&update) { @@ -5625,7 +5664,7 @@ void MessagesManager::on_update_channel_too_long(tl_object_ptrpts == pts); - update_dialog_pos(d, false, "on_update_channel_too_long"); + update_dialog_pos(d, "on_update_channel_too_long"); } } @@ -5910,7 +5949,7 @@ void MessagesManager::on_update_delete_scheduled_messages(DialogId dialog_id, send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false); - send_update_chat_has_scheduled_messages(d); + send_update_chat_has_scheduled_messages(d, true); } void MessagesManager::on_update_include_sponsored_dialog_to_unread_count() { @@ -6014,6 +6053,7 @@ bool MessagesManager::need_cancel_user_dialog_action(int32 action_id, MessageCon case MessageContentType::PassportDataSent: case MessageContentType::PassportDataReceived: case MessageContentType::Poll: + case MessageContentType::Dice: return false; default: UNREACHABLE(); @@ -6126,7 +6166,7 @@ void MessagesManager::cancel_user_dialog_action(DialogId dialog_id, const Messag void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_ptr &&update, int32 new_pts, int32 pts_count, const char *source, - bool is_postponed_udpate) { + bool is_postponed_update) { LOG(INFO) << "Receive from " << source << " pending " << to_string(update); CHECK(update != nullptr); CHECK(dialog_id.get_type() == DialogType::Channel); @@ -6153,7 +6193,7 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p d = add_dialog(dialog_id); CHECK(d != nullptr); CHECK(d->pts == pts); - update_dialog_pos(d, false, "add_pending_channel_update"); + update_dialog_pos(d, "add_pending_channel_update"); } } if (d == nullptr) { @@ -6165,7 +6205,7 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p } else { int32 old_pts = d->pts; if (new_pts <= old_pts) { // very old or unuseful update - if (new_pts < old_pts - 19999 && !is_postponed_udpate) { + if (new_pts < old_pts - 19999 && !is_postponed_update) { // restore channel pts after delete_first_messages LOG(ERROR) << "Restore pts in " << d->dialog_id << " from " << source << " after delete_first_messages from " << old_pts << " to " << new_pts << " is temporarily disabled, pts_count = " << pts_count @@ -6244,6 +6284,13 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p set_channel_pts(d, new_pts, source); } +bool MessagesManager::is_old_channel_update(DialogId dialog_id, int32 new_pts) { + CHECK(dialog_id.get_type() == DialogType::Channel); + + const Dialog *d = get_dialog_force(dialog_id); + return new_pts <= (d == nullptr ? load_channel_pts(dialog_id) : d->pts); +} + void MessagesManager::set_get_difference_timeout(double timeout) { if (!pts_gap_timeout_.has_timeout()) { LOG(INFO) << "Gap in pts has found, current pts is " << td_->updates_manager_->get_pts(); @@ -6457,11 +6504,11 @@ bool MessagesManager::update_dialog_notification_settings(DialogId dialog_id, *current_settings = new_settings; if (!was_muted && is_dialog_muted(d)) { - remove_all_dialog_notifications(d, false, "save_scope_notification_settings"); + remove_all_dialog_notifications(d, false, "update_dialog_notification_settings 2"); } if (is_dialog_pinned_message_notifications_disabled(d) && d->mention_notification_group.group_id.is_valid() && d->pinned_message_notification_message_id.is_valid()) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "update_dialog_notification_settings 3"); } if (was_dialog_mentions_disabled != is_dialog_mention_notifications_disabled(d)) { if (was_dialog_mentions_disabled) { @@ -6507,7 +6554,7 @@ bool MessagesManager::update_scope_notification_settings(NotificationSettingsSco if (d->notification_settings.use_default_disable_pinned_message_notifications && d->mention_notification_group.group_id.is_valid() && d->pinned_message_notification_message_id.is_valid() && get_dialog_notification_setting_scope(d->dialog_id) == scope) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "update_scope_notification_settings"); } } } @@ -6763,12 +6810,12 @@ bool MessagesManager::update_dialog_silent_send_message(Dialog *d, bool silent_s return true; } -void MessagesManager::repair_dialog_action_bar(DialogId dialog_id, const char *source) { +void MessagesManager::reget_dialog_action_bar(DialogId dialog_id, const char *source) { if (G()->close_flag() || !dialog_id.is_valid() || td_->auth_manager_->is_bot()) { return; } - LOG(INFO) << "Repair action bar in " << dialog_id << " from " << source; + LOG(INFO) << "Reget action bar in " << dialog_id << " from " << source; switch (dialog_id.get_type()) { case DialogType::User: td_->contacts_manager_->reload_user_full(dialog_id.get_user_id()); @@ -6787,6 +6834,22 @@ void MessagesManager::repair_dialog_action_bar(DialogId dialog_id, const char *s } } +void MessagesManager::repair_dialog_action_bar(Dialog *d, const char *source) { + CHECK(d != nullptr); + auto dialog_id = d->dialog_id; + d->know_action_bar = false; + if (have_input_peer(dialog_id, AccessRights::Read)) { + create_actor( + "RepairDialogActionBarActor", 1.0, + PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, source](Result result) { + send_closure(actor_id, &MessagesManager::reget_dialog_action_bar, dialog_id, source); + })) + .release(); + } + // there is no need to change action bar + on_dialog_updated(dialog_id, source); +} + void MessagesManager::hide_dialog_action_bar(DialogId dialog_id) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -6934,7 +6997,7 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptrfirst_message_id.is_valid() && d->first_message_id > first_received_message_id) - // << "Receive message " << first_received_message_id << ", but first chat message is " << d->first_message_id; + // << "Receive " << first_received_message_id << ", but first chat message is " << d->first_message_id; bool need_update_database_message_ids = last_added_message_id.is_valid() && (from_the_end || (last_added_message_id >= d->first_database_message_id && @@ -8220,11 +8283,14 @@ void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint3 Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); if (generation < d->scheduled_messages_sync_generation) { + LOG(INFO) << "Ignore scheduled messages with old generation " << generation << " instead of " + << d->scheduled_messages_sync_generation << " in " << dialog_id; return; } d->scheduled_messages_sync_generation = generation; if (is_not_modified) { + LOG(INFO) << "Scheduled messages are mot modified in " << dialog_id; return; } @@ -8239,6 +8305,7 @@ void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint3 } bool is_channel_message = dialog_id.get_type() == DialogType::Channel; + bool has_scheduled_server_messages = false; for (auto &message : messages) { auto message_dialog_id = get_message_dialog_id(message); if (message_dialog_id != dialog_id) { @@ -8249,14 +8316,16 @@ void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint3 continue; } - auto full_message_id = on_get_message(std::move(message), false, is_channel_message, true, false, false, - "on_get_scheduled_server_messages"); + auto full_message_id = on_get_message(std::move(message), d->sent_scheduled_messages, is_channel_message, true, + false, false, "on_get_scheduled_server_messages"); auto message_id = full_message_id.get_message_id(); if (message_id.is_valid_scheduled()) { CHECK(message_id.is_scheduled_server()); old_server_message_ids.erase(message_id.get_scheduled_server_message_id()); + has_scheduled_server_messages = true; } } + on_update_dialog_has_scheduled_server_messages(dialog_id, has_scheduled_server_messages); for (auto it : old_server_message_ids) { auto message_id = it.second; @@ -8265,7 +8334,7 @@ void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint3 send_update_delete_messages(dialog_id, {message->message_id.get()}, true, false); } - send_update_chat_has_scheduled_messages(d); + send_update_chat_has_scheduled_messages(d, false); } void MessagesManager::on_get_recent_locations(DialogId dialog_id, int32 limit, int64 random_id, int32 total_count, @@ -8453,6 +8522,32 @@ bool MessagesManager::can_delete_channel_message(DialogParticipantStatus status, return true; } +bool MessagesManager::can_delete_message(DialogId dialog_id, const Message *m) const { + if (m == nullptr) { + return true; + } + switch (dialog_id.get_type()) { + case DialogType::User: + if (G()->unix_time_cached() < m->date + 86400 && m->content->get_type() == MessageContentType::Dice && + dialog_id != get_my_dialog_id()) { + return false; + } + return true; + case DialogType::Chat: + return true; + case DialogType::Channel: { + auto dialog_status = td_->contacts_manager_->get_channel_permissions(dialog_id.get_channel_id()); + return can_delete_channel_message(dialog_status, m, td_->auth_manager_->is_bot()); + } + case DialogType::SecretChat: + return true; + case DialogType::None: + default: + UNREACHABLE(); + return false; + } +} + bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) const { if (m == nullptr) { return true; @@ -8530,47 +8625,29 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vectormessage_id.is_scheduled()) { - if (message->message_id.is_scheduled_server()) { - deleted_scheduled_server_message_ids.push_back(message->message_id); + auto m = get_message_force(d, message_id, "delete_messages"); + if (m != nullptr) { + if (m->message_id.is_scheduled()) { + if (m->message_id.is_scheduled_server()) { + deleted_scheduled_server_message_ids.push_back(m->message_id); } } else { - if (message->message_id.is_server() || is_secret) { - deleted_server_message_ids.push_back(message->message_id); + if (m->message_id.is_server() || is_secret) { + deleted_server_message_ids.push_back(m->message_id); } } } } bool is_bot = td_->auth_manager_->is_bot(); - switch (dialog_id.get_type()) { - case DialogType::User: - case DialogType::Chat: - if (is_bot) { - for (auto message_id : message_ids) { - if (!message_id.is_scheduled() && message_id.is_server() && - !can_revoke_message(dialog_id, get_message(d, message_id))) { - return promise.set_error(Status::Error(6, "Message can't be deleted")); - } - } - } - break; - case DialogType::Channel: { - auto dialog_status = td_->contacts_manager_->get_channel_permissions(dialog_id.get_channel_id()); - for (auto message_id : message_ids) { - if (!can_delete_channel_message(dialog_status, get_message(d, message_id), is_bot)) { - return promise.set_error(Status::Error(6, "Message can't be deleted")); - } - } - break; + for (auto message_id : message_ids) { + auto m = get_message(d, message_id); + if (!can_delete_message(dialog_id, m)) { + return promise.set_error(Status::Error(6, "Message can't be deleted")); + } + if (is_bot && !message_id.is_scheduled() && message_id.is_server() && !can_revoke_message(dialog_id, m)) { + return promise.set_error(Status::Error(6, "Message can't be deleted for everyone")); } - case DialogType::SecretChat: - break; - case DialogType::None: - default: - UNREACHABLE(); } MultiPromiseActorSafe mpas{"DeleteMessagesFromServerMultiPromiseActor"}; @@ -8598,7 +8675,7 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vectorcreate_handler(std::move(promise))->send(std::move(message_ids), revoke); + td_->create_handler(std::move(promise))->send(dialog_id, std::move(message_ids), revoke); break; case DialogType::Channel: td_->create_handler(std::move(promise)) @@ -8750,7 +8827,6 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from } auto dialog_type = dialog_id.get_type(); - bool is_secret = false; switch (dialog_type) { case DialogType::User: case DialogType::Chat: @@ -8765,7 +8841,6 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from } break; case DialogType::SecretChat: - is_secret = true; // ok break; case DialogType::None: @@ -8775,7 +8850,7 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from } auto last_new_message_id = d->last_new_message_id; - if (!is_secret && last_new_message_id == MessageId()) { + if (dialog_type != DialogType::SecretChat && last_new_message_id == MessageId()) { // TODO get dialog from the server and delete history from last message id } @@ -9154,10 +9229,11 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia set_dialog_last_message_id(d, MessageId(), "delete_all_dialog_messages"); send_update_chat_last_message(d, "delete_all_dialog_messages"); } - if (remove_from_dialog_list && d->pinned_order != DEFAULT_ORDER) { - set_dialog_is_pinned(d, false); + if (remove_from_dialog_list) { + set_dialog_order(d, DEFAULT_ORDER, true, false, "delete_all_dialog_messages 1"); + } else { + update_dialog_pos(d, "delete_all_dialog_messages 2"); } - update_dialog_pos(d, remove_from_dialog_list, "delete_all_dialog_messages"); on_dialog_updated(d->dialog_id, "delete_all_dialog_messages"); @@ -9380,8 +9456,10 @@ int32 MessagesManager::calc_new_unread_count_from_the_end(Dialog *d, MessageId m } // hint_unread_count is definitely wrong, ignore it - LOG(ERROR) << "Receive hint_unread_count = " << hint_unread_count << ", but found " << unread_count - << " unread messages in " << d->dialog_id; + if (need_unread_counter(d->order)) { + LOG(ERROR) << "Receive hint_unread_count = " << hint_unread_count << ", but found " << unread_count + << " unread messages in " << d->dialog_id; + } } if (!is_count_exact) { @@ -9416,9 +9494,12 @@ int32 MessagesManager::calc_new_unread_count(Dialog *d, MessageId max_message_id } void MessagesManager::repair_server_unread_count(DialogId dialog_id, int32 unread_count) { - if (td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot() || !have_input_peer(dialog_id, AccessRights::Read)) { return; } + if (pending_read_history_timeout_.has_timeout(dialog_id.get())) { + return; // postpone until read history request is sent + } LOG(INFO) << "Repair server unread count in " << dialog_id << " from " << unread_count; create_actor("RepairServerUnreadCountSleepActor", 0.2, @@ -9440,14 +9521,17 @@ void MessagesManager::repair_channel_server_unread_count(Dialog *d) { // all messages are already read return; } - if (d->order == 0) { + if (!need_unread_counter(d->order)) { // there is no unread count in left channels return; } + if (!d->need_repair_channel_server_unread_count) { + d->need_repair_channel_server_unread_count = true; + on_dialog_updated(d->dialog_id, "repair_channel_server_unread_count"); + } LOG(INFO) << "Reload ChannelFull for " << d->dialog_id << " to repair unread message counts"; - // TODO logevent? - td_->contacts_manager_->get_channel_full(d->dialog_id.get_channel_id(), Promise()); + td_->contacts_manager_->get_channel_full(d->dialog_id.get_channel_id(), false, Promise()); } void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_message_id, int32 unread_count, @@ -9460,6 +9544,11 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa Dialog *d = get_dialog_force(dialog_id); if (d != nullptr) { + if (d->need_repair_channel_server_unread_count) { + d->need_repair_channel_server_unread_count = false; + on_dialog_updated(dialog_id, "read_history_inbox"); + } + // there can be updateReadHistoryInbox up to message 0, if messages where read and then all messages where deleted if (!max_message_id.is_valid() && max_message_id != MessageId()) { LOG(ERROR) << "Receive read inbox update in " << dialog_id << " up to " << max_message_id << " from " << source; @@ -9468,6 +9557,10 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa if (d->is_last_read_inbox_message_id_inited && max_message_id <= d->last_read_inbox_message_id) { LOG(INFO) << "Receive read inbox update in " << dialog_id << " up to " << max_message_id << " from " << source << ", but all messages have already been read up to " << d->last_read_inbox_message_id; + if (max_message_id == d->last_read_inbox_message_id && unread_count >= 0 && + unread_count != d->server_unread_count) { + set_dialog_last_read_inbox_message_id(d, MessageId::min(), unread_count, d->local_unread_count, true, source); + } return; } @@ -9509,7 +9602,7 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa if (server_unread_count < 0) { server_unread_count = unread_count >= 0 ? unread_count : d->server_unread_count; if (dialog_id.get_type() != DialogType::SecretChat && have_input_peer(dialog_id, AccessRights::Read) && - d->order > 0) { + need_unread_counter(d->order)) { d->need_repair_server_unread_count = true; repair_server_unread_count(dialog_id, server_unread_count); } @@ -9580,15 +9673,24 @@ bool MessagesManager::need_unread_counter(int64 dialog_order) { return dialog_order != DEFAULT_ORDER; } -int32 MessagesManager::get_dialog_total_count(const DialogList &list) { +int32 MessagesManager::get_dialog_total_count(const DialogList &list) const { + int32 sponsored_dialog_count = 0; + if (sponsored_dialog_id_.is_valid() && list.folder_id == FolderId::main()) { + auto d = get_dialog(sponsored_dialog_id_); + CHECK(d != nullptr); + if (is_dialog_sponsored(d)) { + sponsored_dialog_count = 1; + } + } if (list.server_dialog_total_count_ != -1 && list.secret_chat_total_count_ != -1) { return std::max(list.server_dialog_total_count_ + list.secret_chat_total_count_, - list.in_memory_dialog_total_count_); + list.in_memory_dialog_total_count_) + + sponsored_dialog_count; } if (list.last_dialog_date_ == MAX_DIALOG_DATE) { - return list.in_memory_dialog_total_count_; + return list.in_memory_dialog_total_count_ + sponsored_dialog_count; } - return list.in_memory_dialog_total_count_ + 1; + return list.in_memory_dialog_total_count_ + sponsored_dialog_count + 1; } void MessagesManager::repair_server_dialog_total_count(FolderId folder_id) { @@ -9773,8 +9875,7 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId } send_update_unread_chat_count(d->folder_id, d->dialog_id, force_update, source); } - if (message_id != MessageId::min() && d->last_read_inbox_message_id.is_valid() && d->order != DEFAULT_ORDER && - d->order != SPONSORED_DIALOG_ORDER) { + if (message_id != MessageId::min() && d->last_read_inbox_message_id.is_valid() && d->order != DEFAULT_ORDER) { VLOG(notifications) << "Remove some notifications in " << d->dialog_id << " after updating last read inbox message to " << message_id << " and unread message count to " << server_unread_count << " + " << local_unread_count @@ -9807,7 +9908,7 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId if (d->mention_notification_group.group_id.is_valid() && d->pinned_message_notification_message_id.is_valid() && d->pinned_message_notification_message_id <= d->last_read_inbox_message_id) { // remove pinned message notification when it is read - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "set_dialog_last_read_inbox_message_id 2"); } } @@ -10171,10 +10272,10 @@ void MessagesManager::on_message_ttl_expired(Dialog *d, Message *m) { CHECK(m->ttl > 0); CHECK(d->dialog_id.get_type() != DialogType::SecretChat); ttl_unregister_message(d->dialog_id, m, Time::now(), "on_message_ttl_expired"); - unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}); + unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_ttl_expired"); remove_message_file_sources(d->dialog_id, m); on_message_ttl_expired_impl(d, m); - register_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}); + register_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_ttl_expired"); send_update_message_content(d->dialog_id, m->message_id, m->content.get(), m->date, m->is_content_secret, "on_message_ttl_expired"); } @@ -10281,16 +10382,13 @@ void MessagesManager::init() { if (r_dialog_id.is_error()) { LOG(ERROR) << "Can't parse " << sponsored_dialog_id_string; } else { - sponsored_dialog_id_ = DialogId(r_dialog_id.ok()); - if (!sponsored_dialog_id_.is_valid()) { - LOG(ERROR) << "Have invalid chat ID " << sponsored_dialog_id_string; - sponsored_dialog_id_ = DialogId(); + DialogId dialog_id(r_dialog_id.ok()); + + const Dialog *d = get_dialog_force(dialog_id); + if (d != nullptr) { + add_sponsored_dialog(d); } else { - Dialog *d = get_dialog_force(sponsored_dialog_id_); - if (d == nullptr) { - LOG(ERROR) << "Can't load " << sponsored_dialog_id_; - sponsored_dialog_id_ = DialogId(); - } + LOG(ERROR) << "Can't load " << dialog_id; } } } @@ -10391,6 +10489,26 @@ void MessagesManager::init() { } G()->td_db()->get_binlog_pmc()->erase("nsfac"); + auto auth_notification_ids_string = G()->td_db()->get_binlog_pmc()->get("auth_notification_ids"); + if (!auth_notification_ids_string.empty()) { + VLOG(notifications) << "Load auth_notification_ids = " << auth_notification_ids_string; + auto ids = full_split(auth_notification_ids_string, ','); + CHECK(ids.size() % 2 == 0); + bool is_changed = false; + auto min_date = G()->unix_time() - AUTH_NOTIFICATION_ID_CACHE_TIME; + for (size_t i = 0; i < ids.size(); i += 2) { + auto date = to_integer_safe(ids[i + 1]).ok(); + if (date < min_date) { + is_changed = true; + continue; + } + auth_notification_id_date_.emplace(std::move(ids[i]), date); + } + if (is_changed) { + save_auth_notification_ids(); + } + } + /* FI LE *f = std::f open("error.txt", "r"); if (f != nullptr) { @@ -11074,7 +11192,7 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( td_, get_message_text(td_->contacts_manager_.get(), std::move(message->message_), std::move(message->entities_), true, message_info.forward_header ? message_info.forward_header->date_ : message_info.date, - new_source.c_str()), + message_info.media_album_id != 0, new_source.c_str()), std::move(message->media_), message_info.dialog_id, is_content_read, message_info.via_bot_user_id, &message_info.ttl); message_info.reply_markup = @@ -11212,12 +11330,15 @@ std::pair> MessagesManager::creat edit_date = 0; } + auto content_type = message_info.content->get_type(); if (hide_edit_date && td_->auth_manager_->is_bot()) { hide_edit_date = false; } + if (hide_edit_date && content_type == MessageContentType::LiveLocation) { + hide_edit_date = false; + } int32 ttl = message_info.ttl; - auto content_type = message_info.content->get_type(); bool is_content_secret = is_secret_message_content(ttl, content_type); // should be calculated before TTL is adjusted if (ttl < 0) { LOG(ERROR) << "Wrong ttl = " << ttl << " received in " << message_id << " in " << dialog_id; @@ -11336,12 +11457,12 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f new_message->have_previous = have_previous; new_message->have_next = have_next; - bool need_update = from_update || message_id.is_scheduled(); + bool need_update = from_update; bool need_update_dialog_pos = false; MessageId old_message_id = find_old_message_id(dialog_id, message_id); bool need_add_active_live_location = false; - LOG(INFO) << "Found old " << old_message_id << " by " << FullMessageId{dialog_id, message_id}; + LOG(INFO) << "Found temporarily " << old_message_id << " for " << FullMessageId{dialog_id, message_id}; if (old_message_id.is_valid() || old_message_id.is_valid_scheduled()) { Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -11388,6 +11509,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f return FullMessageId(); } + being_readded_message_id_ = {dialog_id, old_message_id}; unique_ptr old_message = delete_message(d, old_message_id, false, &need_update_dialog_pos, "add sent message"); if (old_message == nullptr) { @@ -11395,6 +11517,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f // don't need to send update to the user, because the message has already been deleted LOG(INFO) << "Delete already deleted sent " << new_message->message_id << " from server"; delete_message_from_server(dialog_id, new_message->message_id, true); + being_readded_message_id_ = FullMessageId(); return FullMessageId(); } @@ -11428,6 +11551,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f const Message *m = add_message_to_dialog(dialog_id, std::move(new_message), from_update, &need_update, &need_update_dialog_pos, source); + being_readded_message_id_ = FullMessageId(); Dialog *d = get_dialog(dialog_id); if (m == nullptr) { if (need_update_dialog_pos && d != nullptr) { @@ -11463,7 +11587,7 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f return FullMessageId(); } - send_update_chat_has_scheduled_messages(d); + send_update_chat_has_scheduled_messages(d, false); if (need_update_dialog_pos) { send_update_chat_last_message(d, "on_get_message"); @@ -11674,21 +11798,29 @@ void MessagesManager::set_dialog_is_empty(Dialog *d, const char *source) { set_dialog_last_database_message_id(d, MessageId(), "set_dialog_is_empty"); } - update_dialog_pos(d, false, source); + update_dialog_pos(d, source); } void MessagesManager::set_dialog_is_pinned(DialogId dialog_id, bool is_pinned) { Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); + if (is_removed_from_dialog_list(d) && is_pinned) { + // the chat can't be pinned + return; + } if (!is_pinned && d->pinned_order == DEFAULT_ORDER) { return; } set_dialog_is_pinned(d, is_pinned); - update_dialog_pos(d, false, "set_dialog_is_pinned"); + update_dialog_pos(d, "set_dialog_is_pinned"); } void MessagesManager::set_dialog_is_pinned(Dialog *d, bool is_pinned) { CHECK(d != nullptr); + if (is_removed_from_dialog_list(d) && is_pinned) { + // the chat can't be pinned + return; + } bool was_pinned = d->pinned_order != DEFAULT_ORDER; d->pinned_order = is_pinned ? get_next_pinned_dialog_order() : DEFAULT_ORDER; on_dialog_updated(d->dialog_id, "set_dialog_is_pinned"); @@ -11696,7 +11828,7 @@ void MessagesManager::set_dialog_is_pinned(Dialog *d, bool is_pinned) { if (is_pinned != was_pinned) { LOG(INFO) << "Set " << d->dialog_id << " is pinned to " << is_pinned; LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in set_dialog_is_pinned"; - update_dialog_pos(d, false, "set_dialog_is_pinned", false); + update_dialog_pos(d, "set_dialog_is_pinned", false); send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), is_pinned, get_dialog_public_order(d))); } @@ -11736,7 +11868,7 @@ void MessagesManager::try_restore_dialog_reply_markup(Dialog *d, const Message * } } -void MessagesManager::set_dialog_pinned_message_notification(Dialog *d, MessageId message_id) { +void MessagesManager::set_dialog_pinned_message_notification(Dialog *d, MessageId message_id, const char *source) { CHECK(d != nullptr); CHECK(!message_id.is_scheduled()); auto old_message_id = d->pinned_message_notification_message_id; @@ -11746,24 +11878,23 @@ void MessagesManager::set_dialog_pinned_message_notification(Dialog *d, MessageI VLOG(notifications) << "Change pinned message notification in " << d->dialog_id << " from " << old_message_id << " to " << message_id; if (old_message_id.is_valid()) { - auto m = get_message_force(d, old_message_id, "set_dialog_pinned_message_notification"); + auto m = get_message_force(d, old_message_id, source); if (m != nullptr && m->notification_id.is_valid() && is_message_notification_active(d, m)) { // Can't remove pinned_message_notification_message_id before the call, // because the notification needs to be still active inside remove_message_notification_id remove_message_notification_id(d, m, true, false, true); - on_message_changed(d, m, false, "set_dialog_pinned_message_notification"); + on_message_changed(d, m, false, source); } else { send_closure_later(G()->notification_manager(), &NotificationManager::remove_temporary_notification_by_message_id, - d->mention_notification_group.group_id, old_message_id, false, - "set_dialog_pinned_message_notification 2"); + d->mention_notification_group.group_id, old_message_id, false, source); } } d->pinned_message_notification_message_id = message_id; - on_dialog_updated(d->dialog_id, "set_dialog_pinned_message_notification"); + on_dialog_updated(d->dialog_id, source); } -void MessagesManager::remove_dialog_pinned_message_notification(Dialog *d) { - set_dialog_pinned_message_notification(d, MessageId()); +void MessagesManager::remove_dialog_pinned_message_notification(Dialog *d, const char *source) { + set_dialog_pinned_message_notification(d, MessageId(), source); } void MessagesManager::remove_dialog_mention_notifications(Dialog *d) { @@ -11860,9 +11991,9 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, const FormattedText *old_message_text = get_message_content_text(m->content.get()); CHECK(old_message_text != nullptr); - FormattedText new_message_text = - get_message_text(td_->contacts_manager_.get(), old_message_text->text, std::move(entities), true, - m->forward_info ? m->forward_info->date : m->date, "on_update_sent_text_message"); + FormattedText new_message_text = get_message_text( + td_->contacts_manager_.get(), old_message_text->text, std::move(entities), true, + m->forward_info ? m->forward_info->date : m->date, m->media_album_id != 0, "on_update_sent_text_message"); auto new_content = get_message_content(td_, std::move(new_message_text), std::move(message_media), dialog_id, true /*likely ignored*/, UserId() /*likely ignored*/, nullptr /*ignored*/); if (new_content->get_type() != MessageContentType::Text) { @@ -11876,7 +12007,8 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, is_content_changed, need_update); if (is_content_changed || need_update) { - reregister_message_content(td_, m->content.get(), new_content.get(), full_message_id); + reregister_message_content(td_, m->content.get(), new_content.get(), full_message_id, + "on_update_sent_text_message"); m->content = std::move(new_content); m->is_content_secret = is_secret_message_content(m->ttl, MessageContentType::Text); } @@ -11894,9 +12026,10 @@ void MessagesManager::delete_pending_message_web_page(FullMessageId full_message CHECK(m != nullptr); MessageContent *content = m->content.get(); - unregister_message_content(td_, content, full_message_id); + CHECK(has_message_content_web_page(content)); + unregister_message_content(td_, content, full_message_id, "delete_pending_message_web_page"); remove_message_content_web_page(content); - register_message_content(td_, content, full_message_id); + register_message_content(td_, content, full_message_id, "delete_pending_message_web_page"); // don't need to send an updateMessageContent, because the web page was pending @@ -12147,7 +12280,7 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vectorflags_ & DIALOG_FLAG_IS_PINNED) != 0; + bool is_pinned = !is_removed_from_dialog_list(d) && (dialog->flags_ & DIALOG_FLAG_IS_PINNED) != 0; bool was_pinned = d->pinned_order != DEFAULT_ORDER; if (is_pinned != was_pinned) { set_dialog_is_pinned(d, is_pinned); @@ -12159,18 +12292,44 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vectorparameters().use_message_db || is_new || !d->is_last_read_inbox_message_id_inited || d->need_repair_server_unread_count) { - if (d->need_repair_server_unread_count) { + if (d->last_read_inbox_message_id.is_valid() && !d->last_read_inbox_message_id.is_server() && + read_inbox_max_message_id == d->last_read_inbox_message_id.get_prev_server_message_id()) { + read_inbox_max_message_id = d->last_read_inbox_message_id; + } + if (d->need_repair_server_unread_count && + (d->last_read_inbox_message_id <= read_inbox_max_message_id || !need_unread_counter(d->order) || + !have_input_peer(dialog_id, AccessRights::Read))) { LOG(INFO) << "Repaired server unread count in " << dialog_id << " from " << d->last_read_inbox_message_id << "/" << d->server_unread_count << " to " << read_inbox_max_message_id << "/" << dialog->unread_count_; d->need_repair_server_unread_count = false; - on_dialog_updated(dialog_id, "repair dialog server unread count"); + on_dialog_updated(dialog_id, "repaired dialog server unread count"); } - if (d->server_unread_count != dialog->unread_count_ || + if (d->need_repair_server_unread_count) { + auto &previous_message_id = previous_repaired_read_inbox_max_message_id_[dialog_id]; + if (previous_message_id >= read_inbox_max_message_id) { + // protect from sending the request in a loop + LOG(ERROR) << "Failed to repair server unread count in " << dialog_id + << ", because receive read_inbox_max_message_id = " << read_inbox_max_message_id << " after " + << previous_message_id << ", but messages are read up to " << d->last_read_inbox_message_id; + d->need_repair_server_unread_count = false; + on_dialog_updated(dialog_id, "failed to repair dialog server unread count"); + } else { + LOG(INFO) << "Have last_read_inbox_message_id = " << d->last_read_inbox_message_id << ", but received only " + << read_inbox_max_message_id << " from the server, trying to repair server unread count again"; + previous_message_id = read_inbox_max_message_id; + repair_server_unread_count(dialog_id, d->server_unread_count); + } + } + if (!d->need_repair_server_unread_count) { + previous_repaired_read_inbox_max_message_id_.erase(dialog_id); + } + if ((d->server_unread_count != dialog->unread_count_ && + d->last_read_inbox_message_id == read_inbox_max_message_id) || d->last_read_inbox_message_id < read_inbox_max_message_id) { set_dialog_last_read_inbox_message_id(d, read_inbox_max_message_id, dialog->unread_count_, d->local_unread_count, true, "on_get_dialogs"); @@ -12409,7 +12568,8 @@ void MessagesManager::remove_message_notification_id(Dialog *d, Message *m, bool m->notification_id = NotificationId(); if (d->pinned_message_notification_message_id == m->message_id && is_permanent && !ignore_pinned_message_notification_removal) { - remove_dialog_pinned_message_notification(d); // must be called after notification_id is removed + remove_dialog_pinned_message_notification( + d, "remove_message_notification_id"); // must be called after notification_id is removed } if (group_info.last_notification_id == notification_id) { // last notification is deleted, need to find new last notification @@ -12636,7 +12796,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * set_dialog_last_database_message_id(d, (*it)->message_id, "do_delete_message"); if (d->last_database_message_id < d->first_database_message_id) { LOG(ERROR) << "Last database " << d->last_database_message_id << " became less than first database " - << d->first_database_message_id << " in " << d->dialog_id; + << d->first_database_message_id << " after deletion of " << full_message_id; set_dialog_first_database_message_id(d, d->last_database_message_id, "do_delete_message 2"); } } else { @@ -12723,7 +12883,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * int32 local_unread_count = d->local_unread_count; int32 &unread_count = message_id.is_server() ? server_unread_count : local_unread_count; if (unread_count == 0) { - if (d->order > 0) { + if (need_unread_counter(d->order)) { LOG(ERROR) << "Unread count became negative in " << d->dialog_id << " after deletion of " << message_id << ". Last read is " << d->last_read_inbox_message_id; dump_debug_message_op(d, 3); @@ -12779,7 +12939,7 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanen UNREACHABLE(); } ttl_unregister_message(d->dialog_id, m, Time::now(), source); - unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}); + unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_deleted"); if (m->notification_id.is_valid()) { delete_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id); } @@ -12825,7 +12985,7 @@ unique_ptr MessagesManager::do_delete_scheduled_messag cancel_send_deleted_message(d->dialog_id, result.get(), is_permanently_deleted); - unregister_message_content(td_, result->content.get(), {d->dialog_id, message_id}); + unregister_message_content(td_, result->content.get(), {d->dialog_id, message_id}, "do_delete_scheduled_message"); return result; } @@ -12961,6 +13121,19 @@ vector MessagesManager::get_dialogs(FolderId folder_id, DialogDate off limit = MAX_GET_DIALOGS; } + if (folder_id == FolderId::main() && sponsored_dialog_id_.is_valid()) { + auto d = get_dialog(sponsored_dialog_id_); + CHECK(d != nullptr); + if (is_dialog_sponsored(d)) { + DialogDate date(get_dialog_public_order(d), d->dialog_id); + if (offset < date) { + result.push_back(sponsored_dialog_id_); + offset = date; + limit--; + } + } + } + auto it = list.ordered_dialogs_.upper_bound(offset); auto end = list.ordered_dialogs_.end(); while (it != end && limit-- > 0) { @@ -13067,7 +13240,7 @@ void MessagesManager::on_get_dialogs_from_database(FolderId folder_id, int32 lim continue; } - LOG(INFO) << "Chat " << d->dialog_id << " with order " << d->order << " is loaded from database"; + LOG(INFO) << "Loaded from database " << d->dialog_id << " with order " << d->order; } DialogDate max_dialog_date(dialogs.next_order, dialogs.next_dialog_id); @@ -13145,9 +13318,7 @@ vector MessagesManager::get_pinned_dialogs(FolderId folder_id) const { if (dialog_date.get_date() < MIN_PINNED_DIALOG_DATE) { break; } - if (dialog_date.get_order() != SPONSORED_DIALOG_ORDER) { - result.push_back(dialog_date.get_dialog_id()); - } + result.push_back(dialog_date.get_dialog_id()); } } @@ -13250,8 +13421,9 @@ vector MessagesManager::sort_dialogs_by_order(const vector & auto dialog_dates = transform(dialog_ids, [this, &fake_order](DialogId dialog_id) { const Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - if (is_dialog_inited(d) || d->order != DEFAULT_ORDER) { - return DialogDate(d->order, dialog_id); + auto order = get_dialog_public_order(d); + if (is_dialog_inited(d) || order != DEFAULT_ORDER) { + return DialogDate(order, dialog_id); } // if the dialog is not inited yet, we need to assume that server knows better and the dialog needs to be returned return DialogDate(fake_order--, dialog_id); @@ -13521,7 +13693,14 @@ void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message // so we try to force channel difference first // replied message can't be older than already added original message, but pinned message can be - CHECK(input_message == nullptr || input_message->get_id() == telegram_api::inputMessagePinned::ID); + LOG_CHECK(input_message == nullptr || input_message->get_id() == telegram_api::inputMessagePinned::ID) + << to_string(input_message) << " " << d->dialog_id << " " << message_id << " " << d->last_new_message_id + << " " << d->last_message_id << " " << d->first_database_message_id << " " << d->last_database_message_id + << " " << d->pinned_message_id << " " << d->last_read_all_mentions_message_id << " " + << d->max_unavailable_message_id << " " << d->last_clear_history_message_id << " " << d->order << " " + << d->deleted_last_message_id << " " << d->max_added_message_id << " " << d->pts << " " + << d->last_assigned_message_id << " " << d->debug_last_new_message_id << " " + << d->debug_first_database_message_id << " " << d->debug_last_database_message_id; postponed_get_message_requests_[d->dialog_id].emplace_back(message_id, std::move(promise), std::move(input_message)); get_channel_difference(d->dialog_id, d->pts, true, "get_message"); @@ -13585,13 +13764,13 @@ MessageId MessagesManager::get_replied_message(DialogId dialog_id, MessageId mes void MessagesManager::get_dialog_info_full(DialogId dialog_id, Promise &&promise) { switch (dialog_id.get_type()) { case DialogType::User: - td_->contacts_manager_->get_user_full(dialog_id.get_user_id(), std::move(promise)); + td_->contacts_manager_->get_user_full(dialog_id.get_user_id(), false, std::move(promise)); return; case DialogType::Chat: - td_->contacts_manager_->get_chat_full(dialog_id.get_chat_id(), std::move(promise)); + td_->contacts_manager_->get_chat_full(dialog_id.get_chat_id(), false, std::move(promise)); return; case DialogType::Channel: - td_->contacts_manager_->get_channel_full(dialog_id.get_channel_id(), std::move(promise)); + td_->contacts_manager_->get_channel_full(dialog_id.get_channel_id(), false, std::move(promise)); return; case DialogType::SecretChat: return promise.set_value(Unit()); @@ -14262,6 +14441,9 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn if (!have_input_peer(dialog_id, AccessRights::Read)) { return Status::Error(6, "Can't access the chat"); } + if ((is_removed_from_dialog_list(d) || d->order == DEFAULT_ORDER) && is_pinned) { + return Status::Error(6, "The chat can't be pinned"); + } bool was_pinned = d->pinned_order != DEFAULT_ORDER; if (is_pinned == was_pinned) { @@ -14284,7 +14466,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn } set_dialog_is_pinned(d, is_pinned); - update_dialog_pos(d, false, "toggle_dialog_is_pinned"); + update_dialog_pos(d, "toggle_dialog_is_pinned"); toggle_dialog_is_pinned_on_server(dialog_id, is_pinned, 0); return Status::OK(); @@ -14578,14 +14760,14 @@ void MessagesManager::update_dialog_notification_settings_on_server(DialogId dia }); } - send_update_dialog_notification_settings_query(dialog_id, std::move(promise)); + send_update_dialog_notification_settings_query(d, std::move(promise)); } -void MessagesManager::send_update_dialog_notification_settings_query(DialogId dialog_id, Promise &&promise) { - auto d = get_dialog(dialog_id); +void MessagesManager::send_update_dialog_notification_settings_query(const Dialog *d, Promise &&promise) { CHECK(d != nullptr); // TODO do not send two queries simultaneously or use SequenceDispatcher - td_->create_handler(std::move(promise))->send(dialog_id, d->notification_settings); + td_->create_handler(std::move(promise)) + ->send(d->dialog_id, d->notification_settings); } void MessagesManager::on_updated_dialog_notification_settings(DialogId dialog_id, uint64 generation) { @@ -14801,7 +14983,7 @@ DialogId MessagesManager::migrate_dialog_to_megagroup(DialogId dialog_id, Promis d->debug_message_op.emplace_back(Dialog::MessageOp::SetPts, d->pts, "migrate"); } } - update_dialog_pos(d, false, "migrate_dialog_to_megagroup"); + update_dialog_pos(d, "migrate_dialog_to_megagroup"); } promise.set_value(Unit()); @@ -14833,6 +15015,8 @@ DialogId MessagesManager::get_my_dialog_id() const { } Status MessagesManager::view_messages(DialogId dialog_id, const vector &message_ids, bool force_read) { + CHECK(!td_->auth_manager_->is_bot()); + Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return Status::Error(3, "Chat not found"); @@ -14892,18 +15076,28 @@ Status MessagesManager::view_messages(DialogId dialog_id, const vector d->last_read_inbox_message_id) { MessageId last_read_message_id = max_message_id; MessageId prev_last_read_inbox_message_id = d->last_read_inbox_message_id; - read_history_inbox(d->dialog_id, last_read_message_id, -1, "view_messages"); - + MessageId read_history_on_server_message_id; if (dialog_id.get_type() != DialogType::SecretChat) { if (last_read_message_id.get_prev_server_message_id().get() > prev_last_read_inbox_message_id.get_prev_server_message_id().get()) { - read_history_on_server(d, last_read_message_id.get_prev_server_message_id()); + read_history_on_server_message_id = last_read_message_id.get_prev_server_message_id(); } } else { if (last_read_message_id > prev_last_read_inbox_message_id) { - read_history_on_server(d, last_read_message_id); + read_history_on_server_message_id = last_read_message_id; } } + + if (read_history_on_server_message_id.is_valid()) { + // add dummy timeout to not try to repair unread_count in read_history_inbox before server request succeeds + // the timeout will be overwritten in the read_history_on_server call + pending_read_history_timeout_.add_timeout_in(dialog_id.get(), 0); + } + read_history_inbox(d->dialog_id, last_read_message_id, -1, "view_messages"); + if (read_history_on_server_message_id.is_valid()) { + // call read_history_on_server after read_history_inbox to not have delay before request if all messages are read + read_history_on_server(d, read_history_on_server_message_id); + } } if (need_read && d->is_marked_as_unread) { set_dialog_is_marked_as_unread(d, false); @@ -15037,7 +15231,7 @@ void MessagesManager::open_dialog(Dialog *d) { break; case DialogType::Chat: td_->contacts_manager_->repair_chat_participants(dialog_id.get_chat_id()); - repair_dialog_action_bar(dialog_id, "open_dialog"); + reget_dialog_action_bar(dialog_id, "open_dialog"); break; case DialogType::Channel: if (!is_broadcast_channel(dialog_id)) { @@ -15050,7 +15244,7 @@ void MessagesManager::open_dialog(Dialog *d) { } } get_channel_difference(dialog_id, d->pts, true, "open_dialog"); - repair_dialog_action_bar(dialog_id, "open_dialog"); + reget_dialog_action_bar(dialog_id, "open_dialog"); break; case DialogType::SecretChat: { // to repair dialog action bar @@ -15188,7 +15382,10 @@ td_api::object_ptr MessagesManager::get_chat_type_object(Dialo } } -td_api::object_ptr MessagesManager::get_chat_list_object(const Dialog *d) { +td_api::object_ptr MessagesManager::get_chat_list_object(const Dialog *d) const { + if (is_dialog_sponsored(d)) { + return get_chat_list_object(FolderId::main()); + } if (d->order == DEFAULT_ORDER) { return nullptr; } @@ -15299,20 +15496,21 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * } } - bool has_scheduled_messages = - d->has_scheduled_server_messages || d->has_scheduled_database_messages || d->scheduled_messages != nullptr; + // TODO hide/show draft message when can_send_message(dialog_id) changes + auto draft_message = can_send_message(d->dialog_id).is_ok() ? get_draft_message_object(d->draft_message) : nullptr; + return make_tl_object( d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_chat_list_object(d), get_dialog_title(d->dialog_id), get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)), get_dialog_permissions(d->dialog_id).get_chat_permissions_object(), get_message_object(d->dialog_id, get_message(d, d->last_message_id)), get_dialog_public_order(d), - d->pinned_order != DEFAULT_ORDER, d->is_marked_as_unread, d->order == SPONSORED_DIALOG_ORDER, - has_scheduled_messages, can_delete_for_self, can_delete_for_all_users, can_report_dialog(d->dialog_id), - d->notification_settings.silent_send_message, d->server_unread_count + d->local_unread_count, - d->last_read_inbox_message_id.get(), d->last_read_outbox_message_id.get(), d->unread_mention_count, + d->pinned_order != DEFAULT_ORDER, d->is_marked_as_unread, is_dialog_sponsored(d), + get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users, + can_report_dialog(d->dialog_id), d->notification_settings.silent_send_message, + d->server_unread_count + d->local_unread_count, d->last_read_inbox_message_id.get(), + d->last_read_outbox_message_id.get(), d->unread_mention_count, get_chat_notification_settings_object(&d->notification_settings), get_chat_action_bar_object(d), - d->pinned_message_id.get(), d->reply_markup_message_id.get(), get_draft_message_object(d->draft_message), - d->client_data); + d->pinned_message_id.get(), d->reply_markup_message_id.get(), std::move(draft_message), d->client_data); } tl_object_ptr MessagesManager::get_chat_object(DialogId dialog_id) const { @@ -15886,6 +16084,9 @@ void MessagesManager::read_history_on_server_impl(DialogId dialog_id, MessageId } }); } + if (d->need_repair_server_unread_count && need_unread_counter(d->order)) { + repair_server_unread_count(dialog_id, d->server_unread_count); + } if (!max_message_id.is_valid()) { return promise.set_value(Unit()); @@ -17074,6 +17275,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId bool need_update = false; bool need_update_dialog_pos = false; bool added_new_message = false; + MessageId first_added_message_id; MessageId last_added_message_id; Message *next_message = nullptr; Dependencies dependencies; @@ -17082,7 +17284,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId auto debug_first_database_message_id = d->first_database_message_id; auto debug_last_message_id = d->last_message_id; auto debug_last_new_message_id = d->last_new_message_id; - auto debug_cur_message_id = MessageId::max(); + auto last_received_message_id = MessageId::max(); size_t pos = 0; for (auto &message_slice : messages) { if (!d->first_database_message_id.is_valid() && !d->have_full_history) { @@ -17096,14 +17298,14 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } break; } - if (message->message_id >= debug_cur_message_id) { + if (message->message_id >= last_received_message_id) { // TODO move to ERROR - LOG(FATAL) << "Receive message " << message->message_id << " after " << debug_cur_message_id + LOG(FATAL) << "Receive " << message->message_id << " after " << last_received_message_id << " from database in the history of " << dialog_id << " from " << from_message_id << " with offset " << offset << ", limit " << limit << ", from_the_end = " << from_the_end; break; } - debug_cur_message_id = message->message_id; + last_received_message_id = message->message_id; if (message->message_id < d->first_database_message_id) { if (d->have_full_history) { @@ -17128,6 +17330,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId : add_message_to_dialog(d, std::move(message), false, &need_update, &need_update_dialog_pos, "on_get_history_from_database"); if (m != nullptr) { + first_added_message_id = m->message_id; if (!have_next) { last_added_message_id = m->message_id; } @@ -17139,7 +17342,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } if (next_message != nullptr && !next_message->have_previous) { LOG_CHECK(m->message_id < next_message->message_id) - << m->message_id << ' ' << next_message->message_id << ' ' << debug_cur_message_id << ' ' << dialog_id + << m->message_id << ' ' << next_message->message_id << ' ' << last_received_message_id << ' ' << dialog_id << ' ' << from_message_id << ' ' << offset << ' ' << limit << ' ' << from_the_end << ' ' << only_local << ' ' << messages.size() << ' ' << debug_first_database_message_id << ' ' << last_added_message_id << ' ' << added_new_message << ' ' << pos << ' ' << m << ' ' << next_message << ' ' << old_message << ' ' @@ -17162,6 +17365,13 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } resolve_dependencies_force(td_, dependencies); + if (from_the_end && !last_added_message_id.is_valid() && last_received_message_id < d->first_database_message_id && + !d->have_full_history) { + // failed to load from database a message from first_database_message_id to last_database_message_id; drop them + set_dialog_first_database_message_id(d, MessageId(), "on_get_history_from_database 8"); + set_dialog_last_database_message_id(d, MessageId(), "on_get_history_from_database 9"); + } + if (!added_new_message && !only_local && dialog_id.get_type() != DialogType::SecretChat) { if (from_the_end) { from_message_id = MessageId(); @@ -17202,6 +17412,15 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } } } + if (first_added_message_id.is_valid() && first_added_message_id != d->first_database_message_id && + last_received_message_id < d->first_database_message_id && d->last_new_message_id.is_valid() && + !d->have_full_history) { + CHECK(first_added_message_id > d->first_database_message_id); + set_dialog_first_database_message_id(d, first_added_message_id, "on_get_history_from_database 10"); + if (d->last_database_message_id < d->first_database_message_id) { + set_dialog_last_database_message_id(d, d->first_database_message_id, "on_get_history_from_database 11"); + } + } if (need_update_dialog_pos) { send_update_chat_last_message(d, "on_get_history_from_database 7"); @@ -17220,8 +17439,12 @@ void MessagesManager::get_history_from_the_end(DialogId dialog_id, bool from_dat if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } - const int32 limit = MAX_GET_HISTORY; + int32 limit = MAX_GET_HISTORY; if (from_database && G()->parameters().use_message_db) { + if (!promise) { + // repair last database message ID + limit = 10; + } LOG(INFO) << "Get history from the end of " << dialog_id << " from database"; MessagesDbMessagesQuery db_query; db_query.dialog_id = dialog_id; @@ -17318,13 +17541,15 @@ void MessagesManager::load_messages(DialogId dialog_id, MessageId from_message_i get_history(dialog_id, from_message_id, offset, limit, from_database, only_local, std::move(promise)); } -vector MessagesManager::get_dialog_scheduled_messages(DialogId dialog_id, Promise &&promise) { +vector MessagesManager::get_dialog_scheduled_messages(DialogId dialog_id, bool force, bool ignore_result, + Promise &&promise) { + LOG(INFO) << "Get scheduled messages in " << dialog_id; if (G()->close_flag()) { promise.set_error(Status::Error(500, "Request aborted")); return {}; } - const Dialog *d = get_dialog_force(dialog_id); + Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { promise.set_error(Status::Error(6, "Chat not found")); return {}; @@ -17377,14 +17602,18 @@ vector MessagesManager::get_dialog_scheduled_messages(DialogId dialog } auto hash = get_vector_hash(numbers); - if (d->has_scheduled_server_messages || - (d->scheduled_messages_sync_generation == 0 && !G()->parameters().use_message_db)) { + if (!force && (d->has_scheduled_server_messages || + (d->scheduled_messages_sync_generation == 0 && !G()->parameters().use_message_db))) { load_dialog_scheduled_messages(dialog_id, false, hash, std::move(promise)); return {}; } load_dialog_scheduled_messages(dialog_id, false, hash, Promise()); } + if (!ignore_result) { + d->sent_scheduled_messages = true; + } + promise.set_value(Unit()); return message_ids; } @@ -17439,10 +17668,10 @@ void MessagesManager::on_get_scheduled_messages_from_database(DialogId dialog_id } resolve_dependencies_force(td_, dependencies); - for (auto message_id : added_message_ids) { - send_update_new_message(d, get_message(d, message_id)); - } - send_update_chat_has_scheduled_messages(d); + // for (auto message_id : added_message_ids) { + // send_update_new_message(d, get_message(d, message_id)); + // } + send_update_chat_has_scheduled_messages(d, false); auto it = load_scheduled_messages_from_database_queries_.find(dialog_id); CHECK(it != load_scheduled_messages_from_database_queries_.end()); @@ -17522,20 +17751,13 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial CHECK(sending_state == nullptr); } - bool can_delete = true; - auto dialog_type = dialog_id.get_type(); - auto is_bot = td_->auth_manager_->is_bot(); - if (dialog_type == DialogType::Channel) { - auto dialog_status = td_->contacts_manager_->get_channel_permissions(dialog_id.get_channel_id()); - can_delete = can_delete_channel_message(dialog_status, m, is_bot); - } - + bool can_delete = can_delete_message(dialog_id, m); bool is_scheduled = m->message_id.is_scheduled(); DialogId my_dialog_id = get_my_dialog_id(); bool can_delete_for_self = false; bool can_delete_for_all_users = can_delete && can_revoke_message(dialog_id, m); if (can_delete) { - switch (dialog_type) { + switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: // TODO allow to delete yet unsent message just for self @@ -17580,7 +17802,7 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial ttl = 0; } auto scheduling_state = is_scheduled ? get_message_scheduling_state_object(m->date) : nullptr; - bool can_be_edited = for_event_log ? false : can_edit_message(dialog_id, m, false, is_bot); + bool can_be_edited = for_event_log ? false : can_edit_message(dialog_id, m, false, td_->auth_manager_->is_bot()); bool can_be_forwarded = for_event_log ? false : can_forward_message(dialog_id, m); auto media_album_id = for_event_log ? static_cast(0) : m->media_album_id; auto reply_to_message_id = for_event_log ? static_cast(0) : m->reply_to_message_id.get(); @@ -17657,12 +17879,13 @@ MessagesManager::Message *MessagesManager::get_message_to_send( m->is_channel_post = is_channel_post; m->is_outgoing = is_scheduled || dialog_id != DialogId(my_id); m->from_background = options.from_background; - m->views = is_channel_post ? 1 : 0; + m->views = is_channel_post && !is_scheduled ? 1 : 0; m->content = std::move(content); m->forward_info = std::move(forward_info); m->is_copy = is_copy || forward_info != nullptr; - if (td_->auth_manager_->is_bot() || options.disable_notification) { + if (td_->auth_manager_->is_bot() || options.disable_notification || + G()->shared_config().get_option_boolean("ignore_default_disable_notification")) { m->disable_notification = options.disable_notification; } else { auto notification_settings = get_dialog_notification_settings(dialog_id, true); @@ -17698,8 +17921,8 @@ MessagesManager::Message *MessagesManager::get_message_to_send( bool need_update = false; CHECK(have_input_peer(dialog_id, AccessRights::Read)); auto result = add_message_to_dialog(d, std::move(m), true, &need_update, need_update_dialog_pos, "send message"); - CHECK(result != nullptr); - send_update_chat_has_scheduled_messages(d); + LOG_CHECK(result != nullptr) << message_id << " " << debug_add_message_to_dialog_fail_reason_; + send_update_chat_has_scheduled_messages(d, false); return result; } @@ -17786,6 +18009,9 @@ Status MessagesManager::can_send_message_content(DialogId dialog_id, const Messa if (content_type == MessageContentType::Poll) { return Status::Error(400, "Polls can't be sent to secret chats"); } + if (content_type == MessageContentType::Dice) { + return Status::Error(400, "Dice can't be sent to secret chats"); + } break; case DialogType::None: default: @@ -17808,6 +18034,11 @@ Status MessagesManager::can_send_message_content(DialogId dialog_id, const Messa return Status::Error(400, "Not enough rights to send contacts to the chat"); } break; + case MessageContentType::Dice: + if (!can_send_stickers) { + return Status::Error(400, "Not enough rights to send dice to the chat"); + } + break; case MessageContentType::Document: if (!can_send_media) { return Status::Error(400, "Not enough rights to send documents to the chat"); @@ -18038,7 +18269,8 @@ void MessagesManager::cancel_send_message_query(DialogId dialog_id, Message *m) if (queue.empty()) { yet_unsent_media_queues_.erase(queue_it); } else { - on_yet_unsent_media_queue_updated(dialog_id); + // send later, because do_delete_all_dialog_messages can be called right now + send_closure_later(actor_id(this), &MessagesManager::on_yet_unsent_media_queue_updated, dialog_id); } } } @@ -18242,7 +18474,8 @@ Result MessagesManager::process_input_message_content( return Status::Error(400, "Can't copy message content"); } - return InputMessageContent(std::move(content), copied_message->disable_web_page_preview, false, 0, UserId()); + return InputMessageContent(std::move(content), get_message_disable_web_page_preview(copied_message), false, 0, + UserId()); } TRY_RESULT(content, get_input_message_content(dialog_id, std::move(input_message_content), td_)); @@ -18601,10 +18834,15 @@ void MessagesManager::on_secret_message_media_uploaded(DialogId dialog_id, const LOG(INFO) << "Send secret media from " << m->message_id << " in " << dialog_id << " in reply to " << m->reply_to_message_id; int64 random_id = begin_send_message(dialog_id, m); + auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id()); + auto caption = get_message_content_caption(m->content.get()); + vector> entities; + if (caption != nullptr && !caption->entities.empty()) { + entities = get_input_secret_message_entities(caption->entities, layer); + } send_closure(td_->create_net_actor(), &SendSecretMessageActor::send, dialog_id, - m->reply_to_random_id, m->ttl, "", std::move(secret_input_media), - vector>(), m->via_bot_user_id, m->media_album_id, - random_id); + m->reply_to_random_id, m->ttl, "", std::move(secret_input_media), std::move(entities), + m->via_bot_user_id, m->media_album_id, random_id); })); } @@ -19317,6 +19555,7 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo return !get_message_content_poll_is_closed(td_, m->content.get()); } case MessageContentType::Contact: + case MessageContentType::Dice: case MessageContentType::Location: case MessageContentType::Sticker: case MessageContentType::Venue: @@ -20006,6 +20245,17 @@ void MessagesManager::edit_message_scheduling_state( } } +bool MessagesManager::get_message_disable_web_page_preview(const Message *m) { + // m->disable_web_page_preview is known only for sent from this client messages + if (m->disable_web_page_preview) { + return true; + } + if (m->content->get_type() != MessageContentType::Text) { + return false; + } + return !has_message_content_web_page(m->content.get()); +} + int32 MessagesManager::get_message_flags(const Message *m) { int32 flags = 0; if (m->reply_to_message_id.is_valid()) { @@ -20618,7 +20868,7 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i } if (need_copy) { - copied_messages.push_back({std::move(content), forwarded_message->disable_web_page_preview, i}); + copied_messages.push_back({std::move(content), get_message_disable_web_page_preview(forwarded_message), i}); continue; } @@ -20638,7 +20888,7 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i forward_info->from_dialog_id = saved_from_dialog_id; forward_info->from_message_id = saved_from_message_id; } else { - if (from_dialog_id != DialogId(my_id)) { + if (from_dialog_id != DialogId(my_id) || content_type == MessageContentType::Dice) { if (forwarded_message->is_channel_post) { if (is_broadcast_channel(from_dialog_id)) { auto author_signature = forwarded_message->sender_user_id.is_valid() @@ -20861,6 +21111,7 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v continue; } + being_readded_message_id_ = {dialog_id, message_ids[i]}; unique_ptr message = delete_message(d, message_ids[i], true, &need_update_dialog_pos, "resend_messages"); CHECK(message != nullptr); send_update_delete_messages(dialog_id, {message->message_id.get()}, true, false); @@ -20883,6 +21134,7 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v send_update_new_message(d, m); result[i] = m->message_id; + being_readded_message_id_ = FullMessageId(); } if (need_update_dialog_pos) { @@ -21031,6 +21283,9 @@ Result MessagesManager::add_local_message( if (message_content.content->get_type() == MessageContentType::Game) { return Status::Error(400, "Can't add local game message"); } + if (message_content.content->get_type() == MessageContentType::Dice) { + return Status::Error(400, "Can't add local dice message"); + } bool is_channel_post = is_broadcast_channel(dialog_id); if (sender_user_id != UserId() && !td_->contacts_manager_->have_user_force(sender_user_id)) { @@ -21177,19 +21432,11 @@ bool MessagesManager::on_update_scheduled_message_id(int64 random_id, ScheduledS } bool MessagesManager::on_get_dialog_error(DialogId dialog_id, const Status &status, const string &source) { - if (status.code() == 401) { - // authorization is lost - return true; - } - if (status.code() == 420 || status.code() == 429) { - // flood wait - return true; - } if (status.message() == CSlice("BOT_METHOD_INVALID")) { LOG(ERROR) << "Receive BOT_METHOD_INVALID from " << source; return true; } - if (G()->close_flag()) { + if (G()->is_expected_error(status)) { return true; } @@ -21220,8 +21467,7 @@ void MessagesManager::on_dialog_updated(DialogId dialog_id, const char *source) void MessagesManager::send_update_new_message(const Dialog *d, const Message *m) { CHECK(d != nullptr); CHECK(m != nullptr); - - LOG(INFO) << "Send updateNewMessage for " << m->message_id << " in " << d->dialog_id; + CHECK(d->is_update_new_chat_sent); send_closure(G()->td(), &Td::send_update, make_tl_object(get_message_object(d->dialog_id, m))); } @@ -21281,6 +21527,11 @@ Result MessagesManager::get_messag return Status::Error(406, "Ignore notification in unknown chat"); } + if (is_from_scheduled && dialog_id != get_my_dialog_id() && + G()->shared_config().get_option_boolean("disable_sent_scheduled_message_notifications")) { + return Status::Error("Ignore notification about sent scheduled message"); + } + bool is_new_pinned = is_pinned && message_id.is_valid() && message_id > d->max_notification_message_id; CHECK(!message_id.is_scheduled()); if (message_id.is_valid()) { @@ -21296,6 +21547,15 @@ Result MessagesManager::get_messag if (message_id <= d->last_read_inbox_message_id) { return Status::Error("Ignore notification about read message"); } + if (message_id <= d->last_clear_history_message_id) { + return Status::Error("Ignore notification about message from cleared chat history"); + } + if (d->deleted_message_ids.count(message_id)) { + return Status::Error("Ignore notification about deleted message"); + } + if (message_id <= d->max_unavailable_message_id) { + return Status::Error("Ignore notification about unavailable message"); + } } if (random_id != 0) { CHECK(dialog_id.get_type() == DialogType::SecretChat); @@ -21322,14 +21582,14 @@ Result MessagesManager::get_messag std::tie(have_settings, mute_until) = get_dialog_mute_until(settings_dialog_id, settings_dialog); if (have_settings && mute_until > date) { if (is_new_pinned) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "get_message_push_notification_info"); } return Status::Error("Ignore notification in muted chat"); } if (is_dialog_message_notification_disabled(settings_dialog_id, date)) { if (is_new_pinned) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "get_message_push_notification_info"); } return Status::Error("Ignore notification in chat, because notifications are disabled in the chat"); } @@ -21342,7 +21602,8 @@ Result MessagesManager::get_messag if (message_id.is_valid() && message_id > d->max_notification_message_id) { if (is_new_pinned) { - set_dialog_pinned_message_notification(d, contains_mention ? message_id : MessageId()); + set_dialog_pinned_message_notification(d, contains_mention ? message_id : MessageId(), + "get_message_push_notification_info"); } d->max_notification_message_id = message_id; on_dialog_updated(dialog_id, "set_max_notification_message_id"); @@ -21525,7 +21786,7 @@ void MessagesManager::try_add_pinned_message_notification(Dialog *d, vectordialog_id, m) || td_->auth_manager_->is_bot()) { return true; } + if (m->is_from_scheduled && d->dialog_id != get_my_dialog_id() && + G()->shared_config().get_option_boolean("disable_sent_scheduled_message_notifications")) { + return true; + } switch (m->content->get_type()) { case MessageContentType::ChatDeleteHistory: @@ -22182,7 +22447,7 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f if (!is_active) { VLOG(notifications) << "Disable inactive notification for " << m->message_id << " in " << d->dialog_id; if (is_pinned) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "add_new_message_notification"); } return false; } @@ -22205,7 +22470,7 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f if (mute_until > m->date && (have_settings || force)) { VLOG(notifications) << "Disable notification, because " << settings_dialog_id << " is muted"; if (is_pinned) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "add_new_message_notification"); } return false; } @@ -22285,7 +22550,8 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f "add_new_message_notification 3"); CHECK(is_changed); if (is_pinned) { - set_dialog_pinned_message_notification(d, from_mentions ? m->message_id : MessageId()); + set_dialog_pinned_message_notification(d, from_mentions ? m->message_id : MessageId(), + "add_new_message_notification"); } if (!m->notification_id.is_valid()) { // protection from accidental notification_id removal in set_dialog_pinned_message_notification @@ -22412,6 +22678,7 @@ void MessagesManager::remove_message_dialog_notifications(Dialog *d, MessageId m void MessagesManager::send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const { CHECK(m != nullptr); + CHECK(d->is_update_new_chat_sent); d->yet_unsent_message_id_to_persistent_message_id.emplace(old_message_id, m->message_id); send_closure( G()->td(), &Td::send_update, @@ -22476,13 +22743,15 @@ void MessagesManager::send_update_chat_draft_message(const Dialog *d) { CHECK(d != nullptr); LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_draft_message"; on_dialog_updated(d->dialog_id, "send_update_chat_draft_message"); - send_closure(G()->td(), &Td::send_update, - make_tl_object( - d->dialog_id.get(), get_draft_message_object(d->draft_message), get_dialog_public_order(d))); + if (d->draft_message == nullptr || can_send_message(d->dialog_id).is_ok()) { + send_closure(G()->td(), &Td::send_update, + make_tl_object( + d->dialog_id.get(), get_draft_message_object(d->draft_message), get_dialog_public_order(d))); + } } void MessagesManager::send_update_chat_last_message(Dialog *d, const char *source) { - update_dialog_pos(d, false, source, false); + update_dialog_pos(d, source, false); send_update_chat_last_message_impl(d, source); } @@ -22528,7 +22797,7 @@ void MessagesManager::send_update_unread_message_count(FolderId folder_id, Dialo postponed_unread_message_count_updates_.erase(folder_id); LOG(INFO) << "Send updateUnreadMessageCount in " << folder_id << " to " << list.unread_message_total_count_ << '/' << unread_unmuted_count << " from " << source << " and " << dialog_id; - send_closure(G()->td(), &Td::send_update, get_update_unread_message_count_object(folder_id, list)); + send_closure(G()->td(), &Td::send_update, get_update_unread_message_count_object(list)); } } @@ -22582,7 +22851,7 @@ void MessagesManager::send_update_unread_chat_count(FolderId folder_id, DialogId postponed_unread_chat_count_updates_.insert(folder_id); } else { postponed_unread_chat_count_updates_.erase(folder_id); - send_closure(G()->td(), &Td::send_update, get_update_unread_chat_count_object(folder_id, list)); + send_closure(G()->td(), &Td::send_update, get_update_unread_chat_count_object(list)); } } @@ -22642,11 +22911,10 @@ void MessagesManager::send_update_chat_is_sponsored(const Dialog *d) const { CHECK(d != nullptr); LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_is_sponsored"; - bool is_sponsored = d->order == SPONSORED_DIALOG_ORDER; LOG(INFO) << "Update chat is sponsored for " << d->dialog_id; - send_closure( - G()->td(), &Td::send_update, - make_tl_object(d->dialog_id.get(), is_sponsored, get_dialog_public_order(d))); + send_closure(G()->td(), &Td::send_update, + make_tl_object(d->dialog_id.get(), is_dialog_sponsored(d), + get_dialog_public_order(d))); } void MessagesManager::send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const { @@ -22665,6 +22933,10 @@ void MessagesManager::send_update_chat_chat_list(const Dialog *d) const { } void MessagesManager::send_update_secret_chats_with_user_action_bar(const Dialog *d) const { + if (td_->auth_manager_->is_bot()) { + return; + } + if (d->dialog_id.get_type() != DialogType::User) { return; } @@ -22682,6 +22954,10 @@ void MessagesManager::send_update_secret_chats_with_user_action_bar(const Dialog } void MessagesManager::send_update_chat_action_bar(const Dialog *d) { + if (td_->auth_manager_->is_bot()) { + return; + } + CHECK(d != nullptr); LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_action_bar"; on_dialog_updated(d->dialog_id, "send_update_chat_action_bar"); @@ -22691,13 +22967,35 @@ void MessagesManager::send_update_chat_action_bar(const Dialog *d) { send_update_secret_chats_with_user_action_bar(d); } -void MessagesManager::send_update_chat_has_scheduled_messages(Dialog *d) { - if (d->scheduled_messages == nullptr && d->has_loaded_scheduled_messages_from_database) { - set_dialog_has_scheduled_database_messages_impl(d, false); +void MessagesManager::send_update_chat_has_scheduled_messages(Dialog *d, bool from_deletion) { + if (td_->auth_manager_->is_bot()) { + return; } - bool has_scheduled_messages = - d->has_scheduled_server_messages || d->has_scheduled_database_messages || d->scheduled_messages != nullptr; + if (d->scheduled_messages == nullptr) { + if (d->has_scheduled_database_messages) { + if (d->has_loaded_scheduled_messages_from_database) { + set_dialog_has_scheduled_database_messages_impl(d, false); + } else { + CHECK(G()->parameters().use_message_db); + repair_dialog_scheduled_messages(d); + } + } + if (d->has_scheduled_server_messages) { + if (from_deletion && d->scheduled_messages_sync_generation > 0) { + set_dialog_has_scheduled_server_messages(d, false); + } else { + d->last_repair_scheduled_messages_generation = 0; + repair_dialog_scheduled_messages(d); + } + } + } + + LOG(INFO) << "In " << d->dialog_id << " have scheduled messages on server = " << d->has_scheduled_server_messages + << ", in database = " << d->has_scheduled_database_messages + << " and in memory = " << (d->scheduled_messages != nullptr) + << "; was loaded from database = " << d->has_loaded_scheduled_messages_from_database; + bool has_scheduled_messages = get_dialog_has_scheduled_messages(d); if (has_scheduled_messages == d->last_sent_has_scheduled_messages) { return; } @@ -22732,14 +23030,14 @@ void MessagesManager::check_send_message_result(int64 random_id, DialogId dialog *sent_messages_random_ids.begin() != random_id || get_message_dialog_id(*sent_messages[0]) != dialog_id) { LOG(ERROR) << "Receive wrong result for sending message with random_id " << random_id << " from " << source << " to " << dialog_id << ": " << oneline(to_string(*updates_ptr)); + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); if (dialog_id.get_type() == DialogType::Channel) { - Dialog *d = get_dialog(dialog_id); - CHECK(d != nullptr); get_channel_difference(dialog_id, d->pts, true, "check_send_message_result"); } else { td_->updates_manager_->schedule_get_difference("check_send_message_result"); } - repair_dialog_scheduled_messages(dialog_id); + repair_dialog_scheduled_messages(d); } } @@ -22811,12 +23109,14 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI CHECK(d != nullptr); bool need_update_dialog_pos = false; + being_readded_message_id_ = {dialog_id, old_message_id}; unique_ptr sent_message = delete_message(d, old_message_id, false, &need_update_dialog_pos, source); if (sent_message == nullptr) { // message has already been deleted by the user or sent to inaccessible channel // don't need to send update to the user, because the message has already been deleted LOG(INFO) << "Delete already deleted sent " << new_message_id << " from server"; delete_message_from_server(dialog_id, new_message_id, true); + being_readded_message_id_ = FullMessageId(); return {}; } @@ -22827,10 +23127,11 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI // imitation of update_message(d, sent_message.get(), std::move(new_message), &need_update_dialog_pos); if (date <= 0) { - LOG(ERROR) << "Receive " << new_message_id << " in " << dialog_id << " with wrong date " << date; + LOG(ERROR) << "Receive " << new_message_id << " in " << dialog_id << " with wrong date " << date << " from " + << source; } else { LOG_CHECK(sent_message->date > 0) << old_message_id << ' ' << sent_message->message_id << ' ' << new_message_id - << ' ' << sent_message->date << ' ' << date; + << ' ' << sent_message->date << ' ' << date << ' ' << source; sent_message->date = date; CHECK(d->last_message_id != old_message_id); } @@ -22865,6 +23166,7 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI send_update_chat_last_message(d, "on_send_message_success"); } try_add_active_live_location(dialog_id, m); + being_readded_message_id_ = FullMessageId(); return {dialog_id, new_message_id}; } @@ -23163,6 +23465,8 @@ void MessagesManager::on_send_message_fail(int64 random_id, Status error) { error_message = "Wrong invoice information specified"; } else if (content_type == MessageContentType::Poll) { error_message = "Wrong poll data specified"; + } else if (content_type == MessageContentType::Contact) { + error_message = "Wrong phone number specified"; } else { error_message = "Wrong file identifier/HTTP URL specified"; } @@ -23229,18 +23533,23 @@ MessageId MessagesManager::get_next_local_message_id(Dialog *d) { return get_next_message_id(d, MessageType::Local); } -MessageId MessagesManager::get_next_yet_unsent_scheduled_message_id(const Dialog *d, int32 date) { +MessageId MessagesManager::get_next_yet_unsent_scheduled_message_id(Dialog *d, int32 date) { CHECK(date > 0); + + MessageId message_id(ScheduledServerMessageId(1), date); + auto it = MessagesConstIterator(d, MessageId(ScheduledServerMessageId(), date + 1, true)); - int32 prev_date = 0; - if (*it != nullptr) { - prev_date = (*it)->message_id.get_scheduled_message_date(); + if (*it != nullptr && (*it)->message_id > message_id) { + message_id = (*it)->message_id; } - if (prev_date < date) { - return MessageId(ScheduledServerMessageId(1), date).get_next_message_id(MessageType::YetUnsent); + + auto &last_assigned_message_id = d->last_assigned_scheduled_message_id[date]; + if (last_assigned_message_id != MessageId() && last_assigned_message_id > message_id) { + message_id = last_assigned_message_id; } - CHECK(*it != nullptr); - return (*it)->message_id.get_next_message_id(MessageType::YetUnsent); + + last_assigned_message_id = message_id.get_next_message_id(MessageType::YetUnsent); + return last_assigned_message_id; } void MessagesManager::fail_send_message(FullMessageId full_message_id, int error_code, const string &error_message) { @@ -23252,11 +23561,13 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error CHECK(old_message_id.is_yet_unsent()); bool need_update_dialog_pos = false; + being_readded_message_id_ = full_message_id; unique_ptr message = delete_message(d, old_message_id, false, &need_update_dialog_pos, "fail send message"); if (message == nullptr) { // message has already been deleted by the user or sent to inaccessible channel // don't need to send update to the user, because the message has already been deleted // and there is nothing to be deleted from the server + being_readded_message_id_ = FullMessageId(); return; } @@ -23318,6 +23629,7 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error if (need_update_dialog_pos) { send_update_chat_last_message(d, "fail_send_message"); } + being_readded_message_id_ = FullMessageId(); } void MessagesManager::fail_send_message(FullMessageId full_message_id, Status error) { @@ -23372,7 +23684,7 @@ bool MessagesManager::update_dialog_draft_message(Dialog *d, unique_ptrdraft_message != nullptr) { d->draft_message = nullptr; if (need_update_dialog_pos) { - update_dialog_pos(d, false, "update_dialog_draft_message", false); + update_dialog_pos(d, "update_dialog_draft_message", false); } send_update_chat_draft_message(d); return true; @@ -23381,17 +23693,18 @@ bool MessagesManager::update_dialog_draft_message(Dialog *d, unique_ptrdraft_message != nullptr && d->draft_message->reply_to_message_id == draft_message->reply_to_message_id && d->draft_message->input_message_text == draft_message->input_message_text) { if (d->draft_message->date < draft_message->date) { - if (need_update_dialog_pos) { - update_dialog_pos(d, false, "update_dialog_draft_message 2"); - } d->draft_message->date = draft_message->date; + if (need_update_dialog_pos) { + update_dialog_pos(d, "update_dialog_draft_message 2", false); + } + send_update_chat_draft_message(d); return true; } } else { if (!from_update || d->draft_message == nullptr || d->draft_message->date <= draft_message->date) { d->draft_message = std::move(draft_message); if (need_update_dialog_pos) { - update_dialog_pos(d, false, "update_dialog_draft_message 3", false); + update_dialog_pos(d, "update_dialog_draft_message 3", false); } send_update_chat_draft_message(d); return true; @@ -23413,13 +23726,16 @@ void MessagesManager::on_update_dialog_is_pinned(FolderId folder_id, DialogId di on_update_pinned_dialogs(folder_id); return; } + if (is_removed_from_dialog_list(d) && is_pinned) { + return; + } set_dialog_folder_id(d, folder_id); if (!is_pinned && d->pinned_order == DEFAULT_ORDER) { return; } set_dialog_is_pinned(d, is_pinned); - update_dialog_pos(d, false, "on_update_dialog_is_pinned"); + update_dialog_pos(d, "on_update_dialog_is_pinned"); } void MessagesManager::on_update_pinned_dialogs(FolderId folder_id) { @@ -23428,8 +23744,8 @@ void MessagesManager::on_update_pinned_dialogs(FolderId folder_id) { send_closure(actor_id, &MessagesManager::reload_pinned_dialogs, folder_id, Promise()); }); - // max ordinary pinned dialogs + max pinned secret chats + sponsored proxy - size_t needed_dialogs = 2 * get_pinned_dialogs_limit(folder_id) + (folder_id == FolderId::main() ? 1 : 0); + // max ordinary pinned dialogs + max pinned secret chats + size_t needed_dialogs = 2 * get_pinned_dialogs_limit(folder_id); auto &list = get_dialog_list(folder_id); if (list.ordered_dialogs_.size() >= needed_dialogs) { query_promise.set_value(Unit()); @@ -23529,15 +23845,24 @@ void MessagesManager::set_dialog_pinned_message_id(Dialog *d, MessageId pinned_m make_tl_object(d->dialog_id.get(), pinned_message_id.get())); } -void MessagesManager::repair_dialog_scheduled_messages(DialogId dialog_id) { - if (td_->auth_manager_->is_bot() || dialog_id.get_type() == DialogType::SecretChat) { +void MessagesManager::repair_dialog_scheduled_messages(Dialog *d) { + if (td_->auth_manager_->is_bot() || d->dialog_id.get_type() == DialogType::SecretChat) { return; } + if (d->last_repair_scheduled_messages_generation == scheduled_messages_sync_generation_) { + return; + } + d->last_repair_scheduled_messages_generation = scheduled_messages_sync_generation_; + // TODO create logevent - get_dialog_scheduled_messages(dialog_id, PromiseCreator::lambda([actor_id = actor_id(this), dialog_id](Unit) { + auto dialog_id = d->dialog_id; + LOG(INFO) << "Repair scheduled messages in " << dialog_id << " with generation " + << d->last_repair_scheduled_messages_generation; + get_dialog_scheduled_messages(dialog_id, false, true, + PromiseCreator::lambda([actor_id = actor_id(this), dialog_id](Unit) { send_closure(G()->messages_manager(), &MessagesManager::get_dialog_scheduled_messages, - dialog_id, Promise()); + dialog_id, true, true, Promise()); })); } @@ -23560,6 +23885,9 @@ void MessagesManager::on_update_dialog_has_scheduled_server_messages(DialogId di LOG(INFO) << "Receive has_scheduled_server_messages = " << has_scheduled_server_messages << " in " << dialog_id; if (d->has_scheduled_server_messages != has_scheduled_server_messages) { set_dialog_has_scheduled_server_messages(d, has_scheduled_server_messages); + } else if (has_scheduled_server_messages != + (d->has_scheduled_database_messages || d->scheduled_messages != nullptr)) { + repair_dialog_scheduled_messages(d); } } @@ -23567,12 +23895,12 @@ void MessagesManager::set_dialog_has_scheduled_server_messages(Dialog *d, bool h CHECK(d != nullptr); CHECK(d->has_scheduled_server_messages != has_scheduled_server_messages); d->has_scheduled_server_messages = has_scheduled_server_messages; - repair_dialog_scheduled_messages(d->dialog_id); + repair_dialog_scheduled_messages(d); on_dialog_updated(d->dialog_id, "set_dialog_has_scheduled_server_messages"); LOG(INFO) << "Set " << d->dialog_id << " has_scheduled_server_messages to " << has_scheduled_server_messages; - send_update_chat_has_scheduled_messages(d); + send_update_chat_has_scheduled_messages(d, false); } void MessagesManager::set_dialog_has_scheduled_database_messages(DialogId dialog_id, @@ -23624,8 +23952,9 @@ void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) { // first remove the dialog from the old chat list // this will send updateChatChatList if needed - if (d->pinned_order != DEFAULT_ORDER) { - set_dialog_is_pinned(d, false); + DialogId sponsored_dialog_id = sponsored_dialog_id_; + if (d->dialog_id == sponsored_dialog_id_ && d->order != DEFAULT_ORDER) { + sponsored_dialog_id_ = DialogId(); // supress updateChatIsSponsored } set_dialog_order(d, DEFAULT_ORDER, true, false, "set_dialog_folder_id old"); @@ -23633,9 +23962,11 @@ void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) { d->folder_id = folder_id; d->is_folder_id_inited = true; + sponsored_dialog_id_ = sponsored_dialog_id; + // update dialog position in the new folder // this will send updateChatChatList if needed - update_dialog_pos(d, false, "set_dialog_folder_id new", true, false); + update_dialog_pos(d, "set_dialog_folder_id new"); on_dialog_updated(d->dialog_id, "set_dialog_folder_id"); } @@ -23755,12 +24086,7 @@ void MessagesManager::on_dialog_user_is_contact_updated(DialogId dialog_id, bool send_update_chat_action_bar(d); } } else { - d->know_action_bar = false; - if (have_input_peer(dialog_id, AccessRights::Read)) { - repair_dialog_action_bar(dialog_id, "on_dialog_user_is_contact_updated"); - } - // there is no need to change action bar - on_dialog_updated(dialog_id, "on_dialog_user_is_contact_updated"); + repair_dialog_action_bar(d, "on_dialog_user_is_contact_updated"); } } } @@ -23780,12 +24106,7 @@ void MessagesManager::on_dialog_user_is_blocked_updated(DialogId dialog_id, bool send_update_chat_action_bar(d); } } else { - d->know_action_bar = false; - if (have_input_peer(dialog_id, AccessRights::Read)) { - repair_dialog_action_bar(dialog_id, "on_dialog_user_is_blocked_updated"); - } - // there is no need to change action bar - on_dialog_updated(dialog_id, "on_dialog_user_is_blocked_updated"); + repair_dialog_action_bar(d, "on_dialog_user_is_blocked_updated"); } } } @@ -23804,12 +24125,7 @@ void MessagesManager::on_dialog_user_is_deleted_updated(DialogId dialog_id, bool send_update_chat_action_bar(d); } } else { - d->know_action_bar = false; - if (have_input_peer(dialog_id, AccessRights::Read)) { - repair_dialog_action_bar(dialog_id, "on_dialog_user_is_deleted_updated"); - } - // there is no need to change action bar - on_dialog_updated(dialog_id, "on_dialog_user_is_deleted_updated"); + repair_dialog_action_bar(d, "on_dialog_user_is_deleted_updated"); } } } @@ -24151,6 +24467,40 @@ RestrictedRights MessagesManager::get_dialog_permissions(DialogId dialog_id) con } } +bool MessagesManager::get_dialog_has_scheduled_messages(const Dialog *d) const { + if (!have_input_peer(d->dialog_id, AccessRights::Read)) { + return false; + } + if (is_broadcast_channel(d->dialog_id) && + !td_->contacts_manager_->get_channel_status(d->dialog_id.get_channel_id()).can_post_messages()) { + return false; + } + // TODO send updateChatHasScheduledMessage when can_post_messages changes + + return d->has_scheduled_server_messages || d->has_scheduled_database_messages || d->scheduled_messages != nullptr; +} + +bool MessagesManager::is_dialog_action_unneded(DialogId dialog_id) const { + if (is_broadcast_channel(dialog_id)) { + return true; + } + + auto dialog_type = dialog_id.get_type(); + if (dialog_type == DialogType::User || dialog_type == DialogType::SecretChat) { + UserId user_id = dialog_type == DialogType::User + ? dialog_id.get_user_id() + : td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (!user_id.is_valid() || td_->contacts_manager_->is_user_bot(user_id) || + td_->contacts_manager_->is_user_deleted(user_id)) { + return true; + } + if (!td_->auth_manager_->is_bot() && !td_->contacts_manager_->is_user_status_exact(user_id)) { + // return true; + } + } + return false; +} + void MessagesManager::send_dialog_action(DialogId dialog_id, const tl_object_ptr &action, Promise &&promise) { if (action == nullptr) { @@ -24169,25 +24519,11 @@ void MessagesManager::send_dialog_action(DialogId dialog_id, const tl_object_ptr return promise.set_value(Unit()); } - if (is_broadcast_channel(dialog_id)) { + if (is_dialog_action_unneded(dialog_id)) { return promise.set_value(Unit()); } - auto dialog_type = dialog_id.get_type(); - if (dialog_type == DialogType::User || dialog_type == DialogType::SecretChat) { - UserId user_id = dialog_type == DialogType::User - ? dialog_id.get_user_id() - : td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - if (!user_id.is_valid() || td_->contacts_manager_->is_user_bot(user_id) || - td_->contacts_manager_->is_user_deleted(user_id)) { - return promise.set_value(Unit()); - } - if (!td_->auth_manager_->is_bot() && !td_->contacts_manager_->is_user_status_exact(user_id)) { - // return promise.set_value(Unit()); - } - } - - if (dialog_type == DialogType::SecretChat) { + if (dialog_id.get_type() == DialogType::SecretChat) { tl_object_ptr send_action; switch (action->get_id()) { case td_api::chatActionCancel::ID: @@ -24509,6 +24845,12 @@ void MessagesManager::set_dialog_folder_id(DialogId dialog_id, FolderId folder_i return promise.set_value(Unit()); } + if (folder_id == FolderId::archive() && + (dialog_id == get_my_dialog_id() || + dialog_id == DialogId(td_->contacts_manager_->get_service_notifications_user_id()))) { + return promise.set_error(Status::Error(400, "Chat can't be archived")); + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { return promise.set_error(Status::Error(6, "Can't access the chat")); } @@ -24982,7 +25324,7 @@ std::pair> MessagesManager::search_private_chat break; case DialogParticipantsFilter::Members: user_ids.push_back(my_user_id); - if (peer_user_id.is_valid()) { + if (peer_user_id.is_valid() && peer_user_id != my_user_id) { user_ids.push_back(peer_user_id); } break; @@ -24994,7 +25336,7 @@ std::pair> MessagesManager::search_private_chat if (td_->auth_manager_->is_bot()) { user_ids.push_back(my_user_id); } - if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_bot(peer_user_id)) { + if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_bot(peer_user_id) && peer_user_id != my_user_id) { user_ids.push_back(peer_user_id); } break; @@ -25988,17 +26330,15 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq // in get_message_notification_group_force get_dialog_notification_group_id(d->dialog_id, get_notification_group_info(d, message.get())); } - MessageId added_pinned_message_id; // TODO remove - MessageId preloaded_pinned_message_id; // TODO remove - if (*need_update) { + if (*need_update || (!d->last_new_message_id.is_valid() && !message_id.is_yet_unsent() && !message->from_database)) { auto pinned_message_id = get_message_content_pinned_message_id(message->content.get()); - added_pinned_message_id = pinned_message_id; - if (pinned_message_id.is_valid() && have_message_force({dialog_id, pinned_message_id}, "preload pinned message")) { - preloaded_pinned_message_id = pinned_message_id; + if (pinned_message_id.is_valid() && pinned_message_id < message_id && + have_message_force({dialog_id, pinned_message_id}, "preload pinned message")) { LOG(INFO) << "Preloaded pinned " << pinned_message_id << " from database"; } if (d->pinned_message_notification_message_id.is_valid() && + d->pinned_message_notification_message_id < message_id && have_message_force({dialog_id, d->pinned_message_notification_message_id}, "preload previously pinned message")) { LOG(INFO) << "Preloaded previously pinned " << d->pinned_message_notification_message_id << " from database"; @@ -26008,7 +26348,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq // there must be no two recursive calls to add_message_to_dialog LOG_CHECK(!d->being_added_message_id.is_valid()) << d->dialog_id << " " << d->being_added_message_id << " " << message_id << " " << *need_update << " " - << d->pinned_message_notification_message_id << " " << preloaded_pinned_message_id << " " << source; + << d->pinned_message_notification_message_id << " " << d->last_new_message_id << " " << source; LOG_CHECK(!d->being_deleted_message_id.is_valid()) << d->being_deleted_message_id << " " << message_id << " " << source; @@ -26084,7 +26424,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq if (queue_id & 1) { LOG(INFO) << "Add " << message_id << " from " << source << " to queue " << queue_id; yet_unsent_media_queues_[queue_id][message_id.get()]; // reserve place for promise - if (!td_->auth_manager_->is_bot() && !is_broadcast_channel(dialog_id)) { + if (!td_->auth_manager_->is_bot() && !is_dialog_action_unneded(dialog_id)) { pending_send_dialog_action_timeout_.add_timeout_in(dialog_id.get(), 1.0); } } @@ -26313,7 +26653,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq add_message_file_sources(dialog_id, m); - register_message_content(td_, m->content.get(), {dialog_id, m->message_id}); + register_message_content(td_, m->content.get(), {dialog_id, m->message_id}, "add_message_to_dialog"); if (*need_update && m->message_id.is_server() && message_content_type == MessageContentType::PinMessage) { // always update pinned message from service message, even new pinned_message_id is invalid @@ -26455,6 +26795,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo change_message_files(dialog_id, m, old_file_ids); } if (old_message_id != message_id) { + being_readded_message_id_ = {dialog_id, old_message_id}; message = do_delete_scheduled_message(d, old_message_id, false, "add_scheduled_message_to_dialog"); CHECK(message != nullptr); send_update_delete_messages(dialog_id, {message->message_id.get()}, false, false); @@ -26482,7 +26823,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo add_message_file_sources(dialog_id, m); - register_message_content(td_, m->content.get(), {dialog_id, m->message_id}); + register_message_content(td_, m->content.get(), {dialog_id, m->message_id}, "add_scheduled_message_to_dialog"); if (from_update) { update_sent_message_contents(dialog_id, m); @@ -26498,6 +26839,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo Message *result_message = treap_insert_message(&d->scheduled_messages, std::move(message)); CHECK(result_message != nullptr); CHECK(d->scheduled_messages != nullptr); + being_readded_message_id_ = FullMessageId(); return result_message; } @@ -26585,7 +26927,7 @@ void MessagesManager::delete_all_dialog_messages_from_database(Dialog *d, Messag } if (d->pinned_message_notification_message_id.is_valid() && d->pinned_message_notification_message_id <= max_message_id) { - remove_dialog_pinned_message_notification(d); + remove_dialog_pinned_message_notification(d, "delete_all_dialog_messages_from_database"); } remove_message_dialog_notifications(d, max_message_id, false, source); remove_message_dialog_notifications(d, max_message_id, true, source); @@ -26660,6 +27002,10 @@ void MessagesManager::delete_message_files(DialogId dialog_id, const Message *m) } bool MessagesManager::need_delete_file(FullMessageId full_message_id, FileId file_id) const { + if (being_readded_message_id_ == full_message_id) { + return false; + } + auto main_file_id = td_->file_manager_->get_file_view(file_id).file_id(); auto full_message_ids = td_->file_reference_manager_->get_some_message_file_sources(main_file_id); LOG(INFO) << "Receive " << full_message_ids << " as sources for file " << main_file_id << "/" << file_id << " from " @@ -26682,6 +27028,9 @@ bool MessagesManager::need_delete_message_files(DialogId dialog_id, const Messag if (!m->message_id.is_scheduled() && !m->message_id.is_server() && dialog_type != DialogType::SecretChat) { return false; } + if (being_readded_message_id_ == FullMessageId{dialog_id, m->message_id}) { + return false; + } if (m->forward_info != nullptr && m->forward_info->from_dialog_id.is_valid() && m->forward_info->from_message_id.is_valid()) { @@ -27068,7 +27417,8 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr } if (old_message->is_from_scheduled != new_message->is_from_scheduled) { - old_message->is_from_scheduled = new_message->is_from_scheduled; + // is_from_scheduled flag shouldn't be changed, because we are unable to show/hide message notification + // old_message->is_from_scheduled = new_message->is_from_scheduled; } if (old_message->edit_date > 0) { @@ -27094,7 +27444,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr // MessageGame and MessageInvoice reply markup can be generated server side // some forwards retain their reply markup if (content_type != MessageContentType::Game && content_type != MessageContentType::Invoice && - old_message->forward_info == nullptr && !replace_legacy) { + need_message_changed_warning(old_message) && !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has received reply markup " << *new_message->reply_markup << ", message content type is " << old_message->content->get_type() << '/' << new_message->content->get_type(); @@ -27116,16 +27466,19 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr old_message->had_reply_markup = false; old_message->reply_markup = std::move(new_message->reply_markup); need_send_update = true; - } else { + } else if (need_message_changed_warning(old_message)) { LOG_IF(WARNING, *old_message->reply_markup != *new_message->reply_markup) << message_id << " in " << dialog_id << " has changed reply_markup from " << *old_message->reply_markup << " to " << *new_message->reply_markup; } - } else if (is_new_available) { // if the message is not accessible anymore, then we don't need a warning - LOG(ERROR) << message_id << " in " << dialog_id << " sent by " << old_message->sender_user_id - << " has lost reply markup " << *old_message->reply_markup - << ". Old message: " << to_string(get_message_object(dialog_id, old_message)) - << ". New message: " << to_string(get_message_object(dialog_id, new_message.get())); + } else { + // if the message is not accessible anymore, then we don't need a warning + if (need_message_changed_warning(old_message) && is_new_available) { + LOG(ERROR) << message_id << " in " << dialog_id << " sent by " << old_message->sender_user_id + << " has lost reply markup " << *old_message->reply_markup + << ". Old message: " << to_string(get_message_object(dialog_id, old_message)) + << ". New message: " << to_string(get_message_object(dialog_id, new_message.get())); + } } } } @@ -27147,7 +27500,7 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr if (update_message_content(dialog_id, old_message, std::move(new_message->content), true, message_id.is_yet_unsent() && new_message->edit_date == 0, - !is_scheduled && get_message(d, message_id) != nullptr)) { + get_message(d, message_id) != nullptr)) { need_send_update = true; } @@ -27166,7 +27519,8 @@ bool MessagesManager::need_message_changed_warning(const Message *old_message) { return false; } if (old_message->message_id.is_yet_unsent() && - (old_message->forward_info != nullptr || old_message->had_forward_info)) { + (old_message->forward_info != nullptr || old_message->had_forward_info || + old_message->real_forward_from_dialog_id.is_valid())) { // original message may be edited return false; } @@ -27177,10 +27531,6 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me unique_ptr new_content, bool need_send_update_message_content, bool need_merge_files, bool is_message_in_dialog) { - if (old_message->message_id.is_scheduled()) { - is_message_in_dialog = false; - } - bool is_content_changed = false; bool need_update = false; unique_ptr &old_content = old_message->content; @@ -27245,7 +27595,8 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me if (is_content_changed || need_update) { if (is_message_in_dialog) { - reregister_message_content(td_, old_content.get(), new_content.get(), {dialog_id, old_message->message_id}); + reregister_message_content(td_, old_content.get(), new_content.get(), {dialog_id, old_message->message_id}, + "update_message_content"); } old_content = std::move(new_content); update_message_content_file_id_remote(old_content.get(), old_file_id); @@ -27341,7 +27692,7 @@ void MessagesManager::force_create_dialog(DialogId dialog_id, const char *source } d = add_dialog(dialog_id); - update_dialog_pos(d, false, "force_create_dialog"); + update_dialog_pos(d, "force_create_dialog"); if (dialog_id.get_type() == DialogType::SecretChat && !d->notification_settings.is_synchronized) { // secret chat is being created @@ -27395,7 +27746,7 @@ void MessagesManager::force_create_dialog(DialogId dialog_id, const char *source } } } else if (force_update_dialog_pos) { - update_dialog_pos(d, false, "force update dialog pos"); + update_dialog_pos(d, "force update dialog pos"); } } @@ -27554,7 +27905,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab force_create_dialog(DialogId(user_id), "add chat with user to load/store action_bar"); } } else { - repair_dialog_action_bar(dialog_id, "fix_new_dialog"); + reget_dialog_action_bar(dialog_id, "fix_new_dialog"); } } @@ -27726,7 +28077,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab break; case DialogType::Chat: if (d->last_read_inbox_message_id < d->last_read_outbox_message_id) { - LOG(INFO) << "Have last read outbox message " << d->last_read_outbox_message_id << " in " << dialog_id + LOG(INFO) << "Last read outbox message is " << d->last_read_outbox_message_id << " in " << dialog_id << ", but last read inbox message is " << d->last_read_inbox_message_id; // can't fix last_read_inbox_message_id by last_read_outbox_message_id because last_read_outbox_message_id is // just a message identifier not less than an identifier of last read outgoing message and less than @@ -27759,16 +28110,26 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab } if (need_get_history && !td_->auth_manager_->is_bot() && have_input_peer(dialog_id, AccessRights::Read) && - d->order != DEFAULT_ORDER) { + (d->order != DEFAULT_ORDER || is_dialog_sponsored(d))) { get_history_from_the_end(dialog_id, true, false, Auto()); } - if (d->need_repair_server_unread_count && have_input_peer(dialog_id, AccessRights::Read)) { + if (d->need_repair_server_unread_count && need_unread_counter(d->order)) { CHECK(dialog_type != DialogType::SecretChat); repair_server_unread_count(dialog_id, d->server_unread_count); } + if (d->need_repair_channel_server_unread_count) { + repair_channel_server_unread_count(d); + } - update_dialog_pos(d, false, "fix_new_dialog 7", true, is_loaded_from_database); + if (!G()->parameters().use_message_db) { + d->has_loaded_scheduled_messages_from_database = true; + } + + update_dialog_pos(d, "fix_new_dialog 7", true, is_loaded_from_database); + if (is_loaded_from_database && d->order != order && order < get_dialog_order({}, MIN_PINNED_DIALOG_DATE - 1)) { + LOG(ERROR) << dialog_id << " has order " << d->order << " instead of saved to database order " << order; + } LOG(INFO) << "Loaded " << dialog_id << " with last new " << d->last_new_message_id << ", first database " << d->first_database_message_id << ", last database " << d->last_database_message_id << ", last " @@ -27783,7 +28144,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab << " with last " << d->mention_notification_group.last_notification_id << " sent at " << d->mention_notification_group.last_notification_date << ", max removed " << d->mention_notification_group.max_removed_notification_id << "/" - << d->mention_notification_group.max_removed_message_id << " and pinned message " + << d->mention_notification_group.max_removed_message_id << " and pinned " << d->pinned_message_notification_message_id; VLOG(notifications) << "In " << dialog_id << " have last_read_inbox_message_id = " << d->last_read_inbox_message_id << ", last_new_message_id = " << d->last_new_message_id @@ -27812,7 +28173,7 @@ void MessagesManager::add_dialog_last_database_message(Dialog *d, unique_ptrpending_last_message_date != 0) { d->pending_last_message_date = 0; d->pending_last_message_id = MessageId(); - update_dialog_pos(d, false, "add_dialog_last_database_message 1"); + update_dialog_pos(d, "add_dialog_last_database_message 1"); } return; } @@ -27837,7 +28198,7 @@ void MessagesManager::add_dialog_last_database_message(Dialog *d, unique_ptrorder == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_; +} + int64 MessagesManager::get_dialog_public_order(const Dialog *d) const { - DialogDate dialog_date(d->order, d->dialog_id); + auto order = is_dialog_sponsored(d) ? SPONSORED_DIALOG_ORDER : d->order; + DialogDate dialog_date(order, d->dialog_id); auto *list = get_dialog_list(d->folder_id); - return list != nullptr && dialog_date <= list->last_dialog_date_ ? d->order : 0; + return list != nullptr && dialog_date <= list->last_dialog_date_ ? order : 0; } int64 MessagesManager::get_next_pinned_dialog_order() { @@ -27889,39 +28255,31 @@ int64 MessagesManager::get_next_pinned_dialog_order() { return current_pinned_dialog_order_; } -void MessagesManager::update_dialog_pos(Dialog *d, bool remove_from_dialog_list, const char *source, - bool need_send_update_chat_order, bool is_loaded_from_database) { - CHECK(d != nullptr); - LOG(INFO) << "Trying to update " << d->dialog_id << " order from " << source; - auto dialog_type = d->dialog_id.get_type(); - - switch (dialog_type) { +bool MessagesManager::is_removed_from_dialog_list(const Dialog *d) const { + switch (d->dialog_id.get_type()) { case DialogType::User: break; - case DialogType::Chat: { - auto chat_id = d->dialog_id.get_chat_id(); - if (!td_->contacts_manager_->get_chat_is_active(chat_id)) { - remove_from_dialog_list = true; - } - break; - } - case DialogType::Channel: { - auto channel_id = d->dialog_id.get_channel_id(); - if (!td_->contacts_manager_->get_channel_status(channel_id).is_member()) { - remove_from_dialog_list = true; - } - break; - } + case DialogType::Chat: + return !td_->contacts_manager_->get_chat_is_active(d->dialog_id.get_chat_id()); + case DialogType::Channel: + return !td_->contacts_manager_->get_channel_status(d->dialog_id.get_channel_id()).is_member(); case DialogType::SecretChat: break; case DialogType::None: default: UNREACHABLE(); - return; + break; } + return false; +} + +void MessagesManager::update_dialog_pos(Dialog *d, const char *source, bool need_send_update_chat_order, + bool is_loaded_from_database) { + CHECK(d != nullptr); + LOG(INFO) << "Trying to update " << d->dialog_id << " order from " << source; int64 new_order = DEFAULT_ORDER; - if (!remove_from_dialog_list) { + if (!is_removed_from_dialog_list(d)) { if (d->pinned_order != DEFAULT_ORDER) { LOG(INFO) << "Pin at " << d->pinned_order << " found"; new_order = d->pinned_order; @@ -27954,13 +28312,14 @@ void MessagesManager::update_dialog_pos(Dialog *d, bool remove_from_dialog_list, new_order = pending_order; } } - if (d->draft_message != nullptr) { + if (d->draft_message != nullptr && can_send_message(d->dialog_id).is_ok()) { LOG(INFO) << "Draft message at " << d->draft_message->date << " found"; int64 draft_order = get_dialog_order(MessageId(), d->draft_message->date); if (draft_order > new_order) { new_order = draft_order; } } + auto dialog_type = d->dialog_id.get_type(); if (dialog_type == DialogType::Channel) { auto date = td_->contacts_manager_->get_channel_date(d->dialog_id.get_channel_id()); LOG(INFO) << "Join of channel at " << date << " found"; @@ -27982,15 +28341,11 @@ void MessagesManager::update_dialog_pos(Dialog *d, bool remove_from_dialog_list, } } } - if (d->dialog_id != sponsored_dialog_id_ && new_order == DEFAULT_ORDER && !d->is_empty) { - // if there is no known messages in the dialog, just leave it where it is - LOG(INFO) << "There is no known messages in the dialog"; - return; + if (new_order == DEFAULT_ORDER && !d->is_empty) { + LOG(INFO) << "There is no known messages in the chat, just leave it where it is"; + new_order = d->order; } } - if (new_order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_ && d->folder_id == FolderId::main()) { - new_order = SPONSORED_DIALOG_ORDER; - } if (set_dialog_order(d, new_order, need_send_update_chat_order, is_loaded_from_database, source)) { on_dialog_updated(d->dialog_id, "update_dialog_pos"); @@ -28005,25 +28360,12 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen DialogDate new_date(new_order, dialog_id); auto &list = get_dialog_list(d->folder_id); - std::set *ordered_dialogs_set = nullptr; - switch (dialog_id.get_type()) { - case DialogType::User: - case DialogType::Chat: - case DialogType::Channel: - case DialogType::SecretChat: - ordered_dialogs_set = &list.ordered_server_dialogs_; - break; - case DialogType::None: - default: - UNREACHABLE(); - return false; - } - if (old_date == new_date) { - if (new_order == DEFAULT_ORDER && ordered_dialogs_set->count(old_date) == 0) { - ordered_dialogs_set->insert(new_date); + if (new_order == DEFAULT_ORDER) { + // first addition of a new left dialog + list.ordered_server_dialogs_.insert(new_date); } - LOG(INFO) << "Dialog order is not changed: " << new_order << " from " << source; + LOG(INFO) << "Chat order is not changed: " << new_order << " from " << source; return false; } LOG(INFO) << "Update order of " << dialog_id << " from " << d->order << " to " << new_order << " from " << source; @@ -28035,11 +28377,9 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen } need_update = true; } - if (ordered_dialogs_set->erase(old_date) == 0) { + if (list.ordered_server_dialogs_.erase(old_date) == 0) { LOG_IF(ERROR, d->order != DEFAULT_ORDER) << dialog_id << " not found in the chat list from " << source; } - LOG_IF(ERROR, is_loaded_from_database && d->order != DEFAULT_ORDER) - << dialog_id << " has non-default order after loading from database from " << source; int64 updated_to = 0; if (new_date <= list.last_dialog_date_) { @@ -28047,11 +28387,10 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen need_update = true; updated_to = new_order; } - ordered_dialogs_set->insert(new_date); + list.ordered_server_dialogs_.insert(new_date); bool add_to_hints = (d->order == DEFAULT_ORDER); - bool was_sponsored = (d->order == SPONSORED_DIALOG_ORDER); - bool is_sponsored = (new_order == SPONSORED_DIALOG_ORDER); + bool was_sponsored = is_dialog_sponsored(d); bool had_unread_counter = need_unread_counter(d->order); bool has_unread_counter = need_unread_counter(new_order); bool is_removed_from_folder = new_order == DEFAULT_ORDER; @@ -28073,6 +28412,15 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen } } } + + if (new_order == DEFAULT_ORDER && d->pinned_order != DEFAULT_ORDER) { + d->pinned_order = DEFAULT_ORDER; + send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), false, 0)); + need_update = false; + } + + d->order = new_order; + bool need_update_unread_chat_count = old_dialog_total_count != get_dialog_total_count(list); CHECK(static_cast(list.ordered_dialogs_.size()) <= list.in_memory_dialog_total_count_); CHECK(static_cast(list.in_memory_dialog_total_count_) <= list.ordered_server_dialogs_.size()); @@ -28131,19 +28479,36 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen send_update_unread_chat_count(d->folder_id, dialog_id, true, "changed total_count"); } - d->order = new_order; - if (add_to_hints) { update_dialogs_hints(d); } update_dialogs_hints_rating(d); - if (is_added_to_folder) { + if (is_removed_from_folder && d->folder_id != FolderId::main()) { + if (need_update) { + need_update = false; + send_closure(G()->td(), &Td::send_update, make_tl_object(dialog_id.get(), updated_to)); + } + + d->folder_id = FolderId::main(); + d->is_folder_id_inited = true; + send_update_chat_chat_list(d); + on_dialog_updated(d->dialog_id, "set_dialog_order 5"); + + if (is_dialog_sponsored(d)) { + auto &main_list = get_dialog_list(d->folder_id); + if (main_list.is_dialog_unread_count_inited_) { + send_update_unread_chat_count(d->folder_id, DialogId(), true, "set_dialog_order 6"); + } + } + } + + if (is_added_to_folder && !(dialog_id == sponsored_dialog_id_ && d->folder_id == FolderId::main())) { send_update_chat_chat_list(d); } - if (was_sponsored != is_sponsored) { + if (was_sponsored != is_dialog_sponsored(d)) { send_update_chat_is_sponsored(d); - if (!is_loaded_from_database && is_sponsored) { + if (!is_loaded_from_database && !was_sponsored) { // channel is sponsored only if user isn't a channel member remove_all_dialog_notifications(d, false, "set_dialog_order 3"); remove_all_dialog_notifications(d, true, "set_dialog_order 4"); @@ -28153,7 +28518,7 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen if (need_update && need_send_update_chat_order) { send_closure(G()->td(), &Td::send_update, make_tl_object(dialog_id.get(), updated_to)); } - if (is_removed_from_folder) { + if (is_removed_from_folder && !(dialog_id == sponsored_dialog_id_ && d->folder_id == FolderId::main())) { send_update_chat_chat_list(d); } return true; @@ -28162,7 +28527,7 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen void MessagesManager::update_last_dialog_date(FolderId folder_id) { auto &list = get_dialog_list(folder_id); auto old_last_dialog_date = list.last_dialog_date_; - list.last_dialog_date_ = list.last_server_dialog_date_; // std::min + list.last_dialog_date_ = list.last_server_dialog_date_; CHECK(old_last_dialog_date <= list.last_dialog_date_); LOG(INFO) << "Update last dialog date in " << folder_id << " from " << old_last_dialog_date << " to " @@ -28671,6 +29036,10 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m } } + if (d->last_read_inbox_message_id.is_valid() && !d->last_read_inbox_message_id.is_server() && + read_inbox_max_message_id == d->last_read_inbox_message_id.get_prev_server_message_id()) { + read_inbox_max_message_id = d->last_read_inbox_message_id; + } if (d->server_unread_count != server_unread_count || d->last_read_inbox_message_id != read_inbox_max_message_id) { set_dialog_last_read_inbox_message_id(d, read_inbox_max_message_id, server_unread_count, d->local_unread_count, false, "on_get_channel_dialog 50"); @@ -28817,7 +29186,7 @@ void MessagesManager::on_get_channel_difference( on_update_dialog_notify_settings(dialog_id, std::move(dialog->notify_settings_), "on_get_dialogs"); - bool is_pinned = (dialog->flags_ & DIALOG_FLAG_IS_PINNED) != 0; + bool is_pinned = !is_removed_from_dialog_list(d) && (dialog->flags_ & DIALOG_FLAG_IS_PINNED) != 0; bool was_pinned = d->pinned_order != DEFAULT_ORDER; if (is_pinned != was_pinned) { set_dialog_is_pinned(d, is_pinned); @@ -28845,7 +29214,7 @@ void MessagesManager::on_get_channel_difference( } if (need_update_dialog_pos) { - update_dialog_pos(d, false, "on_get_channel_difference"); + update_dialog_pos(d, "on_get_channel_difference"); } if (!is_final) { @@ -29107,7 +29476,7 @@ MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog add_message_to_dialog(d, std::move(m), true, &need_update, &need_update_dialog_pos, "resend message"); CHECK(result_message != nullptr); - send_update_chat_has_scheduled_messages(d); + send_update_chat_has_scheduled_messages(d, false); send_update_new_message(d, result_message); if (need_update_dialog_pos) { @@ -29322,7 +29691,7 @@ void MessagesManager::on_binlog_events(vector &&events) { send_update_new_message(to_dialog, forwarded_messages.back()); } - send_update_chat_has_scheduled_messages(to_dialog); + send_update_chat_has_scheduled_messages(to_dialog, false); if (need_update_dialog_pos) { send_update_chat_last_message(to_dialog, "on_reforward_message"); @@ -30158,25 +30527,57 @@ void MessagesManager::on_get_sponsored_dialog_id(tl_object_ptrdialog_id; + + if (is_dialog_sponsored(d)) { + send_update_chat_chat_list(d); + auto folder_id = FolderId::main(); + auto &list = get_dialog_list(folder_id); + DialogDate max_dialog_date(SPONSORED_DIALOG_ORDER, d->dialog_id); + if (list.last_server_dialog_date_ < max_dialog_date) { + list.last_server_dialog_date_ = max_dialog_date; + update_last_dialog_date(folder_id); + } + send_update_chat_is_sponsored(d); + } +} + void MessagesManager::set_sponsored_dialog_id(DialogId dialog_id) { if (sponsored_dialog_id_ == dialog_id) { return; } + bool need_update_total_chat_count = false; if (sponsored_dialog_id_.is_valid()) { - Dialog *d = get_dialog(sponsored_dialog_id_); + const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); + bool is_sponsored = is_dialog_sponsored(d); sponsored_dialog_id_ = DialogId(); - update_dialog_pos(d, false, "delete_sponsored_dialog_id"); + if (is_sponsored) { + send_update_chat_is_sponsored(d); + send_update_chat_chat_list(d); + need_update_total_chat_count = true; + } } if (dialog_id.is_valid()) { force_create_dialog(dialog_id, "set_sponsored_dialog_id"); - - Dialog *d = get_dialog(dialog_id); + const Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - sponsored_dialog_id_ = dialog_id; - update_dialog_pos(d, false, "set_sponsored_dialog_id"); + add_sponsored_dialog(d); + if (is_dialog_sponsored(d)) { + need_update_total_chat_count = !need_update_total_chat_count; + } + } + + if (need_update_total_chat_count) { + auto folder_id = FolderId::main(); + auto &list = get_dialog_list(folder_id); + if (list.is_dialog_unread_count_inited_) { + send_update_unread_chat_count(FolderId::main(), DialogId(), true, "set_sponsored_dialog_id"); + } } if (G()->parameters().use_message_db) { @@ -30190,37 +30591,32 @@ void MessagesManager::set_sponsored_dialog_id(DialogId dialog_id) { } td_api::object_ptr MessagesManager::get_update_unread_message_count_object( - FolderId folder_id, const DialogList &list) const { + const DialogList &list) const { CHECK(list.is_message_unread_count_inited_); int32 unread_count = list.unread_message_total_count_; int32 unread_unmuted_count = list.unread_message_total_count_ - list.unread_message_muted_count_; - if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && folder_id == FolderId::main()) { + if (include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && + list.folder_id == FolderId::main()) { const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto sponsored_unread_count = d->server_unread_count + d->local_unread_count; - if (sponsored_unread_count != 0) { - unread_count -= sponsored_unread_count; - if (unread_count < 0) { - unread_count = 0; - } + if (is_dialog_sponsored(d) && sponsored_unread_count != 0) { + unread_count += sponsored_unread_count; if (!is_dialog_muted(d)) { - unread_unmuted_count -= sponsored_unread_count; - if (unread_unmuted_count < 0) { - unread_unmuted_count = 0; - } + unread_unmuted_count += sponsored_unread_count; } } } CHECK(unread_count >= 0); CHECK(unread_unmuted_count >= 0); - return td_api::make_object(get_chat_list_object(folder_id), unread_count, + return td_api::make_object(get_chat_list_object(list.folder_id), unread_count, unread_unmuted_count); } td_api::object_ptr MessagesManager::get_update_unread_chat_count_object( - FolderId folder_id, const DialogList &list) const { + const DialogList &list) const { CHECK(list.is_dialog_unread_count_inited_); int32 unread_count = list.unread_dialog_total_count_; int32 unread_unmuted_count = unread_count - list.unread_dialog_muted_count_; @@ -30231,26 +30627,27 @@ td_api::object_ptr MessagesManager::get_update_un CHECK(unread_marked_count >= 0); CHECK(unread_unmuted_marked_count >= 0); - if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && folder_id == FolderId::main()) { + if (include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && + list.folder_id == FolderId::main()) { const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto sponsored_unread_count = d->server_unread_count + d->local_unread_count; - if (sponsored_unread_count != 0 || d->is_marked_as_unread) { - unread_count = td::max(unread_count - 1, 0); + if (is_dialog_sponsored(d) && (sponsored_unread_count != 0 || d->is_marked_as_unread)) { + unread_count++; if (sponsored_unread_count == 0 && d->is_marked_as_unread) { - unread_marked_count = td::max(unread_marked_count - 1, 0); + unread_marked_count++; } if (!is_dialog_muted(d)) { - unread_unmuted_count = td::max(unread_unmuted_count - 1, 0); + unread_unmuted_count++; if (sponsored_unread_count == 0 && d->is_marked_as_unread) { - unread_unmuted_marked_count = td::max(unread_unmuted_marked_count - 1, 0); + unread_unmuted_marked_count++; } } } } return td_api::make_object( - get_chat_list_object(folder_id), get_dialog_total_count(list), unread_count, unread_unmuted_count, + get_chat_list_object(list.folder_id), get_dialog_total_count(list), unread_count, unread_unmuted_count, unread_marked_count, unread_unmuted_marked_count); } @@ -30260,10 +30657,10 @@ void MessagesManager::get_current_state(vector &filter, bool return_local, int64 &random_id, Promise &&promise); - vector get_dialog_scheduled_messages(DialogId dialog_id, Promise &&promise); + vector get_dialog_scheduled_messages(DialogId dialog_id, bool force, bool ignore_result, + Promise &&promise); tl_object_ptr get_dialog_message_by_date_object(int64 random_id); @@ -664,6 +664,8 @@ class MessagesManager : public Actor { void add_pending_channel_update(DialogId dialog_id, tl_object_ptr &&update, int32 new_pts, int32 pts_count, const char *source, bool is_postponed_update = false); + bool is_old_channel_update(DialogId dialog_id, int32 new_pts); + bool is_update_about_username_change_received(DialogId dialog_id) const; void on_dialog_bots_updated(DialogId dialog_id, vector bot_user_ids); @@ -697,7 +699,7 @@ class MessagesManager : public Actor { void remove_dialog_action_bar(DialogId dialog_id, Promise &&promise); - void repair_dialog_action_bar(DialogId dialog_id, const char *source); + void reget_dialog_action_bar(DialogId dialog_id, const char *source); void report_dialog(DialogId dialog_id, const tl_object_ptr &reason, const vector &message_ids, Promise &&promise); @@ -1067,6 +1069,7 @@ class MessagesManager : public Actor { MessageId max_notification_message_id; MessageId last_edited_message_id; uint32 scheduled_messages_sync_generation = 0; + uint32 last_repair_scheduled_messages_generation = 0; MessageId max_added_message_id; MessageId being_added_message_id; @@ -1103,12 +1106,14 @@ class MessagesManager : public Actor { bool is_pinned_message_id_inited = false; bool is_folder_id_inited = false; bool need_repair_server_unread_count = false; + bool need_repair_channel_server_unread_count = false; bool is_marked_as_unread = false; bool last_sent_has_scheduled_messages = false; bool has_scheduled_server_messages = false; bool has_scheduled_database_messages = false; bool is_has_scheduled_database_messages_checked = false; bool has_loaded_scheduled_messages_from_database = false; + bool sent_scheduled_messages = false; bool increment_view_counter = false; @@ -1130,6 +1135,8 @@ class MessagesManager : public Actor { std::unordered_map yet_unsent_message_id_to_persistent_message_id; + std::unordered_map last_assigned_scheduled_message_id; // date -> message_id + std::unordered_set deleted_message_ids; std::unordered_set deleted_scheduled_server_message_ids; @@ -1185,7 +1192,7 @@ class MessagesManager : public Actor { const char *debug_set_dialog_last_database_message_id = "Unknown"; // to be removed soon vector debug_message_op; - // message identifiers loaded from database, to be removed soon + // message identifiers loaded from database MessageId debug_last_new_message_id; MessageId debug_first_database_message_id; MessageId debug_last_database_message_id; @@ -1219,12 +1226,11 @@ class MessagesManager : public Actor { int32 server_dialog_total_count_ = -1; int32 secret_chat_total_count_ = -1; - std::set ordered_dialogs_; // all dialogs with date <= last_dialog_date_ - std::set ordered_server_dialogs_; // all known dialogs, including with default order - - // date of last dialog in the dialog list - // last_dialog_date_ == min(last_server_dialog_date_, last_secret_chat_dialog_date_) + // date of the last dialog in loaded the dialog list prefix DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory + std::set ordered_dialogs_; // all dialogs with date <= last_dialog_date_ + + std::set ordered_server_dialogs_; // all known dialogs, including with default order // date of last known user/group/channel dialog in the right order DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE; @@ -1450,6 +1456,7 @@ class MessagesManager : public Actor { static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 3 * 86400; static constexpr int32 USERNAME_CACHE_EXPIRE_TIME_SHORT = 900; + static constexpr int32 AUTH_NOTIFICATION_ID_CACHE_TIME = 7 * 86400; static constexpr int32 ONLINE_MEMBER_COUNT_UPDATE_TIME = 5 * 60; @@ -1619,6 +1626,8 @@ class MessagesManager : public Actor { static bool can_delete_channel_message(DialogParticipantStatus status, const Message *m, bool is_bot); + bool can_delete_message(DialogId dialog_id, const Message *m) const; + bool can_revoke_message(DialogId dialog_id, const Message *m) const; bool can_unload_message(const Dialog *d, const Message *m) const; @@ -1923,7 +1932,9 @@ class MessagesManager : public Actor { void send_update_chat_action_bar(const Dialog *d); - void send_update_chat_has_scheduled_messages(Dialog *d); + void send_update_chat_has_scheduled_messages(Dialog *d, bool from_deletion); + + void repair_dialog_action_bar(Dialog *d, const char *source); void hide_dialog_action_bar(Dialog *d); @@ -1945,7 +1956,7 @@ class MessagesManager : public Actor { static bool need_unread_counter(int64 dialog_order); - static int32 get_dialog_total_count(const DialogList &list); + int32 get_dialog_total_count(const DialogList &list) const; void repair_server_dialog_total_count(FolderId folder_id); @@ -1956,10 +1967,9 @@ class MessagesManager : public Actor { void recalc_unread_count(FolderId folder_id); td_api::object_ptr get_update_unread_message_count_object( - FolderId folder_id, const DialogList &list) const; + const DialogList &list) const; - td_api::object_ptr get_update_unread_chat_count_object(FolderId folder_id, - const DialogList &list) const; + td_api::object_ptr get_update_unread_chat_count_object(const DialogList &list) const; void set_dialog_last_read_inbox_message_id(Dialog *d, MessageId message_id, int32 server_unread_count, int32 local_unread_count, bool force_update, const char *source); @@ -1990,7 +2000,7 @@ class MessagesManager : public Actor { void set_dialog_pinned_message_id(Dialog *d, MessageId pinned_message_id); - void repair_dialog_scheduled_messages(DialogId dialog_id); + void repair_dialog_scheduled_messages(Dialog *d); void set_dialog_has_scheduled_server_messages(Dialog *d, bool has_scheduled_server_messages); @@ -2010,9 +2020,9 @@ class MessagesManager : public Actor { void try_restore_dialog_reply_markup(Dialog *d, const Message *m); - void set_dialog_pinned_message_notification(Dialog *d, MessageId message_id); + void set_dialog_pinned_message_notification(Dialog *d, MessageId message_id, const char *source); - void remove_dialog_pinned_message_notification(Dialog *d); + void remove_dialog_pinned_message_notification(Dialog *d, const char *source); void remove_dialog_mention_notifications(Dialog *d); @@ -2040,6 +2050,8 @@ class MessagesManager : public Actor { bool update_dialog_silent_send_message(Dialog *d, bool silent_send_message); + bool is_dialog_action_unneded(DialogId dialog_id) const; + void on_send_dialog_action_timeout(DialogId dialog_id); void on_active_dialog_action_timeout(DialogId dialog_id); @@ -2068,7 +2080,7 @@ class MessagesManager : public Actor { td_api::object_ptr get_chat_type_object(DialogId dialog_id) const; - static td_api::object_ptr get_chat_list_object(const Dialog *d); + td_api::object_ptr get_chat_list_object(const Dialog *d) const; static td_api::object_ptr get_chat_list_object(FolderId folder_id); @@ -2157,6 +2169,8 @@ class MessagesManager : public Actor { void cancel_send_deleted_message(DialogId dialog_id, Message *m, bool is_permanently_deleted); + static bool get_message_disable_web_page_preview(const Message *m); + static int32 get_message_flags(const Message *m); static bool is_forward_info_sender_hidden(const MessageForwardInfo *forward_info); @@ -2244,8 +2258,12 @@ class MessagesManager : public Actor { RestrictedRights get_dialog_permissions(DialogId dialog_id) const; + bool get_dialog_has_scheduled_messages(const Dialog *d) const; + static int64 get_dialog_order(MessageId message_id, int32 message_date); + bool is_dialog_sponsored(const Dialog *d) const; + int64 get_dialog_public_order(const Dialog *d) const; bool update_dialog_draft_message(Dialog *d, unique_ptr &&draft_message, bool from_update, @@ -2257,7 +2275,7 @@ class MessagesManager : public Actor { void update_dialog_notification_settings_on_server(DialogId dialog_id, bool from_binlog); - void send_update_dialog_notification_settings_query(DialogId dialog_id, Promise &&promise); + void send_update_dialog_notification_settings_query(const Dialog *d, Promise &&promise); void on_updated_dialog_notification_settings(DialogId dialog_id, uint64 generation); @@ -2274,8 +2292,10 @@ class MessagesManager : public Actor { int64 get_next_pinned_dialog_order(); - void update_dialog_pos(Dialog *d, bool remove_from_dialog_list, const char *source, - bool need_send_update_chat_order = true, bool is_loaded_from_database = false); + bool is_removed_from_dialog_list(const Dialog *d) const; + + void update_dialog_pos(Dialog *d, const char *source, bool need_send_update_chat_order = true, + bool is_loaded_from_database = false); bool set_dialog_order(Dialog *d, int64 new_order, bool need_send_update_chat_order, bool is_loaded_from_database, const char *source); @@ -2365,19 +2385,23 @@ class MessagesManager : public Actor { tl_object_ptr &&input_chat_photo, Promise &&promise); + void add_sponsored_dialog(const Dialog *d); + void set_sponsored_dialog_id(DialogId dialog_id); static uint64 get_sequence_dispatcher_id(DialogId dialog_id, MessageContentType message_content_type); Dialog *get_service_notifications_dialog(); + void save_auth_notification_ids(); + static MessageId get_next_message_id(Dialog *d, MessageType type); static MessageId get_next_local_message_id(Dialog *d); static MessageId get_next_yet_unsent_message_id(Dialog *d); - static MessageId get_next_yet_unsent_scheduled_message_id(const Dialog *d, int32 date); + static MessageId get_next_yet_unsent_scheduled_message_id(Dialog *d, int32 date); bool add_recently_found_dialog_internal(DialogId dialog_id); @@ -2736,10 +2760,16 @@ class MessagesManager : public Actor { std::unordered_map dialog_online_member_counts_; + std::unordered_map auth_notification_id_date_; + + std::unordered_map previous_repaired_read_inbox_max_message_id_; + uint32 scheduled_messages_sync_generation_ = 1; DialogId sponsored_dialog_id_; + FullMessageId being_readded_message_id_; + DialogId being_added_dialog_id_; DialogId debug_channel_difference_dialog_; diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index bc572f43..56c22ca2 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -73,8 +73,7 @@ class SetContactSignUpNotificationQuery : public Td::ResultHandler { } void send(bool is_disabled) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_setContactSignUpNotification(is_disabled)))); + send_query(G()->net_query_creator().create(telegram_api::account_setContactSignUpNotification(is_disabled))); } void on_result(uint64 id, BufferSlice packet) override { @@ -87,7 +86,7 @@ class SetContactSignUpNotificationQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for set contact sign up notification: " << status; } promise_.set_error(std::move(status)); @@ -102,7 +101,7 @@ class GetContactSignUpNotificationQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::account_getContactSignUpNotification()))); + send_query(G()->net_query_creator().create(telegram_api::account_getContactSignUpNotification())); } void on_result(uint64 id, BufferSlice packet) override { @@ -116,7 +115,7 @@ class GetContactSignUpNotificationQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag() || 1) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for get contact sign up notification: " << status; } promise_.set_error(std::move(status)); @@ -818,7 +817,7 @@ int32 NotificationManager::get_notification_delay_ms(DialogId dialog_id, const P return MIN_NOTIFICATION_DELAY_MS; } - auto delay_ms = [&]() { + auto delay_ms = [&] { auto online_info = td_->contacts_manager_->get_my_online_status(); if (!online_info.is_online_local && online_info.is_online_remote) { // If we are offline, but online from some other client, then delay notification @@ -3247,10 +3246,11 @@ Status NotificationManager::process_push_notification_payload(string payload, bo if (sender_photo != nullptr) { flags |= telegram_api::user::PHOTO_MASK; } + auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name; auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, sender_name, + false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string()); td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload"); } @@ -3570,11 +3570,12 @@ void NotificationManager::add_message_push_notification( if (sender_user_id.is_valid() && !td_->contacts_manager_->have_user_force(sender_user_id)) { int32 flags = telegram_api::user::FIRST_NAME_MASK | telegram_api::user::MIN_MASK; + auto user_name = sender_user_id.get() == 136817688 ? "Channel" : sender_name; auto user = telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, sender_name, string(), - string(), string(), nullptr, nullptr, 0, Auto(), string(), string()); + false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(), + string(), nullptr, nullptr, 0, Auto(), string(), string()); td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification"); } @@ -3590,10 +3591,13 @@ void NotificationManager::add_message_push_notification( auto group_id = info.group_id; CHECK(group_id.is_valid()); + bool is_outgoing = + sender_user_id.is_valid() ? td_->contacts_manager_->get_my_id() == sender_user_id : is_from_scheduled; if (logevent_id != 0) { VLOG(notifications) << "Register temporary " << notification_id << " with logevent " << logevent_id; temporary_notification_logevent_ids_[notification_id] = logevent_id; - temporary_notifications_[FullMessageId(dialog_id, message_id)] = {group_id, notification_id, sender_user_id}; + temporary_notifications_[FullMessageId(dialog_id, message_id)] = {group_id, notification_id, sender_user_id, + sender_name, is_outgoing}; temporary_notification_message_ids_[notification_id] = FullMessageId(dialog_id, message_id); } push_notification_promises_[notification_id].push_back(std::move(promise)); @@ -3601,15 +3605,16 @@ void NotificationManager::add_message_push_notification( auto group_type = info.group_type; auto settings_dialog_id = info.settings_dialog_id; VLOG(notifications) << "Add message push " << notification_id << " of type " << loc_key << " for " << message_id - << "/" << random_id << " in " << dialog_id << ", sent by " << sender_user_id << " at " << date - << " with arg " << arg << ", photo " << photo << " and document " << document << " to " - << group_id << " of type " << group_type << " with settings from " << settings_dialog_id; + << "/" << random_id << " in " << dialog_id << ", sent by " << sender_user_id << "/\"" + << sender_name << "\" at " << date << " with arg " << arg << ", photo " << photo + << " and document " << document << " to " << group_id << " of type " << group_type + << " with settings from " << settings_dialog_id; - add_notification(group_id, group_type, dialog_id, date, settings_dialog_id, initial_is_silent, is_silent, 0, - notification_id, - create_new_push_message_notification(sender_user_id, message_id, std::move(loc_key), std::move(arg), - std::move(photo), std::move(document)), - "add_message_push_notification"); + add_notification( + group_id, group_type, dialog_id, date, settings_dialog_id, initial_is_silent, is_silent, 0, notification_id, + create_new_push_message_notification(sender_user_id, sender_name, is_outgoing, message_id, std::move(loc_key), + std::move(arg), std::move(photo), std::move(document)), + "add_message_push_notification"); } class NotificationManager::EditMessagePushNotificationLogEvent { @@ -3701,6 +3706,8 @@ void NotificationManager::edit_message_push_notification(DialogId dialog_id, Mes auto group_id = it->second.group_id; auto notification_id = it->second.notification_id; auto sender_user_id = it->second.sender_user_id; + auto sender_name = it->second.sender_name; + auto is_outgoing = it->second.is_outgoing; CHECK(group_id.is_valid()); CHECK(notification_id.is_valid()); @@ -3725,9 +3732,10 @@ void NotificationManager::edit_message_push_notification(DialogId dialog_id, Mes push_notification_promises_[notification_id].push_back(std::move(promise)); - edit_notification(group_id, notification_id, - create_new_push_message_notification(sender_user_id, message_id, std::move(loc_key), std::move(arg), - std::move(photo), std::move(document))); + edit_notification( + group_id, notification_id, + create_new_push_message_notification(sender_user_id, std::move(sender_name), is_outgoing, message_id, + std::move(loc_key), std::move(arg), std::move(photo), std::move(document))); } Result NotificationManager::get_push_receiver_id(string payload) { diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index 7ed05aa7..6bc26802 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -388,6 +388,8 @@ class NotificationManager : public Actor { NotificationGroupId group_id; NotificationId notification_id; UserId sender_user_id; + string sender_name; + bool is_outgoing; }; std::unordered_map temporary_notifications_; std::unordered_map temporary_notification_message_ids_; diff --git a/td/telegram/NotificationType.cpp b/td/telegram/NotificationType.cpp index 798578ac..39dfb172 100644 --- a/td/telegram/NotificationType.cpp +++ b/td/telegram/NotificationType.cpp @@ -321,30 +321,35 @@ class NotificationTypePushMessage : public NotificationType { auto sender_user_id = G()->td().get_actor_unsafe()->contacts_manager_->get_user_id_object( sender_user_id_, "get_notification_type_object"); return td_api::make_object( - message_id_.get(), sender_user_id, get_push_message_content_object(key_, arg_, photo_, document_)); + message_id_.get(), sender_user_id, sender_name_, is_outgoing_, + get_push_message_content_object(key_, arg_, photo_, document_)); } StringBuilder &to_string_builder(StringBuilder &string_builder) const override { - return string_builder << "NewPushMessageNotification[" << sender_user_id_ << ", " << message_id_ << ", " << key_ - << ", " << arg_ << ", " << photo_ << ", " << document_ << ']'; + return string_builder << "NewPushMessageNotification[" << sender_user_id_ << "/\"" << sender_name_ << "\", " + << message_id_ << ", " << key_ << ", " << arg_ << ", " << photo_ << ", " << document_ << ']'; } UserId sender_user_id_; MessageId message_id_; + string sender_name_; string key_; string arg_; Photo photo_; Document document_; + bool is_outgoing_; public: - NotificationTypePushMessage(UserId sender_user_id, MessageId message_id, string key, string arg, Photo photo, - Document document) + NotificationTypePushMessage(UserId sender_user_id, string sender_name, bool is_outgoing, MessageId message_id, + string key, string arg, Photo photo, Document document) : sender_user_id_(std::move(sender_user_id)) , message_id_(message_id) + , sender_name_(std::move(sender_name)) , key_(std::move(key)) , arg_(std::move(arg)) , photo_(std::move(photo)) - , document_(std::move(document)) { + , document_(std::move(document)) + , is_outgoing_(is_outgoing) { } }; @@ -360,11 +365,12 @@ unique_ptr create_new_call_notification(CallId call_id) { return make_unique(call_id); } -unique_ptr create_new_push_message_notification(UserId sender_user_id, MessageId message_id, - string key, string arg, Photo photo, - Document document) { - return td::make_unique(sender_user_id, message_id, std::move(key), std::move(arg), - std::move(photo), std::move(document)); +unique_ptr create_new_push_message_notification(UserId sender_user_id, string sender_name, + bool is_outgoing, MessageId message_id, string key, + string arg, Photo photo, Document document) { + return td::make_unique(sender_user_id, std::move(sender_name), is_outgoing, message_id, + std::move(key), std::move(arg), std::move(photo), + std::move(document)); } } // namespace td diff --git a/td/telegram/NotificationType.h b/td/telegram/NotificationType.h index a33b071c..5b49eb33 100644 --- a/td/telegram/NotificationType.h +++ b/td/telegram/NotificationType.h @@ -59,8 +59,8 @@ unique_ptr create_new_secret_chat_notification(); unique_ptr create_new_call_notification(CallId call_id); -unique_ptr create_new_push_message_notification(UserId sender_user_id, MessageId message_id, - string key, string arg, Photo photo, - Document document); +unique_ptr create_new_push_message_notification(UserId sender_user_id, string sender_name, + bool is_outgoing, MessageId message_id, string key, + string arg, Photo photo, Document document); } // namespace td diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index 3f2db0d1..3e867c78 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -83,7 +83,7 @@ tl_object_ptr PasswordManager::get_input_ch auto p_bn = BigNum::from_binary(p); auto B_bn = BigNum::from_binary(B); auto zero = BigNum::from_decimal("0").move_as_ok(); - if (BigNum::compare(zero, B_bn) != -1 || BigNum::compare(B_bn, p_bn) != -1 || B.size() != 256) { + if (BigNum::compare(zero, B_bn) != -1 || BigNum::compare(B_bn, p_bn) != -1 || B.size() < 248 || B.size() > 256) { LOG(ERROR) << "Receive invalid value of B(" << B.size() << "): " << B_bn << " " << p_bn; return make_tl_object(); } @@ -105,7 +105,8 @@ tl_object_ptr PasswordManager::get_input_ch BigNum::mod_exp(A_bn, g_bn, a_bn, p_bn, ctx); string A = A_bn.to_binary(256); - string u = sha256(PSLICE() << A << B); + string B_pad(256 - B.size(), '\0'); + string u = sha256(PSLICE() << A << B_pad << B); auto u_bn = BigNum::from_binary(u); string k = sha256(PSLICE() << p << g_padded); auto k_bn = BigNum::from_binary(k); @@ -133,7 +134,7 @@ tl_object_ptr PasswordManager::get_input_ch for (size_t i = 0; i < h1.size(); i++) { h1[i] = static_cast(static_cast(h1[i]) ^ static_cast(h2[i])); } - auto M = sha256(PSLICE() << h1 << sha256(client_salt) << sha256(server_salt) << A << B << K); + auto M = sha256(PSLICE() << h1 << sha256(client_salt) << sha256(server_salt) << A << B_pad << B << K); LOG(INFO) << "End input password SRP hash calculation"; return make_tl_object(id, BufferSlice(A), BufferSlice(M)); @@ -274,20 +275,19 @@ void PasswordManager::drop_temp_password() { void PasswordManager::do_create_temp_password(string password, int32 timeout, PasswordState &&password_state, Promise promise) { auto hash = get_input_check_password(password, password_state); - send_with_promise( - G()->net_query_creator().create(create_storer(telegram_api::account_getTmpPassword(std::move(hash), timeout))), - PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { - auto r_result = fetch_result(std::move(r_query)); - if (r_result.is_error()) { - return promise.set_error(r_result.move_as_error()); - } - auto result = r_result.move_as_ok(); - TempPasswordState res; - res.has_temp_password = true; - res.temp_password = result->tmp_password_.as_slice().str(); - res.valid_until = result->valid_until_; - promise.set_value(std::move(res)); - })); + send_with_promise(G()->net_query_creator().create(telegram_api::account_getTmpPassword(std::move(hash), timeout)), + PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { + auto r_result = fetch_result(std::move(r_query)); + if (r_result.is_error()) { + return promise.set_error(r_result.move_as_error()); + } + auto result = r_result.move_as_ok(); + TempPasswordState res; + res.has_temp_password = true; + res.temp_password = result->tmp_password_.as_slice().str(); + res.valid_until = result->valid_until_; + promise.set_value(std::move(res)); + })); } void PasswordManager::on_finish_create_temp_password(Result result, bool /*dummy*/) { @@ -351,28 +351,28 @@ void PasswordManager::do_get_full_state(string password, PasswordState state, Pr } auto hash = get_input_check_password(password, state); - send_with_promise( - G()->net_query_creator().create(create_storer(telegram_api::account_getPasswordSettings(std::move(hash)))), - PromiseCreator::lambda( - [promise = std::move(promise), state = std::move(state), password](Result r_query) mutable { - promise.set_result([&]() -> Result { - TRY_RESULT(result, fetch_result(std::move(r_query))); - LOG(INFO) << "Receive password settings: " << to_string(result); - PasswordPrivateState private_state; - private_state.email = std::move(result->email_); + send_with_promise(G()->net_query_creator().create(telegram_api::account_getPasswordSettings(std::move(hash))), + PromiseCreator::lambda([promise = std::move(promise), state = std::move(state), + password](Result r_query) mutable { + promise.set_result([&]() -> Result { + TRY_RESULT(result, fetch_result(std::move(r_query))); + LOG(INFO) << "Receive password settings: " << to_string(result); + PasswordPrivateState private_state; + private_state.email = std::move(result->email_); - if (result->secure_settings_ != nullptr) { - auto r_secret = decrypt_secure_secret(password, std::move(result->secure_settings_->secure_algo_), - result->secure_settings_->secure_secret_.as_slice(), - result->secure_settings_->secure_secret_id_); - if (r_secret.is_ok()) { - private_state.secret = r_secret.move_as_ok(); - } - } + if (result->secure_settings_ != nullptr) { + auto r_secret = + decrypt_secure_secret(password, std::move(result->secure_settings_->secure_algo_), + result->secure_settings_->secure_secret_.as_slice(), + result->secure_settings_->secure_secret_id_); + if (r_secret.is_ok()) { + private_state.secret = r_secret.move_as_ok(); + } + } - return PasswordFullState{std::move(state), std::move(private_state)}; - }()); - })); + return PasswordFullState{std::move(state), std::move(private_state)}; + }()); + })); } void PasswordManager::get_recovery_email_address(string password, @@ -389,8 +389,7 @@ void PasswordManager::get_recovery_email_address(string password, } void PasswordManager::check_recovery_email_address_code(string code, Promise promise) { - auto query = - G()->net_query_creator().create(create_storer(telegram_api::account_confirmPasswordEmail(std::move(code)))); + auto query = G()->net_query_creator().create(telegram_api::account_confirmPasswordEmail(std::move(code))); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); @@ -403,7 +402,7 @@ void PasswordManager::check_recovery_email_address_code(string code, Promise promise) { - auto query = G()->net_query_creator().create(create_storer(telegram_api::account_resendPasswordEmail())); + auto query = G()->net_query_creator().create(telegram_api::account_resendPasswordEmail()); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); @@ -417,8 +416,7 @@ void PasswordManager::resend_recovery_email_address_code(Promise promise) void PasswordManager::send_email_address_verification_code( string email, Promise> promise) { last_verified_email_address_ = email; - auto query = - G()->net_query_creator().create(create_storer(telegram_api::account_sendVerifyEmailCode(std::move(email)))); + auto query = G()->net_query_creator().create(telegram_api::account_sendVerifyEmailCode(std::move(email))); send_with_promise( std::move(query), PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); @@ -447,8 +445,8 @@ void PasswordManager::check_email_address_verification_code(string code, Promise if (last_verified_email_address_.empty()) { return promise.set_error(Status::Error(400, "No email address verification was sent")); } - auto query = G()->net_query_creator().create( - create_storer(telegram_api::account_verifyEmail(last_verified_email_address_, std::move(code)))); + auto query = + G()->net_query_creator().create(telegram_api::account_verifyEmail(last_verified_email_address_, std::move(code))); send_with_promise(std::move(query), PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); @@ -463,7 +461,7 @@ void PasswordManager::request_password_recovery( Promise> promise) { // is called only after authoriation send_with_promise( - G()->net_query_creator().create(create_storer(telegram_api::auth_requestPasswordRecovery())), + G()->net_query_creator().create(telegram_api::auth_requestPasswordRecovery()), PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); if (r_result.is_error()) { @@ -476,7 +474,7 @@ void PasswordManager::request_password_recovery( void PasswordManager::recover_password(string code, Promise promise) { // is called only after authoriation - send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::auth_recoverPassword(std::move(code)))), + send_with_promise(G()->net_query_creator().create(telegram_api::auth_recoverPassword(std::move(code))), PromiseCreator::lambda( [actor_id = actor_id(this), promise = std::move(promise)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); @@ -591,7 +589,7 @@ void PasswordManager::do_update_password_settings_impl(UpdateSettings update_set } auto current_hash = get_input_check_password(state.has_password ? update_settings.current_password : Slice(), state); auto query = G()->net_query_creator().create( - create_storer(telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings)))); + telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings))); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( Result r_query) mutable { @@ -632,7 +630,7 @@ void PasswordManager::get_state(Promise promise) { } void PasswordManager::do_get_state(Promise promise) { - auto query = G()->net_query_creator().create(create_storer(telegram_api::account_getPassword())); + auto query = G()->net_query_creator().create(telegram_api::account_getPassword()); send_with_promise( std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), code_length = last_code_length_, promise = std::move(promise)](Result r_query) mutable { @@ -742,7 +740,7 @@ void PasswordManager::get_ton_wallet_password_salt(Promisenet_query_creator().create(create_storer(telegram_api::wallet_getKeySecretSalt(false))), + send_with_promise(G()->net_query_creator().create(telegram_api::wallet_getKeySecretSalt(false)), PromiseCreator::lambda([actor_id = actor_id(this)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); send_closure(actor_id, &PasswordManager::on_get_ton_wallet_password_salt, std::move(r_result)); diff --git a/td/telegram/Payments.cpp b/td/telegram/Payments.cpp index d964ab79..a73a97fd 100644 --- a/td/telegram/Payments.cpp +++ b/td/telegram/Payments.cpp @@ -12,7 +12,6 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" #include "td/telegram/misc.h" -#include "td/telegram/net/DcId.h" #include "td/telegram/PasswordManager.h" #include "td/telegram/Td.h" #include "td/telegram/UpdatesManager.h" @@ -43,8 +42,8 @@ class SetBotShippingAnswerQuery : public Td::ResultHandler { if (!shipping_options.empty()) { flags |= telegram_api::messages_setBotShippingResults::SHIPPING_OPTIONS_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_setBotShippingResults( - flags, shipping_query_id, error_message, std::move(shipping_options))))); + send_query(G()->net_query_creator().create(telegram_api::messages_setBotShippingResults( + flags, shipping_query_id, error_message, std::move(shipping_options)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -80,8 +79,8 @@ class SetBotPreCheckoutAnswerQuery : public Td::ResultHandler { flags |= telegram_api::messages_setBotPrecheckoutResults::SUCCESS_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_setBotPrecheckoutResults( - flags, false /*ignored*/, pre_checkout_query_id, error_message)))); + send_query(G()->net_query_creator().create(telegram_api::messages_setBotPrecheckoutResults( + flags, false /*ignored*/, pre_checkout_query_id, error_message))); } void on_result(uint64 id, BufferSlice packet) override { @@ -258,8 +257,7 @@ class GetPaymentFormQuery : public Td::ResultHandler { } void send(ServerMessageId server_message_id) { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::payments_getPaymentForm(server_message_id.get())))); + send_query(G()->net_query_creator().create(telegram_api::payments_getPaymentForm(server_message_id.get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -306,8 +304,8 @@ class ValidateRequestedInfoQuery : public Td::ResultHandler { requested_info = make_tl_object(); requested_info->flags_ = 0; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::payments_validateRequestedInfo( - flags, false /*ignored*/, server_message_id.get(), std::move(requested_info))))); + send_query(G()->net_query_creator().create(telegram_api::payments_validateRequestedInfo( + flags, false /*ignored*/, server_message_id.get(), std::move(requested_info)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -347,8 +345,8 @@ class SendPaymentFormQuery : public Td::ResultHandler { if (!shipping_option_id.empty()) { flags |= telegram_api::payments_sendPaymentForm::SHIPPING_OPTION_ID_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::payments_sendPaymentForm( - flags, server_message_id.get(), order_info_id, shipping_option_id, std::move(input_credentials))))); + send_query(G()->net_query_creator().create(telegram_api::payments_sendPaymentForm( + flags, server_message_id.get(), order_info_id, shipping_option_id, std::move(input_credentials)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -391,8 +389,7 @@ class GetPaymentReceiptQuery : public Td::ResultHandler { } void send(ServerMessageId server_message_id) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::payments_getPaymentReceipt(server_message_id.get())))); + send_query(G()->net_query_creator().create(telegram_api::payments_getPaymentReceipt(server_message_id.get()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -434,7 +431,7 @@ class GetSavedInfoQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::payments_getSavedInfo()))); + send_query(G()->net_query_creator().create(telegram_api::payments_getSavedInfo())); } void on_result(uint64 id, BufferSlice packet) override { @@ -470,7 +467,7 @@ class ClearSavedInfoQuery : public Td::ResultHandler { flags |= telegram_api::payments_clearSavedInfo::INFO_MASK; } send_query(G()->net_query_creator().create( - create_storer(telegram_api::payments_clearSavedInfo(flags, false /*ignored*/, false /*ignored*/)))); + telegram_api::payments_clearSavedInfo(flags, false /*ignored*/, false /*ignored*/))); } void on_result(uint64 id, BufferSlice packet) override { @@ -486,6 +483,37 @@ class ClearSavedInfoQuery : public Td::ResultHandler { promise_.set_error(std::move(status)); } }; + +class GetBankCardInfoQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetBankCardInfoQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &bank_card_number) { + send_query(G()->net_query_creator().create(telegram_api::payments_getBankCardData(bank_card_number), + G()->get_webfile_dc_id())); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto response = result_ptr.move_as_ok(); + auto actions = transform(response->open_urls_, [](auto &open_url) { + return td_api::make_object(open_url->name_, open_url->url_); + }); + promise_.set_value(td_api::make_object(response->title_, std::move(actions))); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; /* class SendLiteRequestQuery : public Td::ResultHandler { Promise> promise_; @@ -496,8 +524,7 @@ class SendLiteRequestQuery : public Td::ResultHandler { } void send(BufferSlice request) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::wallet_sendLiteRequest(std::move(request))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + send_query(G()->net_query_creator().create_unauth(telegram_api::wallet_sendLiteRequest(std::move(request)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -666,7 +693,7 @@ Result
address_from_json(Slice json) { auto value = r_value.move_as_ok(); if (value.type() != JsonValue::Type::Object) { - return Status::Error(400, "Address should be an Object"); + return Status::Error(400, "Address must be an Object"); } auto &object = value.get_object(); @@ -896,6 +923,10 @@ void delete_saved_order_info(Promise &&promise) { void delete_saved_credentials(Promise &&promise) { G()->td().get_actor_unsafe()->create_handler(std::move(promise))->send(true, false); } + +void get_bank_card_info(const string &bank_card_number, Promise> &&promise) { + G()->td().get_actor_unsafe()->create_handler(std::move(promise))->send(bank_card_number); +} /* void send_ton_lite_server_request(Slice request, Promise> &&promise) { G()->td().get_actor_unsafe()->create_handler(std::move(promise))->send(BufferSlice{request}); diff --git a/td/telegram/Payments.h b/td/telegram/Payments.h index 075af444..afdd948a 100644 --- a/td/telegram/Payments.h +++ b/td/telegram/Payments.h @@ -154,6 +154,8 @@ void delete_saved_order_info(Promise &&promise); void delete_saved_credentials(Promise &&promise); +void get_bank_card_info(const string &bank_card_number, Promise> &&promise); + // void send_ton_lite_server_request(Slice request, Promise> &&promise); } // namespace td diff --git a/td/telegram/PhoneNumberManager.cpp b/td/telegram/PhoneNumberManager.cpp index 8c387c18..7c3038ce 100644 --- a/td/telegram/PhoneNumberManager.cpp +++ b/td/telegram/PhoneNumberManager.cpp @@ -7,7 +7,6 @@ #include "td/telegram/PhoneNumberManager.h" #include "td/telegram/Global.h" -#include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/Td.h" #include "td/telegram/td_api.h" @@ -39,7 +38,7 @@ PhoneNumberManager::PhoneNumberManager(PhoneNumberManager::Type type, ActorShare template void PhoneNumberManager::process_send_code_result(uint64 query_id, const T &send_code) { on_new_query(query_id); - start_net_query(NetQueryType::SendCode, G()->net_query_creator().create(create_storer(send_code))); + start_net_query(NetQueryType::SendCode, G()->net_query_creator().create(send_code)); } void PhoneNumberManager::set_phone_number(uint64 query_id, string phone_number, Settings settings) { @@ -90,14 +89,12 @@ void PhoneNumberManager::resend_authentication_code(uint64 query_id) { on_new_query(query_id); - start_net_query(NetQueryType::SendCode, - G()->net_query_creator().create(create_storer(r_resend_code.move_as_ok()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth(r_resend_code.move_as_ok())); } template void PhoneNumberManager::send_new_check_code_query(const T &query) { - start_net_query(NetQueryType::CheckCode, G()->net_query_creator().create(create_storer(query))); + start_net_query(NetQueryType::CheckCode, G()->net_query_creator().create(query)); } void PhoneNumberManager::check_code(uint64 query_id, string code) { diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index 26792f5a..a8cf6f48 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -64,8 +64,8 @@ class GetPollResultsQuery : public Td::ResultHandler { } auto message_id = full_message_id.get_message_id().get_server_message_id().get(); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getPollResults(std::move(input_peer), message_id)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getPollResults(std::move(input_peer), message_id))); } void on_result(uint64 id, BufferSlice packet) override { @@ -112,8 +112,8 @@ class GetPollVotersQuery : public Td::ResultHandler { } auto message_id = full_message_id.get_message_id().get_server_message_id().get(); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getPollVotes( - flags, std::move(input_peer), message_id, std::move(option), offset, limit)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getPollVotes( + flags, std::move(input_peer), message_id, std::move(option), offset, limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -152,7 +152,7 @@ class SetPollAnswerActor : public NetActorOnce { auto message_id = full_message_id.get_message_id().get_server_message_id().get(); auto query = G()->net_query_creator().create( - create_storer(telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options)))); + telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options))); *query_ref = query.get_weak(); auto sequence_id = -1; send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, @@ -203,9 +203,9 @@ class StopPollActor : public NetActorOnce { poll->flags_ |= telegram_api::poll::CLOSED_MASK; auto input_media = telegram_api::make_object(0, std::move(poll), vector()); - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_editMessage( + auto query = G()->net_query_creator().create(telegram_api::messages_editMessage( flags, false /*ignored*/, std::move(input_peer), message_id, string(), std::move(input_media), - std::move(input_reply_markup), vector>(), 0))); + std::move(input_reply_markup), vector>(), 0)); auto sequence_id = -1; send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); @@ -314,6 +314,7 @@ string PollManager::get_poll_database_key(PollId poll_id) { void PollManager::save_poll(const Poll *poll, PollId poll_id) { CHECK(!is_local_poll_id(poll_id)); + poll->was_saved = true; if (!G()->parameters().use_message_db) { return; @@ -570,7 +571,7 @@ PollId PollManager::create_poll(string &&question, vector &&options, boo return poll_id; } -void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id) { +void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id, const char *source) { CHECK(have_poll(poll_id)); if (full_message_id.get_message_id().is_scheduled()) { return; @@ -578,15 +579,15 @@ void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id) { if (!full_message_id.get_message_id().is_server()) { return; } - LOG(INFO) << "Register " << poll_id << " from " << full_message_id; + LOG(INFO) << "Register " << poll_id << " from " << full_message_id << " from " << source; bool is_inserted = poll_messages_[poll_id].insert(full_message_id).second; - CHECK(is_inserted); + LOG_CHECK(is_inserted) << source << " " << poll_id << " " << full_message_id; if (!td_->auth_manager_->is_bot() && !is_local_poll_id(poll_id) && !get_poll_is_closed(poll_id)) { update_poll_timeout_.add_timeout_in(poll_id.get(), 0); } } -void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id) { +void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id, const char *source) { CHECK(have_poll(poll_id)); if (full_message_id.get_message_id().is_scheduled()) { return; @@ -594,10 +595,10 @@ void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id) if (!full_message_id.get_message_id().is_server()) { return; } - LOG(INFO) << "Unregister " << poll_id << " from " << full_message_id; + LOG(INFO) << "Unregister " << poll_id << " from " << full_message_id << " from " << source; auto &message_ids = poll_messages_[poll_id]; auto is_deleted = message_ids.erase(full_message_id); - CHECK(is_deleted); + LOG_CHECK(is_deleted) << source << " " << poll_id << " " << full_message_id; if (message_ids.empty()) { poll_messages_.erase(poll_id); update_poll_timeout_.cancel_timeout(poll_id.get()); @@ -788,6 +789,10 @@ void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation, auto promises = std::move(pending_answer.promises_); pending_answers_.erase(it); + auto poll = get_poll(poll_id); + if (poll != nullptr) { + poll->was_saved = false; + } if (result.is_ok()) { td_->updates_manager_->on_get_updates(result.move_as_ok()); @@ -799,6 +804,17 @@ void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation, promise.set_error(result.error().clone()); } } + if (poll != nullptr && !poll->was_saved) { + // no updates was sent during updates processing, so send them + // poll wasn't changed, so there is no reason to actually save it + if (!poll->is_closed) { + LOG(INFO) << "Schedule updating of " << poll_id << " soon"; + update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0); + } + + notify_on_poll_update(poll_id); + poll->was_saved = true; + } } void PollManager::invalidate_poll_voters(const Poll *poll, PollId poll_id) { @@ -973,6 +989,10 @@ void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, int32 limi if (static_cast(user_ids.size()) > limit) { user_ids.resize(limit); } + if (voters.next_offset.empty() && narrow_cast(voters.voter_user_ids.size()) != vote_list->count_) { + // invalidate_poll_option_voters(poll, poll_id, option_id); + voters.was_invalidated = true; + } for (auto &promise : promises) { promise.set_value({vote_list->count_, vector(user_ids)}); @@ -1336,7 +1356,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr recent_voter_user_ids; - if (!td_->auth_manager_->is_bot()) { + if (!is_bot) { for (auto &user_id_int : poll_results->recent_voters_) { UserId user_id(user_id_int); if (user_id.is_valid()) { diff --git a/td/telegram/PollManager.h b/td/telegram/PollManager.h index 36ef5c06..8112fff4 100644 --- a/td/telegram/PollManager.h +++ b/td/telegram/PollManager.h @@ -47,9 +47,9 @@ class PollManager : public Actor { PollId create_poll(string &&question, vector &&options, bool is_anonymous, bool allow_multiple_answers, bool is_quiz, int32 correct_option_id, bool is_closed); - void register_poll(PollId poll_id, FullMessageId full_message_id); + void register_poll(PollId poll_id, FullMessageId full_message_id, const char *source); - void unregister_poll(PollId poll_id, FullMessageId full_message_id); + void unregister_poll(PollId poll_id, FullMessageId full_message_id, const char *source); bool get_poll_is_closed(PollId poll_id) const; @@ -112,6 +112,7 @@ class PollManager : public Actor { bool allow_multiple_answers = false; bool is_quiz = false; bool is_closed = false; + mutable bool was_saved = false; template void store(StorerT &storer) const; diff --git a/td/telegram/PrivacyManager.cpp b/td/telegram/PrivacyManager.cpp index 322f6bf5..e44bae5f 100644 --- a/td/telegram/PrivacyManager.cpp +++ b/td/telegram/PrivacyManager.cpp @@ -27,7 +27,7 @@ namespace td { Result PrivacyManager::UserPrivacySetting::from_td_api( tl_object_ptr key) { if (!key) { - return Status::Error(5, "UserPrivacySetting should not be empty"); + return Status::Error(5, "UserPrivacySetting must be non-empty"); } return UserPrivacySetting(*key); } @@ -368,12 +368,12 @@ Result PrivacyManager::UserPrivacySetti Result PrivacyManager::UserPrivacySettingRules::from_td_api( tl_object_ptr rules) { if (!rules) { - return Status::Error(5, "UserPrivacySettingRules should not be empty"); + return Status::Error(5, "UserPrivacySettingRules must be non-empty"); } UserPrivacySettingRules result; for (auto &rule : rules->rules_) { if (!rule) { - return Status::Error(5, "UserPrivacySettingRule should not be empty"); + return Status::Error(5, "UserPrivacySettingRule must be non-empty"); } result.rules_.emplace_back(*rule); } @@ -409,8 +409,8 @@ void PrivacyManager::get_privacy(tl_object_ptr key, // query has already been sent, just wait for the result return; } - auto net_query = G()->net_query_creator().create( - create_storer(telegram_api::account_getPrivacy(user_privacy_setting.as_telegram_api()))); + auto net_query = + G()->net_query_creator().create(telegram_api::account_getPrivacy(user_privacy_setting.as_telegram_api())); send_with_promise(std::move(net_query), PromiseCreator::lambda([this, user_privacy_setting](Result x_net_query) { @@ -442,8 +442,8 @@ void PrivacyManager::set_privacy(tl_object_ptr key, // TODO cancel previous query return promise.set_error(Status::Error(5, "Another set_privacy query is active")); } - auto net_query = G()->net_query_creator().create(create_storer( - telegram_api::account_setPrivacy(user_privacy_setting.as_telegram_api(), privacy_rules.as_telegram_api()))); + auto net_query = G()->net_query_creator().create( + telegram_api::account_setPrivacy(user_privacy_setting.as_telegram_api(), privacy_rules.as_telegram_api())); info.has_set_query = true; send_with_promise(std::move(net_query), diff --git a/td/telegram/RequestActor.h b/td/telegram/RequestActor.h index 9e1e8f82..f32a7624 100644 --- a/td/telegram/RequestActor.h +++ b/td/telegram/RequestActor.h @@ -30,13 +30,15 @@ class RequestActor : public Actor { } void loop() override { - PromiseActor promise; + PromiseActor promise_actor; FutureActor future; - init_promise_future(&promise, &future); + init_promise_future(&promise_actor, &future); - do_run(PromiseCreator::from_promise_actor(std::move(promise))); + auto promise = PromiseCreator::from_promise_actor(std::move(promise_actor)); + do_run(std::move(promise)); if (future.is_ready()) { + CHECK(!promise); if (future.is_error()) { do_send_error(future.move_as_error()); } else { @@ -45,6 +47,9 @@ class RequestActor : public Actor { } stop(); } else { + LOG_CHECK(!promise.was_set_value) << future.empty() << " " << future.get_state(); + CHECK(!future.empty()); + CHECK(future.get_state() == FutureActor::State::Waiting); if (--tries_left_ == 0) { future.close(); do_send_error(Status::Error(400, "Requested data is inaccessible")); diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 0c85c8e3..1017d26b 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/SecretChatActor.h" +#include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/ServerMessageId.h" @@ -68,6 +69,12 @@ SecretChatActor::SecretChatActor(int32 id, unique_ptr context, bool can auth_state_.id = id; } +template +NetQueryPtr SecretChatActor::create_net_query(QueryType type, const T &function) { + return context_->net_query_creator().create(UniqueId::next(UniqueId::Type::Default, static_cast(type)), + function, DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On); +} + void SecretChatActor::update_chat(telegram_api::object_ptr chat) { if (close_flag_) { return; @@ -358,9 +365,8 @@ void SecretChatActor::send_message_action(tl_object_ptrget_id() != secret_api::sendMessageCancelAction::ID; - auto net_query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Ignore)), - create_storer(telegram_api::messages_setEncryptedTyping(get_input_chat(), flag))); + auto net_query = + create_net_query(QueryType::Ignore, telegram_api::messages_setEncryptedTyping(get_input_chat(), flag)); if (!set_typing_query_.empty()) { LOG(INFO) << "Cancel previous set typing query"; cancel_query(set_typing_query_); @@ -390,9 +396,8 @@ void SecretChatActor::send_read_history(int32 date, Promise<> promise) { cancel_query(read_history_query_); } - auto net_query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::ReadHistory)), - create_storer(telegram_api::messages_readEncryptedHistory(get_input_chat(), date))); + auto net_query = + create_net_query(QueryType::ReadHistory, telegram_api::messages_readEncryptedHistory(get_input_chat(), date)); read_history_query_ = net_query.get_weak(); last_read_history_date_ = date; read_history_promise_ = std::move(promise); @@ -566,11 +571,9 @@ Status SecretChatActor::run_auth() { return Status::OK(); } // messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; - telegram_api::messages_requestEncryption tl_query(get_input_user(), auth_state_.random_id, - BufferSlice(auth_state_.handshake.get_g_b())); - auto query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::EncryptedChat)), - create_storer(tl_query)); + auto query = create_net_query(QueryType::EncryptedChat, telegram_api::messages_requestEncryption( + get_input_user(), auth_state_.random_id, + BufferSlice(auth_state_.handshake.get_g_b()))); context_->send_net_query(std::move(query), actor_shared(this), false); auth_state_.state = State::WaitRequestResponse; return Status::OK(); @@ -584,11 +587,10 @@ Status SecretChatActor::run_auth() { pfs_state_.auth_key = mtproto::AuthKey(id_and_key.first, std::move(id_and_key.second)); calc_key_hash(); // messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; - telegram_api::messages_acceptEncryption tl_query(get_input_chat(), BufferSlice(auth_state_.handshake.get_g_b()), - pfs_state_.auth_key.id()); - auto query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::EncryptedChat)), - create_storer(tl_query)); + auto query = create_net_query( + QueryType::EncryptedChat, + telegram_api::messages_acceptEncryption(get_input_chat(), BufferSlice(auth_state_.handshake.get_g_b()), + pfs_state_.auth_key.id())); context_->send_net_query(std::move(query), actor_shared(this), false); auth_state_.state = State::WaitAcceptResponse; return Status::OK(); @@ -756,10 +758,7 @@ void SecretChatActor::do_close_chat_impl(unique_ptr e context_->secret_chat_db()->erase_value(config_state_); context_->secret_chat_db()->erase_value(pfs_state_); context_->secret_chat_db()->erase_value(seq_no_state_); - telegram_api::messages_discardEncryption tl_query(auth_state_.id); - auto query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::DiscardEncryption)), - create_storer(tl_query)); + auto query = create_net_query(QueryType::DiscardEncryption, telegram_api::messages_discardEncryption(auth_state_.id)); send_update_secret_chat(); @@ -895,9 +894,8 @@ Status SecretChatActor::do_inbound_message_encrypted(unique_ptrlayer_; - if (layer < DEFAULT_LAYER && false /*TODO: fix android app bug? */) { - LOG(ERROR) << "All or nothing, " << tag("layer", layer) << " is not supported, drop message " - << to_string(message_with_layer); + if (layer < DEFAULT_LAYER && false /* Android app can send such messages */) { + LOG(ERROR) << "Layer " << layer << " is not supported, drop message " << to_string(message_with_layer); return Status::OK(); } if (config_state_.his_layer < layer) { @@ -917,7 +915,7 @@ Status SecretChatActor::do_inbound_message_encrypted(unique_ptr(data_buffer.as_slice())); } } else { - status = Status::Error(PSLICE() << "Unknown constructor " << tag("ID", format::as_hex(id))); + status = Status::Error(PSLICE() << "Unknown constructor " << format::as_hex(id)); } // support for older layer @@ -1464,22 +1462,19 @@ NetQueryPtr SecretChatActor::create_net_query(const logevent::OutboundSecretMess NetQueryPtr query; if (message.is_service) { CHECK(message.file.empty()); - query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Message)), - create_storer(telegram_api::messages_sendEncryptedService(get_input_chat(), message.random_id, - message.encrypted_message.clone()))); - query->total_timeout_limit = 1000000000; // inf. We will re-sent it immediately anyway. + query = create_net_query(QueryType::Message, + telegram_api::messages_sendEncryptedService(get_input_chat(), message.random_id, + message.encrypted_message.clone())); + query->total_timeout_limit = 1000000000; // inf. We will re-sent it immediately anyway } else if (message.file.empty()) { - query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Message)), - create_storer(telegram_api::messages_sendEncrypted(get_input_chat(), message.random_id, - message.encrypted_message.clone()))); + query = create_net_query( + QueryType::Message, + telegram_api::messages_sendEncrypted(get_input_chat(), message.random_id, message.encrypted_message.clone())); } else { - query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Message)), - create_storer(telegram_api::messages_sendEncryptedFile(get_input_chat(), message.random_id, - message.encrypted_message.clone(), - message.file.as_input_encrypted_file()))); + query = create_net_query( + QueryType::Message, + telegram_api::messages_sendEncryptedFile(get_input_chat(), message.random_id, message.encrypted_message.clone(), + message.file.as_input_encrypted_file())); } if (message.is_external && context_->get_config_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = @@ -1964,11 +1959,8 @@ void SecretChatActor::get_dh_config() { } auto version = auth_state_.dh_config.version; - int random_length = 0; - telegram_api::messages_getDhConfig tl_query(version, random_length); - - auto query = context_->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::DhConfig)), create_storer(tl_query)); + int32 random_length = 0; + auto query = create_net_query(QueryType::DhConfig, telegram_api::messages_getDhConfig(version, random_length)); context_->send_net_query(std::move(query), actor_shared(this), false); } diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index 10b93853..3adbd471 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -18,6 +18,7 @@ #include "td/telegram/DhConfig.h" #include "td/telegram/logevent/SecretChatEvent.h" #include "td/telegram/MessageId.h" +#include "td/telegram/net/NetQuery.h" #include "td/telegram/SecretChatDb.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/UserId.h" @@ -589,6 +590,9 @@ class SecretChatActor : public NetQueryCallback { int32 last_read_history_date_ = -1; Promise read_history_promise_; + template + NetQueryPtr create_net_query(QueryType type, const T &function); + enum SendFlag : int32 { None = 0, External = 1, diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index 857037ba..32dfd4d5 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -136,7 +136,7 @@ class SetSecureValueErrorsQuery : public Td::ResultHandler { void send(tl_object_ptr input_user, vector> input_errors) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::users_setSecureValueErrors(std::move(input_user), std::move(input_errors))))); + telegram_api::users_setSecureValueErrors(std::move(input_user), std::move(input_errors)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -178,7 +178,7 @@ void GetSecureValue::on_error(Status error) { void GetSecureValue::on_secret(Result r_secret, bool dummy) { if (r_secret.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); @@ -208,7 +208,7 @@ void GetSecureValue::start_up() { std::vector> types; types.push_back(get_input_secure_value_type(type_)); - auto query = G()->net_query_creator().create(create_storer(telegram_api::account_getSecureValue(std::move(types)))); + auto query = G()->net_query_creator().create(telegram_api::account_getSecureValue(std::move(types))); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); @@ -257,7 +257,7 @@ void GetAllSecureValues::on_error(Status error) { void GetAllSecureValues::on_secret(Result r_secret, bool dummy) { if (r_secret.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); @@ -288,7 +288,7 @@ void GetAllSecureValues::loop() { } void GetAllSecureValues::start_up() { - auto query = G()->net_query_creator().create(create_storer(telegram_api::account_getAllSecureValues())); + auto query = G()->net_query_creator().create(telegram_api::account_getAllSecureValues()); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); @@ -393,7 +393,7 @@ void SetSecureValue::on_error(Status error) { void SetSecureValue::on_secret(Result r_secret, bool x) { if (r_secret.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); @@ -572,7 +572,7 @@ void SetSecureValue::loop() { files_to_upload_, front_side_, reverse_side_, selfie_, translations_to_upload_); auto save_secure_value = telegram_api::account_saveSecureValue(std::move(input_secure_value), secret_.value().get_hash()); - auto query = G()->net_query_creator().create(create_storer(save_secure_value)); + auto query = G()->net_query_creator().create(save_secure_value); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); state_ = State::WaitSetValue; @@ -664,8 +664,7 @@ class DeleteSecureValue : public NetQueryCallback { void start_up() override { std::vector> types; types.push_back(get_input_secure_value_type(type_)); - auto query = - G()->net_query_creator().create(create_storer(telegram_api::account_deleteSecureValue(std::move(types)))); + auto query = G()->net_query_creator().create(telegram_api::account_deleteSecureValue(std::move(types))); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } @@ -710,7 +709,7 @@ class GetPassportAuthorizationForm : public NetQueryCallback { void start_up() override { auto account_get_authorization_form = telegram_api::account_getAuthorizationForm(bot_user_id_.get(), std::move(scope_), std::move(public_key_)); - auto query = G()->net_query_creator().create(create_storer(account_get_authorization_form)); + auto query = G()->net_query_creator().create(account_get_authorization_form); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } @@ -764,7 +763,7 @@ class GetPassportConfig : public NetQueryCallback { Promise> promise_; void start_up() override { - auto query = G()->net_query_creator().create(create_storer(telegram_api::help_getPassportConfig(0))); + auto query = G()->net_query_creator().create(telegram_api::help_getPassportConfig(0)); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } @@ -1062,7 +1061,7 @@ void SecureManager::on_get_passport_authorization_form_secret(int32 authorizatio if (r_secret.is_error()) { auto error = r_secret.move_as_error(); - if (!G()->close_flag()) { + if (!G()->is_expected_error(error)) { LOG(ERROR) << "Receive error instead of secret: " << error; } if (error.code() <= 0) { @@ -1269,7 +1268,7 @@ void SecureManager::send_passport_authorization_form(int32 authorization_form_id auto td_query = telegram_api::account_acceptAuthorization( it->second.bot_user_id.get(), it->second.scope, it->second.public_key, std::move(hashes), get_secure_credentials_encrypted_object(r_encrypted_credentials.move_as_ok())); - auto query = G()->net_query_creator().create(create_storer(td_query)); + auto query = G()->net_query_creator().create(td_query); auto new_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_net_query_ptr) mutable { auto r_result = fetch_result(std::move(r_net_query_ptr)); diff --git a/td/telegram/SecureStorage.cpp b/td/telegram/SecureStorage.cpp index c2c43aaa..f1ca6be9 100644 --- a/td/telegram/SecureStorage.cpp +++ b/td/telegram/SecureStorage.cpp @@ -225,7 +225,7 @@ Secret Secret::clone() const { } EncryptedSecret Secret::encrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm) { - auto aes_cbc_state = [&]() { + auto aes_cbc_state = [&] { switch (algorithm) { case EnryptionAlgorithm::Sha512: return calc_aes_cbc_state_sha512(PSLICE() << salt << key << salt); @@ -255,7 +255,7 @@ Result EncryptedSecret::create(Slice encrypted_secret) { } Result EncryptedSecret::decrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm) { - auto aes_cbc_state = [&]() { + auto aes_cbc_state = [&] { switch (algorithm) { case EnryptionAlgorithm::Sha512: return calc_aes_cbc_state_sha512(PSLICE() << salt << key << salt); @@ -288,7 +288,7 @@ Result Decryptor::append(BufferSlice data) { return BufferSlice(); } if (data.size() % 16 != 0) { - return Status::Error("Part size should be divisible by 16"); + return Status::Error("Part size must be divisible by 16"); } aes_cbc_state_.decrypt(data.as_slice(), data.as_slice()); sha256_state_.feed(data.as_slice()); @@ -330,7 +330,7 @@ Result Encryptor::pread(int64 offset, int64 size) const { return Status::Error("Arbitrary offset is not supported"); } if (size % 16 != 0) { - return Status::Error("Part size should be divisible by 16"); + return Status::Error("Part size must be divisible by 16"); } TRY_RESULT(part, data_view_.pread(offset, size)); aes_cbc_state_.encrypt(part.as_slice(), part.as_slice()); diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index 470b6d90..11aad991 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -796,7 +796,7 @@ static Result> get_personal_details_ auto value = r_value.move_as_ok(); if (value.type() != JsonValue::Type::Object) { - return Status::Error(400, "Personal details should be an Object"); + return Status::Error(400, "Personal details must be an Object"); } auto &object = value.get_object(); @@ -888,7 +888,7 @@ static Result get_identity_document(SecureValueType type, FileManag } } else { if (!need_reverse_side) { - return Status::Error(400, "Document shouldn't have a reverse side"); + return Status::Error(400, "Document can't have a reverse side"); } } @@ -930,7 +930,7 @@ static Result> get_identity_documen auto json_value = r_value.move_as_ok(); if (json_value.type() != JsonValue::Type::Object) { - return Status::Error(400, "Identity document should be an Object"); + return Status::Error(400, "Identity document must be an Object"); } auto &object = json_value.get_object(); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 843ac9f7..e20ed895 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -61,9 +61,9 @@ class GetAllStickersQuery : public Td::ResultHandler { void send(bool is_masks, int32 hash) { is_masks_ = is_masks; if (is_masks) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getMaskStickers(hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getMaskStickers(hash))); } else { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getAllStickers(hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getAllStickers(hash))); } } @@ -82,7 +82,7 @@ class GetAllStickersQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for get all stickers: " << status; } td->stickers_manager_->on_get_installed_sticker_sets_failed(is_masks_, std::move(status)); @@ -95,7 +95,7 @@ class SearchStickersQuery : public Td::ResultHandler { public: void send(string emoji) { emoji_ = std::move(emoji); - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getStickers(emoji_, 0)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getStickers(emoji_, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -110,7 +110,7 @@ class SearchStickersQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for search stickers: " << status; } td->stickers_manager_->on_find_stickers_fail(emoji_, std::move(status)); @@ -125,8 +125,8 @@ class GetEmojiKeywordsLanguageQuery : public Td::ResultHandler { } void send(vector &&language_codes) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getEmojiKeywordsLanguages(std::move(language_codes))))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getEmojiKeywordsLanguages(std::move(language_codes)))); } void on_result(uint64 id, BufferSlice packet) override { auto result_ptr = fetch_result(packet); @@ -153,7 +153,7 @@ class GetEmojiKeywordsQuery : public Td::ResultHandler { } void send(const string &language_code) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getEmojiKeywords(language_code)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getEmojiKeywords(language_code))); } void on_result(uint64 id, BufferSlice packet) override { @@ -180,8 +180,8 @@ class GetEmojiKeywordsDifferenceQuery : public Td::ResultHandler { } void send(const string &language_code, int32 version) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getEmojiKeywordsDifference(language_code, version)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getEmojiKeywordsDifference(language_code, version))); } void on_result(uint64 id, BufferSlice packet) override { @@ -207,7 +207,7 @@ class GetEmojiUrlQuery : public Td::ResultHandler { } void send(const string &language_code) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getEmojiURL(language_code)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getEmojiURL(language_code))); } void on_result(uint64 id, BufferSlice packet) override { @@ -245,8 +245,8 @@ class GetArchivedStickerSetsQuery : public Td::ResultHandler { } is_masks_ = is_masks; - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_getArchivedStickers(flags, is_masks /*ignored*/, offset_sticker_set_id.get(), limit)))); + send_query(G()->net_query_creator().create( + telegram_api::messages_getArchivedStickers(flags, is_masks /*ignored*/, offset_sticker_set_id.get(), limit))); } void on_result(uint64 id, BufferSlice packet) override { @@ -272,7 +272,7 @@ class GetFeaturedStickerSetsQuery : public Td::ResultHandler { public: void send(int32 hash) { LOG(INFO) << "Get featured sticker sets with hash " << hash; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getFeaturedStickers(hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getFeaturedStickers(hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -304,8 +304,8 @@ class GetAttachedStickerSetsQuery : public Td::ResultHandler { tl_object_ptr &&input_stickered_media) { file_id_ = file_id; file_reference_ = std::move(file_reference); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getAttachedStickers(std::move(input_stickered_media))))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getAttachedStickers(std::move(input_stickered_media)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -354,7 +354,7 @@ class GetRecentStickersQuery : public Td::ResultHandler { } send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getRecentStickers(flags, is_attached /*ignored*/, hash)))); + telegram_api::messages_getRecentStickers(flags, is_attached /*ignored*/, hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -370,7 +370,7 @@ class GetRecentStickersQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for get recent " << (is_attached_ ? "attached " : "") << "stickers: " << status; } td->stickers_manager_->on_get_recent_stickers_failed(is_repair_, is_attached_, std::move(status)); @@ -402,8 +402,8 @@ class SaveRecentStickerQuery : public Td::ResultHandler { flags |= telegram_api::messages_saveRecentSticker::ATTACHED_MASK; } - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_saveRecentSticker(flags, is_attached /*ignored*/, std::move(input_document), unsave)))); + send_query(G()->net_query_creator().create( + telegram_api::messages_saveRecentSticker(flags, is_attached /*ignored*/, std::move(input_document), unsave))); } void on_result(uint64 id, BufferSlice packet) override { @@ -438,7 +438,7 @@ class SaveRecentStickerQuery : public Td::ResultHandler { return; } - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for save recent " << (is_attached_ ? "attached " : "") << "sticker: " << status; } td->stickers_manager_->reload_recent_stickers(is_attached_, true); @@ -462,8 +462,8 @@ class ClearRecentStickersQuery : public Td::ResultHandler { flags |= telegram_api::messages_clearRecentStickers::ATTACHED_MASK; } - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_clearRecentStickers(flags, is_attached /*ignored*/)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_clearRecentStickers(flags, is_attached /*ignored*/))); } void on_result(uint64 id, BufferSlice packet) override { @@ -482,7 +482,7 @@ class ClearRecentStickersQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for clear recent " << (is_attached_ ? "attached " : "") << "stickers: " << status; } td->stickers_manager_->reload_recent_stickers(is_attached_, true); @@ -497,7 +497,7 @@ class GetFavedStickersQuery : public Td::ResultHandler { void send(bool is_repair, int32 hash) { is_repair_ = is_repair; LOG(INFO) << "Send get favorite stickers request with hash = " << hash; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getFavedStickers(hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getFavedStickers(hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -511,7 +511,7 @@ class GetFavedStickersQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for get favorite stickers: " << status; } td->stickers_manager_->on_get_favorite_stickers_failed(is_repair_, std::move(status)); @@ -536,8 +536,7 @@ class FaveStickerQuery : public Td::ResultHandler { file_reference_ = input_document->file_reference_.as_slice().str(); unsave_ = unsave; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_faveSticker(std::move(input_document), unsave)))); + send_query(G()->net_query_creator().create(telegram_api::messages_faveSticker(std::move(input_document), unsave))); } void on_result(uint64 id, BufferSlice packet) override { @@ -572,7 +571,7 @@ class FaveStickerQuery : public Td::ResultHandler { return; } - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for fave sticker: " << status; } td->stickers_manager_->reload_favorite_stickers(true); @@ -590,8 +589,8 @@ class ReorderStickerSetsQuery : public Td::ResultHandler { if (is_masks) { flags |= telegram_api::messages_reorderStickerSets::MASKS_MASK; } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_reorderStickerSets( - flags, is_masks /*ignored*/, StickersManager::convert_sticker_set_ids(sticker_set_ids))))); + send_query(G()->net_query_creator().create(telegram_api::messages_reorderStickerSets( + flags, is_masks /*ignored*/, StickersManager::convert_sticker_set_ids(sticker_set_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -607,10 +606,10 @@ class ReorderStickerSetsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for ReorderStickerSetsQuery: " << status; - td->stickers_manager_->reload_installed_sticker_sets(is_masks_, true); } + td->stickers_manager_->reload_installed_sticker_sets(is_masks_, true); } }; @@ -630,8 +629,7 @@ class GetStickerSetQuery : public Td::ResultHandler { static_cast(input_sticker_set.get())->short_name_; } LOG(INFO) << "Load " << sticker_set_id << " from server: " << to_string(input_sticker_set); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getStickerSet(std::move(input_sticker_set))))); + send_query(G()->net_query_creator().create(telegram_api::messages_getStickerSet(std::move(input_sticker_set)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -664,11 +662,24 @@ class GetStickerSetQuery : public Td::ResultHandler { } }; -class ReloadAnimatedEmojiStickerSetQuery : public Td::ResultHandler { +class ReloadSpecialStickerSetQuery : public Td::ResultHandler { + StickersManager::SpecialStickerSetType type_; + public: - void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getStickerSet( - telegram_api::make_object())))); + void send(StickersManager::SpecialStickerSetType type) { + type_ = type; + auto input_sticker_set = [type]() -> telegram_api::object_ptr { + switch (type) { + case StickersManager::SpecialStickerSetType::AnimatedEmoji: + return telegram_api::make_object(); + case StickersManager::SpecialStickerSetType::AnimatedDice: + return telegram_api::make_object(); + default: + UNREACHABLE(); + return nullptr; + } + }(); + send_query(G()->net_query_creator().create(telegram_api::messages_getStickerSet(std::move(input_sticker_set)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -677,15 +688,15 @@ class ReloadAnimatedEmojiStickerSetQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - auto sticker_set_id = td->stickers_manager_->on_get_messages_sticker_set( - StickerSetId(), result_ptr.move_as_ok(), true, "ReloadAnimatedEmojiStickerSetQuery"); + auto sticker_set_id = td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), + true, "ReloadSpecialStickerSetQuery"); if (sticker_set_id.is_valid()) { - td->stickers_manager_->on_get_animated_emoji_sticker_set(sticker_set_id); + td->stickers_manager_->on_get_special_sticker_set(sticker_set_id, type_); } } void on_error(uint64 id, Status status) override { - LOG(WARNING) << "Receive error for ReloadAnimatedEmojiStickerSetQuery: " << status; + LOG(WARNING) << "Receive error for ReloadSpecialStickerSetQuery: " << status; } }; @@ -695,8 +706,8 @@ class SearchStickerSetsQuery : public Td::ResultHandler { public: void send(string query) { query_ = std::move(query); - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_searchStickerSets(0, false /*ignored*/, query_, 0)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_searchStickerSets(0, false /*ignored*/, query_, 0))); } void on_result(uint64 id, BufferSlice packet) override { @@ -711,7 +722,7 @@ class SearchStickerSetsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for search sticker sets: " << status; } td->stickers_manager_->on_find_sticker_sets_fail(query_, std::move(status)); @@ -730,8 +741,8 @@ class InstallStickerSetQuery : public Td::ResultHandler { void send(StickerSetId set_id, tl_object_ptr &&input_set, bool is_archived) { set_id_ = set_id; is_archived_ = is_archived; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_installStickerSet(std::move(input_set), is_archived)))); + send_query( + G()->net_query_creator().create(telegram_api::messages_installStickerSet(std::move(input_set), is_archived))); } void on_result(uint64 id, BufferSlice packet) override { @@ -761,8 +772,7 @@ class UninstallStickerSetQuery : public Td::ResultHandler { void send(StickerSetId set_id, tl_object_ptr &&input_set) { set_id_ = set_id; - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_uninstallStickerSet(std::move(input_set))))); + send_query(G()->net_query_creator().create(telegram_api::messages_uninstallStickerSet(std::move(input_set)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -791,8 +801,8 @@ class ReadFeaturedStickerSetsQuery : public Td::ResultHandler { public: void send(vector sticker_set_ids) { LOG(INFO) << "Read featured sticker sets " << format::as_array(sticker_set_ids); - send_query(G()->net_query_creator().create(create_storer( - telegram_api::messages_readFeaturedStickers(StickersManager::convert_sticker_set_ids(sticker_set_ids))))); + send_query(G()->net_query_creator().create( + telegram_api::messages_readFeaturedStickers(StickersManager::convert_sticker_set_ids(sticker_set_ids)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -806,7 +816,7 @@ class ReadFeaturedStickerSetsQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for ReadFeaturedStickerSetsQuery: " << status; } td->stickers_manager_->reload_featured_sticker_sets(true); @@ -829,7 +839,7 @@ class UploadStickerFileQuery : public Td::ResultHandler { file_id_ = file_id; was_uploaded_ = FileManager::extract_was_uploaded(input_media); send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_uploadMedia(std::move(input_peer), std::move(input_media))))); + telegram_api::messages_uploadMedia(std::move(input_peer), std::move(input_media)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -869,16 +879,21 @@ class CreateNewStickerSetQuery : public Td::ResultHandler { } void send(tl_object_ptr &&input_user, const string &title, const string &short_name, - bool is_masks, vector> &&input_stickers) { + bool is_masks, bool is_animated, + vector> &&input_stickers) { CHECK(input_user != nullptr); int32 flags = 0; if (is_masks) { flags |= telegram_api::stickers_createStickerSet::MASKS_MASK; } + if (is_animated) { + flags |= telegram_api::stickers_createStickerSet::ANIMATED_MASK; + } - send_query(G()->net_query_creator().create(create_storer(telegram_api::stickers_createStickerSet( - flags, false /*ignored*/, std::move(input_user), title, short_name, std::move(input_stickers))))); + send_query(G()->net_query_creator().create( + telegram_api::stickers_createStickerSet(flags, false /*ignored*/, false /*ignored*/, std::move(input_user), + title, short_name, nullptr, std::move(input_stickers)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -907,8 +922,8 @@ class AddStickerToSetQuery : public Td::ResultHandler { } void send(const string &short_name, tl_object_ptr &&input_sticker) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::stickers_addStickerToSet( - make_tl_object(short_name), std::move(input_sticker))))); + send_query(G()->net_query_creator().create(telegram_api::stickers_addStickerToSet( + make_tl_object(short_name), std::move(input_sticker)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -929,6 +944,36 @@ class AddStickerToSetQuery : public Td::ResultHandler { } }; +class SetStickerSetThumbnailQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit SetStickerSetThumbnailQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(const string &short_name, tl_object_ptr &&input_document) { + send_query(G()->net_query_creator().create(telegram_api::stickers_setStickerSetThumb( + make_tl_object(short_name), std::move(input_document)))); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, + "SetStickerSetThumbnailQuery"); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + CHECK(status.is_error()); + promise_.set_error(std::move(status)); + } +}; + class SetStickerPositionQuery : public Td::ResultHandler { Promise promise_; @@ -938,7 +983,7 @@ class SetStickerPositionQuery : public Td::ResultHandler { void send(tl_object_ptr &&input_document, int32 position) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::stickers_changeStickerPosition(std::move(input_document), position)))); + telegram_api::stickers_changeStickerPosition(std::move(input_document), position))); } void on_result(uint64 id, BufferSlice packet) override { @@ -967,8 +1012,7 @@ class DeleteStickerFromSetQuery : public Td::ResultHandler { } void send(tl_object_ptr &&input_document) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::stickers_removeStickerFromSet(std::move(input_document))))); + send_query(G()->net_query_creator().create(telegram_api::stickers_removeStickerFromSet(std::move(input_document)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1068,44 +1112,57 @@ StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent void StickersManager::start_up() { // add animated emoji sticker set + special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji].type_ = "animated_emoji_sticker_set"; + special_sticker_sets_[SpecialStickerSetType::AnimatedDice].type_ = "animated_dice_sticker_set"; if (G()->is_test_dc()) { - int64 sticker_set_id_int = 1258816259751954; - animated_emoji_sticker_set_id_ = StickerSetId(sticker_set_id_int); - animated_emoji_sticker_set_access_hash_ = 4879754868529595811; - animated_emoji_sticker_set_name_ = "emojies"; + init_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji], 1258816259751954, + 4879754868529595811, "emojies"); + init_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedDice], 1258816259751954, + 4879754868529595811, "emojies"); } else { - int64 sticker_set_id_int = 1258816259751983; - animated_emoji_sticker_set_id_ = StickerSetId(sticker_set_id_int); - animated_emoji_sticker_set_access_hash_ = 5100237018658464041; - animated_emoji_sticker_set_name_ = "animatedemojies"; + init_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji], 1258816259751983, + 5100237018658464041, "AnimatedEmojies"); + init_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedDice], 1258816259751985, + 5078269940036727612, "AnimatedDices"); } + load_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji]); + load_special_sticker_set(special_sticker_sets_[SpecialStickerSetType::AnimatedDice]); +} + +void StickersManager::init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash, + string name) { + sticker_set.id_ = StickerSetId(sticker_set_id); + sticker_set.access_hash_ = access_hash; + sticker_set.name_ = std::move(name); +} + +void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) { if (G()->parameters().use_file_db) { - string animated_emoji_sticker_set_string = G()->td_db()->get_binlog_pmc()->get("animated_emoji_sticker_set"); - if (!animated_emoji_sticker_set_string.empty()) { - auto parts = full_split(animated_emoji_sticker_set_string); + string sticker_set_string = G()->td_db()->get_binlog_pmc()->get(sticker_set.type_); + if (!sticker_set_string.empty()) { + auto parts = full_split(sticker_set_string); if (parts.size() != 3) { - LOG(ERROR) << "Can't parse " << animated_emoji_sticker_set_string; + LOG(ERROR) << "Can't parse " << sticker_set_string; } else { auto r_sticker_set_id = to_integer_safe(parts[0]); auto r_sticker_set_access_hash = to_integer_safe(parts[1]); auto sticker_set_name = parts[2]; if (r_sticker_set_id.is_error() || r_sticker_set_access_hash.is_error() || clean_username(sticker_set_name) != sticker_set_name || sticker_set_name.empty()) { - LOG(ERROR) << "Can't parse " << animated_emoji_sticker_set_string; + LOG(ERROR) << "Can't parse " << sticker_set_string; } else { - animated_emoji_sticker_set_id_ = StickerSetId(r_sticker_set_id.ok()); - animated_emoji_sticker_set_access_hash_ = r_sticker_set_access_hash.ok(); - animated_emoji_sticker_set_name_ = std::move(sticker_set_name); + init_special_sticker_set(sticker_set, r_sticker_set_id.ok(), r_sticker_set_access_hash.ok(), + std::move(sticker_set_name)); } } } } else { - G()->td_db()->get_binlog_pmc()->erase("animated_emoji_sticker_set"); + G()->td_db()->get_binlog_pmc()->erase(sticker_set.type_); } - add_sticker_set(animated_emoji_sticker_set_id_, animated_emoji_sticker_set_access_hash_); - short_name_to_sticker_set_id_.emplace(animated_emoji_sticker_set_name_, animated_emoji_sticker_set_id_); - G()->shared_config().set_option_string("animated_emoji_sticker_set_name", animated_emoji_sticker_set_name_); + add_sticker_set(sticker_set.id_, sticker_set.access_hash_); + short_name_to_sticker_set_id_.emplace(sticker_set.name_, sticker_set.id_); + G()->shared_config().set_option_string(PSLICE() << sticker_set.type_ << "_name", sticker_set.name_); } void StickersManager::tear_down() { @@ -1410,6 +1467,12 @@ StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptr(set_ptr.get())->short_name_, Auto()); + case telegram_api::inputStickerSetAnimatedEmoji::ID: + LOG(ERROR) << "Receive animated emoji sticker set"; + return special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji].id_; + case telegram_api::inputStickerSetDice::ID: + LOG(ERROR) << "Receive dice sticker set"; + return special_sticker_sets_[SpecialStickerSetType::AnimatedDice].id_; default: UNREACHABLE(); return StickerSetId(); @@ -1432,6 +1495,12 @@ StickerSetId StickersManager::add_sticker_set(tl_object_ptrshort_name_, Auto()); } + case telegram_api::inputStickerSetAnimatedEmoji::ID: + LOG(ERROR) << "Receive animated emoji sticker set"; + return special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji].id_; + case telegram_api::inputStickerSetDice::ID: + LOG(ERROR) << "Receive dice sticker set"; + return special_sticker_sets_[SpecialStickerSetType::AnimatedDice].id_; default: UNREACHABLE(); return StickerSetId(); @@ -1522,7 +1591,8 @@ bool StickersManager::merge_stickers(FileId new_id, FileId old_id, bool can_dele CHECK(new_ != nullptr); if (old_->alt != new_->alt || old_->set_id != new_->set_id || - (old_->dimensions.width != 0 && old_->dimensions.height != 0 && old_->dimensions != new_->dimensions)) { + (!old_->is_animated && !new_->is_animated && old_->dimensions.width != 0 && old_->dimensions.height != 0 && + old_->dimensions != new_->dimensions)) { LOG(ERROR) << "Sticker has changed: alt = (" << old_->alt << ", " << new_->alt << "), set_id = (" << old_->set_id << ", " << new_->set_id << "), dimensions = (" << old_->dimensions << ", " << new_->dimensions << ")"; } @@ -1607,6 +1677,10 @@ StickerSetId StickersManager::on_get_input_sticker_set(FileId sticker_file_id, } return set_id; } + case telegram_api::inputStickerSetAnimatedEmoji::ID: + return special_sticker_sets_[SpecialStickerSetType::AnimatedEmoji].id_; + case telegram_api::inputStickerSetDice::ID: + return special_sticker_sets_[SpecialStickerSetType::AnimatedDice].id_; default: UNREACHABLE(); return StickerSetId(); @@ -2021,10 +2095,10 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s document_id_to_sticker_id.insert(sticker_id); } } - if (static_cast(s->sticker_ids.size()) != s->sticker_count) { + if (static_cast(s->sticker_ids.size()) != s->sticker_count) { LOG(ERROR) << "Wrong sticker set size " << s->sticker_count << " instead of " << s->sticker_ids.size() << " specified in " << set_id << " from " << source; - s->sticker_count = static_cast(s->sticker_ids.size()); + s->sticker_count = static_cast(s->sticker_ids.size()); } if (!is_bot) { @@ -2106,26 +2180,25 @@ void StickersManager::update_load_request(uint32 load_request_id, const Status & } } -void StickersManager::on_get_animated_emoji_sticker_set(StickerSetId sticker_set_id) { +void StickersManager::on_get_special_sticker_set(StickerSetId sticker_set_id, SpecialStickerSetType type) { auto s = get_sticker_set(sticker_set_id); CHECK(s != nullptr); CHECK(s->is_inited); CHECK(s->is_loaded); - if (sticker_set_id == animated_emoji_sticker_set_id_ && s->short_name == animated_emoji_sticker_set_name_ && - !s->short_name.empty()) { + auto &sticker_set = special_sticker_sets_[type]; + if (sticker_set_id == sticker_set.id_ && s->short_name == sticker_set.name_ && !s->short_name.empty()) { return; } - animated_emoji_sticker_set_id_ = sticker_set_id; - animated_emoji_sticker_set_access_hash_ = s->access_hash; - animated_emoji_sticker_set_name_ = clean_username(s->short_name); + sticker_set.id_ = sticker_set_id; + sticker_set.access_hash_ = s->access_hash; + sticker_set.name_ = clean_username(s->short_name); - G()->td_db()->get_binlog_pmc()->set("animated_emoji_sticker_set", PSTRING() - << animated_emoji_sticker_set_id_.get() << ' ' - << animated_emoji_sticker_set_access_hash_ - << ' ' << animated_emoji_sticker_set_name_); - G()->shared_config().set_option_string("animated_emoji_sticker_set_name", animated_emoji_sticker_set_name_); + G()->td_db()->get_binlog_pmc()->set(sticker_set.type_, PSTRING() + << sticker_set.id_.get() << ' ' << sticker_set.access_hash_ + << ' ' << sticker_set.name_); + G()->shared_config().set_option_string(PSLICE() << sticker_set.type_ << "_name", sticker_set.name_); } void StickersManager::on_get_installed_sticker_sets(bool is_masks, @@ -3507,27 +3580,54 @@ void StickersManager::reorder_installed_sticker_sets(bool is_masks, const vector promise.set_value(Unit()); } -Result> StickersManager::prepare_input_sticker(td_api::inputSticker *sticker) { +string &StickersManager::get_input_sticker_emojis(td_api::InputSticker *sticker) { + CHECK(sticker != nullptr); + auto constructor_id = sticker->get_id(); + if (constructor_id == td_api::inputStickerStatic::ID) { + return static_cast(sticker)->emojis_; + } + CHECK(constructor_id == td_api::inputStickerAnimated::ID); + return static_cast(sticker)->emojis_; +} + +Result> StickersManager::prepare_input_sticker(td_api::InputSticker *sticker) { if (sticker == nullptr) { - return Status::Error(3, "Input sticker shouldn't be empty"); + return Status::Error(3, "Input sticker must not be empty"); } - if (!clean_input_string(sticker->emojis_)) { + if (!clean_input_string(get_input_sticker_emojis(sticker))) { return Status::Error(400, "Emojis must be encoded in UTF-8"); } - return prepare_input_file(sticker->png_sticker_); + switch (sticker->get_id()) { + case td_api::inputStickerStatic::ID: + return prepare_input_file(static_cast(sticker)->sticker_, false, false); + case td_api::inputStickerAnimated::ID: + return prepare_input_file(static_cast(sticker)->sticker_, true, false); + default: + UNREACHABLE(); + return {}; + } } -Result> StickersManager::prepare_input_file( - const tl_object_ptr &input_file) { - auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Document, input_file, {}, false, false, false); +Result> StickersManager::prepare_input_file( + const tl_object_ptr &input_file, bool is_animated, bool for_thumbnail) { + auto r_file_id = td_->file_manager_->get_input_file_id(is_animated ? FileType::Sticker : FileType::Document, + input_file, DialogId(), for_thumbnail, false); if (r_file_id.is_error()) { return Status::Error(7, r_file_id.error().message()); } auto file_id = r_file_id.move_as_ok(); + if (file_id.empty()) { + return std::make_tuple(FileId(), false, false, false); + } - td_->documents_manager_->create_document(file_id, string(), PhotoSize(), "sticker.png", "image/png", false); + if (is_animated) { + int32 width = for_thumbnail ? 100 : 512; + td_->stickers_manager_->create_sticker(file_id, PhotoSize(), get_dimensions(width, width), nullptr, true, nullptr); + } else { + td_->documents_manager_->create_document(file_id, string(), PhotoSize(), "sticker.png", "image/png", false); + } FileView file_view = td_->file_manager_->get_file_view(file_id); if (file_view.is_encrypted()) { @@ -3545,13 +3645,20 @@ Result> StickersManager::prepare_input_file( if (file_view.has_url()) { is_url = true; } else { - if (file_view.has_local_location() && file_view.expected_size() > MAX_STICKER_FILE_SIZE) { + auto max_file_size = [&] { + if (for_thumbnail) { + return is_animated ? MAX_ANIMATED_THUMBNAIL_FILE_SIZE : MAX_THUMBNAIL_FILE_SIZE; + } else { + return is_animated ? MAX_ANIMATED_STICKER_FILE_SIZE : MAX_STICKER_FILE_SIZE; + } + }(); + if (file_view.has_local_location() && file_view.expected_size() > max_file_size) { return Status::Error(400, "File is too big"); } is_local = true; } } - return std::make_tuple(file_id, is_url, is_local); + return std::make_tuple(file_id, is_url, is_local, is_animated); } FileId StickersManager::upload_sticker_file(UserId user_id, const tl_object_ptr &sticker, @@ -3568,7 +3675,7 @@ FileId StickersManager::upload_sticker_file(UserId user_id, const tl_object_ptr< return FileId(); } - auto r_file_id = prepare_input_file(sticker); + auto r_file_id = prepare_input_file(sticker, false, false); if (r_file_id.is_error()) { promise.set_error(r_file_id.move_as_error()); return FileId(); @@ -3588,31 +3695,35 @@ FileId StickersManager::upload_sticker_file(UserId user_id, const tl_object_ptr< return file_id; } -tl_object_ptr StickersManager::get_input_sticker(td_api::inputSticker *sticker, +tl_object_ptr StickersManager::get_input_sticker(td_api::InputSticker *sticker, FileId file_id) const { + CHECK(sticker != nullptr); FileView file_view = td_->file_manager_->get_file_view(file_id); CHECK(file_view.has_remote_location()); auto input_document = file_view.main_remote_location().as_input_document(); tl_object_ptr mask_coords; - if (sticker->mask_position_ != nullptr && sticker->mask_position_->point_ != nullptr) { - auto point = [mask_point = std::move(sticker->mask_position_->point_)]() { - switch (mask_point->get_id()) { - case td_api::maskPointForehead::ID: - return 0; - case td_api::maskPointEyes::ID: - return 1; - case td_api::maskPointMouth::ID: - return 2; - case td_api::maskPointChin::ID: - return 3; - default: - UNREACHABLE(); - return -1; - } - }(); - mask_coords = make_tl_object( - point, sticker->mask_position_->x_shift_, sticker->mask_position_->y_shift_, sticker->mask_position_->scale_); + if (sticker->get_id() == td_api::inputStickerStatic::ID) { + auto mask_position = static_cast(sticker)->mask_position_.get(); + if (mask_position != nullptr && mask_position->point_ != nullptr) { + auto point = [mask_point = std::move(mask_position->point_)] { + switch (mask_point->get_id()) { + case td_api::maskPointForehead::ID: + return 0; + case td_api::maskPointEyes::ID: + return 1; + case td_api::maskPointMouth::ID: + return 2; + case td_api::maskPointChin::ID: + return 3; + default: + UNREACHABLE(); + return -1; + } + }(); + mask_coords = make_tl_object(point, mask_position->x_shift_, mask_position->y_shift_, + mask_position->scale_); + } } int32 flags = 0; @@ -3620,12 +3731,12 @@ tl_object_ptr StickersManager::get_input_stic flags |= telegram_api::inputStickerSetItem::MASK_COORDS_MASK; } - return make_tl_object(flags, std::move(input_document), sticker->emojis_, - std::move(mask_coords)); + return make_tl_object(flags, std::move(input_document), + get_input_sticker_emojis(sticker), std::move(mask_coords)); } void StickersManager::create_new_sticker_set(UserId user_id, string &title, string &short_name, bool is_masks, - vector> &&stickers, + vector> &&stickers, Promise &&promise) { auto input_user = td_->contacts_manager_->get_input_user(user_id); if (input_user == nullptr) { @@ -3647,10 +3758,15 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri return promise.set_error(Status::Error(3, "Sticker set name can't be empty")); } + if (stickers.empty()) { + return promise.set_error(Status::Error(400, "At least 1 sticker must be specified")); + } + vector file_ids; file_ids.reserve(stickers.size()); vector local_file_ids; vector url_file_ids; + size_t animated_sticker_count = 0; for (auto &sticker : stickers) { auto r_file_id = prepare_input_sticker(sticker.get()); if (r_file_id.is_error()) { @@ -3659,6 +3775,13 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri auto file_id = std::get<0>(r_file_id.ok()); auto is_url = std::get<1>(r_file_id.ok()); auto is_local = std::get<2>(r_file_id.ok()); + auto is_animated = std::get<3>(r_file_id.ok()); + if (is_animated) { + animated_sticker_count++; + if (is_url) { + return promise.set_error(Status::Error(400, "Animated stickers can't be uploaded by URL")); + } + } file_ids.push_back(file_id); if (is_url) { @@ -3667,12 +3790,17 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri local_file_ids.push_back(file_id); } } + if (animated_sticker_count != stickers.size() && animated_sticker_count != 0) { + return promise.set_error(Status::Error(400, "All stickers must be either animated or static")); + } + bool is_animated = animated_sticker_count == stickers.size(); auto pending_new_sticker_set = make_unique(); pending_new_sticker_set->user_id = user_id; pending_new_sticker_set->title = std::move(title); pending_new_sticker_set->short_name = short_name; pending_new_sticker_set->is_masks = is_masks; + pending_new_sticker_set->is_animated = is_animated; pending_new_sticker_set->file_ids = std::move(file_ids); pending_new_sticker_set->stickers = std::move(stickers); pending_new_sticker_set->promise = std::move(promise); @@ -3703,9 +3831,14 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri } void StickersManager::upload_sticker_file(UserId user_id, FileId file_id, Promise &&promise) { - CHECK(td_->documents_manager_->get_input_media(file_id, nullptr, nullptr) == nullptr); - - auto upload_file_id = td_->documents_manager_->dup_document(td_->file_manager_->dup_file_id(file_id), file_id); + FileId upload_file_id; + if (td_->file_manager_->get_file_view(file_id).get_type() == FileType::Sticker) { + CHECK(get_input_media(file_id, nullptr, nullptr) == nullptr); + upload_file_id = dup_sticker(td_->file_manager_->dup_file_id(file_id), file_id); + } else { + CHECK(td_->documents_manager_->get_input_media(file_id, nullptr, nullptr) == nullptr); + upload_file_id = td_->documents_manager_->dup_document(td_->file_manager_->dup_file_id(file_id), file_id); + } being_uploaded_files_[upload_file_id] = {user_id, std::move(promise)}; LOG(INFO) << "Ask to upload sticker file " << upload_file_id; @@ -3757,8 +3890,12 @@ void StickersManager::do_upload_sticker_file(UserId user_id, FileId file_id, return promise.set_error(Status::Error(3, "Have no access to the user")); } + FileView file_view = td_->file_manager_->get_file_view(file_id); + bool is_animated = file_view.get_type() == FileType::Sticker; + bool had_input_file = input_file != nullptr; - auto input_media = td_->documents_manager_->get_input_media(file_id, std::move(input_file), nullptr); + auto input_media = is_animated ? get_input_media(file_id, std::move(input_file), nullptr) + : td_->documents_manager_->get_input_media(file_id, std::move(input_file), nullptr); CHECK(input_media != nullptr); if (had_input_file && !FileManager::extract_was_uploaded(input_media)) { // if we had InputFile, but has failed to use it, then we need to immediately cancel file upload @@ -3773,6 +3910,7 @@ void StickersManager::do_upload_sticker_file(UserId user_id, FileId file_id, void StickersManager::on_uploaded_sticker_file(FileId file_id, tl_object_ptr media, Promise &&promise) { CHECK(media != nullptr); + LOG(INFO) << "Receive uploaded sticker file " << to_string(media); if (media->get_id() != telegram_api::messageMediaDocument::ID) { return promise.set_error(Status::Error(400, "Can't upload sticker file: wrong file type")); } @@ -3785,13 +3923,21 @@ void StickersManager::on_uploaded_sticker_file(FileId file_id, tl_object_ptrfile_manager_->get_file_view(file_id); + bool is_animated = file_view.get_type() == FileType::Sticker; + auto expected_document_type = is_animated ? Document::Type::Sticker : Document::Type::General; + auto parsed_document = td_->documents_manager_->on_get_document( move_tl_object_as(document_ptr), DialogId(), nullptr); - if (parsed_document.type != Document::Type::General) { + if (parsed_document.type != expected_document_type) { return promise.set_error(Status::Error(400, "Wrong file type")); } - td_->documents_manager_->merge_documents(parsed_document.file_id, file_id, true); + if (is_animated) { + merge_stickers(parsed_document.file_id, file_id, true); + } else { + td_->documents_manager_->merge_documents(parsed_document.file_id, file_id, true); + } promise.set_value(Unit()); } @@ -3817,6 +3963,7 @@ void StickersManager::on_new_stickers_uploaded(int64 random_id, Result res } bool is_masks = pending_new_sticker_set->is_masks; + bool is_animated = pending_new_sticker_set->is_animated; auto sticker_count = pending_new_sticker_set->stickers.size(); vector> input_stickers; @@ -3828,11 +3975,11 @@ void StickersManager::on_new_stickers_uploaded(int64 random_id, Result res td_->create_handler(std::move(pending_new_sticker_set->promise)) ->send(std::move(input_user), pending_new_sticker_set->title, pending_new_sticker_set->short_name, is_masks, - std::move(input_stickers)); + is_animated, std::move(input_stickers)); } void StickersManager::add_sticker_to_set(UserId user_id, string &short_name, - tl_object_ptr &&sticker, Promise &&promise) { + tl_object_ptr &&sticker, Promise &&promise) { auto input_user = td_->contacts_manager_->get_input_user(user_id); if (input_user == nullptr) { return promise.set_error(Status::Error(3, "User not found")); @@ -3900,6 +4047,112 @@ void StickersManager::on_added_sticker_uploaded(int64 random_id, Result re get_input_sticker(pending_add_sticker_to_set->sticker.get(), pending_add_sticker_to_set->file_id)); } +void StickersManager::set_sticker_set_thumbnail(UserId user_id, string &short_name, + tl_object_ptr &&thumbnail, Promise &&promise) { + auto input_user = td_->contacts_manager_->get_input_user(user_id); + if (input_user == nullptr) { + return promise.set_error(Status::Error(3, "User not found")); + } + DialogId dialog_id(user_id); + auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write); + if (input_peer == nullptr) { + return promise.set_error(Status::Error(3, "Have no access to the user")); + } + + short_name = clean_username(strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH)); + if (short_name.empty()) { + return promise.set_error(Status::Error(3, "Sticker set name can't be empty")); + } + + auto it = short_name_to_sticker_set_id_.find(short_name); + const StickerSet *sticker_set = it == short_name_to_sticker_set_id_.end() ? nullptr : get_sticker_set(it->second); + if (sticker_set != nullptr && sticker_set->was_loaded) { + return do_set_sticker_set_thumbnail(user_id, short_name, std::move(thumbnail), std::move(promise)); + } + + do_reload_sticker_set( + StickerSetId(), make_tl_object(short_name), + PromiseCreator::lambda([actor_id = actor_id(this), user_id, short_name, thumbnail = std::move(thumbnail), + promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &StickersManager::do_set_sticker_set_thumbnail, user_id, std::move(short_name), + std::move(thumbnail), std::move(promise)); + } + })); +} + +void StickersManager::do_set_sticker_set_thumbnail(UserId user_id, string short_name, + tl_object_ptr &&thumbnail, + Promise &&promise) { + auto it = short_name_to_sticker_set_id_.find(short_name); + const StickerSet *sticker_set = it == short_name_to_sticker_set_id_.end() ? nullptr : get_sticker_set(it->second); + if (sticker_set == nullptr || !sticker_set->was_loaded) { + return promise.set_error(Status::Error(3, "Sticker set not found")); + } + + auto r_file_id = prepare_input_file(thumbnail, sticker_set->is_animated, true); + if (r_file_id.is_error()) { + return promise.set_error(r_file_id.move_as_error()); + } + auto file_id = std::get<0>(r_file_id.ok()); + auto is_url = std::get<1>(r_file_id.ok()); + auto is_local = std::get<2>(r_file_id.ok()); + + if (!file_id.is_valid()) { + td_->create_handler(std::move(promise)) + ->send(short_name, telegram_api::make_object()); + return; + } + + auto pending_set_sticker_set_thumbnail = make_unique(); + pending_set_sticker_set_thumbnail->short_name = short_name; + pending_set_sticker_set_thumbnail->file_id = file_id; + pending_set_sticker_set_thumbnail->promise = std::move(promise); + + int64 random_id; + do { + random_id = Random::secure_int64(); + } while (random_id == 0 || + pending_set_sticker_set_thumbnails_.find(random_id) != pending_set_sticker_set_thumbnails_.end()); + pending_set_sticker_set_thumbnails_[random_id] = std::move(pending_set_sticker_set_thumbnail); + + auto on_upload_promise = PromiseCreator::lambda([random_id](Result result) { + send_closure(G()->stickers_manager(), &StickersManager::on_sticker_set_thumbnail_uploaded, random_id, + std::move(result)); + }); + + if (is_url) { + do_upload_sticker_file(user_id, file_id, nullptr, std::move(on_upload_promise)); + } else if (is_local) { + upload_sticker_file(user_id, file_id, std::move(on_upload_promise)); + } else { + on_upload_promise.set_value(Unit()); + } +} + +void StickersManager::on_sticker_set_thumbnail_uploaded(int64 random_id, Result result) { + auto it = pending_set_sticker_set_thumbnails_.find(random_id); + CHECK(it != pending_set_sticker_set_thumbnails_.end()); + + auto pending_set_sticker_set_thumbnail = std::move(it->second); + CHECK(pending_set_sticker_set_thumbnail != nullptr); + + pending_set_sticker_set_thumbnails_.erase(it); + + if (result.is_error()) { + pending_set_sticker_set_thumbnail->promise.set_error(result.move_as_error()); + return; + } + + FileView file_view = td_->file_manager_->get_file_view(pending_set_sticker_set_thumbnail->file_id); + CHECK(file_view.has_remote_location()); + + td_->create_handler(std::move(pending_set_sticker_set_thumbnail->promise)) + ->send(pending_set_sticker_set_thumbnail->short_name, file_view.main_remote_location().as_input_document()); +} + void StickersManager::set_sticker_position_in_set(const tl_object_ptr &sticker, int32 position, Promise &&promise) { if (position < 0) { @@ -4217,8 +4470,10 @@ int32 StickersManager::get_recent_stickers_hash(const vector &sticker_id CHECK(sticker != nullptr); auto file_view = td_->file_manager_->get_file_view(sticker_id); CHECK(file_view.has_remote_location()); - CHECK(file_view.remote_location().is_document()); - CHECK(!file_view.remote_location().is_web()); + if (!file_view.remote_location().is_document()) { + LOG(ERROR) << "Recent sticker remote location is not document: " << file_view.remote_location(); + continue; + } auto id = static_cast(file_view.remote_location().get_id()); numbers.push_back(static_cast(id >> 32)); numbers.push_back(static_cast(id & 0xFFFFFFFF)); @@ -4939,7 +5194,7 @@ void StickersManager::on_get_language_codes(const string &key, Resultclose_flag()) { + if (!G()->is_expected_error(result.error())) { LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsLanguageQuery"; } for (auto &promise : promises) { @@ -5043,7 +5298,7 @@ void StickersManager::on_get_emoji_keywords( load_emoji_keywords_queries_.erase(it); if (result.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(result.error())) { LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsQuery"; } for (auto &promise : promises) { @@ -5125,7 +5380,7 @@ void StickersManager::on_get_emoji_keywords_difference( const string &language_code, int32 from_version, Result> &&result) { if (result.is_error()) { - if (!G()->close_flag()) { + if (!G()->is_expected_error(result.error())) { LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsDifferenceQuery"; } emoji_language_code_last_difference_times_[language_code] = Time::now_cached() - EMOJI_KEYWORDS_UPDATE_DELAY - 2; @@ -5325,7 +5580,7 @@ string StickersManager::remove_emoji_modifiers(string emoji) { } void StickersManager::after_get_difference() { - if (!td_->auth_manager_->is_bot()) { + if (td_->auth_manager_->is_bot()) { return; } if (td_->is_online()) { @@ -5335,7 +5590,8 @@ void StickersManager::after_get_difference() { get_recent_stickers(false, Auto()); get_recent_stickers(true, Auto()); get_favorite_stickers(Auto()); - td_->create_handler()->send(); + td_->create_handler()->send(SpecialStickerSetType::AnimatedEmoji); + td_->create_handler()->send(SpecialStickerSetType::AnimatedDice); } } diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index 2e98fd23..93d5bce3 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -108,7 +108,9 @@ class StickersManager : public Actor { StickerSetId on_get_sticker_set_covered(tl_object_ptr &&set_ptr, bool is_changed, const char *source); - void on_get_animated_emoji_sticker_set(StickerSetId sticker_set_id); + enum SpecialStickerSetType : int32 { AnimatedEmoji, AnimatedDice }; + + void on_get_special_sticker_set(StickerSetId sticker_set_id, SpecialStickerSetType type); void on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error); @@ -145,11 +147,14 @@ class StickersManager : public Actor { FileId upload_sticker_file(UserId user_id, const tl_object_ptr &sticker, Promise &&promise); void create_new_sticker_set(UserId user_id, string &title, string &short_name, bool is_masks, - vector> &&stickers, Promise &&promise); + vector> &&stickers, Promise &&promise); - void add_sticker_to_set(UserId user_id, string &short_name, tl_object_ptr &&sticker, + void add_sticker_to_set(UserId user_id, string &short_name, tl_object_ptr &&sticker, Promise &&promise); + void set_sticker_set_thumbnail(UserId user_id, string &short_name, tl_object_ptr &&thumbnail, + Promise &&promise); + void set_sticker_position_in_set(const tl_object_ptr &sticker, int32 position, Promise &&promise); @@ -265,10 +270,13 @@ class StickersManager : public Actor { private: static constexpr int32 MAX_FEATURED_STICKER_SET_VIEW_DELAY = 5; - static constexpr int32 MAX_FOUND_STICKERS = 100; // server side limit - static constexpr int64 MAX_STICKER_FILE_SIZE = 1 << 19; // server side limit - static constexpr size_t MAX_STICKER_SET_TITLE_LENGTH = 64; // server side limit - static constexpr size_t MAX_STICKER_SET_SHORT_NAME_LENGTH = 64; // server side limit + static constexpr int32 MAX_FOUND_STICKERS = 100; // server side limit + static constexpr int64 MAX_STICKER_FILE_SIZE = 1 << 19; // server side limit + static constexpr int64 MAX_THUMBNAIL_FILE_SIZE = 1 << 17; // server side limit + static constexpr int64 MAX_ANIMATED_STICKER_FILE_SIZE = 1 << 16; // server side limit + static constexpr int64 MAX_ANIMATED_THUMBNAIL_FILE_SIZE = 1 << 15; // server side limit + static constexpr size_t MAX_STICKER_SET_TITLE_LENGTH = 64; // server side limit + static constexpr size_t MAX_STICKER_SET_SHORT_NAME_LENGTH = 64; // server side limit static constexpr int32 EMOJI_KEYWORDS_UPDATE_DELAY = 3600; @@ -329,18 +337,32 @@ class StickersManager : public Actor { string title; string short_name; bool is_masks; + bool is_animated; vector file_ids; - vector> stickers; + vector> stickers; Promise<> promise; }; struct PendingAddStickerToSet { string short_name; FileId file_id; - tl_object_ptr sticker; + tl_object_ptr sticker; Promise<> promise; }; + struct PendingSetStickerSetThumbnail { + string short_name; + FileId file_id; + Promise<> promise; + }; + + struct SpecialStickerSet { + StickerSetId id_; + int64 access_hash_ = 0; + string name_; + string type_; + }; + class StickerListLogEvent; class StickerSetListLogEvent; @@ -463,11 +485,14 @@ class StickersManager : public Actor { template void parse_sticker_set(StickerSet *sticker_set, ParserT &parser); - Result> prepare_input_file(const tl_object_ptr &input_file); + static string &get_input_sticker_emojis(td_api::InputSticker *sticker); - Result> prepare_input_sticker(td_api::inputSticker *sticker); + Result> prepare_input_file(const tl_object_ptr &input_file, + bool is_animated, bool for_thumbnail); - tl_object_ptr get_input_sticker(td_api::inputSticker *sticker, + Result> prepare_input_sticker(td_api::InputSticker *sticker); + + tl_object_ptr get_input_sticker(td_api::InputSticker *sticker, FileId file_id) const; void upload_sticker_file(UserId user_id, FileId file_id, Promise &&promise); @@ -483,12 +508,22 @@ class StickersManager : public Actor { void on_added_sticker_uploaded(int64 random_id, Result result); + void on_sticker_set_thumbnail_uploaded(int64 random_id, Result result); + + void do_set_sticker_set_thumbnail(UserId user_id, string short_name, tl_object_ptr &&thumbnail, + Promise &&promise); + bool update_sticker_set_cache(const StickerSet *sticker_set, Promise &promise); void start_up() override; void tear_down() override; + static void init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash, + string name); + + void load_special_sticker_set(SpecialStickerSet &sticker_set); + static void add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail); static string get_sticker_mime_type(const Sticker *s); @@ -589,9 +624,7 @@ class StickersManager : public Actor { int32 recent_stickers_limit_ = 200; int32 favorite_stickers_limit_ = 5; - StickerSetId animated_emoji_sticker_set_id_; - int64 animated_emoji_sticker_set_access_hash_ = 0; - string animated_emoji_sticker_set_name_; + SpecialStickerSet special_sticker_sets_[2]; struct StickerSetLoadRequest { Promise promise; @@ -606,6 +639,8 @@ class StickersManager : public Actor { std::unordered_map> pending_add_sticker_to_sets_; + std::unordered_map> pending_set_sticker_set_thumbnails_; + std::shared_ptr upload_sticker_file_callback_; std::unordered_map>, FileIdHash> being_uploaded_files_; diff --git a/td/telegram/StickersManager.hpp b/td/telegram/StickersManager.hpp index f56cf529..6366811c 100644 --- a/td/telegram/StickersManager.hpp +++ b/td/telegram/StickersManager.hpp @@ -199,7 +199,7 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser parse(access_hash, parser); CHECK(sticker_set->id.get() == sticker_set_id); if (sticker_set->access_hash != access_hash) { - LOG(ERROR) << "Access hash of " << sticker_set_id << " has changed from " << access_hash << " to " + LOG(ERROR) << "Access hash of " << sticker_set->id << " has changed from " << access_hash << " to " << sticker_set->access_hash; } diff --git a/td/telegram/StorageManager.cpp b/td/telegram/StorageManager.cpp index 373332cf..886c3071 100644 --- a/td/telegram/StorageManager.cpp +++ b/td/telegram/StorageManager.cpp @@ -7,7 +7,6 @@ #include "td/telegram/StorageManager.h" #include "td/telegram/ConfigShared.h" -#include "td/telegram/DialogId.h" #include "td/telegram/files/FileGcWorker.h" #include "td/telegram/files/FileStatsWorker.h" #include "td/telegram/Global.h" @@ -42,13 +41,19 @@ void StorageManager::start_up() { load_fast_stat(); } -void StorageManager::on_new_file(int64 size, int32 cnt) { - LOG(INFO) << "Add " << cnt << " file of size " << size << " to fast storage statistics"; +void StorageManager::on_new_file(int64 size, int64 real_size, int32 cnt) { + LOG(INFO) << "Add " << cnt << " file of size " << size << " with real size " << real_size + << " to fast storage statistics"; fast_stat_.cnt += cnt; - fast_stat_.size += size; +#if TD_WINDOWS + auto add_size = size; +#else + auto add_size = real_size; +#endif + fast_stat_.size += add_size; if (fast_stat_.cnt < 0 || fast_stat_.size < 0) { - LOG(ERROR) << "Wrong fast stat after adding size " << size << " and cnt " << cnt; + LOG(ERROR) << "Wrong fast stat after adding size " << add_size << " and cnt " << cnt; fast_stat_ = FileTypeStat(); } save_fast_stat(); @@ -67,7 +72,7 @@ void StorageManager::get_storage_stats(bool need_all_files, int32 dialog_limit, //TODO group same queries close_stats_worker(); } - if (!pending_run_gc_.empty()) { + if (!pending_run_gc_[0].empty() || !pending_run_gc_[1].empty()) { close_gc_worker(); } stats_dialog_limit_ = dialog_limit; @@ -101,12 +106,12 @@ void StorageManager::update_use_storage_optimizer() { schedule_next_gc(); } -void StorageManager::run_gc(FileGcParameters parameters, Promise promise) { +void StorageManager::run_gc(FileGcParameters parameters, bool return_deleted_file_statistics, + Promise promise) { if (is_closed_) { - promise.set_error(Status::Error(500, "Request aborted")); - return; + return promise.set_error(Status::Error(500, "Request aborted")); } - if (!pending_run_gc_.empty()) { + if (!pending_run_gc_[0].empty() || !pending_run_gc_[1].empty()) { close_gc_worker(); } @@ -116,11 +121,11 @@ void StorageManager::run_gc(FileGcParameters parameters, Promise prom PromiseCreator::lambda( [actor_id = actor_id(this), parameters = std::move(parameters)](Result file_stats) { send_closure(actor_id, &StorageManager::on_all_files, std::move(parameters), - std::move(file_stats), false); + std::move(file_stats)); })); //NB: get_storage_stats will cancel all gc queries, so promise needs to be added after the call - pending_run_gc_.emplace_back(std::move(promise)); + pending_run_gc_[return_deleted_file_statistics].push_back(std::move(promise)); } void StorageManager::on_file_stats(Result r_file_stats, uint32 generation) { @@ -135,6 +140,7 @@ void StorageManager::on_file_stats(Result r_file_stats, uint32 genera return; } + update_fast_stats(r_file_stats.ok()); send_stats(r_file_stats.move_as_ok(), stats_dialog_limit_, std::move(pending_storage_stats_)); } @@ -147,20 +153,20 @@ void StorageManager::create_stats_worker() { } } -void StorageManager::on_all_files(FileGcParameters gc_parameters, Result r_file_stats, bool dummy) { +void StorageManager::on_all_files(FileGcParameters gc_parameters, Result r_file_stats) { int32 dialog_limit = gc_parameters.dialog_limit; if (is_closed_ && r_file_stats.is_ok()) { r_file_stats = Status::Error(500, "Request aborted"); } if (r_file_stats.is_error()) { - return on_gc_finished(dialog_limit, std::move(r_file_stats), false); + return on_gc_finished(dialog_limit, r_file_stats.move_as_error()); } create_gc_worker(); - send_closure(gc_worker_, &FileGcWorker::run_gc, std::move(gc_parameters), r_file_stats.move_as_ok().all_files, - PromiseCreator::lambda([actor_id = actor_id(this), dialog_limit](Result r_file_stats) { - send_closure(actor_id, &StorageManager::on_gc_finished, dialog_limit, std::move(r_file_stats), false); + send_closure(gc_worker_, &FileGcWorker::run_gc, std::move(gc_parameters), std::move(r_file_stats.ok_ref().all_files), + PromiseCreator::lambda([actor_id = actor_id(this), dialog_limit](Result r_file_gc_result) { + send_closure(actor_id, &StorageManager::on_gc_finished, dialog_limit, std::move(r_file_gc_result)); })); } @@ -206,19 +212,27 @@ void StorageManager::create_gc_worker() { } } -void StorageManager::on_gc_finished(int32 dialog_limit, Result r_file_stats, bool dummy) { - if (r_file_stats.is_error()) { - if (r_file_stats.error().code() != 500) { - LOG(ERROR) << "GC failed: " << r_file_stats.error(); +void StorageManager::on_gc_finished(int32 dialog_limit, Result r_file_gc_result) { + if (r_file_gc_result.is_error()) { + if (r_file_gc_result.error().code() != 500) { + LOG(ERROR) << "GC failed: " << r_file_gc_result.error(); } - auto promises = std::move(pending_run_gc_); + auto promises = std::move(pending_run_gc_[0]); + append(promises, std::move(pending_run_gc_[1])); + pending_run_gc_[0].clear(); + pending_run_gc_[1].clear(); for (auto &promise : promises) { - promise.set_error(r_file_stats.error().clone()); + promise.set_error(r_file_gc_result.error().clone()); } return; } - send_stats(r_file_stats.move_as_ok(), dialog_limit, std::move(pending_run_gc_)); + update_fast_stats(r_file_gc_result.ok().kept_file_stats_); + + auto kept_file_promises = std::move(pending_run_gc_[0]); + auto removed_file_promises = std::move(pending_run_gc_[1]); + send_stats(std::move(r_file_gc_result.ok_ref().kept_file_stats_), dialog_limit, std::move(kept_file_promises)); + send_stats(std::move(r_file_gc_result.ok_ref().removed_file_stats_), dialog_limit, std::move(removed_file_promises)); } void StorageManager::save_fast_stat() { @@ -233,21 +247,26 @@ void StorageManager::load_fast_stat() { LOG(INFO) << "Loaded fast storage statistics with " << fast_stat_.cnt << " files of total size " << fast_stat_.size; } -void StorageManager::send_stats(FileStats &&stats, int32 dialog_limit, std::vector> promises) { +void StorageManager::update_fast_stats(const FileStats &stats) { fast_stat_ = stats.get_total_nontemp_stat(); LOG(INFO) << "Recalculate fast storage statistics to " << fast_stat_.cnt << " files of total size " << fast_stat_.size; save_fast_stat(); +} + +void StorageManager::send_stats(FileStats &&stats, int32 dialog_limit, std::vector> &&promises) { + if (promises.empty()) { + return; + } stats.apply_dialog_limit(dialog_limit); - std::vector dialog_ids = stats.get_dialog_ids(); + auto dialog_ids = stats.get_dialog_ids(); - auto promise = - PromiseCreator::lambda([promises = std::move(promises), stats = std::move(stats)](Result) mutable { - for (auto &promise : promises) { - promise.set_value(FileStats(stats)); - } - }); + auto promise = PromiseCreator::lambda([promises = std::move(promises), stats = std::move(stats)](Unit) mutable { + for (auto &promise : promises) { + promise.set_value(FileStats(stats)); + } + }); send_closure(G()->messages_manager(), &MessagesManager::load_dialogs, std::move(dialog_ids), std::move(promise)); } @@ -276,8 +295,10 @@ void StorageManager::close_stats_worker() { } void StorageManager::close_gc_worker() { - auto promises = std::move(pending_run_gc_); - pending_run_gc_.clear(); + auto promises = std::move(pending_run_gc_[0]); + append(promises, std::move(pending_run_gc_[1])); + pending_run_gc_[0].clear(); + pending_run_gc_[1].clear(); for (auto &promise : promises) { promise.set_error(Status::Error(500, "Request aborted")); } @@ -332,14 +353,14 @@ void StorageManager::timeout_expired() { if (next_gc_at_ == 0) { return; } - if (!pending_run_gc_.empty() || !pending_storage_stats_.empty()) { + if (!pending_run_gc_[0].empty() || !pending_run_gc_[1].empty() || !pending_storage_stats_.empty()) { set_timeout_in(60); return; } next_gc_at_ = 0; - run_gc({}, PromiseCreator::lambda([actor_id = actor_id(this)](Result r_stats) { + run_gc({}, false, PromiseCreator::lambda([actor_id = actor_id(this)](Result r_stats) { if (!r_stats.is_error() || r_stats.error().code() != 500) { - // do not save gc timestamp is request was cancelled + // do not save gc timestamp if request was cancelled send_closure(actor_id, &StorageManager::save_last_gc_timestamp); } send_closure(actor_id, &StorageManager::schedule_next_gc); diff --git a/td/telegram/StorageManager.h b/td/telegram/StorageManager.h index af3c4c56..7af4086d 100644 --- a/td/telegram/StorageManager.h +++ b/td/telegram/StorageManager.h @@ -35,10 +35,10 @@ class StorageManager : public Actor { void get_storage_stats(bool need_all_files, int32 dialog_limit, Promise promise); void get_storage_stats_fast(Promise promise); void get_database_stats(Promise promise); - void run_gc(FileGcParameters parameters, Promise promise); + void run_gc(FileGcParameters parameters, bool return_deleted_file_statistics, Promise promise); void update_use_storage_optimizer(); - void on_new_file(int64 size, int32 cnt); + void on_new_file(int64 size, int64 real_size, int32 cnt); private: static constexpr uint32 GC_EACH = 60 * 60 * 24; // 1 day @@ -63,7 +63,8 @@ class StorageManager : public Actor { void on_file_stats(Result r_file_stats, uint32 generation); void create_stats_worker(); - void send_stats(FileStats &&stats, int32 dialog_limit, std::vector> promises); + void update_fast_stats(const FileStats &stats); + void send_stats(FileStats &&stats, int32 dialog_limit, std::vector> &&promises); void save_fast_stat(); void load_fast_stat(); @@ -82,14 +83,14 @@ class StorageManager : public Actor { // Gc ActorOwn gc_worker_; - std::vector> pending_run_gc_; + std::vector> pending_run_gc_[2]; uint32 last_gc_timestamp_ = 0; double next_gc_at_ = 0; - void on_all_files(FileGcParameters gc_parameters, Result r_file_stats, bool dummy); + void on_all_files(FileGcParameters gc_parameters, Result r_file_stats); void create_gc_worker(); - void on_gc_finished(int32 dialog_limit, Result r_file_stats, bool dummy); + void on_gc_finished(int32 dialog_limit, Result r_file_gc_result); void close_stats_worker(); void close_gc_worker(); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index eafec324..9d646f9f 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -104,7 +104,6 @@ #include "td/mtproto/HandshakeActor.h" #include "td/mtproto/RawConnection.h" #include "td/mtproto/TransportType.h" -#include "td/mtproto/utils.h" // for create_storer, fetch_result, etc, TODO #include "td/utils/buffer.h" #include "td/utils/filesystem.h" @@ -157,8 +156,7 @@ class GetNearestDcQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getNearestDc()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + send_query(G()->net_query_creator().create_unauth(telegram_api::help_getNearestDc())); } void on_result(uint64 id, BufferSlice packet) override { @@ -172,7 +170,9 @@ class GetNearestDcQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - LOG(ERROR) << "GetNearestDc returned " << status; + if (!G()->is_expected_error(status) && status.message() != "BOT_METHOD_INVALID") { + LOG(ERROR) << "GetNearestDc returned " << status; + } promise_.set_error(std::move(status)); } }; @@ -185,7 +185,7 @@ class GetRecentMeUrlsQuery : public Td::ResultHandler { } void send(const string &referrer) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getRecentMeUrls(referrer)))); + send_query(G()->net_query_creator().create(telegram_api::help_getRecentMeUrls(referrer))); } void on_result(uint64 id, BufferSlice packet) override { @@ -272,15 +272,16 @@ class GetRecentMeUrlsQuery : public Td::ResultHandler { }; class SendCustomRequestQuery : public Td::ResultHandler { - Promise promise_; + Promise> promise_; public: - explicit SendCustomRequestQuery(Promise &&promise) : promise_(std::move(promise)) { + explicit SendCustomRequestQuery(Promise> &&promise) + : promise_(std::move(promise)) { } void send(const string &method, const string ¶meters) { - send_query(G()->net_query_creator().create(create_storer( - telegram_api::bots_sendCustomRequest(method, make_tl_object(parameters))))); + send_query(G()->net_query_creator().create( + telegram_api::bots_sendCustomRequest(method, make_tl_object(parameters)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -290,7 +291,7 @@ class SendCustomRequestQuery : public Td::ResultHandler { } auto result = result_ptr.move_as_ok(); - promise_.set_value(std::move(result->data_)); + promise_.set_value(td_api::make_object(result->data_)); } void on_error(uint64 id, Status status) override { @@ -306,8 +307,8 @@ class AnswerCustomQueryQuery : public Td::ResultHandler { } void send(int64 custom_query_id, const string &data) { - send_query(G()->net_query_creator().create(create_storer( - telegram_api::bots_answerWebhookJSONQuery(custom_query_id, make_tl_object(data))))); + send_query(G()->net_query_creator().create( + telegram_api::bots_answerWebhookJSONQuery(custom_query_id, make_tl_object(data)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -331,8 +332,8 @@ class AnswerCustomQueryQuery : public Td::ResultHandler { class SetBotUpdatesStatusQuery : public Td::ResultHandler { public: void send(int32 pending_update_count, const string &error_message) { - send_query(G()->net_query_creator().create( - create_storer(telegram_api::help_setBotUpdatesStatus(pending_update_count, error_message)))); + send_query( + G()->net_query_creator().create(telegram_api::help_setBotUpdatesStatus(pending_update_count, error_message))); } void on_result(uint64 id, BufferSlice packet) override { @@ -346,7 +347,7 @@ class SetBotUpdatesStatusQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { + if (!G()->is_expected_error(status)) { LOG(WARNING) << "Receive error for SetBotUpdatesStatus: " << status; } status.ignore(); @@ -359,7 +360,7 @@ class UpdateStatusQuery : public Td::ResultHandler { public: NetQueryRef send(bool is_offline) { is_offline_ = is_offline; - auto net_query = G()->net_query_creator().create(create_storer(telegram_api::account_updateStatus(is_offline))); + auto net_query = G()->net_query_creator().create(telegram_api::account_updateStatus(is_offline)); auto result = net_query.get_weak(); send_query(std::move(net_query)); return result; @@ -377,7 +378,7 @@ class UpdateStatusQuery : public Td::ResultHandler { } void on_error(uint64 id, Status status) override { - if (status.code() != NetQuery::Cancelled && !G()->close_flag()) { + if (status.code() != NetQuery::Cancelled && !G()->is_expected_error(status)) { LOG(ERROR) << "Receive error for UpdateStatusQuery: " << status; } status.ignore(); @@ -392,7 +393,7 @@ class GetInviteTextQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getInviteText()))); + send_query(G()->net_query_creator().create(telegram_api::help_getInviteText())); } void on_result(uint64 id, BufferSlice packet) override { @@ -431,8 +432,7 @@ class GetDeepLinkInfoQuery : public Td::ResultHandler { pos++; } link.truncate(pos); - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getDeepLinkInfo(link.str())), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + send_query(G()->net_query_creator().create_unauth(telegram_api::help_getDeepLinkInfo(link.str()))); } void on_result(uint64 id, BufferSlice packet) override { @@ -456,7 +456,7 @@ class GetDeepLinkInfoQuery : public Td::ResultHandler { if (!clean_input_string(info->message_)) { info->message_.clear(); } - entities.clear(); + entities = find_entities(info->message_, true); } FormattedText text{std::move(info->message_), std::move(entities)}; return promise_.set_value( @@ -484,9 +484,7 @@ class SaveAppLogQuery : public Td::ResultHandler { vector> input_app_events; input_app_events.push_back( make_tl_object(G()->server_time_cached(), type, peer_id, std::move(data))); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::help_saveAppLog(std::move(input_app_events))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + send_query(G()->net_query_creator().create_unauth(telegram_api::help_saveAppLog(std::move(input_app_events)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -513,8 +511,7 @@ class TestQuery : public Td::ResultHandler { } void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getConfig()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + send_query(G()->net_query_creator().create_unauth(telegram_api::help_getConfig())); } void on_result(uint64 id, BufferSlice packet) override { @@ -643,83 +640,6 @@ class TestProxyRequest : public RequestOnceActor { } }; -class GetAccountTtlRequest : public RequestActor { - int32 account_ttl_; - - void do_run(Promise &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(account_ttl_)); - return; - } - - td->contacts_manager_->get_account_ttl(std::move(promise)); - } - - void do_set_result(int32 &&result) override { - account_ttl_ = result; - } - - void do_send_result() override { - send_result(make_tl_object(account_ttl_)); - } - - public: - GetAccountTtlRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - -class GetActiveSessionsRequest : public RequestActor> { - tl_object_ptr sessions_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(sessions_)); - return; - } - - td->contacts_manager_->get_active_sessions(std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - sessions_ = std::move(result); - } - - void do_send_result() override { - CHECK(sessions_ != nullptr); - send_result(std::move(sessions_)); - } - - public: - GetActiveSessionsRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - -class GetConnectedWebsitesRequest : public RequestActor> { - tl_object_ptr connected_websites_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(connected_websites_)); - return; - } - - td->contacts_manager_->get_connected_websites(std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - connected_websites_ = std::move(result); - } - - void do_send_result() override { - CHECK(connected_websites_ != nullptr); - send_result(std::move(connected_websites_)); - } - - public: - GetConnectedWebsitesRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - class GetMeRequest : public RequestActor<> { UserId user_id_; @@ -758,7 +678,7 @@ class GetUserFullInfoRequest : public RequestActor<> { UserId user_id_; void do_run(Promise &&promise) override { - td->contacts_manager_->get_user_full(user_id_, std::move(promise)); + td->contacts_manager_->get_user_full(user_id_, get_tries() < 2, std::move(promise)); } void do_send_result() override { @@ -793,7 +713,7 @@ class GetGroupFullInfoRequest : public RequestActor<> { ChatId chat_id_; void do_run(Promise &&promise) override { - td->contacts_manager_->get_chat_full(chat_id_, std::move(promise)); + td->contacts_manager_->get_chat_full(chat_id_, get_tries() < 2, std::move(promise)); } void do_send_result() override { @@ -828,7 +748,7 @@ class GetSupergroupFullInfoRequest : public RequestActor<> { ChannelId channel_id_; void do_run(Promise &&promise) override { - td->contacts_manager_->get_channel_full(channel_id_, std::move(promise)); + td->contacts_manager_->get_channel_full(channel_id_, get_tries() < 2, std::move(promise)); } void do_send_result() override { @@ -1330,24 +1250,6 @@ class EditMessageReplyMarkupRequest : public RequestOnceActor { } }; -class EditMessageSchedulingStateRequest : public RequestOnceActor { - FullMessageId full_message_id_; - td_api::object_ptr scheduling_state_; - - void do_run(Promise &&promise) override { - td->messages_manager_->edit_message_scheduling_state(full_message_id_, std::move(scheduling_state_), - std::move(promise)); - } - - public: - EditMessageSchedulingStateRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id, - td_api::object_ptr scheduling_state) - : RequestOnceActor(std::move(td), request_id) - , full_message_id_(DialogId(dialog_id), MessageId(message_id)) - , scheduling_state_(std::move(scheduling_state)) { - } -}; - class SetGameScoreRequest : public RequestOnceActor { FullMessageId full_message_id_; bool edit_message_; @@ -1710,7 +1612,8 @@ class GetChatScheduledMessagesRequest : public RequestActor<> { vector message_ids_; void do_run(Promise &&promise) override { - message_ids_ = td->messages_manager_->get_dialog_scheduled_messages(dialog_id_, std::move(promise)); + message_ids_ = + td->messages_manager_->get_dialog_scheduled_messages(dialog_id_, get_tries() < 2, false, std::move(promise)); } void do_send_result() override { @@ -1720,7 +1623,7 @@ class GetChatScheduledMessagesRequest : public RequestActor<> { public: GetChatScheduledMessagesRequest(ActorShared td, uint64 request_id, int64 dialog_id) : RequestActor(std::move(td), request_id), dialog_id_(dialog_id) { - set_tries(3); + set_tries(4); } }; @@ -2594,7 +2497,7 @@ class CreateNewStickerSetRequest : public RequestOnceActor { string title_; string name_; bool is_masks_; - vector> stickers_; + vector> stickers_; void do_run(Promise &&promise) override { td->stickers_manager_->create_new_sticker_set(user_id_, title_, name_, is_masks_, std::move(stickers_), @@ -2611,7 +2514,7 @@ class CreateNewStickerSetRequest : public RequestOnceActor { public: CreateNewStickerSetRequest(ActorShared td, uint64 request_id, int32 user_id, string &&title, string &&name, - bool is_masks, vector> &&stickers) + bool is_masks, vector> &&stickers) : RequestOnceActor(std::move(td), request_id) , user_id_(user_id) , title_(std::move(title)) @@ -2624,7 +2527,7 @@ class CreateNewStickerSetRequest : public RequestOnceActor { class AddStickerToSetRequest : public RequestOnceActor { UserId user_id_; string name_; - tl_object_ptr sticker_; + tl_object_ptr sticker_; void do_run(Promise &&promise) override { td->stickers_manager_->add_sticker_to_set(user_id_, name_, std::move(sticker_), std::move(promise)); @@ -2640,7 +2543,7 @@ class AddStickerToSetRequest : public RequestOnceActor { public: AddStickerToSetRequest(ActorShared td, uint64 request_id, int32 user_id, string &&name, - tl_object_ptr &&sticker) + tl_object_ptr &&sticker) : RequestOnceActor(std::move(td), request_id) , user_id_(user_id) , name_(std::move(name)) @@ -2648,6 +2551,33 @@ class AddStickerToSetRequest : public RequestOnceActor { } }; +class SetStickerSetThumbnailRequest : public RequestOnceActor { + UserId user_id_; + string name_; + tl_object_ptr thumbnail_; + + void do_run(Promise &&promise) override { + td->stickers_manager_->set_sticker_set_thumbnail(user_id_, name_, std::move(thumbnail_), std::move(promise)); + } + + void do_send_result() override { + auto set_id = td->stickers_manager_->search_sticker_set(name_, Auto()); + if (!set_id.is_valid()) { + return send_error(Status::Error(500, "Sticker set not found")); + } + send_result(td->stickers_manager_->get_sticker_set_object(set_id)); + } + + public: + SetStickerSetThumbnailRequest(ActorShared td, uint64 request_id, int32 user_id, string &&name, + tl_object_ptr &&thumbnail) + : RequestOnceActor(std::move(td), request_id) + , user_id_(user_id) + , name_(std::move(name)) + , thumbnail_(std::move(thumbnail)) { + } +}; + class GetRecentStickersRequest : public RequestActor<> { bool is_attached_; @@ -2931,163 +2861,6 @@ class GetCallbackQueryAnswerRequest : public RequestOnceActor { } }; -class GetPaymentFormRequest : public RequestActor> { - FullMessageId full_message_id_; - - tl_object_ptr payment_form_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(payment_form_)); - return; - } - - td->messages_manager_->get_payment_form(full_message_id_, std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - payment_form_ = std::move(result); - } - - void do_send_result() override { - CHECK(payment_form_ != nullptr); - send_result(std::move(payment_form_)); - } - - public: - GetPaymentFormRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id) - : RequestActor(std::move(td), request_id), full_message_id_(DialogId(dialog_id), MessageId(message_id)) { - } -}; - -class ValidateOrderInfoRequest : public RequestActor> { - FullMessageId full_message_id_; - tl_object_ptr order_info_; - bool allow_save_; - - tl_object_ptr validated_order_info_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(validated_order_info_)); - return; - } - - td->messages_manager_->validate_order_info(full_message_id_, std::move(order_info_), allow_save_, - std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - validated_order_info_ = std::move(result); - } - - void do_send_result() override { - CHECK(validated_order_info_ != nullptr); - send_result(std::move(validated_order_info_)); - } - - public: - ValidateOrderInfoRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id, - tl_object_ptr order_info, bool allow_save) - : RequestActor(std::move(td), request_id) - , full_message_id_(DialogId(dialog_id), MessageId(message_id)) - , order_info_(std::move(order_info)) - , allow_save_(allow_save) { - } -}; - -class SendPaymentFormRequest : public RequestActor> { - FullMessageId full_message_id_; - string order_info_id_; - string shipping_option_id_; - tl_object_ptr credentials_; - - tl_object_ptr payment_result_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(payment_result_)); - return; - } - - td->messages_manager_->send_payment_form(full_message_id_, order_info_id_, shipping_option_id_, credentials_, - std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - payment_result_ = std::move(result); - } - - void do_send_result() override { - CHECK(payment_result_ != nullptr); - send_result(std::move(payment_result_)); - } - - public: - SendPaymentFormRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id, string order_info_id, - string shipping_option_id, tl_object_ptr credentials) - : RequestActor(std::move(td), request_id) - , full_message_id_(DialogId(dialog_id), MessageId(message_id)) - , order_info_id_(std::move(order_info_id)) - , shipping_option_id_(std::move(shipping_option_id)) - , credentials_(std::move(credentials)) { - } -}; - -class GetPaymentReceiptRequest : public RequestActor> { - FullMessageId full_message_id_; - - tl_object_ptr payment_receipt_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(payment_receipt_)); - return; - } - - td->messages_manager_->get_payment_receipt(full_message_id_, std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - payment_receipt_ = std::move(result); - } - - void do_send_result() override { - CHECK(payment_receipt_ != nullptr); - send_result(std::move(payment_receipt_)); - } - - public: - GetPaymentReceiptRequest(ActorShared td, uint64 request_id, int64 dialog_id, int64 message_id) - : RequestActor(std::move(td), request_id), full_message_id_(DialogId(dialog_id), MessageId(message_id)) { - } -}; - -class GetSavedOrderInfoRequest : public RequestActor> { - tl_object_ptr order_info_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(order_info_)); - return; - } - - get_saved_order_info(std::move(promise)); - } - - void do_set_result(tl_object_ptr &&result) override { - order_info_ = std::move(result); - } - - void do_send_result() override { - send_result(std::move(order_info_)); - } - - public: - GetSavedOrderInfoRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - class GetSupportUserRequest : public RequestActor<> { UserId user_id_; @@ -3168,138 +2941,6 @@ class SetBackgroundRequest : public RequestActor<> { } }; -class RemoveBackgroundRequest : public RequestOnceActor { - BackgroundId background_id_; - - void do_run(Promise &&promise) override { - td->background_manager_->remove_background(background_id_, std::move(promise)); - } - - public: - RemoveBackgroundRequest(ActorShared td, uint64 request_id, int64 background_id) - : RequestOnceActor(std::move(td), request_id), background_id_(background_id) { - } -}; - -class ResetBackgroundsRequest : public RequestOnceActor { - void do_run(Promise &&promise) override { - td->background_manager_->reset_backgrounds(std::move(promise)); - } - - public: - ResetBackgroundsRequest(ActorShared td, uint64 request_id) : RequestOnceActor(std::move(td), request_id) { - } -}; - -class GetRecentlyVisitedTMeUrlsRequest : public RequestActor> { - string referrer_; - - tl_object_ptr urls_; - - void do_run(Promise> &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(urls_)); - return; - } - - td->create_handler(std::move(promise))->send(referrer_); - } - - void do_set_result(tl_object_ptr &&result) override { - urls_ = std::move(result); - } - - void do_send_result() override { - CHECK(urls_ != nullptr); - send_result(std::move(urls_)); - } - - public: - GetRecentlyVisitedTMeUrlsRequest(ActorShared td, uint64 request_id, string referrer) - : RequestActor(std::move(td), request_id), referrer_(std::move(referrer)) { - } -}; - -class SendCustomRequestRequest : public RequestActor { - string method_; - string parameters_; - - string request_result_; - - void do_run(Promise &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(request_result_)); - return; - } - - td->create_handler(std::move(promise))->send(method_, parameters_); - } - - void do_set_result(string &&result) override { - request_result_ = std::move(result); - } - - void do_send_result() override { - send_result(make_tl_object(request_result_)); - } - - public: - SendCustomRequestRequest(ActorShared td, uint64 request_id, string &&method, string &¶meters) - : RequestActor(std::move(td), request_id), method_(method), parameters_(parameters) { - } -}; - -class GetCountryCodeRequest : public RequestActor { - string country_code_; - - void do_run(Promise &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(country_code_)); - return; - } - - td->create_handler(std::move(promise))->send(); - } - - void do_set_result(string &&result) override { - country_code_ = std::move(result); - } - - void do_send_result() override { - send_result(make_tl_object(country_code_)); - } - - public: - GetCountryCodeRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - -class GetInviteTextRequest : public RequestActor { - string text_; - - void do_run(Promise &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(text_)); - return; - } - - td->create_handler(std::move(promise))->send(); - } - - void do_set_result(string &&result) override { - text_ = std::move(result); - } - - void do_send_result() override { - send_result(make_tl_object(text_)); - } - - public: - GetInviteTextRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { - } -}; - -/** Td **/ Td::Td(unique_ptr callback) : callback_(std::move(callback)) { } @@ -3462,6 +3103,8 @@ bool Td::is_synchronous_request(int32 id) { switch (id) { case td_api::getTextEntities::ID: case td_api::parseTextEntities::ID: + case td_api::parseMarkdown::ID: + case td_api::getMarkdownText::ID: case td_api::getFileMimeType::ID: case td_api::getFileExtension::ID: case td_api::cleanFileName::ID: @@ -3684,6 +3327,8 @@ td_api::object_ptr Td::static_request(td_api::object_ptrstorage_manager(), &StorageManager::on_new_file, size, cnt); + void on_new_file(int64 size, int64 real_size, int32 cnt) final { + send_closure(G()->storage_manager(), &StorageManager::on_new_file, size, real_size, cnt); } void on_file_updated(FileId file_id) final { @@ -5141,7 +4786,7 @@ void Td::on_request(uint64 id, td_api::processPushNotification &request) { void Td::on_request(uint64 id, td_api::registerDevice &request) { CHECK_IS_USER(); if (request.device_token_ == nullptr) { - return send_error_raw(id, 400, "Device token should not be empty"); + return send_error_raw(id, 400, "Device token must be non-empty"); } CREATE_REQUEST_PROMISE(); send_closure(device_token_manager_, &DeviceTokenManager::register_device, std::move(request.device_token_), @@ -5163,13 +4808,21 @@ void Td::on_request(uint64 id, td_api::setUserPrivacySettingRules &request) { void Td::on_request(uint64 id, const td_api::getAccountTtl &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetAccountTtlRequest); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(td_api::make_object(result.ok())); + } + }); + contacts_manager_->get_account_ttl(std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::setAccountTtl &request) { CHECK_IS_USER(); if (request.ttl_ == nullptr) { - return send_error_raw(id, 400, "New account TTL should not be empty"); + return send_error_raw(id, 400, "New account TTL must be non-empty"); } CREATE_OK_REQUEST_PROMISE(); contacts_manager_->set_account_ttl(request.ttl_->days_, std::move(promise)); @@ -5201,7 +4854,8 @@ void Td::on_request(uint64 id, td_api::resendChangePhoneNumberCode &request) { void Td::on_request(uint64 id, const td_api::getActiveSessions &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetActiveSessionsRequest); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_active_sessions(std::move(promise)); } void Td::on_request(uint64 id, const td_api::terminateSession &request) { @@ -5218,7 +4872,8 @@ void Td::on_request(uint64 id, const td_api::terminateAllOtherSessions &request) void Td::on_request(uint64 id, const td_api::getConnectedWebsites &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetConnectedWebsitesRequest); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_connected_websites(std::move(promise)); } void Td::on_request(uint64 id, const td_api::disconnectWebsite &request) { @@ -5360,7 +5015,7 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) { std::vector file_types; for (auto &file_type : request.file_types_) { if (file_type == nullptr) { - return send_error_raw(id, 400, "File type should not be empty"); + return send_error_raw(id, 400, "File type must be non-empty"); } file_types.push_back(from_td_api(*file_type)); @@ -5393,7 +5048,8 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) { promise.set_value(result.ok().as_td_api()); } }); - send_closure(storage_manager_, &StorageManager::run_gc, std::move(parameters), std::move(query_promise)); + send_closure(storage_manager_, &StorageManager::run_gc, std::move(parameters), + request.return_deleted_file_statistics_, std::move(query_promise)); } void Td::on_request(uint64 id, td_api::getNetworkStatistics &request) { @@ -5417,7 +5073,7 @@ void Td::on_request(uint64 id, td_api::resetNetworkStatistics &request) { void Td::on_request(uint64 id, td_api::addNetworkStatistics &request) { if (request.entry_ == nullptr) { - return send_error_raw(id, 400, "Network statistics entry should not be empty"); + return send_error_raw(id, 400, "Network statistics entry must be non-empty"); } NetworkStatsEntry entry; @@ -5492,7 +5148,7 @@ void Td::on_request(uint64 id, td_api::getTopChats &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); if (request.category_ == nullptr) { - return promise.set_error(Status::Error(400, "Top chat category should not be empty")); + return promise.set_error(Status::Error(400, "Top chat category must be non-empty")); } if (request.limit_ <= 0) { return promise.set_error(Status::Error(400, "Limit must be positive")); @@ -5512,7 +5168,7 @@ void Td::on_request(uint64 id, td_api::getTopChats &request) { void Td::on_request(uint64 id, const td_api::removeTopChat &request) { CHECK_IS_USER(); if (request.category_ == nullptr) { - return send_error_raw(id, 400, "Top chat category should not be empty"); + return send_error_raw(id, 400, "Top chat category must be non-empty"); } DialogId dialog_id(request.chat_id_); @@ -5897,8 +5553,9 @@ void Td::on_request(uint64 id, td_api::editInlineMessageReplyMarkup &request) { void Td::on_request(uint64 id, td_api::editMessageSchedulingState &request) { CHECK_IS_USER(); - CREATE_REQUEST(EditMessageSchedulingStateRequest, request.chat_id_, request.message_id_, - std::move(request.scheduling_state_)); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->edit_message_scheduling_state({DialogId(request.chat_id_), MessageId(request.message_id_)}, + std::move(request.scheduling_state_), std::move(promise)); } void Td::on_request(uint64 id, td_api::setGameScore &request) { @@ -6542,6 +6199,18 @@ void Td::on_request(uint64 id, td_api::setUsername &request) { contacts_manager_->set_username(request.username_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::setCommands &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->set_commands(std::move(request.commands_), std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::setLocation &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->set_location(Location(request.location_), std::move(promise)); +} + void Td::on_request(uint64 id, td_api::setProfilePhoto &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); @@ -6697,6 +6366,13 @@ void Td::on_request(uint64 id, td_api::addStickerToSet &request) { CREATE_REQUEST(AddStickerToSetRequest, request.user_id_, std::move(request.name_), std::move(request.sticker_)); } +void Td::on_request(uint64 id, td_api::setStickerSetThumbnail &request) { + CHECK_IS_BOT(); + CLEAN_INPUT_STRING(request.name_); + CREATE_REQUEST(SetStickerSetThumbnailRequest, request.user_id_, std::move(request.name_), + std::move(request.thumbnail_)); +} + void Td::on_request(uint64 id, td_api::setStickerPositionInSet &request) { CHECK_IS_BOT(); CREATE_OK_REQUEST_PROMISE(); @@ -6818,6 +6494,19 @@ void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getChatStatistics &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + contacts_manager_->get_channel_statistics(DialogId(request.chat_id_), request.is_dark_, std::move(promise)); +} + +void Td::on_request(uint64 id, td_api::getChatStatisticsGraph &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.token_); + contacts_manager_->load_statistics_graph(DialogId(request.chat_id_), request.token_, request.x_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::setChatNotificationSettings &request) { CHECK_IS_USER(); answer_ok_query(id, messages_manager_->set_dialog_notification_settings(DialogId(request.chat_id_), @@ -7067,6 +6756,11 @@ void Td::on_request(uint64 id, td_api::setOption &request) { bool is_bot = auth_manager_ != nullptr && auth_manager_->is_authorized() && auth_manager_->is_bot(); switch (request.name_[0]) { + case 'a': + if (set_boolean_option("always_parse_markdown")) { + return; + } + break; case 'c': if (!is_bot && set_string_option("connection_parameters", [](Slice value) { string value_copy = value.str(); @@ -7083,6 +6777,9 @@ void Td::on_request(uint64 id, td_api::setOption &request) { if (!is_bot && set_boolean_option("disable_contact_registered_notifications")) { return; } + if (!is_bot && set_boolean_option("disable_sent_scheduled_message_notifications")) { + return; + } if (!is_bot && set_boolean_option("disable_top_chats")) { return; } @@ -7097,6 +6794,9 @@ void Td::on_request(uint64 id, td_api::setOption &request) { if (set_boolean_option("ignore_background_updates")) { return; } + if (set_boolean_option("ignore_default_disable_notification")) { + return; + } if (set_boolean_option("ignore_inline_thumbnails")) { return; } @@ -7130,6 +6830,10 @@ void Td::on_request(uint64 id, td_api::setOption &request) { std::move(promise)); return; } + if (set_boolean_option("is_location_visible")) { + contacts_manager_->set_location_visibility(); + return; + } break; case 'l': if (!is_bot && set_string_option("language_pack_database_path", [](Slice value) { return true; })) { @@ -7337,15 +7041,24 @@ void Td::on_request(uint64 id, td_api::answerPreCheckoutQuery &request) { answer_pre_checkout_query(request.pre_checkout_query_id_, request.error_message_, std::move(promise)); } +void Td::on_request(uint64 id, td_api::getBankCardInfo &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.bank_card_number_); + CREATE_REQUEST_PROMISE(); + get_bank_card_info(request.bank_card_number_, std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getPaymentForm &request) { CHECK_IS_USER(); - CREATE_REQUEST(GetPaymentFormRequest, request.chat_id_, request.message_id_); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_payment_form({DialogId(request.chat_id_), MessageId(request.message_id_)}, std::move(promise)); } void Td::on_request(uint64 id, td_api::validateOrderInfo &request) { CHECK_IS_USER(); - CREATE_REQUEST(ValidateOrderInfoRequest, request.chat_id_, request.message_id_, std::move(request.order_info_), - request.allow_save_); + CREATE_REQUEST_PROMISE(); + messages_manager_->validate_order_info({DialogId(request.chat_id_), MessageId(request.message_id_)}, + std::move(request.order_info_), request.allow_save_, std::move(promise)); } void Td::on_request(uint64 id, td_api::sendPaymentForm &request) { @@ -7355,18 +7068,23 @@ void Td::on_request(uint64 id, td_api::sendPaymentForm &request) { if (request.credentials_ == nullptr) { return send_error_raw(id, 400, "Input payments credentials must not be empty"); } - CREATE_REQUEST(SendPaymentFormRequest, request.chat_id_, request.message_id_, std::move(request.order_info_id_), - std::move(request.shipping_option_id_), std::move(request.credentials_)); + CREATE_REQUEST_PROMISE(); + messages_manager_->send_payment_form({DialogId(request.chat_id_), MessageId(request.message_id_)}, + request.order_info_id_, request.shipping_option_id_, request.credentials_, + std::move(promise)); } void Td::on_request(uint64 id, const td_api::getPaymentReceipt &request) { CHECK_IS_USER(); - CREATE_REQUEST(GetPaymentReceiptRequest, request.chat_id_, request.message_id_); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_payment_receipt({DialogId(request.chat_id_), MessageId(request.message_id_)}, + std::move(promise)); } void Td::on_request(uint64 id, const td_api::getSavedOrderInfo &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetSavedOrderInfoRequest); + CREATE_REQUEST_PROMISE(); + get_saved_order_info(std::move(promise)); } void Td::on_request(uint64 id, const td_api::deleteSavedOrderInfo &request) { @@ -7586,18 +7304,21 @@ void Td::on_request(uint64 id, td_api::setBackground &request) { void Td::on_request(uint64 id, const td_api::removeBackground &request) { CHECK_IS_USER(); - CREATE_REQUEST(RemoveBackgroundRequest, request.background_id_); + CREATE_OK_REQUEST_PROMISE(); + background_manager_->remove_background(BackgroundId(request.background_id_), std::move(promise)); } void Td::on_request(uint64 id, const td_api::resetBackgrounds &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(ResetBackgroundsRequest); + CREATE_OK_REQUEST_PROMISE(); + background_manager_->reset_backgrounds(std::move(promise)); } void Td::on_request(uint64 id, td_api::getRecentlyVisitedTMeUrls &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.referrer_); - CREATE_REQUEST(GetRecentlyVisitedTMeUrlsRequest, std::move(request.referrer_)); + CREATE_REQUEST_PROMISE(); + create_handler(std::move(promise))->send(request.referrer_); } void Td::on_request(uint64 id, td_api::setBotUpdatesStatus &request) { @@ -7611,7 +7332,8 @@ void Td::on_request(uint64 id, td_api::sendCustomRequest &request) { CHECK_IS_BOT(); CLEAN_INPUT_STRING(request.method_); CLEAN_INPUT_STRING(request.parameters_); - CREATE_REQUEST(SendCustomRequestRequest, std::move(request.method_), std::move(request.parameters_)); + CREATE_REQUEST_PROMISE(); + create_handler(std::move(promise))->send(request.method_, request.parameters_); } void Td::on_request(uint64 id, td_api::answerCustomQuery &request) { @@ -7669,12 +7391,28 @@ void Td::on_request(uint64 id, td_api::acceptTermsOfService &request) { } void Td::on_request(uint64 id, const td_api::getCountryCode &request) { - CREATE_NO_ARGS_REQUEST(GetCountryCodeRequest); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(make_tl_object(result.move_as_ok())); + } + }); + create_handler(std::move(query_promise))->send(); } void Td::on_request(uint64 id, const td_api::getInviteText &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetInviteTextRequest); + CREATE_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + promise.set_value(make_tl_object(result.move_as_ok())); + } + }); + create_handler(std::move(query_promise))->send(); } void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) { @@ -7767,6 +7505,14 @@ void Td::on_request(uint64 id, const td_api::parseTextEntities &request) { UNREACHABLE(); } +void Td::on_request(uint64 id, const td_api::parseMarkdown &request) { + UNREACHABLE(); +} + +void Td::on_request(uint64 id, const td_api::getMarkdownText &request) { + UNREACHABLE(); +} + void Td::on_request(uint64 id, const td_api::getFileMimeType &request) { UNREACHABLE(); } @@ -7869,6 +7615,44 @@ td_api::object_ptr Td::do_static_request(td_api::parseTextEntiti return make_tl_object(std::move(request.text_), get_text_entities_object(r_entities.ok())); } +td_api::object_ptr Td::do_static_request(td_api::parseMarkdown &request) { + if (request.text_ == nullptr) { + return make_error(400, "Text must be non-empty"); + } + + auto r_entities = get_message_entities(nullptr, std::move(request.text_->entities_), true); + if (r_entities.is_error()) { + return make_error(400, r_entities.error().message()); + } + auto entities = r_entities.move_as_ok(); + auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true); + if (status.is_error()) { + return make_error(400, status.error().message()); + } + + auto parsed_text = parse_markdown_v3({std::move(request.text_->text_), std::move(entities)}); + fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).ensure(); + return get_formatted_text_object(parsed_text); +} + +td_api::object_ptr Td::do_static_request(td_api::getMarkdownText &request) { + if (request.text_ == nullptr) { + return make_error(400, "Text must be non-empty"); + } + + auto r_entities = get_message_entities(nullptr, std::move(request.text_->entities_)); + if (r_entities.is_error()) { + return make_error(400, r_entities.error().message()); + } + auto entities = r_entities.move_as_ok(); + auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true); + if (status.is_error()) { + return make_error(400, status.error().message()); + } + + return get_formatted_text_object(get_markdown_v3({std::move(request.text_->text_), std::move(entities)})); +} + td_api::object_ptr Td::do_static_request(const td_api::getFileMimeType &request) { // don't check file name UTF-8 correctness return make_tl_object(MimeType::from_extension(PathView(request.file_name_).extension())); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 2af2706e..e84c3cdf 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -227,7 +227,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr static_request(td_api::object_ptr function); private: - static constexpr const char *TDLIB_VERSION = "1.5.5"; + static constexpr const char *TDLIB_VERSION = "1.6.2"; static constexpr int64 ONLINE_ALARM_ID = 0; static constexpr int64 PING_SERVER_ALARM_ID = -1; static constexpr int32 PING_SERVER_TIMEOUT = 300; @@ -781,6 +781,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setUsername &request); + void on_request(uint64 id, td_api::setCommands &request); + + void on_request(uint64 id, const td_api::setLocation &request); + void on_request(uint64 id, td_api::setProfilePhoto &request); void on_request(uint64 id, const td_api::deleteProfilePhoto &request); @@ -835,6 +839,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::addStickerToSet &request); + void on_request(uint64 id, td_api::setStickerSetThumbnail &request); + void on_request(uint64 id, td_api::setStickerPositionInSet &request); void on_request(uint64 id, td_api::removeStickerFromSet &request); @@ -881,6 +887,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatStatisticsUrl &request); + void on_request(uint64 id, const td_api::getChatStatistics &request); + + void on_request(uint64 id, td_api::getChatStatisticsGraph &request); + void on_request(uint64 id, const td_api::getMapThumbnailFile &request); void on_request(uint64 id, const td_api::getLocalizationTargetInfo &request); @@ -927,6 +937,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::answerPreCheckoutQuery &request); + void on_request(uint64 id, td_api::getBankCardInfo &request); + void on_request(uint64 id, const td_api::getPaymentForm &request); void on_request(uint64 id, td_api::validateOrderInfo &request); @@ -1041,6 +1053,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::parseTextEntities &request); + void on_request(uint64 id, const td_api::parseMarkdown &request); + + void on_request(uint64 id, const td_api::getMarkdownText &request); + void on_request(uint64 id, const td_api::getFileMimeType &request); void on_request(uint64 id, const td_api::getFileExtension &request); @@ -1092,6 +1108,8 @@ class Td final : public NetQueryCallback { } static td_api::object_ptr do_static_request(const td_api::getTextEntities &request); static td_api::object_ptr do_static_request(td_api::parseTextEntities &request); + static td_api::object_ptr do_static_request(td_api::parseMarkdown &request); + static td_api::object_ptr do_static_request(td_api::getMarkdownText &request); static td_api::object_ptr do_static_request(const td_api::getFileMimeType &request); static td_api::object_ptr do_static_request(const td_api::getFileExtension &request); static td_api::object_ptr do_static_request(const td_api::cleanFileName &request); diff --git a/td/telegram/TermsOfService.cpp b/td/telegram/TermsOfService.cpp index 943c1b24..f8c641c9 100644 --- a/td/telegram/TermsOfService.cpp +++ b/td/telegram/TermsOfService.cpp @@ -27,7 +27,7 @@ class GetTermsOfServiceUpdateQuery : public Td::ResultHandler { void send() { // we don't poll terms of service before authorization - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getTermsOfServiceUpdate()))); + send_query(G()->net_query_creator().create(telegram_api::help_getTermsOfServiceUpdate())); } void on_result(uint64 id, BufferSlice packet) override { @@ -66,8 +66,8 @@ class AcceptTermsOfServiceQuery : public Td::ResultHandler { } void send(string terms_of_service_id) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_acceptTermsOfService( - telegram_api::make_object(std::move(terms_of_service_id)))))); + send_query(G()->net_query_creator().create(telegram_api::help_acceptTermsOfService( + telegram_api::make_object(std::move(terms_of_service_id))))); } void on_result(uint64 id, BufferSlice packet) override { @@ -100,7 +100,7 @@ TermsOfService::TermsOfService(telegram_api::object_ptrtext_)) { terms->text_.clear(); } - entities.clear(); + entities = find_entities(terms->text_, true); } if (terms->text_.empty()) { id_.clear(); diff --git a/td/telegram/TopDialogManager.cpp b/td/telegram/TopDialogManager.cpp index 03ce2130..c2d5f369 100644 --- a/td/telegram/TopDialogManager.cpp +++ b/td/telegram/TopDialogManager.cpp @@ -140,7 +140,7 @@ void TopDialogManager::send_toggle_top_peers(bool is_enabled) { LOG(DEBUG) << "Send toggle top peers query to " << is_enabled; have_toggle_top_peers_query_ = true; toggle_top_peers_query_is_enabled_ = is_enabled; - auto net_query = G()->net_query_creator().create(create_storer(telegram_api::contacts_toggleTopPeers(is_enabled))); + auto net_query = G()->net_query_creator().create(telegram_api::contacts_toggleTopPeers(is_enabled)); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 2)); } @@ -201,7 +201,7 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog if (input_peer != nullptr) { auto query = telegram_api::contacts_resetTopPeerRating(top_dialog_category_as_telegram_api(category), std::move(input_peer)); - auto net_query = G()->net_query_creator().create(create_storer(query)); + auto net_query = G()->net_query_creator().create(query); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 1)); } @@ -398,7 +398,7 @@ void TopDialogManager::do_get_top_peers() { 0 /*offset*/, 100 /*limit*/, hash}; - auto net_query = G()->net_query_creator().create(create_storer(query)); + auto net_query = G()->net_query_creator().create(query); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this)); } diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 1742556c..b8337daa 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -79,7 +79,7 @@ class GetUpdatesStateQuery : public Td::ResultHandler { void send() { // TODO this call must be first after client is logged in, there must be no API calls before // it succeeds - send_query(G()->net_query_creator().create(create_storer(telegram_api::updates_getState()))); + send_query(G()->net_query_creator().create(telegram_api::updates_getState())); } void on_result(uint64 id, BufferSlice packet) override { @@ -106,7 +106,7 @@ class GetUpdatesStateQuery : public Td::ResultHandler { class PingServerQuery : public Td::ResultHandler { public: void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::updates_getState()))); + send_query(G()->net_query_creator().create(telegram_api::updates_getState())); } void on_result(uint64 id, BufferSlice packet) override { @@ -138,8 +138,7 @@ class GetDifferenceQuery : public Td::ResultHandler { VLOG(get_difference) << tag("pts", pts) << tag("qts", qts) << tag("date", date); - send_query( - G()->net_query_creator().create(create_storer(telegram_api::updates_getDifference(0, pts, 0, date, qts)))); + send_query(G()->net_query_creator().create(telegram_api::updates_getDifference(0, pts, 0, date, qts))); } void on_result(uint64 id, BufferSlice packet) override { @@ -189,6 +188,9 @@ void UpdatesManager::fill_get_difference_gap(void *td) { void UpdatesManager::fill_gap(void *td, const char *source) { CHECK(td != nullptr); + if (G()->close_flag()) { + return; + } auto updates_manager = static_cast(td)->updates_manager_.get(); LOG(WARNING) << "Filling gap in " << source << " by running getDifference"; @@ -636,10 +638,25 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat LOG(INFO) << "Receive " << to_string(updates_ptr); } if (!td_->auth_manager_->is_authorized()) { - if (updates_type == telegram_api::updateShort::ID && - static_cast(updates_ptr.get())->update_->get_id() == - telegram_api::updateLoginToken::ID) { - return td_->auth_manager_->on_update_login_token(); + if (updates_type == telegram_api::updateShort::ID && !G()->close_flag()) { + auto &update = static_cast(updates_ptr.get())->update_; + auto update_id = update->get_id(); + if (update_id == telegram_api::updateLoginToken::ID) { + return td_->auth_manager_->on_update_login_token(); + } + + switch (update_id) { + case telegram_api::updateServiceNotification::ID: + case telegram_api::updateDcOptions::ID: + case telegram_api::updateConfig::ID: + case telegram_api::updateLangPackTooLong::ID: + case telegram_api::updateLangPack::ID: + LOG(INFO) << "Apply without authorization " << to_string(updates_ptr); + downcast_call(*update, OnUpdate(this, update, false)); + return; + default: + break; + } } LOG(INFO) << "Ignore received before authorization or after logout " << to_string(updates_ptr); return; @@ -1095,7 +1112,7 @@ void UpdatesManager::after_get_difference() { } if (postponed_updates_.size()) { - VLOG(get_difference) << "Begin to apply postponed updates"; + VLOG(get_difference) << "Begin to apply " << postponed_updates_.size() << " postponed updates"; while (!postponed_updates_.empty()) { auto it = postponed_updates_.begin(); auto updates = std::move(it->second.updates); @@ -1105,7 +1122,8 @@ void UpdatesManager::after_get_difference() { postponed_updates_.erase(it); on_pending_updates(std::move(updates), updates_seq_begin, updates_seq_end, 0, "postponed updates"); if (running_get_difference_) { - VLOG(get_difference) << "Finish to apply postponed updates because forced to run getDifference"; + VLOG(get_difference) << "Finish to apply postponed updates with " << postponed_updates_.size() + << " updates left, because forced to run getDifference"; return; } } @@ -1172,16 +1190,23 @@ void UpdatesManager::on_pending_updates(vectorget_id() != telegram_api::messageService::ID) { + if (message_ptr != nullptr) { auto dialog_id = td_->messages_manager_->get_message_dialog_id(*message_ptr); if (dialog_id.get_type() == DialogType::Channel) { - LOG(INFO) << "Replace update about new message with updateChannelTooLong in " << dialog_id; auto channel_id = dialog_id.get_channel_id(); if (td_->contacts_manager_->have_channel_force(channel_id)) { - update = telegram_api::make_object( - telegram_api::updateChannelTooLong::PTS_MASK, channel_id.get(), pts); - continue; + if (td_->messages_manager_->is_old_channel_update(dialog_id, pts)) { + // the update will be ignored anyway, so there is no reason to replace it or force get_difference + LOG(INFO) << "Allow an outdated unacceptable update from " << source; + continue; + } + if ((*message_ptr)->get_id() != telegram_api::messageService::ID) { + // don't replace service messages, because they can be about bot's kicking + LOG(INFO) << "Replace update about new message with updateChannelTooLong in " << dialog_id; + update = telegram_api::make_object( + telegram_api::updateChannelTooLong::PTS_MASK, channel_id.get(), pts); + continue; + } } } else { LOG(ERROR) << "Update is not from a channel: " << to_string(update); @@ -2007,4 +2032,13 @@ void UpdatesManager::on_update(tl_object_ptr upd void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { } +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { +} + +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { +} + +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { +} + } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index e2c95c8b..28036f15 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -291,6 +291,10 @@ class UpdatesManager : public Actor { // unsupported updates void on_update(tl_object_ptr update, bool /*force_apply*/); + + void on_update(tl_object_ptr update, bool /*force_apply*/); + void on_update(tl_object_ptr update, bool /*force_apply*/); + void on_update(tl_object_ptr update, bool /*force_apply*/); }; } // namespace td diff --git a/td/telegram/Version.h b/td/telegram/Version.h index 2854ee93..113a6231 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -8,7 +8,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 109; +constexpr int32 MTPROTO_LAYER = 111; enum class Version : int32 { Initial, diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 000e4a69..b213a918 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -17,7 +17,6 @@ #include "td/telegram/DocumentsManager.h" #include "td/telegram/DocumentsManager.hpp" #include "td/telegram/files/FileId.h" -#include "td/telegram/Global.h" #include "td/telegram/Location.h" #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" @@ -38,11 +37,29 @@ namespace td { -namespace { +class RichText; + +struct GetWebPageBlockObjectContext { + Td *td_ = nullptr; + Slice base_url_; + + bool is_first_pass_ = true; + bool has_anchor_urls_ = false; + std::unordered_map anchors_; // anchor -> text +}; + +static vector> get_page_block_objects( + const vector> &page_blocks, GetWebPageBlockObjectContext *context) { + return transform(page_blocks, [context](const unique_ptr &page_block) { + return page_block->get_page_block_object(context); + }); +} class RichText { - static vector> get_rich_text_objects(const vector &rich_texts) { - return transform(rich_texts, [](const RichText &rich_text) { return rich_text.get_rich_text_object(); }); + static vector> get_rich_text_objects(const vector &rich_texts, + GetWebPageBlockObjectContext *context) { + return transform(rich_texts, + [context](const RichText &rich_text) { return rich_text.get_rich_text_object(context); }); } public: @@ -73,54 +90,86 @@ class RichText { return type == Type::Plain && content.empty(); } - void append_file_ids(vector &file_ids) const { + void append_file_ids(const Td *td, vector &file_ids) const { if (type == RichText::Type::Icon) { CHECK(document_file_id.is_valid()); - Document(Document::Type::General, document_file_id).append_file_ids(G()->td().get_actor_unsafe(), file_ids); + Document(Document::Type::General, document_file_id).append_file_ids(td, file_ids); } else { for (auto &text : texts) { - text.append_file_ids(file_ids); + text.append_file_ids(td, file_ids); } } } - td_api::object_ptr get_rich_text_object() const { + td_api::object_ptr get_rich_text_object(GetWebPageBlockObjectContext *context) const { switch (type) { case RichText::Type::Plain: return make_tl_object(content); case RichText::Type::Bold: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Italic: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Underline: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Strikethrough: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Fixed: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Url: - return make_tl_object(texts[0].get_rich_text_object(), content, web_page_id.is_valid()); + if (!context->base_url_.empty() && begins_with(content, context->base_url_) && + content[context->base_url_.size()] == '#') { + if (context->is_first_pass_) { + context->has_anchor_urls_ = true; + } else { + auto anchor = Slice(content).substr(context->base_url_.size() + 1); + auto it = context->anchors_.find(anchor); + if (it != context->anchors_.end()) { + if (it->second == nullptr) { + return make_tl_object(texts[0].get_rich_text_object(context), anchor.str(), + content); + } else { + context->is_first_pass_ = true; + auto reference_text = it->second->get_rich_text_object(context); + context->is_first_pass_ = false; + return make_tl_object(texts[0].get_rich_text_object(context), + std::move(reference_text), content); + } + } + } + } + return make_tl_object(texts[0].get_rich_text_object(context), content, + web_page_id.is_valid()); case RichText::Type::EmailAddress: - return make_tl_object(texts[0].get_rich_text_object(), content); + return make_tl_object(texts[0].get_rich_text_object(context), content); case RichText::Type::Concatenation: - return make_tl_object(get_rich_text_objects(texts)); + return make_tl_object(get_rich_text_objects(texts, context)); case RichText::Type::Subscript: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Superscript: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::Marked: - return make_tl_object(texts[0].get_rich_text_object()); + return make_tl_object(texts[0].get_rich_text_object(context)); case RichText::Type::PhoneNumber: - return make_tl_object(texts[0].get_rich_text_object(), content); + return make_tl_object(texts[0].get_rich_text_object(context), content); case RichText::Type::Icon: { auto dimensions = to_integer(content); auto width = static_cast(dimensions / 65536); auto height = static_cast(dimensions % 65536); return make_tl_object( - G()->td().get_actor_unsafe()->documents_manager_->get_document_object(document_file_id), width, height); + context->td_->documents_manager_->get_document_object(document_file_id), width, height); + } + case RichText::Type::Anchor: { + if (context->is_first_pass_) { + context->anchors_.emplace(Slice(content), texts[0].empty() ? nullptr : &texts[0]); + } + if (texts[0].empty()) { + return make_tl_object(content); + } + auto result = make_tl_object(); + result->texts_.push_back(make_tl_object(content)); + result->texts_.push_back(texts[0].get_rich_text_object(context)); + return std::move(result); } - case RichText::Type::Anchor: - return make_tl_object(texts[0].get_rich_text_object(), content); } UNREACHABLE(); return nullptr; @@ -163,18 +212,22 @@ class RichText { } }; +namespace { + class WebPageBlockCaption { public: RichText text; RichText credit; - void append_file_ids(vector &file_ids) const { - text.append_file_ids(file_ids); - credit.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const { + text.append_file_ids(td, file_ids); + credit.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_caption_object() const { - return td_api::make_object(text.get_rich_text_object(), credit.get_rich_text_object()); + td_api::object_ptr get_page_block_caption_object( + GetWebPageBlockObjectContext *context) const { + return td_api::make_object(text.get_rich_text_object(context), + credit.get_rich_text_object(context)); } template @@ -209,7 +262,8 @@ class WebPageBlockTableCell { int32 colspan = 1; int32 rowspan = 1; - td_api::object_ptr get_page_block_table_cell_object() const { + td_api::object_ptr get_page_block_table_cell_object( + GetWebPageBlockObjectContext *context) const { auto align = [&]() -> td_api::object_ptr { if (align_left) { return td_api::make_object(); @@ -236,7 +290,7 @@ class WebPageBlockTableCell { UNREACHABLE(); return nullptr; }(); - return td_api::make_object(text.empty() ? nullptr : text.get_rich_text_object(), + return td_api::make_object(text.empty() ? nullptr : text.get_rich_text_object(context), is_header, colspan, rowspan, std::move(align), std::move(valign)); } @@ -394,12 +448,12 @@ class WebPageBlockTitle : public WebPageBlock { return Type::Title; } - void append_file_ids(vector &file_ids) const override { - title.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + title.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(title.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(title.get_rich_text_object(context)); } template @@ -427,12 +481,12 @@ class WebPageBlockSubtitle : public WebPageBlock { return Type::Subtitle; } - void append_file_ids(vector &file_ids) const override { - subtitle.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + subtitle.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(subtitle.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(subtitle.get_rich_text_object(context)); } template @@ -461,12 +515,12 @@ class WebPageBlockAuthorDate : public WebPageBlock { return Type::AuthorDate; } - void append_file_ids(vector &file_ids) const override { - author.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + author.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(author.get_rich_text_object(), date); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(author.get_rich_text_object(context), date); } template @@ -496,12 +550,12 @@ class WebPageBlockHeader : public WebPageBlock { return Type::Header; } - void append_file_ids(vector &file_ids) const override { - header.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + header.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(header.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(header.get_rich_text_object(context)); } template @@ -529,12 +583,12 @@ class WebPageBlockSubheader : public WebPageBlock { return Type::Subheader; } - void append_file_ids(vector &file_ids) const override { - subheader.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + subheader.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(subheader.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(subheader.get_rich_text_object(context)); } template @@ -562,12 +616,12 @@ class WebPageBlockKicker : public WebPageBlock { return Type::Kicker; } - void append_file_ids(vector &file_ids) const override { - kicker.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + kicker.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(kicker.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(kicker.get_rich_text_object(context)); } template @@ -595,12 +649,12 @@ class WebPageBlockParagraph : public WebPageBlock { return Type::Paragraph; } - void append_file_ids(vector &file_ids) const override { - text.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + text.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(text.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(text.get_rich_text_object(context)); } template @@ -629,12 +683,12 @@ class WebPageBlockPreformatted : public WebPageBlock { return Type::Preformatted; } - void append_file_ids(vector &file_ids) const override { - text.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + text.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(text.get_rich_text_object(), language); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(text.get_rich_text_object(context), language); } template @@ -664,12 +718,12 @@ class WebPageBlockFooter : public WebPageBlock { return Type::Footer; } - void append_file_ids(vector &file_ids) const override { - footer.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + footer.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(footer.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(footer.get_rich_text_object(context)); } template @@ -691,10 +745,10 @@ class WebPageBlockDivider : public WebPageBlock { return Type::Divider; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object(); } @@ -719,10 +773,13 @@ class WebPageBlockAnchor : public WebPageBlock { return Type::Anchor; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { + if (context->is_first_pass_) { + context->anchors_.emplace(name, nullptr); + } return make_tl_object(name); } @@ -763,10 +820,11 @@ class WebPageBlockList : public WebPageBlock { private: vector items; - static td_api::object_ptr get_page_block_list_item_object(const Item &item) { + static td_api::object_ptr get_page_block_list_item_object(const Item &item, + Context *context) { // if label is empty, then Bullet U+2022 is used as a label return td_api::make_object(item.label.empty() ? "\xE2\x80\xA2" : item.label, - get_page_block_objects(item.page_blocks)); + get_page_block_objects(item.page_blocks, context)); } public: @@ -778,17 +836,17 @@ class WebPageBlockList : public WebPageBlock { return Type::List; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { for (auto &item : items) { for (auto &page_block : item.page_blocks) { - page_block->append_file_ids(file_ids); + page_block->append_file_ids(td, file_ids); } } } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return td_api::make_object( - transform(items, [](const Item &item) { return get_page_block_list_item_object(item); })); + transform(items, [context](const Item &item) { return get_page_block_list_item_object(item, context); })); } template @@ -841,13 +899,14 @@ class WebPageBlockBlockQuote : public WebPageBlock { return Type::BlockQuote; } - void append_file_ids(vector &file_ids) const override { - text.append_file_ids(file_ids); - credit.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + text.append_file_ids(td, file_ids); + credit.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(text.get_rich_text_object(), credit.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(text.get_rich_text_object(context), + credit.get_rich_text_object(context)); } template @@ -878,13 +937,14 @@ class WebPageBlockPullQuote : public WebPageBlock { return Type::PullQuote; } - void append_file_ids(vector &file_ids) const override { - text.append_file_ids(file_ids); - credit.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + text.append_file_ids(td, file_ids); + credit.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(text.get_rich_text_object(), credit.get_rich_text_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(text.get_rich_text_object(context), + credit.get_rich_text_object(context)); } template @@ -917,16 +977,15 @@ class WebPageBlockAnimation : public WebPageBlock { return Type::Animation; } - void append_file_ids(vector &file_ids) const override { - caption.append_file_ids(file_ids); - Document(Document::Type::Animation, animation_file_id).append_file_ids(G()->td().get_actor_unsafe(), file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + caption.append_file_ids(td, file_ids); + Document(Document::Type::Animation, animation_file_id).append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object( - G()->td().get_actor_unsafe()->animations_manager_->get_animation_object(animation_file_id, - "get_page_block_object"), - caption.get_page_block_caption_object(), need_autoplay); + context->td_->animations_manager_->get_animation_object(animation_file_id, "get_page_block_object"), + caption.get_page_block_caption_object(context), need_autoplay); } template @@ -985,15 +1044,14 @@ class WebPageBlockPhoto : public WebPageBlock { return Type::Photo; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { append(file_ids, photo_get_file_ids(photo)); - caption.append_file_ids(file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object( - get_photo_object(G()->td().get_actor_unsafe()->file_manager_.get(), &photo), - caption.get_page_block_caption_object(), url); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(get_photo_object(context->td_->file_manager_.get(), &photo), + caption.get_page_block_caption_object(context), url); } template @@ -1036,15 +1094,15 @@ class WebPageBlockVideo : public WebPageBlock { return Type::Video; } - void append_file_ids(vector &file_ids) const override { - caption.append_file_ids(file_ids); - Document(Document::Type::Video, video_file_id).append_file_ids(G()->td().get_actor_unsafe(), file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + caption.append_file_ids(td, file_ids); + Document(Document::Type::Video, video_file_id).append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object( - G()->td().get_actor_unsafe()->videos_manager_->get_video_object(video_file_id), - caption.get_page_block_caption_object(), need_autoplay, is_looped); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(context->td_->videos_manager_->get_video_object(video_file_id), + caption.get_page_block_caption_object(context), need_autoplay, + is_looped); } template @@ -1101,12 +1159,12 @@ class WebPageBlockCover : public WebPageBlock { return Type::Cover; } - void append_file_ids(vector &file_ids) const override { - cover->append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + cover->append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(cover->get_page_block_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(cover->get_page_block_object(context)); } template @@ -1148,15 +1206,15 @@ class WebPageBlockEmbedded : public WebPageBlock { return Type::Embedded; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { append(file_ids, photo_get_file_ids(poster_photo)); - caption.append_file_ids(file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object( - url, html, get_photo_object(G()->td().get_actor_unsafe()->file_manager_.get(), &poster_photo), dimensions.width, - dimensions.height, caption.get_page_block_caption_object(), is_full_width, allow_scrolling); + url, html, get_photo_object(context->td_->file_manager_.get(), &poster_photo), dimensions.width, + dimensions.height, caption.get_page_block_caption_object(context), is_full_width, allow_scrolling); } template @@ -1214,18 +1272,18 @@ class WebPageBlockEmbeddedPost : public WebPageBlock { return Type::EmbeddedPost; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { append(file_ids, photo_get_file_ids(author_photo)); for (auto &page_block : page_blocks) { - page_block->append_file_ids(file_ids); + page_block->append_file_ids(td, file_ids); } - caption.append_file_ids(file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object( - url, author, get_photo_object(G()->td().get_actor_unsafe()->file_manager_.get(), &author_photo), date, - get_page_block_objects(page_blocks), caption.get_page_block_caption_object()); + url, author, get_photo_object(context->td_->file_manager_.get(), &author_photo), date, + get_page_block_objects(page_blocks, context), caption.get_page_block_caption_object(context)); } template @@ -1265,16 +1323,16 @@ class WebPageBlockCollage : public WebPageBlock { return Type::Collage; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { for (auto &page_block : page_blocks) { - page_block->append_file_ids(file_ids); + page_block->append_file_ids(td, file_ids); } - caption.append_file_ids(file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(get_page_block_objects(page_blocks), - caption.get_page_block_caption_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(get_page_block_objects(page_blocks, context), + caption.get_page_block_caption_object(context)); } template @@ -1306,16 +1364,16 @@ class WebPageBlockSlideshow : public WebPageBlock { return Type::Slideshow; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { for (auto &page_block : page_blocks) { - page_block->append_file_ids(file_ids); + page_block->append_file_ids(td, file_ids); } - caption.append_file_ids(file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(get_page_block_objects(page_blocks), - caption.get_page_block_caption_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(get_page_block_objects(page_blocks, context), + caption.get_page_block_caption_object(context)); } template @@ -1348,13 +1406,13 @@ class WebPageBlockChatLink : public WebPageBlock { return Type::ChatLink; } - void append_file_ids(vector &file_ids) const override { + void append_file_ids(const Td *td, vector &file_ids) const override { append(file_ids, dialog_photo_get_file_ids(photo)); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object( - title, get_chat_photo_object(G()->td().get_actor_unsafe()->file_manager_.get(), &photo), username); + title, get_chat_photo_object(context->td_->file_manager_.get(), &photo), username); } template @@ -1388,15 +1446,14 @@ class WebPageBlockAudio : public WebPageBlock { return Type::Audio; } - void append_file_ids(vector &file_ids) const override { - Document(Document::Type::Audio, audio_file_id).append_file_ids(G()->td().get_actor_unsafe(), file_ids); - caption.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + Document(Document::Type::Audio, audio_file_id).append_file_ids(td, file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object( - G()->td().get_actor_unsafe()->audios_manager_->get_audio_object(audio_file_id), - caption.get_page_block_caption_object()); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(context->td_->audios_manager_->get_audio_object(audio_file_id), + caption.get_page_block_caption_object(context)); } template @@ -1460,22 +1517,23 @@ class WebPageBlockTable : public WebPageBlock { return Type::Table; } - void append_file_ids(vector &file_ids) const override { - title.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + title.append_file_ids(td, file_ids); for (auto &row : cells) { for (auto &cell : row) { - cell.text.append_file_ids(file_ids); + cell.text.append_file_ids(td, file_ids); } } } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { auto cell_objects = transform(cells, [&](const vector &row) { - return transform(row, [&](const WebPageBlockTableCell &cell) { return cell.get_page_block_table_cell_object(); }); + return transform( + row, [&](const WebPageBlockTableCell &cell) { return cell.get_page_block_table_cell_object(context); }); }); - return make_tl_object(title.get_rich_text_object(), std::move(cell_objects), is_bordered, - is_striped); + return make_tl_object(title.get_rich_text_object(context), std::move(cell_objects), + is_bordered, is_striped); } template @@ -1516,16 +1574,16 @@ class WebPageBlockDetails : public WebPageBlock { return Type::Details; } - void append_file_ids(vector &file_ids) const override { - header.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + header.append_file_ids(td, file_ids); for (auto &page_block : page_blocks) { - page_block->append_file_ids(file_ids); + page_block->append_file_ids(td, file_ids); } } - td_api::object_ptr get_page_block_object() const override { - return make_tl_object(header.get_rich_text_object(), get_page_block_objects(page_blocks), - is_open); + td_api::object_ptr get_page_block_object(Context *context) const override { + return make_tl_object(header.get_rich_text_object(context), + get_page_block_objects(page_blocks, context), is_open); } template @@ -1563,8 +1621,8 @@ class WebPageBlockRelatedArticles : public WebPageBlock { return Type::RelatedArticles; } - void append_file_ids(vector &file_ids) const override { - header.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + header.append_file_ids(td, file_ids); for (auto &article : related_articles) { if (article.photo.id != -2) { append(file_ids, photo_get_file_ids(article.photo)); @@ -1572,14 +1630,13 @@ class WebPageBlockRelatedArticles : public WebPageBlock { } } - td_api::object_ptr get_page_block_object() const override { - auto related_article_objects = transform(related_articles, [](const RelatedArticle &article) { + td_api::object_ptr get_page_block_object(Context *context) const override { + auto related_article_objects = transform(related_articles, [context](const RelatedArticle &article) { return td_api::make_object( article.url, article.title, article.description, - get_photo_object(G()->td().get_actor_unsafe()->file_manager_.get(), &article.photo), article.author, - article.published_date); + get_photo_object(context->td_->file_manager_.get(), &article.photo), article.author, article.published_date); }); - return make_tl_object(header.get_rich_text_object(), + return make_tl_object(header.get_rich_text_object(context), std::move(related_article_objects)); } @@ -1614,13 +1671,13 @@ class WebPageBlockMap : public WebPageBlock { return Type::Map; } - void append_file_ids(vector &file_ids) const override { - caption.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object(location.get_location_object(), zoom, dimensions.width, - dimensions.height, caption.get_page_block_caption_object()); + dimensions.height, caption.get_page_block_caption_object(context)); } template @@ -1656,15 +1713,15 @@ class WebPageBlockVoiceNote : public WebPageBlock { return Type::VoiceNote; } - void append_file_ids(vector &file_ids) const override { - Document(Document::Type::VoiceNote, voice_note_file_id).append_file_ids(G()->td().get_actor_unsafe(), file_ids); - caption.append_file_ids(file_ids); + void append_file_ids(const Td *td, vector &file_ids) const override { + Document(Document::Type::VoiceNote, voice_note_file_id).append_file_ids(td, file_ids); + caption.append_file_ids(td, file_ids); } - td_api::object_ptr get_page_block_object() const override { + td_api::object_ptr get_page_block_object(Context *context) const override { return make_tl_object( - G()->td().get_actor_unsafe()->voice_notes_manager_->get_voice_note_object(voice_note_file_id), - caption.get_page_block_caption_object()); + context->td_->voice_notes_manager_->get_voice_note_object(voice_note_file_id), + caption.get_page_block_caption_object(context)); } template @@ -2331,9 +2388,18 @@ vector> get_web_page_blocks( } vector> get_page_block_objects( - const vector> &page_blocks) { - return transform(page_blocks, - [](const unique_ptr &page_block) { return page_block->get_page_block_object(); }); + const vector> &page_blocks, Td *td, Slice base_url) { + GetWebPageBlockObjectContext context; + context.td_ = td; + context.base_url_ = base_url; + auto blocks = get_page_block_objects(page_blocks, &context); + if (context.anchors_.empty() || !context.has_anchor_urls_) { + return blocks; + } + + context.is_first_pass_ = false; + context.anchors_.emplace(Slice(), nullptr); // back to top + return get_page_block_objects(page_blocks, &context); } } // namespace td diff --git a/td/telegram/WebPageBlock.h b/td/telegram/WebPageBlock.h index 556ceb00..e293558a 100644 --- a/td/telegram/WebPageBlock.h +++ b/td/telegram/WebPageBlock.h @@ -14,11 +14,14 @@ #include "td/telegram/Photo.h" #include "td/utils/common.h" +#include "td/utils/Slice.h" #include namespace td { +struct GetWebPageBlockObjectContext; + class Td; class WebPageBlock { @@ -73,6 +76,8 @@ class WebPageBlock { template friend void parse_web_page_block(unique_ptr &block, ParserT &parser); + using Context = GetWebPageBlockObjectContext; + public: WebPageBlock() = default; WebPageBlock(const WebPageBlock &) = delete; @@ -81,9 +86,9 @@ class WebPageBlock { WebPageBlock &operator=(WebPageBlock &&) = delete; virtual ~WebPageBlock() = default; - virtual void append_file_ids(vector &file_ids) const = 0; + virtual void append_file_ids(const Td *td, vector &file_ids) const = 0; - virtual td_api::object_ptr get_page_block_object() const = 0; + virtual td_api::object_ptr get_page_block_object(Context *context) const = 0; }; void store(const unique_ptr &block, LogEventStorerCalcLength &storer); @@ -99,6 +104,6 @@ vector> get_web_page_blocks( const std::unordered_map &videos, const std::unordered_map &voice_notes); vector> get_page_block_objects( - const vector> &page_blocks); + const vector> &page_blocks, Td *td, Slice base_url); } // namespace td diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index 30526c84..55d5ecd9 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -41,11 +41,13 @@ #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/format.h" +#include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" +#include "td/utils/utf8.h" namespace td { @@ -68,8 +70,8 @@ class GetWebPagePreviewQuery : public Td::ResultHandler { flags |= telegram_api::messages_getWebPagePreview::ENTITIES_MASK; } - send_query(G()->net_query_creator().create( - create_storer(telegram_api::messages_getWebPagePreview(flags, text, std::move(entities))))); + send_query( + G()->net_query_creator().create(telegram_api::messages_getWebPagePreview(flags, text, std::move(entities)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -90,15 +92,17 @@ class GetWebPagePreviewQuery : public Td::ResultHandler { class GetWebPageQuery : public Td::ResultHandler { Promise promise_; + WebPageId web_page_id_; string url_; public: explicit GetWebPageQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(const string &url, int32 hash) { + void send(WebPageId web_page_id, const string &url, int32 hash) { + web_page_id_ = web_page_id; url_ = url; - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getWebPage(url, hash)))); + send_query(G()->net_query_creator().create(telegram_api::messages_getWebPage(url, hash))); } void on_result(uint64 id, BufferSlice packet) override { @@ -109,7 +113,17 @@ class GetWebPageQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetWebPageQuery " << to_string(ptr); - if (ptr->get_id() != telegram_api::webPageNotModified::ID) { + if (ptr->get_id() == telegram_api::webPageNotModified::ID) { + if (web_page_id_.is_valid()) { + auto web_page = move_tl_object_as(ptr); + int32 view_count = (web_page->flags_ & telegram_api::webPageNotModified::CACHED_PAGE_VIEWS_MASK) != 0 + ? web_page->cached_page_views_ + : 0; + td->web_pages_manager_->on_get_web_page_instant_view_view_count(web_page_id_, view_count); + } else { + LOG(ERROR) << "Receive webPageNotModified for " << url_; + } + } else { auto web_page_id = td->web_pages_manager_->on_get_web_page(std::move(ptr), DialogId()); td->web_pages_manager_->on_get_web_page_by_url(url_, web_page_id, false); } @@ -126,6 +140,7 @@ class WebPagesManager::WebPageInstantView { public: vector> page_blocks; string url; + int32 view_count = 0; int32 hash = 0; bool is_v2 = false; bool is_rtl = false; @@ -138,12 +153,14 @@ class WebPagesManager::WebPageInstantView { void store(StorerT &storer) const { using ::td::store; bool has_url = !url.empty(); + bool has_view_count = view_count > 0; BEGIN_STORE_FLAGS(); STORE_FLAG(is_full); STORE_FLAG(is_loaded); STORE_FLAG(is_rtl); STORE_FLAG(is_v2); STORE_FLAG(has_url); + STORE_FLAG(has_view_count); END_STORE_FLAGS(); store(page_blocks, storer); @@ -151,6 +168,9 @@ class WebPagesManager::WebPageInstantView { if (has_url) { store(url, storer); } + if (has_view_count) { + store(view_count, storer); + } CHECK(!is_empty); } @@ -158,12 +178,14 @@ class WebPagesManager::WebPageInstantView { void parse(ParserT &parser) { using ::td::parse; bool has_url; + bool has_view_count; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_full); PARSE_FLAG(is_loaded); PARSE_FLAG(is_rtl); PARSE_FLAG(is_v2); PARSE_FLAG(has_url); + PARSE_FLAG(has_view_count); END_PARSE_FLAGS(); parse(page_blocks, parser); @@ -171,15 +193,19 @@ class WebPagesManager::WebPageInstantView { if (has_url) { parse(url, parser); } + if (has_view_count) { + parse(view_count, parser); + } is_empty = false; } friend StringBuilder &operator<<(StringBuilder &string_builder, const WebPagesManager::WebPageInstantView &instant_view) { return string_builder << "InstantView(url = " << instant_view.url << ", size = " << instant_view.page_blocks.size() - << ", hash = " << instant_view.hash << ", is_empty = " << instant_view.is_empty - << ", is_v2 = " << instant_view.is_v2 << ", is_rtl = " << instant_view.is_rtl - << ", is_full = " << instant_view.is_full << ", is_loaded = " << instant_view.is_loaded + << ", view_count = " << instant_view.view_count << ", hash = " << instant_view.hash + << ", is_empty = " << instant_view.is_empty << ", is_v2 = " << instant_view.is_v2 + << ", is_rtl = " << instant_view.is_rtl << ", is_full = " << instant_view.is_full + << ", is_loaded = " << instant_view.is_loaded << ", was_loaded_from_database = " << instant_view.was_loaded_from_database << ")"; } }; @@ -631,11 +657,29 @@ bool WebPagesManager::need_use_old_instant_view(const WebPageInstantView &new_in return new_instant_view.was_loaded_from_database; } +void WebPagesManager::on_get_web_page_instant_view_view_count(WebPageId web_page_id, int32 view_count) { + if (get_web_page_instant_view(web_page_id) == nullptr) { + return; + } + + auto *instant_view = &web_pages_[web_page_id]->instant_view; + CHECK(!instant_view->is_empty); + if (instant_view->view_count >= view_count) { + return; + } + instant_view->view_count = view_count; + if (G()->parameters().use_message_db) { + LOG(INFO) << "Save instant view of " << web_page_id << " to database after updating view count to " << view_count; + G()->td_db()->get_sqlite_pmc()->set(get_web_page_instant_view_database_key(web_page_id), + log_event_store(*instant_view).as_slice().str(), Auto()); + } +} + void WebPagesManager::on_get_web_page_by_url(const string &url, WebPageId web_page_id, bool from_database) { auto &cached_web_page_id = url_to_web_page_id_[url]; if (!from_database && G()->parameters().use_message_db) { if (web_page_id.is_valid()) { - if (cached_web_page_id != web_page_id) { //not already saved + if (cached_web_page_id != web_page_id) { // not already saved G()->td_db()->get_sqlite_pmc()->set(get_web_page_url_database_key(url), to_string(web_page_id.get()), Auto()); } } else { @@ -650,14 +694,14 @@ void WebPagesManager::on_get_web_page_by_url(const string &url, WebPageId web_pa cached_web_page_id = web_page_id; } -void WebPagesManager::register_web_page(WebPageId web_page_id, FullMessageId full_message_id) { +void WebPagesManager::register_web_page(WebPageId web_page_id, FullMessageId full_message_id, const char *source) { if (!web_page_id.is_valid()) { return; } - LOG(INFO) << "Register " << web_page_id << " from " << full_message_id; + LOG(INFO) << "Register " << web_page_id << " from " << full_message_id << " from " << source; bool is_inserted = web_page_messages_[web_page_id].insert(full_message_id).second; - CHECK(is_inserted); + LOG_CHECK(is_inserted) << source << " " << web_page_id << " " << full_message_id; if (!td_->auth_manager_->is_bot() && !have_web_page_force(web_page_id)) { LOG(INFO) << "Waiting for " << web_page_id << " needed in " << full_message_id; @@ -665,15 +709,15 @@ void WebPagesManager::register_web_page(WebPageId web_page_id, FullMessageId ful } } -void WebPagesManager::unregister_web_page(WebPageId web_page_id, FullMessageId full_message_id) { +void WebPagesManager::unregister_web_page(WebPageId web_page_id, FullMessageId full_message_id, const char *source) { if (!web_page_id.is_valid()) { return; } - LOG(INFO) << "Unregister " << web_page_id << " from " << full_message_id; + LOG(INFO) << "Unregister " << web_page_id << " from " << full_message_id << " from " << source; auto &message_ids = web_page_messages_[web_page_id]; auto is_deleted = message_ids.erase(full_message_id); - CHECK(is_deleted); + LOG_CHECK(is_deleted) << source << " " << web_page_id << " " << full_message_id; if (message_ids.empty()) { web_page_messages_.erase(web_page_id); @@ -748,7 +792,7 @@ int64 WebPagesManager::get_web_page_preview(td_api::object_ptrtext_, entities, true, false, true, false); - if (text->text_.empty()) { + if (result.is_error() || text->text_.empty()) { promise.set_value(Unit()); return 0; } @@ -871,7 +915,7 @@ void WebPagesManager::reload_web_page_instant_view(WebPageId web_page_id) { } td_->create_handler(std::move(promise)) - ->send(web_page->url, web_page->instant_view.is_full ? web_page->instant_view.hash : 0); + ->send(web_page_id, web_page->url, web_page->instant_view.is_full ? web_page->instant_view.hash : 0); } void WebPagesManager::on_load_web_page_instant_view_from_database(WebPageId web_page_id, string value) { @@ -1090,7 +1134,7 @@ void WebPagesManager::reload_web_page_by_url(const string &url, Promise && } LOG(INFO) << "Reload url \"" << url << '"'; - td_->create_handler(std::move(promise))->send(url, 0); + td_->create_handler(std::move(promise))->send(WebPageId(), url, 0); } SecretInputMedia WebPagesManager::get_secret_input_media(WebPageId web_page_id) const { @@ -1129,10 +1173,83 @@ tl_object_ptr WebPagesManager::get_web_page_object(WebPageId we } return 1; }(); + + FormattedText description; + description.text = web_page->description; + description.entities = find_entities(web_page->description, true); + + auto r_url = parse_url(web_page->display_url); + if (r_url.is_ok()) { + Slice host = r_url.ok().host_; + if (!host.empty() && host.back() == '.') { + host.truncate(host.size() - 1); + } + + auto replace_entities = [](Slice text, vector &entities, auto replace_url) { + int32 current_offset = 0; + for (auto &entity : entities) { + CHECK(entity.offset >= current_offset); + text = utf8_utf16_substr(text, static_cast(entity.offset - current_offset)); + auto entity_text = utf8_utf16_substr(text, 0, static_cast(entity.length)); + text = text.substr(entity_text.size()); + current_offset = entity.offset + entity.length; + + auto replaced_url = replace_url(entity, entity_text); + if (!replaced_url.empty()) { + entity = MessageEntity(MessageEntity::Type::TextUrl, entity.offset, entity.length, std::move(replaced_url)); + } + } + }; + + if (host == "instagram.com" || ends_with(host, ".instagram.com")) { + replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) { + if (entity.type == MessageEntity::Type::Mention) { + return PSTRING() << "https://www.instagram.com/" << text.substr(1) << '/'; + } + if (entity.type == MessageEntity::Type::Hashtag) { + return PSTRING() << "https://www.instagram.com/explore/tags/" << url_encode(text.substr(1)) << '/'; + } + return string(); + }); + } else if (host == "twitter.com" || ends_with(host, ".twitter.com")) { + replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) { + if (entity.type == MessageEntity::Type::Mention) { + return PSTRING() << "https://twitter.com/" << text.substr(1); + } + if (entity.type == MessageEntity::Type::Hashtag) { + return PSTRING() << "https://twitter.com/hashtag/" << url_encode(text.substr(1)); + } + return string(); + }); + } else if (host == "t.me" || host == "telegram.me" || host == "telegram.dog" || host == "telesco.pe") { + // leave everything as is + } else { + td::remove_if(description.entities, + [](const MessageEntity &entity) { return entity.type == MessageEntity::Type::Mention; }); + + if (host == "youtube.com" || host == "www.youtube.com") { + replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) { + if (entity.type == MessageEntity::Type::Hashtag) { + return PSTRING() << "https://www.youtube.com/results?search_query=" << url_encode(text); + } + return string(); + }); + } else if (host == "music.youtube.com") { + replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) { + if (entity.type == MessageEntity::Type::Hashtag) { + return PSTRING() << "https://music.youtube.com/search?q=" << url_encode(text); + } + return string(); + }); + } + } + } + return make_tl_object( - web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title, web_page->description, - get_photo_object(td_->file_manager_.get(), &web_page->photo), web_page->embed_url, web_page->embed_type, - web_page->embed_dimensions.width, web_page->embed_dimensions.height, web_page->duration, web_page->author, + web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title, + get_formatted_text_object(description), get_photo_object(td_->file_manager_.get(), &web_page->photo), + web_page->embed_url, web_page->embed_type, web_page->embed_dimensions.width, web_page->embed_dimensions.height, + web_page->duration, web_page->author, web_page->document.type == Document::Type::Animation ? td_->animations_manager_->get_animation_object(web_page->document.file_id, "get_web_page_object") : nullptr, @@ -1171,9 +1288,10 @@ tl_object_ptr WebPagesManager::get_web_page_instant_ LOG(ERROR) << "Trying to get not loaded web page instant view"; return nullptr; } - return make_tl_object(get_page_block_objects(web_page_instant_view->page_blocks), - web_page_instant_view->is_v2 ? 2 : 1, web_page_instant_view->url, - web_page_instant_view->is_rtl, web_page_instant_view->is_full); + return td_api::make_object( + get_page_block_objects(web_page_instant_view->page_blocks, td_, web_page_instant_view->url), + web_page_instant_view->view_count, web_page_instant_view->is_v2 ? 2 : 1, web_page_instant_view->is_rtl, + web_page_instant_view->is_full); } void WebPagesManager::on_web_page_changed(WebPageId web_page_id, bool have_web_page) { @@ -1285,20 +1403,20 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ std::unordered_map videos; std::unordered_map voice_notes; std::unordered_map others; - auto get_map = [&](Document::Type document_type) -> std::unordered_map & { + auto get_map = [&](Document::Type document_type) { switch (document_type) { case Document::Type::Animation: - return animations; + return &animations; case Document::Type::Audio: - return audios; + return &audios; case Document::Type::General: - return documents; + return &documents; case Document::Type::Video: - return videos; + return &videos; case Document::Type::VoiceNote: - return voice_notes; + return &voice_notes; default: - return others; + return &others; } }; @@ -1308,7 +1426,7 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ auto document_id = document->id_; auto parsed_document = td_->documents_manager_->on_get_document(std::move(document), owner_dialog_id); if (!parsed_document.empty()) { - get_map(parsed_document.type).emplace(document_id, parsed_document.file_id); + get_map(parsed_document.type)->emplace(document_id, parsed_document.file_id); } } } @@ -1320,7 +1438,7 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ auto add_document = [&](const Document &document) { auto file_view = td_->file_manager_->get_file_view(document.file_id); if (file_view.has_remote_location()) { - get_map(document.type).emplace(file_view.remote_location().get_id(), document.file_id); + get_map(document.type)->emplace(file_view.remote_location().get_id(), document.file_id); } else { LOG(ERROR) << document.type << " has no remote location"; } @@ -1337,6 +1455,7 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ << " photos, " << videos.size() << " videos and " << voice_notes.size() << " voice notes"; web_page->instant_view.page_blocks = get_web_page_blocks(td_, std::move(page->blocks_), animations, audios, documents, photos, videos, voice_notes); + web_page->instant_view.view_count = (page->flags_ & telegram_api::page::VIEWS_MASK) != 0 ? page->views_ : 0; web_page->instant_view.is_v2 = (page->flags_ & telegram_api::page::V2_MASK) != 0; web_page->instant_view.is_rtl = (page->flags_ & telegram_api::page::RTL_MASK) != 0; web_page->instant_view.hash = hash; @@ -1566,7 +1685,7 @@ vector WebPagesManager::get_web_page_file_ids(const WebPage *web_page) c } if (!web_page->instant_view.is_empty) { for (auto &page_block : web_page->instant_view.page_blocks) { - page_block->append_file_ids(result); + page_block->append_file_ids(td_, result); } } return result; diff --git a/td/telegram/WebPagesManager.h b/td/telegram/WebPagesManager.h index 6ee613f2..8b80a7b9 100644 --- a/td/telegram/WebPagesManager.h +++ b/td/telegram/WebPagesManager.h @@ -47,9 +47,11 @@ class WebPagesManager : public Actor { void on_get_web_page_by_url(const string &url, WebPageId web_page_id, bool from_database); - void register_web_page(WebPageId web_page_id, FullMessageId full_message_id); + void on_get_web_page_instant_view_view_count(WebPageId web_page_id, int32 view_count); - void unregister_web_page(WebPageId web_page_id, FullMessageId full_message_id); + void register_web_page(WebPageId web_page_id, FullMessageId full_message_id, const char *source); + + void unregister_web_page(WebPageId web_page_id, FullMessageId full_message_id, const char *source); bool have_web_page(WebPageId web_page_id) const; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 9f5f0f22..117e822c 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -432,7 +432,7 @@ class CliClient final : public Actor { static char get_delimiter(Slice str) { std::unordered_set chars; for (auto c : trim(str)) { - if (c < '0' || c > '9') { + if (!is_alnum(c) && c != '-') { chars.insert(c); } } @@ -524,8 +524,11 @@ class CliClient final : public Actor { return result; } - static int32 as_supergroup_id(Slice str) { + int32 as_supergroup_id(Slice str) { str = trim(str); + if (str[0] == '@') { + return username_to_supergroup_id_[to_lower(str.substr(1))]; + } auto result = to_integer(str); int64 shift = static_cast(-1000000000000ll); if (result <= shift) { @@ -1572,6 +1575,8 @@ class CliClient final : public Actor { std::tie(secret, other_user_ids_str) = split(args); send_request(td_api::make_object( td_api::make_object(endpoint, key, secret), as_user_ids(other_user_ids_str))); + } else if (op == "gbci") { + send_request(td_api::make_object(args)); } else if (op == "gpf") { string chat_id; string message_id; @@ -1826,6 +1831,19 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_chat_id(chat_id), "", my_id_, 0, 0, to_integer(limit), nullptr)); + } else if (op == "SMU") { + string chat_id; + string user_id; + string limit; + + std::tie(chat_id, args) = split(args); + std::tie(user_id, limit) = split(args); + if (limit.empty()) { + limit = "10"; + } + + send_request(td_api::make_object(as_chat_id(chat_id), "", as_user_id(user_id), 0, 0, + to_integer(limit), nullptr)); } else if (op == "SM") { string chat_id; string filter; @@ -2235,7 +2253,7 @@ class CliClient final : public Actor { send_request(td_api::make_object()); } else if (op == "database") { send_request(td_api::make_object()); - } else if (op == "optimize_storage") { + } else if (op == "optimize_storage" || op == "optimize_storage_all") { string chat_ids; string exclude_chat_ids; string chat_ids_limit; @@ -2243,14 +2261,14 @@ class CliClient final : public Actor { std::tie(exclude_chat_ids, chat_ids_limit) = split(args); send_request(td_api::make_object( 10000000, -1, -1, 0, std::vector>(), as_chat_ids(chat_ids), - as_chat_ids(exclude_chat_ids), to_integer(chat_ids_limit))); + as_chat_ids(exclude_chat_ids), op == "optimize_storage", to_integer(chat_ids_limit))); } else if (op == "clean_storage_default") { send_request(td_api::make_object()); } else if (op == "clean_photos") { std::vector> types; types.push_back(td_api::make_object()); send_request(td_api::make_object(0, 0, 0, 0, std::move(types), as_chat_ids(""), - as_chat_ids(""), 20)); + as_chat_ids(""), true, 20)); } else if (op == "clean_storage") { std::vector> types; types.push_back(td_api::make_object()); @@ -2267,7 +2285,7 @@ class CliClient final : public Actor { types.push_back(td_api::make_object()); types.push_back(td_api::make_object()); send_request(td_api::make_object(0, -1, -1, 0, std::move(types), as_chat_ids(args), - as_chat_ids(""), 20)); + as_chat_ids(""), true, 20)); } else if (op == "network") { send_request(td_api::make_object()); } else if (op == "current_network") { @@ -2639,7 +2657,7 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_secret_chat_id(args))); } else if (op == "cc" || op == "CreateCall") { send_request(td_api::make_object( - as_user_id(args), td_api::make_object(true, true, 65, 65))); + as_user_id(args), td_api::make_object(true, true, 65, 65, vector{"2.6"}))); } else if (op == "dc" || op == "DiscardCall") { string call_id; string is_disconnected; @@ -2648,7 +2666,7 @@ class CliClient final : public Actor { send_request(td_api::make_object(as_call_id(call_id), as_bool(is_disconnected), 0, 0)); } else if (op == "ac" || op == "AcceptCall") { send_request(td_api::make_object( - as_call_id(args), td_api::make_object(true, true, 65, 65))); + as_call_id(args), td_api::make_object(true, true, 65, 65, vector{"2.6"}))); } else if (op == "scr" || op == "SendCallRating") { string call_id; string rating; @@ -2675,6 +2693,9 @@ class CliClient final : public Actor { send_request(td_api::make_object(args)); } else if (op == "gtes") { execute(td_api::make_object(args)); + } else if (op == "pm") { + send_request( + td_api::make_object(td_api::make_object(args, Auto()))); } else if (op == "pte") { send_request( td_api::make_object(args, td_api::make_object(2))); @@ -2761,8 +2782,9 @@ class CliClient final : public Actor { td_api::make_object(0, 1, td_api::make_object())); draft_message = td_api::make_object( - as_message_id(reply_to_message_id), td_api::make_object( - as_formatted_text(message, std::move(entities)), true, false)); + as_message_id(reply_to_message_id), 0, + td_api::make_object(as_formatted_text(message, std::move(entities)), true, + false)); } send_request(td_api::make_object(as_chat_id(chat_id), std::move(draft_message))); } else if (op == "cadm") { @@ -3101,6 +3123,8 @@ class CliClient final : public Actor { send_message(chat_id, td_api::make_object(as_chat_id(from_chat_id), as_message_id(from_message_id), true, op == "scopy", Random::fast(0, 1) == 0)); + } else if (op == "sdice") { + send_message(args, td_api::make_object()); } else if (op == "sd") { string chat_id; string document_path; @@ -3225,7 +3249,7 @@ class CliClient final : public Actor { send_message(chat_id, td_api::make_object( as_input_file(photo_path), nullptr, std::move(sticker_file_ids), 0, 0, as_caption(op == "spcaption" ? "cap \n\n\n\n tion " : ""), op == "spttl" ? 10 : 0)); - } else if (op == "spg") { + } else if (op == "spg" || op == "spgttl") { string chat_id; string photo_path; string conversion; @@ -3236,7 +3260,7 @@ class CliClient final : public Actor { send_message(chat_id, td_api::make_object( as_generated_file(photo_path, conversion, to_integer(expected_size)), nullptr, - vector(), 0, 0, as_caption(""), 0)); + vector(), 0, 0, as_caption(""), op == "spgttl" ? 10 : 0)); } else if (op == "spt") { string chat_id; string photo_path; @@ -3699,6 +3723,12 @@ class CliClient final : public Actor { std::tie(latitude, longitude) = split(args); send_request(td_api::make_object(as_location(latitude, longitude))); + } else if (op == "sloc") { + string latitude; + string longitude; + + std::tie(latitude, longitude) = split(args); + send_request(td_api::make_object(as_location(latitude, longitude))); } else if (op == "sco") { string limit; string query; @@ -3839,7 +3869,23 @@ class CliClient final : public Actor { std::tie(chat_id, args) = split(args); std::tie(parameters, is_dark) = split(args); - send_request(td_api::make_object(as_chat_id(args), parameters, as_bool(is_dark))); + send_request( + td_api::make_object(as_chat_id(chat_id), parameters, as_bool(is_dark))); + } else if (op == "gcst") { + string chat_id; + string is_dark; + std::tie(chat_id, is_dark) = split(args); + + send_request(td_api::make_object(as_chat_id(chat_id), as_bool(is_dark))); + } else if (op == "gcstg") { + string chat_id; + string token; + string x; + std::tie(chat_id, args) = split(args); + std::tie(token, x) = split(args); + + send_request( + td_api::make_object(as_chat_id(chat_id), token, to_integer(x))); } else if (op == "glui" || op == "glu" || op == "glua") { string chat_id; string message_id; @@ -4217,8 +4263,6 @@ void main(int argc, char **argv) { } SET_VERBOSITY_LEVEL(new_verbosity_level); - ClientActor::execute(td_api::make_object("update_file", 2)); - ClientActor::execute(td_api::make_object("file_reference", 2)); { ConcurrentScheduler scheduler; diff --git a/td/telegram/files/FileDownloader.cpp b/td/telegram/files/FileDownloader.cpp index b3adea04..854253f5 100644 --- a/td/telegram/files/FileDownloader.cpp +++ b/td/telegram/files/FileDownloader.cpp @@ -235,6 +235,7 @@ Result> FileDownloader::start_part(Part part, int32 callback_->on_start_download(); + auto net_query_type = is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download; NetQueryPtr net_query; if (!use_cdn_) { int32 flags = 0; @@ -245,15 +246,19 @@ Result> FileDownloader::start_part(Part part, int32 } #endif DcId dc_id = remote_.is_web() ? G()->get_webfile_dc_id() : remote_.get_dc_id(); - net_query = G()->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Default)), - remote_.is_web() - ? create_storer(telegram_api::upload_getWebFile(remote_.as_input_web_file_location(), - static_cast(part.offset), static_cast(size))) - : create_storer(telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, - remote_.as_input_file_location(), - static_cast(part.offset), static_cast(size))), - dc_id, is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download); + auto id = UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Default)); + net_query = remote_.is_web() + ? G()->net_query_creator().create( + id, + telegram_api::upload_getWebFile(remote_.as_input_web_file_location(), + static_cast(part.offset), static_cast(size)), + dc_id, net_query_type, NetQuery::AuthFlag::On) + : G()->net_query_creator().create( + id, + telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, + remote_.as_input_file_location(), + static_cast(part.offset), static_cast(size)), + dc_id, net_query_type, NetQuery::AuthFlag::On); } else { if (remote_.is_web()) { return Status::Error("Can't download web file from CDN"); @@ -264,15 +269,15 @@ Result> FileDownloader::start_part(Part part, int32 static_cast(size)); cdn_part_file_token_generation_[part.id] = cdn_file_token_generation_; LOG(DEBUG) << part.id << " " << to_string(query); - net_query = G()->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::CDN)), create_storer(query), cdn_dc_id_, - is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download, NetQuery::AuthFlag::Off); + net_query = + G()->net_query_creator().create(UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::CDN)), + query, cdn_dc_id_, net_query_type, NetQuery::AuthFlag::Off); } else { auto query = telegram_api::upload_reuploadCdnFile(BufferSlice(cdn_file_token_), BufferSlice(it->second)); LOG(DEBUG) << part.id << " " << to_string(query); net_query = G()->net_query_creator().create( - UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::ReuploadCDN)), create_storer(query), - remote_.get_dc_id(), is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download); + UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::ReuploadCDN)), query, + remote_.get_dc_id(), net_query_type, NetQuery::AuthFlag::On); cdn_part_reupload_token_.erase(it); } } @@ -464,9 +469,8 @@ Result FileDownloader::check_loop(int64 checked_prefix_si has_hash_query_ = true; auto query = telegram_api::upload_getFileHashes(remote_.as_input_file_location(), narrow_cast(checked_prefix_size)); - auto net_query = - G()->net_query_creator().create(create_storer(query), remote_.get_dc_id(), - is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download); + auto net_query_type = is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download; + auto net_query = G()->net_query_creator().create(query, remote_.get_dc_id(), net_query_type); info.queries.push_back(std::move(net_query)); break; } diff --git a/td/telegram/files/FileGcWorker.cpp b/td/telegram/files/FileGcWorker.cpp index 6bca6b75..3efe6567 100644 --- a/td/telegram/files/FileGcWorker.cpp +++ b/td/telegram/files/FileGcWorker.cpp @@ -26,18 +26,8 @@ namespace td { int VERBOSITY_NAME(file_gc) = VERBOSITY_NAME(INFO); -void FileGcWorker::do_remove_file(const FullFileInfo &info) { - // LOG(WARNING) << "Gc remove file: " << tag("path", file) << tag("mtime", stat.mtime_nsec_ / 1000000000) - // << tag("atime", stat.atime_nsec_ / 1000000000); - // TODO: remove file from database too - auto status = unlink(info.path); - LOG_IF(WARNING, status.is_error()) << "Failed to unlink file during files gc: " << status; - send_closure(G()->file_manager(), &FileManager::on_file_unlink, - FullLocalFileLocation(info.file_type, info.path, info.mtime_nsec)); -} - void FileGcWorker::run_gc(const FileGcParameters ¶meters, std::vector files, - Promise promise) { + Promise promise) { auto begin_time = Time::now(); VLOG(file_gc) << "Start files gc with " << parameters; // quite stupid implementations @@ -90,37 +80,48 @@ void FileGcWorker::run_gc(const FileGcParameters ¶meters, std::vectorfile_manager(), &FileManager::on_file_unlink, + FullLocalFileLocation(info.file_type, info.path, info.mtime_nsec)); + }; - // Remove all files with atime > now - max_time_from_last_access double now = Clocks::system(); + + // Keep all immune files + // Remove all files with (atime > now - max_time_from_last_access) td::remove_if(files, [&](const FullFileInfo &info) { if (token_) { return false; } if (immune_types[narrow_cast(info.file_type)]) { type_immunity_ignored_cnt++; - new_stats.add(FullFileInfo(info)); + new_stats.add_copy(info); return true; } if (td::contains(parameters.exclude_owner_dialog_ids, info.owner_dialog_id)) { exclude_owner_dialog_id_ignored_cnt++; - new_stats.add(FullFileInfo(info)); + new_stats.add_copy(info); return true; } if (!parameters.owner_dialog_ids.empty() && !td::contains(parameters.owner_dialog_ids, info.owner_dialog_id)) { owner_dialog_id_ignored_cnt++; - new_stats.add(FullFileInfo(info)); + new_stats.add_copy(info); return true; } - if (static_cast(info.mtime_nsec / 1000000000) > now - parameters.immunity_delay) { + if (static_cast(info.mtime_nsec) * 1e-9 > now - parameters.immunity_delay) { // new files are immune to gc time_immunity_ignored_cnt++; - new_stats.add(FullFileInfo(info)); + new_stats.add_copy(info); return true; } - if (static_cast(info.atime_nsec / 1000000000) < now - parameters.max_time_from_last_access) { + if (static_cast(info.atime_nsec) * 1e-9 < now - parameters.max_time_from_last_access) { do_remove_file(info); total_removed_size += info.size; remove_by_atime_cnt++; @@ -135,8 +136,8 @@ void FileGcWorker::run_gc(const FileGcParameters ¶meters, std::vector parameters.max_file_count) { remove_count = files.size() - parameters.max_file_count; @@ -168,7 +169,7 @@ void FileGcWorker::run_gc(const FileGcParameters ¶meters, std::vector parent, CancellationToken token) : parent_(std::move(parent)), token_(std::move(token)) { } - void run_gc(const FileGcParameters ¶meters, std::vector files, Promise promise); + void run_gc(const FileGcParameters ¶meters, std::vector files, Promise promise); private: ActorShared<> parent_; CancellationToken token_; - void do_remove_file(const FullFileInfo &info); }; } // namespace td diff --git a/td/telegram/files/FileGenerateManager.cpp b/td/telegram/files/FileGenerateManager.cpp index 19e27579..25ee6dfb 100644 --- a/td/telegram/files/FileGenerateManager.cpp +++ b/td/telegram/files/FileGenerateManager.cpp @@ -203,9 +203,9 @@ class MapDownloadGenerateActor : public FileGenerateActor { net_callback_ = create_actor("MapDownloadGenerateCallback", actor_id(this)); LOG(INFO) << "Download " << conversion_; - auto query = G()->net_query_creator().create( - create_storer(telegram_api::upload_getWebFile(r_input_web_file.move_as_ok(), 0, 1 << 20)), - G()->get_webfile_dc_id(), NetQuery::Type::DownloadSmall); + auto query = + G()->net_query_creator().create(telegram_api::upload_getWebFile(r_input_web_file.move_as_ok(), 0, 1 << 20), + G()->get_webfile_dc_id(), NetQuery::Type::DownloadSmall); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), {net_callback_.get(), 0}); } diff --git a/td/telegram/files/FileHashUploader.cpp b/td/telegram/files/FileHashUploader.cpp index 62063f71..234d5fb5 100644 --- a/td/telegram/files/FileHashUploader.cpp +++ b/td/telegram/files/FileHashUploader.cpp @@ -74,7 +74,7 @@ Status FileHashUploader::loop_impl() { auto query = telegram_api::messages_getDocumentByHash(std::move(hash), static_cast(size_), std::move(mime_type)); LOG(INFO) << "Send getDocumentByHash request: " << to_string(query); - auto ptr = G()->net_query_creator().create(create_storer(query)); + auto ptr = G()->net_query_creator().create(query); G()->net_query_dispatcher().dispatch_with_callback(std::move(ptr), actor_shared(this)); state_ = State::WaitNetResult; } diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 9a5e07d2..e15a9567 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -867,7 +867,7 @@ bool FileManager::are_modification_times_equal(int64 old_mtime, int64 new_mtime) } Status FileManager::check_local_location(FullLocalFileLocation &location, int64 &size) { - constexpr int64 MAX_THUMBNAIL_SIZE = 200 * (1 << 10) /* 200 kB */; + constexpr int64 MAX_THUMBNAIL_SIZE = 200 * (1 << 10) /* 200 KB */; constexpr int64 MAX_PHOTO_SIZE = 10 * (1 << 20) /* 10 MB */; if (location.path_.empty()) { @@ -1020,6 +1020,7 @@ FileId FileManager::register_empty(FileType type) { } void FileManager::on_file_unlink(const FullLocalFileLocation &location) { + // TODO: remove file from the database too auto it = local_location_to_file_id_.find(location); if (it == local_location_to_file_id_.end()) { return; @@ -1393,9 +1394,11 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy if (x_node->remote_.full && y_node->remote_.full && !x_node->remote_.full.value().is_web() && !y_node->remote_.full.value().is_web() && y_node->remote_.is_full_alive && + x_node->remote_.full_source == FileLocationSource::FromServer && + y_node->remote_.full_source == FileLocationSource::FromServer && x_node->remote_.full.value().get_dc_id() != y_node->remote_.full.value().get_dc_id()) { - LOG(WARNING) << "File remote location was changed from " << y_node->remote_.full.value() << " to " - << x_node->remote_.full.value(); + LOG(ERROR) << "File remote location was changed from " << y_node->remote_.full.value() << " to " + << x_node->remote_.full.value(); } auto count_local = [](auto &node) { return std::accumulate(node->file_ids_.begin(), node->file_ids_.end(), 0, @@ -2020,7 +2023,7 @@ void FileManager::delete_file(FileId file_id, Promise promise, const char LOG(INFO) << "Unlink file " << file_id << " at " << file_view.local_location().path_; clear_from_pmc(node); - context_->on_new_file(-file_view.get_allocated_local_size(), -1); + context_->on_new_file(-file_view.size(), -file_view.get_allocated_local_size(), -1); unlink(file_view.local_location().path_).ignore(); node->drop_local_location(); try_flush_node(node, "delete_file 1"); @@ -2987,11 +2990,11 @@ Result FileManager::get_input_thumbnail_file_id(const tl_object_ptr FileManager::get_input_file_id(FileType type, const tl_object_ptr &file, DialogId owner_dialog_id, bool allow_zero, bool is_encrypted, bool get_by_hash, bool is_secure) { - if (!file) { + if (file == nullptr) { if (allow_zero) { return FileId(); } - return Status::Error(6, "InputFile not specified"); + return Status::Error(6, "InputFile is not specified"); } if (is_encrypted || is_secure) { @@ -3272,7 +3275,7 @@ void FileManager::on_download_ok(QueryId query_id, const FullLocalFileLocation & LOG(ERROR) << "Can't register local file after download: " << r_new_file_id.error(); } else { if (is_new) { - context_->on_new_file(get_file_view(r_new_file_id.ok()).get_allocated_local_size(), 1); + context_->on_new_file(size, get_file_view(r_new_file_id.ok()).get_allocated_local_size(), 1); } LOG_STATUS(merge(r_new_file_id.ok(), file_id)); } @@ -3439,7 +3442,7 @@ void FileManager::on_generate_ok(QueryId query_id, const FullLocalFileLocation & FileView file_view(file_node); if (!file_view.has_generate_location() || !begins_with(file_view.generate_location().conversion_, "#file_id#")) { - context_->on_new_file(file_view.get_allocated_local_size(), 1); + context_->on_new_file(file_view.size(), file_view.get_allocated_local_size(), 1); } run_upload(file_node, {}); diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 820460c5..08f8a8a5 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -361,7 +361,7 @@ class FileManager : public FileLoadManager::Callback { class Context { public: - virtual void on_new_file(int64 size, int32 cnt) = 0; + virtual void on_new_file(int64 size, int64 real_size, int32 cnt) = 0; virtual void on_file_updated(FileId size) = 0; diff --git a/td/telegram/files/FileStats.cpp b/td/telegram/files/FileStats.cpp index 5dca628b..e919363d 100644 --- a/td/telegram/files/FileStats.cpp +++ b/td/telegram/files/FileStats.cpp @@ -31,14 +31,25 @@ void FileStats::add(StatByType &by_type, FileType file_type, int64 size) { by_type[pos].cnt++; } -void FileStats::add(FullFileInfo &&info) { +void FileStats::add_impl(const FullFileInfo &info) { if (split_by_owner_dialog_id) { add(stat_by_owner_dialog_id[info.owner_dialog_id], info.file_type, info.size); } else { add(stat_by_type, info.file_type, info.size); } +} + +void FileStats::add_copy(const FullFileInfo &info) { + add_impl(info); if (need_all_files) { - all_files.emplace_back(std::move(info)); + all_files.push_back(info); + } +} + +void FileStats::add(FullFileInfo &&info) { + add_impl(info); + if (need_all_files) { + all_files.push_back(std::move(info)); } } @@ -52,6 +63,7 @@ FileTypeStat get_nontemp_stat(const FileStats::StatByType &by_type) { } return stat; } + FileTypeStat FileStats::get_total_nontemp_stat() const { if (!split_by_owner_dialog_id) { return get_nontemp_stat(stat_by_type); @@ -64,6 +76,7 @@ FileTypeStat FileStats::get_total_nontemp_stat() const { } return stat; } + void FileStats::apply_dialog_limit(int32 limit) { if (limit == -1) { return; @@ -244,4 +257,5 @@ StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) { return sb; } + } // namespace td diff --git a/td/telegram/files/FileStats.h b/td/telegram/files/FileStats.h index 4d76d66c..7638a6af 100644 --- a/td/telegram/files/FileStats.h +++ b/td/telegram/files/FileStats.h @@ -79,6 +79,7 @@ struct FileStats { std::vector all_files; + void add_copy(const FullFileInfo &info); void add(FullFileInfo &&info); void apply_dialog_limit(int32 limit); @@ -87,6 +88,7 @@ struct FileStats { FileTypeStat get_total_nontemp_stat() const; private: + void add_impl(const FullFileInfo &info); void add(StatByType &by_type, FileType file_type, int64 size); }; diff --git a/td/telegram/files/FileUploader.cpp b/td/telegram/files/FileUploader.cpp index c4b7809f..664876a4 100644 --- a/td/telegram/files/FileUploader.cpp +++ b/td/telegram/files/FileUploader.cpp @@ -277,12 +277,10 @@ Result> FileUploader::start_part(Part part, int32 p if (big_flag_) { auto query = telegram_api::upload_saveBigFilePart(file_id_, part.id, local_is_ready_ ? part_count : -1, std::move(bytes)); - net_query = G()->net_query_creator().create(create_storer(query), DcId::main(), NetQuery::Type::Upload, - NetQuery::AuthFlag::On, NetQuery::GzipFlag::Off); + net_query = G()->net_query_creator().create(query, DcId::main(), NetQuery::Type::Upload); } else { auto query = telegram_api::upload_saveFilePart(file_id_, part.id, std::move(bytes)); - net_query = G()->net_query_creator().create(create_storer(query), DcId::main(), NetQuery::Type::Upload, - NetQuery::AuthFlag::On, NetQuery::GzipFlag::Off); + net_query = G()->net_query_creator().create(query, DcId::main(), NetQuery::Type::Upload); } net_query->file_type_ = narrow_cast(file_type_); return std::make_pair(std::move(net_query), false); diff --git a/td/telegram/files/PartsManager.cpp b/td/telegram/files/PartsManager.cpp index 7a154c14..8e19f626 100644 --- a/td/telegram/files/PartsManager.cpp +++ b/td/telegram/files/PartsManager.cpp @@ -120,7 +120,6 @@ Status PartsManager::init(int64 size, int64 expected_size, bool is_size_final, s return Status::Error("FILE_UPLOAD_RESTART"); } } else { - // TODO choose part_size_ depending on size part_size_ = 64 * (1 << 10); while (use_part_count_limit && calc_part_count(expected_size_, part_size_) > MAX_PART_COUNT) { part_size_ *= 2; diff --git a/td/telegram/logevent/LogEventHelper.h b/td/telegram/logevent/LogEventHelper.h index 31bdc7da..3fb858ba 100644 --- a/td/telegram/logevent/LogEventHelper.h +++ b/td/telegram/logevent/LogEventHelper.h @@ -36,13 +36,12 @@ inline Promise get_erase_logevent_promise(uint64 logevent_id, Promise void store_time(double time_at, StorerT &storer) { - double server_time = storer.context()->server_time(); if (time_at == 0) { store(-1.0, storer); } else { double time_left = max(time_at - Time::now(), 0.0); store(time_left, storer); - store(server_time, storer); + store(get_server_time(), storer); } } diff --git a/td/telegram/misc.cpp b/td/telegram/misc.cpp index 9405df4a..e6d9e944 100644 --- a/td/telegram/misc.cpp +++ b/td/telegram/misc.cpp @@ -52,6 +52,20 @@ string clean_username(string str) { return trim(str); } +void replace_offending_characters(string &str) { + // "(\xe2\x80\x8f|\xe2\x80\x8e){N}(\xe2\x80\x8f|\xe2\x80\x8e)" -> "(\xe2\x80\x8c){N}$2" + auto s = MutableSlice(str).ubegin(); + for (size_t pos = 0; pos < str.size(); pos++) { + if (s[pos] == 0xe2 && s[pos + 1] == 0x80 && (s[pos + 2] == 0x8e || s[pos + 2] == 0x8f)) { + while (s[pos + 3] == 0xe2 && s[pos + 4] == 0x80 && (s[pos + 5] == 0x8e || s[pos + 5] == 0x8f)) { + s[pos + 2] = static_cast(0x8c); + pos += 3; + } + pos += 2; + } + } +} + bool clean_input_string(string &str) { constexpr size_t LENGTH_LIMIT = 35000; // server side limit if (!check_utf8(str)) { @@ -133,6 +147,9 @@ bool clean_input_string(string &str) { } str.resize(new_size); + + replace_offending_characters(str); + return true; } diff --git a/td/telegram/misc.h b/td/telegram/misc.h index 2f033150..0b62e315 100644 --- a/td/telegram/misc.h +++ b/td/telegram/misc.h @@ -18,6 +18,9 @@ string clean_name(string str, size_t max_length) TD_WARN_UNUSED_RESULT; // prepares username/stickername for search string clean_username(string str) TD_WARN_UNUSED_RESULT; +// replaces some offending characters without changing string length +void replace_offending_characters(string &str); + // removes control characters from the string, will fail if input string is not in UTF-8 bool clean_input_string(string &str) TD_WARN_UNUSED_RESULT; diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index 7b5a7cbf..d45fa428 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -694,7 +694,7 @@ Result ConnectionCreator::find_connection(const Proxy &proxy, const IP if (proxy.use_mtproto_proxy()) { extra.debug_str = PSTRING() << "MTProto " << proxy_ip_address << extra.debug_str; - LOG(INFO) << "Create: " << extra.debug_str; + VLOG(connections) << "Create: " << extra.debug_str; return SocketFd::open(proxy_ip_address); } @@ -704,11 +704,11 @@ Result ConnectionCreator::find_connection(const Proxy &proxy, const IP extra.mtproto_ip = info.option->get_ip_address(); extra.debug_str = PSTRING() << (proxy.use_socks5_proxy() ? "Socks5" : (only_http ? "HTTP_ONLY" : "HTTP_TCP")) << ' ' << proxy_ip_address << " --> " << extra.mtproto_ip << extra.debug_str; - LOG(INFO) << "Create: " << extra.debug_str; + VLOG(connections) << "Create: " << extra.debug_str; return SocketFd::open(proxy_ip_address); } else { extra.debug_str = PSTRING() << info.option->get_ip_address() << extra.debug_str; - LOG(INFO) << "Create: " << extra.debug_str; + VLOG(connections) << "Create: " << extra.debug_str; return SocketFd::open(info.option->get_ip_address()); } } @@ -762,8 +762,9 @@ ActorOwn<> ConnectionCreator::prepare_connection(SocketFd socket_fd, const Proxy bool use_connection_token_; bool was_connected_{false}; }; - LOG(INFO) << "Start " << (proxy.use_socks5_proxy() ? "Socks5" : (proxy.use_http_tcp_proxy() ? "HTTP" : "TLS")) - << ": " << debug_str; + VLOG(connections) << "Start " + << (proxy.use_socks5_proxy() ? "Socks5" : (proxy.use_http_tcp_proxy() ? "HTTP" : "TLS")) << ": " + << debug_str; auto callback = make_unique(std::move(promise), std::move(stats_callback), use_connection_token, !proxy.use_socks5_proxy()); if (proxy.use_socks5_proxy()) { @@ -1009,13 +1010,13 @@ void ConnectionCreator::client_add_connection(size_t hash, Resultsave_server_time(); client_loop(clients_[hash]); } void ConnectionCreator::on_dc_options(DcOptions new_dc_options) { - LOG(INFO) << "SAVE " << new_dc_options; + VLOG(connections) << "SAVE " << new_dc_options; G()->td_db()->get_binlog_pmc()->set("dc_options", serialize(new_dc_options)); dc_options_set_.reset(); dc_options_set_.add_dc_options(get_default_dc_options(G()->is_test_dc())); @@ -1237,7 +1238,7 @@ void ConnectionCreator::loop() { if (get_proxy_info_timestamp_.is_in_past()) { if (get_proxy_info_query_token_ == 0) { get_proxy_info_query_token_ = next_token(); - auto query = G()->net_query_creator().create(create_storer(telegram_api::help_getProxyData())); + auto query = G()->net_query_creator().create(telegram_api::help_getProxyData()); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, get_proxy_info_query_token_)); } @@ -1299,7 +1300,7 @@ void ConnectionCreator::on_result(NetQueryPtr query) { void ConnectionCreator::on_get_proxy_info(telegram_api::object_ptr proxy_data_ptr) { CHECK(proxy_data_ptr != nullptr); - LOG(INFO) << "Receive " << to_string(proxy_data_ptr); + LOG(DEBUG) << "Receive " << to_string(proxy_data_ptr); int32 expires = 0; switch (proxy_data_ptr->get_id()) { case telegram_api::help_proxyDataEmpty::ID: { diff --git a/td/telegram/net/DcAuthManager.cpp b/td/telegram/net/DcAuthManager.cpp index 85fd52b6..9cc4467f 100644 --- a/td/telegram/net/DcAuthManager.cpp +++ b/td/telegram/net/DcAuthManager.cpp @@ -171,11 +171,10 @@ void DcAuthManager::dc_loop(DcInfo &dc) { // send auth.exportAuthorization to auth_dc VLOG(dc) << "Send exportAuthorization to " << dc.dc_id; auto id = UniqueId::next(); - G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create( - id, create_storer(telegram_api::auth_exportAuthorization(dc.dc_id.get_raw_id())), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::On, 60 * 60 * 24), - actor_shared(this, dc.dc_id.get_raw_id())); + auto query = G()->net_query_creator().create(id, telegram_api::auth_exportAuthorization(dc.dc_id.get_raw_id()), + DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On); + query->total_timeout_limit = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, dc.dc_id.get_raw_id())); dc.wait_id = id; dc.export_id = -1; dc.state = DcInfo::State::Import; @@ -188,21 +187,19 @@ void DcAuthManager::dc_loop(DcInfo &dc) { } uint64 id = UniqueId::next(); VLOG(dc) << "Send importAuthorization to " << dc.dc_id; - G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create( - id, create_storer(telegram_api::auth_importAuthorization(dc.export_id, std::move(dc.export_bytes))), - dc.dc_id, NetQuery::Type::Common, NetQuery::AuthFlag::Off, NetQuery::GzipFlag::On, 60 * 60 * 24), - actor_shared(this, dc.dc_id.get_raw_id())); + auto query = G()->net_query_creator().create( + id, telegram_api::auth_importAuthorization(dc.export_id, std::move(dc.export_bytes)), dc.dc_id, + NetQuery::Type::Common, NetQuery::AuthFlag::Off); + query->total_timeout_limit = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, dc.dc_id.get_raw_id())); dc.wait_id = id; dc.state = DcInfo::State::BeforeOk; break; } - case DcInfo::State::BeforeOk: { + case DcInfo::State::BeforeOk: break; - } - case DcInfo::State::Ok: { + case DcInfo::State::Ok: break; - } } } diff --git a/td/telegram/net/NetQuery.h b/td/telegram/net/NetQuery.h index 444e2614..3ed0763e 100644 --- a/td/telegram/net/NetQuery.h +++ b/td/telegram/net/NetQuery.h @@ -13,17 +13,17 @@ #include "td/actor/PromiseFuture.h" #include "td/actor/SignalSlot.h" -#include "td/mtproto/utils.h" // for create_storer, fetch_result TODO - #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/List.h" #include "td/utils/logging.h" #include "td/utils/ObjectPool.h" +#include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" #include "td/utils/Time.h" +#include "td/utils/tl_parsers.h" #include #include @@ -330,7 +330,7 @@ class NetQuery : public ListNode { NetQueryCounter nq_counter_; NetQuery(State state, uint64 id, BufferSlice &&query, BufferSlice &&answer, DcId dc_id, Type type, AuthFlag auth_flag, - GzipFlag gzip_flag, int32 tl_constructor) + GzipFlag gzip_flag, int32 tl_constructor, double total_timeout_limit) : state_(state) , type_(type) , auth_flag_(auth_flag) @@ -341,6 +341,7 @@ class NetQuery : public ListNode { , query_(std::move(query)) , answer_(std::move(answer)) , tl_constructor_(tl_constructor) + , total_timeout_limit(total_timeout_limit) , nq_counter_(true) { my_id_ = get_my_id(); start_timestamp_ = Time::now(); @@ -365,6 +366,7 @@ inline StringBuilder &operator<<(StringBuilder &stream, const NetQuery &net_quer stream << "]"; return stream; } + inline StringBuilder &operator<<(StringBuilder &stream, const NetQueryPtr &net_query_ptr) { return stream << *net_query_ptr; } @@ -378,6 +380,21 @@ inline void cancel_query(NetQueryRef &ref) { ref->cancel(ref.generation()); } +template +Result fetch_result(const BufferSlice &message) { + TlBufferParser parser(&message); + auto result = T::fetch_result(parser); + parser.fetch_end(); + + const char *error = parser.get_error(); + if (error != nullptr) { + LOG(ERROR) << "Can't parse: " << format::as_hex_dump<4>(message.as_slice()); + return Status::Error(500, Slice(error)); + } + + return std::move(result); +} + template Result fetch_result(NetQueryPtr query) { CHECK(!query.empty()); @@ -397,6 +414,7 @@ Result fetch_result(Result r_query) { inline void NetQueryCallback::on_result(NetQueryPtr query) { on_result_resendable(std::move(query), Auto()); } + inline void NetQueryCallback::on_result_resendable(NetQueryPtr query, Promise promise) { on_result(std::move(query)); } @@ -404,6 +422,7 @@ inline void NetQueryCallback::on_result_resendable(NetQueryPtr query, Promisestart_migrate(sched_id); } + inline void finish_migrate(NetQueryPtr &net_query) { net_query->finish_migrate(); } diff --git a/td/telegram/net/NetQueryCreator.cpp b/td/telegram/net/NetQueryCreator.cpp index 3777a6f8..fe4b62cd 100644 --- a/td/telegram/net/NetQueryCreator.cpp +++ b/td/telegram/net/NetQueryCreator.cpp @@ -6,29 +6,47 @@ // #include "td/telegram/net/NetQueryCreator.h" +#include "td/telegram/AuthManager.h" +#include "td/telegram/Global.h" +#include "td/telegram/Td.h" +#include "td/telegram/telegram_api.h" + #include "td/utils/format.h" #include "td/utils/Gzip.h" #include "td/utils/logging.h" #include "td/utils/Slice.h" +#include "td/utils/Storer.h" namespace td { -NetQueryCreator::Ptr NetQueryCreator::create(uint64 id, const Storer &storer, DcId dc_id, NetQuery::Type type, - NetQuery::AuthFlag auth_flag, NetQuery::GzipFlag gzip_flag, - double total_timeout_limit) { +NetQueryPtr NetQueryCreator::create(const telegram_api::Function &function, DcId dc_id, NetQuery::Type type) { + return create(UniqueId::next(), function, dc_id, type, NetQuery::AuthFlag::On); +} + +NetQueryPtr NetQueryCreator::create(uint64 id, const telegram_api::Function &function, DcId dc_id, NetQuery::Type type, + NetQuery::AuthFlag auth_flag) { + LOG(DEBUG) << "Create query " << to_string(function); + auto storer = DefaultStorer(function); BufferSlice slice(storer.size()); auto real_size = storer.store(slice.as_slice().ubegin()); LOG_CHECK(real_size == slice.size()) << real_size << " " << slice.size() << " " << format::as_hex_dump<4>(Slice(slice.as_slice())); - // TODO: magic constant - if (slice.size() < (1 << 8)) { - gzip_flag = NetQuery::GzipFlag::Off; - } int32 tl_constructor = NetQuery::tl_magic(slice); + + size_t MIN_GZIPPED_SIZE = 128; + auto gzip_flag = slice.size() < MIN_GZIPPED_SIZE ? NetQuery::GzipFlag::Off : NetQuery::GzipFlag::On; + if (slice.size() >= 16384) { + // test compression ratio for the middle part + // if it is less than 0.9, then try to compress the whole request + size_t TESTED_SIZE = 1024; + BufferSlice compressed_part = gzencode(slice.as_slice().substr((slice.size() - TESTED_SIZE) / 2, TESTED_SIZE), 0.9); + if (compressed_part.empty()) { + gzip_flag = NetQuery::GzipFlag::Off; + } + } if (gzip_flag == NetQuery::GzipFlag::On) { - // TODO: try to compress files? - BufferSlice compressed = gzencode(slice.as_slice()); + BufferSlice compressed = gzencode(slice.as_slice(), 0.9); if (compressed.empty()) { gzip_flag = NetQuery::GzipFlag::Off; } else { @@ -36,10 +54,19 @@ NetQueryCreator::Ptr NetQueryCreator::create(uint64 id, const Storer &storer, Dc } } + double total_timeout_limit = 60; + if (!G()->close_flag()) { + auto td = G()->td(); + if (!td.empty()) { + auto auth_manager = td.get_actor_unsafe()->auth_manager_.get(); + if (auth_manager && auth_manager->is_bot()) { + total_timeout_limit = 8; + } + } + } auto query = object_pool_.create(NetQuery::State::Query, id, std::move(slice), BufferSlice(), dc_id, type, auth_flag, - gzip_flag, tl_constructor); + gzip_flag, tl_constructor, total_timeout_limit); query->set_cancellation_token(query.generation()); - query->total_timeout_limit = total_timeout_limit; return query; } diff --git a/td/telegram/net/NetQueryCreator.h b/td/telegram/net/NetQueryCreator.h index a7ca101f..ba4e6b8f 100644 --- a/td/telegram/net/NetQueryCreator.h +++ b/td/telegram/net/NetQueryCreator.h @@ -12,15 +12,15 @@ #include "td/utils/buffer.h" #include "td/utils/ObjectPool.h" -#include "td/utils/StorerBase.h" namespace td { +namespace telegram_api { +class Function; +} // namespace telegram_api + class NetQueryCreator { public: - using Ptr = NetQueryPtr; - using Ref = NetQueryRef; - NetQueryCreator() { object_pool_.set_check_empty(true); } @@ -29,26 +29,20 @@ class NetQueryCreator { object_pool_.set_check_empty(false); } - Ptr create_result(BufferSlice &&buffer, DcId dc_id = DcId::main(), - NetQuery::AuthFlag auth_flag = NetQuery::AuthFlag::On, - NetQuery::GzipFlag gzip_flag = NetQuery::GzipFlag::Off) { - return create_result(0, std::move(buffer), dc_id, auth_flag, gzip_flag); - } - Ptr create_result(uint64 id, BufferSlice &&buffer, DcId dc_id = DcId::main(), - NetQuery::AuthFlag auth_flag = NetQuery::AuthFlag::On, - NetQuery::GzipFlag gzip_flag = NetQuery::GzipFlag::Off) { - return object_pool_.create(NetQuery::State::OK, id, BufferSlice(), std::move(buffer), dc_id, NetQuery::Type::Common, - auth_flag, gzip_flag, 0); + NetQueryPtr create_update(BufferSlice &&buffer) { + return object_pool_.create(NetQuery::State::OK, 0, BufferSlice(), std::move(buffer), DcId::main(), + NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::Off, 0, 0); } - Ptr create(const Storer &storer, DcId dc_id = DcId::main(), NetQuery::Type type = NetQuery::Type::Common, - NetQuery::AuthFlag auth_flag = NetQuery::AuthFlag::On, - NetQuery::GzipFlag gzip_flag = NetQuery::GzipFlag::On, double total_timeout_limit = 60) { - return create(UniqueId::next(), storer, dc_id, type, auth_flag, gzip_flag, total_timeout_limit); + NetQueryPtr create(const telegram_api::Function &function, DcId dc_id = DcId::main(), + NetQuery::Type type = NetQuery::Type::Common); + + NetQueryPtr create_unauth(const telegram_api::Function &function, DcId dc_id = DcId::main()) { + return create(UniqueId::next(), function, dc_id, NetQuery::Type::Common, NetQuery::AuthFlag::Off); } - Ptr create(uint64 id, const Storer &storer, DcId dc_id = DcId::main(), NetQuery::Type type = NetQuery::Type::Common, - NetQuery::AuthFlag auth_flag = NetQuery::AuthFlag::On, - NetQuery::GzipFlag gzip_flag = NetQuery::GzipFlag::On, double total_timeout_limit = 60); + + NetQueryPtr create(uint64 id, const telegram_api::Function &function, DcId dc_id, NetQuery::Type type, + NetQuery::AuthFlag auth_flag); private: ObjectPool object_pool_; diff --git a/td/telegram/net/NetStatsManager.cpp b/td/telegram/net/NetStatsManager.cpp index 0c9179f0..2f2a4b94 100644 --- a/td/telegram/net/NetStatsManager.cpp +++ b/td/telegram/net/NetStatsManager.cpp @@ -9,6 +9,7 @@ #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" +#include "td/telegram/ConfigShared.h" #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/StateManager.h" @@ -213,9 +214,13 @@ void NetStatsManager::start_up() { auto since_str = G()->td_db()->get_binlog_pmc()->get("net_stats_since"); if (!since_str.empty()) { auto since = to_integer(since_str); + auto authorization_date = G()->shared_config().get_option_integer("authorization_date"); if (unix_time < since) { since_total_ = unix_time; G()->td_db()->get_binlog_pmc()->set("net_stats_since", to_string(since_total_)); + } else if (since < authorization_date - 3600) { + since_total_ = authorization_date; + G()->td_db()->get_binlog_pmc()->set("net_stats_since", to_string(since_total_)); } else { since_total_ = since; } diff --git a/td/telegram/net/Proxy.cpp b/td/telegram/net/Proxy.cpp index b20c9478..0e6e2e09 100644 --- a/td/telegram/net/Proxy.cpp +++ b/td/telegram/net/Proxy.cpp @@ -12,10 +12,10 @@ namespace td { Result Proxy::from_td_api(string server, int port, td_api::ProxyType *proxy_type) { if (proxy_type == nullptr) { - return Status::Error(400, "Proxy type should not be empty"); + return Status::Error(400, "Proxy type must be non-empty"); } if (server.empty()) { - return Status::Error(400, "Server name can't be empty"); + return Status::Error(400, "Server name must be non-empty"); } if (server.size() > 255) { return Status::Error(400, "Server name is too long"); diff --git a/td/telegram/net/PublicRsaKeyShared.cpp b/td/telegram/net/PublicRsaKeyShared.cpp index 5066a8ff..c6580efa 100644 --- a/td/telegram/net/PublicRsaKeyShared.cpp +++ b/td/telegram/net/PublicRsaKeyShared.cpp @@ -21,7 +21,7 @@ PublicRsaKeyShared::PublicRsaKeyShared(DcId dc_id, bool is_test) : dc_id_(dc_id) return; } auto add_pem = [this](CSlice pem) { - auto r_rsa = RSA::from_pem(pem); + auto r_rsa = RSA::from_pem_public_key(pem); LOG_CHECK(r_rsa.is_ok()) << r_rsa.error() << " " << pem; if (r_rsa.is_ok()) { diff --git a/td/telegram/net/PublicRsaKeyWatchdog.cpp b/td/telegram/net/PublicRsaKeyWatchdog.cpp index 6556647c..25b96c44 100644 --- a/td/telegram/net/PublicRsaKeyWatchdog.cpp +++ b/td/telegram/net/PublicRsaKeyWatchdog.cpp @@ -7,7 +7,7 @@ #include "td/telegram/net/PublicRsaKeyWatchdog.h" #include "td/telegram/Global.h" -#include "td/telegram/net/DcId.h" +#include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/TdDb.h" #include "td/telegram/telegram_api.h" @@ -68,11 +68,9 @@ void PublicRsaKeyWatchdog::loop() { } flood_control_.add_event(static_cast(Time::now_cached())); has_query_ = true; - G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(create_storer(telegram_api::help_getCdnConfig()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::On, - 60 * 60 * 24), - actor_shared(this)); + auto query = G()->net_query_creator().create(telegram_api::help_getCdnConfig()); + query->total_timeout_limit = 60 * 60 * 24; + G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } void PublicRsaKeyWatchdog::on_result(NetQueryPtr net_query) { @@ -110,7 +108,7 @@ void PublicRsaKeyWatchdog::sync_key(std::shared_ptr &key) { } for (auto &config_key : cdn_config_->public_keys_) { if (key->dc_id().get_raw_id() == config_key->dc_id_) { - auto r_rsa = RSA::from_pem(config_key->public_key_); + auto r_rsa = RSA::from_pem_public_key(config_key->public_key_); if (r_rsa.is_error()) { LOG(ERROR) << r_rsa.error(); continue; diff --git a/td/telegram/net/Session.cpp b/td/telegram/net/Session.cpp index b4f34073..edf9c0ea 100644 --- a/td/telegram/net/Session.cpp +++ b/td/telegram/net/Session.cpp @@ -10,6 +10,7 @@ #include "td/telegram/DhCache.h" #include "td/telegram/Global.h" +#include "td/telegram/net/DcId.h" #include "td/telegram/net/MtprotoHeader.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/net/NetQueryDispatcher.h" @@ -25,6 +26,7 @@ #include "td/mtproto/SessionConnection.h" #include "td/mtproto/TransportType.h" +#include "td/utils/as.h" #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" @@ -540,12 +542,9 @@ void Session::on_session_created(uint64 unique_id, uint64 first_id) { LOG(INFO) << "New session " << unique_id << " created with first message_id " << first_id; if (is_main_) { LOG(DEBUG) << "Sending updatesTooLong to force getDifference"; - telegram_api::updatesTooLong too_long_; - auto storer = create_storer(too_long_); - BufferSlice packet(storer.size()); - auto real_size = storer.store(packet.as_slice().ubegin()); - CHECK(real_size == packet.size()); - return_query(G()->net_query_creator().create_result(0, std::move(packet))); + BufferSlice packet(4); + as(packet.as_slice().begin()) = telegram_api::updatesTooLong::ID; + return_query(G()->net_query_creator().create_update(std::move(packet))); } for (auto it = sent_queries_.begin(); it != sent_queries_.end();) { @@ -682,7 +681,7 @@ Status Session::on_message_result_ok(uint64 id, BufferSlice packet, size_t origi if (is_cdn_) { return Status::Error("Got update from CDN connection"); } - return_query(G()->net_query_creator().create_result(0, std::move(packet))); + return_query(G()->net_query_creator().create_update(std::move(packet))); return Status::OK(); } @@ -1105,8 +1104,8 @@ bool Session::connection_send_check_main_key(ConnectionInfo *info) { LOG(INFO) << "Check main key"; being_checked_main_auth_key_id_ = key_id; last_check_query_id_ = UniqueId::next(UniqueId::BindKey); - NetQueryPtr query = - G()->net_query_creator().create(last_check_query_id_, create_storer(telegram_api::help_getNearestDc())); + NetQueryPtr query = G()->net_query_creator().create(last_check_query_id_, telegram_api::help_getNearestDc(), + DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On); query->dispatch_ttl = 0; query->set_callback(actor_shared(this)); connection_send_query(info, std::move(query)); @@ -1142,7 +1141,8 @@ bool Session::connection_send_bind_key(ConnectionInfo *info) { LOG(INFO) << "Bind key: " << tag("tmp", key_id) << tag("perm", static_cast(perm_auth_key_id)); NetQueryPtr query = G()->net_query_creator().create( last_bind_query_id_, - create_storer(telegram_api::auth_bindTempAuthKey(perm_auth_key_id, nonce, expires_at, std::move(encrypted)))); + telegram_api::auth_bindTempAuthKey(perm_auth_key_id, nonce, expires_at, std::move(encrypted)), DcId::main(), + NetQuery::Type::Common, NetQuery::AuthFlag::On); query->dispatch_ttl = 0; query->set_callback(actor_shared(this)); connection_send_query(info, std::move(query), message_id); diff --git a/td/telegram/net/SessionMultiProxy.cpp b/td/telegram/net/SessionMultiProxy.cpp index 3adb8fdb..ca3ff44d 100644 --- a/td/telegram/net/SessionMultiProxy.cpp +++ b/td/telegram/net/SessionMultiProxy.cpp @@ -39,7 +39,7 @@ SessionMultiProxy::SessionMultiProxy(int32 session_count, std::shared_ptrauth_flag() == NetQuery::AuthFlag::On && query->total_timeout_limit > 50) { + if (query->auth_flag() == NetQuery::AuthFlag::On && query->total_timeout_limit > 7) { if (query->session_rand()) { pos = query->session_rand() % sessions_.size(); } else { diff --git a/td/telegram/net/SessionProxy.cpp b/td/telegram/net/SessionProxy.cpp index 17a11052..2b57dd96 100644 --- a/td/telegram/net/SessionProxy.cpp +++ b/td/telegram/net/SessionProxy.cpp @@ -171,7 +171,7 @@ void SessionProxy::open_session(bool force) { // 1. All unauthorized query will be sent into the same SessionProxy // 2. All authorized query are delayed before we have authorization // So only one SessionProxy will be active before we have authorization key - auto should_open = [&]() { + auto should_open = [&] { if (force) { return true; } diff --git a/td/telegram/net/TempAuthKeyWatchdog.h b/td/telegram/net/TempAuthKeyWatchdog.h index d90766a4..4a31e59c 100644 --- a/td/telegram/net/TempAuthKeyWatchdog.h +++ b/td/telegram/net/TempAuthKeyWatchdog.h @@ -9,7 +9,6 @@ #include "td/actor/actor.h" #include "td/telegram/Global.h" -#include "td/telegram/net/DcId.h" #include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/net/NetQueryDispatcher.h" @@ -111,8 +110,7 @@ class TempAuthKeyWatchdog : public NetQueryCallback { return; } LOG(WARNING) << "Start auth_dropTempAuthKeys except keys " << format::as_array(ids); - auto query = G()->net_query_creator().create(create_storer(telegram_api::auth_dropTempAuthKeys(std::move(ids))), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off); + auto query = G()->net_query_creator().create_unauth(telegram_api::auth_dropTempAuthKeys(std::move(ids))); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index 4a7ccf7a..2d030ecd 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -15,7 +15,7 @@ * The JSON serialization of TDLib API objects is straightforward: all API objects are represented as JSON objects with * the same keys as the API object field names. The object type name is stored in the special field '@type' which is * optional in places where type is uniquely determined by the context. - * Fields of Bool type are stored as Boolean, fields of int32, int53 and double types are stored as Number, fields of + * Fields of Bool type are stored as Boolean, fields of int32, int53, and double types are stored as Number, fields of * int64 and string types are stored as String, fields of bytes type are base64 encoded and then stored as String, * fields of vector type are stored as Array. * The main TDLib interface is asynchronous. To match requests with a corresponding response a field "@extra" can diff --git a/td/tl/tl_dotnet_object.h b/td/tl/tl_dotnet_object.h index ea1c8366..a1afce34 100644 --- a/td/tl/tl_dotnet_object.h +++ b/td/tl/tl_dotnet_object.h @@ -63,7 +63,7 @@ inline String^ FromUnmanaged(const std::string &from) { } inline auto CLRCALL BytesFromUnmanaged(const std::string &from) { - Array^ res = REF_NEW Vector(static_cast(from.size())); + Array^ res = REF_NEW Vector(static_cast(from.size())); ArrayIndexType i = 0; for (auto b : from) { ArraySet(res, i++, b); @@ -160,7 +160,7 @@ inline std::string ToUnmanaged(String ^from) { return string_to_unmanaged(from); } -inline std::string ToUnmanaged(Array^ from) { +inline std::string ToUnmanaged(Array^ from) { if (!from) { return std::string(); } diff --git a/td/tl/tl_jni_object.h b/td/tl/tl_jni_object.h index 31c7fe7c..5c4c6e73 100644 --- a/td/tl/tl_jni_object.h +++ b/td/tl/tl_jni_object.h @@ -210,7 +210,7 @@ struct FetchVector { result.reserve(length); for (jsize i = 0; i < length; i++) { jstring str = (jstring)env->GetObjectArrayElement(arr, i); - result.push_back(jni::from_jstring(env, str)); + result.push_back(from_jstring(env, str)); if (str) { env->DeleteLocalRef(str); } @@ -221,6 +221,26 @@ struct FetchVector { } }; +template <> +struct FetchVector { + static std::vector fetch(JNIEnv *env, jobjectArray arr) { + std::vector result; + if (arr != nullptr) { + jsize length = env->GetArrayLength(arr); + result.reserve(length); + for (jsize i = 0; i < length; i++) { + jbyteArray bytes = (jbyteArray)env->GetObjectArrayElement(arr, i); + result.push_back(from_bytes(env, bytes)); + if (bytes) { + env->DeleteLocalRef(bytes); + } + } + env->DeleteLocalRef(arr); + } + return result; + } +}; + template struct FetchVector> { static auto fetch(JNIEnv *env, jobjectArray arr) { diff --git a/tdactor/td/actor/PromiseFuture.h b/tdactor/td/actor/PromiseFuture.h index a8527d7d..bd3f4b80 100644 --- a/tdactor/td/actor/PromiseFuture.h +++ b/tdactor/td/actor/PromiseFuture.h @@ -62,10 +62,12 @@ class SafePromise; template class Promise { public: + bool was_set_value{false}; void set_value(T &&value) { if (!promise_) { return; } + was_set_value = true; promise_->set_value(std::move(value)); promise_.reset(); } @@ -73,6 +75,7 @@ class Promise { if (!promise_) { return; } + was_set_value = true; promise_->set_error(std::move(error)); promise_.reset(); } @@ -80,6 +83,7 @@ class Promise { if (!promise_) { return; } + was_set_value = true; promise_->set_result(std::move(result)); promise_.reset(); } @@ -404,9 +408,9 @@ class PromiseActor final : public PromiseInterface { template class FutureActor final : public Actor { friend class PromiseActor; + public: enum State { Waiting, Ready }; - public: static constexpr int Hangup = 426487; FutureActor() = default; @@ -457,6 +461,10 @@ class FutureActor final : public Actor { } } + State get_state() const { + return state_; + } + template friend void init_promise_future(PromiseActor *promise, FutureActor *future); diff --git a/tdactor/td/actor/impl/ConcurrentScheduler.cpp b/tdactor/td/actor/impl/ConcurrentScheduler.cpp index cab7841b..f2676b42 100644 --- a/tdactor/td/actor/impl/ConcurrentScheduler.cpp +++ b/tdactor/td/actor/impl/ConcurrentScheduler.cpp @@ -78,7 +78,7 @@ void ConcurrentScheduler::start() { #if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED for (size_t i = 1; i + extra_scheduler_ < schedulers_.size(); i++) { auto &sched = schedulers_[i]; - threads_.push_back(td::thread([&]() { + threads_.push_back(td::thread([&] { #if TD_PORT_WINDOWS detail::Iocp::Guard iocp_guard(iocp_.get()); #endif diff --git a/tdactor/td/actor/impl/Scheduler.cpp b/tdactor/td/actor/impl/Scheduler.cpp index 1c653507..9eaaedac 100644 --- a/tdactor/td/actor/impl/Scheduler.cpp +++ b/tdactor/td/actor/impl/Scheduler.cpp @@ -244,7 +244,7 @@ void Scheduler::clear() { if (callback_) { // can't move lambda with unique_ptr inside into std::function auto ptr = actor_info_pool_.release(); - callback_->register_at_finish([=]() { delete ptr; }); + callback_->register_at_finish([ptr] { delete ptr; }); } else { actor_info_pool_.reset(); } diff --git a/tdactor/td/actor/impl/Scheduler.h b/tdactor/td/actor/impl/Scheduler.h index c72b158a..36b7b552 100644 --- a/tdactor/td/actor/impl/Scheduler.h +++ b/tdactor/td/actor/impl/Scheduler.h @@ -235,7 +235,7 @@ void Scheduler::send_lambda(ActorRef actor_ref, EventT &&lambda) { event_context_ptr_->link_token = actor_ref.token(); lambda(); }, - [&]() { + [&] { auto event = Event::lambda(std::forward(lambda)); event.set_link_token(actor_ref.token()); return event; @@ -250,7 +250,7 @@ void Scheduler::send_closure(ActorRef actor_ref, EventT &&closure) { event_context_ptr_->link_token = actor_ref.token(); closure.run(static_cast(actor_info->get_actor_unsafe())); }, - [&]() { + [&] { auto event = Event::immediate_closure(std::forward(closure)); event.set_link_token(actor_ref.token()); return event; @@ -262,7 +262,7 @@ void Scheduler::send(ActorRef actor_ref, Event &&event) { event.set_link_token(actor_ref.token()); return send_impl( actor_ref.get(), [&](ActorInfo *actor_info) { do_event(actor_info, std::move(event)); }, - [&]() { return std::move(event); }); + [&] { return std::move(event); }); } inline void Scheduler::subscribe(PollableFd fd, PollFlags flags) { diff --git a/tddb/td/db/SqliteConnectionSafe.cpp b/tddb/td/db/SqliteConnectionSafe.cpp index 6977d3e4..9b616d42 100644 --- a/tddb/td/db/SqliteConnectionSafe.cpp +++ b/tddb/td/db/SqliteConnectionSafe.cpp @@ -19,9 +19,9 @@ SqliteConnectionSafe::SqliteConnectionSafe(string path, DbKey key) if (r_db.is_error()) { auto r_stat = stat(path); if (r_stat.is_error()) { - LOG(FATAL) << "Can't open database " << path << " (" << r_stat.error() << "): " << r_db.error(); + LOG(FATAL) << "Can't open database (" << r_stat.error() << "): " << r_db.error(); } else { - LOG(FATAL) << "Can't open database " << path << " of size " << r_stat.ok().size_ << ": " << r_db.error(); + LOG(FATAL) << "Can't open database of size " << r_stat.ok().size_ << ": " << r_db.error(); } } auto db = r_db.move_as_ok(); diff --git a/tddb/td/db/SqliteDb.cpp b/tddb/td/db/SqliteDb.cpp index 6e5de16c..9379d511 100644 --- a/tddb/td/db/SqliteDb.cpp +++ b/tddb/td/db/SqliteDb.cpp @@ -72,7 +72,7 @@ Status SqliteDb::init(CSlice path, bool *was_created) { int rc = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); if (rc != SQLITE_OK) { - auto res = Status::Error(PSLICE() << "Failed to open database: " << detail::RawSqliteDb::last_error(db)); + auto res = Status::Error(PSLICE() << "Failed to open database: " << detail::RawSqliteDb::last_error(db, path)); sqlite3_close(db); return res; } @@ -102,12 +102,16 @@ void SqliteDb::trace(bool flag) { Status SqliteDb::exec(CSlice cmd) { CHECK(!empty()); char *msg; - VLOG(sqlite) << "Start exec " << tag("query", cmd) << tag("database", raw_->db()); + if (enable_logging_) { + VLOG(sqlite) << "Start exec " << tag("query", cmd) << tag("database", raw_->db()); + } auto rc = sqlite3_exec(raw_->db(), cmd.c_str(), nullptr, nullptr, &msg); - VLOG(sqlite) << "Finish exec " << tag("query", cmd) << tag("database", raw_->db()); + if (enable_logging_) { + VLOG(sqlite) << "Finish exec " << tag("query", cmd) << tag("database", raw_->db()); + } if (rc != SQLITE_OK) { CHECK(msg != nullptr); - return Status::Error(PSLICE() << tag("query", cmd) << " failed: " << msg); + return Status::Error(PSLICE() << tag("query", cmd) << " to database \"" << raw_->path() << "\" failed: " << msg); } CHECK(msg == nullptr); return Status::OK(); @@ -135,7 +139,7 @@ Result SqliteDb::user_version() { TRY_RESULT(get_version_stmt, get_statement("PRAGMA user_version")); TRY_STATUS(get_version_stmt.step()); if (!get_version_stmt.has_row()) { - return Status::Error("PRAGMA user_version failed"); + return Status::Error(PSLICE() << "PRAGMA user_version failed for database \"" << raw_->path() << '"'); } return get_version_stmt.view_int32(0); } diff --git a/tddb/td/db/SqliteDb.h b/tddb/td/db/SqliteDb.h index 9f7955bd..6152106f 100644 --- a/tddb/td/db/SqliteDb.h +++ b/tddb/td/db/SqliteDb.h @@ -80,6 +80,7 @@ class SqliteDb { explicit SqliteDb(std::shared_ptr raw) : raw_(std::move(raw)) { } std::shared_ptr raw_; + bool enable_logging_ = false; Status check_encryption(); }; diff --git a/tddb/td/db/SqliteKeyValue.cpp b/tddb/td/db/SqliteKeyValue.cpp index b0cf00a1..c9687d90 100644 --- a/tddb/td/db/SqliteKeyValue.cpp +++ b/tddb/td/db/SqliteKeyValue.cpp @@ -24,7 +24,7 @@ Result SqliteKeyValue::init(string path) { } Status SqliteKeyValue::init_with_connection(SqliteDb connection, string table_name) { - auto init_guard = ScopeExit() + [&]() { + auto init_guard = ScopeExit() + [&] { close(); }; db_ = std::move(connection); diff --git a/tddb/td/db/detail/RawSqliteDb.cpp b/tddb/td/db/detail/RawSqliteDb.cpp index 3199ad40..05997b58 100644 --- a/tddb/td/db/detail/RawSqliteDb.cpp +++ b/tddb/td/db/detail/RawSqliteDb.cpp @@ -9,7 +9,6 @@ #include "sqlite/sqlite3.h" #include "td/utils/common.h" -#include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/port/path.h" #include "td/utils/port/Stat.h" @@ -17,8 +16,8 @@ namespace td { namespace detail { -Status RawSqliteDb::last_error(sqlite3 *db) { - return Status::Error(Slice(sqlite3_errmsg(db))); +Status RawSqliteDb::last_error(sqlite3 *db, CSlice path) { + return Status::Error(PSLICE() << Slice(sqlite3_errmsg(db)) << " for database \"" << path << '"'); } Status RawSqliteDb::destroy(Slice path) { @@ -26,7 +25,7 @@ Status RawSqliteDb::destroy(Slice path) { with_db_path(path, [&](auto path) { unlink(path).ignore(); if (!stat(path).is_error()) { - error = Status::Error(PSLICE() << "Failed to delete " << tag("path", path)); + error = Status::Error(PSLICE() << "Failed to delete file \"" << path << '"'); } }); return error; @@ -39,12 +38,12 @@ Status RawSqliteDb::last_error() { destroy(path_).ignore(); } - return last_error(db_); + return last_error(db_, path()); } RawSqliteDb::~RawSqliteDb() { auto rc = sqlite3_close(db_); - LOG_IF(FATAL, rc != SQLITE_OK) << last_error(db_); + LOG_IF(FATAL, rc != SQLITE_OK) << last_error(db_, path()); } } // namespace detail diff --git a/tddb/td/db/detail/RawSqliteDb.h b/tddb/td/db/detail/RawSqliteDb.h index 02941d40..e81d967f 100644 --- a/tddb/td/db/detail/RawSqliteDb.h +++ b/tddb/td/db/detail/RawSqliteDb.h @@ -42,7 +42,7 @@ class RawSqliteDb { } Status last_error(); - static Status last_error(sqlite3 *db); + static Status last_error(sqlite3 *db, CSlice path); bool on_begin() { begin_cnt_++; diff --git a/tdnet/td/net/SslStream.cpp b/tdnet/td/net/SslStream.cpp index 36636104..ab1a2e4b 100644 --- a/tdnet/td/net/SslStream.cpp +++ b/tdnet/td/net/SslStream.cpp @@ -224,7 +224,7 @@ class SslStreamImpl { if (ssl_ctx == nullptr) { return create_openssl_error(-7, "Failed to create an SSL context"); } - auto ssl_ctx_guard = ScopeExit() + [&]() { + auto ssl_ctx_guard = ScopeExit() + [&] { SSL_CTX_free(ssl_ctx); }; long options = 0; @@ -313,7 +313,7 @@ class SslStreamImpl { if (ssl_handle == nullptr) { return create_openssl_error(-13, "Failed to create an SSL handle"); } - auto ssl_handle_guard = ScopeExit() + [&]() { + auto ssl_handle_guard = ScopeExit() + [&] { do_ssl_shutdown(ssl_handle); SSL_free(ssl_handle); }; diff --git a/tdutils/td/utils/BigNum.cpp b/tdutils/td/utils/BigNum.cpp index d943039b..3d216466 100644 --- a/tdutils/td/utils/BigNum.cpp +++ b/tdutils/td/utils/BigNum.cpp @@ -122,10 +122,6 @@ BigNum BigNum::from_raw(void *openssl_big_num) { BigNum::BigNum(unique_ptr &&impl) : impl_(std::move(impl)) { } -void BigNum::ensure_const_time() { - BN_set_flags(impl_->big_num, BN_FLG_CONSTTIME); -} - int BigNum::get_num_bits() const { return BN_num_bits(impl_->big_num); } diff --git a/tdutils/td/utils/BigNum.h b/tdutils/td/utils/BigNum.h index 4df9b49c..6e006bcc 100644 --- a/tdutils/td/utils/BigNum.h +++ b/tdutils/td/utils/BigNum.h @@ -53,8 +53,6 @@ class BigNum { void set_value(uint32 new_value); - void ensure_const_time(); - int get_num_bits() const; int get_num_bytes() const; diff --git a/tdutils/td/utils/Gzip.cpp b/tdutils/td/utils/Gzip.cpp index a5e6d492..6235a1ed 100644 --- a/tdutils/td/utils/Gzip.cpp +++ b/tdutils/td/utils/Gzip.cpp @@ -184,12 +184,12 @@ BufferSlice gzdecode(Slice s) { return message.extract_reader().move_as_buffer_slice(); } -BufferSlice gzencode(Slice s, double k) { +BufferSlice gzencode(Slice s, double max_compression_ratio) { Gzip gzip; gzip.init_encode().ensure(); gzip.set_input(s); gzip.close_input(); - size_t max_size = static_cast(static_cast(s.size()) * k); + size_t max_size = static_cast(static_cast(s.size()) * max_compression_ratio); BufferWriter message{max_size}; gzip.set_output(message.prepare_append()); auto r_state = gzip.run(); diff --git a/tdutils/td/utils/Gzip.h b/tdutils/td/utils/Gzip.h index 17cf6434..e5e21f14 100644 --- a/tdutils/td/utils/Gzip.h +++ b/tdutils/td/utils/Gzip.h @@ -99,7 +99,7 @@ class Gzip { BufferSlice gzdecode(Slice s); -BufferSlice gzencode(Slice s, double k = 0.9); +BufferSlice gzencode(Slice s, double max_compression_ratio); } // namespace td diff --git a/tdutils/td/utils/Random.cpp b/tdutils/td/utils/Random.cpp index 234a081e..1ef70ba9 100644 --- a/tdutils/td/utils/Random.cpp +++ b/tdutils/td/utils/Random.cpp @@ -135,7 +135,7 @@ int Random::fast(int min, int max) { } Random::Xorshift128plus::Xorshift128plus(uint64 seed) { - auto next = [&]() { + auto next = [&] { // splitmix64 seed += static_cast(0x9E3779B97F4A7C15); uint64 z = seed; diff --git a/tdutils/td/utils/ScopeGuard.h b/tdutils/td/utils/ScopeGuard.h index c4c8f9fb..a100f2f7 100644 --- a/tdutils/td/utils/ScopeGuard.h +++ b/tdutils/td/utils/ScopeGuard.h @@ -74,4 +74,4 @@ auto operator+(ScopeExit, FunctionT &&func) { } // namespace td -#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]() +#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&] diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index 3a40d07c..393db001 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -75,19 +75,19 @@ #if TD_PORT_POSIX #define OS_ERROR(message) \ - [&]() { \ + [&] { \ auto saved_errno = errno; \ return ::td::Status::PosixError(saved_errno, (message)); \ }() #define OS_SOCKET_ERROR(message) OS_ERROR(message) #elif TD_PORT_WINDOWS #define OS_ERROR(message) \ - [&]() { \ + [&] { \ auto saved_error = ::GetLastError(); \ return ::td::Status::WindowsError(saved_error, (message)); \ }() #define OS_SOCKET_ERROR(message) \ - [&]() { \ + [&] { \ auto saved_error = ::WSAGetLastError(); \ return ::td::Status::WindowsError(saved_error, (message)); \ }() diff --git a/tdutils/td/utils/bits.h b/tdutils/td/utils/bits.h index 25c26864..70ebde63 100644 --- a/tdutils/td/utils/bits.h +++ b/tdutils/td/utils/bits.h @@ -127,7 +127,11 @@ inline uint64 bswap64(uint64 x) { } inline int32 count_bits32(uint32 x) { - return __popcnt(x); + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += x >> 8; + return (x + (x >> 16)) & 0x3F; } inline int32 count_bits64(uint64 x) { @@ -197,11 +201,15 @@ inline uint64 bswap64(uint64 x) { } inline int32 count_bits32(uint32 x) { - return _popcnt32(static_cast(x)); + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += x >> 8; + return (x + (x >> 16)) & 0x3F; } inline int32 count_bits64(uint64 x) { - return _popcnt64(static_cast<__int64>(x)); + return count_bits32(static_cast(x >> 32)) + count_bits32(static_cast(x)); } #else diff --git a/tdutils/td/utils/format.h b/tdutils/td/utils/format.h index e646a583..0cf0f8a2 100644 --- a/tdutils/td/utils/format.h +++ b/tdutils/td/utils/format.h @@ -192,7 +192,7 @@ inline StringBuilder &operator<<(StringBuilder &logger, Size t) { uint64 value; }; - static constexpr NamedValue sizes[] = {{"B", 1}, {"kB", 1 << 10}, {"MB", 1 << 20}, {"GB", 1 << 30}}; + static constexpr NamedValue sizes[] = {{"B", 1}, {"KB", 1 << 10}, {"MB", 1 << 20}, {"GB", 1 << 30}}; static constexpr size_t sizes_n = sizeof(sizes) / sizeof(NamedValue); size_t i = 0; diff --git a/tdutils/td/utils/port/Clocks.cpp b/tdutils/td/utils/port/Clocks.cpp index 4d80568d..abee5d68 100644 --- a/tdutils/td/utils/port/Clocks.cpp +++ b/tdutils/td/utils/port/Clocks.cpp @@ -7,17 +7,55 @@ #include "td/utils/port/Clocks.h" #include +#include namespace td { -ClocksDefault::Duration ClocksDefault::monotonic() { +double Clocks::monotonic() { + // TODO write system specific functions, because std::chrono::steady_clock is steady only under Windows auto duration = std::chrono::steady_clock::now().time_since_epoch(); return static_cast(std::chrono::duration_cast(duration).count()) * 1e-9; } -ClocksDefault::Duration ClocksDefault::system() { +double Clocks::system() { auto duration = std::chrono::system_clock::now().time_since_epoch(); return static_cast(std::chrono::duration_cast(duration).count()) * 1e-9; } +int Clocks::tz_offset() { + // not thread-safe on POSIX, so calculate the offset only once + static int offset = [] { + auto now = std::time(nullptr); + + auto time_ptr = std::localtime(&now); + if (time_ptr == nullptr) { + return 0; + } + auto local_time = *time_ptr; + + time_ptr = std::gmtime(&now); + if (time_ptr == nullptr) { + return 0; + } + auto utc_time = *time_ptr; + + int minute_offset = local_time.tm_min - utc_time.tm_min; + int hour_offset = local_time.tm_hour - utc_time.tm_hour; + int day_offset = local_time.tm_mday - utc_time.tm_mday; + if (day_offset >= 20) { + day_offset = -1; + } else if (day_offset <= -20) { + day_offset = 1; + } + int sec_offset = day_offset * 86400 + hour_offset * 3600 + minute_offset * 60; + if (sec_offset >= 15 * 3600 || sec_offset <= -15 * 3600) { + return 0; + } + return sec_offset / 900 * 900; // round to 900 just in case + }(); + return offset; +} + +static int init_tz_offset = Clocks::tz_offset(); + } // namespace td diff --git a/tdutils/td/utils/port/Clocks.h b/tdutils/td/utils/port/Clocks.h index 0ae04c20..b11322cf 100644 --- a/tdutils/td/utils/port/Clocks.h +++ b/tdutils/td/utils/port/Clocks.h @@ -8,21 +8,12 @@ namespace td { -class ClocksBase { - public: - using Duration = double; +struct Clocks { + static double monotonic(); + + static double system(); + + static int tz_offset(); }; -// TODO: (maybe) write system specific functions. -class ClocksDefault { - public: - using Duration = ClocksBase::Duration; - - static Duration monotonic(); - - static Duration system(); -}; - -using Clocks = ClocksDefault; - } // namespace td diff --git a/tdutils/td/utils/port/CxCli.h b/tdutils/td/utils/port/CxCli.h index 6c2691c4..0eaa7d60 100644 --- a/tdutils/td/utils/port/CxCli.h +++ b/tdutils/td/utils/port/CxCli.h @@ -9,7 +9,6 @@ #include "td/utils/port/config.h" #include "td/utils/common.h" -#undef small #if TD_WINRT @@ -21,6 +20,8 @@ #include #include +#undef small + #define REF_NEW ref new #define CLRCALL #define DEPRECATED_ATTRIBUTE(message) ::Windows::Foundation::Metadata::Deprecated(message,\ @@ -90,6 +91,8 @@ inline String^ string_from_unmanaged(const std::string &from) { #elif TD_CLI +#undef small + #define REF_NEW gcnew #define CLRCALL __clrcall #define DEPRECATED_ATTRIBUTE(message) System::ObsoleteAttribute(message) diff --git a/tdutils/td/utils/port/FileFd.cpp b/tdutils/td/utils/port/FileFd.cpp index f49184fd..8b9529eb 100644 --- a/tdutils/td/utils/port/FileFd.cpp +++ b/tdutils/td/utils/port/FileFd.cpp @@ -11,6 +11,7 @@ #include "td/utils/port/wstring_convert.h" #endif +#include "td/utils/common.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/port/detail/PollableFd.h" @@ -32,6 +33,10 @@ #include #endif +#if TD_PORT_WINDOWS && defined(WIN32_LEAN_AND_MEAN) +#include +#endif + namespace td { namespace { diff --git a/tdutils/td/utils/port/Stat.cpp b/tdutils/td/utils/port/Stat.cpp index ff1e20d9..a97e915a 100644 --- a/tdutils/td/utils/port/Stat.cpp +++ b/tdutils/td/utils/port/Stat.cpp @@ -251,7 +251,7 @@ Result mem_stat() { LOG(ERROR) << "Failed to parse memory stats " << tag("name", name) << tag("value", value); *x = static_cast(-1); } else { - *x = r_mem.ok() * 1024; // memory is in kB + *x = r_mem.ok() * 1024; // memory is in KB } } if (*s == 0) { diff --git a/tdutils/td/utils/port/detail/Epoll.cpp b/tdutils/td/utils/port/detail/Epoll.cpp index a14465d3..e691570d 100644 --- a/tdutils/td/utils/port/detail/Epoll.cpp +++ b/tdutils/td/utils/port/detail/Epoll.cpp @@ -70,7 +70,8 @@ void Epoll::unsubscribe(PollableFdRef fd_ref) { int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr); auto epoll_ctl_errno = errno; LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed") - << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd << fd.native_fd().validate(); + << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd + << ", status = " << fd.native_fd().validate(); } void Epoll::unsubscribe_before_close(PollableFdRef fd) { diff --git a/tdutils/td/utils/port/detail/NativeFd.cpp b/tdutils/td/utils/port/detail/NativeFd.cpp index 00946516..3899b4a7 100644 --- a/tdutils/td/utils/port/detail/NativeFd.cpp +++ b/tdutils/td/utils/port/detail/NativeFd.cpp @@ -104,7 +104,7 @@ FdSet &get_fd_set() { Status NativeFd::validate() const { #if TD_FD_DEBUG - return get_fd_set().validate(fd_.get()); + return get_fd_set().validate(fd_); #else return Status::OK(); #endif @@ -113,13 +113,13 @@ Status NativeFd::validate() const { NativeFd::NativeFd(Fd fd) : fd_(fd) { VLOG(fd) << *this << " create"; #if TD_FD_DEBUG - get_fd_set().on_create_fd(fd_.get()); + get_fd_set().on_create_fd(fd_); #endif } NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) { #if TD_FD_DEBUG - get_fd_set().on_create_fd(fd_.get()); + get_fd_set().on_create_fd(fd_); #endif } @@ -127,18 +127,26 @@ NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) { NativeFd::NativeFd(Socket socket) : fd_(reinterpret_cast(socket)), is_socket_(true) { VLOG(fd) << *this << " create"; #if TD_FD_DEBUG - get_fd_set().on_create_fd(fd_.get()); + get_fd_set().on_create_fd(fd_); #endif } #endif -NativeFd &NativeFd::operator=(NativeFd &&from) { - CHECK(this != &from); - close(); - fd_ = std::move(from.fd_); +NativeFd::NativeFd(NativeFd &&other) : fd_(other.fd_) { #if TD_PORT_WINDOWS - is_socket_ = from.is_socket_; + is_socket_ = other.is_socket_; #endif + other.fd_ = empty_fd(); +} + +NativeFd &NativeFd::operator=(NativeFd &&other) { + CHECK(this != &other); + close(); + fd_ = other.fd_; +#if TD_PORT_WINDOWS + is_socket_ = other.is_socket_; +#endif + other.fd_ = empty_fd(); return *this; } @@ -147,7 +155,7 @@ NativeFd::~NativeFd() { } NativeFd::operator bool() const { - return fd_.get() != empty_fd(); + return fd_ != empty_fd(); } NativeFd::Fd NativeFd::empty_fd() { @@ -159,7 +167,7 @@ NativeFd::Fd NativeFd::empty_fd() { } NativeFd::Fd NativeFd::fd() const { - return fd_.get(); + return fd_; } NativeFd::Socket NativeFd::socket() const { @@ -167,7 +175,7 @@ NativeFd::Socket NativeFd::socket() const { return fd(); #elif TD_PORT_WINDOWS CHECK(is_socket_); - return reinterpret_cast(fd_.get()); + return reinterpret_cast(fd_); #endif } @@ -231,13 +239,13 @@ void NativeFd::close() { auto error = OS_ERROR("Close fd"); LOG(ERROR) << error; } - fd_ = {}; + fd_ = empty_fd(); } NativeFd::Fd NativeFd::release() { VLOG(fd) << *this << " release"; - auto res = fd_.get(); - fd_ = {}; + auto res = fd_; + fd_ = empty_fd(); #if TD_FD_DEBUG get_fd_set().on_close_fd(res); #endif diff --git a/tdutils/td/utils/port/detail/NativeFd.h b/tdutils/td/utils/port/detail/NativeFd.h index e57d8afc..f6dcce9b 100644 --- a/tdutils/td/utils/port/detail/NativeFd.h +++ b/tdutils/td/utils/port/detail/NativeFd.h @@ -9,7 +9,6 @@ #include "td/utils/port/config.h" #include "td/utils/common.h" -#include "td/utils/MovableValue.h" #include "td/utils/Status.h" #include "td/utils/StringBuilder.h" @@ -25,8 +24,6 @@ class NativeFd { using Socket = SOCKET; #endif NativeFd() = default; - NativeFd(NativeFd &&) = default; - NativeFd &operator=(NativeFd &&); explicit NativeFd(Fd fd); NativeFd(Fd fd, bool nolog); #if TD_PORT_WINDOWS @@ -34,12 +31,12 @@ class NativeFd { #endif NativeFd(const NativeFd &) = delete; NativeFd &operator=(const NativeFd &) = delete; + NativeFd(NativeFd &&other); + NativeFd &operator=(NativeFd &&other); ~NativeFd(); explicit operator bool() const; - static Fd empty_fd(); - Fd fd() const; Socket socket() const; @@ -55,10 +52,10 @@ class NativeFd { Status validate() const; private: -#if TD_PORT_POSIX - MovableValue fd_; -#elif TD_PORT_WINDOWS - MovableValue fd_; + static Fd empty_fd(); + + Fd fd_ = empty_fd(); +#if TD_PORT_WINDOWS bool is_socket_{false}; #endif }; diff --git a/tdutils/td/utils/tests.h b/tdutils/td/utils/tests.h index 68f9dd24..d6730083 100644 --- a/tdutils/td/utils/tests.h +++ b/tdutils/td/utils/tests.h @@ -110,8 +110,8 @@ class Stage { std::atomic value_{0}; }; -inline string rand_string(char from, char to, int len) { - string res(len, 0); +inline string rand_string(int from, int to, size_t len) { + string res(len, '\0'); for (auto &c : res) { c = static_cast(Random::fast(from, to)); } diff --git a/tdutils/td/utils/tl_parsers.cpp b/tdutils/td/utils/tl_parsers.cpp index e5fb3e06..916d9258 100644 --- a/tdutils/td/utils/tl_parsers.cpp +++ b/tdutils/td/utils/tl_parsers.cpp @@ -39,15 +39,17 @@ void TlParser::set_error(const string &error_message) { left_len = 0; data_len = 0; } else { + LOG_CHECK(error_pos != std::numeric_limits::max() && data_len == 0 && left_len == 0) + << data_len << " " << left_len << " " << data << " " << &empty_data[0] << " " << error_pos << " " << error + << " " << data << " " << &empty_data; data = empty_data; - CHECK(error_pos != std::numeric_limits::max()); - LOG_CHECK(data_len == 0) << data_len << " " << left_len << " " << data << " " << &empty_data[0] << " " << error_pos - << " " << error; - CHECK(left_len == 0); } } BufferSlice TlBufferParser::as_buffer_slice(Slice slice) { + if (slice.empty()) { + return BufferSlice(); + } if (is_aligned_pointer<4>(slice.data())) { return parent_->from_slice(slice); } diff --git a/tdutils/td/utils/unicode.cpp b/tdutils/td/utils/unicode.cpp index 20e3b2ff..bc875393 100644 --- a/tdutils/td/utils/unicode.cpp +++ b/tdutils/td/utils/unicode.cpp @@ -15,128 +15,130 @@ namespace td { // list of [(range_begin << 5) + range_type] static const uint32 unicode_simple_category_ranges[] = { - 0, 1028, 1056, 1538, 1856, 2081, 2912, 3105, 3936, 5124, 5152, 5441, - 5472, 5699, 5760, 5793, 5824, 5923, 5953, 5984, 6019, 6112, 6145, 6880, - 6913, 7904, 7937, 22592, 22721, 23104, 23553, 23712, 23937, 23968, 24001, 24032, - 28161, 28320, 28353, 28416, 28481, 28608, 28641, 28672, 28865, 28896, 28929, 29024, - 29057, 29088, 29121, 29760, 29793, 32448, 32481, 36928, 37185, 42496, 42529, 43744, - 43809, 43840, 44033, 45344, 47617, 48480, 48609, 48736, 50177, 51552, 52226, 52544, - 52673, 52736, 52769, 55936, 55969, 56000, 56481, 56544, 56769, 56834, 57153, 57248, - 57313, 57344, 57857, 57888, 57921, 58880, 59809, 62656, 63009, 63040, 63490, 63809, - 64864, 65153, 65216, 65345, 65376, 65537, 66240, 66369, 66400, 66689, 66720, 66817, - 66848, 67585, 68384, 68609, 68960, 70657, 71328, 71361, 71616, 73857, 75584, 75681, - 75712, 76289, 76320, 76545, 76864, 76994, 77312, 77345, 77856, 77985, 78240, 78305, - 78368, 78433, 79136, 79169, 79392, 79425, 79456, 79553, 79680, 79777, 79808, 80321, - 80352, 80769, 80832, 80865, 80960, 81090, 81409, 81472, 81539, 81728, 81793, 81824, - 82081, 82272, 82401, 82464, 82529, 83232, 83265, 83488, 83521, 83584, 83617, 83680, - 83713, 83776, 84769, 84896, 84929, 84960, 85186, 85504, 85569, 85664, 86177, 86464, - 86497, 86592, 86625, 87328, 87361, 87584, 87617, 87680, 87713, 87872, 87969, 88000, - 88577, 88608, 89089, 89152, 89282, 89600, 89889, 89920, 90273, 90528, 90593, 90656, - 90721, 91424, 91457, 91680, 91713, 91776, 91809, 91968, 92065, 92096, 93057, 93120, - 93153, 93248, 93378, 93696, 93729, 93763, 93952, 94305, 94336, 94369, 94560, 94657, - 94752, 94785, 94912, 95009, 95072, 95105, 95136, 95169, 95232, 95329, 95392, 95489, - 95584, 95681, 96064, 96769, 96800, 97474, 97795, 97888, 98465, 98720, 98753, 98848, - 98881, 99616, 99649, 100160, 100257, 100288, 101121, 101216, 101377, 101440, 101570, 101888, - 102147, 102368, 102401, 102432, 102561, 102816, 102849, 102944, 102977, 103712, 103745, 104064, - 104097, 104256, 104353, 104384, 105409, 105440, 105473, 105536, 105666, 105984, 106017, 106080, - 106657, 106912, 106945, 107040, 107073, 108384, 108449, 108480, 108993, 109024, 109185, 109280, - 109315, 109537, 109632, 109762, 110083, 110368, 110401, 110592, 110753, 111328, 111425, 112192, - 112225, 112512, 112545, 112576, 112641, 112864, 113858, 114176, 114721, 116256, 116289, 116352, - 116737, 116960, 117250, 117568, 118817, 118880, 118913, 118944, 118977, 119136, 119169, 119936, - 119969, 120000, 120033, 120352, 120385, 120448, 120737, 120768, 120833, 120992, 121025, 121056, - 121346, 121664, 121729, 121856, 122881, 122912, 123906, 124227, 124544, 124929, 125184, 125217, - 126368, 127233, 127392, 131073, 132448, 133089, 133122, 133440, 133633, 133824, 133953, 134080, - 134177, 134208, 134305, 134368, 134593, 134688, 134817, 135232, 135617, 135648, 135682, 136000, - 136193, 137408, 137441, 137472, 137633, 137664, 137729, 139104, 139137, 149792, 149825, 149952, - 150017, 150240, 150273, 150304, 150337, 150464, 150529, 151840, 151873, 152000, 152065, 153120, - 153153, 153280, 153345, 153568, 153601, 153632, 153665, 153792, 153857, 154336, 154369, 156192, - 156225, 156352, 156417, 158560, 159011, 159648, 159745, 160256, 160769, 163520, 163585, 163776, - 163873, 183712, 183777, 184324, 184353, 185184, 185345, 187744, 187843, 187937, 188192, 188417, - 188832, 188865, 188992, 189441, 190016, 190465, 191040, 191489, 191904, 191937, 192032, 192513, - 194176, 195297, 195328, 195457, 195488, 195586, 195904, 196099, 196416, 197122, 197440, 197633, - 200480, 200705, 200864, 200929, 202016, 202049, 202080, 202241, 204480, 204801, 205792, 207042, - 207361, 208320, 208385, 208544, 208897, 210304, 210433, 211264, 211458, 211779, 211808, 212993, - 213728, 214017, 215712, 217090, 217408, 217602, 217920, 218337, 218368, 221345, 222848, 223393, - 223616, 223746, 224064, 225377, 226336, 226753, 226818, 227137, 228544, 229377, 230528, 231426, - 231744, 231841, 231938, 232257, 233408, 233473, 233760, 233985, 235360, 235425, 235520, 236833, - 236960, 236993, 237184, 237217, 237280, 237377, 237408, 237569, 243712, 245761, 254656, 254721, - 254912, 254977, 256192, 256257, 256448, 256513, 256768, 256801, 256832, 256865, 256896, 256929, - 256960, 256993, 257984, 258049, 259744, 259777, 260000, 260033, 260064, 260161, 260256, 260289, - 260512, 260609, 260736, 260801, 260992, 261121, 261536, 261697, 261792, 261825, 262048, 262148, - 262496, 263428, 263488, 263652, 263680, 265188, 265216, 265731, 265761, 265792, 265859, 266048, - 266209, 266243, 266560, 266753, 267168, 270401, 270432, 270561, 270592, 270657, 270976, 271009, - 271040, 271137, 271296, 271489, 271520, 271553, 271584, 271617, 271648, 271681, 271808, 271841, - 272192, 272257, 272384, 272545, 272704, 272833, 272864, 272899, 274529, 274595, 274752, 297987, - 299904, 302403, 303104, 323267, 324224, 360449, 361952, 361985, 363488, 363521, 367776, 367969, - 368096, 368193, 368256, 368547, 368576, 368641, 369856, 369889, 369920, 370081, 370112, 370177, - 371968, 372193, 372224, 372737, 373472, 373761, 373984, 374017, 374240, 374273, 374496, 374529, - 374752, 374785, 375008, 375041, 375264, 375297, 375520, 375553, 375776, 378337, 378368, 393220, - 393248, 393377, 393443, 393472, 394275, 394560, 394785, 394944, 395011, 395105, 395168, 395297, - 398048, 398241, 398336, 398369, 401248, 401281, 401408, 401569, 402944, 402977, 405984, 406083, - 406208, 406529, 407392, 409089, 409600, 410627, 410944, 411907, 412160, 412195, 412672, 413699, - 414016, 415267, 415744, 425985, 636608, 638977, 1310208, 1310721, 1348000, 1350145, 1351616, 1351681, - 1360288, 1360385, 1360898, 1361217, 1361280, 1361921, 1363424, 1363937, 1364928, 1364993, 1367235, 1367552, - 1368801, 1369088, 1369153, 1372448, 1372513, 1374208, 1374273, 1374432, 1375969, 1376320, 1376353, 1376448, - 1376481, 1376608, 1376641, 1377376, 1377795, 1377984, 1378305, 1379968, 1380417, 1382016, 1382914, 1383232, - 1384001, 1384192, 1384289, 1384320, 1384353, 1384416, 1384450, 1384769, 1385664, 1385985, 1386720, 1387521, - 1388448, 1388673, 1390176, 1391073, 1391106, 1391424, 1391617, 1391776, 1391809, 1392130, 1392449, 1392608, - 1392641, 1393952, 1394689, 1394784, 1394817, 1395072, 1395202, 1395520, 1395713, 1396448, 1396545, 1396576, - 1396673, 1398272, 1398305, 1398336, 1398433, 1398496, 1398561, 1398720, 1398785, 1398816, 1398849, 1398880, - 1399649, 1399744, 1399809, 1400160, 1400385, 1400480, 1400865, 1401056, 1401121, 1401312, 1401377, 1401568, - 1401857, 1402080, 1402113, 1402336, 1402369, 1403744, 1403777, 1404160, 1404417, 1408096, 1408514, 1408832, - 1409025, 1766528, 1766913, 1767648, 1767777, 1769344, 2039809, 2051520, 2051585, 2054976, 2056193, 2056416, - 2056801, 2056960, 2057121, 2057152, 2057185, 2057504, 2057537, 2057952, 2057985, 2058144, 2058177, 2058208, - 2058241, 2058304, 2058337, 2058400, 2058433, 2061888, 2062945, 2074560, 2075137, 2077184, 2077249, 2078976, - 2080257, 2080640, 2084353, 2084512, 2084545, 2088864, 2089474, 2089792, 2090017, 2090848, 2091041, 2091872, - 2092225, 2095072, 2095169, 2095360, 2095425, 2095616, 2095681, 2095872, 2095937, 2096032, 2097153, 2097536, - 2097569, 2098400, 2098433, 2099040, 2099073, 2099136, 2099169, 2099648, 2099713, 2100160, 2101249, 2105184, - 2105571, 2107008, 2107395, 2109216, 2109763, 2109824, 2117633, 2118560, 2118657, 2120224, 2120739, 2121600, - 2121729, 2122755, 2122880, 2123169, 2123811, 2123841, 2124099, 2124128, 2124289, 2125504, 2125825, 2126784, - 2126849, 2128000, 2128129, 2128384, 2128419, 2128576, 2129921, 2134976, 2135042, 2135360, 2135553, 2136704, - 2136833, 2137984, 2138113, 2139392, 2139649, 2141312, 2146305, 2156256, 2156545, 2157248, 2157569, 2157824, - 2162689, 2162880, 2162945, 2162976, 2163009, 2164416, 2164449, 2164512, 2164609, 2164640, 2164705, 2165440, - 2165507, 2165761, 2166496, 2166563, 2166785, 2167776, 2168035, 2168320, 2169857, 2170464, 2170497, 2170560, - 2170723, 2170881, 2171587, 2171776, 2171905, 2172736, 2174977, 2176768, 2176899, 2176961, 2177027, 2177536, - 2177603, 2179073, 2179104, 2179585, 2179712, 2179745, 2179840, 2179873, 2180800, 2181123, 2181408, 2182145, - 2183075, 2183136, 2183169, 2184099, 2184192, 2185217, 2185472, 2185505, 2186400, 2186595, 2186752, 2187265, - 2188992, 2189313, 2190016, 2190083, 2190337, 2190944, 2191107, 2191361, 2191936, 2192675, 2192896, 2195457, - 2197792, 2199553, 2201184, 2201601, 2203232, 2203459, 2203649, 2204800, 2205186, 2205504, 2214915, 2215904, - 2220033, 2220963, 2221281, 2221312, 2221569, 2222272, 2222627, 2222752, 2227201, 2227936, 2228321, 2230016, - 2230851, 2231490, 2231808, 2232417, 2233856, 2234881, 2235680, 2235906, 2236224, 2236513, 2237664, 2238146, - 2238464, 2238593, 2238624, 2238977, 2240096, 2240193, 2240224, 2240609, 2242144, 2242593, 2242720, 2243074, - 2243393, 2243424, 2243457, 2243488, 2243619, 2244256, 2244609, 2245184, 2245217, 2246016, 2248705, 2248928, - 2248961, 2248992, 2249025, 2249152, 2249185, 2249664, 2249697, 2250016, 2250241, 2251744, 2252290, 2252608, - 2252961, 2253216, 2253281, 2253344, 2253409, 2254112, 2254145, 2254368, 2254401, 2254464, 2254497, 2254656, - 2254753, 2254784, 2255361, 2255392, 2255777, 2255936, 2260993, 2262688, 2263265, 2263392, 2263554, 2263872, - 2264033, 2264064, 2265089, 2266624, 2267265, 2267328, 2267361, 2267392, 2267650, 2267968, 2273281, 2274784, - 2276097, 2276224, 2277377, 2278912, 2279553, 2279584, 2279938, 2280256, 2281473, 2282848, 2283265, 2283296, - 2283522, 2283840, 2285569, 2286432, 2287106, 2287427, 2287488, 2293761, 2295168, 2298881, 2300930, 2301251, - 2301536, 2301921, 2301952, 2307073, 2307328, 2307393, 2308640, 2309153, 2309184, 2309217, 2309248, 2310145, - 2310176, 2310497, 2311776, 2312001, 2312032, 2312705, 2312736, 2313089, 2314560, 2315169, 2315200, 2316289, - 2318112, 2326529, 2326816, 2326849, 2328032, 2328577, 2328608, 2329090, 2329411, 2330016, 2330177, 2331136, - 2334721, 2334944, 2334977, 2335040, 2335073, 2336288, 2336961, 2336992, 2337282, 2337600, 2337793, 2337984, - 2338017, 2338080, 2338113, 2339136, 2339585, 2339616, 2339842, 2340160, 2350081, 2350688, 2357251, 2357920, - 2359297, 2388800, 2392067, 2395616, 2396161, 2402432, 2490369, 2524640, 2654209, 2672864, 2949121, 2967328, - 2967553, 2968544, 2968578, 2968896, 2972161, 2973120, 2973697, 2975232, 2975745, 2975872, 2976258, 2976576, - 2976611, 2976832, 2976865, 2977536, 2977697, 2978304, 3000321, 3002371, 3003104, 3006465, 3008864, 3009025, - 3009056, 3011169, 3011584, 3013633, 3013696, 3013729, 3013760, 3014657, 3211008, 3211265, 3235424, 3538945, - 3548128, 3549697, 3549792, 3550337, 3550464, 3550721, 3563392, 3637249, 3640672, 3640833, 3641248, 3641345, - 3641632, 3641857, 3642176, 3824643, 3825280, 3828739, 3829536, 3833857, 3836576, 3836609, 3838880, 3838913, - 3838976, 3839041, 3839072, 3839137, 3839200, 3839265, 3839392, 3839425, 3839808, 3839841, 3839872, 3839905, - 3840128, 3840161, 3842240, 3842273, 3842400, 3842465, 3842720, 3842753, 3842976, 3843009, 3843904, 3843937, - 3844064, 3844097, 3844256, 3844289, 3844320, 3844417, 3844640, 3844673, 3855552, 3855617, 3856416, 3856449, - 3857248, 3857281, 3858272, 3858305, 3859104, 3859137, 3860128, 3860161, 3860960, 3860993, 3861984, 3862017, - 3862816, 3862849, 3863840, 3863873, 3864672, 3864705, 3864960, 3865026, 3866624, 3940353, 3941792, 3942113, - 3942336, 3942402, 3942720, 3942849, 3942880, 3954689, 3956096, 3956226, 3956544, 3997697, 4004000, 4004067, - 4004352, 4005889, 4008064, 4008289, 4008320, 4008450, 4008768, 4034083, 4035968, 4036003, 4036096, 4036131, - 4036256, 4038691, 4040128, 4040163, 4040640, 4046849, 4046976, 4047009, 4047872, 4047905, 4047968, 4048001, - 4048032, 4048097, 4048128, 4048161, 4048480, 4048513, 4048640, 4048673, 4048704, 4048737, 4048768, 4048961, - 4048992, 4049121, 4049152, 4049185, 4049216, 4049249, 4049280, 4049313, 4049408, 4049441, 4049504, 4049537, - 4049568, 4049633, 4049664, 4049697, 4049728, 4049761, 4049792, 4049825, 4049856, 4049889, 4049920, 4049953, - 4050016, 4050049, 4050080, 4050145, 4050272, 4050305, 4050528, 4050561, 4050688, 4050721, 4050848, 4050881, - 4050912, 4050945, 4051264, 4051297, 4051840, 4052001, 4052096, 4052129, 4052288, 4052321, 4052864, 4071427, - 4071840, 4194305, 5561056, 5562369, 5695136, 5695489, 5702592, 5702657, 5887040, 5887489, 6126624, 6225921, - 6243264, 4294967295}; + 0, 1028, 1056, 1538, 1856, 2081, 2912, 3105, 3936, 5124, 5152, 5441, + 5472, 5699, 5760, 5793, 5824, 5923, 5953, 5984, 6019, 6112, 6145, 6880, + 6913, 7904, 7937, 22592, 22721, 23104, 23553, 23712, 23937, 23968, 24001, 24032, + 28161, 28320, 28353, 28416, 28481, 28608, 28641, 28672, 28865, 28896, 28929, 29024, + 29057, 29088, 29121, 29760, 29793, 32448, 32481, 36928, 37185, 42496, 42529, 43744, + 43809, 43840, 44033, 45344, 47617, 48480, 48609, 48736, 50177, 51552, 52226, 52544, + 52673, 52736, 52769, 55936, 55969, 56000, 56481, 56544, 56769, 56834, 57153, 57248, + 57313, 57344, 57857, 57888, 57921, 58880, 59809, 62656, 63009, 63040, 63490, 63809, + 64864, 65153, 65216, 65345, 65376, 65537, 66240, 66369, 66400, 66689, 66720, 66817, + 66848, 67585, 68384, 68609, 68960, 70657, 71328, 71361, 71936, 73857, 75584, 75681, + 75712, 76289, 76320, 76545, 76864, 76994, 77312, 77345, 77856, 77985, 78240, 78305, + 78368, 78433, 79136, 79169, 79392, 79425, 79456, 79553, 79680, 79777, 79808, 80321, + 80352, 80769, 80832, 80865, 80960, 81090, 81409, 81472, 81539, 81728, 81793, 81824, + 82081, 82272, 82401, 82464, 82529, 83232, 83265, 83488, 83521, 83584, 83617, 83680, + 83713, 83776, 84769, 84896, 84929, 84960, 85186, 85504, 85569, 85664, 86177, 86464, + 86497, 86592, 86625, 87328, 87361, 87584, 87617, 87680, 87713, 87872, 87969, 88000, + 88577, 88608, 89089, 89152, 89282, 89600, 89889, 89920, 90273, 90528, 90593, 90656, + 90721, 91424, 91457, 91680, 91713, 91776, 91809, 91968, 92065, 92096, 93057, 93120, + 93153, 93248, 93378, 93696, 93729, 93763, 93952, 94305, 94336, 94369, 94560, 94657, + 94752, 94785, 94912, 95009, 95072, 95105, 95136, 95169, 95232, 95329, 95392, 95489, + 95584, 95681, 96064, 96769, 96800, 97474, 97795, 97888, 98465, 98720, 98753, 98848, + 98881, 99616, 99649, 100160, 100257, 100288, 101121, 101216, 101377, 101440, 101570, 101888, + 102147, 102368, 102401, 102432, 102561, 102816, 102849, 102944, 102977, 103712, 103745, 104064, + 104097, 104256, 104353, 104384, 105409, 105440, 105473, 105536, 105666, 105984, 106017, 106080, + 106625, 106912, 106945, 107040, 107073, 108384, 108449, 108480, 108993, 109024, 109185, 109280, + 109315, 109537, 109632, 109762, 110083, 110368, 110401, 110592, 110753, 111328, 111425, 112192, + 112225, 112512, 112545, 112576, 112641, 112864, 113858, 114176, 114721, 116256, 116289, 116352, + 116737, 116960, 117250, 117568, 118817, 118880, 118913, 118944, 118977, 119136, 119169, 119936, + 119969, 120000, 120033, 120352, 120385, 120448, 120737, 120768, 120833, 120992, 121025, 121056, + 121346, 121664, 121729, 121856, 122881, 122912, 123906, 124227, 124544, 124929, 125184, 125217, + 126368, 127233, 127392, 131073, 132448, 133089, 133122, 133440, 133633, 133824, 133953, 134080, + 134177, 134208, 134305, 134368, 134593, 134688, 134817, 135232, 135617, 135648, 135682, 136000, + 136193, 137408, 137441, 137472, 137633, 137664, 137729, 139104, 139137, 149792, 149825, 149952, + 150017, 150240, 150273, 150304, 150337, 150464, 150529, 151840, 151873, 152000, 152065, 153120, + 153153, 153280, 153345, 153568, 153601, 153632, 153665, 153792, 153857, 154336, 154369, 156192, + 156225, 156352, 156417, 158560, 159011, 159648, 159745, 160256, 160769, 163520, 163585, 163776, + 163873, 183712, 183777, 184324, 184353, 185184, 185345, 187744, 187843, 187937, 188192, 188417, + 188832, 188865, 188992, 189441, 190016, 190465, 191040, 191489, 191904, 191937, 192032, 192513, + 194176, 195297, 195328, 195457, 195488, 195586, 195904, 196099, 196416, 197122, 197440, 197633, + 200480, 200705, 200864, 200929, 202016, 202049, 202080, 202241, 204480, 204801, 205792, 207042, + 207361, 208320, 208385, 208544, 208897, 210304, 210433, 211264, 211458, 211779, 211808, 212993, + 213728, 214017, 215712, 217090, 217408, 217602, 217920, 218337, 218368, 221345, 222848, 223393, + 223616, 223746, 224064, 225377, 226336, 226753, 226818, 227137, 228544, 229377, 230528, 231426, + 231744, 231841, 231938, 232257, 233408, 233473, 233760, 233985, 235360, 235425, 235520, 236833, + 236960, 236993, 237184, 237217, 237280, 237377, 237408, 237569, 243712, 245761, 254656, 254721, + 254912, 254977, 256192, 256257, 256448, 256513, 256768, 256801, 256832, 256865, 256896, 256929, + 256960, 256993, 257984, 258049, 259744, 259777, 260000, 260033, 260064, 260161, 260256, 260289, + 260512, 260609, 260736, 260801, 260992, 261121, 261536, 261697, 261792, 261825, 262048, 262148, + 262496, 263428, 263488, 263652, 263680, 265188, 265216, 265731, 265761, 265792, 265859, 266048, + 266209, 266243, 266560, 266753, 267168, 270401, 270432, 270561, 270592, 270657, 270976, 271009, + 271040, 271137, 271296, 271489, 271520, 271553, 271584, 271617, 271648, 271681, 271808, 271841, + 272192, 272257, 272384, 272545, 272704, 272833, 272864, 272899, 274529, 274595, 274752, 297987, + 299904, 302403, 303104, 323267, 324224, 360449, 361952, 361985, 363488, 363521, 367776, 367969, + 368096, 368193, 368256, 368547, 368576, 368641, 369856, 369889, 369920, 370081, 370112, 370177, + 371968, 372193, 372224, 372737, 373472, 373761, 373984, 374017, 374240, 374273, 374496, 374529, + 374752, 374785, 375008, 375041, 375264, 375297, 375520, 375553, 375776, 378337, 378368, 393220, + 393248, 393377, 393443, 393472, 394275, 394560, 394785, 394944, 395011, 395105, 395168, 395297, + 398048, 398241, 398336, 398369, 401248, 401281, 401408, 401569, 402944, 402977, 405984, 406083, + 406208, 406529, 407552, 409089, 409600, 410627, 410944, 411907, 412160, 412195, 412672, 413699, + 414016, 415267, 415744, 425985, 636928, 638977, 1310624, 1310721, 1348000, 1350145, 1351616, 1351681, + 1360288, 1360385, 1360898, 1361217, 1361280, 1361921, 1363424, 1363937, 1364928, 1364993, 1367235, 1367552, + 1368801, 1369088, 1369153, 1372448, 1372513, 1374208, 1374273, 1374560, 1375905, 1376320, 1376353, 1376448, + 1376481, 1376608, 1376641, 1377376, 1377795, 1377984, 1378305, 1379968, 1380417, 1382016, 1382914, 1383232, + 1384001, 1384192, 1384289, 1384320, 1384353, 1384416, 1384450, 1384769, 1385664, 1385985, 1386720, 1387521, + 1388448, 1388673, 1390176, 1391073, 1391106, 1391424, 1391617, 1391776, 1391809, 1392130, 1392449, 1392608, + 1392641, 1393952, 1394689, 1394784, 1394817, 1395072, 1395202, 1395520, 1395713, 1396448, 1396545, 1396576, + 1396673, 1398272, 1398305, 1398336, 1398433, 1398496, 1398561, 1398720, 1398785, 1398816, 1398849, 1398880, + 1399649, 1399744, 1399809, 1400160, 1400385, 1400480, 1400865, 1401056, 1401121, 1401312, 1401377, 1401568, + 1401857, 1402080, 1402113, 1402336, 1402369, 1403744, 1403777, 1404224, 1404417, 1408096, 1408514, 1408832, + 1409025, 1766528, 1766913, 1767648, 1767777, 1769344, 2039809, 2051520, 2051585, 2054976, 2056193, 2056416, + 2056801, 2056960, 2057121, 2057152, 2057185, 2057504, 2057537, 2057952, 2057985, 2058144, 2058177, 2058208, + 2058241, 2058304, 2058337, 2058400, 2058433, 2061888, 2062945, 2074560, 2075137, 2077184, 2077249, 2078976, + 2080257, 2080640, 2084353, 2084512, 2084545, 2088864, 2089474, 2089792, 2090017, 2090848, 2091041, 2091872, + 2092225, 2095072, 2095169, 2095360, 2095425, 2095616, 2095681, 2095872, 2095937, 2096032, 2097153, 2097536, + 2097569, 2098400, 2098433, 2099040, 2099073, 2099136, 2099169, 2099648, 2099713, 2100160, 2101249, 2105184, + 2105571, 2107008, 2107395, 2109216, 2109763, 2109824, 2117633, 2118560, 2118657, 2120224, 2120739, 2121600, + 2121729, 2122755, 2122880, 2123169, 2123811, 2123841, 2124099, 2124128, 2124289, 2125504, 2125825, 2126784, + 2126849, 2128000, 2128129, 2128384, 2128419, 2128576, 2129921, 2134976, 2135042, 2135360, 2135553, 2136704, + 2136833, 2137984, 2138113, 2139392, 2139649, 2141312, 2146305, 2156256, 2156545, 2157248, 2157569, 2157824, + 2162689, 2162880, 2162945, 2162976, 2163009, 2164416, 2164449, 2164512, 2164609, 2164640, 2164705, 2165440, + 2165507, 2165761, 2166496, 2166563, 2166785, 2167776, 2168035, 2168320, 2169857, 2170464, 2170497, 2170560, + 2170723, 2170881, 2171587, 2171776, 2171905, 2172736, 2174977, 2176768, 2176899, 2176961, 2177027, 2177536, + 2177603, 2179073, 2179104, 2179585, 2179712, 2179745, 2179840, 2179873, 2180800, 2181123, 2181408, 2182145, + 2183075, 2183136, 2183169, 2184099, 2184192, 2185217, 2185472, 2185505, 2186400, 2186595, 2186752, 2187265, + 2188992, 2189313, 2190016, 2190083, 2190337, 2190944, 2191107, 2191361, 2191936, 2192675, 2192896, 2195457, + 2197792, 2199553, 2201184, 2201601, 2203232, 2203459, 2203649, 2204800, 2205186, 2205504, 2214915, 2215904, + 2215937, 2217280, 2217473, 2217536, 2220033, 2220963, 2221281, 2221312, 2221569, 2222272, 2222627, 2222752, + 2225665, 2226339, 2226560, 2227201, 2227936, 2228321, 2230016, 2230851, 2231490, 2231808, 2232417, 2233856, + 2234881, 2235680, 2235906, 2236224, 2236513, 2237664, 2238146, 2238464, 2238593, 2238624, 2238689, 2238720, + 2238977, 2240096, 2240193, 2240224, 2240609, 2242144, 2242593, 2242720, 2243074, 2243393, 2243424, 2243457, + 2243488, 2243619, 2244256, 2244609, 2245184, 2245217, 2246016, 2248705, 2248928, 2248961, 2248992, 2249025, + 2249152, 2249185, 2249664, 2249697, 2250016, 2250241, 2251744, 2252290, 2252608, 2252961, 2253216, 2253281, + 2253344, 2253409, 2254112, 2254145, 2254368, 2254401, 2254464, 2254497, 2254656, 2254753, 2254784, 2255361, + 2255392, 2255777, 2255936, 2260993, 2262688, 2263265, 2263392, 2263554, 2263872, 2264033, 2264128, 2265089, + 2266624, 2267265, 2267328, 2267361, 2267392, 2267650, 2267968, 2273281, 2274784, 2276097, 2276224, 2277377, + 2278912, 2279553, 2279584, 2279938, 2280256, 2281473, 2282848, 2283265, 2283296, 2283522, 2283840, 2285569, + 2286432, 2287106, 2287427, 2287488, 2293761, 2295168, 2298881, 2300930, 2301251, 2301536, 2301921, 2302176, + 2302241, 2302272, 2302337, 2302592, 2302625, 2302688, 2302721, 2303488, 2303969, 2304000, 2304033, 2304064, + 2304514, 2304832, 2307073, 2307328, 2307393, 2308640, 2309153, 2309184, 2309217, 2309248, 2310145, 2310176, + 2310497, 2311776, 2312001, 2312032, 2312705, 2312736, 2313089, 2314560, 2315169, 2315200, 2316289, 2318112, + 2326529, 2326816, 2326849, 2328032, 2328577, 2328608, 2329090, 2329411, 2330016, 2330177, 2331136, 2334721, + 2334944, 2334977, 2335040, 2335073, 2336288, 2336961, 2336992, 2337282, 2337600, 2337793, 2337984, 2338017, + 2338080, 2338113, 2339136, 2339585, 2339616, 2339842, 2340160, 2350081, 2350688, 2356737, 2356768, 2357251, + 2357920, 2359297, 2388800, 2392067, 2395616, 2396161, 2402432, 2490369, 2524640, 2654209, 2672864, 2949121, + 2967328, 2967553, 2968544, 2968578, 2968896, 2972161, 2973120, 2973697, 2975232, 2975745, 2975872, 2976258, + 2976576, 2976611, 2976832, 2976865, 2977536, 2977697, 2978304, 3000321, 3002371, 3003104, 3006465, 3008864, + 3009025, 3009056, 3011169, 3011584, 3013633, 3013696, 3013729, 3013760, 3014657, 3211008, 3211265, 3250880, + 3252225, 3252512, 3538945, 3548128, 3549697, 3549792, 3550337, 3550464, 3550721, 3563392, 3637249, 3640672, + 3640833, 3641248, 3641345, 3641632, 3641857, 3642176, 3824643, 3825280, 3828739, 3829536, 3833857, 3836576, + 3836609, 3838880, 3838913, 3838976, 3839041, 3839072, 3839137, 3839200, 3839265, 3839392, 3839425, 3839808, + 3839841, 3839872, 3839905, 3840128, 3840161, 3842240, 3842273, 3842400, 3842465, 3842720, 3842753, 3842976, + 3843009, 3843904, 3843937, 3844064, 3844097, 3844256, 3844289, 3844320, 3844417, 3844640, 3844673, 3855552, + 3855617, 3856416, 3856449, 3857248, 3857281, 3858272, 3858305, 3859104, 3859137, 3860128, 3860161, 3860960, + 3860993, 3861984, 3862017, 3862816, 3862849, 3863840, 3863873, 3864672, 3864705, 3864960, 3865026, 3866624, + 3940353, 3941792, 3942113, 3942336, 3942402, 3942720, 3942849, 3942880, 3954689, 3956096, 3956226, 3956544, + 3997697, 4004000, 4004067, 4004352, 4005889, 4008064, 4008289, 4008320, 4008450, 4008768, 4034083, 4035968, + 4036003, 4036096, 4036131, 4036256, 4038691, 4040128, 4040163, 4040640, 4046849, 4046976, 4047009, 4047872, + 4047905, 4047968, 4048001, 4048032, 4048097, 4048128, 4048161, 4048480, 4048513, 4048640, 4048673, 4048704, + 4048737, 4048768, 4048961, 4048992, 4049121, 4049152, 4049185, 4049216, 4049249, 4049280, 4049313, 4049408, + 4049441, 4049504, 4049537, 4049568, 4049633, 4049664, 4049697, 4049728, 4049761, 4049792, 4049825, 4049856, + 4049889, 4049920, 4049953, 4050016, 4050049, 4050080, 4050145, 4050272, 4050305, 4050528, 4050561, 4050688, + 4050721, 4050848, 4050881, 4050912, 4050945, 4051264, 4051297, 4051840, 4052001, 4052096, 4052129, 4052288, + 4052321, 4052864, 4071427, 4071840, 4161026, 4161344, 4194305, 5561280, 5562369, 5695136, 5695489, 5702592, + 5702657, 5887040, 5887489, 6126624, 6225921, 6243264, 6291457, 6449504, 4294967295}; static constexpr uint32 TABLE_SIZE = 1280; @@ -211,235 +213,240 @@ static const int16 prepare_search_character_table[TABLE_SIZE] = { 1273, 1275, 1275, 1277, 1277, 1279, 1279}; static const int32 prepare_search_character_ranges[] = { - 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 1370, 32, 1376, -1377, - 1417, 32, 1419, -1420, 1421, 32, 1424, 1424, 1425, 0, 1470, 32, - 1471, 0, 1472, 32, 1473, 0, 1475, 32, 1476, 0, 1478, 32, - 1479, 0, 1480, -1481, 1523, 32, 1525, -1526, 1536, 0, 1542, 32, - 1552, 0, 1563, 32, 1564, 0, 1565, 1565, 1566, 32, 1568, -1569, - 1611, 0, 1632, -1633, 1642, 32, 1646, -1647, 1648, 0, 1649, -1650, - 1748, 32, 1749, 1749, 1750, 0, 1758, 32, 1759, 0, 1765, -1766, - 1767, 0, 1769, 32, 1770, 0, 1774, -1775, 1789, 32, 1791, 1791, - 1792, 32, 1806, 1806, 1807, 0, 1808, 1808, 1809, 0, 1810, -1811, - 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, 2036, -2037, - 2038, 32, 2042, -2043, 2045, 0, 2046, 32, 2048, -2049, 2070, 0, - 2074, 2074, 2075, 0, 2084, 2084, 2085, 0, 2088, 2088, 2089, 0, - 2094, -2095, 2096, 32, 2111, -2112, 2137, 0, 2140, -2141, 2142, 32, - 2143, -2144, 2259, 0, 2308, -2309, 2362, 0, 2365, 2365, 2366, 0, - 2384, 2384, 2385, 0, 2392, -2393, 2402, 0, 2404, 32, 2406, -2407, - 2416, 32, 2417, -2418, 2433, 0, 2436, -2437, 2492, 0, 2493, 2493, - 2494, 0, 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, 2510, -2511, - 2519, 0, 2520, -2521, 2530, 0, 2532, -2533, 2546, 32, 2548, -2549, - 2554, 32, 2556, 2556, 2557, 32, 2558, 0, 2559, -2560, 2561, 0, - 2564, -2565, 2620, 0, 2621, 2621, 2622, 0, 2627, -2628, 2631, 0, - 2633, -2634, 2635, 0, 2638, -2639, 2641, 0, 2642, -2643, 2672, 0, - 2674, -2675, 2677, 0, 2678, 32, 2679, -2680, 2689, 0, 2692, -2693, - 2748, 0, 2749, 2749, 2750, 0, 2758, 2758, 2759, 0, 2762, 2762, - 2763, 0, 2766, -2767, 2786, 0, 2788, -2789, 2800, 32, 2802, -2803, - 2810, 0, 2816, 2816, 2817, 0, 2820, -2821, 2876, 0, 2877, 2877, - 2878, 0, 2885, -2886, 2887, 0, 2889, -2890, 2891, 0, 2894, -2895, - 2902, 0, 2904, -2905, 2914, 0, 2916, -2917, 2928, 32, 2929, -2930, - 2946, 0, 2947, -2948, 3006, 0, 3011, -3012, 3014, 0, 3017, 3017, - 3018, 0, 3022, -3023, 3031, 0, 3032, -3033, 3059, 32, 3067, -3068, - 3072, 0, 3077, -3078, 3134, 0, 3141, 3141, 3142, 0, 3145, 3145, - 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0, 3172, -3173, - 3191, 32, 3192, -3193, 3199, 32, 3200, 3200, 3201, 0, 3204, 32, - 3205, -3206, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269, 3270, 0, - 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288, 3298, 0, - 3300, -3301, 3328, 0, 3332, -3333, 3387, 0, 3389, 3389, 3390, 0, - 3397, 3397, 3398, 0, 3401, 3401, 3402, 0, 3406, 3406, 3407, 32, - 3408, -3409, 3415, 0, 3416, -3417, 3426, 0, 3428, -3429, 3449, 32, - 3450, -3451, 3458, 0, 3460, -3461, 3530, 0, 3531, -3532, 3535, 0, - 3541, 3541, 3542, 0, 3543, 3543, 3544, 0, 3552, -3553, 3570, 0, - 3572, 32, 3573, -3574, 3633, 0, 3634, -3635, 3636, 0, 3643, -3644, - 3647, 32, 3648, -3649, 3655, 0, 3663, 32, 3664, -3665, 3674, 32, - 3676, -3677, 3761, 0, 3762, -3763, 3764, 0, 3773, -3774, 3784, 0, - 3790, -3791, 3841, 32, 3864, 0, 3866, 32, 3872, -3873, 3892, 32, - 3893, 0, 3894, 32, 3895, 0, 3896, 32, 3897, 0, 3898, 32, - 3902, 0, 3904, -3905, 3953, 0, 3973, 32, 3974, 0, 3976, -3977, - 3981, 0, 3992, 3992, 3993, 0, 4029, 4029, 4030, 32, 4038, 0, - 4039, 32, 4045, 4045, 4046, 32, 4059, -4060, 4139, 0, 4159, -4160, - 4170, 32, 4176, -4177, 4182, 0, 4186, -4187, 4190, 0, 4193, 4193, - 4194, 0, 4197, -4198, 4199, 0, 4206, -4207, 4209, 0, 4213, -4214, - 4226, 0, 4238, 4238, 4239, 0, 4240, -4241, 4250, 0, 4254, 32, - 4256, -11521, 4294, 4294, 4295, 11559, 4296, -4297, 4301, 11565, 4302, -4303, - 4347, 32, 4348, -4349, 4957, 0, 4960, 32, 4969, -4970, 5008, 32, - 5018, -5019, 5112, -5105, 5118, -5119, 5120, 32, 5121, -5122, 5741, 32, - 5743, -5744, 5760, 32, 5761, -5762, 5787, 32, 5789, -5790, 5867, 32, - 5870, -5871, 5906, 0, 5909, -5910, 5938, 0, 5941, 32, 5943, -5944, - 5970, 0, 5972, -5973, 6002, 0, 6004, -6005, 6068, 0, 6100, 32, - 6103, 6103, 6104, 32, 6108, 6108, 6109, 0, 6110, -6111, 6144, 32, - 6155, 0, 6159, -6160, 6277, 0, 6279, -6280, 6313, 0, 6314, -6315, - 6432, 0, 6444, -6445, 6448, 0, 6460, -6461, 6464, 32, 6465, -6466, - 6468, 32, 6470, -6471, 6622, 32, 6656, -6657, 6679, 0, 6684, -6685, - 6686, 32, 6688, -6689, 6741, 0, 6751, 6751, 6752, 0, 6781, -6782, - 6783, 0, 6784, -6785, 6816, 32, 6823, 6823, 6824, 32, 6830, -6831, - 6832, 0, 6847, -6848, 6912, 0, 6917, -6918, 6964, 0, 6981, -6982, - 7002, 32, 7019, 0, 7028, 32, 7037, -7038, 7040, 0, 7043, -7044, - 7073, 0, 7086, -7087, 7142, 0, 7156, -7157, 7164, 32, 7168, -7169, - 7204, 0, 7224, -7225, 7227, 32, 7232, -7233, 7294, 32, 7296, 1074, - 7297, 1076, 7298, 1086, 7299, -1090, 7301, 1090, 7302, 1098, 7303, 1123, - 7304, 42571, 7305, -7306, 7312, -4305, 7355, -7356, 7357, -4350, 7360, 32, - 7368, -7369, 7376, 0, 7379, 32, 7380, 0, 7401, -7402, 7405, 0, - 7406, -7407, 7412, 0, 7413, -7414, 7415, 0, 7418, -7419, 7468, 97, - 7469, 230, 7470, 98, 7471, 7471, 7472, -101, 7474, 477, 7475, -104, - 7483, 7483, 7484, 111, 7485, 547, 7486, 112, 7487, 114, 7488, -117, - 7490, 119, 7491, -7492, 7616, 0, 7674, 7674, 7675, 0, 7680, 2097153, - 7830, -7831, 7835, 7777, 7836, -7837, 7838, 223, 7839, 2097153, 7936, -7937, - 7944, -7937, 7952, -7953, 7960, -7953, 7966, -7967, 7976, -7969, 7984, -7985, - 7992, -7985, 8000, -8001, 8008, -8001, 8014, -8015, 8025, 8017, 8026, 8026, - 8027, 8019, 8028, 8028, 8029, 8021, 8030, 8030, 8031, 8023, 8032, -8033, - 8040, -8033, 8048, -8049, 8072, -8065, 8080, -8081, 8088, -8081, 8096, -8097, - 8104, -8097, 8112, -8113, 8120, -8113, 8122, -8049, 8124, 8115, 8125, 32, - 8126, 953, 8127, 32, 8130, -8131, 8136, -8051, 8140, 8131, 8141, 32, - 8144, -8145, 8152, -8145, 8154, -8055, 8156, 8156, 8157, 32, 8160, -8161, - 8168, -8161, 8170, -8059, 8172, 8165, 8173, 32, 8176, -8177, 8184, -8057, - 8186, -8061, 8188, 8179, 8189, 32, 8191, 8191, 8192, 32, 8203, 0, - 8208, 32, 8234, 0, 8239, 32, 8288, 0, 8293, 8293, 8294, 0, - 8304, -8305, 8314, 32, 8319, -8320, 8330, 32, 8335, -8336, 8352, 32, - 8384, -8385, 8400, 0, 8433, -8434, 8448, 32, 8450, 99, 8452, 32, - 8455, 603, 8456, 32, 8457, 102, 8458, 8458, 8459, 104, 8462, -8463, - 8464, 105, 8466, 108, 8467, 8467, 8468, 32, 8469, 110, 8470, 32, - 8473, -113, 8476, 114, 8478, 32, 8484, 122, 8485, 32, 8486, 969, - 8487, 32, 8488, 122, 8489, 32, 8490, 107, 8491, 229, 8492, -99, - 8494, 32, 8495, 8495, 8496, -102, 8498, 8526, 8499, 109, 8500, -8501, - 8506, 32, 8508, -8509, 8510, 947, 8511, 960, 8512, 32, 8517, 100, - 8518, -8519, 8522, 32, 8526, 8526, 8527, 32, 8528, -8529, 8544, -8561, - 8560, -8561, 8579, 8580, 8581, -8582, 8586, 32, 8588, -8589, 8592, 32, - 9255, -9256, 9280, 32, 9291, -9292, 9372, 32, 9398, -9425, 9424, -9425, - 9472, 32, 10102, -10103, 10132, 32, 11124, -11125, 11126, 32, 11158, -11159, - 11160, 32, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549, - 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593, - 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382, - 11383, -11384, 11389, 118, 11390, -576, 11392, 2097153, 11492, 11492, 11493, 32, - 11499, 11500, 11501, 11502, 11503, 0, 11506, 11507, 11508, -11509, 11513, 32, - 11517, 11517, 11518, 32, 11520, -11521, 11632, 32, 11633, -11634, 11647, 0, - 11648, -11649, 11744, 0, 11776, 32, 11823, 11823, 11824, 32, 11856, -11857, - 11904, 32, 11930, 11930, 11931, 32, 11935, 11935, 11936, 32, 12019, -12020, - 12272, 32, 12284, -12285, 12288, 32, 12293, -12294, 12296, 32, 12321, -12322, - 12330, 0, 12336, 32, 12337, -12338, 12342, 32, 12344, -12345, 12349, 32, - 12352, -12353, 12441, 0, 12443, 32, 12445, -12446, 12448, 32, 12449, -12450, - 12539, 32, 12540, 0, 12541, -12542, 12688, 32, 12690, -12691, 12736, 32, - 12772, -12773, 12800, 32, 12831, -12832, 12842, 32, 12868, -12869, 12880, 32, - 12881, -12882, 12910, 32, 12928, -12929, 12992, 32, 13008, -13009, 13055, 32, - 13312, -13313, 19904, 32, 19968, -19969, 42128, 32, 42183, -42184, 42238, 32, - 42240, -42241, 42509, 32, 42512, -42513, 42560, 2097153, 42606, 42606, 42607, 0, - 42611, 32, 42612, 0, 42622, 32, 42623, 2097153, 42652, -42653, 42654, 0, - 42656, -42657, 42736, 0, 42738, 32, 42744, -42745, 42752, 32, 42775, -42776, - 42784, 32, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865, 42873, 42874, - 42875, 42876, 42877, 7545, 42878, 2097153, 42888, 42888, 42889, 32, 42891, 42892, - 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902, 2097153, 42922, 614, - 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927, 42928, 670, - 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42944, -42945, 42946, 42947, - 42948, 42900, 42949, 642, 42950, 7566, 42951, -42952, 43000, 295, 43001, -43002, - 43010, 0, 43011, -43012, 43014, 0, 43015, -43016, 43019, 0, 43020, -43021, - 43043, 0, 43048, 32, 43052, -43053, 43062, 32, 43066, -43067, 43124, 32, - 43128, -43129, 43136, 0, 43138, -43139, 43188, 0, 43206, -43207, 43214, 32, - 43216, -43217, 43232, 0, 43250, -43251, 43256, 32, 43259, 43259, 43260, 32, - 43261, -43262, 43263, 0, 43264, -43265, 43302, 0, 43310, 32, 43312, -43313, - 43335, 0, 43348, -43349, 43359, 32, 43360, -43361, 43392, 0, 43396, -43397, - 43443, 0, 43457, 32, 43470, -43471, 43486, 32, 43488, -43489, 43493, 0, - 43494, -43495, 43561, 0, 43575, -43576, 43587, 0, 43588, -43589, 43596, 0, - 43598, -43599, 43612, 32, 43616, -43617, 43639, 32, 43642, 43642, 43643, 0, - 43646, -43647, 43696, 0, 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, - 43705, -43706, 43710, 0, 43712, 43712, 43713, 0, 43714, -43715, 43742, 32, - 43744, -43745, 43755, 0, 43760, 32, 43762, -43763, 43765, 0, 43767, -43768, - 43867, 32, 43868, -43869, 43888, -5025, 43968, -43969, 44003, 0, 44011, 32, - 44012, 0, 44014, -44015, 55296, 0, 57344, -57345, 64286, 0, 64287, -64288, - 64297, 32, 64298, -64299, 64434, 32, 64450, -64451, 64830, 32, 64832, -64833, - 64976, 32, 65008, -65009, 65020, 32, 65022, -65023, 65024, 0, 65040, 32, - 65050, -65051, 65056, 0, 65072, 32, 65107, 65107, 65108, 32, 65127, 65127, - 65128, 32, 65132, -65133, 65279, 0, 65280, 65280, 65281, 32, 65296, -65297, - 65306, 32, 65313, -65346, 65339, 32, 65345, -65346, 65371, 32, 65382, -65383, - 65504, 32, 65511, 65511, 65512, 32, 65519, -65520, 65529, 0, 65532, 32, - 65536, -65537, 65792, 32, 65795, -65796, 65847, 32, 65856, -65857, 65913, 32, - 65930, -65931, 65932, 32, 65935, 65935, 65936, 32, 65948, -65949, 65952, 32, - 65953, -65954, 66000, 32, 66045, 0, 66046, -66047, 66272, 0, 66273, -66274, - 66422, 0, 66427, -66428, 66463, 32, 66464, -66465, 66512, 32, 66513, -66514, - 66560, -66601, 66600, -66601, 66736, -66777, 66772, -66773, 66927, 32, 66928, -66929, - 67671, 32, 67672, -67673, 67703, 32, 67705, -67706, 67871, 32, 67872, -67873, - 67903, 32, 67904, -67905, 68097, 0, 68100, 68100, 68101, 0, 68103, -68104, - 68108, 0, 68112, -68113, 68152, 0, 68155, -68156, 68159, 0, 68160, -68161, - 68176, 32, 68185, -68186, 68223, 32, 68224, -68225, 68296, 32, 68297, -68298, - 68325, 0, 68327, -68328, 68336, 32, 68343, -68344, 68409, 32, 68416, -68417, - 68505, 32, 68509, -68510, 68736, -68801, 68787, -68788, 68900, 0, 68904, -68905, - 69446, 0, 69457, -69458, 69461, 32, 69466, -69467, 69632, 0, 69635, -69636, - 69688, 0, 69703, 32, 69710, -69711, 69759, 0, 69763, -69764, 69808, 0, - 69819, 32, 69821, 0, 69822, 32, 69826, -69827, 69837, 0, 69838, -69839, - 69888, 0, 69891, -69892, 69927, 0, 69941, -69942, 69952, 32, 69956, 69956, - 69957, 0, 69959, -69960, 70003, 0, 70004, 32, 70006, -70007, 70016, 0, - 70019, -70020, 70067, 0, 70081, -70082, 70085, 32, 70089, 0, 70093, 32, - 70094, -70095, 70107, 32, 70108, 70108, 70109, 32, 70112, -70113, 70188, 0, - 70200, 32, 70206, 0, 70207, -70208, 70313, 32, 70314, -70315, 70367, 0, - 70379, -70380, 70400, 0, 70404, -70405, 70459, 0, 70461, 70461, 70462, 0, - 70469, -70470, 70471, 0, 70473, -70474, 70475, 0, 70478, -70479, 70487, 0, - 70488, -70489, 70498, 0, 70500, -70501, 70502, 0, 70509, -70510, 70512, 0, - 70517, -70518, 70709, 0, 70727, -70728, 70731, 32, 70736, -70737, 70747, 32, - 70748, 70748, 70749, 32, 70750, 0, 70751, -70752, 70832, 0, 70852, -70853, - 70854, 32, 70855, -70856, 71087, 0, 71094, -71095, 71096, 0, 71105, 32, - 71128, -71129, 71132, 0, 71134, -71135, 71216, 0, 71233, 32, 71236, -71237, - 71264, 32, 71277, -71278, 71339, 0, 71352, -71353, 71453, 0, 71468, -71469, - 71484, 32, 71488, -71489, 71724, 0, 71739, 32, 71740, -71741, 71840, -71873, - 71872, -71873, 72145, 0, 72152, -72153, 72154, 0, 72161, 72161, 72162, 32, - 72163, 72163, 72164, 0, 72165, -72166, 72193, 0, 72203, -72204, 72243, 0, - 72250, 72250, 72251, 0, 72255, 32, 72263, 0, 72264, -72265, 72273, 0, - 72284, -72285, 72330, 0, 72346, 32, 72349, 72349, 72350, 32, 72355, -72356, - 72751, 0, 72759, 72759, 72760, 0, 72768, 72768, 72769, 32, 72774, -72775, - 72816, 32, 72818, -72819, 72850, 0, 72872, 72872, 72873, 0, 72887, -72888, - 73009, 0, 73015, -73016, 73018, 0, 73019, 73019, 73020, 0, 73022, 73022, - 73023, 0, 73030, 73030, 73031, 0, 73032, -73033, 73098, 0, 73103, 73103, - 73104, 0, 73106, 73106, 73107, 0, 73112, -73113, 73459, 0, 73463, 32, - 73465, -73466, 73685, 32, 73714, -73715, 73727, 32, 73728, -73729, 74864, 32, - 74869, -74870, 78896, 0, 78905, -78906, 92782, 32, 92784, -92785, 92912, 0, - 92917, 32, 92918, -92919, 92976, 0, 92983, 32, 92992, -92993, 92996, 32, - 92998, -92999, 93760, -93793, 93792, -93793, 93847, 32, 93851, -93852, 94031, 0, - 94032, 94032, 94033, 0, 94088, -94089, 94095, 0, 94099, -94100, 94178, 32, - 94179, -94180, 113820, 32, 113821, 0, 113823, 32, 113824, 0, 113828, -113829, - 118784, 32, 119030, -119031, 119040, 32, 119079, -119080, 119081, 32, 119141, 0, - 119146, 32, 119149, 0, 119171, 32, 119173, 0, 119180, 32, 119210, 0, - 119214, 32, 119273, -119274, 119296, 32, 119362, 0, 119365, 32, 119366, -119367, - 119552, 32, 119639, -119640, 119808, -98, 119834, -119835, 119860, -98, 119886, -119887, - 119912, -98, 119938, -119939, 119964, 97, 119965, 119965, 119966, -100, 119968, -119969, - 119970, 103, 119971, -119972, 119973, -107, 119975, -119976, 119977, -111, 119981, 119981, - 119982, -116, 119990, -119991, 120016, -98, 120042, -120043, 120068, -98, 120070, 120070, - 120071, -101, 120075, -120076, 120077, -107, 120085, 120085, 120086, -116, 120093, -120094, - 120120, -98, 120122, 120122, 120123, -101, 120127, 120127, 120128, -106, 120133, 120133, - 120134, 111, 120135, -120136, 120138, -116, 120145, -120146, 120172, -98, 120198, -120199, - 120224, -98, 120250, -120251, 120276, -98, 120302, -120303, 120328, -98, 120354, -120355, - 120380, -98, 120406, -120407, 120432, -98, 120458, -120459, 120488, -946, 120505, 952, - 120506, -964, 120513, 32, 120514, -120515, 120531, 963, 120532, -120533, 120539, 32, - 120540, -120541, 120546, -946, 120563, 952, 120564, -964, 120571, 32, 120572, -120573, - 120589, 963, 120590, -120591, 120597, 32, 120598, -120599, 120604, -946, 120621, 952, - 120622, -964, 120629, 32, 120630, -120631, 120647, 963, 120648, -120649, 120655, 32, - 120656, -120657, 120662, -946, 120679, 952, 120680, -964, 120687, 32, 120688, -120689, - 120705, 963, 120706, -120707, 120713, 32, 120714, -120715, 120720, -946, 120737, 952, - 120738, -964, 120745, 32, 120746, -120747, 120763, 963, 120764, -120765, 120771, 32, - 120772, -120773, 120778, 989, 120779, -120780, 120832, 32, 121344, 0, 121399, 32, - 121403, 0, 121453, 32, 121461, 0, 121462, 32, 121476, 0, 121477, 32, - 121484, -121485, 121499, 0, 121504, 121504, 121505, 0, 121520, -121521, 122880, 0, - 122887, 122887, 122888, 0, 122905, -122906, 122907, 0, 122914, 122914, 122915, 0, - 122917, 122917, 122918, 0, 122923, -122924, 123184, 0, 123191, -123192, 123215, 32, - 123216, -123217, 123628, 0, 123632, -123633, 123647, 32, 123648, -123649, 125136, 0, - 125143, -125144, 125184, -125219, 125218, -125219, 125252, 0, 125259, -125260, 125278, 32, - 125280, -125281, 126124, 32, 126125, -126126, 126128, 32, 126129, -126130, 126254, 32, - 126255, -126256, 126704, 32, 126706, -126707, 126976, 32, 127020, -127021, 127024, 32, - 127124, -127125, 127136, 32, 127151, -127152, 127153, 32, 127168, 127168, 127169, 32, - 127184, 127184, 127185, 32, 127222, -127223, 127248, 32, 127275, 99, 127276, 114, - 127277, 32, 127280, -98, 127306, 32, 127341, -127342, 127344, 32, 127405, -127406, - 127462, 32, 127490, -127491, 127552, 32, 127561, -127562, 127584, 32, 127590, -127591, - 127744, 32, 128726, -128727, 128736, 32, 128749, -128750, 128752, 32, 128763, -128764, - 128768, 32, 128884, -128885, 128896, 32, 128985, -128986, 128992, 32, 129004, -129005, - 129024, 32, 129036, -129037, 129040, 32, 129096, -129097, 129104, 32, 129114, -129115, - 129120, 32, 129160, -129161, 129168, 32, 129198, -129199, 129280, 32, 129292, 129292, - 129293, 32, 129394, 129394, 129395, 32, 129399, -129400, 129402, 32, 129443, -129444, - 129445, 32, 129451, -129452, 129454, 32, 129483, -129484, 129485, 32, 129620, -129621, - 129632, 32, 129646, -129647, 129648, 32, 129652, -129653, 129656, 32, 129659, -129660, - 129664, 32, 129667, -129668, 129680, 32, 129686, -129687, 131070, 32, 131072, -131073, - 196606, 32, 196608, -196609, 262142, 32, 262144, -262145, 327678, 32, 327680, -327681, - 393214, 32, 393216, -393217, 458750, 32, 458752, -458753, 524286, 32, 524288, -524289, - 589822, 32, 589824, -589825, 655358, 32, 655360, -655361, 720894, 32, 720896, -720897, - 786430, 32, 786432, -786433, 851966, 32, 851968, -851969, 917502, 32, 917504, 917504, - 917505, 0, 917506, -917507, 917536, 0, 917632, -917633, 917760, 0, 918000, -918001, - 983038, 32, 983040, -983041, 1048574, 32, 1048576, -1048577, 1114110, 32, 2147483647, 0}; + 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 1370, 32, 1376, -1377, + 1417, 32, 1419, -1420, 1421, 32, 1424, 1424, 1425, 0, 1470, 32, + 1471, 0, 1472, 32, 1473, 0, 1475, 32, 1476, 0, 1478, 32, + 1479, 0, 1480, -1481, 1523, 32, 1525, -1526, 1536, 0, 1542, 32, + 1552, 0, 1563, 32, 1564, 0, 1565, 1565, 1566, 32, 1568, -1569, + 1611, 0, 1632, -1633, 1642, 32, 1646, -1647, 1648, 0, 1649, -1650, + 1748, 32, 1749, 1749, 1750, 0, 1758, 32, 1759, 0, 1765, -1766, + 1767, 0, 1769, 32, 1770, 0, 1774, -1775, 1789, 32, 1791, 1791, + 1792, 32, 1806, 1806, 1807, 0, 1808, 1808, 1809, 0, 1810, -1811, + 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, 2036, -2037, + 2038, 32, 2042, -2043, 2045, 0, 2046, 32, 2048, -2049, 2070, 0, + 2074, 2074, 2075, 0, 2084, 2084, 2085, 0, 2088, 2088, 2089, 0, + 2094, -2095, 2096, 32, 2111, -2112, 2137, 0, 2140, -2141, 2142, 32, + 2143, -2144, 2259, 0, 2308, -2309, 2362, 0, 2365, 2365, 2366, 0, + 2384, 2384, 2385, 0, 2392, -2393, 2402, 0, 2404, 32, 2406, -2407, + 2416, 32, 2417, -2418, 2433, 0, 2436, -2437, 2492, 0, 2493, 2493, + 2494, 0, 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, 2510, -2511, + 2519, 0, 2520, -2521, 2530, 0, 2532, -2533, 2546, 32, 2548, -2549, + 2554, 32, 2556, 2556, 2557, 32, 2558, 0, 2559, -2560, 2561, 0, + 2564, -2565, 2620, 0, 2621, 2621, 2622, 0, 2627, -2628, 2631, 0, + 2633, -2634, 2635, 0, 2638, -2639, 2641, 0, 2642, -2643, 2672, 0, + 2674, -2675, 2677, 0, 2678, 32, 2679, -2680, 2689, 0, 2692, -2693, + 2748, 0, 2749, 2749, 2750, 0, 2758, 2758, 2759, 0, 2762, 2762, + 2763, 0, 2766, -2767, 2786, 0, 2788, -2789, 2800, 32, 2802, -2803, + 2810, 0, 2816, 2816, 2817, 0, 2820, -2821, 2876, 0, 2877, 2877, + 2878, 0, 2885, -2886, 2887, 0, 2889, -2890, 2891, 0, 2894, -2895, + 2901, 0, 2904, -2905, 2914, 0, 2916, -2917, 2928, 32, 2929, -2930, + 2946, 0, 2947, -2948, 3006, 0, 3011, -3012, 3014, 0, 3017, 3017, + 3018, 0, 3022, -3023, 3031, 0, 3032, -3033, 3059, 32, 3067, -3068, + 3072, 0, 3077, -3078, 3134, 0, 3141, 3141, 3142, 0, 3145, 3145, + 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0, 3172, -3173, + 3191, 32, 3192, -3193, 3199, 32, 3200, 3200, 3201, 0, 3204, 32, + 3205, -3206, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269, 3270, 0, + 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288, 3298, 0, + 3300, -3301, 3328, 0, 3332, -3333, 3387, 0, 3389, 3389, 3390, 0, + 3397, 3397, 3398, 0, 3401, 3401, 3402, 0, 3406, 3406, 3407, 32, + 3408, -3409, 3415, 0, 3416, -3417, 3426, 0, 3428, -3429, 3449, 32, + 3450, -3451, 3457, 0, 3460, -3461, 3530, 0, 3531, -3532, 3535, 0, + 3541, 3541, 3542, 0, 3543, 3543, 3544, 0, 3552, -3553, 3570, 0, + 3572, 32, 3573, -3574, 3633, 0, 3634, -3635, 3636, 0, 3643, -3644, + 3647, 32, 3648, -3649, 3655, 0, 3663, 32, 3664, -3665, 3674, 32, + 3676, -3677, 3761, 0, 3762, -3763, 3764, 0, 3773, -3774, 3784, 0, + 3790, -3791, 3841, 32, 3864, 0, 3866, 32, 3872, -3873, 3892, 32, + 3893, 0, 3894, 32, 3895, 0, 3896, 32, 3897, 0, 3898, 32, + 3902, 0, 3904, -3905, 3953, 0, 3973, 32, 3974, 0, 3976, -3977, + 3981, 0, 3992, 3992, 3993, 0, 4029, 4029, 4030, 32, 4038, 0, + 4039, 32, 4045, 4045, 4046, 32, 4059, -4060, 4139, 0, 4159, -4160, + 4170, 32, 4176, -4177, 4182, 0, 4186, -4187, 4190, 0, 4193, 4193, + 4194, 0, 4197, -4198, 4199, 0, 4206, -4207, 4209, 0, 4213, -4214, + 4226, 0, 4238, 4238, 4239, 0, 4240, -4241, 4250, 0, 4254, 32, + 4256, -11521, 4294, 4294, 4295, 11559, 4296, -4297, 4301, 11565, 4302, -4303, + 4347, 32, 4348, -4349, 4957, 0, 4960, 32, 4969, -4970, 5008, 32, + 5018, -5019, 5112, -5105, 5118, -5119, 5120, 32, 5121, -5122, 5741, 32, + 5743, -5744, 5760, 32, 5761, -5762, 5787, 32, 5789, -5790, 5867, 32, + 5870, -5871, 5906, 0, 5909, -5910, 5938, 0, 5941, 32, 5943, -5944, + 5970, 0, 5972, -5973, 6002, 0, 6004, -6005, 6068, 0, 6100, 32, + 6103, 6103, 6104, 32, 6108, 6108, 6109, 0, 6110, -6111, 6144, 32, + 6155, 0, 6159, -6160, 6277, 0, 6279, -6280, 6313, 0, 6314, -6315, + 6432, 0, 6444, -6445, 6448, 0, 6460, -6461, 6464, 32, 6465, -6466, + 6468, 32, 6470, -6471, 6622, 32, 6656, -6657, 6679, 0, 6684, -6685, + 6686, 32, 6688, -6689, 6741, 0, 6751, 6751, 6752, 0, 6781, -6782, + 6783, 0, 6784, -6785, 6816, 32, 6823, 6823, 6824, 32, 6830, -6831, + 6832, 0, 6849, -6850, 6912, 0, 6917, -6918, 6964, 0, 6981, -6982, + 7002, 32, 7019, 0, 7028, 32, 7037, -7038, 7040, 0, 7043, -7044, + 7073, 0, 7086, -7087, 7142, 0, 7156, -7157, 7164, 32, 7168, -7169, + 7204, 0, 7224, -7225, 7227, 32, 7232, -7233, 7294, 32, 7296, 1074, + 7297, 1076, 7298, 1086, 7299, -1090, 7301, 1090, 7302, 1098, 7303, 1123, + 7304, 42571, 7305, -7306, 7312, -4305, 7355, -7356, 7357, -4350, 7360, 32, + 7368, -7369, 7376, 0, 7379, 32, 7380, 0, 7401, -7402, 7405, 0, + 7406, -7407, 7412, 0, 7413, -7414, 7415, 0, 7418, -7419, 7468, 97, + 7469, 230, 7470, 98, 7471, 7471, 7472, -101, 7474, 477, 7475, -104, + 7483, 7483, 7484, 111, 7485, 547, 7486, 112, 7487, 114, 7488, -117, + 7490, 119, 7491, -7492, 7616, 0, 7674, 7674, 7675, 0, 7680, 2097153, + 7830, -7831, 7835, 7777, 7836, -7837, 7838, 223, 7839, 2097153, 7936, -7937, + 7944, -7937, 7952, -7953, 7960, -7953, 7966, -7967, 7976, -7969, 7984, -7985, + 7992, -7985, 8000, -8001, 8008, -8001, 8014, -8015, 8025, 8017, 8026, 8026, + 8027, 8019, 8028, 8028, 8029, 8021, 8030, 8030, 8031, 8023, 8032, -8033, + 8040, -8033, 8048, -8049, 8072, -8065, 8080, -8081, 8088, -8081, 8096, -8097, + 8104, -8097, 8112, -8113, 8120, -8113, 8122, -8049, 8124, 8115, 8125, 32, + 8126, 953, 8127, 32, 8130, -8131, 8136, -8051, 8140, 8131, 8141, 32, + 8144, -8145, 8152, -8145, 8154, -8055, 8156, 8156, 8157, 32, 8160, -8161, + 8168, -8161, 8170, -8059, 8172, 8165, 8173, 32, 8176, -8177, 8184, -8057, + 8186, -8061, 8188, 8179, 8189, 32, 8191, 8191, 8192, 32, 8203, 0, + 8208, 32, 8234, 0, 8239, 32, 8288, 0, 8293, 8293, 8294, 0, + 8304, -8305, 8314, 32, 8319, -8320, 8330, 32, 8335, -8336, 8352, 32, + 8384, -8385, 8400, 0, 8433, -8434, 8448, 32, 8450, 99, 8452, 32, + 8455, 603, 8456, 32, 8457, 102, 8458, 8458, 8459, 104, 8462, -8463, + 8464, 105, 8466, 108, 8467, 8467, 8468, 32, 8469, 110, 8470, 32, + 8473, -113, 8476, 114, 8478, 32, 8484, 122, 8485, 32, 8486, 969, + 8487, 32, 8488, 122, 8489, 32, 8490, 107, 8491, 229, 8492, -99, + 8494, 32, 8495, 8495, 8496, -102, 8498, 8526, 8499, 109, 8500, -8501, + 8506, 32, 8508, -8509, 8510, 947, 8511, 960, 8512, 32, 8517, 100, + 8518, -8519, 8522, 32, 8526, 8526, 8527, 32, 8528, -8529, 8544, -8561, + 8560, -8561, 8579, 8580, 8581, -8582, 8586, 32, 8588, -8589, 8592, 32, + 9255, -9256, 9280, 32, 9291, -9292, 9372, 32, 9398, -9425, 9424, -9425, + 9472, 32, 10102, -10103, 10132, 32, 11124, -11125, 11126, 32, 11158, 11158, + 11159, 32, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549, + 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593, + 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382, + 11383, -11384, 11389, 118, 11390, -576, 11392, 2097153, 11492, 11492, 11493, 32, + 11499, 11500, 11501, 11502, 11503, 0, 11506, 11507, 11508, -11509, 11513, 32, + 11517, 11517, 11518, 32, 11520, -11521, 11632, 32, 11633, -11634, 11647, 0, + 11648, -11649, 11744, 0, 11776, 32, 11823, 11823, 11824, 32, 11859, -11860, + 11904, 32, 11930, 11930, 11931, 32, 11935, 11935, 11936, 32, 12019, -12020, + 12272, 32, 12284, -12285, 12288, 32, 12293, -12294, 12296, 32, 12321, -12322, + 12330, 0, 12336, 32, 12337, -12338, 12342, 32, 12344, -12345, 12349, 32, + 12352, -12353, 12441, 0, 12443, 32, 12445, -12446, 12448, 32, 12449, -12450, + 12539, 32, 12540, 0, 12541, -12542, 12688, 32, 12690, -12691, 12736, 32, + 12772, -12773, 12800, 32, 12831, -12832, 12842, 32, 12868, -12869, 12880, 32, + 12881, -12882, 12910, 32, 12928, -12929, 12992, 32, 13008, -13009, 13055, 32, + 13312, -13313, 19904, 32, 19968, -19969, 42128, 32, 42183, -42184, 42238, 32, + 42240, -42241, 42509, 32, 42512, -42513, 42560, 2097153, 42606, 42606, 42607, 0, + 42611, 32, 42612, 0, 42622, 32, 42623, 2097153, 42652, -42653, 42654, 0, + 42656, -42657, 42736, 0, 42738, 32, 42744, -42745, 42752, 32, 42775, -42776, + 42784, 32, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865, 42873, 42874, + 42875, 42876, 42877, 7545, 42878, 2097153, 42888, 42888, 42889, 32, 42891, 42892, + 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902, 2097153, 42922, 614, + 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927, 42928, 670, + 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42944, -42945, 42946, 42947, + 42948, 42900, 42949, 642, 42950, 7566, 42951, 42952, 42953, 42954, 42955, -42956, + 42997, 42998, 42999, 42999, 43000, 295, 43001, -43002, 43010, 0, 43011, -43012, + 43014, 0, 43015, -43016, 43019, 0, 43020, -43021, 43043, 0, 43048, 32, + 43052, 0, 43053, -43054, 43062, 32, 43066, -43067, 43124, 32, 43128, -43129, + 43136, 0, 43138, -43139, 43188, 0, 43206, -43207, 43214, 32, 43216, -43217, + 43232, 0, 43250, -43251, 43256, 32, 43259, 43259, 43260, 32, 43261, -43262, + 43263, 0, 43264, -43265, 43302, 0, 43310, 32, 43312, -43313, 43335, 0, + 43348, -43349, 43359, 32, 43360, -43361, 43392, 0, 43396, -43397, 43443, 0, + 43457, 32, 43470, -43471, 43486, 32, 43488, -43489, 43493, 0, 43494, -43495, + 43561, 0, 43575, -43576, 43587, 0, 43588, -43589, 43596, 0, 43598, -43599, + 43612, 32, 43616, -43617, 43639, 32, 43642, 43642, 43643, 0, 43646, -43647, + 43696, 0, 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, 43705, -43706, + 43710, 0, 43712, 43712, 43713, 0, 43714, -43715, 43742, 32, 43744, -43745, + 43755, 0, 43760, 32, 43762, -43763, 43765, 0, 43767, -43768, 43867, 32, + 43868, -43869, 43882, 32, 43884, -43885, 43888, -5025, 43968, -43969, 44003, 0, + 44011, 32, 44012, 0, 44014, -44015, 55296, 0, 57344, -57345, 64286, 0, + 64287, -64288, 64297, 32, 64298, -64299, 64434, 32, 64450, -64451, 64830, 32, + 64832, -64833, 64976, 32, 65008, -65009, 65020, 32, 65022, -65023, 65024, 0, + 65040, 32, 65050, -65051, 65056, 0, 65072, 32, 65107, 65107, 65108, 32, + 65127, 65127, 65128, 32, 65132, -65133, 65279, 0, 65280, 65280, 65281, 32, + 65296, -65297, 65306, 32, 65313, -65346, 65339, 32, 65345, -65346, 65371, 32, + 65382, -65383, 65504, 32, 65511, 65511, 65512, 32, 65519, -65520, 65529, 0, + 65532, 32, 65536, -65537, 65792, 32, 65795, -65796, 65847, 32, 65856, -65857, + 65913, 32, 65930, -65931, 65932, 32, 65935, 65935, 65936, 32, 65949, -65950, + 65952, 32, 65953, -65954, 66000, 32, 66045, 0, 66046, -66047, 66272, 0, + 66273, -66274, 66422, 0, 66427, -66428, 66463, 32, 66464, -66465, 66512, 32, + 66513, -66514, 66560, -66601, 66600, -66601, 66736, -66777, 66772, -66773, 66927, 32, + 66928, -66929, 67671, 32, 67672, -67673, 67703, 32, 67705, -67706, 67871, 32, + 67872, -67873, 67903, 32, 67904, -67905, 68097, 0, 68100, 68100, 68101, 0, + 68103, -68104, 68108, 0, 68112, -68113, 68152, 0, 68155, -68156, 68159, 0, + 68160, -68161, 68176, 32, 68185, -68186, 68223, 32, 68224, -68225, 68296, 32, + 68297, -68298, 68325, 0, 68327, -68328, 68336, 32, 68343, -68344, 68409, 32, + 68416, -68417, 68505, 32, 68509, -68510, 68736, -68801, 68787, -68788, 68900, 0, + 68904, -68905, 69291, 0, 69293, 32, 69294, -69295, 69446, 0, 69457, -69458, + 69461, 32, 69466, -69467, 69632, 0, 69635, -69636, 69688, 0, 69703, 32, + 69710, -69711, 69759, 0, 69763, -69764, 69808, 0, 69819, 32, 69821, 0, + 69822, 32, 69826, -69827, 69837, 0, 69838, -69839, 69888, 0, 69891, -69892, + 69927, 0, 69941, -69942, 69952, 32, 69956, 69956, 69957, 0, 69959, -69960, + 70003, 0, 70004, 32, 70006, -70007, 70016, 0, 70019, -70020, 70067, 0, + 70081, -70082, 70085, 32, 70089, 0, 70093, 32, 70094, 0, 70096, -70097, + 70107, 32, 70108, 70108, 70109, 32, 70112, -70113, 70188, 0, 70200, 32, + 70206, 0, 70207, -70208, 70313, 32, 70314, -70315, 70367, 0, 70379, -70380, + 70400, 0, 70404, -70405, 70459, 0, 70461, 70461, 70462, 0, 70469, -70470, + 70471, 0, 70473, -70474, 70475, 0, 70478, -70479, 70487, 0, 70488, -70489, + 70498, 0, 70500, -70501, 70502, 0, 70509, -70510, 70512, 0, 70517, -70518, + 70709, 0, 70727, -70728, 70731, 32, 70736, -70737, 70746, 32, 70748, 70748, + 70749, 32, 70750, 0, 70751, -70752, 70832, 0, 70852, -70853, 70854, 32, + 70855, -70856, 71087, 0, 71094, -71095, 71096, 0, 71105, 32, 71128, -71129, + 71132, 0, 71134, -71135, 71216, 0, 71233, 32, 71236, -71237, 71264, 32, + 71277, -71278, 71339, 0, 71352, -71353, 71453, 0, 71468, -71469, 71484, 32, + 71488, -71489, 71724, 0, 71739, 32, 71740, -71741, 71840, -71873, 71872, -71873, + 71984, 0, 71990, 71990, 71991, 0, 71993, -71994, 71995, 0, 71999, 71999, + 72000, 0, 72001, 72001, 72002, 0, 72004, 32, 72007, -72008, 72145, 0, + 72152, -72153, 72154, 0, 72161, 72161, 72162, 32, 72163, 72163, 72164, 0, + 72165, -72166, 72193, 0, 72203, -72204, 72243, 0, 72250, 72250, 72251, 0, + 72255, 32, 72263, 0, 72264, -72265, 72273, 0, 72284, -72285, 72330, 0, + 72346, 32, 72349, 72349, 72350, 32, 72355, -72356, 72751, 0, 72759, 72759, + 72760, 0, 72768, 72768, 72769, 32, 72774, -72775, 72816, 32, 72818, -72819, + 72850, 0, 72872, 72872, 72873, 0, 72887, -72888, 73009, 0, 73015, -73016, + 73018, 0, 73019, 73019, 73020, 0, 73022, 73022, 73023, 0, 73030, 73030, + 73031, 0, 73032, -73033, 73098, 0, 73103, 73103, 73104, 0, 73106, 73106, + 73107, 0, 73112, -73113, 73459, 0, 73463, 32, 73465, -73466, 73685, 32, + 73714, -73715, 73727, 32, 73728, -73729, 74864, 32, 74869, -74870, 78896, 0, + 78905, -78906, 92782, 32, 92784, -92785, 92912, 0, 92917, 32, 92918, -92919, + 92976, 0, 92983, 32, 92992, -92993, 92996, 32, 92998, -92999, 93760, -93793, + 93792, -93793, 93847, 32, 93851, -93852, 94031, 0, 94032, 94032, 94033, 0, + 94088, -94089, 94095, 0, 94099, -94100, 94178, 32, 94179, 94179, 94180, 0, + 94181, -94182, 94192, 0, 94194, -94195, 113820, 32, 113821, 0, 113823, 32, + 113824, 0, 113828, -113829, 118784, 32, 119030, -119031, 119040, 32, 119079, -119080, + 119081, 32, 119141, 0, 119146, 32, 119149, 0, 119171, 32, 119173, 0, + 119180, 32, 119210, 0, 119214, 32, 119273, -119274, 119296, 32, 119362, 0, + 119365, 32, 119366, -119367, 119552, 32, 119639, -119640, 119808, -98, 119834, -119835, + 119860, -98, 119886, -119887, 119912, -98, 119938, -119939, 119964, 97, 119965, 119965, + 119966, -100, 119968, -119969, 119970, 103, 119971, -119972, 119973, -107, 119975, -119976, + 119977, -111, 119981, 119981, 119982, -116, 119990, -119991, 120016, -98, 120042, -120043, + 120068, -98, 120070, 120070, 120071, -101, 120075, -120076, 120077, -107, 120085, 120085, + 120086, -116, 120093, -120094, 120120, -98, 120122, 120122, 120123, -101, 120127, 120127, + 120128, -106, 120133, 120133, 120134, 111, 120135, -120136, 120138, -116, 120145, -120146, + 120172, -98, 120198, -120199, 120224, -98, 120250, -120251, 120276, -98, 120302, -120303, + 120328, -98, 120354, -120355, 120380, -98, 120406, -120407, 120432, -98, 120458, -120459, + 120488, -946, 120505, 952, 120506, -964, 120513, 32, 120514, -120515, 120531, 963, + 120532, -120533, 120539, 32, 120540, -120541, 120546, -946, 120563, 952, 120564, -964, + 120571, 32, 120572, -120573, 120589, 963, 120590, -120591, 120597, 32, 120598, -120599, + 120604, -946, 120621, 952, 120622, -964, 120629, 32, 120630, -120631, 120647, 963, + 120648, -120649, 120655, 32, 120656, -120657, 120662, -946, 120679, 952, 120680, -964, + 120687, 32, 120688, -120689, 120705, 963, 120706, -120707, 120713, 32, 120714, -120715, + 120720, -946, 120737, 952, 120738, -964, 120745, 32, 120746, -120747, 120763, 963, + 120764, -120765, 120771, 32, 120772, -120773, 120778, 989, 120779, -120780, 120832, 32, + 121344, 0, 121399, 32, 121403, 0, 121453, 32, 121461, 0, 121462, 32, + 121476, 0, 121477, 32, 121484, -121485, 121499, 0, 121504, 121504, 121505, 0, + 121520, -121521, 122880, 0, 122887, 122887, 122888, 0, 122905, -122906, 122907, 0, + 122914, 122914, 122915, 0, 122917, 122917, 122918, 0, 122923, -122924, 123184, 0, + 123191, -123192, 123215, 32, 123216, -123217, 123628, 0, 123632, -123633, 123647, 32, + 123648, -123649, 125136, 0, 125143, -125144, 125184, -125219, 125218, -125219, 125252, 0, + 125259, -125260, 125278, 32, 125280, -125281, 126124, 32, 126125, -126126, 126128, 32, + 126129, -126130, 126254, 32, 126255, -126256, 126704, 32, 126706, -126707, 126976, 32, + 127020, -127021, 127024, 32, 127124, -127125, 127136, 32, 127151, -127152, 127153, 32, + 127168, 127168, 127169, 32, 127184, 127184, 127185, 32, 127222, -127223, 127245, 32, + 127275, 99, 127276, 114, 127277, 32, 127280, -98, 127306, 32, 127406, -127407, + 127462, 32, 127490, -127491, 127552, 32, 127561, -127562, 127584, 32, 127590, -127591, + 127744, 32, 128728, -128729, 128736, 32, 128749, -128750, 128752, 32, 128765, -128766, + 128768, 32, 128884, -128885, 128896, 32, 128985, -128986, 128992, 32, 129004, -129005, + 129024, 32, 129036, -129037, 129040, 32, 129096, -129097, 129104, 32, 129114, -129115, + 129120, 32, 129160, -129161, 129168, 32, 129198, -129199, 129200, 32, 129202, -129203, + 129280, 32, 129401, 129401, 129402, 32, 129484, 129484, 129485, 32, 129620, -129621, + 129632, 32, 129646, -129647, 129648, 32, 129653, -129654, 129656, 32, 129659, -129660, + 129664, 32, 129671, -129672, 129680, 32, 129705, -129706, 129712, 32, 129719, -129720, + 129728, 32, 129731, -129732, 129744, 32, 129751, -129752, 129792, 32, 129939, 129939, + 129940, 32, 129995, -129996, 131070, 32, 131072, -131073, 196606, 32, 196608, -196609, + 262142, 32, 262144, -262145, 327678, 32, 327680, -327681, 393214, 32, 393216, -393217, + 458750, 32, 458752, -458753, 524286, 32, 524288, -524289, 589822, 32, 589824, -589825, + 655358, 32, 655360, -655361, 720894, 32, 720896, -720897, 786430, 32, 786432, -786433, + 851966, 32, 851968, -851969, 917502, 32, 917504, 917504, 917505, 0, 917506, -917507, + 917536, 0, 917632, -917633, 917760, 0, 918000, -918001, 983038, 32, 983040, -983041, + 1048574, 32, 1048576, -1048577, 1114110, 32, 2147483647, 0}; static const int16 to_lower_table[TABLE_SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, @@ -512,32 +519,31 @@ static const int16 to_lower_table[TABLE_SIZE] = { 1273, 1275, 1275, 1277, 1277, 1279, 1279}; static const int32 to_lower_ranges[] = { - 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 4256, -11521, 4294, 4294, - 4295, 11559, 4296, -4297, 4301, 11565, 4302, -4303, 5024, -43889, 5104, -5113, - 5110, -5111, 7312, -4305, 7355, -7356, 7357, -4350, 7360, -7361, 7680, 2097153, - 7830, -7831, 7838, 223, 7839, 2097153, 7936, -7937, 7944, -7937, 7952, -7953, - 7960, -7953, 7966, -7967, 7976, -7969, 7984, -7985, 7992, -7985, 8000, -8001, - 8008, -8001, 8014, -8015, 8025, 8017, 8026, 8026, 8027, 8019, 8028, 8028, - 8029, 8021, 8030, 8030, 8031, 8023, 8032, -8033, 8040, -8033, 8048, -8049, - 8072, -8065, 8080, -8081, 8088, -8081, 8096, -8097, 8104, -8097, 8112, -8113, - 8120, -8113, 8122, -8049, 8124, 8115, 8125, -8126, 8136, -8051, 8140, 8131, - 8141, -8142, 8152, -8145, 8154, -8055, 8156, -8157, 8168, -8161, 8170, -8059, - 8172, 8165, 8173, -8174, 8184, -8057, 8186, -8061, 8188, 8179, 8189, -8190, - 8486, 969, 8487, -8488, 8490, 107, 8491, 229, 8492, -8493, 8498, 8526, - 8499, -8500, 8544, -8561, 8560, -8561, 8579, 8580, 8581, -8582, 9398, -9425, - 9424, -9425, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549, - 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593, - 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382, - 11383, -11384, 11390, -576, 11392, 2097153, 11492, -11493, 11499, 11500, 11501, 11502, - 11503, -11504, 11506, 11507, 11508, -11509, 42560, 2097153, 42606, -42607, 42624, 2097153, - 42652, -42653, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865, 42873, 42874, - 42875, 42876, 42877, 7545, 42878, 2097153, 42888, -42889, 42891, 42892, 42893, 613, - 42894, -42895, 42896, 2097153, 42900, -42901, 42902, 2097153, 42922, 614, 42923, 604, - 42924, 609, 42925, 620, 42926, 618, 42927, 42927, 42928, 670, 42929, 647, - 42930, 669, 42931, 43859, 42932, 2097153, 42944, -42945, 42946, 42947, 42948, 42900, - 42949, 642, 42950, 7566, 42951, -42952, 65313, -65346, 65339, -65340, 66560, -66601, - 66600, -66601, 66736, -66777, 66772, -66773, 68736, -68801, 68787, -68788, 71840, -71873, - 71872, -71873, 93760, -93793, 93792, -93793, 125184, -125219, 125218, -125219, 2147483647, 0}; + 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 4256, -11521, 4294, 4294, 4295, + 11559, 4296, -4297, 4301, 11565, 4302, -4303, 5024, -43889, 5104, -5113, 5110, -5111, + 7312, -4305, 7355, -7356, 7357, -4350, 7360, -7361, 7680, 2097153, 7830, -7831, 7838, + 223, 7839, 2097153, 7936, -7937, 7944, -7937, 7952, -7953, 7960, -7953, 7966, -7967, + 7976, -7969, 7984, -7985, 7992, -7985, 8000, -8001, 8008, -8001, 8014, -8015, 8025, + 8017, 8026, 8026, 8027, 8019, 8028, 8028, 8029, 8021, 8030, 8030, 8031, 8023, + 8032, -8033, 8040, -8033, 8048, -8049, 8072, -8065, 8080, -8081, 8088, -8081, 8096, + -8097, 8104, -8097, 8112, -8113, 8120, -8113, 8122, -8049, 8124, 8115, 8125, -8126, + 8136, -8051, 8140, 8131, 8141, -8142, 8152, -8145, 8154, -8055, 8156, -8157, 8168, + -8161, 8170, -8059, 8172, 8165, 8173, -8174, 8184, -8057, 8186, -8061, 8188, 8179, + 8189, -8190, 8486, 969, 8487, -8488, 8490, 107, 8491, 229, 8492, -8493, 8498, + 8526, 8499, -8500, 8544, -8561, 8560, -8561, 8579, 8580, 8581, -8582, 9398, -9425, + 9424, -9425, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549, 11364, + 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593, 11374, 625, + 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382, 11383, -11384, 11390, + -576, 11392, 2097153, 11492, -11493, 11499, 11500, 11501, 11502, 11503, -11504, 11506, 11507, + 11508, -11509, 42560, 2097153, 42606, -42607, 42624, 2097153, 42652, -42653, 42786, 2097153, 42800, + -42801, 42802, 2097153, 42864, -42865, 42873, 42874, 42875, 42876, 42877, 7545, 42878, 2097153, + 42888, -42889, 42891, 42892, 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902, + 2097153, 42922, 614, 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927, + 42928, 670, 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42944, -42945, 42946, + 42947, 42948, 42900, 42949, 642, 42950, 7566, 42951, 42952, 42953, 42954, 42955, -42956, + 42997, 42998, 42999, -43000, 65313, -65346, 65339, -65340, 66560, -66601, 66600, -66601, 66736, + -66777, 66772, -66773, 68736, -68801, 68787, -68788, 71840, -71873, 71872, -71873, 93760, -93793, + 93792, -93793, 125184, -125219, 125218, -125219, 2147483647, 0}; static const int16 without_diacritics_table[TABLE_SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, @@ -635,7 +641,7 @@ static const int32 without_diacritics_ranges[] = { 2759, 0, 2762, 2762, 2763, 0, 2766, -2767, 2786, 0, 2788, -2789, 2810, 0, 2816, 2816, 2817, 0, 2820, -2821, 2876, 0, 2877, 2877, 2878, 0, 2885, -2886, 2887, 0, 2889, -2890, 2891, 0, 2894, -2895, - 2902, 0, 2904, -2905, 2908, -2850, 2910, -2911, 2914, 0, 2916, -2917, + 2901, 0, 2904, -2905, 2908, -2850, 2910, -2911, 2914, 0, 2916, -2917, 2946, 0, 2947, -2948, 2964, 2962, 2965, -2966, 3006, 0, 3011, -3012, 3014, 0, 3017, 3017, 3018, 0, 3022, -3023, 3031, 0, 3032, -3033, 3072, 0, 3077, -3078, 3134, 0, 3141, 3141, 3142, 0, 3145, 3145, @@ -644,7 +650,7 @@ static const int32 without_diacritics_ranges[] = { 3270, 0, 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288, 3298, 0, 3300, -3301, 3328, 0, 3332, -3333, 3387, 0, 3389, 3389, 3390, 0, 3397, 3397, 3398, 0, 3401, 3401, 3402, 0, 3406, -3407, - 3415, 0, 3416, -3417, 3426, 0, 3428, -3429, 3458, 0, 3460, -3461, + 3415, 0, 3416, -3417, 3426, 0, 3428, -3429, 3457, 0, 3460, -3461, 3530, 0, 3531, -3532, 3535, 0, 3541, 3541, 3542, 0, 3543, 3543, 3544, 0, 3552, -3553, 3570, 0, 3572, -3573, 3633, 0, 3634, 3634, 3636, 0, 3643, -3644, 3655, 0, 3663, -3664, 3761, 0, 3762, 3762, @@ -663,7 +669,7 @@ static const int32 without_diacritics_ranges[] = { 6109, 0, 6110, -6111, 6155, 0, 6159, -6160, 6277, 0, 6279, -6280, 6313, 0, 6314, -6315, 6432, 0, 6444, -6445, 6448, 0, 6460, -6461, 6679, 0, 6684, -6685, 6741, 0, 6751, 6751, 6752, 0, 6781, -6782, - 6783, 0, 6784, -6785, 6832, 0, 6847, -6848, 6912, 0, 6917, 2097154, + 6783, 0, 6784, -6785, 6832, 0, 6849, -6850, 6912, 0, 6917, 2097154, 6928, -6929, 6930, 2097154, 6932, -6933, 6964, 0, 6981, -6982, 7019, 0, 7028, -7029, 7040, 0, 7043, -7044, 7073, 0, 7086, -7087, 7142, 0, 7156, -7157, 7204, 0, 7224, -7225, 7376, 0, 7379, 7379, 7380, 0, @@ -836,154 +842,157 @@ static const int32 without_diacritics_ranges[] = { 42612, 0, 42622, -42623, 42652, 1098, 42653, 1100, 42654, 0, 42656, -42657, 42736, 0, 42738, -42739, 42864, 2097154, 42866, -42867, 43000, 294, 43001, 339, 43002, -43003, 43010, 0, 43011, -43012, 43014, 0, 43015, -43016, 43019, 0, - 43020, -43021, 43043, 0, 43048, -43049, 43136, 0, 43138, -43139, 43188, 0, - 43206, -43207, 43232, 0, 43250, -43251, 43263, 0, 43264, -43265, 43302, 0, - 43310, -43311, 43335, 0, 43348, -43349, 43392, 0, 43396, -43397, 43443, 0, - 43457, -43458, 43493, 0, 43494, -43495, 43561, 0, 43575, -43576, 43587, 0, - 43588, -43589, 43596, 0, 43598, -43599, 43643, 0, 43646, -43647, 43696, 0, - 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, 43705, -43706, 43710, 0, - 43712, 43712, 43713, 0, 43714, -43715, 43755, 0, 43760, -43761, 43765, 0, - 43767, -43768, 43868, 42791, 43869, 43831, 43870, 619, 43871, 43858, 43872, -43873, - 44003, 0, 44011, 44011, 44012, 0, 44014, -44015, 55296, 0, 57344, -57345, - 63744, 35912, 63745, 26356, 63746, 36554, 63747, 36040, 63748, 28369, 63749, 20018, - 63750, 21477, 63751, 40860, 63753, 22865, 63754, 37329, 63755, 21895, 63756, 22856, - 63757, 25078, 63758, 30313, 63759, 32645, 63760, 34367, 63761, 34746, 63762, 35064, - 63763, 37007, 63764, 27138, 63765, 27931, 63766, 28889, 63767, 29662, 63768, 33853, - 63769, 37226, 63770, 39409, 63771, 20098, 63772, 21365, 63773, 27396, 63774, 29211, - 63775, 34349, 63776, 40478, 63777, 23888, 63778, 28651, 63779, 34253, 63780, 35172, - 63781, 25289, 63782, 33240, 63783, 34847, 63784, 24266, 63785, 26391, 63786, 28010, - 63787, 29436, 63788, 37070, 63789, 20358, 63790, 20919, 63791, 21214, 63792, 25796, - 63793, 27347, 63794, 29200, 63795, 30439, 63796, 32769, 63797, 34310, 63798, 34396, - 63799, 36335, 63800, 38706, 63801, 39791, 63802, 40442, 63803, 30860, 63804, 31103, - 63805, 32160, 63806, 33737, 63807, 37636, 63808, 40575, 63809, 35542, 63810, 22751, - 63811, 24324, 63812, 31840, 63813, 32894, 63814, 29282, 63815, 30922, 63816, 36034, - 63817, 38647, 63818, 22744, 63819, 23650, 63820, 27155, 63821, 28122, 63822, 28431, - 63823, 32047, 63824, 32311, 63825, 38475, 63826, 21202, 63827, 32907, 63828, 20956, - 63829, 20940, 63830, 31260, 63831, 32190, 63832, 33777, 63833, 38517, 63834, 35712, - 63835, 25295, 63836, 27138, 63837, 35582, 63838, 20025, 63839, 23527, 63840, 24594, - 63841, 29575, 63842, 30064, 63843, 21271, 63844, 30971, 63845, 20415, 63846, 24489, - 63847, 19981, 63848, 27852, 63849, 25976, 63850, 32034, 63851, 21443, 63852, 22622, - 63853, 30465, 63854, 33865, 63855, 35498, 63856, 27578, 63857, 36784, 63858, 27784, - 63859, 25342, 63860, 33509, 63861, 25504, 63862, 30053, 63863, 20142, 63864, 20841, - 63865, 20937, 63866, 26753, 63867, 31975, 63868, 33391, 63869, 35538, 63870, 37327, - 63871, 21237, 63872, 21570, 63873, 22899, 63874, 24300, 63875, 26053, 63876, 28670, - 63877, 31018, 63878, 38317, 63879, 39530, 63880, 40599, 63881, 40654, 63882, 21147, - 63883, 26310, 63884, 27511, 63885, 36706, 63886, 24180, 63887, 24976, 63888, 25088, - 63889, 25754, 63890, 28451, 63891, 29001, 63892, 29833, 63893, 31178, 63894, 32244, - 63895, 32879, 63896, 36646, 63897, 34030, 63898, 36899, 63899, 37706, 63900, 21015, - 63901, 21155, 63902, 21693, 63903, 28872, 63904, 35010, 63905, 35498, 63906, 24265, - 63907, 24565, 63908, 25467, 63909, 27566, 63910, 31806, 63911, 29557, 63912, 20196, - 63913, 22265, 63914, 23527, 63915, 23994, 63916, 24604, 63917, 29618, 63918, 29801, - 63919, 32666, 63920, 32838, 63921, 37428, 63922, 38646, 63923, 38728, 63924, 38936, - 63925, 20363, 63926, 31150, 63927, 37300, 63928, 38584, 63929, 24801, 63930, 20102, - 63931, 20698, 63932, 23534, 63933, 23615, 63934, 26009, 63935, 27138, 63936, 29134, - 63937, 30274, 63938, 34044, 63939, 36988, 63940, 40845, 63941, 26248, 63942, 38446, - 63943, 21129, 63944, 26491, 63945, 26611, 63946, 27969, 63947, 28316, 63948, 29705, - 63949, 30041, 63950, 30827, 63951, 32016, 63952, 39006, 63953, 20845, 63954, 25134, - 63955, 38520, 63956, 20523, 63957, 23833, 63958, 28138, 63959, 36650, 63960, 24459, - 63961, 24900, 63962, 26647, 63963, 29575, 63964, 38534, 63965, 21033, 63966, 21519, - 63967, 23653, 63968, 26131, 63969, 26446, 63970, 26792, 63971, 27877, 63972, 29702, - 63973, 30178, 63974, 32633, 63975, 35023, 63976, 35041, 63977, 37324, 63978, 38626, - 63979, 21311, 63980, 28346, 63981, 21533, 63982, 29136, 63983, 29848, 63984, 34298, - 63985, 38563, 63986, 40023, 63987, 40607, 63988, 26519, 63989, 28107, 63990, 33256, - 63991, 31435, 63992, 31520, 63993, 31890, 63994, 29376, 63995, 28825, 63996, 35672, - 63997, 20160, 63998, 33590, 63999, 21050, 64000, 20999, 64001, 24230, 64002, 25299, - 64003, 31958, 64004, 23429, 64005, 27934, 64006, 26292, 64007, 36667, 64008, 34892, - 64009, 38477, 64010, 35211, 64011, 24275, 64012, 20800, 64013, 21952, 64014, -64015, - 64016, 22618, 64017, 64017, 64018, 26228, 64019, -64020, 64021, 20958, 64022, 29482, - 64023, 30410, 64024, 31036, 64025, 31070, 64026, 31077, 64027, 31119, 64028, 38742, - 64029, 31934, 64030, 32701, 64031, 64031, 64032, 34322, 64033, 64033, 64034, 35576, - 64035, -64036, 64037, 36920, 64038, 37117, 64039, -64040, 64042, 39151, 64043, 39164, - 64044, 39208, 64045, 40372, 64046, 37086, 64047, 38583, 64048, 20398, 64049, 20711, - 64050, 20813, 64051, 21193, 64052, 21220, 64053, 21329, 64054, 21917, 64055, 22022, - 64056, 22120, 64057, 22592, 64058, 22696, 64059, 23652, 64060, 23662, 64061, 24724, - 64062, 24936, 64063, 24974, 64064, 25074, 64065, 25935, 64066, 26082, 64067, 26257, - 64068, 26757, 64069, 28023, 64070, 28186, 64071, 28450, 64072, 29038, 64073, 29227, - 64074, 29730, 64075, 30865, 64076, 31038, 64077, 31049, 64078, 31048, 64079, 31056, - 64080, 31062, 64081, 31069, 64082, -31118, 64084, 31296, 64085, 31361, 64086, 31680, - 64087, 32244, 64088, 32265, 64089, 32321, 64090, 32626, 64091, 32773, 64092, 33261, - 64093, 33401, 64095, 33879, 64096, 35088, 64097, 35222, 64098, 35585, 64099, 35641, - 64100, 36051, 64101, 36104, 64102, 36790, 64103, 36920, 64104, 38627, 64105, 38911, - 64106, 38971, 64107, 24693, 64108, 148206, 64109, 33304, 64110, -64111, 64112, 20006, - 64113, 20917, 64114, 20840, 64115, 20352, 64116, 20805, 64117, 20864, 64118, 21191, - 64119, 21242, 64120, 21917, 64121, 21845, 64122, 21913, 64123, 21986, 64124, 22618, - 64125, 22707, 64126, 22852, 64127, 22868, 64128, 23138, 64129, 23336, 64130, 24274, - 64131, 24281, 64132, 24425, 64133, 24493, 64134, 24792, 64135, 24910, 64136, 24840, - 64137, 24974, 64138, 24928, 64139, 25074, 64140, 25140, 64141, 25540, 64142, 25628, - 64143, 25682, 64144, 25942, 64145, 26228, 64146, 26391, 64147, 26395, 64148, 26454, - 64149, 27513, 64150, 27578, 64151, 27969, 64152, 28379, 64153, 28363, 64154, 28450, - 64155, 28702, 64156, 29038, 64157, 30631, 64158, 29237, 64159, 29359, 64160, 29482, - 64161, 29809, 64162, 29958, 64163, 30011, 64164, 30237, 64165, 30239, 64166, 30410, - 64167, 30427, 64168, 30452, 64169, 30538, 64170, 30528, 64171, 30924, 64172, 31409, - 64173, 31680, 64174, 31867, 64175, 32091, 64176, 32244, 64177, 32574, 64178, 32773, - 64179, 33618, 64180, 33775, 64181, 34681, 64182, 35137, 64183, 35206, 64184, 35222, - 64185, 35519, 64186, 35576, 64187, 35531, 64188, 35585, 64189, 35582, 64190, 35565, - 64191, 35641, 64192, 35722, 64193, 36104, 64194, 36664, 64195, 36978, 64196, 37273, - 64197, 37494, 64198, 38524, 64199, 38627, 64200, 38742, 64201, 38875, 64202, 38911, - 64203, 38923, 64204, 38971, 64205, 39698, 64206, 40860, 64207, 141386, 64208, 141380, - 64209, 144341, 64210, 15261, 64211, 16408, 64212, 16441, 64213, 152137, 64214, 154832, - 64215, 163539, 64216, 40771, 64217, 40846, 64218, -64219, 64285, 1497, 64286, 0, - 64287, 1522, 64288, 1506, 64289, 1488, 64290, -1492, 64292, -1500, 64295, 1512, - 64296, 1514, 64297, 43, 64298, 1513, 64302, 1488, 64305, -1490, 64311, 64311, - 64312, -1497, 64317, 64317, 64318, 1502, 64319, 64319, 64320, -1505, 64322, 64322, - 64323, -1508, 64325, 64325, 64326, -1511, 64331, 1493, 64332, 1489, 64333, 1499, - 64334, 1508, 64335, 64335, 64336, 1649, 64338, 1659, 64342, 1662, 64346, 1664, - 64350, 1658, 64354, 1663, 64358, 1657, 64362, 1700, 64366, 1702, 64370, 1668, - 64374, 1667, 64378, 1670, 64382, 1671, 64386, 1677, 64388, 1676, 64390, 1678, - 64392, 1672, 64394, 1688, 64396, 1681, 64398, 1705, 64402, 1711, 64406, 1715, - 64410, 1713, 64414, 1722, 64416, 1723, 64420, 1749, 64422, 1729, 64426, 1726, - 64430, 1746, 64434, -64435, 64467, 1709, 64471, 1735, 64473, 1734, 64475, 1736, - 64477, 1655, 64478, 1739, 64480, 1733, 64482, 1737, 64484, 1744, 64488, 1609, - 64490, -64491, 64508, 1740, 64512, -64513, 64603, -1585, 64605, 1609, 64606, 32, - 64612, -64613, 64656, 1609, 64657, -64658, 64729, 1607, 64730, -64731, 64754, 1600, - 64757, -64758, 64828, 1575, 64830, -64831, 65024, 0, 65040, 44, 65041, -12290, - 65043, -59, 65045, 33, 65046, 63, 65047, -12311, 65049, 8230, 65050, -65051, - 65056, 0, 65072, 8229, 65073, 8212, 65074, 8211, 65075, 95, 65077, -41, - 65079, 123, 65080, 125, 65081, -12309, 65083, -12305, 65085, -12299, 65087, -12297, - 65089, -12301, 65093, -65094, 65095, 91, 65096, 93, 65097, 32, 65101, 95, - 65104, 44, 65105, 12289, 65106, 46, 65107, 65107, 65108, 59, 65109, 58, - 65110, 63, 65111, 33, 65112, 8212, 65113, -41, 65115, 123, 65116, 125, - 65117, -12309, 65119, 35, 65120, 38, 65121, -43, 65123, 45, 65124, 60, - 65125, 62, 65126, 61, 65127, 65127, 65128, 92, 65129, -37, 65131, 64, - 65132, -65133, 65136, 32, 65137, 1600, 65138, 32, 65139, 65139, 65140, 32, - 65141, 65141, 65142, 32, 65143, 1600, 65144, 32, 65145, 1600, 65146, 32, - 65147, 1600, 65148, 32, 65149, 1600, 65150, 32, 65151, 1600, 65152, 1569, - 65153, 1575, 65157, 1608, 65159, 1575, 65161, 1610, 65165, 1575, 65167, 1576, - 65171, 1577, 65173, 1578, 65177, 1579, 65181, 1580, 65185, 1581, 65189, 1582, - 65193, 1583, 65195, 1584, 65197, 1585, 65199, 1586, 65201, 1587, 65205, 1588, - 65209, 1589, 65213, 1590, 65217, 1591, 65221, 1592, 65225, 1593, 65229, 1594, - 65233, 1601, 65237, 1602, 65241, 1603, 65245, 1604, 65249, 1605, 65253, 1606, - 65257, 1607, 65261, 1608, 65263, 1609, 65265, 1610, 65269, -65270, 65279, 0, - 65280, 65280, 65281, -34, 65375, -10630, 65377, 12290, 65378, -12301, 65380, 12289, - 65381, 12539, 65382, 12530, 65383, 12449, 65384, 12451, 65385, 12453, 65386, 12455, - 65387, 12457, 65388, 12515, 65389, 12517, 65390, 12519, 65391, 12483, 65392, 65392, - 65393, 12450, 65394, 12452, 65395, 12454, 65396, 12456, 65397, -12459, 65399, 12461, - 65400, 12463, 65401, 12465, 65402, 12467, 65403, 12469, 65404, 12471, 65405, 12473, - 65406, 12475, 65407, 12477, 65408, 12479, 65409, 12481, 65410, 12484, 65411, 12486, - 65412, 12488, 65413, -12491, 65419, 12498, 65420, 12501, 65421, 12504, 65422, 12507, - 65423, -12511, 65428, 12516, 65429, 12518, 65430, -12521, 65436, 12527, 65437, 12531, - 65438, -65439, 65440, 4448, 65441, -4353, 65443, 4522, 65444, 4354, 65445, -4525, - 65447, -4356, 65450, -4529, 65456, 4378, 65457, -4359, 65460, 4385, 65461, -4362, - 65471, -65472, 65474, -4450, 65480, -65481, 65482, -4456, 65488, -65489, 65490, -4462, - 65496, -65497, 65498, -4468, 65501, -65502, 65504, -163, 65506, 172, 65507, 32, - 65508, 166, 65509, 165, 65510, 8361, 65511, 65511, 65512, 9474, 65513, -8593, - 65517, 9632, 65518, 9675, 65519, -65520, 65529, 0, 65532, -65533, 66045, 0, - 66046, -66047, 66272, 0, 66273, -66274, 66422, 0, 66427, -66428, 68097, 0, - 68100, 68100, 68101, 0, 68103, -68104, 68108, 0, 68112, -68113, 68152, 0, - 68155, -68156, 68159, 0, 68160, -68161, 68325, 0, 68327, -68328, 68900, 0, - 68904, -68905, 69446, 0, 69457, -69458, 69632, 0, 69635, -69636, 69688, 0, + 43020, -43021, 43043, 0, 43048, -43049, 43052, 0, 43053, -43054, 43136, 0, + 43138, -43139, 43188, 0, 43206, -43207, 43232, 0, 43250, -43251, 43263, 0, + 43264, -43265, 43302, 0, 43310, -43311, 43335, 0, 43348, -43349, 43392, 0, + 43396, -43397, 43443, 0, 43457, -43458, 43493, 0, 43494, -43495, 43561, 0, + 43575, -43576, 43587, 0, 43588, -43589, 43596, 0, 43598, -43599, 43643, 0, + 43646, -43647, 43696, 0, 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, + 43705, -43706, 43710, 0, 43712, 43712, 43713, 0, 43714, -43715, 43755, 0, + 43760, -43761, 43765, 0, 43767, -43768, 43868, 42791, 43869, 43831, 43870, 619, + 43871, 43858, 43872, -43873, 43881, 653, 43882, -43883, 44003, 0, 44011, 44011, + 44012, 0, 44014, -44015, 55296, 0, 57344, -57345, 63744, 35912, 63745, 26356, + 63746, 36554, 63747, 36040, 63748, 28369, 63749, 20018, 63750, 21477, 63751, 40860, + 63753, 22865, 63754, 37329, 63755, 21895, 63756, 22856, 63757, 25078, 63758, 30313, + 63759, 32645, 63760, 34367, 63761, 34746, 63762, 35064, 63763, 37007, 63764, 27138, + 63765, 27931, 63766, 28889, 63767, 29662, 63768, 33853, 63769, 37226, 63770, 39409, + 63771, 20098, 63772, 21365, 63773, 27396, 63774, 29211, 63775, 34349, 63776, 40478, + 63777, 23888, 63778, 28651, 63779, 34253, 63780, 35172, 63781, 25289, 63782, 33240, + 63783, 34847, 63784, 24266, 63785, 26391, 63786, 28010, 63787, 29436, 63788, 37070, + 63789, 20358, 63790, 20919, 63791, 21214, 63792, 25796, 63793, 27347, 63794, 29200, + 63795, 30439, 63796, 32769, 63797, 34310, 63798, 34396, 63799, 36335, 63800, 38706, + 63801, 39791, 63802, 40442, 63803, 30860, 63804, 31103, 63805, 32160, 63806, 33737, + 63807, 37636, 63808, 40575, 63809, 35542, 63810, 22751, 63811, 24324, 63812, 31840, + 63813, 32894, 63814, 29282, 63815, 30922, 63816, 36034, 63817, 38647, 63818, 22744, + 63819, 23650, 63820, 27155, 63821, 28122, 63822, 28431, 63823, 32047, 63824, 32311, + 63825, 38475, 63826, 21202, 63827, 32907, 63828, 20956, 63829, 20940, 63830, 31260, + 63831, 32190, 63832, 33777, 63833, 38517, 63834, 35712, 63835, 25295, 63836, 27138, + 63837, 35582, 63838, 20025, 63839, 23527, 63840, 24594, 63841, 29575, 63842, 30064, + 63843, 21271, 63844, 30971, 63845, 20415, 63846, 24489, 63847, 19981, 63848, 27852, + 63849, 25976, 63850, 32034, 63851, 21443, 63852, 22622, 63853, 30465, 63854, 33865, + 63855, 35498, 63856, 27578, 63857, 36784, 63858, 27784, 63859, 25342, 63860, 33509, + 63861, 25504, 63862, 30053, 63863, 20142, 63864, 20841, 63865, 20937, 63866, 26753, + 63867, 31975, 63868, 33391, 63869, 35538, 63870, 37327, 63871, 21237, 63872, 21570, + 63873, 22899, 63874, 24300, 63875, 26053, 63876, 28670, 63877, 31018, 63878, 38317, + 63879, 39530, 63880, 40599, 63881, 40654, 63882, 21147, 63883, 26310, 63884, 27511, + 63885, 36706, 63886, 24180, 63887, 24976, 63888, 25088, 63889, 25754, 63890, 28451, + 63891, 29001, 63892, 29833, 63893, 31178, 63894, 32244, 63895, 32879, 63896, 36646, + 63897, 34030, 63898, 36899, 63899, 37706, 63900, 21015, 63901, 21155, 63902, 21693, + 63903, 28872, 63904, 35010, 63905, 35498, 63906, 24265, 63907, 24565, 63908, 25467, + 63909, 27566, 63910, 31806, 63911, 29557, 63912, 20196, 63913, 22265, 63914, 23527, + 63915, 23994, 63916, 24604, 63917, 29618, 63918, 29801, 63919, 32666, 63920, 32838, + 63921, 37428, 63922, 38646, 63923, 38728, 63924, 38936, 63925, 20363, 63926, 31150, + 63927, 37300, 63928, 38584, 63929, 24801, 63930, 20102, 63931, 20698, 63932, 23534, + 63933, 23615, 63934, 26009, 63935, 27138, 63936, 29134, 63937, 30274, 63938, 34044, + 63939, 36988, 63940, 40845, 63941, 26248, 63942, 38446, 63943, 21129, 63944, 26491, + 63945, 26611, 63946, 27969, 63947, 28316, 63948, 29705, 63949, 30041, 63950, 30827, + 63951, 32016, 63952, 39006, 63953, 20845, 63954, 25134, 63955, 38520, 63956, 20523, + 63957, 23833, 63958, 28138, 63959, 36650, 63960, 24459, 63961, 24900, 63962, 26647, + 63963, 29575, 63964, 38534, 63965, 21033, 63966, 21519, 63967, 23653, 63968, 26131, + 63969, 26446, 63970, 26792, 63971, 27877, 63972, 29702, 63973, 30178, 63974, 32633, + 63975, 35023, 63976, 35041, 63977, 37324, 63978, 38626, 63979, 21311, 63980, 28346, + 63981, 21533, 63982, 29136, 63983, 29848, 63984, 34298, 63985, 38563, 63986, 40023, + 63987, 40607, 63988, 26519, 63989, 28107, 63990, 33256, 63991, 31435, 63992, 31520, + 63993, 31890, 63994, 29376, 63995, 28825, 63996, 35672, 63997, 20160, 63998, 33590, + 63999, 21050, 64000, 20999, 64001, 24230, 64002, 25299, 64003, 31958, 64004, 23429, + 64005, 27934, 64006, 26292, 64007, 36667, 64008, 34892, 64009, 38477, 64010, 35211, + 64011, 24275, 64012, 20800, 64013, 21952, 64014, -64015, 64016, 22618, 64017, 64017, + 64018, 26228, 64019, -64020, 64021, 20958, 64022, 29482, 64023, 30410, 64024, 31036, + 64025, 31070, 64026, 31077, 64027, 31119, 64028, 38742, 64029, 31934, 64030, 32701, + 64031, 64031, 64032, 34322, 64033, 64033, 64034, 35576, 64035, -64036, 64037, 36920, + 64038, 37117, 64039, -64040, 64042, 39151, 64043, 39164, 64044, 39208, 64045, 40372, + 64046, 37086, 64047, 38583, 64048, 20398, 64049, 20711, 64050, 20813, 64051, 21193, + 64052, 21220, 64053, 21329, 64054, 21917, 64055, 22022, 64056, 22120, 64057, 22592, + 64058, 22696, 64059, 23652, 64060, 23662, 64061, 24724, 64062, 24936, 64063, 24974, + 64064, 25074, 64065, 25935, 64066, 26082, 64067, 26257, 64068, 26757, 64069, 28023, + 64070, 28186, 64071, 28450, 64072, 29038, 64073, 29227, 64074, 29730, 64075, 30865, + 64076, 31038, 64077, 31049, 64078, 31048, 64079, 31056, 64080, 31062, 64081, 31069, + 64082, -31118, 64084, 31296, 64085, 31361, 64086, 31680, 64087, 32244, 64088, 32265, + 64089, 32321, 64090, 32626, 64091, 32773, 64092, 33261, 64093, 33401, 64095, 33879, + 64096, 35088, 64097, 35222, 64098, 35585, 64099, 35641, 64100, 36051, 64101, 36104, + 64102, 36790, 64103, 36920, 64104, 38627, 64105, 38911, 64106, 38971, 64107, 24693, + 64108, 148206, 64109, 33304, 64110, -64111, 64112, 20006, 64113, 20917, 64114, 20840, + 64115, 20352, 64116, 20805, 64117, 20864, 64118, 21191, 64119, 21242, 64120, 21917, + 64121, 21845, 64122, 21913, 64123, 21986, 64124, 22618, 64125, 22707, 64126, 22852, + 64127, 22868, 64128, 23138, 64129, 23336, 64130, 24274, 64131, 24281, 64132, 24425, + 64133, 24493, 64134, 24792, 64135, 24910, 64136, 24840, 64137, 24974, 64138, 24928, + 64139, 25074, 64140, 25140, 64141, 25540, 64142, 25628, 64143, 25682, 64144, 25942, + 64145, 26228, 64146, 26391, 64147, 26395, 64148, 26454, 64149, 27513, 64150, 27578, + 64151, 27969, 64152, 28379, 64153, 28363, 64154, 28450, 64155, 28702, 64156, 29038, + 64157, 30631, 64158, 29237, 64159, 29359, 64160, 29482, 64161, 29809, 64162, 29958, + 64163, 30011, 64164, 30237, 64165, 30239, 64166, 30410, 64167, 30427, 64168, 30452, + 64169, 30538, 64170, 30528, 64171, 30924, 64172, 31409, 64173, 31680, 64174, 31867, + 64175, 32091, 64176, 32244, 64177, 32574, 64178, 32773, 64179, 33618, 64180, 33775, + 64181, 34681, 64182, 35137, 64183, 35206, 64184, 35222, 64185, 35519, 64186, 35576, + 64187, 35531, 64188, 35585, 64189, 35582, 64190, 35565, 64191, 35641, 64192, 35722, + 64193, 36104, 64194, 36664, 64195, 36978, 64196, 37273, 64197, 37494, 64198, 38524, + 64199, 38627, 64200, 38742, 64201, 38875, 64202, 38911, 64203, 38923, 64204, 38971, + 64205, 39698, 64206, 40860, 64207, 141386, 64208, 141380, 64209, 144341, 64210, 15261, + 64211, 16408, 64212, 16441, 64213, 152137, 64214, 154832, 64215, 163539, 64216, 40771, + 64217, 40846, 64218, -64219, 64285, 1497, 64286, 0, 64287, 1522, 64288, 1506, + 64289, 1488, 64290, -1492, 64292, -1500, 64295, 1512, 64296, 1514, 64297, 43, + 64298, 1513, 64302, 1488, 64305, -1490, 64311, 64311, 64312, -1497, 64317, 64317, + 64318, 1502, 64319, 64319, 64320, -1505, 64322, 64322, 64323, -1508, 64325, 64325, + 64326, -1511, 64331, 1493, 64332, 1489, 64333, 1499, 64334, 1508, 64335, 64335, + 64336, 1649, 64338, 1659, 64342, 1662, 64346, 1664, 64350, 1658, 64354, 1663, + 64358, 1657, 64362, 1700, 64366, 1702, 64370, 1668, 64374, 1667, 64378, 1670, + 64382, 1671, 64386, 1677, 64388, 1676, 64390, 1678, 64392, 1672, 64394, 1688, + 64396, 1681, 64398, 1705, 64402, 1711, 64406, 1715, 64410, 1713, 64414, 1722, + 64416, 1723, 64420, 1749, 64422, 1729, 64426, 1726, 64430, 1746, 64434, -64435, + 64467, 1709, 64471, 1735, 64473, 1734, 64475, 1736, 64477, 1655, 64478, 1739, + 64480, 1733, 64482, 1737, 64484, 1744, 64488, 1609, 64490, -64491, 64508, 1740, + 64512, -64513, 64603, -1585, 64605, 1609, 64606, 32, 64612, -64613, 64656, 1609, + 64657, -64658, 64729, 1607, 64730, -64731, 64754, 1600, 64757, -64758, 64828, 1575, + 64830, -64831, 65024, 0, 65040, 44, 65041, -12290, 65043, -59, 65045, 33, + 65046, 63, 65047, -12311, 65049, 8230, 65050, -65051, 65056, 0, 65072, 8229, + 65073, 8212, 65074, 8211, 65075, 95, 65077, -41, 65079, 123, 65080, 125, + 65081, -12309, 65083, -12305, 65085, -12299, 65087, -12297, 65089, -12301, 65093, -65094, + 65095, 91, 65096, 93, 65097, 32, 65101, 95, 65104, 44, 65105, 12289, + 65106, 46, 65107, 65107, 65108, 59, 65109, 58, 65110, 63, 65111, 33, + 65112, 8212, 65113, -41, 65115, 123, 65116, 125, 65117, -12309, 65119, 35, + 65120, 38, 65121, -43, 65123, 45, 65124, 60, 65125, 62, 65126, 61, + 65127, 65127, 65128, 92, 65129, -37, 65131, 64, 65132, -65133, 65136, 32, + 65137, 1600, 65138, 32, 65139, 65139, 65140, 32, 65141, 65141, 65142, 32, + 65143, 1600, 65144, 32, 65145, 1600, 65146, 32, 65147, 1600, 65148, 32, + 65149, 1600, 65150, 32, 65151, 1600, 65152, 1569, 65153, 1575, 65157, 1608, + 65159, 1575, 65161, 1610, 65165, 1575, 65167, 1576, 65171, 1577, 65173, 1578, + 65177, 1579, 65181, 1580, 65185, 1581, 65189, 1582, 65193, 1583, 65195, 1584, + 65197, 1585, 65199, 1586, 65201, 1587, 65205, 1588, 65209, 1589, 65213, 1590, + 65217, 1591, 65221, 1592, 65225, 1593, 65229, 1594, 65233, 1601, 65237, 1602, + 65241, 1603, 65245, 1604, 65249, 1605, 65253, 1606, 65257, 1607, 65261, 1608, + 65263, 1609, 65265, 1610, 65269, -65270, 65279, 0, 65280, 65280, 65281, -34, + 65375, -10630, 65377, 12290, 65378, -12301, 65380, 12289, 65381, 12539, 65382, 12530, + 65383, 12449, 65384, 12451, 65385, 12453, 65386, 12455, 65387, 12457, 65388, 12515, + 65389, 12517, 65390, 12519, 65391, 12483, 65392, 65392, 65393, 12450, 65394, 12452, + 65395, 12454, 65396, 12456, 65397, -12459, 65399, 12461, 65400, 12463, 65401, 12465, + 65402, 12467, 65403, 12469, 65404, 12471, 65405, 12473, 65406, 12475, 65407, 12477, + 65408, 12479, 65409, 12481, 65410, 12484, 65411, 12486, 65412, 12488, 65413, -12491, + 65419, 12498, 65420, 12501, 65421, 12504, 65422, 12507, 65423, -12511, 65428, 12516, + 65429, 12518, 65430, -12521, 65436, 12527, 65437, 12531, 65438, -65439, 65440, 4448, + 65441, -4353, 65443, 4522, 65444, 4354, 65445, -4525, 65447, -4356, 65450, -4529, + 65456, 4378, 65457, -4359, 65460, 4385, 65461, -4362, 65471, -65472, 65474, -4450, + 65480, -65481, 65482, -4456, 65488, -65489, 65490, -4462, 65496, -65497, 65498, -4468, + 65501, -65502, 65504, -163, 65506, 172, 65507, 32, 65508, 166, 65509, 165, + 65510, 8361, 65511, 65511, 65512, 9474, 65513, -8593, 65517, 9632, 65518, 9675, + 65519, -65520, 65529, 0, 65532, -65533, 66045, 0, 66046, -66047, 66272, 0, + 66273, -66274, 66422, 0, 66427, -66428, 68097, 0, 68100, 68100, 68101, 0, + 68103, -68104, 68108, 0, 68112, -68113, 68152, 0, 68155, -68156, 68159, 0, + 68160, -68161, 68325, 0, 68327, -68328, 68900, 0, 68904, -68905, 69291, 0, + 69293, -69294, 69446, 0, 69457, -69458, 69632, 0, 69635, -69636, 69688, 0, 69703, -69704, 69759, 0, 69763, -69764, 69786, 2097154, 69790, -69791, 69803, 69797, 69804, -69805, 69808, 0, 69819, -69820, 69821, 0, 69822, -69823, 69837, 0, 69838, -69839, 69888, 0, 69891, -69892, 69927, 0, 69941, -69942, 69957, 0, 69959, -69960, 70003, 0, 70004, -70005, 70016, 0, 70019, -70020, 70067, 0, - 70081, -70082, 70089, 0, 70093, -70094, 70188, 0, 70200, -70201, 70206, 0, - 70207, -70208, 70367, 0, 70379, -70380, 70400, 0, 70404, -70405, 70459, 0, - 70461, 70461, 70462, 0, 70469, -70470, 70471, 0, 70473, -70474, 70475, 0, - 70478, -70479, 70487, 0, 70488, -70489, 70498, 0, 70500, -70501, 70502, 0, - 70509, -70510, 70512, 0, 70517, -70518, 70709, 0, 70727, -70728, 70750, 0, - 70751, -70752, 70832, 0, 70852, -70853, 71087, 0, 71094, -71095, 71096, 0, - 71105, -71106, 71132, 0, 71134, -71135, 71216, 0, 71233, -71234, 71339, 0, - 71352, -71353, 71453, 0, 71468, -71469, 71724, 0, 71739, -71740, 72145, 0, + 70081, -70082, 70089, 0, 70093, 70093, 70094, 0, 70096, -70097, 70188, 0, + 70200, -70201, 70206, 0, 70207, -70208, 70367, 0, 70379, -70380, 70400, 0, + 70404, -70405, 70459, 0, 70461, 70461, 70462, 0, 70469, -70470, 70471, 0, + 70473, -70474, 70475, 0, 70478, -70479, 70487, 0, 70488, -70489, 70498, 0, + 70500, -70501, 70502, 0, 70509, -70510, 70512, 0, 70517, -70518, 70709, 0, + 70727, -70728, 70750, 0, 70751, -70752, 70832, 0, 70852, -70853, 71087, 0, + 71094, -71095, 71096, 0, 71105, -71106, 71132, 0, 71134, -71135, 71216, 0, + 71233, -71234, 71339, 0, 71352, -71353, 71453, 0, 71468, -71469, 71724, 0, + 71739, -71740, 71984, 0, 71990, 71990, 71991, 0, 71993, -71994, 71995, 0, + 71999, 71999, 72000, 0, 72001, 72001, 72002, 0, 72004, -72005, 72145, 0, 72152, -72153, 72154, 0, 72161, -72162, 72164, 0, 72165, -72166, 72193, 0, 72203, -72204, 72243, 0, 72250, 72250, 72251, 0, 72255, -72256, 72263, 0, 72264, -72265, 72273, 0, 72284, -72285, 72330, 0, 72346, -72347, 72751, 0, @@ -993,72 +1002,73 @@ static const int32 without_diacritics_ranges[] = { 73103, 73103, 73104, 0, 73106, 73106, 73107, 0, 73112, -73113, 73459, 0, 73463, -73464, 78896, 0, 78905, -78906, 92912, 0, 92917, -92918, 92976, 0, 92983, -92984, 94031, 0, 94032, 94032, 94033, 0, 94088, -94089, 94095, 0, - 94099, -94100, 113821, 0, 113823, 113823, 113824, 0, 113828, -113829, 119134, -119128, - 119136, 119128, 119141, 0, 119146, -119147, 119149, 0, 119171, -119172, 119173, 0, - 119180, -119181, 119210, 0, 119214, -119215, 119227, -119226, 119229, -119226, 119231, -119226, - 119233, -119234, 119362, 0, 119365, -119366, 119808, -66, 119834, -98, 119860, -66, - 119886, -98, 119893, 119893, 119894, -106, 119912, -66, 119938, -98, 119964, 65, - 119965, 119965, 119966, -68, 119968, -119969, 119970, 71, 119971, -119972, 119973, -75, - 119975, -119976, 119977, -79, 119981, 119981, 119982, -84, 119990, -98, 119994, 119994, - 119995, 102, 119996, 119996, 119997, -105, 120004, 120004, 120005, -113, 120016, -66, - 120042, -98, 120068, -66, 120070, 120070, 120071, -69, 120075, -120076, 120077, -75, - 120085, 120085, 120086, -84, 120093, 120093, 120094, -98, 120120, -66, 120122, 120122, - 120123, -69, 120127, 120127, 120128, -74, 120133, 120133, 120134, 79, 120135, -120136, - 120138, -84, 120145, 120145, 120146, -98, 120172, -66, 120198, -98, 120224, -66, - 120250, -98, 120276, -66, 120302, -98, 120328, -66, 120354, -98, 120380, -66, - 120406, -98, 120432, -66, 120458, -98, 120484, 305, 120485, 567, 120486, -120487, - 120488, -914, 120505, 920, 120506, -932, 120513, 8711, 120514, -946, 120539, 8706, - 120540, 949, 120541, 952, 120542, 954, 120543, 966, 120544, 961, 120545, 960, - 120546, -914, 120563, 920, 120564, -932, 120571, 8711, 120572, -946, 120597, 8706, - 120598, 949, 120599, 952, 120600, 954, 120601, 966, 120602, 961, 120603, 960, - 120604, -914, 120621, 920, 120622, -932, 120629, 8711, 120630, -946, 120655, 8706, - 120656, 949, 120657, 952, 120658, 954, 120659, 966, 120660, 961, 120661, 960, - 120662, -914, 120679, 920, 120680, -932, 120687, 8711, 120688, -946, 120713, 8706, - 120714, 949, 120715, 952, 120716, 954, 120717, 966, 120718, 961, 120719, 960, - 120720, -914, 120737, 920, 120738, -932, 120745, 8711, 120746, -946, 120771, 8706, - 120772, 949, 120773, 952, 120774, 954, 120775, 966, 120776, 961, 120777, 960, - 120778, -989, 120780, -120781, 120782, -49, 120792, -49, 120802, -49, 120812, -49, - 120822, -49, 120832, -120833, 121344, 0, 121399, -121400, 121403, 0, 121453, -121454, - 121461, 0, 121462, -121463, 121476, 0, 121477, -121478, 121499, 0, 121504, 121504, - 121505, 0, 121520, -121521, 122880, 0, 122887, 122887, 122888, 0, 122905, -122906, - 122907, 0, 122914, 122914, 122915, 0, 122917, 122917, 122918, 0, 122923, -122924, - 123184, 0, 123191, -123192, 123628, 0, 123632, -123633, 125136, 0, 125143, -125144, - 125252, 0, 125259, -125260, 126464, -1576, 126466, 1580, 126467, 1583, 126468, 126468, - 126469, 1608, 126470, 1586, 126471, 1581, 126472, 1591, 126473, 1610, 126474, -1604, - 126478, 1587, 126479, 1593, 126480, 1601, 126481, 1589, 126482, 1602, 126483, 1585, - 126484, 1588, 126485, -1579, 126487, 1582, 126488, 1584, 126489, 1590, 126490, 1592, - 126491, 1594, 126492, 1646, 126493, 1722, 126494, 1697, 126495, 1647, 126496, 126496, - 126497, 1576, 126498, 1580, 126499, 126499, 126500, 1607, 126501, -126502, 126503, 1581, - 126504, 126504, 126505, 1610, 126506, -1604, 126510, 1587, 126511, 1593, 126512, 1601, - 126513, 1589, 126514, 1602, 126515, 126515, 126516, 1588, 126517, -1579, 126519, 1582, - 126520, 126520, 126521, 1590, 126522, 126522, 126523, 1594, 126524, -126525, 126530, 1580, - 126531, -126532, 126535, 1581, 126536, 126536, 126537, 1610, 126538, 126538, 126539, 1604, - 126540, 126540, 126541, 1606, 126542, 1587, 126543, 1593, 126544, 126544, 126545, 1589, - 126546, 1602, 126547, 126547, 126548, 1588, 126549, -126550, 126551, 1582, 126552, 126552, - 126553, 1590, 126554, 126554, 126555, 1594, 126556, 126556, 126557, 1722, 126558, 126558, - 126559, 1647, 126560, 126560, 126561, 1576, 126562, 1580, 126563, 126563, 126564, 1607, - 126565, -126566, 126567, 1581, 126568, 1591, 126569, 1610, 126570, 1603, 126571, 126571, - 126572, -1606, 126574, 1587, 126575, 1593, 126576, 1601, 126577, 1589, 126578, 1602, - 126579, 126579, 126580, 1588, 126581, -1579, 126583, 1582, 126584, 126584, 126585, 1590, - 126586, 1592, 126587, 1594, 126588, 1646, 126589, 126589, 126590, 1697, 126591, 126591, - 126592, -1576, 126594, 1580, 126595, 1583, 126596, -1608, 126598, 1586, 126599, 1581, - 126600, 1591, 126601, 1610, 126602, 126602, 126603, -1605, 126606, 1587, 126607, 1593, - 126608, 1601, 126609, 1589, 126610, 1602, 126611, 1585, 126612, 1588, 126613, -1579, - 126615, 1582, 126616, 1584, 126617, 1590, 126618, 1592, 126619, 1594, 126620, -126621, - 126625, 1576, 126626, 1580, 126627, 1583, 126628, 126628, 126629, 1608, 126630, 1586, - 126631, 1581, 126632, 1591, 126633, 1610, 126634, 126634, 126635, -1605, 126638, 1587, - 126639, 1593, 126640, 1601, 126641, 1589, 126642, 1602, 126643, 1585, 126644, 1588, - 126645, -1579, 126647, 1582, 126648, 1584, 126649, 1590, 126650, 1592, 126651, 1594, - 126652, -126653, 127232, 48, 127234, -50, 127243, -127244, 127275, 67, 127276, 82, - 127277, -127278, 127280, -66, 127306, -127307, 127490, 12469, 127491, -127492, 127504, 25163, - 127505, 23383, 127506, 21452, 127507, 12486, 127508, 20108, 127509, 22810, 127510, 35299, - 127511, 22825, 127512, 20132, 127513, 26144, 127514, 28961, 127515, 26009, 127516, 21069, - 127517, 24460, 127518, 20877, 127519, 26032, 127520, 21021, 127521, 32066, 127522, 29983, - 127523, 36009, 127524, 22768, 127525, 21561, 127526, 28436, 127527, 25237, 127528, 25429, - 127529, 19968, 127530, 19977, 127531, 36938, 127532, 24038, 127533, 20013, 127534, 21491, - 127535, 25351, 127536, 36208, 127537, 25171, 127538, 31105, 127539, 31354, 127540, 21512, - 127541, 28288, 127542, 26377, 127543, 26376, 127544, 30003, 127545, 21106, 127546, 21942, - 127547, 37197, 127548, -127549, 127568, 24471, 127569, 21487, 127570, -127571, 194560, 20029, + 94099, -94100, 94180, 0, 94181, -94182, 94192, 0, 94194, -94195, 113821, 0, + 113823, 113823, 113824, 0, 113828, -113829, 119134, -119128, 119136, 119128, 119141, 0, + 119146, -119147, 119149, 0, 119171, -119172, 119173, 0, 119180, -119181, 119210, 0, + 119214, -119215, 119227, -119226, 119229, -119226, 119231, -119226, 119233, -119234, 119362, 0, + 119365, -119366, 119808, -66, 119834, -98, 119860, -66, 119886, -98, 119893, 119893, + 119894, -106, 119912, -66, 119938, -98, 119964, 65, 119965, 119965, 119966, -68, + 119968, -119969, 119970, 71, 119971, -119972, 119973, -75, 119975, -119976, 119977, -79, + 119981, 119981, 119982, -84, 119990, -98, 119994, 119994, 119995, 102, 119996, 119996, + 119997, -105, 120004, 120004, 120005, -113, 120016, -66, 120042, -98, 120068, -66, + 120070, 120070, 120071, -69, 120075, -120076, 120077, -75, 120085, 120085, 120086, -84, + 120093, 120093, 120094, -98, 120120, -66, 120122, 120122, 120123, -69, 120127, 120127, + 120128, -74, 120133, 120133, 120134, 79, 120135, -120136, 120138, -84, 120145, 120145, + 120146, -98, 120172, -66, 120198, -98, 120224, -66, 120250, -98, 120276, -66, + 120302, -98, 120328, -66, 120354, -98, 120380, -66, 120406, -98, 120432, -66, + 120458, -98, 120484, 305, 120485, 567, 120486, -120487, 120488, -914, 120505, 920, + 120506, -932, 120513, 8711, 120514, -946, 120539, 8706, 120540, 949, 120541, 952, + 120542, 954, 120543, 966, 120544, 961, 120545, 960, 120546, -914, 120563, 920, + 120564, -932, 120571, 8711, 120572, -946, 120597, 8706, 120598, 949, 120599, 952, + 120600, 954, 120601, 966, 120602, 961, 120603, 960, 120604, -914, 120621, 920, + 120622, -932, 120629, 8711, 120630, -946, 120655, 8706, 120656, 949, 120657, 952, + 120658, 954, 120659, 966, 120660, 961, 120661, 960, 120662, -914, 120679, 920, + 120680, -932, 120687, 8711, 120688, -946, 120713, 8706, 120714, 949, 120715, 952, + 120716, 954, 120717, 966, 120718, 961, 120719, 960, 120720, -914, 120737, 920, + 120738, -932, 120745, 8711, 120746, -946, 120771, 8706, 120772, 949, 120773, 952, + 120774, 954, 120775, 966, 120776, 961, 120777, 960, 120778, -989, 120780, -120781, + 120782, -49, 120792, -49, 120802, -49, 120812, -49, 120822, -49, 120832, -120833, + 121344, 0, 121399, -121400, 121403, 0, 121453, -121454, 121461, 0, 121462, -121463, + 121476, 0, 121477, -121478, 121499, 0, 121504, 121504, 121505, 0, 121520, -121521, + 122880, 0, 122887, 122887, 122888, 0, 122905, -122906, 122907, 0, 122914, 122914, + 122915, 0, 122917, 122917, 122918, 0, 122923, -122924, 123184, 0, 123191, -123192, + 123628, 0, 123632, -123633, 125136, 0, 125143, -125144, 125252, 0, 125259, -125260, + 126464, -1576, 126466, 1580, 126467, 1583, 126468, 126468, 126469, 1608, 126470, 1586, + 126471, 1581, 126472, 1591, 126473, 1610, 126474, -1604, 126478, 1587, 126479, 1593, + 126480, 1601, 126481, 1589, 126482, 1602, 126483, 1585, 126484, 1588, 126485, -1579, + 126487, 1582, 126488, 1584, 126489, 1590, 126490, 1592, 126491, 1594, 126492, 1646, + 126493, 1722, 126494, 1697, 126495, 1647, 126496, 126496, 126497, 1576, 126498, 1580, + 126499, 126499, 126500, 1607, 126501, -126502, 126503, 1581, 126504, 126504, 126505, 1610, + 126506, -1604, 126510, 1587, 126511, 1593, 126512, 1601, 126513, 1589, 126514, 1602, + 126515, 126515, 126516, 1588, 126517, -1579, 126519, 1582, 126520, 126520, 126521, 1590, + 126522, 126522, 126523, 1594, 126524, -126525, 126530, 1580, 126531, -126532, 126535, 1581, + 126536, 126536, 126537, 1610, 126538, 126538, 126539, 1604, 126540, 126540, 126541, 1606, + 126542, 1587, 126543, 1593, 126544, 126544, 126545, 1589, 126546, 1602, 126547, 126547, + 126548, 1588, 126549, -126550, 126551, 1582, 126552, 126552, 126553, 1590, 126554, 126554, + 126555, 1594, 126556, 126556, 126557, 1722, 126558, 126558, 126559, 1647, 126560, 126560, + 126561, 1576, 126562, 1580, 126563, 126563, 126564, 1607, 126565, -126566, 126567, 1581, + 126568, 1591, 126569, 1610, 126570, 1603, 126571, 126571, 126572, -1606, 126574, 1587, + 126575, 1593, 126576, 1601, 126577, 1589, 126578, 1602, 126579, 126579, 126580, 1588, + 126581, -1579, 126583, 1582, 126584, 126584, 126585, 1590, 126586, 1592, 126587, 1594, + 126588, 1646, 126589, 126589, 126590, 1697, 126591, 126591, 126592, -1576, 126594, 1580, + 126595, 1583, 126596, -1608, 126598, 1586, 126599, 1581, 126600, 1591, 126601, 1610, + 126602, 126602, 126603, -1605, 126606, 1587, 126607, 1593, 126608, 1601, 126609, 1589, + 126610, 1602, 126611, 1585, 126612, 1588, 126613, -1579, 126615, 1582, 126616, 1584, + 126617, 1590, 126618, 1592, 126619, 1594, 126620, -126621, 126625, 1576, 126626, 1580, + 126627, 1583, 126628, 126628, 126629, 1608, 126630, 1586, 126631, 1581, 126632, 1591, + 126633, 1610, 126634, 126634, 126635, -1605, 126638, 1587, 126639, 1593, 126640, 1601, + 126641, 1589, 126642, 1602, 126643, 1585, 126644, 1588, 126645, -1579, 126647, 1582, + 126648, 1584, 126649, 1590, 126650, 1592, 126651, 1594, 126652, -126653, 127232, 48, + 127234, -50, 127243, -127244, 127275, 67, 127276, 82, 127277, -127278, 127280, -66, + 127306, -127307, 127490, 12469, 127491, -127492, 127504, 25163, 127505, 23383, 127506, 21452, + 127507, 12486, 127508, 20108, 127509, 22810, 127510, 35299, 127511, 22825, 127512, 20132, + 127513, 26144, 127514, 28961, 127515, 26009, 127516, 21069, 127517, 24460, 127518, 20877, + 127519, 26032, 127520, 21021, 127521, 32066, 127522, 29983, 127523, 36009, 127524, 22768, + 127525, 21561, 127526, 28436, 127527, 25237, 127528, 25429, 127529, 19968, 127530, 19977, + 127531, 36938, 127532, 24038, 127533, 20013, 127534, 21491, 127535, 25351, 127536, 36208, + 127537, 25171, 127538, 31105, 127539, 31354, 127540, 21512, 127541, 28288, 127542, 26377, + 127543, 26376, 127544, 30003, 127545, 21106, 127546, 21942, 127547, 37197, 127548, -127549, + 127568, 24471, 127569, 21487, 127570, -127571, 130032, -49, 130042, -130043, 194560, 20029, 194561, 20024, 194562, 20033, 194563, 131362, 194564, 20320, 194565, 20398, 194566, 20411, 194567, 20482, 194568, 20602, 194569, 20633, 194570, 20711, 194571, 20687, 194572, 13470, 194573, 132666, 194574, 20813, 194575, 20820, 194576, 20836, 194577, 20855, 194578, 132380, diff --git a/tdutils/td/utils/utf8.h b/tdutils/td/utils/utf8.h index b2da2d83..093a3fdb 100644 --- a/tdutils/td/utils/utf8.h +++ b/tdutils/td/utils/utf8.h @@ -89,6 +89,9 @@ T utf8_utf16_truncate(T str, size_t length) { template T utf8_substr(T str, size_t offset) { + if (offset == 0) { + return str; + } auto offset_pos = utf8_truncate(str, offset).size(); return str.substr(offset_pos); } @@ -100,6 +103,9 @@ T utf8_substr(T str, size_t offset, size_t length) { template T utf8_utf16_substr(T str, size_t offset) { + if (offset == 0) { + return str; + } auto offset_pos = utf8_utf16_truncate(str, offset).size(); return str.substr(offset_pos); } diff --git a/tdutils/test/gzip.cpp b/tdutils/test/gzip.cpp index 10724bc7..daade895 100644 --- a/tdutils/test/gzip.cpp +++ b/tdutils/test/gzip.cpp @@ -6,33 +6,40 @@ // #include "td/utils/buffer.h" #include "td/utils/ByteFlow.h" +#include "td/utils/common.h" #include "td/utils/Gzip.h" #include "td/utils/GzipByteFlow.h" #include "td/utils/logging.h" #include "td/utils/Status.h" #include "td/utils/tests.h" +#include "td/utils/Time.h" static void encode_decode(td::string s) { auto r = td::gzencode(s, 2); ASSERT_TRUE(!r.empty()); - if (r.empty()) { - return; - } - auto new_s = td::gzdecode(r.as_slice()); - ASSERT_TRUE(!new_s.empty()); - if (new_s.empty()) { - return; - } - ASSERT_EQ(s, new_s.as_slice().str()); + ASSERT_EQ(s, td::gzdecode(r.as_slice())); } TEST(Gzip, gzencode_gzdecode) { - auto str = td::rand_string(0, 127, 1000); - encode_decode(str); - str = td::rand_string('a', 'z', 1000000); - encode_decode(str); - str = td::string(1000000, 'a'); - encode_decode(str); + encode_decode(td::rand_string(0, 255, 1000)); + encode_decode(td::rand_string('a', 'z', 1000000)); + encode_decode(td::string(1000000, 'a')); +} + +static void test_gzencode(td::string s) { + auto begin_time = td::Time::now(); + auto r = td::gzencode(s, td::max(2, static_cast(100 / s.size()))); + ASSERT_TRUE(!r.empty()); + LOG(INFO) << "Encoded string of size " << s.size() << " in " << (td::Time::now() - begin_time) + << " with compression ratio " << static_cast(r.size()) / static_cast(s.size()); +} + +TEST(Gzip, gzencode) { + for (size_t len = 1; len <= 10000000; len *= 10) { + test_gzencode(td::rand_string('a', 'a', len)); + test_gzencode(td::rand_string('a', 'z', len)); + test_gzencode(td::rand_string(0, 255, len)); + } } TEST(Gzip, flow) { @@ -63,7 +70,8 @@ TEST(Gzip, flow) { } TEST(Gzip, flow_error) { auto str = td::rand_string('a', 'z', 1000000); - auto zip = td::gzencode(str).as_slice().str(); + auto zip = td::gzencode(str, 0.9).as_slice().str(); + ASSERT_TRUE(!zip.empty()); zip.resize(zip.size() - 1); auto parts = td::rand_split(zip); diff --git a/tdutils/test/misc.cpp b/tdutils/test/misc.cpp index c288b15a..ec61a518 100644 --- a/tdutils/test/misc.cpp +++ b/tdutils/test/misc.cpp @@ -829,6 +829,14 @@ TEST(Misc, Bits) { ASSERT_EQ(0x12345678u, td::bswap32(0x78563412u)); ASSERT_EQ(0x12345678abcdef67ull, td::bswap64(0x67efcdab78563412ull)); + uint8 buf[8] = {1, 90, 2, 18, 129, 255, 0, 2}; + uint64 num2 = bswap64(as(buf)); + uint64 num = (static_cast(buf[0]) << 56) | (static_cast(buf[1]) << 48) | + (static_cast(buf[2]) << 40) | (static_cast(buf[3]) << 32) | + (static_cast(buf[4]) << 24) | (static_cast(buf[5]) << 16) | + (static_cast(buf[6]) << 8) | (static_cast(buf[7])); + ASSERT_EQ(num, num2); + ASSERT_EQ(0, count_bits32(0)); ASSERT_EQ(0, count_bits64(0)); ASSERT_EQ(4, count_bits32((1u << 31) | 7)); diff --git a/tdutils/test/port.cpp b/tdutils/test/port.cpp index 9ff5841d..e1903f51 100644 --- a/tdutils/test/port.cpp +++ b/tdutils/test/port.cpp @@ -105,7 +105,7 @@ TEST(Port, SparseFiles) { fd.pwrite("a", offset).ensure(); ASSERT_EQ(offset + 1, fd.get_size().move_as_ok()); auto real_size = fd.get_real_size().move_as_ok(); - if (real_size == offset + 1) { + if (real_size >= offset + 1) { LOG(ERROR) << "File system doesn't support sparse files, rewind during streaming can be slow"; } unlink(path).ensure(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff9e46f3..eaa93a43 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,7 +27,7 @@ set(TESTS_MAIN add_library(all_tests STATIC ${TD_TEST_SOURCE}) target_include_directories(all_tests PUBLIC $) -target_link_libraries(all_tests PRIVATE tdactor tddb tdcore tdnet tdutils tdclient) +target_link_libraries(all_tests PRIVATE tdcore tdclient) if (NOT CMAKE_CROSSCOMPILING OR EMSCRIPTEN) #Tests @@ -37,11 +37,11 @@ if (NOT CMAKE_CROSSCOMPILING OR EMSCRIPTEN) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fno-sanitize=vptr") endif() target_include_directories(run_all_tests PUBLIC $) - target_link_libraries(run_all_tests PRIVATE tdactor tddb tdcore tdnet tdutils tdclient) + target_link_libraries(run_all_tests PRIVATE tdcore tdclient) if (CLANG) # add_executable(fuzz_url fuzz_url.cpp) -# target_link_libraries(fuzz_url PRIVATE tdclient) +# target_link_libraries(fuzz_url PRIVATE tdcore) # target_compile_options(fuzz_url PRIVATE "-fsanitize-coverage=trace-pc-guard") endif() diff --git a/test/db.cpp b/test/db.cpp index f9f33726..19cc9441 100644 --- a/test/db.cpp +++ b/test/db.cpp @@ -377,7 +377,7 @@ TEST(DB, thread_key_value) { std::vector threads(threads_n); std::vector> res(threads_n); for (int i = 0; i < threads_n; i++) { - threads[i] = thread([&ts_kv, &queries, &res, i]() { + threads[i] = thread([&ts_kv, &queries, &res, i] { for (auto q : queries[i]) { ts_kv.do_query(q); res[i].push_back(q); diff --git a/test/http.cpp b/test/http.cpp index e9acf422..834a75cb 100644 --- a/test/http.cpp +++ b/test/http.cpp @@ -354,7 +354,7 @@ TEST(Http, chunked_flow_error) { TEST(Http, gzip_chunked_flow) { auto str = rand_string('a', 'z', 1000000); - auto parts = rand_split(make_chunked(gzencode(str).as_slice().str())); + auto parts = rand_split(make_chunked(gzencode(str, 2.0).as_slice().str())); ChainBufferWriter input_writer; auto input = input_writer.extract_reader(); diff --git a/test/message_entities.cpp b/test/message_entities.cpp index b43e4b5f..ae4b7a31 100644 --- a/test/message_entities.cpp +++ b/test/message_entities.cpp @@ -10,6 +10,7 @@ #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" +#include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/tests.h" #include "td/utils/utf8.h" @@ -164,6 +165,46 @@ TEST(MessageEntities, cashtag) { check_cashtag(u8"\u2122$ABC\u2122", {"$ABC"}); } +static void check_bank_card_number(const td::string &str, const td::vector &expected) { + auto result_slice = td::find_bank_card_numbers(str); + td::vector result; + for (auto &it : result_slice) { + result.push_back(it.str()); + } + if (result != expected) { + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result)) + << td::tag("expected", td::format::as_array(expected)); + } +} + +TEST(MessageEntities, bank_card_number) { + check_bank_card_number("", {}); + check_bank_card_number("123456789015", {}); + check_bank_card_number("1234567890120", {}); + check_bank_card_number("1234567890121", {}); + check_bank_card_number("1234567890122", {}); + check_bank_card_number("1234567890123", {}); + check_bank_card_number("1234567890124", {}); + check_bank_card_number("1234567890125", {}); + check_bank_card_number("1234567890126", {}); + check_bank_card_number("1234567890127", {}); + check_bank_card_number("1234567890128", {"1234567890128"}); + check_bank_card_number("1234567890129", {}); + check_bank_card_number("12345678901500", {"12345678901500"}); + check_bank_card_number("123456789012800", {"123456789012800"}); + check_bank_card_number("1234567890151800", {"1234567890151800"}); + check_bank_card_number("12345678901280000", {"12345678901280000"}); + check_bank_card_number("123456789015009100", {"123456789015009100"}); + check_bank_card_number("1234567890128000000", {"1234567890128000000"}); + check_bank_card_number("12345678901500910000", {}); + check_bank_card_number(" - - - -1 - -- 2 - - -- 34 - - - 56- - 7890150000 - - - -", {}); + check_bank_card_number(" - - - -1 - -- 234 - - 56- - 7890150000 - - - -", {"1 - -- 234 - - 56- - 7890150000"}); + check_bank_card_number("4916-3385-0608-2832; 5280 9342 8317 1080 ;345936346788903", + {"4916-3385-0608-2832", "5280 9342 8317 1080", "345936346788903"}); + check_bank_card_number("4556728228023269,4916141675244747020,49161416752447470,4556728228023269", + {"4556728228023269", "4916141675244747020", "4556728228023269"}); +} + static void check_is_email_address(const td::string &str, bool expected) { bool result = td::is_email_address(str); LOG_IF(FATAL, result != expected) << "Expected " << expected << " as result of is_email_address(" << str << ")"; @@ -535,8 +576,9 @@ TEST(MessageEntities, url) { static void check_fix_formatted_text(td::string str, td::vector entities, const td::string &expected_str, - const td::vector &expected_entities, bool allow_empty, - bool skip_new_entities, bool skip_bot_commands, bool for_draft) { + const td::vector &expected_entities, bool allow_empty = true, + bool skip_new_entities = false, bool skip_bot_commands = false, + bool for_draft = true) { ASSERT_TRUE( td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_ok()); ASSERT_STREQ(expected_str, str); @@ -677,13 +719,13 @@ TEST(MessageEntities, fix_formatted_text) { td::vector entities; entities.emplace_back(td::MessageEntity::Type::Bold, offset, length); td::vector fixed_entities; - if (length > 0 && offset >= 0 && static_cast(length + offset) > str.size()) { + if (length < 0 || offset < 0 || (length > 0 && static_cast(length + offset) > str.size())) { check_fix_formatted_text(str, entities, true, false, false, false); check_fix_formatted_text(str, entities, false, false, false, true); continue; } - if (length > 0 && offset >= 0 && (length >= 2 || offset != 3)) { + if (length > 0 && (length >= 2 || offset != 3)) { fixed_entities.emplace_back(td::MessageEntity::Type::Bold, offset, length); } check_fix_formatted_text(str, entities, str, fixed_entities, true, false, false, false); @@ -740,6 +782,238 @@ TEST(MessageEntities, fix_formatted_text) { check_fix_formatted_text(text, {{type, 0, 1, "http://telegram.org/"}}, "", {}, true, false, false, true); } } + + check_fix_formatted_text("a\rbc\r", + {{td::MessageEntity::Type::Italic, 0, 1}, + {td::MessageEntity::Type::Bold, 0, 2}, + {td::MessageEntity::Type::Italic, 3, 2}, + {td::MessageEntity::Type::Bold, 3, 1}}, + "abc", + {{td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Italic, 0, 1}, + {td::MessageEntity::Type::Bold, 2, 1}, + {td::MessageEntity::Type::Italic, 2, 1}}); + check_fix_formatted_text("a ", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Bold, 0, 1}}, "a", + {{td::MessageEntity::Type::Bold, 0, 1}, {td::MessageEntity::Type::Italic, 0, 1}}, false, + false, false, false); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 1, 1}, {td::MessageEntity::Type::Italic, 0, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 2}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 1, 1}, {td::MessageEntity::Type::Italic, 1, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 1, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Italic, 1, 2}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 3}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Italic, 2, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 3}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Italic, 2, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Italic, 2, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Bold, 1, 2}}, + "abc", + {{td::MessageEntity::Type::Italic, 0, 1}, + {td::MessageEntity::Type::Bold, 1, 2}, + {td::MessageEntity::Type::Italic, 1, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Bold, 2, 1}}, + "abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Bold, 2, 1}}); + + // _a*b*_ + check_fix_formatted_text( + "ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 1, 1}}, "ab", + {{td::MessageEntity::Type::Underline, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}}); + check_fix_formatted_text("ab", + {{td::MessageEntity::Type::Underline, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}}, + "ab", + {{td::MessageEntity::Type::Underline, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}}); + check_fix_formatted_text( + "ab", {{td::MessageEntity::Type::Strikethrough, 0, 2}, {td::MessageEntity::Type::Underline, 1, 1}}, "ab", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}}); + check_fix_formatted_text("ab", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}, + {td::MessageEntity::Type::Underline, 1, 1}}, + "ab", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 1, 1}}); + + // _*a*b_ + check_fix_formatted_text( + "ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}}, "ab", + {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}}); + check_fix_formatted_text( + "ab", + {{td::MessageEntity::Type::Underline, 0, 1}, + {td::MessageEntity::Type::Underline, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 0, 1}}, + "ab", {{td::MessageEntity::Type::Underline, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 1}}); + + // _*a*_\r_*b*_ + check_fix_formatted_text("a\rb", + {{td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Bold, 2, 1}, + {td::MessageEntity::Type::Strikethrough, 2, 1}}, + "ab", + {{td::MessageEntity::Type::Bold, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 2}}); + check_fix_formatted_text("a\nb", + {{td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Bold, 2, 1}, + {td::MessageEntity::Type::Strikethrough, 2, 1}}, + "a\nb", + {{td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Bold, 2, 1}, + {td::MessageEntity::Type::Strikethrough, 2, 1}}); + + // _`a`_ + check_fix_formatted_text("a", {{td::MessageEntity::Type::Pre, 0, 1}, {td::MessageEntity::Type::Strikethrough, 0, 1}}, + "a", {{td::MessageEntity::Type::Pre, 0, 1}}); + check_fix_formatted_text("a", {{td::MessageEntity::Type::Strikethrough, 0, 1}, {td::MessageEntity::Type::Pre, 0, 1}}, + "a", {{td::MessageEntity::Type::Pre, 0, 1}}); + check_fix_formatted_text("abc", + {{td::MessageEntity::Type::Pre, 0, 3}, {td::MessageEntity::Type::Strikethrough, 1, 1}}, + "abc", {{td::MessageEntity::Type::Pre, 0, 3}}); + check_fix_formatted_text( + "abc", {{td::MessageEntity::Type::Pre, 1, 1}, {td::MessageEntity::Type::Strikethrough, 0, 3}}, "abc", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, + {td::MessageEntity::Type::Pre, 1, 1}, + {td::MessageEntity::Type::Strikethrough, 2, 1}}); + check_fix_formatted_text( + "abc", {{td::MessageEntity::Type::Pre, 1, 1}, {td::MessageEntity::Type::Strikethrough, 1, 2}}, "abc", + {{td::MessageEntity::Type::Pre, 1, 1}, {td::MessageEntity::Type::Strikethrough, 2, 1}}); + check_fix_formatted_text( + "abc", {{td::MessageEntity::Type::Pre, 1, 1}, {td::MessageEntity::Type::Strikethrough, 0, 2}}, "abc", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, {td::MessageEntity::Type::Pre, 1, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::Pre, 0, 3}, {td::MessageEntity::Type::BlockQuote, 1, 1}}, + "abc", {{td::MessageEntity::Type::BlockQuote, 1, 1}}); + check_fix_formatted_text("abc", {{td::MessageEntity::Type::BlockQuote, 0, 3}, {td::MessageEntity::Type::Pre, 1, 1}}, + "abc", {{td::MessageEntity::Type::BlockQuote, 0, 3}, {td::MessageEntity::Type::Pre, 1, 1}}); + + check_fix_formatted_text("example.com", {}, "example.com", {{td::MessageEntity::Type::Url, 0, 11}}); + check_fix_formatted_text("example.com", {{td::MessageEntity::Type::Pre, 0, 3}}, "example.com", + {{td::MessageEntity::Type::Pre, 0, 3}}); + check_fix_formatted_text("example.com", {{td::MessageEntity::Type::BlockQuote, 0, 3}}, "example.com", + {{td::MessageEntity::Type::BlockQuote, 0, 3}}); + check_fix_formatted_text("example.com", {{td::MessageEntity::Type::BlockQuote, 0, 11}}, "example.com", + {{td::MessageEntity::Type::BlockQuote, 0, 11}, {td::MessageEntity::Type::Url, 0, 11}}); + check_fix_formatted_text("example.com", {{td::MessageEntity::Type::Italic, 0, 11}}, "example.com", + {{td::MessageEntity::Type::Url, 0, 11}, {td::MessageEntity::Type::Italic, 0, 11}}); + check_fix_formatted_text("example.com", {{td::MessageEntity::Type::Italic, 0, 3}}, "example.com", + {{td::MessageEntity::Type::Url, 0, 11}, {td::MessageEntity::Type::Italic, 0, 3}}); + check_fix_formatted_text("example.com a", {{td::MessageEntity::Type::Italic, 0, 13}}, "example.com a", + {{td::MessageEntity::Type::Url, 0, 11}, + {td::MessageEntity::Type::Italic, 0, 11}, + {td::MessageEntity::Type::Italic, 11, 2}}); + check_fix_formatted_text("a example.com", {{td::MessageEntity::Type::Italic, 0, 13}}, "a example.com", + {{td::MessageEntity::Type::Italic, 0, 2}, + {td::MessageEntity::Type::Url, 2, 11}, + {td::MessageEntity::Type::Italic, 2, 11}}); + + for (size_t test_n = 0; test_n < 100000; test_n++) { + bool is_url = td::Random::fast(0, 1) == 1; + td::int32 url_offset = 0; + td::int32 url_end = 0; + if (is_url) { + str = td::string(td::Random::fast(1, 5), 'a') + ":example.com:" + td::string(td::Random::fast(1, 5), 'a'); + url_offset = static_cast(str.find('e')); + url_end = url_offset + 11; + } else { + str = td::string(td::Random::fast(1, 20), 'a'); + } + + auto n = td::Random::fast(1, 20); + td::vector entities; + for (int j = 0; j < n; j++) { + td::int32 type = td::Random::fast(4, 16); + td::int32 offset = td::Random::fast(0, static_cast(str.size()) - 1); + auto max_length = static_cast(str.size() - offset); + if ((test_n & 1) != 0 && max_length > 4) { + max_length = 4; + } + td::int32 length = td::Random::fast(0, max_length); + entities.emplace_back(static_cast(type), offset, length); + } + + auto get_type_mask = [](std::size_t length, const td::vector &entities) { + td::vector result(length); + for (auto &entity : entities) { + for (auto pos = 0; pos < entity.length; pos++) { + result[entity.offset + pos] |= (1 << static_cast(entity.type)); + } + } + return result; + }; + auto old_type_mask = get_type_mask(str.size(), entities); + ASSERT_TRUE(td::fix_formatted_text(str, entities, false, false, true, false).is_ok()); + auto new_type_mask = get_type_mask(str.size(), entities); + auto splittable_mask = (1 << 5) | (1 << 6) | (1 << 14) | (1 << 15); + auto pre_mask = (1 << 7) | (1 << 8) | (1 << 9); + for (std::size_t pos = 0; pos < str.size(); pos++) { + if ((new_type_mask[pos] & pre_mask) != 0) { + ASSERT_EQ(0, new_type_mask[pos] & splittable_mask); + } else { + ASSERT_EQ(old_type_mask[pos] & splittable_mask, new_type_mask[pos] & splittable_mask); + } + } + bool keep_url = is_url; + td::MessageEntity url_entity(td::MessageEntity::Type::Url, url_offset, url_end - url_offset); + for (auto &entity : entities) { + if (entity == url_entity) { + continue; + } + td::int32 offset = entity.offset; + td::int32 end = offset + entity.length; + + if (keep_url && ((1 << static_cast(entity.type)) & splittable_mask) == 0 && + !(end <= url_offset || url_end <= offset)) { + keep_url = (entity.type == td::MessageEntity::Type::BlockQuote && offset <= url_offset && url_end <= end); + } + } + ASSERT_EQ(keep_url, std::count(entities.begin(), entities.end(), url_entity) == 1); + + for (size_t i = 0; i < entities.size(); i++) { + auto type_mask = 1 << static_cast(entities[i].type); + for (size_t j = i + 1; j < entities.size(); j++) { + // sorted + ASSERT_TRUE(entities[j].offset > entities[i].offset || + (entities[j].offset == entities[i].offset && entities[j].length <= entities[i].length)); + + // not intersecting + ASSERT_TRUE(entities[j].offset >= entities[i].offset + entities[i].length || + entities[j].offset + entities[j].length <= entities[i].offset + entities[i].length); + + if (entities[j].offset < entities[i].offset + entities[i].length) { // if nested + // types are different + ASSERT_TRUE(entities[j].type != entities[i].type); + + // pre can't contain other entities + ASSERT_TRUE((type_mask & pre_mask) == 0); + + if ((type_mask & splittable_mask) == 0 && entities[i].type != td::MessageEntity::Type::BlockQuote) { + // continuous entities can contain only splittable entities + ASSERT_TRUE(((1 << static_cast(entities[j].type)) & splittable_mask) != 0); + } + } + } + } + } + + check_fix_formatted_text( + "\xe2\x80\x8f\xe2\x80\x8f \xe2\x80\x8e\xe2\x80\x8e\xe2\x80\x8e\xe2\x80\x8c \xe2\x80\x8f\xe2\x80\x8e " + "\xe2\x80\x8f", + {}, + "\xe2\x80\x8c\xe2\x80\x8f \xe2\x80\x8c\xe2\x80\x8c\xe2\x80\x8e\xe2\x80\x8c \xe2\x80\x8c\xe2\x80\x8e " + "\xe2\x80\x8f", + {}); } static void check_parse_html(td::string text, const td::string &result, const td::vector &entities) { @@ -993,3 +1267,364 @@ TEST(MessageEntities, parse_markdown) { check_parse_markdown("[telegram\\.org](asdasd)", "telegram.org", {}); check_parse_markdown("[telegram\\.org](tg:user?id=123456)", "telegram.org", {{0, 12, td::UserId(123456)}}); } + +static void check_parse_markdown_v3(td::string text, td::vector entities, + const td::string &result_text, const td::vector &result_entities, + bool fix = false) { + auto parsed_text = td::parse_markdown_v3({std::move(text), std::move(entities)}); + if (fix) { + ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok()); + } + ASSERT_STREQ(result_text, parsed_text.text); + ASSERT_EQ(result_entities, parsed_text.entities); + if (fix) { + auto markdown_text = td::get_markdown_v3(parsed_text); + ASSERT_TRUE(parsed_text == markdown_text || parsed_text == td::parse_markdown_v3(markdown_text)); + } +} + +static void check_parse_markdown_v3(td::string text, const td::string &result_text, + const td::vector &result_entities, bool fix = false) { + check_parse_markdown_v3(std::move(text), td::vector(), result_text, result_entities, fix); +} + +TEST(MessageEntities, parse_markdown_v3) { + check_parse_markdown_v3("๐ŸŸ````๐ŸŸ``๐ŸŸ`aba๐ŸŸ```c๐ŸŸ`aba๐ŸŸ daba๐ŸŸ```c๐ŸŸ`aba๐ŸŸ```๐ŸŸ `๐ŸŸ``๐ŸŸ```", + "๐ŸŸ````๐ŸŸ``๐ŸŸaba๐ŸŸ```c๐ŸŸaba๐ŸŸ daba๐ŸŸc๐ŸŸ`aba๐ŸŸ๐ŸŸ `๐ŸŸ``๐ŸŸ```", + {{td::MessageEntity::Type::Code, 12, 11}, {td::MessageEntity::Type::Pre, 35, 9}}); + check_parse_markdown_v3( + "๐ŸŸ````๐ŸŸ``๐ŸŸ`aba๐ŸŸ```c๐ŸŸ`aba๐ŸŸ daba๐ŸŸ```c๐ŸŸ`aba๐ŸŸ๐ŸŸ```๐ŸŸ `๐ŸŸ``๐ŸŸ```", + {{td::MessageEntity::Type::Italic, 12, 1}, + {td::MessageEntity::Type::Italic, 44, 1}, + {td::MessageEntity::Type::Bold, 45, 1}, + {td::MessageEntity::Type::Bold, 49, 2}}, + "๐ŸŸ````๐ŸŸ``๐ŸŸ`aba๐ŸŸc๐ŸŸ`aba๐ŸŸ daba๐ŸŸc๐ŸŸ`aba๐ŸŸ๐ŸŸ๐ŸŸ `๐ŸŸ``๐ŸŸ", + {{td::MessageEntity::Type::Italic, 12, 1}, + {td::MessageEntity::Type::Pre, 18, 16}, + {td::MessageEntity::Type::Italic, 38, 1}, + {td::MessageEntity::Type::Bold, 39, 1}, + {td::MessageEntity::Type::Bold, 43, 2}, + {td::MessageEntity::Type::Pre, 45, 10}}); + check_parse_markdown_v3("` `", " ", {{td::MessageEntity::Type::Code, 0, 1}}); + check_parse_markdown_v3("`\n`", "\n", {{td::MessageEntity::Type::Code, 0, 1}}); + check_parse_markdown_v3("` `a", " a", {{td::MessageEntity::Type::Code, 0, 1}}, true); + check_parse_markdown_v3("`\n`a", "\na", {}, true); + check_parse_markdown_v3("``", "``", {}); + check_parse_markdown_v3("`a````b```", "`a````b```", {}); + check_parse_markdown_v3("ab", {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Pre, 1, 1}}, "ab", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Pre, 1, 1}}); + + check_parse_markdown_v3("[a](b[c](t.me)", "[a](b[c](t.me)", {}); + check_parse_markdown_v3("[](t.me)", "[](t.me)", {}); + check_parse_markdown_v3("[ ](t.me)", " ", {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}}); + check_parse_markdown_v3("[ ](t.me)", "", {}, true); + check_parse_markdown_v3("[ ](t.me)a", " a", {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}}, true); + check_parse_markdown_v3( + "[ ](t.me) [ ](t.me)", {{td::MessageEntity::Type::TextUrl, 8, 1, "http://t.me/"}, {10, 1, td::UserId(1)}}, + "[ ](t.me) [ ](t.me)", {{td::MessageEntity::Type::TextUrl, 8, 1, "http://t.me/"}, {10, 1, td::UserId(1)}}); + check_parse_markdown_v3("[\n](t.me)", "\n", {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}}); + check_parse_markdown_v3("[\n](t.me)a", "\na", {}, true); + check_parse_markdown_v3("asd[abcd](google.com)", {{td::MessageEntity::Type::Italic, 0, 5}}, "asdabcd", + {{td::MessageEntity::Type::Italic, 0, 3}, + {td::MessageEntity::Type::TextUrl, 3, 4, "http://google.com/"}, + {td::MessageEntity::Type::Italic, 3, 1}}); + check_parse_markdown_v3("asd[abcd](google.com)efg[hi](https://t.me?t=1#h)e", + {{td::MessageEntity::Type::Italic, 0, 5}, {td::MessageEntity::Type::Italic, 18, 31}}, + "asdabcdefghie", + {{td::MessageEntity::Type::Italic, 0, 3}, + {td::MessageEntity::Type::TextUrl, 3, 4, "http://google.com/"}, + {td::MessageEntity::Type::Italic, 3, 1}, + {td::MessageEntity::Type::Italic, 7, 3}, + {td::MessageEntity::Type::TextUrl, 10, 2, "https://t.me/?t=1#h"}, + {td::MessageEntity::Type::Italic, 10, 2}, + {td::MessageEntity::Type::Italic, 12, 1}}); + check_parse_markdown_v3( + "๐ŸŸ๐ŸŸ๐ŸŸ[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#1)๐Ÿค™๐Ÿค™๐Ÿค™[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#2)๐Ÿค™๐Ÿค™๐Ÿค™[" + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#3)๐ŸŸ๐ŸŸ๐ŸŸ[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#4)๐Ÿค™๐Ÿค™", + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐Ÿค™๐Ÿค™๐Ÿค™๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐Ÿค™๐Ÿค™๐Ÿค™๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ" + "๐ŸŸ๐Ÿค™๐Ÿค™", + {{td::MessageEntity::Type::TextUrl, 6, 10, "http://www.๐Ÿค™.tk/#1"}, + {td::MessageEntity::Type::TextUrl, 22, 8, "http://www.๐Ÿค™.tk/#2"}, + {td::MessageEntity::Type::TextUrl, 36, 8, "http://www.๐Ÿค™.tk/#3"}, + {td::MessageEntity::Type::TextUrl, 50, 8, "http://www.๐Ÿค™.tk/#4"}}); + check_parse_markdown_v3( + "[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#1)[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#2)[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#3)[" + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk#4)", + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ", + {{td::MessageEntity::Type::TextUrl, 0, 10, "http://www.๐Ÿค™.tk/#1"}, + {td::MessageEntity::Type::TextUrl, 10, 8, "http://www.๐Ÿค™.tk/#2"}, + {td::MessageEntity::Type::TextUrl, 18, 8, "http://www.๐Ÿค™.tk/#3"}, + {td::MessageEntity::Type::TextUrl, 26, 8, "http://www.๐Ÿค™.tk/#4"}}); + check_parse_markdown_v3( + "๐ŸŸ๐ŸŸ๐ŸŸ[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk)๐Ÿค™๐Ÿค™๐Ÿค™[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk)๐Ÿค™๐Ÿค™๐Ÿค™[" + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk)๐ŸŸ๐ŸŸ๐ŸŸ[๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ](www.๐Ÿค™.tk)๐Ÿค™๐Ÿค™", + {{td::MessageEntity::Type::Bold, 0, 2}, + {td::MessageEntity::Type::Bold, 4, 2}, + {td::MessageEntity::Type::Bold, 7, 2}, + {td::MessageEntity::Type::Bold, 11, 2}, + {td::MessageEntity::Type::Bold, 15, 2}, + {td::MessageEntity::Type::Bold, 18, 2}, + {td::MessageEntity::Type::Bold, 26, 2}, + {31, 2, td::UserId(1)}, + {td::MessageEntity::Type::Bold, 35, 1}, + {td::MessageEntity::Type::Bold, 44, 2}, + {td::MessageEntity::Type::Bold, 50, 2}, + {td::MessageEntity::Type::Bold, 54, 2}, + {56, 2, td::UserId(2)}, + {td::MessageEntity::Type::Bold, 58, 7}, + {60, 2, td::UserId(3)}, + {td::MessageEntity::Type::Bold, 67, 7}, + {td::MessageEntity::Type::Bold, 80, 7}, + {td::MessageEntity::Type::Bold, 89, 25}}, + "๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐Ÿค™๐Ÿค™๐Ÿค™๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐Ÿค™๐Ÿค™๐Ÿค™๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ๐ŸŸ" + "๐ŸŸ๐Ÿค™๐Ÿค™", + {{td::MessageEntity::Type::Bold, 0, 2}, + {td::MessageEntity::Type::Bold, 4, 2}, + {td::MessageEntity::Type::TextUrl, 6, 10, "http://www.๐Ÿค™.tk/"}, + {td::MessageEntity::Type::Bold, 6, 2}, + {td::MessageEntity::Type::Bold, 10, 2}, + {td::MessageEntity::Type::Bold, 14, 2}, + {18, 2, td::UserId(1)}, + {td::MessageEntity::Type::TextUrl, 22, 8, "http://www.๐Ÿค™.tk/"}, + {30, 2, td::UserId(2)}, + {td::MessageEntity::Type::Bold, 32, 2}, + {34, 2, td::UserId(3)}, + {td::MessageEntity::Type::Bold, 34, 2}, + {td::MessageEntity::Type::TextUrl, 36, 8, "http://www.๐Ÿค™.tk/"}, + {td::MessageEntity::Type::Bold, 36, 2}, + {td::MessageEntity::Type::Bold, 40, 4}, + {td::MessageEntity::Type::Bold, 44, 4}, + {td::MessageEntity::Type::TextUrl, 50, 8, "http://www.๐Ÿค™.tk/"}, + {td::MessageEntity::Type::Bold, 50, 8}, + {td::MessageEntity::Type::Bold, 58, 4}}); + check_parse_markdown_v3("[`a`](t.me) [b](t.me)", {{td::MessageEntity::Type::Code, 13, 1}}, "[a](t.me) [b](t.me)", + {{td::MessageEntity::Type::Code, 1, 1}, {td::MessageEntity::Type::Code, 11, 1}}); + check_parse_markdown_v3( + "[text](example.com)", + {{td::MessageEntity::Type::Strikethrough, 0, 1}, {td::MessageEntity::Type::Strikethrough, 5, 14}}, "text", + {{td::MessageEntity::Type::TextUrl, 0, 4, "http://example.com/"}}); + + check_parse_markdown_v3("๐ŸŸ[๐ŸŸ](t.me) `๐ŸŸ` [๐ŸŸ](t.me) `a`", "๐ŸŸ๐ŸŸ ๐ŸŸ ๐ŸŸ a", + {{td::MessageEntity::Type::TextUrl, 2, 2, "http://t.me/"}, + {td::MessageEntity::Type::Code, 5, 2}, + {td::MessageEntity::Type::TextUrl, 8, 2, "http://t.me/"}, + {td::MessageEntity::Type::Code, 11, 1}}); + + check_parse_markdown_v3("__ __", " ", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_parse_markdown_v3("__\n__", "\n", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_parse_markdown_v3("__ __a", " a", {}, true); + check_parse_markdown_v3("__\n__a", "\na", {}, true); + check_parse_markdown_v3("**** __a__ **b** ~~c~~", "**** a b c", + {{td::MessageEntity::Type::Italic, 5, 1}, + {td::MessageEntity::Type::Bold, 7, 1}, + {td::MessageEntity::Type::Strikethrough, 9, 1}}); + check_parse_markdown_v3("ั‚ะตัั‚ __ะฐะฐะฐะฐ__ **ะฑะฑะฑะฑ** ~~ะฒะฒะฒะฒ~~", "ั‚ะตัั‚ ะฐะฐะฐะฐ ะฑะฑะฑะฑ ะฒะฒะฒะฒ", + {{td::MessageEntity::Type::Italic, 5, 4}, + {td::MessageEntity::Type::Bold, 10, 4}, + {td::MessageEntity::Type::Strikethrough, 15, 4}}); + check_parse_markdown_v3("___a___ ***b** ~c~~", "___a___ ***b** ~c~~", {}); + check_parse_markdown_v3( + "__asd[ab__cd](t.me)", "asdabcd", + {{td::MessageEntity::Type::Italic, 0, 5}, {td::MessageEntity::Type::TextUrl, 3, 4, "http://t.me/"}}); + check_parse_markdown_v3("__asd[ab__cd](t.me)", "asdabcd", + {{td::MessageEntity::Type::Italic, 0, 3}, + {td::MessageEntity::Type::TextUrl, 3, 4, "http://t.me/"}, + {td::MessageEntity::Type::Italic, 3, 2}}, + true); + check_parse_markdown_v3("__a #test__test", "__a #test__test", {}); + check_parse_markdown_v3("a #testtest", {{td::MessageEntity::Type::Italic, 0, 7}}, "a #testtest", + {{td::MessageEntity::Type::Italic, 0, 7}}); + + // TODO parse_markdown_v3 is not idempotent now, which is bad + check_parse_markdown_v3( + "~~**~~__**a__", {{td::MessageEntity::Type::Strikethrough, 2, 1}, {td::MessageEntity::Type::Bold, 6, 1}}, + "**__**a__", {{td::MessageEntity::Type::Strikethrough, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}}, true); + check_parse_markdown_v3("**__**a__", + {{td::MessageEntity::Type::Strikethrough, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}}, + "__a__", {{td::MessageEntity::Type::Bold, 0, 2}}, true); + check_parse_markdown_v3("__a__", {{td::MessageEntity::Type::Bold, 0, 2}}, "a", + {{td::MessageEntity::Type::Italic, 0, 1}}, true); + check_parse_markdown_v3("~~__~~#test__test", "__#test__test", {{td::MessageEntity::Type::Strikethrough, 0, 2}}); + check_parse_markdown_v3("__#test__test", {{td::MessageEntity::Type::Strikethrough, 0, 2}}, "#testtest", + {{td::MessageEntity::Type::Italic, 0, 5}}); + + check_parse_markdown_v3("__[ab_](t.me)_", "__ab__", {{td::MessageEntity::Type::TextUrl, 2, 3, "http://t.me/"}}); + check_parse_markdown_v3( + "__[ab__](t.me)_", "ab_", + {{td::MessageEntity::Type::TextUrl, 0, 2, "http://t.me/"}, {td::MessageEntity::Type::Italic, 0, 2}}); + check_parse_markdown_v3("__[__ab__](t.me)__", "____ab____", + {{td::MessageEntity::Type::TextUrl, 2, 6, "http://t.me/"}}); + check_parse_markdown_v3( + "__[__ab__](t.me)a__", "____aba", + {{td::MessageEntity::Type::TextUrl, 2, 4, "http://t.me/"}, {td::MessageEntity::Type::Italic, 6, 1}}); + check_parse_markdown_v3("`a` __ab__", {{td::MessageEntity::Type::Bold, 6, 3}}, "a __ab__", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Bold, 4, 3}}); + check_parse_markdown_v3("`a` __ab__", {{td::MessageEntity::Type::Underline, 5, 1}}, "a __ab__", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Underline, 3, 1}}); + + check_parse_markdown_v3("`a` @test__test__test", "a @test__test__test", {{td::MessageEntity::Type::Code, 0, 1}}); + check_parse_markdown_v3("`a` #test__test__test", "a #test__test__test", {{td::MessageEntity::Type::Code, 0, 1}}); + check_parse_markdown_v3("`a` __@test_test_test__", "a @test_test_test", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Italic, 2, 15}}); + check_parse_markdown_v3("`a` __#test_test_test__", "a #test_test_test", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Italic, 2, 15}}); + check_parse_markdown_v3("[a](t.me) __@test**test**test__", "a @testtesttest", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}, + {td::MessageEntity::Type::Italic, 2, 13}, + {td::MessageEntity::Type::Bold, 7, 4}}); + check_parse_markdown_v3("[a](t.me) __#test~~test~~test__", "a #testtesttest", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}, + {td::MessageEntity::Type::Italic, 2, 13}, + {td::MessageEntity::Type::Strikethrough, 7, 4}}); + check_parse_markdown_v3("[a](t.me) __@test__test__test__", "a @testtesttest", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}, + {td::MessageEntity::Type::Italic, 2, 5}, + {td::MessageEntity::Type::Italic, 11, 4}}); + check_parse_markdown_v3("__**~~__gh**~~", "gh", + {{td::MessageEntity::Type::Bold, 0, 2}, {td::MessageEntity::Type::Strikethrough, 0, 2}}); + check_parse_markdown_v3("__ab**cd~~ef__gh**ij~~", "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 6}, + {td::MessageEntity::Type::Bold, 2, 6}, + {td::MessageEntity::Type::Strikethrough, 4, 6}}); + check_parse_markdown_v3("__ab**cd~~ef__gh**ij~~", "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 2}, + {td::MessageEntity::Type::Bold, 2, 2}, + {td::MessageEntity::Type::Italic, 2, 2}, + {td::MessageEntity::Type::Strikethrough, 4, 6}, + {td::MessageEntity::Type::Bold, 4, 4}, + {td::MessageEntity::Type::Italic, 4, 2}}, + true); + check_parse_markdown_v3("__ab**[cd~~ef__](t.me)gh**ij~~", "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 6}, + {td::MessageEntity::Type::Bold, 2, 6}, + {td::MessageEntity::Type::TextUrl, 2, 4, "http://t.me/"}, + {td::MessageEntity::Type::Strikethrough, 4, 6}}); + check_parse_markdown_v3("__ab**[cd~~e](t.me)f__gh**ij~~", "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 6}, + {td::MessageEntity::Type::Bold, 2, 6}, + {td::MessageEntity::Type::TextUrl, 2, 3, "http://t.me/"}, + {td::MessageEntity::Type::Strikethrough, 4, 6}}); + check_parse_markdown_v3("__ab**[cd~~](t.me)ef__gh**ij~~", "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 6}, + {td::MessageEntity::Type::Bold, 2, 6}, + {td::MessageEntity::Type::TextUrl, 2, 2, "http://t.me/"}, + {td::MessageEntity::Type::Strikethrough, 4, 6}}); + check_parse_markdown_v3("[__**bold italic link**__](example.com)", "bold italic link", + {{td::MessageEntity::Type::TextUrl, 0, 16, "http://example.com/"}, + {td::MessageEntity::Type::Bold, 0, 16}, + {td::MessageEntity::Type::Italic, 0, 16}}); + check_parse_markdown_v3( + "__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) __italic**bold " + "italic__bold**__italic__ ~~strikethrough~~ **bold** `code` ```pre``` __[italic__ text_url](telegram.org) " + "__italic**bold italic__bold**", + "italic strikethrough bold code pre italic text_url italicbold italicbolditalic strikethrough bold code pre " + "italic text_url italicbold italicbold", + {{td::MessageEntity::Type::Italic, 0, 6}, + {td::MessageEntity::Type::Strikethrough, 7, 13}, + {td::MessageEntity::Type::Bold, 21, 4}, + {td::MessageEntity::Type::Code, 26, 4}, + {td::MessageEntity::Type::Pre, 31, 3}, + {td::MessageEntity::Type::TextUrl, 35, 15, "http://telegram.org/"}, + {td::MessageEntity::Type::Italic, 35, 6}, + {td::MessageEntity::Type::Italic, 51, 17}, + {td::MessageEntity::Type::Bold, 57, 15}, + {td::MessageEntity::Type::Italic, 72, 6}, + {td::MessageEntity::Type::Strikethrough, 79, 13}, + {td::MessageEntity::Type::Bold, 93, 4}, + {td::MessageEntity::Type::Code, 98, 4}, + {td::MessageEntity::Type::Pre, 103, 3}, + {td::MessageEntity::Type::TextUrl, 107, 15, "http://telegram.org/"}, + {td::MessageEntity::Type::Italic, 107, 6}, + {td::MessageEntity::Type::Italic, 123, 17}, + {td::MessageEntity::Type::Bold, 129, 15}}); + + td::vector parts{"a", " #test__a", "__", "**", "~~", "[", "](t.me)", "`"}; + td::vector types{ + td::MessageEntity::Type::Bold, td::MessageEntity::Type::Italic, td::MessageEntity::Type::Underline, + td::MessageEntity::Type::Strikethrough, td::MessageEntity::Type::Code, td::MessageEntity::Type::Pre, + td::MessageEntity::Type::PreCode, td::MessageEntity::Type::TextUrl, td::MessageEntity::Type::MentionName, + td::MessageEntity::Type::Cashtag}; + for (size_t test_n = 0; test_n < 1000; test_n++) { + td::string str; + int part_n = td::Random::fast(1, 200); + for (int i = 0; i < part_n; i++) { + str += parts[td::Random::fast(0, static_cast(parts.size()) - 1)]; + } + td::vector entities; + int entity_n = td::Random::fast(1, 20); + for (int i = 0; i < entity_n; i++) { + auto type = types[td::Random::fast(0, static_cast(types.size()) - 1)]; + td::int32 offset = td::Random::fast(0, static_cast(str.size()) - 1); + auto max_length = static_cast(str.size() - offset); + if ((test_n & 1) != 0 && max_length > 4) { + max_length = 4; + } + td::int32 length = td::Random::fast(0, max_length); + entities.emplace_back(type, offset, length); + } + + td::FormattedText text{std::move(str), std::move(entities)}; + while (true) { + ASSERT_TRUE(fix_formatted_text(text.text, text.entities, true, true, true, true).is_ok()); + auto parsed_text = td::parse_markdown_v3(text); + ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok()); + if (parsed_text == text) { + break; + } + text = std::move(parsed_text); + } + ASSERT_EQ(text, td::parse_markdown_v3(text)); + auto markdown_text = td::get_markdown_v3(text); + ASSERT_TRUE(text == markdown_text || text == td::parse_markdown_v3(markdown_text)); + } +} + +static void check_get_markdown_v3(td::string result_text, td::vector result_entities, + const td::string &text, const td::vector &entities) { + auto markdown_text = td::get_markdown_v3({std::move(text), std::move(entities)}); + ASSERT_STREQ(result_text, markdown_text.text); + ASSERT_EQ(result_entities, markdown_text.entities); +} + +TEST(MessageEntities, get_markdown_v3) { + check_get_markdown_v3("``` ```", {}, " ", {{td::MessageEntity::Type::Pre, 0, 1}}); + check_get_markdown_v3("` `", {}, " ", {{td::MessageEntity::Type::Code, 0, 1}}); + check_get_markdown_v3("`\n`", {}, "\n", {{td::MessageEntity::Type::Code, 0, 1}}); + check_get_markdown_v3("ab", {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Pre, 1, 1}}, "ab", + {{td::MessageEntity::Type::Code, 0, 1}, {td::MessageEntity::Type::Pre, 1, 1}}); + + check_get_markdown_v3("[ ](http://t.me/)", {}, " ", {{td::MessageEntity::Type::TextUrl, 0, 1, "http://t.me/"}}); + check_get_markdown_v3("[ ]t.me[)](http://t.me/) [ ](t.me)", {{25, 1, td::UserId(1)}}, "[ ]t.me) [ ](t.me)", + {{td::MessageEntity::Type::TextUrl, 7, 1, "http://t.me/"}, {9, 1, td::UserId(1)}}); + + check_get_markdown_v3("__ __", {}, " ", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_get_markdown_v3("** **", {}, " ", {{td::MessageEntity::Type::Bold, 0, 1}}); + check_get_markdown_v3("~~ ~~", {}, " ", {{td::MessageEntity::Type::Strikethrough, 0, 1}}); + check_get_markdown_v3("__a__ **b** ~~c~~ d", {{td::MessageEntity::Type::PreCode, 18, 1, "C++"}}, "a b c d", + {{td::MessageEntity::Type::Italic, 0, 1}, + {td::MessageEntity::Type::Bold, 2, 1}, + {td::MessageEntity::Type::Strikethrough, 4, 1}, + {td::MessageEntity::Type::PreCode, 6, 1, "C++"}}); + check_get_markdown_v3("`ab` ```cd``` ef", {{td::MessageEntity::Type::PreCode, 14, 2, "C++"}}, "ab cd ef", + {{td::MessageEntity::Type::Code, 0, 2}, + {td::MessageEntity::Type::Pre, 3, 2}, + {td::MessageEntity::Type::PreCode, 6, 2, "C++"}}); + check_get_markdown_v3("__asd__[__ab__cd](http://t.me/)", {}, "asdabcd", + {{td::MessageEntity::Type::Italic, 0, 3}, + {td::MessageEntity::Type::TextUrl, 3, 4, "http://t.me/"}, + {td::MessageEntity::Type::Italic, 3, 2}}); + + check_get_markdown_v3("__ab", {{td::MessageEntity::Type::Italic, 3, 1}}, "__ab", + {{td::MessageEntity::Type::Italic, 3, 1}}); + check_get_markdown_v3("__ab__**__cd__**~~**__ef__gh**ij~~", {}, "abcdefghij", + {{td::MessageEntity::Type::Italic, 0, 2}, + {td::MessageEntity::Type::Bold, 2, 2}, + {td::MessageEntity::Type::Italic, 2, 2}, + {td::MessageEntity::Type::Strikethrough, 4, 6}, + {td::MessageEntity::Type::Bold, 4, 4}, + {td::MessageEntity::Type::Italic, 4, 2}}); + check_get_markdown_v3("[**__bold italic link__**](http://example.com/)", {}, "bold italic link", + {{td::MessageEntity::Type::TextUrl, 0, 16, "http://example.com/"}, + {td::MessageEntity::Type::Bold, 0, 16}, + {td::MessageEntity::Type::Italic, 0, 16}}); +} diff --git a/test/mtproto.cpp b/test/mtproto.cpp index 3a49b2d8..991a6c01 100644 --- a/test/mtproto.cpp +++ b/test/mtproto.cpp @@ -527,6 +527,7 @@ class FastPingTestActor : public Actor { void got_connection(Result> r_raw_connection, int32 dummy) { if (r_raw_connection.is_error()) { *result_ = r_raw_connection.move_as_error(); + LOG(INFO) << "Receive " << *result_ << " instead of a connection"; return stop(); } connection_ = r_raw_connection.move_as_ok(); @@ -536,6 +537,7 @@ class FastPingTestActor : public Actor { void got_handshake(Result> r_handshake, int32 dummy) { if (r_handshake.is_error()) { *result_ = r_handshake.move_as_error(); + LOG(INFO) << "Receive " << *result_ << " instead of a handshake"; return stop(); } handshake_ = r_handshake.move_as_ok(); @@ -544,8 +546,8 @@ class FastPingTestActor : public Actor { void got_raw_connection(Result> r_connection) { if (r_connection.is_error()) { - Scheduler::instance()->finish(); *result_ = r_connection.move_as_error(); + LOG(INFO) << "Receive " << *result_ << " instead of a handshake"; return stop(); } connection_ = r_connection.move_as_ok(); @@ -558,13 +560,12 @@ class FastPingTestActor : public Actor { if (handshake_ && connection_) { LOG(INFO) << "Iteration " << iteration_; if (iteration_ == 6) { - Scheduler::instance()->finish(); return stop(); } unique_ptr auth_data; if (iteration_ % 2 == 0) { auth_data = make_unique(); - auth_data->set_tmp_auth_key(handshake_->release_auth_key()); + auth_data->set_tmp_auth_key(handshake_->get_auth_key()); auth_data->set_server_time_difference(handshake_->get_server_time_diff()); auth_data->set_server_salt(handshake_->get_server_salt(), Time::now()); auth_data->set_future_salts({mtproto::ServerSalt{0u, 1e20, 1e30}}, Time::now()); @@ -584,6 +585,10 @@ class FastPingTestActor : public Actor { ActorShared<>()); } } + + void tear_down() override { + Scheduler::instance()->finish(); + } }; class Mtproto_FastPing : public Test { @@ -647,7 +652,12 @@ TEST(Mtproto, TlsTransport) { const std::string domain = "www.google.com"; IPAddress ip_address; - ip_address.init_host_port(domain, 443).ensure(); + auto resolve_status = ip_address.init_host_port(domain, 443); + if (resolve_status.is_error()) { + LOG(ERROR) << resolve_status; + Scheduler::instance()->finish(); + return; + } SocketFd fd = SocketFd::open(ip_address).move_as_ok(); create_actor("TlsInit", std::move(fd), domain, "0123456789secret", make_unique(), ActorShared<>(), Clocks::system() - Time::now()) diff --git a/test/string_cleaning.cpp b/test/string_cleaning.cpp index 890c1a5b..4ac98fc2 100644 --- a/test/string_cleaning.cpp +++ b/test/string_cleaning.cpp @@ -66,6 +66,12 @@ TEST(StringCleaning, clean_input_string) { check_clean_input_string( "\xe2\x80\xa7\xe2\x80\xa8\xe2\x80\xa9\xe2\x80\xaa\xe2\x80\xab\xe2\x80\xac\xe2\x80\xad\xe2\x80\xae\xe2\x80\xaf", "\xe2\x80\xa7\xe2\x80\xaf", true); + check_clean_input_string( + "\xe2\x80\x8f\xe2\x80\x8f \xe2\x80\x8e\xe2\x80\x8e\xe2\x80\x8e\xe2\x80\x8c \xe2\x80\x8f\xe2\x80\x8e " + "\xe2\x80\x8f", + "\xe2\x80\x8c\xe2\x80\x8f \xe2\x80\x8c\xe2\x80\x8c\xe2\x80\x8e\xe2\x80\x8c \xe2\x80\x8c\xe2\x80\x8e " + "\xe2\x80\x8f", + true); check_clean_input_string("\xcc\xb3\xcc\xbf\xcc\x8a", "", true); }