diff --git a/CMakeLists.txt b/CMakeLists.txt index e2c1a8f0..5d3ac731 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.1 LANGUAGES CXX C) +project(TDLib VERSION 1.5.4 LANGUAGES CXX C) if (POLICY CMP0054) # do not expand quoted arguments @@ -87,8 +87,8 @@ if (EMSCRIPTEN) set(ZLIB_LIBRARIES) set(ZLIB_INCLUDE_DIR) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s MEMFS_APPEND_TO_TYPED_ARRAYS=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s WEBSOCKET_URL=\"'wss:#'\" -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS','cwrap']\"") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s MEMFS_APPEND_TO_TYPED_ARRAYS=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s WEBSOCKET_URL=\"'wss:#'\" -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS','cwrap']\"") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s MEMFS_APPEND_TO_TYPED_ARRAYS=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s WEBSOCKET_URL=\"'wss:#'\" -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS','cwrap']\" -lidbfs.js -lworkerfs.js") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s MEMFS_APPEND_TO_TYPED_ARRAYS=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s WEBSOCKET_URL=\"'wss:#'\" -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS','cwrap']\" -lidbfs.js -lworkerfs.js") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1") @@ -301,7 +301,7 @@ endif() #SOURCE SETS set_source_files_properties(${TL_TD_AUTO} PROPERTIES GENERATED TRUE) -if (TD_ENABLE_JNI) +if (TD_ENABLE_JNI OR ANDROID) set(TL_JNI_OBJECT td/tl/tl_jni_object.cpp td/tl/tl_jni_object.h @@ -373,8 +373,10 @@ set(TDLIB_SOURCE td/telegram/DelayDispatcher.cpp td/telegram/DeviceTokenManager.cpp td/telegram/DhCache.cpp + td/telegram/DialogAdministrator.cpp td/telegram/DialogDb.cpp td/telegram/DialogId.cpp + td/telegram/DialogLocation.cpp td/telegram/DialogParticipant.cpp td/telegram/Document.cpp td/telegram/DocumentsManager.cpp @@ -409,6 +411,7 @@ set(TDLIB_SOURCE td/telegram/Logging.cpp td/telegram/MessageContent.cpp td/telegram/MessageEntity.cpp + td/telegram/MessageId.cpp td/telegram/MessagesDb.cpp td/telegram/MessagesManager.cpp td/telegram/misc.cpp @@ -442,6 +445,7 @@ set(TDLIB_SOURCE td/telegram/PollManager.cpp td/telegram/QueryCombiner.cpp td/telegram/ReplyMarkup.cpp + td/telegram/RestrictionReason.cpp td/telegram/SecretChatActor.cpp td/telegram/SecretChatDb.cpp td/telegram/SecretChatsManager.cpp @@ -458,6 +462,7 @@ set(TDLIB_SOURCE td/telegram/TermsOfService.cpp td/telegram/TopDialogManager.cpp td/telegram/UpdatesManager.cpp + td/telegram/Venue.cpp td/telegram/VideoNotesManager.cpp td/telegram/VideosManager.cpp td/telegram/VoiceNotesManager.cpp @@ -516,9 +521,11 @@ set(TDLIB_SOURCE td/telegram/DeviceTokenManager.h td/telegram/DhCache.h td/telegram/DhConfig.h + td/telegram/DialogAdministrator.h td/telegram/DialogDate.h td/telegram/DialogDb.h td/telegram/DialogId.h + td/telegram/DialogLocation.h td/telegram/DialogParticipant.h td/telegram/Document.h td/telegram/DocumentsManager.h @@ -550,6 +557,8 @@ set(TDLIB_SOURCE td/telegram/files/PartsManager.h td/telegram/files/ResourceManager.h td/telegram/files/ResourceState.h + td/telegram/FolderId.h + td/telegram/FullMessageId.h td/telegram/Game.h td/telegram/Global.h td/telegram/HashtagHints.h @@ -607,9 +616,12 @@ set(TDLIB_SOURCE td/telegram/PollManager.h td/telegram/PrivacyManager.h td/telegram/PtsManager.h + td/telegram/PublicDialogType.h td/telegram/QueryCombiner.h td/telegram/ReplyMarkup.h td/telegram/RequestActor.h + td/telegram/RestrictionReason.h + td/telegram/ScheduledServerMessageId.h td/telegram/SecretChatActor.h td/telegram/SecretChatId.h td/telegram/SecretChatDb.h @@ -620,8 +632,10 @@ set(TDLIB_SOURCE td/telegram/SecureValue.h td/telegram/SendCodeHelper.h td/telegram/SequenceDispatcher.h + td/telegram/ServerMessageId.h td/telegram/SetWithPosition.h td/telegram/StateManager.h + td/telegram/StickerSetId.h td/telegram/StickersManager.h td/telegram/StorageManager.h td/telegram/Td.h @@ -633,6 +647,7 @@ set(TDLIB_SOURCE td/telegram/UniqueId.h td/telegram/UpdatesManager.h td/telegram/UserId.h + td/telegram/Venue.h td/telegram/Version.h td/telegram/VideoNotesManager.h td/telegram/VideosManager.h @@ -665,6 +680,7 @@ set(TDLIB_SOURCE td/telegram/ReplyMarkup.hpp td/telegram/SecureValue.hpp td/telegram/SendCodeHelper.hpp + td/telegram/StickerSetId.hpp td/telegram/StickersManager.hpp td/telegram/VideoNotesManager.hpp td/telegram/VideosManager.hpp diff --git a/README.md b/README.md index a108f2e3..64e9aded 100644 --- a/README.md +++ b/README.md @@ -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.1 REQUIRED) +find_package(Td 1.5.4 REQUIRED) target_link_libraries(YourTarget PRIVATE Td::TdStatic) ``` See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt). diff --git a/SplitSource.php b/SplitSource.php index 74e454ef..d36dd2ac 100644 --- a/SplitSource.php +++ b/SplitSource.php @@ -157,6 +157,7 @@ function split_file($file, $chunks, $undo) { '(CREATE_REQUEST|CREATE_NO_ARGS_REQUEST)[(](?[A-Z][A-Za-z]*)|'. '(?complete_pending_preauthentication_requests)|'. '(Up|Down)load[a-zA-Z]*C(?allback)|(up|down)load_[a-z_]*_c(?allback)_|'. + '(?lazy_to_json)|'. '(?LogEvent)[^sA]|'. '(?parse)[(]|'. '(?store)[(]/', $f, $matches, PREG_SET_ORDER)) { @@ -274,6 +275,7 @@ function split_file($file, $chunks, $undo) { 'get_erase_logevent_promise|parse_time|store_time' => 'logevent/LogEventHelper', 'messages_manager[_(-][^.]|MessagesManager' => 'MessagesManager', 'notification_manager[_(-][^.]|NotificationManager|notifications[)]' => 'NotificationManager', + 'PublicDialogType|get_public_dialog_type' => 'PublicDialogType', 'SecretChatActor' => 'SecretChatActor', 'secret_chats_manager[_(-][^.]|SecretChatsManager' => 'SecretChatsManager', 'stickers_manager[_(-][^.]|StickersManager' => 'StickersManager', diff --git a/benchmark/bench_actor.cpp b/benchmark/bench_actor.cpp index 9c050463..674ca33e 100644 --- a/benchmark/bench_actor.cpp +++ b/benchmark/bench_actor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_crypto.cpp b/benchmark/bench_crypto.cpp index 82bbe06f..edc74af0 100644 --- a/benchmark/bench_crypto.cpp +++ b/benchmark/bench_crypto.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_db.cpp b/benchmark/bench_db.cpp index 96a3a1c3..aee56f61 100644 --- a/benchmark/bench_db.cpp +++ b/benchmark/bench_db.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,6 +9,7 @@ #include "td/db/binlog/Binlog.h" #include "td/db/binlog/ConcurrentBinlog.h" #include "td/db/BinlogKeyValue.h" +#include "td/db/DbKey.h" #include "td/db/SeqKeyValue.h" #include "td/db/SqliteConnectionSafe.h" #include "td/db/SqliteDb.h" diff --git a/benchmark/bench_empty.cpp b/benchmark/bench_empty.cpp index fc441668..24bce2a1 100644 --- a/benchmark/bench_empty.cpp +++ b/benchmark/bench_empty.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_handshake.cpp b/benchmark/bench_handshake.cpp index 8d6e5b3b..376175ff 100644 --- a/benchmark/bench_handshake.cpp +++ b/benchmark/bench_handshake.cpp @@ -1,10 +1,10 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "td/utils/benchmark.h" // for bench, do_not_optimize_away, etc +#include "td/utils/benchmark.h" #include "td/mtproto/DhHandshake.h" diff --git a/benchmark/bench_http.cpp b/benchmark/bench_http.cpp index 1bca2daf..22ab30f8 100644 --- a/benchmark/bench_http.cpp +++ b/benchmark/bench_http.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_http_reader.cpp b/benchmark/bench_http_reader.cpp index d02e76a7..132c7059 100644 --- a/benchmark/bench_http_reader.cpp +++ b/benchmark/bench_http_reader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_http_server.cpp b/benchmark/bench_http_server.cpp index 3e06a9a1..89b4e4ef 100644 --- a/benchmark/bench_http_server.cpp +++ b/benchmark/bench_http_server.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_http_server_cheat.cpp b/benchmark/bench_http_server_cheat.cpp index 162cc051..8716ec5c 100644 --- a/benchmark/bench_http_server_cheat.cpp +++ b/benchmark/bench_http_server_cheat.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_http_server_fast.cpp b/benchmark/bench_http_server_fast.cpp index b208ab7c..0d012f11 100644 --- a/benchmark/bench_http_server_fast.cpp +++ b/benchmark/bench_http_server_fast.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_log.cpp b/benchmark/bench_log.cpp index c0687acd..afc23e9a 100644 --- a/benchmark/bench_log.cpp +++ b/benchmark/bench_log.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_misc.cpp b/benchmark/bench_misc.cpp index 2c5113da..84c4465c 100644 --- a/benchmark/bench_misc.cpp +++ b/benchmark/bench_misc.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -47,7 +47,7 @@ class F { }; BENCH(Call, "TL Call") { - tl_object_ptr x = make_tl_object(); + tl_object_ptr x = make_tl_object(0); uint32 res = 0; F f(res); for (int i = 0; i < n; i++) { diff --git a/benchmark/bench_queue.cpp b/benchmark/bench_queue.cpp index 94139962..7dbe02b8 100644 --- a/benchmark/bench_queue.cpp +++ b/benchmark/bench_queue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/bench_tddb.cpp b/benchmark/bench_tddb.cpp index 3c8d485d..26f358c7 100644 --- a/benchmark/bench_tddb.cpp +++ b/benchmark/bench_tddb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/telegram/MessageId.h" #include "td/telegram/MessagesDb.h" #include "td/telegram/NotificationId.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" diff --git a/benchmark/check_proxy.cpp b/benchmark/check_proxy.cpp index da8ace40..efe081ce 100644 --- a/benchmark/check_proxy.cpp +++ b/benchmark/check_proxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/check_tls.cpp b/benchmark/check_tls.cpp index c2ac3c56..e21d4119 100644 --- a/benchmark/check_tls.cpp +++ b/benchmark/check_tls.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/rmdir.cpp b/benchmark/rmdir.cpp index 456b3ae6..2b30b1ee 100644 --- a/benchmark/rmdir.cpp +++ b/benchmark/rmdir.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/benchmark/wget.cpp b/benchmark/wget.cpp index 5a2bb63d..da2d5bc2 100644 --- a/benchmark/wget.cpp +++ b/benchmark/wget.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/build.html b/build.html index f0d47d31..a6262a1b 100644 --- a/build.html +++ b/build.html @@ -170,7 +170,7 @@ function getSupportedOs(language) { case 'Kotlin': return ['Android', 'Windows', 'Linux', 'macOS', 'FreeBSD', 'OpenBSD']; case 'C#': - return ['Windows (through C++/CLI)', 'Universal Windows Platform (through C++/CX)', 'Windows (.Net Core)', 'Linux (.Net Core)', 'macOS (.Net Core)', 'FreeBSD (.Net Core)']; + return ['Windows (through C++/CLI)', 'Universal Windows Platform (through C++/CX)', 'Windows (.NET Core)', 'Linux (.NET Core)', 'macOS (.NET Core)', 'FreeBSD (.NET Core)']; case 'Dart': return ['Android', 'iOS', 'Windows', 'Linux', 'macOS', 'tvOS', 'watchOS']; case 'Swift': @@ -411,7 +411,7 @@ function onOptionsChanged() { var use_vcpkg = os_windows; var use_lto = false; - if (!use_msvc && language !== 'Java' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Other')))) { + if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Other')))) { document.getElementById('buildLtoDiv').style.display = 'block'; use_lto = document.getElementById('buildLtoCheckbox').checked; } else { @@ -609,7 +609,7 @@ function onOptionsChanged() { } if (use_clang) { if (linux_distro === 'Ubuntu 18') { - packages += ' clang-6.0 libc++abi-dev'; + packages += ' clang-6.0 libc++-dev libc++abi-dev'; } else { if (linux_distro === 'Ubuntu 14') { packages += ' clang-3.9'; @@ -666,6 +666,7 @@ function onOptionsChanged() { } } commands.push('git clone https://github.com/tdlib/td.git'); + commands.push('git checkout v1.5.0'); commands.push('cd td'); @@ -781,12 +782,14 @@ function onOptionsChanged() { var prefix = ''; if (os_linux) { if (use_clang) { - prefix = 'CC=/usr/bin/clang CXX=/usr/bin/clang++ '; if (linux_distro === 'Ubuntu 18') { + prefix = 'CC=/usr/bin/clang-6.0 CXX=/usr/bin/clang++-6.0 '; options.push('-DCMAKE_AR=/usr/bin/llvm-ar-6.0'); options.push('-DCMAKE_NM=/usr/bin/llvm-nm-6.0'); options.push('-DCMAKE_OBJDUMP=/usr/bin/llvm-objdump-6.0'); options.push('-DCMAKE_RANLIB=/usr/bin/llvm-ranlib-6.0'); + } else { + prefix = 'CC=/usr/bin/clang CXX=/usr/bin/clang++ '; } } else if (linux_distro === 'Ubuntu 14') { prefix = 'CC=/usr/bin/gcc-4.9 CXX=/usr/bin/g++-4.9 '; diff --git a/example/README.md b/example/README.md index a85a0f04..7bdbd7fe 100644 --- a/example/README.md +++ b/example/README.md @@ -102,7 +102,7 @@ TDLib provides a native [.NET](https://github.com/tdlib/td#using-dotnet) interfa See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for an example of building TDLib SDK for the Universal Windows Platform and an example of its usage from C#. See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for an example of building TDLib with `C++/CLI` support and an example of TDLib usage from C# on Windows. -If you want to write a cross-platform C# application using .Net Core, see [tdsharp](https://github.com/x2bool/tdsharp). It uses our [JSON](https://github.com/tdlib/td#using-json) interface, +If you want to write a cross-platform C# application using .NET Core, see [tdsharp](https://github.com/egramtel/tdsharp). It uses our [JSON](https://github.com/tdlib/td#using-json) interface, provides an asynchronous interface for interaction with TDLib, automatically generated classes for TDLib API and has some examples. Also see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featured client rewritten from scratch in C# using TDLib SDK for Universal Windows Platform in less than 2 months, or diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index c1d34414..e506abfe 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.1 REQUIRED) +find_package(Td 1.5.4 REQUIRED) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/example/cpp/td_example.cpp b/example/cpp/td_example.cpp index 8bb7aafa..0e8094e5 100644 --- a/example/cpp/td_example.cpp +++ b/example/cpp/td_example.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -64,7 +64,7 @@ class TdExample { } else if (!are_authorized_) { process_response(client_->receive(10)); } else { - std::cerr << "Enter action [q] quit [u] check for updates and request results [c] show chats [m ] " + std::cout << "Enter action [q] quit [u] check for updates and request results [c] show chats [m ] " "send message [me] show self [l] logout: " << std::endl; std::string line; @@ -78,7 +78,7 @@ class TdExample { return; } if (action == "u") { - std::cerr << "Checking for updates..." << std::endl; + std::cout << "Checking for updates..." << std::endl; while (true) { auto response = client_->receive(0); if (response.object) { @@ -88,13 +88,13 @@ class TdExample { } } } else if (action == "close") { - std::cerr << "Closing..." << std::endl; + std::cout << "Closing..." << std::endl; send_query(td_api::make_object(), {}); } else if (action == "me") { send_query(td_api::make_object(), - [this](Object object) { std::cerr << to_string(object) << std::endl; }); + [this](Object object) { std::cout << to_string(object) << std::endl; }); } else if (action == "l") { - std::cerr << "Logging out..." << std::endl; + std::cout << "Logging out..." << std::endl; send_query(td_api::make_object(), {}); } else if (action == "m") { std::int64_t chat_id; @@ -103,7 +103,7 @@ class TdExample { std::string text; std::getline(ss, text); - std::cerr << "Sending message to chat " << chat_id << "..." << std::endl; + std::cout << "Sending message to chat " << chat_id << "..." << std::endl; auto send_message = td_api::make_object(); send_message->chat_id_ = chat_id; auto message_content = td_api::make_object(); @@ -113,15 +113,15 @@ class TdExample { send_query(std::move(send_message), {}); } else if (action == "c") { - std::cerr << "Loading chat list..." << std::endl; - send_query(td_api::make_object(std::numeric_limits::max(), 0, 20), + std::cout << "Loading chat list..." << std::endl; + send_query(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 20), [this](Object object) { if (object->get_id() == td_api::error::ID) { return; } auto chats = td::move_tl_object_as(object); for (auto chat_id : chats->chat_ids_) { - std::cerr << "[id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl; + std::cout << "[id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl; } }); } @@ -162,7 +162,7 @@ class TdExample { if (!response.object) { return; } - //std::cerr << response.id << " " << to_string(response.object) << std::endl; + //std::cout << response.id << " " << to_string(response.object) << std::endl; if (response.id == 0) { return process_update(std::move(response.object)); } @@ -204,7 +204,7 @@ class TdExample { if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) { text = static_cast(*update_new_message.message_->content_).text_->text_; } - std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] [" + std::cout << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] [" << text << "]" << std::endl; }, [](auto &update) {})); @@ -225,20 +225,20 @@ class TdExample { overloaded( [this](td_api::authorizationStateReady &) { are_authorized_ = true; - std::cerr << "Got authorization" << std::endl; + std::cout << "Got authorization" << std::endl; }, [this](td_api::authorizationStateLoggingOut &) { are_authorized_ = false; - std::cerr << "Logging out" << std::endl; + std::cout << "Logging out" << std::endl; }, - [this](td_api::authorizationStateClosing &) { std::cerr << "Closing" << std::endl; }, + [this](td_api::authorizationStateClosing &) { std::cout << "Closing" << std::endl; }, [this](td_api::authorizationStateClosed &) { are_authorized_ = false; need_restart_ = true; - std::cerr << "Terminated" << std::endl; + std::cout << "Terminated" << std::endl; }, [this](td_api::authorizationStateWaitCode &) { - std::cerr << "Enter authentication code: "; + std::cout << "Enter authentication code: " << std::flush; std::string code; std::cin >> code; send_query(td_api::make_object(code), @@ -247,29 +247,32 @@ class TdExample { [this](td_api::authorizationStateWaitRegistration &) { std::string first_name; std::string last_name; - std::cerr << "Enter your first name: "; + std::cout << "Enter your first name: " << std::flush; std::cin >> first_name; - std::cerr << "Enter your last name: "; + std::cout << "Enter your last name: " << std::flush; std::cin >> last_name; send_query(td_api::make_object(first_name, last_name), create_authentication_query_handler()); }, [this](td_api::authorizationStateWaitPassword &) { - std::cerr << "Enter authentication password: "; + std::cout << "Enter authentication password: " << std::flush; std::string password; std::cin >> password; send_query(td_api::make_object(password), create_authentication_query_handler()); }, + [this](td_api::authorizationStateWaitOtherDeviceConfirmation &state) { + std::cout << "Confirm this login link on another device: " << state.link_ << std::endl; + }, [this](td_api::authorizationStateWaitPhoneNumber &) { - std::cerr << "Enter phone number: "; + std::cout << "Enter phone number: " << std::flush; std::string phone_number; std::cin >> phone_number; send_query(td_api::make_object(phone_number, nullptr), create_authentication_query_handler()); }, [this](td_api::authorizationStateWaitEncryptionKey &) { - std::cerr << "Enter encryption key or DESTROY: "; + std::cout << "Enter encryption key or DESTROY: " << std::flush; std::string key; std::getline(std::cin, key); if (key == "DESTROY") { @@ -299,7 +302,7 @@ class TdExample { void check_authentication_error(Object object) { if (object->get_id() == td_api::error::ID) { auto error = td::move_tl_object_as(object); - std::cerr << "Error: " << to_string(error); + std::cout << "Error: " << to_string(error) << std::flush; on_authorization_state_update(); } } diff --git a/example/cpp/tdjson_example.cpp b/example/cpp/tdjson_example.cpp index 2e261375..c22320ee 100644 --- a/example/cpp/tdjson_example.cpp +++ b/example/cpp/tdjson_example.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,7 +18,7 @@ int main() { void *client = td_json_client_create(); // somehow share the client with other threads, which will be able to send requests via td_json_client_send - const bool test_incorrect_queries = true; + const bool test_incorrect_queries = false; if (test_incorrect_queries) { td_json_client_execute(nullptr, "{\"@type\":\"setLogVerbosityLevel\", \"new_verbosity_level\":3}"); td_json_client_execute(nullptr, ""); diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs index 72820897..4eff7475 100644 --- a/example/csharp/TdExample.cs +++ b/example/csharp/TdExample.cs @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -95,6 +95,10 @@ namespace TdExample string phoneNumber = ReadLine("Please enter phone number: "); _client.Send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler()); } + else if (_authorizationState is TdApi.AuthorizationStateWaitOtherDeviceConfirmation state) + { + Console.WriteLine("Please confirm this login link on another device: " + state.Link); + } else if (_authorizationState is TdApi.AuthorizationStateWaitCode) { string code = ReadLine("Please enter authentication code: "); @@ -206,7 +210,7 @@ namespace TdExample TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][] { row, row, row }); TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true); - _client.Send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), _defaultHandler); + _client.Send(new TdApi.SendMessage(chatId, 0, null, replyMarkup, content), _defaultHandler); } static void Main() @@ -231,7 +235,7 @@ namespace TdExample _gotAuthorization.Reset(); _gotAuthorization.WaitOne(); - _client.Send(new TdApi.GetChats(Int64.MaxValue, 0, 100), _defaultHandler); // preload chat list + _client.Send(new TdApi.GetChats(null, Int64.MaxValue, 0, 100), _defaultHandler); // preload main chat list while (_haveAuthorization) { GetCommand(); diff --git a/example/java/org/drinkless/tdlib/Client.java b/example/java/org/drinkless/tdlib/Client.java index 166ecd5b..8b022db0 100644 --- a/example/java/org/drinkless/tdlib/Client.java +++ b/example/java/org/drinkless/tdlib/Client.java @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/java/org/drinkless/tdlib/Log.java b/example/java/org/drinkless/tdlib/Log.java index bdad2ac7..0db0126a 100644 --- a/example/java/org/drinkless/tdlib/Log.java +++ b/example/java/org/drinkless/tdlib/Log.java @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/java/org/drinkless/tdlib/example/Example.java b/example/java/org/drinkless/tdlib/example/Example.java index 70602f3c..57a1cf43 100644 --- a/example/java/org/drinkless/tdlib/example/Example.java +++ b/example/java/org/drinkless/tdlib/example/Example.java @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -42,8 +42,8 @@ public final class Example { private static final ConcurrentMap secretChats = new ConcurrentHashMap(); private static final ConcurrentMap chats = new ConcurrentHashMap(); - private static final NavigableSet chatList = new TreeSet(); - private static boolean haveFullChatList = false; + private static final NavigableSet mainChatList = new TreeSet(); + private static boolean haveFullMainChatList = false; private static final ConcurrentMap usersFullInfo = new ConcurrentHashMap(); private static final ConcurrentMap basicGroupsFullInfo = new ConcurrentHashMap(); @@ -72,17 +72,23 @@ public final class Example { } private static void setChatOrder(TdApi.Chat chat, long order) { - synchronized (chatList) { - if (chat.order != 0) { - boolean isRemoved = chatList.remove(new OrderedChat(chat.order, chat.id)); - assert isRemoved; - } + synchronized (mainChatList) { + synchronized (chat) { + if (chat.chatList == null || chat.chatList.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) { + return; + } - chat.order = order; + if (chat.order != 0) { + boolean isRemoved = mainChatList.remove(new OrderedChat(chat.order, chat.id)); + assert isRemoved; + } - if (chat.order != 0) { - boolean isAdded = chatList.add(new OrderedChat(chat.order, chat.id)); - assert isAdded; + chat.order = order; + + if (chat.order != 0) { + boolean isAdded = mainChatList.add(new OrderedChat(chat.order, chat.id)); + assert isAdded; + } } } } @@ -115,6 +121,11 @@ public final class Example { client.send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler()); break; } + case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR: { + String link = ((TdApi.AuthorizationStateWaitOtherDeviceConfirmation) Example.authorizationState).link; + System.out.println("Please confirm this login link on another device: " + link); + break; + } case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: { String code = promptString("Please enter authentication code: "); client.send(new TdApi.CheckAuthenticationCode(code), new AuthorizationRequestHandler()); @@ -201,7 +212,7 @@ public final class Example { if (commands.length > 1) { limit = toInt(commands[1]); } - getChatList(limit); + getMainChatList(limit); break; } case "gc": @@ -232,18 +243,18 @@ public final class Example { } } - private static void getChatList(final int limit) { - synchronized (chatList) { - if (!haveFullChatList && limit > chatList.size()) { + private static void getMainChatList(final int limit) { + synchronized (mainChatList) { + if (!haveFullMainChatList && limit > mainChatList.size()) { // have enough chats in the chat list or chat list is too small long offsetOrder = Long.MAX_VALUE; long offsetChatId = 0; - if (!chatList.isEmpty()) { - OrderedChat last = chatList.last(); + if (!mainChatList.isEmpty()) { + OrderedChat last = mainChatList.last(); offsetOrder = last.order; offsetChatId = last.chatId; } - client.send(new TdApi.GetChats(offsetOrder, offsetChatId, limit - chatList.size()), new Client.ResultHandler() { + client.send(new TdApi.GetChats(new TdApi.ChatListMain(), offsetOrder, offsetChatId, limit - mainChatList.size()), new Client.ResultHandler() { @Override public void onResult(TdApi.Object object) { switch (object.getConstructor()) { @@ -253,12 +264,12 @@ public final class Example { case TdApi.Chats.CONSTRUCTOR: long[] chatIds = ((TdApi.Chats) object).chatIds; if (chatIds.length == 0) { - synchronized (chatList) { - haveFullChatList = true; + synchronized (mainChatList) { + haveFullMainChatList = true; } } // chats had already been received through updates, let's retry request - getChatList(limit); + getMainChatList(limit); break; default: System.err.println("Receive wrong response from TDLib:" + newLine + object); @@ -269,9 +280,9 @@ public final class Example { } // have enough chats in the chat list to answer request - java.util.Iterator iter = chatList.iterator(); + java.util.Iterator iter = mainChatList.iterator(); System.out.println(); - System.out.println("First " + limit + " chat(s) out of " + chatList.size() + " known chat(s):"); + System.out.println("First " + limit + " chat(s) out of " + mainChatList.size() + " known chat(s):"); for (int i = 0; i < limit; i++) { long chatId = iter.next().chatId; TdApi.Chat chat = chats.get(chatId); @@ -289,7 +300,7 @@ public final class Example { TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][]{row, row, row}); TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true); - client.send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), defaultHandler); + client.send(new TdApi.SendMessage(chatId, 0, null, replyMarkup, content), defaultHandler); } public static void main(String[] args) throws InterruptedException { @@ -418,6 +429,17 @@ public final class Example { } break; } + case TdApi.UpdateChatChatList.CONSTRUCTOR: { + TdApi.UpdateChatChatList updateChat = (TdApi.UpdateChatChatList) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (mainChatList) { // to not change Chat.chatList while mainChatList is locked + synchronized (chat) { + assert chat.order == 0; // guaranteed by TDLib + chat.chatList = updateChat.chatList; + } + } + break; + } case TdApi.UpdateChatLastMessage.CONSTRUCTOR: { TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object; TdApi.Chat chat = chats.get(updateChat.chatId); diff --git a/example/java/td_jni.cpp b/example/java/td_jni.cpp index 913fb540..34ad9c83 100644 --- a/example/java/td_jni.cpp +++ b/example/java/td_jni.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/python/tdjson_example.py b/example/python/tdjson_example.py index 1ff1035d..884ab547 100644 --- a/example/python/tdjson_example.py +++ b/example/python/tdjson_example.py @@ -1,6 +1,6 @@ # # Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com), -# Pellegrino Prevete (pellegrinoprevete@gmail.com) 2014-2019 +# Pellegrino Prevete (pellegrinoprevete@gmail.com) 2014-2020 # # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/swift/src/main.swift b/example/swift/src/main.swift index f66c01ba..6060981b 100644 --- a/example/swift/src/main.swift +++ b/example/swift/src/main.swift @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -127,9 +127,9 @@ func updateAuthorizationState(authorizationState: Dictionary) { client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "key":"cucumber"]) case "authorizationStateWaitPhoneNumber": - print("Enter your phone: ") - let phone = myReadLine() - client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone], f:checkAuthenticationError) + print("Enter your phone number: ") + let phone_number = myReadLine() + client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone_number], f:checkAuthenticationError) case "authorizationStateWaitCode": var code: String = "" @@ -154,8 +154,17 @@ func updateAuthorizationState(authorizationState: Dictionary) { case "authorizationStateReady": () + case "authorizationStateLoggingOut": + print("Logging out...") + + case "authorizationStateClosing": + print("Closing...") + + case "authorizationStateLoggingOut": + print("Closed.") + default: - assert(false, "TODO: Unknown authorization state"); + assert(false, "TODO: Unexpected authorization state"); } } diff --git a/example/swift/src/td-Bridging-Header.h b/example/swift/src/td-Bridging-Header.h index 1ea9f20b..e3514d0b 100644 --- a/example/swift/src/td-Bridging-Header.h +++ b/example/swift/src/td-Bridging-Header.h @@ -1,11 +1,11 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/uwp/app/App.xaml.cs b/example/uwp/app/App.xaml.cs index 5fc641f8..52746ca8 100644 --- a/example/uwp/app/App.xaml.cs +++ b/example/uwp/app/App.xaml.cs @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/uwp/app/MainPage.xaml.cs b/example/uwp/app/MainPage.xaml.cs index 4a8d85c9..fb581357 100644 --- a/example/uwp/app/MainPage.xaml.cs +++ b/example/uwp/app/MainPage.xaml.cs @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/example/uwp/app/Properties/AssemblyInfo.cs b/example/uwp/app/Properties/AssemblyInfo.cs index 2941159b..c8708286 100644 --- a/example/uwp/app/Properties/AssemblyInfo.cs +++ b/example/uwp/app/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("App2")] -[assembly: AssemblyCopyright("Copyright © 2015-2019")] +[assembly: AssemblyCopyright("Copyright © 2015-2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/example/uwp/app/TdApp.csproj b/example/uwp/app/TdApp.csproj index b3d1d944..2b0de8df 100644 --- a/example/uwp/app/TdApp.csproj +++ b/example/uwp/app/TdApp.csproj @@ -88,7 +88,7 @@ true - + PreserveNewest diff --git a/example/uwp/extension.vsixmanifest b/example/uwp/extension.vsixmanifest index ec64da5b..97531c32 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/build-openssl.sh b/example/web/build-openssl.sh index ec339d54..0766399f 100755 --- a/example/web/build-openssl.sh +++ b/example/web/build-openssl.sh @@ -1,6 +1,6 @@ #!/bin/sh -emconfigure 2> /dev/null || { echo 'emconfigure not found. Install emsdk and add emconfigure and emmake to PATH environment variable. See instruction at https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. Do not forget to add `emconfigure` and `emmake` to the PATH environment variable via `emsdk/emsdk_env.sh` script.'; exit 1; } +emconfigure true 2> /dev/null || { echo 'emconfigure not found. Install emsdk and add emconfigure and emmake to PATH environment variable. See instruction at https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. Do not forget to add `emconfigure` and `emmake` to the PATH environment variable via `emsdk/emsdk_env.sh` script.'; exit 1; } OPENSSL=OpenSSL_1_1_0j if [ ! -f $OPENSSL.tar.gz ]; then @@ -12,7 +12,7 @@ echo "Unpacking OpenSSL sources..." tar xzf $OPENSSL.tar.gz || exit 1 cd openssl-$OPENSSL -emconfigure ./Configure linux-generic32 no-shared no-dso no-engine no-unit-test no-ui || exit 1 +emconfigure ./Configure linux-generic32 no-shared no-threads no-dso no-engine no-unit-test no-ui || exit 1 sed -i.bak 's/CROSS_COMPILE=.*/CROSS_COMPILE=/g' Makefile || exit 1 sed -i.bak 's/-ldl //g' Makefile || exit 1 sed -i.bak 's/-O3/-Os/g' Makefile || exit 1 diff --git a/example/web/build-tdlib.sh b/example/web/build-tdlib.sh index cd83361c..6ab6c345 100755 --- a/example/web/build-tdlib.sh +++ b/example/web/build-tdlib.sh @@ -1,6 +1,6 @@ #!/bin/sh -emconfigure 2> /dev/null || { echo 'emconfigure not found. Install emsdk and add emconfigure and emmake to PATH environment variable. See instruction at https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. Do not forget to add `emconfigure` and `emmake` to the PATH environment variable via `emsdk/emsdk_env.sh` script.'; exit 1; } +emconfigure true 2> /dev/null || { echo 'emconfigure not found. Install emsdk and add emconfigure and emmake to PATH environment variable. See instruction at https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. Do not forget to add `emconfigure` and `emmake` to the PATH environment variable via `emsdk/emsdk_env.sh` script.'; exit 1; } rm -rf build/generate rm -rf build/asmjs diff --git a/example/web/tdweb/src/index.js b/example/web/tdweb/src/index.js index 183e52a4..34a02928 100644 --- a/example/web/tdweb/src/index.js +++ b/example/web/tdweb/src/index.js @@ -442,7 +442,7 @@ class FileManager { init() { this.idb = new Promise((resolve, reject) => { - const request = window.indexedDB.open(this.instanceName); + const request = indexedDB.open(this.instanceName); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); diff --git a/memprof/memprof.cpp b/memprof/memprof.cpp index 7ce971f4..dc0bac7f 100644 --- a/memprof/memprof.cpp +++ b/memprof/memprof.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/memprof/memprof.h b/memprof/memprof.h index acf524d5..dba2b760 100644 --- a/memprof/memprof.h +++ b/memprof/memprof.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/DoxygenTlDocumentationGenerator.php b/td/generate/DoxygenTlDocumentationGenerator.php index 5951334a..afe94559 100644 --- a/td/generate/DoxygenTlDocumentationGenerator.php +++ b/td/generate/DoxygenTlDocumentationGenerator.php @@ -38,6 +38,11 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator protected function escapeDocumentation($doc) { $doc = htmlspecialchars($doc); + $doc = preg_replace_callback('/"((http|https|tg):\/\/[^" ]*)"/', + function ($quoted_link) + { + return """.$quoted_link[1]."""; + }, $doc); $doc = str_replace('*/', '*/', $doc); $doc = str_replace('#', '\#', $doc); return $doc; @@ -181,8 +186,8 @@ EOT * \\code * auto get_authorization_state_request = td::td_api::make_object(); * auto message_text = td::td_api::make_object("Hello, world!!!", - * std::vector>()); - * auto send_message_request = td::td_api::make_object(chat_id, 0, false, false, nullptr, + * std::vector>()); + * auto send_message_request = td::td_api::make_object(chat_id, 0, nullptr, nullptr, * td::td_api::make_object(std::move(message_text), false, true)); * \\endcode * diff --git a/td/generate/generate_c.cpp b/td/generate/generate_c.cpp index dad6290e..3dbee73b 100644 --- a/td/generate/generate_c.cpp +++ b/td/generate/generate_c.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/generate_common.cpp b/td/generate/generate_common.cpp index 7c70a081..5d1b6f8d 100644 --- a/td/generate/generate_common.cpp +++ b/td/generate/generate_common.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/generate_dotnet.cpp b/td/generate/generate_dotnet.cpp index 882c760e..916affd4 100644 --- a/td/generate/generate_dotnet.cpp +++ b/td/generate/generate_dotnet.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/generate_java.cpp b/td/generate/generate_java.cpp index c1b1863d..ecfa5a0f 100644 --- a/td/generate/generate_java.cpp +++ b/td/generate/generate_java.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/generate_json.cpp b/td/generate/generate_json.cpp index d8d64eeb..5c7783ca 100644 --- a/td/generate/generate_json.cpp +++ b/td/generate/generate_json.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/remove_documentation.cpp b/td/generate/remove_documentation.cpp index c0bdee59..47a57328 100644 --- a/td/generate/remove_documentation.cpp +++ b/td/generate/remove_documentation.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/scheme/secret_api.tl b/td/generate/scheme/secret_api.tl index 32db7251..74d55329 100644 --- a/td/generate/scheme/secret_api.tl +++ b/td/generate/scheme/secret_api.tl @@ -110,6 +110,12 @@ documentAttributeVideo66#ef02ce6 flags:# round_message:flags.0?true duration:int decryptedMessage#91cc4674 flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage; +// layer 101 + +messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity; +messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity; +messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity; + ---functions--- test.dummyFunction = Bool; diff --git a/td/generate/scheme/secret_api.tlo b/td/generate/scheme/secret_api.tlo index d6be2161..5a6e4ad2 100644 Binary files a/td/generate/scheme/secret_api.tlo and b/td/generate/scheme/secret_api.tlo differ diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 52ef4b79..3406f656 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -69,7 +69,8 @@ textEntity offset:int32 length:int32 type:TextEntityType = TextEntity; //@description Contains a list of text entities @entities List of text entities textEntities entities:vector = TextEntities; -//@description A text with some entities @text The text @entities Entities contained in the text +//@description A text with some entities @text The text @entities Entities contained in the text. Entities can be nested, but must not mutually intersect each other. +//-Pre, Code and PreCode entities can't contain other entities. Bold, Italic, Underline and Strikethrough entities can contain and to be contained in any other entities. All other entities can't contain each other formattedText text:string entities:vector = FormattedText; @@ -85,12 +86,15 @@ authorizationStateWaitTdlibParameters = AuthorizationState; //@description TDLib needs an encryption key to decrypt the local database @is_encrypted True, if the database is currently encrypted authorizationStateWaitEncryptionKey is_encrypted:Bool = AuthorizationState; -//@description TDLib needs the user's phone number to authorize +//@description TDLib needs the user's phone number to authorize. Call `setAuthenticationPhoneNumber` to provide the phone number, or use `requestQrCodeAuthentication`, or `checkAuthenticationBotToken` for other authentication options authorizationStateWaitPhoneNumber = AuthorizationState; //@description TDLib needs the user's authentication code to authorize @code_info Information about the authorization code that was sent authorizationStateWaitCode code_info:authenticationCodeInfo = AuthorizationState; +//@description The user needs to confirm authorization on another logged in device by scanning a QR code with the provided link @link A tg:// URL for the QR code. The link will be updated frequently +authorizationStateWaitOtherDeviceConfirmation link:string = AuthorizationState; + //@description The user is unregistered and need to accept terms of service and enter their first name and last name to finish registration @terms_of_service Telegram terms of service authorizationStateWaitRegistration terms_of_service:termsOfService = AuthorizationState; @@ -137,12 +141,14 @@ temporaryPasswordState has_password:Bool valid_for:int32 = TemporaryPasswordStat localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_active:Bool is_downloading_completed:Bool download_offset:int32 downloaded_prefix_size:int32 downloaded_size:int32 = LocalFile; //@description Represents a remote file -//@id Remote file identifier; may be empty. Can be used across application restarts or even from other devices for the current user. If the ID starts with "http://" or "https://", it represents the HTTP URL of the file. TDLib is currently unable to download files if only their URL is known. +//@id Remote file identifier; may be empty. Can be used across application restarts or even from other devices for the current user. Uniquely identifies a file, but a file can have a lot of different valid identifiers. +//-If the ID starts with "http://" or "https://", it represents the HTTP URL of the file. TDLib is currently unable to download files if only their URL is known. //-If downloadFile is called on such a file or if it is sent to a secret chat, TDLib starts a file generation process by sending updateFileGenerationStart to the client with the HTTP URL in the original_path and "#url#" as the conversion string. Clients should generate the file by downloading it to the specified location +//@unique_id Unique file identifier; may be empty if unknown. The unique file identifier which is the same for the same file even for different users and is persistent over time //@is_uploading_active True, if the file is currently being uploaded (or a remote copy is being generated by some other means) //@is_uploading_completed True, if a remote copy is fully available //@uploaded_size Size of the remote available part of the file; 0 if unknown -remoteFile id:string is_uploading_active:Bool is_uploading_completed:Bool uploaded_size:int32 = RemoteFile; +remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_completed:Bool uploaded_size:int32 = RemoteFile; //@description Represents a file //@id Unique file identifier @@ -158,7 +164,7 @@ file id:int32 size:int32 expected_size:int32 local:localFile remote:remoteFile = //@description A file defined by its unique ID @id Unique file identifier inputFileId id:int32 = InputFile; -//@description A file defined by its remote ID. The remote ID is guaranteed to work only if it was received after TDLib launch and the corresponding file is still accessible to the user. +//@description A file defined by its remote ID. The remote ID is guaranteed to be usable only if the corresponding file is still accessible to the user and known to TDLib. //-For example, if the file is from a message, then the message must be not deleted and accessible to the user. If a file database is disabled, then the corresponding object with the file must be preloaded by the client //@id Remote file identifier inputFileRemote id:string = InputFile; @@ -263,24 +269,12 @@ profilePhoto id:int64 small:file big:file = ProfilePhoto; chatPhoto small:file big:file = ChatPhoto; -//@class LinkState @description Represents the relationship between user A and user B. For incoming_link, user A is the current user; for outgoing_link, user B is the current user - -//@description The phone number of user A is not known to user B -linkStateNone = LinkState; - -//@description The phone number of user A is known but that number has not been saved to the contact list of user B -linkStateKnowsPhoneNumber = LinkState; - -//@description The phone number of user A has been saved to the contact list of user B -linkStateIsContact = LinkState; - - //@class UserType @description Represents the type of the user. The following types are possible: regular users, deleted users and bots //@description A regular user userTypeRegular = UserType; -//@description A deleted user or deleted bot. No information on the user besides the user_id is available. It is not possible to perform any active actions on this type of user +//@description A deleted user or deleted bot. No information on the user besides the user identifier is available. It is not possible to perform any active actions on this type of user userTypeDeleted = UserType; //@description A bot (see https://core.telegram.org/bots) @can_join_groups True, if the bot can be invited to basic group and supergroup chats @@ -288,7 +282,7 @@ userTypeDeleted = UserType; //@is_inline True, if the bot supports inline queries @inline_query_placeholder Placeholder for inline queries (displayed on the client input field) @need_location True, if the location of the user should be sent with every inline query to this bot userTypeBot can_join_groups:Bool can_read_all_group_messages:Bool is_inline:Bool inline_query_placeholder:string need_location:Bool = UserType; -//@description No information on the user besides the user_id is available, yet this user has not been deleted. This object is extremely rare and must be handled like a deleted user. It is not possible to perform any actions on users of this type +//@description No information on the user besides the user identifier is available, yet this user has not been deleted. This object is extremely rare and must be handled like a deleted user. It is not possible to perform any actions on users of this type userTypeUnknown = UserType; @@ -299,19 +293,25 @@ botCommand command:string description:string = BotCommand; botInfo description:string commands:vector = BotInfo; +//@description Represents a location of a chat @location The location @address Location address; 1-64 characters, as defined by the chat owner +chatLocation location:location address:string = ChatLocation; + + //@description Represents a user @id User identifier @first_name First name of the user @last_name Last name of the user @username Username of the user //@phone_number Phone number of the user @status Current online status of the user @profile_photo Profile photo of the user; may be null -//@outgoing_link Relationship from the current user to the other user @incoming_link Relationship from the other user to the current user +//@is_contact The user is a contact of the current user +//@is_mutual_contact The user is a contact of the current user and the current user is a contact of the user //@is_verified True, if the user is verified @is_support True, if the user is Telegram support account -//@restriction_reason If non-empty, it contains the reason why access to this user must be restricted. The format of the string is "{type}: {description}". -//-{type} contains the type of the restriction and at least one of the suffixes "-all", "-ios", "-android", or "-wp", which describe the platforms on which access should be restricted. (For example, "terms-ios-android". {description} contains a human-readable description of the restriction, which can be shown to the user) +//@restriction_reason If non-empty, it contains a human-readable description of the reason why access to this user must be restricted //@is_scam True, if many users reported this user as a scam //@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. It can't be passed to any method except GetUser @type Type of the user @language_code IETF language tag of the user's language; only available to bots -user id:int32 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto outgoing_link:LinkState incoming_link:LinkState is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool have_access:Bool type:UserType language_code:string = User; +user id:int32 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool have_access:Bool type:UserType language_code:string = User; -//@description Contains full information about a user (except the full list of profile photos) @is_blocked True, if the user is blacklisted by the current user @can_be_called True, if the user can be called @has_private_calls True, if the user can't be called due to their privacy settings +//@description Contains full information about a user (except the full list of profile photos) @is_blocked True, if the user is blacklisted by the current user +//@can_be_called True, if the user can be called @has_private_calls True, if the user can't be called due to their privacy settings +//@need_phone_number_privacy_exception True, if the current user needs to explicitly allow to share their phone number with the user when the method addContact is used //@bio A short user bio @share_text For bots, the text that is included with the link when users share the bot @group_in_common_count Number of group chats where both the other user and the current user are a member; 0 for the current user @bot_info If the user is a bot, information about the bot; may be null -userFullInfo is_blocked:Bool can_be_called:Bool has_private_calls:Bool bio:string share_text:string group_in_common_count:int32 bot_info:botInfo = UserFullInfo; +userFullInfo is_blocked:Bool can_be_called:Bool has_private_calls:Bool need_phone_number_privacy_exception:Bool bio:string share_text:string group_in_common_count:int32 bot_info:botInfo = UserFullInfo; //@description Contains full information about a user profile photo @id Unique user profile photo identifier @added_date Point in time (Unix timestamp) when the photo has been added @sizes Available variants of the user photo, in different sizes userProfilePhoto id:int64 added_date:int32 sizes:vector = UserProfilePhoto; @@ -323,6 +323,13 @@ userProfilePhotos total_count:int32 photos:vector = UserProfil users total_count:int32 user_ids:vector = Users; +//@description Contains information about a chat administrator @user_id User identifier of the administrator @custom_title Custom title of the administrator @is_owner True, if the user is the owner of the chat +chatAdministrator user_id:int32 custom_title:string is_owner:Bool = ChatAdministrator; + +//@description Represents a list of chat administrators @administrators A list of chat administrators +chatAdministrators administrators:vector = ChatAdministrators; + + //@description Describes actions that a user is allowed to take in a chat //@can_send_messages True, if the user can send text messages, contacts, locations, and venues //@can_send_media_messages True, if the user can send audio files, documents, photos, videos, video notes, and voice notes. Implies can_send_messages permissions @@ -337,10 +344,13 @@ chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_pol //@class ChatMemberStatus @description Provides information about the status of a member in a chat -//@description The user is the creator of a chat and has all the administrator privileges @is_member True, if the user is a member of the chat -chatMemberStatusCreator is_member:Bool = ChatMemberStatus; +//@description The user is the owner of a chat and has all the administrator privileges +//@custom_title A custom title of the owner; 0-16 characters without emojis; applicable to supergroups only +//@is_member True, if the user is a member of the chat +chatMemberStatusCreator custom_title:string is_member:Bool = ChatMemberStatus; //@description The user is a member of a chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, and ban unprivileged members. In supergroups and channels, there are more detailed options for administrator privileges +//@custom_title A custom title of the administrator; 0-16 characters without emojis; applicable to supergroups only //@can_be_edited True, if the current user can edit the administrator privileges for the called user //@can_change_info True, if the administrator can change the chat title, photo, and other settings //@can_post_messages True, if the administrator can create channel posts; applicable to channels only @@ -349,8 +359,8 @@ chatMemberStatusCreator is_member:Bool = ChatMemberStatus; //@can_invite_users True, if the administrator can invite new users to the chat //@can_restrict_members True, if the administrator can restrict, ban, or unban chat members //@can_pin_messages True, if the administrator can pin messages; applicable to groups only -//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by him -chatMemberStatusAdministrator can_be_edited:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool = ChatMemberStatus; +//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them +chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool = ChatMemberStatus; //@description The user is a member of a chat, without any additional privileges or restrictions chatMemberStatusMember = ChatMemberStatus; @@ -382,7 +392,7 @@ chatMembers total_count:int32 members:vector = ChatMembers; //@description Returns contacts of the user chatMembersFilterContacts = ChatMembersFilter; -//@description Returns the creator and administrators +//@description Returns the owner and administrators chatMembersFilterAdministrators = ChatMembersFilter; //@description Returns all chat members, including restricted chat members @@ -406,7 +416,7 @@ supergroupMembersFilterRecent = SupergroupMembersFilter; //@description Returns contacts of the user, which are members of the supergroup or channel @query Query to search for supergroupMembersFilterContacts query:string = SupergroupMembersFilter; -//@description Returns the creator and administrators +//@description Returns the owner and administrators supergroupMembersFilterAdministrators = SupergroupMembersFilter; //@description Used to search for supergroup or channel members via a (string) query @query Query to search for @@ -430,7 +440,7 @@ supergroupMembersFilterBots = SupergroupMembersFilter; //@upgraded_to_supergroup_id Identifier of the supergroup to which this group was upgraded; 0 if none basicGroup id:int32 member_count:int32 status:ChatMemberStatus is_active:Bool upgraded_to_supergroup_id:int32 = BasicGroup; -//@description Contains full information about a basic group @param_description Group description @creator_user_id User identifier of the creator of the group; 0 if unknown @members Group members @invite_link Invite link for this group; available only for the group creator and only after it has been generated at least once +//@description Contains full information about a basic group @param_description Group description @creator_user_id User identifier of the creator of the group; 0 if unknown @members Group members @invite_link Invite link for this group; available only after it has been generated at least once and only for the group creator basicGroupFullInfo description:string creator_user_id:int32 members:vector invite_link:string = BasicGroupFullInfo; @@ -438,15 +448,17 @@ basicGroupFullInfo description:string creator_user_id:int32 members:vector= 66 +//@layer Secret chat layer; determines features supported by the other client. Video notes are supported if the layer >= 66; nested text entities and underline and strikethrough entities are supported if the layer >= 101 secretChat id:int32 user_id:int32 state:SecretChatState is_outbound:Bool ttl:int32 key_hash:bytes layer:int32 = SecretChat; @@ -508,8 +525,8 @@ messageForwardOriginChannel chat_id:int53 message_id:int53 author_signature:stri //@description Contains information about a forwarded message //@origin Origin of a forwarded message //@date Point in time (Unix timestamp) when the message was originally sent -//@from_chat_id For messages forwarded to the chat with the current user (saved messages) or to the channel discussion supergroup, the identifier of the chat from which the message was forwarded last time; 0 if unknown -//@from_message_id For messages forwarded to the chat with the current user (saved messages) or to the channel discussion supergroup, the identifier of the original message from which the new message was forwarded last time; 0 if unknown +//@from_chat_id For messages forwarded to the chat with the current user (Saved Messages) or to the channel's discussion group, the identifier of the chat from which the message was forwarded last time; 0 if unknown +//@from_message_id For messages forwarded to the chat with the current user (Saved Messages) or to the channel's discussion group, the identifier of the original message from which the new message was forwarded last time; 0 if unknown messageForwardInfo origin:MessageForwardOrigin date:int32 from_chat_id:int53 from_message_id:int53 = MessageForwardInfo; @@ -528,8 +545,9 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r //@sender_user_id Identifier of the user who sent the message; 0 if unknown. Currently, it is unknown for channel posts and for channel posts automatically forwarded to discussion group //@chat_id Chat identifier //@sending_state Information about the sending state of the message; may be null +//@scheduling_state Information about the scheduling state of the message; may be null //@is_outgoing True, if the message is outgoing -//@can_be_edited True, if the message can be edited. For live location and poll messages this fields shows, whether editMessageLiveLocation or stopPoll can be used with this message by the client +//@can_be_edited True, if the message can be edited. For live location and poll messages this fields shows whether editMessageLiveLocation or stopPoll can be used with this message by the client //@can_be_forwarded True, if the message can be forwarded //@can_be_deleted_only_for_self True, if the message can be deleted only for the current user while other users will continue to see it //@can_be_deleted_for_all_users True, if the message can be deleted for all users @@ -545,9 +563,10 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r //@author_signature For channel posts, optional author signature //@views Number of times this message was viewed //@media_album_id Unique identifier of an album this message belongs to. Only photos and videos can be grouped together in albums +//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted //@content Content of the message //@reply_markup Reply markup for the message; may be null -message id:int53 sender_user_id:int32 chat_id:int53 sending_state:MessageSendingState is_outgoing:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo reply_to_message_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string views:int32 media_album_id:int64 content:MessageContent reply_markup:ReplyMarkup = Message; +message id:int53 sender_user_id:int32 chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo reply_to_message_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string views:int32 media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message; //@description Contains a list of messages @total_count Approximate total count of messages found @messages List of messages; messages may be null messages total_count:int32 messages:vector = Messages; @@ -604,9 +623,19 @@ chatTypeSupergroup supergroup_id:int32 is_channel:Bool = ChatType; chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType; +//@class ChatList @description Describes a list of chats + +//@description A main list of chats +chatListMain = ChatList; + +//@description A list of chats usually located at the top of the main chat list. Unmuted chats are automatically moved from the Archive to the Main chat list when a new message arrives +chatListArchive = ChatList; + + //@description A chat. (Can be a private chat, basic group, supergroup, or secret chat) //@id Chat unique identifier //@type Type of the chat +//@chat_list A chat list to which the chat belongs; may be null //@title Chat title //@photo Chat photo; may be null //@permissions Actions that non-administrator chat members are allowed to take in the chat @@ -615,6 +644,7 @@ chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType; //@is_pinned True, if the chat is pinned //@is_marked_as_unread True, if the chat is marked as unread //@is_sponsored True, if the chat is sponsored by the user's MTProxy server +//@has_scheduled_messages True, if the chat has scheduled messages //@can_be_deleted_only_for_self True, if the chat messages can be deleted only for the current user while other users will continue to see the messages //@can_be_deleted_for_all_users True, if the chat messages can be deleted for all users //@can_be_reported True, if the chat can be reported to Telegram moderators through reportChat @@ -624,16 +654,24 @@ chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType; //@last_read_outbox_message_id Identifier of the last read outgoing message //@unread_mention_count Number of unread messages with a mention/reply in the chat //@notification_settings Notification settings for this chat +//@action_bar Describes actions which should be possible to do through a chat action bar; may be null //@pinned_message_id Identifier of the pinned message in the chat; 0 if none //@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat //@draft_message A draft of a message in the chat; may be null //@client_data Contains client-specific data associated with the chat. (For example, the chat position or local chat notification settings can be stored here.) Persistent if a message database is used -chat id:int53 type:ChatType title:string photo:chatPhoto permissions:chatPermissions last_message:message order:int64 is_pinned:Bool is_marked_as_unread:Bool is_sponsored:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; +chat id:int53 type:ChatType chat_list:ChatList title:string photo:chatPhoto permissions:chatPermissions last_message:message order:int64 is_pinned:Bool is_marked_as_unread:Bool is_sponsored:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings action_bar:ChatActionBar pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat; //@description Represents a list of chats @chat_ids List of chat identifiers chats chat_ids:vector = Chats; +//@description Describes a chat located nearby @chat_id Chat identifier @distance Distance to the chat location in meters +chatNearby chat_id:int53 distance:int32 = ChatNearby; + +//@description Represents a list of chats located nearby @users_nearby List of users nearby @supergroups_nearby List of location-based supergroups nearby +chatsNearby users_nearby:vector supergroups_nearby:vector = ChatsNearby; + + //@description Contains a chat invite link @invite_link Chat invite link chatInviteLink invite_link:string = ChatInviteLink; @@ -644,10 +682,37 @@ chatInviteLink invite_link:string = ChatInviteLink; //@photo Chat photo; may be null //@member_count Number of members //@member_user_ids User identifiers of some chat members that may be known to the current user -//@is_public True, if the chat is a public supergroup or a channel with a username +//@is_public True, if the chat is a public supergroup or channel, i.e. it has a username or it is a location-based supergroup chatInviteLinkInfo chat_id:int53 type:ChatType title:string photo:chatPhoto member_count:int32 member_user_ids:vector is_public:Bool = ChatInviteLinkInfo; +//@class PublicChatType @description Describes a type of public chats + +//@description The chat is public, because it has username +publicChatTypeHasUsername = PublicChatType; + +//@description The chat is public, because it is a location-based supergroup +publicChatTypeIsLocationBased = PublicChatType; + + +//@class ChatActionBar @description Describes actions which should be possible to do through a chat action bar + +//@description The chat can be reported as spam using the method reportChat with the reason chatReportReasonSpam +chatActionBarReportSpam = ChatActionBar; + +//@description The chat is a location-based supergroup, which can be reported as having unrelated location using the method reportChat with the reason chatReportReasonUnrelatedLocation +chatActionBarReportUnrelatedLocation = ChatActionBar; + +//@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be added to the contact list using the method addContact, or the other user can be blocked using the method blockUser +chatActionBarReportAddBlock = ChatActionBar; + +//@description The chat is a private or secret chat and the other user can be added to the contact list using the method addContact +chatActionBarAddContact = ChatActionBar; + +//@description The chat is a private or secret chat with a mutual contact and the user's phone number can be shared with the other user using the method sharePhoneNumber +chatActionBarSharePhoneNumber = ChatActionBar; + + //@class KeyboardButtonType @description Describes a keyboard button type //@description A simple button, with text that should be sent when the button is pressed @@ -669,7 +734,7 @@ keyboardButton text:string type:KeyboardButtonType = KeyboardButton; //@description A button that opens a specified URL @url HTTP or tg:// URL to open inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType; -//@description A button that opens a specified URL and automatically logs in in current user if they allowed to do that @url HTTP URL to open @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages +//@description A button that opens a specified URL and automatically logs in in current user if they allowed to do that @url An HTTP URL to open @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages inlineKeyboardButtonTypeLoginUrl url:string id:int32 forward_text:string = InlineKeyboardButtonType; //@description A button that sends a special callback query to a bot @data Data to be sent to the bot via a callback query @@ -711,6 +776,16 @@ replyMarkupShowKeyboard rows:vector> resize_keyboard:Bool replyMarkupInlineKeyboard rows:vector> = ReplyMarkup; +//@class LoginUrlInfo @description Contains information about an inline button of type inlineKeyboardButtonTypeLoginUrl + +//@description An HTTP url needs to be open @url The URL to open @skip_confirm True, if there is no need to show an ordinary open URL confirm +loginUrlInfoOpen url:string skip_confirm:Bool = LoginUrlInfo; + +//@description An authorization confirmation dialog needs to be shown to the user @url An HTTP URL to be opened @domain A domain of the URL +//@bot_user_id User identifier of a bot linked with the website @request_write_access True, if the user needs to be requested to give the permission to the bot to send them messages +loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int32 request_write_access:Bool = LoginUrlInfo; + + //@class RichText @description Describes a text object inside an instant-view web page //@description A plain text @text Text @@ -725,7 +800,7 @@ richTextItalic text:RichText = RichText; //@description An underlined rich text @text Text richTextUnderline text:RichText = RichText; -//@description A strike-through rich text @text Text +//@description A strikethrough rich text @text Text richTextStrikethrough text:RichText = RichText; //@description A fixed-width rich text @text Text @@ -1370,12 +1445,21 @@ textEntityTypeUrl = TextEntityType; //@description An email address textEntityTypeEmailAddress = TextEntityType; +//@description A phone number +textEntityTypePhoneNumber = TextEntityType; + //@description A bold text textEntityTypeBold = TextEntityType; //@description An italic text textEntityTypeItalic = TextEntityType; +//@description An underlined text +textEntityTypeUnderline = TextEntityType; + +//@description A strikethrough text +textEntityTypeStrikethrough = TextEntityType; + //@description Text that must be formatted as if inside a code HTML tag textEntityTypeCode = TextEntityType; @@ -1391,18 +1475,31 @@ textEntityTypeTextUrl url:string = TextEntityType; //@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user textEntityTypeMentionName user_id:int32 = TextEntityType; -//@description A phone number -textEntityTypePhoneNumber = 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 //@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; +//@class MessageSchedulingState @description Contains information about the time when a scheduled message will be sent + +//@description The message will be sent at the specified date @send_date Date the message will be sent. The date must be within 367 days in the future +messageSchedulingStateSendAtDate send_date:int32 = MessageSchedulingState; + +//@description The message will be sent when the peer will be online. Applicable to private chats only and when the exact online status of the peer is known +messageSchedulingStateSendWhenOnline = MessageSchedulingState; + + +//@description Options to be used when a message is send +//@disable_notification Pass true to disable notification for the message. Must be false if the message is sent to a secret chat +//@from_background Pass true if the message is sent from the background +//@scheduling_state Message scheduling state. Messages sent to a secret chat, live location messages and self-destructing messages can't be scheduled +sendMessageOptions disable_notification:Bool from_background:Bool scheduling_state:MessageSchedulingState = SendMessageOptions; + + //@class InputMessageContent @description The content of a message to send -//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Code, Pre, PreCode and TextUrl entities are allowed to be specified manually +//@description A text message @text Formatted text to be sent; 1-GetOption("message_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Code, Pre, PreCode, TextUrl and MentionName entities are allowed to be specified manually //@disable_web_page_preview True, if rich web page previews for URLs in the message text should be disabled @clear_draft True, if a chat message draft should be deleted inputMessageText text:formattedText disable_web_page_preview:Bool clear_draft:Bool = InputMessageContent; @@ -1735,8 +1832,8 @@ inputInlineQueryResultLocation id:string location:location live_period:int32 tit //@input_message_content The content of the message to be sent. Must be one of the following types: InputMessageText, InputMessagePhoto, InputMessageLocation, InputMessageVenue or InputMessageContact inputInlineQueryResultPhoto id:string title:string description:string thumbnail_url:string photo_url:string photo_width:int32 photo_height:int32 reply_markup:ReplyMarkup input_message_content:InputMessageContent = InputInlineQueryResult; -//@description Represents a link to a WEBP or a TGS sticker @id Unique identifier of the query result @thumbnail_url URL of the sticker thumbnail, if it exists -//@sticker_url The URL of the WEBP or a TGS sticker (sticker file size must not exceed 5MB) @sticker_width Width of the sticker @sticker_height Height of the sticker +//@description Represents a link to a WEBP or TGS sticker @id Unique identifier of the query result @thumbnail_url URL of the sticker thumbnail, if it exists +//@sticker_url The URL of the WEBP or TGS sticker (sticker file size must not exceed 5MB) @sticker_width Width of the sticker @sticker_height Height of the sticker //@reply_markup The message reply markup. Must be of type replyMarkupInlineKeyboard or null //@input_message_content The content of the message to be sent. Must be one of the following types: InputMessageText, inputMessageSticker, InputMessageLocation, InputMessageVenue or InputMessageContact inputInlineQueryResultSticker id:string thumbnail_url:string sticker_url:string sticker_width:int32 sticker_height:int32 reply_markup:ReplyMarkup input_message_content:InputMessageContent = InputInlineQueryResult; @@ -1886,12 +1983,21 @@ chatEventPhotoChanged old_photo:photo new_photo:photo = ChatEventAction; //@description The can_invite_users permission of a supergroup chat was toggled @can_invite_users New value of can_invite_users permission chatEventInvitesToggled can_invite_users:Bool = ChatEventAction; +//@description The linked chat of a supergroup was changed @old_linked_chat_id Previous supergroup linked chat identifier @new_linked_chat_id New supergroup linked chat identifier +chatEventLinkedChatChanged old_linked_chat_id:int53 new_linked_chat_id:int53 = ChatEventAction; + +//@description The slow_mode_delay setting of a supergroup was changed @old_slow_mode_delay Previous value of slow_mode_delay @new_slow_mode_delay New value of slow_mode_delay +chatEventSlowModeDelayChanged old_slow_mode_delay:int32 new_slow_mode_delay:int32 = ChatEventAction; + //@description The sign_messages setting of a channel was toggled @sign_messages New value of sign_messages chatEventSignMessagesToggled sign_messages:Bool = ChatEventAction; //@description The supergroup sticker set was changed @old_sticker_set_id Previous identifier of the chat sticker set; 0 if none @new_sticker_set_id New identifier of the chat sticker set; 0 if none chatEventStickerSetChanged old_sticker_set_id:int64 new_sticker_set_id:int64 = ChatEventAction; +//@description The supergroup location was changed @old_location Previous location; may be null @new_location New location; may be null +chatEventLocationChanged old_location:chatLocation new_location:chatLocation = ChatEventAction; + //@description The is_all_history_available setting of a supergroup was toggled @is_all_history_available New value of is_all_history_available chatEventIsAllHistoryAvailableToggled is_all_history_available:Bool = ChatEventAction; @@ -1990,21 +2096,31 @@ deviceTokenTizenPush reg_id:string = DeviceToken; pushReceiverId id:int64 = PushReceiverId; +//@class BackgroundFill @description Describes a fill of a background + +//@description Describes a solid fill of a background @color A color of the background in the RGB24 format +backgroundFillSolid color:int32 = BackgroundFill; + +//@description Describes a gradient fill of a background @top_color A top color of the background in the RGB24 format @bottom_color A bottom color of the background in the RGB24 format +//@rotation_angle Clockwise rotation angle of the gradient, in degrees; 0-359. Should be always divisible by 45 +backgroundFillGradient top_color:int32 bottom_color:int32 rotation_angle:int32 = BackgroundFill; + + //@class BackgroundType @description Describes a type of a background //@description A wallpaper in JPEG format //@is_blurred True, if the wallpaper must be downscaled to fit in 450x450 square and then box-blurred with radius 12 -//@is_moving True, if the background needs to be slightly moved when device is rotated +//@is_moving True, if the background needs to be slightly moved when device is tilted backgroundTypeWallpaper is_blurred:Bool is_moving:Bool = BackgroundType; -//@description A PNG pattern to be combined with the color chosen by the user -//@is_moving True, if the background needs to be slightly moved when device is rotated -//@color Main color of the background in RGB24 format -//@intensity Intensity of the pattern when it is shown above the main background color, 0-100 -backgroundTypePattern is_moving:Bool color:int32 intensity:int32 = BackgroundType; +//@description A PNG or TGV (gzipped subset of SVG with mime-type "application/x-tgwallpattern") pattern to be combined with the background fill chosen by the user +//@fill Description of the background fill +//@intensity Intensity of the pattern when it is shown above the filled background, 0-100 +//@is_moving True, if the background needs to be slightly moved when device is tilted +backgroundTypePattern fill:BackgroundFill intensity:int32 is_moving:Bool = BackgroundType; -//@description A solid background @color A color of the background in RGB24 format -backgroundTypeSolid color:int32 = BackgroundType; +//@description A filled background @fill Description of the background fill +backgroundTypeFill fill:BackgroundFill = BackgroundType; //@description Describes a chat background @@ -2012,7 +2128,7 @@ backgroundTypeSolid color:int32 = BackgroundType; //@is_default True, if this is one of default backgrounds //@is_dark True, if the background is dark and is recommended to be used with dark theme //@name Unique background name -//@document Document with the background; may be null. Null only for solid backgrounds +//@document Document with the background; may be null. Null only for filled backgrounds //@type Type of the background background id:int64 is_default:Bool is_dark:Bool name:string document:document type:BackgroundType = Background; @@ -2034,6 +2150,21 @@ inputBackgroundRemote background_id:int64 = InputBackground; hashtags hashtags:vector = Hashtags; +//@class CanTransferOwnershipResult @description Represents result of checking whether the current session can be used to transfer a chat ownership to another user + +//@description The session can be used +canTransferOwnershipResultOk = CanTransferOwnershipResult; + +//@description The 2-step verification needs to be enabled first +canTransferOwnershipResultPasswordNeeded = CanTransferOwnershipResult; + +//@description The 2-step verification was enabled recently, user needs to wait @retry_after Time left before the session can be used to transfer ownership of a chat, in seconds +canTransferOwnershipResultPasswordTooFresh retry_after:int32 = CanTransferOwnershipResult; + +//@description The session was created recently, user needs to wait @retry_after Time left before the session can be used to transfer ownership of a chat, in seconds +canTransferOwnershipResultSessionTooFresh retry_after:int32 = CanTransferOwnershipResult; + + //@class CheckChatUsernameResult @description Represents result of checking whether a username can be set for a chat //@description The username can be set @@ -2112,7 +2243,7 @@ pushMessageContentVoiceNote voice_note:voiceNote is_pinned:Bool = PushMessageCon pushMessageContentBasicGroupChatCreate = PushMessageContent; //@description New chat members were invited to a group @member_name Name of the added member @is_current_user True, if the current user was added to the group -//@is_returned True, if the user has returned to the group himself +//@is_returned True, if the user has returned to the group themself pushMessageContentChatAddMembers member_name:string is_current_user:Bool is_returned:Bool = PushMessageContent; //@description A chat photo was edited @@ -2122,7 +2253,7 @@ pushMessageContentChatChangePhoto = PushMessageContent; pushMessageContentChatChangeTitle title:string = PushMessageContent; //@description A chat member was deleted @member_name Name of the deleted member @is_current_user True, if the current user was deleted from the group -//@is_left True, if the user has left the group himself +//@is_left True, if the user has left the group themself pushMessageContentChatDeleteMember member_name:string is_current_user:Bool is_left:Bool = PushMessageContent; //@description A new member joined the chat by invite link @@ -2224,18 +2355,24 @@ userPrivacySettingRuleAllowAll = UserPrivacySettingRule; //@description A rule to allow all of a user's contacts to do something userPrivacySettingRuleAllowContacts = UserPrivacySettingRule; -//@description A rule to allow certain specified users to do something @user_ids The user identifiers +//@description A rule to allow certain specified users to do something @user_ids The user identifiers, total number of users in all rules must not exceed 1000 userPrivacySettingRuleAllowUsers user_ids:vector = UserPrivacySettingRule; +//@description A rule to allow all members of certain specified basic groups and supergroups to doing something @chat_ids The chat identifiers, total number of chats in all rules must not exceed 20 +userPrivacySettingRuleAllowChatMembers chat_ids:vector = UserPrivacySettingRule; + //@description A rule to restrict all users from doing something userPrivacySettingRuleRestrictAll = UserPrivacySettingRule; //@description A rule to restrict all contacts of a user from doing something userPrivacySettingRuleRestrictContacts = UserPrivacySettingRule; -//@description A rule to restrict all specified users from doing something @user_ids The user identifiers +//@description A rule to restrict all specified users from doing something @user_ids The user identifiers, total number of users in all rules must not exceed 1000 userPrivacySettingRuleRestrictUsers user_ids:vector = UserPrivacySettingRule; +//@description A rule to restrict all members of specified basic groups and supergroups from doing something @chat_ids The chat identifiers, total number of chats in all rules must not exceed 20 +userPrivacySettingRuleRestrictChatMembers chat_ids:vector = UserPrivacySettingRule; + //@description A list of privacy rules. Rules are matched in the specified order. The first matched rule defines the privacy setting for a given user. If no rule matches, the action is not allowed @rules A list of rules userPrivacySettingRules rules:vector = UserPrivacySettingRules; @@ -2250,6 +2387,9 @@ userPrivacySettingShowProfilePhoto = UserPrivacySetting; //@description A privacy setting for managing whether a link to the user's account is included in forwarded messages userPrivacySettingShowLinkInForwardedMessages = UserPrivacySetting; +//@description A privacy setting for managing whether the user's phone number is visible +userPrivacySettingShowPhoneNumber = UserPrivacySetting; + //@description A privacy setting for managing whether the user can be invited to chats userPrivacySettingAllowChatInvites = UserPrivacySetting; @@ -2259,6 +2399,9 @@ userPrivacySettingAllowCalls = UserPrivacySetting; //@description A privacy setting for managing whether peer-to-peer connections can be used for calls userPrivacySettingAllowPeerToPeerCalls = UserPrivacySetting; +//@description A privacy setting for managing whether the user can be found by its phone number. Checked only if the phone number is not known to the other user. Can be set only to "Allow contacts" or "Allow all" +userPrivacySettingAllowFindingByPhoneNumber = UserPrivacySetting; + //@description Contains information about the period of inactivity after which the current user's account will automatically be deleted @days Number of days of inactivity before the account will be flagged for deletion; should range from 30-366 days accountTtl days:int32 = AccountTtl; @@ -2295,9 +2438,6 @@ connectedWebsite id:int64 domain_name:string bot_user_id:int32 browser:string pl connectedWebsites websites:vector = ConnectedWebsites; -//@description Contains information about the availability of the "Report spam" action for a chat @can_report_spam True, if a prompt with the "Report spam" action should be shown to the user -chatReportSpamState can_report_spam:Bool = ChatReportSpamState; - //@class ChatReportReason @description Describes the reason why a chat is reported //@description The chat contains spam messages @@ -2315,11 +2455,14 @@ chatReportReasonChildAbuse = ChatReportReason; //@description The chat contains copyrighted content chatReportReasonCopyright = ChatReportReason; +//@description The chat has unrelated location +chatReportReasonUnrelatedLocation = ChatReportReason; + //@description A custom reason provided by the user @text Report text chatReportReasonCustom text:string = ChatReportReason; -//@description Contains a public HTTPS link to a message in a public supergroup or channel with a username @link Message link @html HTML-code for embedding the message +//@description Contains a public HTTPS link to a message in a supergroup or channel with a username @link Message link @html HTML-code for embedding the message publicMessageLink link:string html:string = PublicMessageLink; //@description Contains information about a link to a message in a chat @@ -2440,10 +2583,11 @@ networkStatistics since_date:int32 entries:vector = Netw //@max_photo_file_size Maximum size of a photo file to be auto-downloaded //@max_video_file_size Maximum size of a video file to be auto-downloaded //@max_other_file_size Maximum size of other file types to be auto-downloaded +//@video_upload_bitrate Suggested maximum bitrate for uploaded videos //@preload_large_videos True, if the beginning of videos needs to be preloaded for instant playback //@preload_next_audio True, if the next audio track needs to be preloaded while the user is listening to an audio file //@use_less_data_for_calls True, if "use less data for calls" option needs to be enabled -autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int32 max_other_file_size:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings; +autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int32 max_other_file_size:int32 video_upload_bitrate:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings; //@description Contains auto-download settings presets for the user //@low Preset with lowest settings; supposed to be used by default when roaming @@ -2490,6 +2634,9 @@ topChatCategoryInlineBots = TopChatCategory; //@description A category containing frequently used chats used for calls topChatCategoryCalls = TopChatCategory; +//@description A category containing frequently used chats used to forward messages +topChatCategoryForwardChats = TopChatCategory; + //@class TMeUrlType @description Describes the type of a URL linking to an internal Telegram entity @@ -2529,7 +2676,8 @@ 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 -textParseModeMarkdown = TextParseMode; +//@version Version of the parser: 0 or 1 - Bot API Markdown parse mode, 2 - Bot API MarkdownV2 parse mode +textParseModeMarkdown version:int32 = TextParseMode; //@description The text should be parsed in HTML-style textParseModeHTML = TextParseMode; @@ -2592,9 +2740,16 @@ updateMessageContentOpened chat_id:int53 message_id:int53 = Update; //@description A message with an unread mention was read @chat_id Chat identifier @message_id Message identifier @unread_mention_count The new number of unread mention messages left in the chat updateMessageMentionRead chat_id:int53 message_id:int53 unread_mention_count:int32 = Update; +//@description A message with a live location was viewed. When the update is received, the client is supposed to update the live location +//@chat_id Identifier of the chat with the live location message @message_id Identifier of the message with live location +updateMessageLiveLocationViewed chat_id:int53 message_id:int53 = Update; + //@description A new chat has been loaded/created. This update is guaranteed to come before the chat identifier is returned to the client. The chat field changes will be reported through separate updates @chat The chat updateNewChat chat:chat = Update; +//@description The list to which the chat belongs was changed. This update is guaranteed to be sent only when chat.order == 0 and the current or the new chat list is null @chat_id Chat identifier @chat_list The new chat's chat list; may be null +updateChatChatList chat_id:int53 chat_list:ChatList = Update; + //@description The title of a chat was changed @chat_id Chat identifier @title The new chat title updateChatTitle chat_id:int53 title:string = Update; @@ -2607,7 +2762,7 @@ updateChatPermissions chat_id:int53 permissions:chatPermissions = Update; //@description The last message of a chat was changed. If last_message is null, then the last message in the chat became unknown. Some new unknown messages might be added to the chat in this case @chat_id Chat identifier @last_message The new last message in the chat; may be null @order New value of the chat order updateChatLastMessage chat_id:int53 last_message:message order:int64 = Update; -//@description The order of the chat in the chat list has changed. Instead of this update updateChatLastMessage, updateChatIsPinned or updateChatDraftMessage might be sent @chat_id Chat identifier @order New value of the order +//@description The order of the chat in the chat list has changed. Instead of this update updateChatLastMessage, updateChatIsPinned, updateChatDraftMessage, or updateChatIsSponsored might be sent @chat_id Chat identifier @order New value of the order updateChatOrder chat_id:int53 order:int64 = Update; //@description A chat was pinned or unpinned @chat_id Chat identifier @is_pinned New value of is_pinned @order New value of the chat order @@ -2619,6 +2774,9 @@ updateChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Update; //@description A chat's is_sponsored field has changed @chat_id Chat identifier @is_sponsored New value of is_sponsored @order New value of chat order updateChatIsSponsored chat_id:int53 is_sponsored:Bool order:int64 = Update; +//@description A chat's has_scheduled_messages field has changed @chat_id Chat identifier @has_scheduled_messages New value of has_scheduled_messages +updateChatHasScheduledMessages chat_id:int53 has_scheduled_messages:Bool = Update; + //@description The value of the default disable_notification parameter, used when a message is sent to the chat, was changed @chat_id Chat identifier @default_disable_notification The new default_disable_notification value updateChatDefaultDisableNotification chat_id:int53 default_disable_notification:Bool = Update; @@ -2637,6 +2795,9 @@ updateChatNotificationSettings chat_id:int53 notification_settings:chatNotificat //@description Notification settings for some type of chats were updated @scope Types of chats for which notification settings were updated @notification_settings The new notification settings updateScopeNotificationSettings scope:NotificationSettingsScope notification_settings:scopeNotificationSettings = Update; +//@description The chat action bar was changed @chat_id Chat identifier @action_bar The new value of the action bar; may be null +updateChatActionBar chat_id:int53 action_bar:ChatActionBar = Update; + //@description The chat pinned message was changed @chat_id Chat identifier @pinned_message_id The new identifier of the pinned message; 0 if there is no pinned message in the chat updateChatPinnedMessage chat_id:int53 pinned_message_id:int53 = Update; @@ -2666,7 +2827,7 @@ updateNotificationGroup notification_group_id:int32 type:NotificationGroupType c //@description Contains active notifications that was shown on previous application launches. This update is sent only if a message database is used. In that case it comes once before any updateNotification and updateNotificationGroup update @groups Lists of active notification groups updateActiveNotifications groups:vector = Update; -//@description Describes, whether there are some pending notification updates. Can be used to prevent application from killing, while there are some pending notifications +//@description Describes whether there are some pending notification updates. Can be used to prevent application from killing, while there are some pending notifications //@have_delayed_notifications True, if there are some delayed notification updates, which will be sent soon //@have_unreceived_notifications True, if there can be some yet unreceived notifications, which are being fetched from the server updateHavePendingNotifications have_delayed_notifications:Bool have_unreceived_notifications:Bool = Update; @@ -2727,13 +2888,16 @@ updateCall call:call = Update; //@description Some privacy setting rules have been changed @setting The privacy setting @rules New privacy rules updateUserPrivacySettingRules setting:UserPrivacySetting rules:userPrivacySettingRules = Update; -//@description Number of unread messages has changed. This update is sent only if a message database is used @unread_count Total number of unread messages @unread_unmuted_count Total number of unread messages in unmuted chats -updateUnreadMessageCount unread_count:int32 unread_unmuted_count:int32 = Update; +//@description Number of unread messages in a chat list has changed. This update is sent only if a message database is used @chat_list The chat list with changed number of unread messages +//@unread_count Total number of unread messages @unread_unmuted_count Total number of unread messages in unmuted chats +updateUnreadMessageCount chat_list:ChatList unread_count:int32 unread_unmuted_count:int32 = Update; //@description Number of unread chats, i.e. with unread messages or marked as unread, has changed. This update is sent only if a message database is used +//@chat_list The chat list with changed number of unread messages +//@total_count Approximate total number of chats in the chat list //@unread_count Total number of unread chats @unread_unmuted_count Total number of unread unmuted chats //@marked_as_unread_count Total number of chats marked as unread @marked_as_unread_unmuted_count Total number of unmuted chats marked as unread -updateUnreadChatCount unread_count:int32 unread_unmuted_count:int32 marked_as_unread_count:int32 marked_as_unread_unmuted_count:int32 = Update; +updateUnreadChatCount chat_list:ChatList total_count:int32 unread_count:int32 unread_unmuted_count:int32 marked_as_unread_count:int32 marked_as_unread_unmuted_count:int32 = Update; //@description An option changed its value @name The option name @value The new option value updateOption name:string value:OptionValue = Update; @@ -2765,14 +2929,20 @@ updateConnectionState state:ConnectionState = Update; //@description New terms of service must be accepted by the user. If the terms of service are declined, then the deleteAccount method should be called with the reason "Decline ToS update" @terms_of_service_id Identifier of the terms of service @terms_of_service The new terms of service updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService = Update; -//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null @query Text of the query @offset Offset of the first entry to return +//@description List of users nearby has changed. The update is sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby +updateUsersNearby users_nearby:vector = Update; + +//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null +//@query Text of the query @offset Offset of the first entry to return updateNewInlineQuery id:int64 sender_user_id:int32 user_location:location query:string offset:string = Update; -//@description The user has chosen a result of an inline query; for bots only @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null @query Text of the query @result_id Identifier of the chosen result @inline_message_id Identifier of the sent inline message, if known +//@description The user has chosen a result of an inline query; for bots only @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null +//@query Text of the query @result_id Identifier of the chosen result @inline_message_id Identifier of the sent inline message, if known updateNewChosenInlineResult sender_user_id:int32 user_location:location query:string result_id:string inline_message_id:string = Update; -//@description A new incoming callback query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @chat_id Identifier of the chat, in which the query was sent -//@message_id Identifier of the message, from which the query originated @chat_instance Identifier that uniquely corresponds to the chat to which the message was sent @payload Query payload +//@description A new incoming callback query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query +//@chat_id Identifier of the chat where the query was sent @message_id Identifier of the message, from which the query originated +//@chat_instance Identifier that uniquely corresponds to the chat to which the message was sent @payload Query payload updateNewCallbackQuery id:int64 sender_user_id:int32 chat_id:int53 message_id:int53 chat_instance:int64 payload:CallbackQueryPayload = Update; //@description A new incoming callback query from a message sent via a bot; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @inline_message_id Identifier of the inline message, from which the query originated @@ -2857,6 +3027,9 @@ resendAuthenticationCode = Ok; //@description Checks the authentication code. Works only when the current authorization state is authorizationStateWaitCode @code The verification code received via SMS, Telegram message, phone call, or flash call checkAuthenticationCode code:string = Ok; +//@description Requests QR code authentication by scanning a QR code on another logged in device. Works only when the current authorization state is authorizationStateWaitPhoneNumber @other_user_ids List of user identifiers of other users currently using the client +requestQrCodeAuthentication other_user_ids:vector = Ok; + //@description Finishes user registration. Works only when the current authorization state is authorizationStateWaitRegistration //@first_name The first name of the user; 1-64 characters @last_name The last name of the user; 0-64 characters registerUser first_name:string last_name:string = Ok; @@ -2883,6 +3056,10 @@ close = Ok; destroy = Ok; +//@description Confirms QR code authentication on another device. Returns created session on success @link A link from a QR code. The link must be scanned by the in-app camera +confirmQrCodeAuthentication link:string = Session; + + //@description Returns all updates needed to restore current TDLib state, i.e. all actual UpdateAuthorizationState/UpdateUser/UpdateNewChat and others. This is especially usefull if TDLib is run in a separate process. This is an offline method. Can be called before authorization getCurrentState = Updates; @@ -2939,10 +3116,10 @@ getBasicGroup basic_group_id:int32 = BasicGroup; //@description Returns full information about a basic group by its identifier @basic_group_id Basic group identifier getBasicGroupFullInfo basic_group_id:int32 = BasicGroupFullInfo; -//@description Returns information about a supergroup or channel by its identifier. This is an offline request if the current user is not a bot @supergroup_id Supergroup or channel identifier +//@description Returns information about a supergroup or a channel by its identifier. This is an offline request if the current user is not a bot @supergroup_id Supergroup or channel identifier getSupergroup supergroup_id:int32 = Supergroup; -//@description Returns full information about a supergroup or channel by its identifier, cached for up to 1 minute @supergroup_id Supergroup or channel identifier +//@description Returns full information about a supergroup or a channel by its identifier, cached for up to 1 minute @supergroup_id Supergroup or channel identifier getSupergroupFullInfo supergroup_id:int32 = SupergroupFullInfo; //@description Returns information about a secret chat by its identifier. This is an offline request @secret_chat_id Secret chat identifier @@ -2974,10 +3151,12 @@ getFile file_id:int32 = File; //@remote_file_id Remote identifier of the file to get @file_type File type, if known getRemoteFile remote_file_id:string file_type:FileType = File; -//@description Returns an ordered list of chats. Chats are sorted by the pair (order, chat_id) in decreasing order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1). -//-For optimal performance the number of returned chats is chosen by the library @offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from +//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (order, chat_id) in decreasing order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1). +//-For optimal performance the number of returned chats is chosen by the library +//@chat_list The chat list in which to return chats +//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from //@limit The maximum number of chats to be returned. It is possible that fewer chats than the limit are returned even if the end of the list is not reached -getChats offset_order:int64 offset_chat_id:int53 limit:int32 = Chats; +getChats chat_list:ChatList offset_order:int64 offset_chat_id:int53 limit:int32 = Chats; //@description Searches a public chat by its username. Currently only private chats, supergroups and channels can be public. Returns the chat if found; otherwise an error is returned @username Username to be resolved searchPublicChat username:string = Chat; @@ -2991,6 +3170,9 @@ searchChats query:string limit:int32 = Chats; //@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the chat list @query Query to search for @limit Maximum number of chats to be returned searchChatsOnServer query:string limit:int32 = Chats; +//@description Returns a list of users and location-based supergroups nearby. The list of users nearby will be updated for 60 seconds after the request by the updates updateUsersNearby. The request should be sent again every 25 seconds with adjusted location to not miss new chats @location Current user location +searchChatsNearby location:location = ChatsNearby; + //@description Returns a list of frequently used chats. Supported only if the chat info database is enabled @category Category of chats to be returned @limit Maximum number of chats to be returned; up to 30 getTopChats category:TopChatCategory limit:int32 = Chats; @@ -3009,8 +3191,17 @@ clearRecentlyFoundChats = Ok; //@description Checks whether a username can be set for a chat @chat_id Chat identifier; should be identifier of a supergroup chat, or a channel chat, or a private chat with self, or zero if chat is being created @username Username to be checked checkChatUsername chat_id:int53 username:string = CheckChatUsernameResult; -//@description Returns a list of public chats with username created by the user -getCreatedPublicChats = Chats; +//@description Returns a list of public chats of the specified type, owned by the user @type Type of the public chats to return +getCreatedPublicChats type:PublicChatType = Chats; + +//@description Checks whether a maximum number of owned public chats is reached. Returns corresponding error if the limit was reached @type Type of the public chats, for which to check the limit +checkCreatedPublicChatsLimit type:PublicChatType = Ok; + +//@description Returns a list of basic group and supergroup chats, which can be used as a discussion group for a channel. Basic group chats need to be first upgraded to supergroups before they can be set as a discussion group +getSuitableDiscussionChats = Chats; + +//@description Returns a list of recently inactive supergroups and channels. Can be used when user reaches limit on the number of joined supergroups and channels and receives CHANNELS_TOO_MUCH error +getInactiveSupergroupChats = Chats; //@description Returns a list of common group chats with a given user. Chats are sorted by their type and creation date @user_id User identifier @offset_chat_id Chat identifier starting from which to return chats; use 0 for the first request @limit Maximum number of chats to be returned; up to 100 @@ -3043,12 +3234,13 @@ searchChatMessages chat_id:int53 query:string sender_user_id:int32 from_message_ //@description Searches for messages in all chats except secret chats. Returns the results in reverse chronological order (i.e., in order of decreasing (date, chat_id, message_id)). //-For optimal performance the number of returned messages is chosen by the library +//@chat_list Chat list in which to search messages; pass null to search in all chats regardless of their chat list //@query Query to search for //@offset_date The date of the message starting from which the results should be fetched. Use 0 or any date in the future to get results from the last message //@offset_chat_id The chat identifier of the last found message, or 0 for the first request //@offset_message_id The message identifier of the last found message, or 0 for the first request //@limit The maximum number of messages to be returned, up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached -searchMessages query:string offset_date:int32 offset_chat_id:int53 offset_message_id:int53 limit:int32 = Messages; +searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:int53 offset_message_id:int53 limit:int32 = Messages; //@description Searches for messages in secret chats. Returns the results in reverse chronological order. For optimal performance the number of returned messages is chosen by the library //@chat_id Identifier of the chat in which to search. Specify 0 to search in all secret chats @query Query to search for. If empty, searchChatMessages should be used instead @@ -3074,6 +3266,9 @@ getChatMessageByDate chat_id:int53 date:int32 = Message; //@description Returns approximate number of messages of the specified type in the chat @chat_id Identifier of the chat in which to count messages @filter Filter for message content; searchMessagesFilterEmpty is unsupported in this function @return_local If true, returns count that is available locally without sending network requests, returning -1 if the number of messages is unknown getChatMessageCount chat_id:int53 filter:SearchMessagesFilter return_local:Bool = Count; +//@description Returns all scheduled messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id) @chat_id Chat identifier +getChatScheduledMessages chat_id:int53 = Messages; + //@description Removes an active notification from notification list. Needs to be called only if the notification is removed by the current user @notification_group_id Identifier of notification group to which the notification belongs @notification_id Identifier of removed notification removeNotification notification_group_id:int32 notification_id:int32 = Ok; @@ -3082,7 +3277,7 @@ removeNotification notification_group_id:int32 notification_id:int32 = Ok; removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = Ok; -//@description Returns a public HTTPS link to a message. Available only for messages in supergroups and channels with username +//@description Returns a public HTTPS link to a message. Available only for messages in supergroups and channels with a username //@chat_id Identifier of the chat to which the message belongs //@message_id Identifier of the message //@for_album Pass true if a link for a whole media album should be returned @@ -3097,33 +3292,36 @@ getMessageLink chat_id:int53 message_id:int53 = HttpUrl; getMessageLinkInfo url:string = MessageLinkInfo; -//@description Sends a message. Returns the sent message @chat_id Target chat @reply_to_message_id Identifier of the message to reply to or 0 -//@disable_notification Pass true to disable notification for the message. Not supported in secret chats @from_background Pass true if the message is sent from the background +//@description Sends a message. Returns the sent message +//@chat_id Target chat @reply_to_message_id Identifier of the message to reply to or 0 +//@options Options to be used to send the message //@reply_markup Markup for replying to the message; for bots only @input_message_content The content of the message to be sent -sendMessage chat_id:int53 reply_to_message_id:int53 disable_notification:Bool from_background:Bool reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; +sendMessage chat_id:int53 reply_to_message_id:int53 options:sendMessageOptions reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message; -//@description Sends messages grouped together into an album. Currently only photo and video messages can be grouped into an album. Returns sent messages @chat_id Target chat @reply_to_message_id Identifier of a message to reply to or 0 -//@disable_notification Pass true to disable notification for the messages. Not supported in secret chats @from_background Pass true if the messages are sent from the background +//@description Sends messages grouped together into an album. Currently only photo and video messages can be grouped into an album. Returns sent messages +//@chat_id Target chat @reply_to_message_id Identifier of a message to reply to or 0 +//@options Options to be used to send the messages //@input_message_contents Contents of messages to be sent -sendMessageAlbum chat_id:int53 reply_to_message_id:int53 disable_notification:Bool from_background:Bool input_message_contents:vector = Messages; +sendMessageAlbum chat_id:int53 reply_to_message_id:int53 options:sendMessageOptions input_message_contents:vector = Messages; //@description Invites a bot to a chat (if it is not yet a member) and sends it the /start command. Bots can't be invited to a private chat other than the chat with the bot. Bots can't be invited to channels (although they can be added as admins) and secret chats. Returns the sent message //@bot_user_id Identifier of the bot @chat_id Identifier of the target chat @parameter A hidden parameter sent to the bot for deep linking purposes (https://core.telegram.org/bots#deep-linking) sendBotStartMessage bot_user_id:int32 chat_id:int53 parameter:string = Message; -//@description Sends the result of an inline query as a message. Returns the sent message. Always clears a chat draft message @chat_id Target chat @reply_to_message_id Identifier of a message to reply to or 0 -//@disable_notification Pass true to disable notification for the message. Not supported in secret chats @from_background Pass true if the message is sent from background +//@description Sends the result of an inline query as a message. Returns the sent message. Always clears a chat draft message +//@chat_id Target chat @reply_to_message_id Identifier of a message to reply to or 0 +//@options Options to be used to send the message //@query_id Identifier of the inline query @result_id Identifier of the inline result //@hide_via_bot If true, there will be no mention of a bot, via which the message is sent. Can be used only for bots GetOption("animation_search_bot_username"), GetOption("photo_search_bot_username") and GetOption("venue_search_bot_username") -sendInlineQueryResultMessage chat_id:int53 reply_to_message_id:int53 disable_notification:Bool from_background:Bool query_id:int64 result_id:string hide_via_bot:Bool = Message; +sendInlineQueryResultMessage chat_id:int53 reply_to_message_id:int53 options:sendMessageOptions query_id:int64 result_id:string hide_via_bot:Bool = Message; //@description Forwards previously sent messages. Returns the forwarded messages in the same order as the message identifiers passed in message_ids. If a message can't be forwarded, null will be returned instead of the message //@chat_id Identifier of the chat to which to forward messages @from_chat_id Identifier of the chat from which to forward messages @message_ids Identifiers of the messages to forward -//@disable_notification Pass true to disable notification for the message, doesn't work if messages are forwarded to a secret chat @from_background Pass true if the messages are sent from the background +//@options Options to be used to send the messages //@as_album True, if the messages should be grouped into an album after forwarding. For this to work, no more than 10 messages may be forwarded, and all of them must be photo or video messages //@send_copy True, if content of the messages needs to be copied without links to the original messages. Always true if the messages are forwarded to a secret chat //@remove_caption True, if media captions of message copies needs to be removed. Ignored if send_copy is false -forwardMessages chat_id:int53 from_chat_id:int53 message_ids:vector disable_notification:Bool from_background:Bool as_album:Bool send_copy:Bool remove_caption:Bool = Messages; +forwardMessages chat_id:int53 from_chat_id:int53 message_ids:vector options:sendMessageOptions as_album:Bool send_copy:Bool remove_caption:Bool = Messages; //@description Resends messages which failed to send. Can be called only for messages for which messageSendingStateFailed.can_retry is true and after specified in messageSendingStateFailed.retry_after time passed. //-If a message is re-sent, the corresponding failed to send message is deleted. Returns the sent messages in the same order as the message identifiers passed in message_ids. If a message can't be re-sent, null will be returned instead of the message @@ -3143,7 +3341,7 @@ addLocalMessage chat_id:int53 sender_user_id:int32 reply_to_message_id:int53 dis //@description Deletes messages @chat_id Chat identifier @message_ids Identifiers of the messages to be deleted @revoke Pass true to try to delete messages for all chat members. Always true for supergroups, channels and secret chats deleteMessages chat_id:int53 message_ids:vector revoke:Bool = Ok; -//@description Deletes all messages sent by the specified user to a chat. Supported only in supergroups; requires can_delete_messages administrator privileges @chat_id Chat identifier @user_id User identifier +//@description Deletes all messages sent by the specified user to a chat. Supported only for supergroups; requires can_delete_messages administrator privileges @chat_id Chat identifier @user_id User identifier deleteChatMessagesFromUser chat_id:int53 user_id:int32 = Ok; @@ -3183,11 +3381,14 @@ editInlineMessageCaption inline_message_id:string reply_markup:ReplyMarkup capti //@description Edits the reply markup of an inline message sent via a bot; for bots only @inline_message_id Inline message identifier @reply_markup The new message reply markup editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup = Ok; +//@description Edits the time when a scheduled message will be sent. Scheduling state of all messages in the same album or forwarded together with the message will be also changed @chat_id The chat the message belongs to @message_id Identifier of the message @scheduling_state The new message scheduling state. Pass null to send the message immediately +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 getTextEntities text:string = TextEntities; -//@description Parses Bold, Italic, Code, Pre, PreCode and TextUrl 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 which should be parsed @parse_mode Text parse mode parseTextEntities text:string parse_mode:TextParseMode = 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 @@ -3219,8 +3420,19 @@ setPollAnswer chat_id:int53 message_id:int53 option_ids:vector = Ok; stopPoll chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Ok; +//@description Returns information about a button of type inlineKeyboardButtonTypeLoginUrl. The method needs to be called when the user presses the button +//@chat_id Chat identifier of the message with the button @message_id Message identifier of the message with the button @button_id Button identifier +getLoginUrlInfo chat_id:int53 message_id:int53 button_id:int32 = LoginUrlInfo; + +//@description Returns an HTTP URL which can be used to automatically authorize the user on a website after clicking an inline button of type inlineKeyboardButtonTypeLoginUrl. +//-Use the method getLoginUrlInfo to find whether a prior user confirmation is needed. If an error is returned, then the button must be handled as an ordinary URL button +//@chat_id Chat identifier of the message with the button @message_id Message identifier of the message with the button @button_id Button identifier +//@allow_write_access True, if the user allowed the bot to send them messages +getLoginUrl chat_id:int53 message_id:int53 button_id:int32 allow_write_access:Bool = HttpUrl; + + //@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot -//@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return +//@chat_id Identifier of the chat where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return getInlineQueryResults bot_user_id:int32 chat_id:int53 user_location:location query:string offset:string = InlineQueryResults; //@description Sets the result of an inline query; for bots only @inline_query_id Identifier of the inline query @is_personal True, if the result of the query can be cached for the specified user @@ -3300,8 +3512,8 @@ createSecretChat secret_chat_id:int32 = Chat; //@description Creates a new basic group and sends a corresponding messageBasicGroupChatCreate. Returns the newly created chat @user_ids Identifiers of users to be added to the basic group @title Title of the new basic group; 1-128 characters createNewBasicGroupChat user_ids:vector title:string = Chat; -//@description Creates a new supergroup or channel and sends a corresponding messageSupergroupChatCreate. Returns the newly created chat @title Title of the new chat; 1-128 characters @is_channel True, if a channel chat should be created @param_description Chat description; 0-255 characters -createNewSupergroupChat title:string is_channel:Bool description:string = Chat; +//@description Creates a new supergroup or channel and sends a corresponding messageSupergroupChatCreate. Returns the newly created chat @title Title of the new chat; 1-128 characters @is_channel True, if a channel chat should be created @param_description Chat description; 0-255 characters @location Chat location if a location-based supergroup is being created +createNewSupergroupChat title:string is_channel:Bool description:string location:chatLocation = Chat; //@description Creates a new secret chat. Returns the newly created chat @user_id Identifier of the target user createNewSecretChat user_id:int32 = Chat; @@ -3310,6 +3522,9 @@ 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 +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 //@chat_id Chat identifier @title New title of the chat; 1-128 characters setChatTitle chat_id:int53 title:string = Ok; @@ -3325,10 +3540,11 @@ setChatPermissions chat_id:int53 permissions:chatPermissions = Ok; //@description Changes the draft message in a chat @chat_id Chat identifier @draft_message New draft message; may be null setChatDraftMessage chat_id:int53 draft_message:draftMessage = Ok; -//@description Changes the notification settings of a chat @chat_id Chat identifier @notification_settings New notification settings for the chat +//@description Changes the notification settings of a chat. Notification settings of a chat with the current user (Saved Messages) can't be changed +//@chat_id Chat identifier @notification_settings New notification settings for the chat. If the chat is muted for more than 1 week, it is considered to be muted forever setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok; -//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max") non-secret chats and the same number of secret chats @chat_id Chat identifier @is_pinned New value of is_pinned +//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") non-secret chats and the same number of secret chats in the main/archive chat list @chat_id Chat identifier @is_pinned New value of is_pinned toggleChatIsPinned chat_id:int53 is_pinned:Bool = Ok; //@description Changes the marked as unread state of a chat @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread @@ -3343,12 +3559,23 @@ setChatClientData chat_id:int53 client_data:string = Ok; //@description Changes information about a chat. Available for basic groups, supergroups, and channels. Requires can_change_info rights @chat_id Identifier of the chat @param_description New chat description; 0-255 characters setChatDescription chat_id:int53 description:string = Ok; +//@description Changes the discussion group of a channel chat; requires can_change_info rights in the channel if it is specified @chat_id Identifier of the channel chat. Pass 0 to remove a link from the supergroup passed in the second argument to a linked channel chat (requires can_pin_messages rights in the supergroup) @discussion_chat_id Identifier of a new channel's discussion group. Use 0 to remove the discussion group. +//-Use the method getSuitableDiscussionChats to find all suitable groups. Basic group chats needs to be first upgraded to supergroup chats. If new chat members don't have access to old messages in the supergroup, then toggleSupergroupIsAllHistoryAvailable needs to be used first to change that +setChatDiscussionGroup chat_id:int53 discussion_chat_id:int53 = Ok; + +//@description Changes the location of a chat. Available only for some location-based supergroups, use supergroupFullInfo.can_set_location to check whether the method is allowed to use @chat_id Chat identifier @location New location for the chat; must be valid and not null +setChatLocation chat_id:int53 location:chatLocation = Ok; + +//@description Changes the slow mode delay of a chat. Available only for supergroups; requires can_restrict_members rights @chat_id Chat identifier @slow_mode_delay New slow mode delay for the chat; must be one of 0, 10, 30, 60, 300, 900, 3600 +setChatSlowModeDelay chat_id:int53 slow_mode_delay:int32 = Ok; + //@description Pins a message in a chat; requires can_pin_messages rights @chat_id Identifier of the chat @message_id Identifier of the new pinned message @disable_notification True, if there should be no notification about the pinned message pinChatMessage chat_id:int53 message_id:int53 disable_notification:Bool = Ok; //@description Removes the pinned message from a chat; requires can_pin_messages rights in the group or channel @chat_id Identifier of the chat unpinChatMessage chat_id:int53 = Ok; + //@description Adds current user as a new member to a chat. Private and secret chats can't be joined using this method @chat_id Chat identifier joinChat chat_id:int53 = Ok; @@ -3363,18 +3590,25 @@ addChatMember chat_id:int53 user_id:int32 forward_limit:int32 = Ok; //@chat_id Chat identifier @user_ids Identifiers of the users to be added to the chat addChatMembers chat_id:int53 user_ids:vector = Ok; -//@description Changes the status of a chat member, needs appropriate privileges. This function is currently not suitable for adding new members to the chat; instead, use addChatMember. The chat member status will not be changed until it has been synchronized with the server +//@description Changes the status of a chat member, needs appropriate privileges. This function is currently not suitable for adding new members to the chat and transferring chat ownership; instead, use addChatMember or transferChatOwnership. The chat member status will not be changed until it has been synchronized with the server //@chat_id Chat identifier @user_id User identifier @status The new status of the member in the chat setChatMemberStatus chat_id:int53 user_id:int32 status:ChatMemberStatus = Ok; +//@description Checks whether the current session can be used to transfer a chat ownership to another user +canTransferOwnership = CanTransferOwnershipResult; + +//@description Changes the owner of a chat. The current user must be a current owner of the chat. Use the method canTransferOwnership to check whether the ownership can be transferred from the current session. Available only for supergroups and channel chats +//@chat_id Chat identifier @user_id Identifier of the user to which transfer the ownership. The ownership can't be transferred to a bot or to a deleted user @password The password of the current user +transferChatOwnership chat_id:int53 user_id:int32 password:string = Ok; + //@description Returns information about a single member of a chat @chat_id Chat identifier @user_id User identifier getChatMember chat_id:int53 user_id:int32 = ChatMember; //@description Searches for a specified query in the first name, last name and username of the members of a specified chat. Requires administrator rights in channels @chat_id Chat identifier @query Query to search for @limit The maximum number of users to be returned @filter The type of users to return. By default, chatMembersFilterMembers searchChatMembers chat_id:int53 query:string limit:int32 filter:ChatMembersFilter = ChatMembers; -//@description Returns a list of users who are administrators of the chat @chat_id Chat identifier -getChatAdministrators chat_id:int53 = Users; +//@description Returns a list of administrators of the chat with their custom titles @chat_id Chat identifier +getChatAdministrators chat_id:int53 = ChatAdministrators; //@description Clears draft messages in all chats @exclude_secret_chats If true, local draft messages in secret chats will not be cleared @@ -3394,8 +3628,8 @@ setScopeNotificationSettings scope:NotificationSettingsScope notification_settin resetAllNotificationSettings = Ok; -//@description Changes the order of pinned chats @chat_ids The new list of pinned chats -setPinnedChats chat_ids:vector = Ok; +//@description Changes the order of pinned chats @chat_list Chat list in which to change the order of pinned chats @chat_ids The new list of pinned chats +setPinnedChats chat_list:ChatList chat_ids:vector = Ok; //@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates @@ -3483,7 +3717,11 @@ unblockUser user_id:int32 = Ok; getBlockedUsers offset:int32 limit:int32 = Users; -//@description Adds new contacts or edits existing contacts; contacts' user identifiers are ignored @contacts The list of contacts to import or edit, contact's vCard are ignored and are not imported +//@description Adds a user to the contact list or edits an existing contact by their user identifier @contact The contact to add or edit; phone number can be empty and needs to be specified only if known, vCard is ignored +//@share_phone_number True, if the new contact needs to be allowed to see current user's phone number. A corresponding rule to userPrivacySettingShowPhoneNumber will be added if needed. Use the field UserFullInfo.need_phone_number_privacy_exception to check whether the current user needs to be asked to share their phone number +addContact contact:contact share_phone_number:Bool = Ok; + +//@description Adds new contacts or edits existing contacts by their phone numbers; contacts' user identifiers are ignored @contacts The list of contacts to import or edit; contacts' vCard are ignored and are not imported importContacts contacts:vector = ImportedContacts; //@description Returns all user contacts @@ -3506,6 +3744,10 @@ changeImportedContacts contacts:vector = ImportedContacts; clearImportedContacts = Ok; +//@description Shares a phone number of the current user with a mutual contact. Supposed to be called when the user clicks on chatActionBarSharePhoneNumber @user_id Identifier of the user with whom to share the phone number. The user must be a mutual contact +sharePhoneNumber user_id:int32 = Ok; + + //@description Returns the profile photos of a user. The result of this query may be outdated: some photos might have been deleted already @user_id User identifier @offset The number of photos to skip; must be non-negative @limit Maximum number of photos to be returned; up to 100 getUserProfilePhotos user_id:int32 offset:int32 limit:int32 = UserProfilePhotos; @@ -3657,7 +3899,7 @@ disconnectWebsite website_id:int64 = Ok; disconnectAllWebsites = Ok; -//@description Changes the username of a supergroup or channel, requires creator privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username +//@description Changes the username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username setSupergroupUsername supergroup_id:int32 username:string = Ok; //@description Changes the sticker set of a supergroup; requires can_change_info rights @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set @@ -3676,7 +3918,7 @@ reportSupergroupSpam supergroup_id:int32 user_id:int32 message_ids:vector //@filter The type of users to return. By default, supergroupMembersRecent @offset Number of users to skip @limit The maximum number of users be returned; up to 200 getSupergroupMembers supergroup_id:int32 filter:SupergroupMembersFilter offset:int32 limit:int32 = ChatMembers; -//@description Deletes a supergroup or channel along with all messages in the corresponding chat. This will release the supergroup or channel username and remove all members; requires creator privileges in the supergroup or channel. Chats with more than 1000 members can't be deleted using this method @supergroup_id Identifier of the supergroup or channel +//@description Deletes a supergroup or channel along with all messages in the corresponding chat. This will release the supergroup or channel username and remove all members; requires owner privileges in the supergroup or channel. Chats with more than 1000 members can't be deleted using this method @supergroup_id Identifier of the supergroup or channel deleteSupergroup supergroup_id:int32 = Ok; @@ -3684,7 +3926,7 @@ deleteSupergroup supergroup_id:int32 = Ok; closeSecretChat secret_chat_id:int32 = Ok; -//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only in supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i. e., in order of decreasing event_id) +//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only for supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i. e., in order of decreasing event_id) //@chat_id Chat identifier @query Search query by which to filter events @from_event_id Identifier of an event from which to return results. Use 0 to get results from the latest events @limit Maximum number of events to return; up to 100 //@filters The types of events to return. By default, all types will be returned @user_ids User identifiers by which to filter events. By default, events relating to all users will be returned getChatEventLog chat_id:int53 query:string from_event_id:int64 limit:int32 filters:chatEventLogFilters user_ids:vector = ChatEvents; @@ -3727,7 +3969,7 @@ getBackgroundUrl name:string type:BackgroundType = HttpUrl; searchBackground name:string = Background; //@description Changes the background selected by the user; adds background to the list of installed backgrounds -//@background The input background to use, null for solid backgrounds +//@background The input background to use, null for filled backgrounds //@type Background type; null for default background. The method will return error 404 if type is null //@for_dark_theme True, if the background is chosen for dark theme setBackground background:InputBackground type:BackgroundType for_dark_theme:Bool = Background; @@ -3808,17 +4050,14 @@ getAccountTtl = AccountTtl; deleteAccount reason:string = Ok; -//@description Returns information on whether the current chat can be reported as spam @chat_id Chat identifier -getChatReportSpamState chat_id:int53 = ChatReportSpamState; +//@description Removes a chat action bar without any other action @chat_id Chat identifier +removeChatActionBar chat_id:int53 = Ok; -//@description Reports to the server whether a chat is a spam chat or not. Can be used only if ChatReportSpamState.can_report_spam is true. After this request, ChatReportSpamState.can_report_spam becomes false forever @chat_id Chat identifier @is_spam_chat If true, the chat will be reported as spam; otherwise it will be marked as not spam -changeChatReportSpamState chat_id:int53 is_spam_chat:Bool = 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 @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. 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 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 @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 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 getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl; @@ -3962,7 +4201,7 @@ sendCustomRequest method:string parameters:string = CustomRequestResult; answerCustomQuery custom_query_id:int64 data:string = Ok; -//@description Sends a request to TON lite server through Telegram servers @request The request +//@description Sends a request to TON lite server through Telegram servers. Can be called before authorization @request The request sendTonLiteServerRequest request:bytes = TonLiteServerResponse; //@description Returns a salt to be used with locally stored password to access a local TON-based wallet diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index daa7b565..c55c3ac8 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 e278fc94..8ac37eaa 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -31,10 +31,13 @@ inputPeerSelf#7da07ec9 = InputPeer; inputPeerChat#179be863 chat_id:int = InputPeer; inputPeerUser#7b8e7de6 user_id:int access_hash:long = InputPeer; inputPeerChannel#20adaef8 channel_id:int access_hash:long = InputPeer; +inputPeerUserFromMessage#17bae2e6 peer:InputPeer msg_id:int user_id:int = InputPeer; +inputPeerChannelFromMessage#9c95f7bb peer:InputPeer msg_id:int channel_id:int = InputPeer; inputUserEmpty#b98886cf = InputUser; inputUserSelf#f7c1b13f = InputUser; inputUser#d8292816 user_id:int access_hash:long = InputUser; +inputUserFromMessage#2d117597 peer:InputPeer msg_id:int user_id:int = InputUser; inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact; @@ -73,6 +76,7 @@ inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation; inputTakeoutFileLocation#29be5899 = InputFileLocation; inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation; +inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation; inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation; inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation; @@ -92,7 +96,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; userEmpty#200250ba id:int = User; -user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; +user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; @@ -107,11 +111,11 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#22a235da flags:# can_set_username:flags.7?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 = ChatFull; -channelFull#1c87a71a 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 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 = ChatFull; +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; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -124,7 +128,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; messageEmpty#83e5de54 id:int = Message; -message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message; +message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector = Message; messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message; messageMediaEmpty#3ded6320 = MessageMedia; @@ -164,7 +168,8 @@ messageActionSecureValuesSentMe#1b287353 values:Vector credentials: messageActionSecureValuesSent#d95c6154 types:Vector = MessageAction; messageActionContactSignUp#f3f25f76 = MessageAction; -dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog; +dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; +dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; photoEmpty#2331b22d id:long = Photo; photo#d07504a5 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector dc_id:int = Photo; @@ -177,9 +182,10 @@ photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; geoPointEmpty#1117dd5f = GeoPoint; geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint; -auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode; +auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization; +auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization; @@ -192,9 +198,10 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; -peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings; +peerSettings#818426cd flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; +wallPaperNoFile#8af40b25 flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; @@ -202,8 +209,9 @@ inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonChildAbuse#adf44ee3 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; +inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; -userFull#8ea4a881 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int = UserFull; +userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -213,8 +221,6 @@ contactBlocked#561bc879 user_id:int date:int = ContactBlocked; contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus; -contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User = contacts.Link; - contacts.contactsNotModified#b74ba9d2 = contacts.Contacts; contacts.contacts#eae87e42 contacts:Vector saved_count:int users:Vector = contacts.Contacts; @@ -228,7 +234,7 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector messages:Vector< messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs; messages.messages#8c718e87 messages:Vector chats:Vector users:Vector = messages.Messages; -messages.messagesSlice#a6c47aaa flags:# inexact:flags.1?true count:int messages:Vector chats:Vector users:Vector = messages.Messages; +messages.messagesSlice#c8edce1e flags:# inexact:flags.1?true count:int next_rate:flags.0?int messages:Vector chats:Vector users:Vector = messages.Messages; messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector chats:Vector users:Vector = messages.Messages; messages.messagesNotModified#74535f21 count:int = messages.Messages; @@ -265,7 +271,6 @@ updateChatParticipants#7761198 participants:ChatParticipants = Update; updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update; updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update; -updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update; updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update; updateEncryptedChatTyping#1710f156 chat_id:int = Update; updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update; @@ -278,14 +283,14 @@ updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector = Update; updatePrivacy#ee3b272a key:PrivacyKey rules:Vector = Update; updateUserPhone#12b9417b user_id:int phone:string = Update; -updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Update; +updateReadHistoryInbox#9c974fdf flags:# folder_id:flags.0?int peer:Peer max_id:int still_unread_count:int pts:int pts_count:int = Update; updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update; updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update; updateReadMessagesContents#68c13933 messages:Vector pts:int pts_count:int = Update; updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update; updateChannel#b6d45656 channel_id:int = Update; updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update; -updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update; +updateReadChannelInbox#330b5424 flags:# folder_id:flags.0?int channel_id:int max_id:int still_unread_count:int pts:int = Update; updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector pts:int pts_count:int = Update; updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update; @@ -307,8 +312,8 @@ updateRecentStickers#9a422c20 = Update; updateConfig#a229dd06 = Update; updatePtsChanged#3354678f = Update; updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update; -updateDialogPinned#19d27f3c flags:# pinned:flags.0?true peer:DialogPeer = Update; -updatePinnedDialogs#ea4cb65b flags:# order:flags.0?Vector = Update; +updateDialogPinned#6e6fe51c flags:# pinned:flags.0?true folder_id:flags.1?int peer:DialogPeer = Update; +updatePinnedDialogs#fa0f3ca2 flags:# folder_id:flags.1?int order:flags.0?Vector = Update; updateBotWebhookJSON#8317c0c3 data:DataJSON = Update; updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update; updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update; @@ -325,6 +330,14 @@ updateUserPinnedMessage#4c43da18 user_id:int id:int = Update; updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update; +updateFolderPeers#19360dc0 folder_peers:Vector pts:int pts_count:int = Update; +updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update; +updatePeerLocated#b4afcfb0 peers:Vector = Update; +updateNewScheduledMessage#39a51dfb message:Message = Update; +updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector = Update; +updateTheme#8216fba3 theme:Theme = Update; +updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update; +updateLoginToken#564fe691 = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -351,7 +364,7 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption; -config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config; +config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; @@ -420,6 +433,8 @@ inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey; inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey; +inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey; +inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; @@ -427,6 +442,8 @@ privacyKeyPhoneCall#3d662b7b = PrivacyKey; privacyKeyPhoneP2P#39491cc8 = PrivacyKey; privacyKeyForwards#69ec56a3 = PrivacyKey; privacyKeyProfilePhoto#96151fed = PrivacyKey; +privacyKeyPhoneNumber#d19ae46d = PrivacyKey; +privacyKeyAddedByPhone#42ffd42b = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -434,6 +451,8 @@ inputPrivacyValueAllowUsers#131cc67f users:Vector = InputPrivacyRule; inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule; inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule; inputPrivacyValueDisallowUsers#90110467 users:Vector = InputPrivacyRule; +inputPrivacyValueAllowChatParticipants#4c81c1ba chats:Vector = InputPrivacyRule; +inputPrivacyValueDisallowChatParticipants#d82363af chats:Vector = InputPrivacyRule; privacyValueAllowContacts#fffe1bac = PrivacyRule; privacyValueAllowAll#65427b82 = PrivacyRule; @@ -441,8 +460,10 @@ privacyValueAllowUsers#4d5bbe0c users:Vector = PrivacyRule; privacyValueDisallowContacts#f888fa1a = PrivacyRule; privacyValueDisallowAll#8b73e763 = PrivacyRule; privacyValueDisallowUsers#c7f49b7 users:Vector = PrivacyRule; +privacyValueAllowChatParticipants#18be796b chats:Vector = PrivacyRule; +privacyValueDisallowChatParticipants#acae0690 chats:Vector = PrivacyRule; -account.privacyRules#554abb6f rules:Vector users:Vector = account.PrivacyRules; +account.privacyRules#50a04e45 rules:Vector chats:Vector users:Vector = account.PrivacyRules; accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; @@ -464,14 +485,9 @@ messages.allStickers#edfd405f hash:int sets:Vector = messages.AllSti messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages; -contactLinkUnknown#5f4f9247 = ContactLink; -contactLinkNone#feedd3ad = ContactLink; -contactLinkHasPhone#268f3f59 = ContactLink; -contactLinkContact#d502c2d0 = ContactLink; - webPageEmpty#eb1477e8 id:long = WebPage; webPagePending#c586da1c id:long date:int = WebPage; -webPage#5f07b4bc 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 = WebPage; +webPage#fa64e172 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 documents:flags.11?Vector cached_page:flags.10?Page = WebPage; webPageNotModified#85849473 = 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; @@ -497,6 +513,7 @@ chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:f inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; +inputStickerSetAnimatedEmoji#28703c8 = 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; @@ -539,16 +556,20 @@ messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEnt inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity; messageEntityPhone#9b69e34b offset:int length:int = MessageEntity; 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; inputChannelEmpty#ee8c1e86 = InputChannel; inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; +inputChannelFromMessage#2a286531 peer:InputPeer msg_id:int channel_id:int = InputChannel; contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer; messageRange#ae30253 min_id:int max_id:int = MessageRange; updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference; -updates.channelDifferenceTooLong#6a9d7b35 flags:# final:flags.0?true pts:int timeout:flags.1?int top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int messages:Vector chats:Vector users:Vector = updates.ChannelDifference; +updates.channelDifferenceTooLong#a4bcc6fe flags:# final:flags.0?true timeout:flags.1?int dialog:Dialog messages:Vector chats:Vector users:Vector = updates.ChannelDifference; updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector other_updates:Vector chats:Vector users:Vector = updates.ChannelDifference; channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; @@ -556,8 +577,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges: channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; -channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant; -channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant; +channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant; +channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant; channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; @@ -637,6 +658,8 @@ topPeerCategoryCorrespondents#637b7ed = TopPeerCategory; topPeerCategoryGroups#bd17a14a = TopPeerCategory; topPeerCategoryChannels#161d9628 = TopPeerCategory; topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory; +topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory; +topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory; topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector = TopPeerCategoryPeers; @@ -756,7 +779,7 @@ payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; -payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult; +payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult; payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; @@ -821,6 +844,9 @@ channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputSticker channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction; channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -852,8 +878,10 @@ inputMessageReplyTo#bad88395 id:int = InputMessage; inputMessagePinned#86872538 = InputMessage; inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer; +inputDialogPeerFolder#64600527 folder_id:int = InputDialogPeer; dialogPeer#e56dbf05 peer:Peer = DialogPeer; +dialogPeerFolder#514519e2 folder_id:int = DialogPeer; messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets; messages.foundStickerSets#5108d648 hash:int sets:Vector = messages.FoundStickerSets; @@ -990,15 +1018,16 @@ chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper; inputWallPaperSlug#72091c80 slug:string = InputWallPaper; +inputWallPaperNoFile#8427bbac = InputWallPaper; account.wallPapersNotModified#1c199183 = account.WallPapers; account.wallPapers#702b65a9 hash:int wallpapers:Vector = account.WallPapers; -codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings; +codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings; -wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings; +wallPaperSettings#5086cf8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings; -autoDownloadSettings#d246fd47 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int = AutoDownloadSettings; +autoDownloadSettings#e04232f3 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int video_upload_maxbitrate:int = AutoDownloadSettings; account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings; @@ -1011,11 +1040,56 @@ emojiURL#a575739d url:string = EmojiURL; emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage; +fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation; + +folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder; + +inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer; + +folderPeer#e9baa668 peer:Peer folder_id:int = FolderPeer; + +messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter; + +urlAuthResultRequest#92d33a0e flags:# request_write_access:flags.0?true bot:User domain:string = UrlAuthResult; +urlAuthResultAccepted#8f8c0e4e url:string = UrlAuthResult; +urlAuthResultDefault#a9d6db1f = UrlAuthResult; + +channelLocationEmpty#bfb5ad8b = ChannelLocation; +channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation; + +peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated; + +restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason; + +inputTheme#3c5693e9 id:long access_hash:long = InputTheme; +inputThemeSlug#f5890df1 slug:string = InputTheme; + +theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:int = Theme; + +account.themesNotModified#f41eb622 = account.Themes; +account.themes#7f676421 hash:int themes:Vector = account.Themes; + wallet.liteResponse#764386d7 response:bytes = wallet.LiteResponse; wallet.secretSalt#dd484d64 salt:bytes = wallet.KeySecretSalt; -fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation; +auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken; +auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken; +auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken; + +account.contentSettings#57e28221 flags:# sensitive_enabled:flags.0?true sensitive_can_change:flags.1?true = account.ContentSettings; + +messages.inactiveChats#a927fec5 dates:Vector chats:Vector users:Vector = messages.InactiveChats; + +baseThemeClassic#c3a12462 = BaseTheme; +baseThemeDay#fbd81688 = BaseTheme; +baseThemeNight#b7b31ea8 = BaseTheme; +baseThemeTinted#6d5f77ee = BaseTheme; +baseThemeArctic#5b11125a = BaseTheme; + +inputThemeSettings#bd507cd1 flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings; + +themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?WallPaper = ThemeSettings; ---functions--- @@ -1028,7 +1102,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; -auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; +auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; auth.logOut#5717da40 = Bool; auth.resetAuthorizations#9fab0d1a = Bool; @@ -1042,8 +1116,11 @@ auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool; +auth.exportLoginToken#b1b41517 api_id:int api_hash:string except_ids:Vector = auth.LoginToken; +auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken; +auth.acceptLoginToken#e894ad4d token:bytes = Authorization; -account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; +account.registerDevice#68976c6f flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector = Bool; account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; @@ -1098,6 +1175,16 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet account.resetWallPapers#bb3b9804 = Bool; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; +account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document; +account.createTheme#8432c21f flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; +account.updateTheme#5cb367d5 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; +account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; +account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool; +account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme; +account.getThemes#285946f8 format:string hash:int = account.Themes; +account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool; +account.getContentSettings#8b9b4dae = account.ContentSettings; +account.getMultiWallPapers#65ad71dc wallpapers:Vector = Vector; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1107,22 +1194,24 @@ contacts.getContactIDs#2caa4a42 hash:int = Vector; contacts.getStatuses#c4a353ee = Vector; contacts.getContacts#c023849f hash:int = contacts.Contacts; contacts.importContacts#2c800be5 contacts:Vector = contacts.ImportedContacts; -contacts.deleteContact#8e953744 id:InputUser = contacts.Link; -contacts.deleteContacts#59ab389e id:Vector = Bool; +contacts.deleteContacts#96a0e00 id:Vector = Updates; contacts.deleteByPhones#1013fd9e phones:Vector = Bool; contacts.block#332b49fc id:InputUser = Bool; contacts.unblock#e54100bd id:InputUser = Bool; contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked; contacts.search#11f812d8 q:string limit:int = contacts.Found; contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; -contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; +contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; contacts.resetSaved#879537f1 = Bool; 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; messages.getMessages#63c66506 id:Vector = messages.Messages; -messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs; +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; messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; @@ -1130,11 +1219,10 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; -messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer = Updates; +messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; +messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; +messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer schedule_date:flags.10?int = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; -messages.hideReportSpam#a8f1709b peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; messages.report#bd82b658 peer:InputPeer id:Vector reason:ReportReason = Bool; messages.getChats#3c6aa187 id:Vector = messages.Chats; @@ -1169,7 +1257,7 @@ messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_par messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector increment:Bool = Vector; messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; messages.migrateChat#15a3b8e3 chat_id:int = Updates; -messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector = Bool; messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; @@ -1177,9 +1265,9 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; -messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; +messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; -messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; +messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.15?int = Updates; messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer; messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; @@ -1202,8 +1290,8 @@ messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = message messages.getAllChats#eba80ff0 except_ids:Vector = messages.Chats; messages.getWebPage#32ca8f91 url:string hash:int = WebPage; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; -messages.reorderPinnedDialogs#5b51d63f flags:# force:flags.0?true order:Vector = Bool; -messages.getPinnedDialogs#e254d64e = messages.PeerDialogs; +messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector = Bool; +messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector = Bool; messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool; messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia; @@ -1213,7 +1301,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory; messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages; -messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector = Updates; +messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector schedule_date:flags.10?int = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets; messages.getSplitRanges#1cff7e08 = Vector; @@ -1231,6 +1319,14 @@ messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference; messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference; messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector = Vector; messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL; +messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector = Vector; +messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult; +messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult; +messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool; +messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages; +messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector = messages.Messages; +messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates; +messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1242,7 +1338,7 @@ photos.deletePhotos#87cf7f2f id:Vector = Vector; photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos; upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; -upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File; +upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File; upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile; @@ -1279,8 +1375,8 @@ channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipant channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant; channels.getChannels#a7f6bbb id:Vector = messages.Chats; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; -channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates; -channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates; +channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; +channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool; @@ -1291,7 +1387,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector = channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; -channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; +channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats; channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates; channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector max_id:long min_id:long limit:int = channels.AdminLogResults; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; @@ -1299,6 +1395,12 @@ channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector = Bool channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; channels.getLeftChannels#8341ecc0 offset:int = messages.Chats; +channels.getGroupsForDiscussion#f5dad378 = messages.Chats; +channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool; +channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates; +channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool; +channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; +channels.getInactiveChannels#11e831ee = messages.InactiveChats; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; @@ -1324,11 +1426,14 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; -wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse; -wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt; - langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:int = LangPackDifference; langpack.getLanguages#42c6978f lang_pack:string = Vector; langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage; + +folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates; +folders.deleteFolder#1c295881 folder_id:int = Updates; + +wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse; +wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index 865b58fc..c4dd03ab 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_json_converter.cpp b/td/generate/tl_json_converter.cpp index bb16fb34..545f4d1b 100644 --- a/td/generate/tl_json_converter.cpp +++ b/td/generate/tl_json_converter.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -26,35 +26,42 @@ void gen_to_json_constructor(StringBuilder &sb, const T *constructor, bool is_he sb << "void to_json(JsonValueScope &jv, " << "const td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &object)"; if (is_header) { - sb << ";\n"; + sb << ";\n\n"; return; } sb << " {\n"; sb << " auto jo = jv.enter_object();\n"; - sb << " jo << ctie(\"@type\", \"" << tl::simple::gen_cpp_name(constructor->name) << "\");\n"; + sb << " jo(\"@type\", \"" << tl::simple::gen_cpp_name(constructor->name) << "\");\n"; for (auto &arg : constructor->args) { - auto field = tl::simple::gen_cpp_field_name(arg.name); - // TODO: or as null + auto field_name = tl::simple::gen_cpp_field_name(arg.name); bool is_custom = arg.type->type == tl::simple::Type::Custom; + auto object = PSTRING() << "object." << field_name; if (is_custom) { - sb << " if (object." << field << ") {\n "; + sb << " if (" << object << ") {\n "; } - auto object = PSTRING() << "object." << tl::simple::gen_cpp_field_name(arg.name); if (arg.type->type == tl::simple::Type::Bytes) { object = PSTRING() << "base64_encode(" << object << ")"; + } else if (arg.type->type == tl::simple::Type::Bool) { + object = PSTRING() << "JsonBool{" << object << "}"; } else if (arg.type->type == tl::simple::Type::Int64) { object = PSTRING() << "JsonInt64{" << object << "}"; } else if (arg.type->type == tl::simple::Type::Vector && arg.type->vector_value_type->type == tl::simple::Type::Int64) { object = PSTRING() << "JsonVectorInt64{" << object << "}"; } - sb << " jo << ctie(\"" << arg.name << "\", ToJson(" << object << "));\n"; + if (is_custom) { + sb << " jo(\"" << arg.name << "\", ToJson(*" << object << "));\n"; + } else if (arg.type->type == tl::simple::Type::Int64 || arg.type->type == tl::simple::Type::Vector) { + sb << " jo(\"" << arg.name << "\", ToJson(" << object << "));\n"; + } else { + sb << " jo(\"" << arg.name << "\", " << object << ");\n"; + } if (is_custom) { sb << " }\n"; } } - sb << "}\n"; + sb << "}\n\n"; } void gen_to_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header, Mode mode) { @@ -66,13 +73,13 @@ void gen_to_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_he auto type_name = tl::simple::gen_cpp_name(custom_type->name); sb << "void to_json(JsonValueScope &jv, const td_api::" << type_name << " &object)"; if (is_header) { - sb << ";\n"; + sb << ";\n\n"; } else { sb << " {\n" << " td_api::downcast_call(const_cast(object), [&jv](const auto &object) { " "to_json(jv, object); });\n" - << "}\n"; + << "}\n\n"; } } for (auto *constructor : custom_type->constructors) { @@ -91,23 +98,16 @@ template void gen_from_json_constructor(StringBuilder &sb, const T *constructor, bool is_header) { sb << "Status from_json(td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &to, JsonObject &from)"; if (is_header) { - sb << ";\n"; + sb << ";\n\n"; } else { sb << " {\n"; for (auto &arg : constructor->args) { - sb << " {\n"; - sb << " auto value = get_json_object_field_force(from, \"" << tl::simple::gen_cpp_name(arg.name) << "\");\n"; - sb << " if (value.type() != td::JsonValue::Type::Null) {\n"; - if (arg.type->type == tl::simple::Type::Bytes) { - sb << " TRY_STATUS(from_json_bytes(to." << tl::simple::gen_cpp_field_name(arg.name) << ", value));\n"; - } else { - sb << " TRY_STATUS(from_json(to." << tl::simple::gen_cpp_field_name(arg.name) << ", value));\n"; - } - sb << " }\n"; - sb << " }\n"; + sb << " TRY_STATUS(from_json" << (arg.type->type == tl::simple::Type::Bytes ? "_bytes" : "") << "(to." + << tl::simple::gen_cpp_field_name(arg.name) << ", get_json_object_field_force(from, \"" + << tl::simple::gen_cpp_name(arg.name) << "\")));\n"; } sb << " return Status::OK();\n"; - sb << "}\n"; + sb << "}\n\n"; } } @@ -132,7 +132,7 @@ using Vec = std::vector>; void gen_tl_constructor_from_string(StringBuilder &sb, Slice name, const Vec &vec, bool is_header) { sb << "Result tl_constructor_from_string(td_api::" << name << " *object, const std::string &str)"; if (is_header) { - sb << ";\n"; + sb << ";\n\n"; return; } sb << " {\n"; @@ -153,7 +153,7 @@ void gen_tl_constructor_from_string(StringBuilder &sb, Slice name, const Vec &ve << " return Status::Error(PSLICE() << \"Unknown class \\\"\" << str << \"\\\"\");\n" << " }\n" << " return it->second;\n"; - sb << "}\n"; + sb << "}\n\n"; } void gen_tl_constructor_from_string(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header, Mode mode) { @@ -218,10 +218,46 @@ void gen_json_converter_file(const tl::simple::Schema &schema, const std::string sb << "#include \"td/utils/common.h\"\n"; sb << "#include \"td/utils/Slice.h\"\n\n"; + sb << "#include \n"; sb << "#include \n\n"; } sb << "namespace td {\n"; - sb << "namespace td_api{\n"; + sb << "namespace td_api {\n"; + if (is_header) { + sb << "\nvoid to_json(JsonValueScope &jv, const tl_object_ptr &value);\n"; + sb << "\nStatus from_json(tl_object_ptr &to, td::JsonValue from);\n"; + sb << "\nvoid to_json(JsonValueScope &jv, const Object &object);\n"; + sb << "\nvoid to_json(JsonValueScope &jv, const Function &object);\n\n"; + } else { + sb << R"ABCD( +void to_json(JsonValueScope &jv, const tl_object_ptr &value) { + td::to_json(jv, std::move(value)); +} + +Status from_json(tl_object_ptr &to, td::JsonValue from) { + return td::from_json(to, std::move(from)); +} + +template +auto lazy_to_json(JsonValueScope &jv, const T &t) -> decltype(td_api::to_json(jv, t)) { + return td_api::to_json(jv, t); +} + +template +void lazy_to_json(std::reference_wrapper, const T &t) { + UNREACHABLE(); +} + +void to_json(JsonValueScope &jv, const Object &object) { + downcast_call(const_cast(object), [&jv](const auto &object) { lazy_to_json(jv, object); }); +} + +void to_json(JsonValueScope &jv, const Function &object) { + downcast_call(const_cast(object), [&jv](const auto &object) { lazy_to_json(jv, object); }); +} + +)ABCD"; + } gen_tl_constructor_from_string(sb, schema, is_header, mode); gen_from_json(sb, schema, is_header, mode); gen_to_json(sb, schema, is_header, mode); diff --git a/td/generate/tl_json_converter.h b/td/generate/tl_json_converter.h index ded58af4..862053ce 100644 --- a/td/generate/tl_json_converter.h +++ b/td/generate/tl_json_converter.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/tl_writer_c.h b/td/generate/tl_writer_c.h index b48c7d93..1f23711e 100644 --- a/td/generate/tl_writer_c.h +++ b/td/generate/tl_writer_c.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -335,7 +335,7 @@ class TlWriterCCommon : public tl::TL_writer { return ""; } - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override { + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override { if (!is_default || is_header_ == -1 || class_name == "") { return ""; } @@ -344,7 +344,7 @@ class TlWriterCCommon : public tl::TL_writer { ss << "};\n"; } ss << "struct Td" + gen_class_name(class_name) + " *TdCreateObject" + gen_class_name(class_name) + " (" + - (fields_num ? "" : "void"); + (field_count ? "" : "void"); return ss.str(); } std::string gen_constructor_parameter(int field_num, const std::string &class_name, const tl::arg &a, @@ -362,7 +362,7 @@ class TlWriterCCommon : public tl::TL_writer { bool is_default) const override { return ""; } - std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override { + std::string gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const override { if (!is_default || is_header_ == -1) { return ""; } @@ -1029,11 +1029,11 @@ class TlWriterCCommon : public tl::TL_writer { } std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override { return ""; } - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override { return ""; } diff --git a/td/generate/tl_writer_cpp.cpp b/td/generate/tl_writer_cpp.cpp index 5bfc251e..2d77c0d0 100644 --- a/td/generate/tl_writer_cpp.cpp +++ b/td/generate/tl_writer_cpp.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -506,7 +506,7 @@ std::string TD_TL_writer_cpp::gen_function_result_type(const tl::tl_tree *result } std::string TD_TL_writer_cpp::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const { for (std::size_t i = 0; i < vars.size(); i++) { assert(vars[i].is_stored == false); @@ -517,15 +517,21 @@ std::string TD_TL_writer_cpp::gen_fetch_function_begin(const std::string &parser assert(arity == 0); if (parser_type == 0) { - return "\n" + returned_type + class_name + "::fetch(" + parser_name + - " &p) {\n" - " return make_tl_object<" + - class_name + - ">(p);\n" - "}\n\n" + - class_name + "::" + class_name + "(" + parser_name + - " &p)\n" - "#define FAIL(error) p.set_error(error)\n"; + std::string result = "\n" + returned_type + class_name + "::fetch(" + parser_name + + " &p) {\n" + " return make_tl_object<" + + class_name + ">("; + if (field_count == 0) { + result += ");\n"; + } else { + result += + "p);\n" + "}\n\n" + + class_name + "::" + class_name + "(" + parser_name + + " &p)\n" + "#define FAIL(error) p.set_error(error)\n"; + } + return result; } return "\n" + returned_type + class_name + "::fetch(" + parser_name + @@ -534,7 +540,7 @@ std::string TD_TL_writer_cpp::gen_fetch_function_begin(const std::string &parser (parser_type == -1 ? "" : " " + fetched_type + "res = make_tl_object<" + class_name + ">();\n"); } -std::string TD_TL_writer_cpp::gen_fetch_function_end(bool has_parent, int field_num, +std::string TD_TL_writer_cpp::gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const { for (std::size_t i = 0; i < vars.size(); i++) { @@ -542,9 +548,11 @@ std::string TD_TL_writer_cpp::gen_fetch_function_end(bool has_parent, int field_ } if (parser_type == 0) { + if (field_count == 0) { + return "}\n"; + } return "#undef FAIL\n" - "{" + - (field_num == 0 ? "\n (void)p;\n" : std::string()) + "}\n"; + "{}\n"; } if (parser_type == -1) { @@ -641,7 +649,7 @@ std::string TD_TL_writer_cpp::gen_fetch_switch_end() const { " }\n"; } -std::string TD_TL_writer_cpp::gen_constructor_begin(int fields_num, const std::string &class_name, +std::string TD_TL_writer_cpp::gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const { return "\n" + class_name + "::" + class_name + "("; } @@ -665,8 +673,8 @@ std::string TD_TL_writer_cpp::gen_constructor_field_init(int field_num, const st (is_default ? "" : gen_field_name(a.name)) + move_end + ")\n"; } -std::string TD_TL_writer_cpp::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const { - if (fields_num == 0) { +std::string TD_TL_writer_cpp::gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const { + if (field_count == 0) { return ") {\n" "}\n"; } diff --git a/td/generate/tl_writer_cpp.h b/td/generate/tl_writer_cpp.h index d626a34a..f757ea7f 100644 --- a/td/generate/tl_writer_cpp.h +++ b/td/generate/tl_writer_cpp.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -77,9 +77,9 @@ class TD_TL_writer_cpp : public TD_TL_writer { std::string gen_function_result_type(const tl::tl_tree *result) const override; std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override; - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override; std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, @@ -97,10 +97,10 @@ class TD_TL_writer_cpp : public TD_TL_writer { std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override; std::string gen_fetch_switch_end() const override; - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override; + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override; std::string gen_constructor_field_init(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; - std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override; + std::string gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const override; }; } // namespace td diff --git a/td/generate/tl_writer_dotnet.h b/td/generate/tl_writer_dotnet.h index 811b0d18..276f4ba3 100644 --- a/td/generate/tl_writer_dotnet.h +++ b/td/generate/tl_writer_dotnet.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -261,7 +261,7 @@ class TlWriterDotNet : public TL_writer { if (is_header_) { ss << ";\n"; } else { - ss << "{\n return REF_NEW NativeObject(::Telegram::Td::Api::ToUnmanaged(this).release());\n}\n"; + ss << " {\n return REF_NEW NativeObject(::Telegram::Td::Api::ToUnmanaged(this).release());\n}\n"; } } return ss.str(); @@ -270,7 +270,7 @@ class TlWriterDotNet : public TL_writer { return ""; } - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override { + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override { std::stringstream ss; ss << "\n"; ss << (is_header_ ? " " : gen_class_name(class_name) + "::") << gen_class_name(class_name) << "("; @@ -312,12 +312,12 @@ class TlWriterDotNet : public TL_writer { return ss.str(); } - std::string gen_constructor_end(const tl_combinator *t, int fields_num, bool is_default) const override { + std::string gen_constructor_end(const tl_combinator *t, int field_count, bool is_default) const override { if (is_header_) { return ");\n"; } std::stringstream ss; - if (fields_num == 0) { + if (field_count == 0) { ss << ") {\n"; } ss << "}\n"; @@ -345,7 +345,7 @@ class TlWriterDotNet : public TL_writer { ss << ";\n"; return; } - ss << "{\n" + ss << " {\n" << " if (!from) {\n" << " return nullptr;\n" << " }\n" @@ -374,7 +374,7 @@ class TlWriterDotNet : public TL_writer { ss << ";\n"; return; } - ss << "{\n" + ss << " {\n" << " return REF_NEW " << class_name << "("; bool is_first = true; for (auto &it : t->args) { @@ -471,11 +471,11 @@ class TlWriterDotNet : public TL_writer { } std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override { return ""; } - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override { return ""; } @@ -522,7 +522,7 @@ class TlWriterDotNet : public TL_writer { ss << ";\n"; return ss.str(); } - ss << "{\n" + ss << " {\n" << " if (!from) {\n" << " return nullptr;\n" << " }\n" @@ -534,7 +534,7 @@ class TlWriterDotNet : public TL_writer { ss << ";\n"; return ss.str(); } - ss << "{\n"; + ss << " {\n"; ss << " return DoFromUnmanaged<" << class_name << "^>(from);\n"; ss << "}\n"; } diff --git a/td/generate/tl_writer_h.cpp b/td/generate/tl_writer_h.cpp index 473f919f..82880695 100644 --- a/td/generate/tl_writer_h.cpp +++ b/td/generate/tl_writer_h.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -253,15 +253,22 @@ std::string TD_TL_writer_h::gen_function_result_type(const tl::tl_tree *result) } std::string TD_TL_writer_h::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const { std::string returned_type = "object_ptr<" + parent_class_name + "> "; if (parser_type == 0) { - return "\n" - " static " + - returned_type + "fetch(" + parser_name + " &p);\n\n" + " explicit " + class_name + "(" + parser_name + - " &p);\n"; + std::string result = + "\n" + " static " + + returned_type + "fetch(" + parser_name + " &p);\n"; + if (field_count != 0) { + result += + "\n" + " explicit " + + class_name + "(" + parser_name + " &p);\n"; + } + return result; } assert(arity == 0); @@ -270,7 +277,7 @@ std::string TD_TL_writer_h::gen_fetch_function_begin(const std::string &parser_n returned_type + "fetch(" + parser_name + " &p);\n"; } -std::string TD_TL_writer_h::gen_fetch_function_end(bool has_parent, int field_num, +std::string TD_TL_writer_h::gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const { return ""; @@ -326,11 +333,11 @@ std::string TD_TL_writer_h::gen_fetch_switch_end() const { return ""; } -std::string TD_TL_writer_h::gen_constructor_begin(int fields_num, const std::string &class_name, +std::string TD_TL_writer_h::gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const { return "\n" " " + - std::string(fields_num == 1 ? "explicit " : "") + class_name + "("; + std::string(field_count == 1 ? "explicit " : "") + class_name + "("; } std::string TD_TL_writer_h::gen_constructor_field_init(int field_num, const std::string &class_name, const tl::arg &a, @@ -338,7 +345,7 @@ std::string TD_TL_writer_h::gen_constructor_field_init(int field_num, const std: return ""; } -std::string TD_TL_writer_h::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const { +std::string TD_TL_writer_h::gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const { return ");\n"; } diff --git a/td/generate/tl_writer_h.h b/td/generate/tl_writer_h.h index bdedd0d7..98e3c416 100644 --- a/td/generate/tl_writer_h.h +++ b/td/generate/tl_writer_h.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -63,9 +63,9 @@ class TD_TL_writer_h : public TD_TL_writer { std::string gen_function_result_type(const tl::tl_tree *result) const override; std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override; - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override; std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, @@ -83,10 +83,10 @@ class TD_TL_writer_h : public TD_TL_writer { std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override; std::string gen_fetch_switch_end() const override; - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override; + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override; std::string gen_constructor_field_init(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; - std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override; + std::string gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const override; }; } // namespace td diff --git a/td/generate/tl_writer_hpp.cpp b/td/generate/tl_writer_hpp.cpp index 6a2557c5..473a97a3 100644 --- a/td/generate/tl_writer_hpp.cpp +++ b/td/generate/tl_writer_hpp.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -138,12 +138,12 @@ std::string TD_TL_writer_hpp::gen_function_result_type(const tl::tl_tree *result } std::string TD_TL_writer_hpp::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const { return ""; } -std::string TD_TL_writer_hpp::gen_fetch_function_end(bool has_parent, int field_num, +std::string TD_TL_writer_hpp::gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const { return ""; @@ -247,7 +247,7 @@ std::string TD_TL_writer_hpp::gen_additional_proxy_function_end(const std::strin "}\n\n"; } -std::string TD_TL_writer_hpp::gen_constructor_begin(int fields_num, const std::string &class_name, +std::string TD_TL_writer_hpp::gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const { return ""; } @@ -262,7 +262,7 @@ std::string TD_TL_writer_hpp::gen_constructor_field_init(int field_num, const st return ""; } -std::string TD_TL_writer_hpp::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const { +std::string TD_TL_writer_hpp::gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const { return ""; } diff --git a/td/generate/tl_writer_hpp.h b/td/generate/tl_writer_hpp.h index 9e87ee84..6f7dca0a 100644 --- a/td/generate/tl_writer_hpp.h +++ b/td/generate/tl_writer_hpp.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -64,9 +64,9 @@ class TD_TL_writer_hpp : public TD_TL_writer { std::string gen_function_result_type(const tl::tl_tree *result) const override; std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override; - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override; std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, @@ -84,12 +84,12 @@ class TD_TL_writer_hpp : public TD_TL_writer { std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override; std::string gen_fetch_switch_end() const override; - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override; + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override; std::string gen_constructor_parameter(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; std::string gen_constructor_field_init(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; - std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override; + std::string gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const override; std::string gen_additional_function(const std::string &function_name, const tl::tl_combinator *t, bool is_function) const override; diff --git a/td/generate/tl_writer_java.cpp b/td/generate/tl_writer_java.cpp index 629d7a74..8bb677de 100644 --- a/td/generate/tl_writer_java.cpp +++ b/td/generate/tl_writer_java.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -347,11 +347,12 @@ std::string TD_TL_writer_java::gen_function_result_type(const tl::tl_tree *resul std::string TD_TL_writer_java::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, const std::string &parent_class_name, int arity, - std::vector &vars, int parser_type) const { + int field_count, std::vector &vars, + int parser_type) const { return ""; } -std::string TD_TL_writer_java::gen_fetch_function_end(bool has_parent, int field_num, +std::string TD_TL_writer_java::gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const { return ""; @@ -402,7 +403,7 @@ std::string TD_TL_writer_java::gen_fetch_switch_end() const { return ""; } -std::string TD_TL_writer_java::gen_constructor_begin(int fields_num, const std::string &class_name, +std::string TD_TL_writer_java::gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const { return "\n" " public " + @@ -442,8 +443,8 @@ std::string TD_TL_writer_java::gen_constructor_field_init(int field_num, const s gen_field_name(a.name) + ";\n"; } -std::string TD_TL_writer_java::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const { - if (fields_num == 0) { +std::string TD_TL_writer_java::gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const { + if (field_count == 0) { return ") {\n" " }\n"; } diff --git a/td/generate/tl_writer_java.h b/td/generate/tl_writer_java.h index 84eb8c2b..06360fc5 100644 --- a/td/generate/tl_writer_java.h +++ b/td/generate/tl_writer_java.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -88,9 +88,9 @@ class TD_TL_writer_java : public tl::TL_writer { std::string gen_function_result_type(const tl::tl_tree *result) const override; std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override; - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override; std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, @@ -108,12 +108,12 @@ class TD_TL_writer_java : public tl::TL_writer { std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override; std::string gen_fetch_switch_end() const override; - std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override; + std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const override; std::string gen_constructor_parameter(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; std::string gen_constructor_field_init(int field_num, const std::string &class_name, const tl::arg &a, bool is_default) const override; - std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override; + std::string gen_constructor_end(const tl::tl_combinator *t, int field_count, bool is_default) const override; }; } // namespace td diff --git a/td/generate/tl_writer_jni_cpp.cpp b/td/generate/tl_writer_jni_cpp.cpp index 2dee197b..cc7bee25 100644 --- a/td/generate/tl_writer_jni_cpp.cpp +++ b/td/generate/tl_writer_jni_cpp.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -380,7 +380,7 @@ std::string TD_TL_writer_jni_cpp::gen_get_id(const std::string &class_name, std: std::string TD_TL_writer_jni_cpp::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, const std::string &parent_class_name, int arity, - std::vector &vars, + int field_count, std::vector &vars, int parser_type) const { for (std::size_t i = 0; i < vars.size(); i++) { assert(vars[i].is_stored == false); @@ -399,7 +399,7 @@ std::string TD_TL_writer_jni_cpp::gen_fetch_function_begin(const std::string &pa fetched_type + "res = make_object<" + class_name + ">();\n"); } -std::string TD_TL_writer_jni_cpp::gen_fetch_function_end(bool has_parent, int field_num, +std::string TD_TL_writer_jni_cpp::gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const { for (std::size_t i = 0; i < vars.size(); i++) { diff --git a/td/generate/tl_writer_jni_cpp.h b/td/generate/tl_writer_jni_cpp.h index 40103d5f..3916bb60 100644 --- a/td/generate/tl_writer_jni_cpp.h +++ b/td/generate/tl_writer_jni_cpp.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -74,9 +74,9 @@ class TD_TL_writer_jni_cpp : public TD_TL_writer_cpp { std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override; std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const override; - std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const override; std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, diff --git a/td/generate/tl_writer_jni_h.cpp b/td/generate/tl_writer_jni_h.cpp index 427ff9a3..0a56d298 100644 --- a/td/generate/tl_writer_jni_h.cpp +++ b/td/generate/tl_writer_jni_h.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/tl_writer_jni_h.h b/td/generate/tl_writer_jni_h.h index 45fe4a99..04e06508 100644 --- a/td/generate/tl_writer_jni_h.h +++ b/td/generate/tl_writer_jni_h.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/generate/tl_writer_td.cpp b/td/generate/tl_writer_td.cpp index c3d9ac95..0b2f64ff 100644 --- a/td/generate/tl_writer_td.cpp +++ b/td/generate/tl_writer_td.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -49,6 +49,10 @@ bool TD_TL_writer::is_combinator_supported(const tl::tl_combinator *constructor) return true; } +bool TD_TL_writer::is_default_constructor_generated(const tl::tl_combinator *t, bool is_function) const { + return tl_name == "td_api" || tl_name == "TdApi" || (t->var_count > 0 && !is_function); +} + int TD_TL_writer::get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const { return storer_name == "TlStorerToString"; } diff --git a/td/generate/tl_writer_td.h b/td/generate/tl_writer_td.h index e630a3b0..a2e530fb 100644 --- a/td/generate/tl_writer_td.h +++ b/td/generate/tl_writer_td.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -35,6 +35,7 @@ class TD_TL_writer : public tl::TL_writer { bool is_built_in_complex_type(const std::string &name) const override; bool is_type_bare(const tl::tl_type *t) const override; bool is_combinator_supported(const tl::tl_combinator *constructor) const override; + bool is_default_constructor_generated(const tl::tl_combinator *t, bool is_function) const override; int get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const override; Mode get_parser_mode(int type) const override; diff --git a/td/mtproto/AuthData.cpp b/td/mtproto/AuthData.cpp index f1aebfa7..61e7e1a2 100644 --- a/td/mtproto/AuthData.cpp +++ b/td/mtproto/AuthData.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -29,7 +29,7 @@ Status MessageIdDuplicateChecker::check(int64 message_id) { } } if (saved_message_ids_.count(message_id) != 0) { - return Status::Error(1, PSLICE() << "Ignore duplicated_message id " << tag("message_id", message_id)); + return Status::Error(1, PSLICE() << "Ignore duplicated message_id " << tag("message_id", message_id)); } saved_message_ids_.insert(message_id); diff --git a/td/mtproto/AuthData.h b/td/mtproto/AuthData.h index fd12a78d..0169c937 100644 --- a/td/mtproto/AuthData.h +++ b/td/mtproto/AuthData.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/AuthKey.h b/td/mtproto/AuthKey.h index 25f15d6a..45fec5f0 100644 --- a/td/mtproto/AuthKey.h +++ b/td/mtproto/AuthKey.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/CryptoStorer.h b/td/mtproto/CryptoStorer.h index ebc8178a..03587a80 100644 --- a/td/mtproto/CryptoStorer.h +++ b/td/mtproto/CryptoStorer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/DhHandshake.cpp b/td/mtproto/DhHandshake.cpp index 69a5015b..51a5d00a 100644 --- a/td/mtproto/DhHandshake.cpp +++ b/td/mtproto/DhHandshake.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/DhHandshake.h b/td/mtproto/DhHandshake.h index e360f5b1..c26f63bb 100644 --- a/td/mtproto/DhHandshake.h +++ b/td/mtproto/DhHandshake.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Handshake.cpp b/td/mtproto/Handshake.cpp index 6b5b12df..111d6c82 100644 --- a/td/mtproto/Handshake.cpp +++ b/td/mtproto/Handshake.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Handshake.h b/td/mtproto/Handshake.h index 1ab9e2fa..718f568c 100644 --- a/td/mtproto/Handshake.h +++ b/td/mtproto/Handshake.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/HandshakeActor.cpp b/td/mtproto/HandshakeActor.cpp index fae45f6c..efcfb8ba 100644 --- a/td/mtproto/HandshakeActor.cpp +++ b/td/mtproto/HandshakeActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/HandshakeActor.h b/td/mtproto/HandshakeActor.h index 4ec17240..652fed1a 100644 --- a/td/mtproto/HandshakeActor.h +++ b/td/mtproto/HandshakeActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/HandshakeConnection.h b/td/mtproto/HandshakeConnection.h index e106b6df..9bd8f60a 100644 --- a/td/mtproto/HandshakeConnection.h +++ b/td/mtproto/HandshakeConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/HttpTransport.cpp b/td/mtproto/HttpTransport.cpp index 4f3738e7..46497da0 100644 --- a/td/mtproto/HttpTransport.cpp +++ b/td/mtproto/HttpTransport.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/HttpTransport.h b/td/mtproto/HttpTransport.h index 76b47690..b96bf7ed 100644 --- a/td/mtproto/HttpTransport.h +++ b/td/mtproto/HttpTransport.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/IStreamTransport.cpp b/td/mtproto/IStreamTransport.cpp index af41f98d..4f3aed7b 100644 --- a/td/mtproto/IStreamTransport.cpp +++ b/td/mtproto/IStreamTransport.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/IStreamTransport.h b/td/mtproto/IStreamTransport.h index fbf85cdf..74621aec 100644 --- a/td/mtproto/IStreamTransport.h +++ b/td/mtproto/IStreamTransport.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/KDF.cpp b/td/mtproto/KDF.cpp index f247c6a3..f77cac0c 100644 --- a/td/mtproto/KDF.cpp +++ b/td/mtproto/KDF.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/KDF.h b/td/mtproto/KDF.h index d8056b2c..d01e261d 100644 --- a/td/mtproto/KDF.h +++ b/td/mtproto/KDF.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/NoCryptoStorer.h b/td/mtproto/NoCryptoStorer.h index def745cd..c3ffb7d8 100644 --- a/td/mtproto/NoCryptoStorer.h +++ b/td/mtproto/NoCryptoStorer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/PacketInfo.h b/td/mtproto/PacketInfo.h index ee8e64d4..2370dc86 100644 --- a/td/mtproto/PacketInfo.h +++ b/td/mtproto/PacketInfo.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/PacketStorer.h b/td/mtproto/PacketStorer.h index 7ad51482..e5ce2673 100644 --- a/td/mtproto/PacketStorer.h +++ b/td/mtproto/PacketStorer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Ping.cpp b/td/mtproto/Ping.cpp index f7748e0c..f9c4e410 100644 --- a/td/mtproto/Ping.cpp +++ b/td/mtproto/Ping.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Ping.h b/td/mtproto/Ping.h index fa670d2a..471be12d 100644 --- a/td/mtproto/Ping.h +++ b/td/mtproto/Ping.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/PingConnection.cpp b/td/mtproto/PingConnection.cpp index cb5c3664..561125ab 100644 --- a/td/mtproto/PingConnection.cpp +++ b/td/mtproto/PingConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/PingConnection.h b/td/mtproto/PingConnection.h index a026679c..01afaf90 100644 --- a/td/mtproto/PingConnection.h +++ b/td/mtproto/PingConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/ProxySecret.cpp b/td/mtproto/ProxySecret.cpp index 0c401ad0..de4962e1 100644 --- a/td/mtproto/ProxySecret.cpp +++ b/td/mtproto/ProxySecret.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/ProxySecret.h b/td/mtproto/ProxySecret.h index 960c30f5..c4aade6c 100644 --- a/td/mtproto/ProxySecret.h +++ b/td/mtproto/ProxySecret.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Query.h b/td/mtproto/Query.h index 9b4887c1..8c3d8369 100644 --- a/td/mtproto/Query.h +++ b/td/mtproto/Query.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/RawConnection.cpp b/td/mtproto/RawConnection.cpp index ad032c5d..e514d7ff 100644 --- a/td/mtproto/RawConnection.cpp +++ b/td/mtproto/RawConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -39,7 +39,7 @@ void RawConnection::send_crypto(const Storer &storer, int64 session_id, int64 sa if (tmp.second) { use_quick_ack = true; } else { - LOG(ERROR) << "Quick ack collision " << tag("quick_ack", info.message_ack); + LOG(ERROR) << "Quick ack " << info.message_ack << " collision"; } } @@ -130,20 +130,20 @@ Status RawConnection::on_read_mtproto_error(int32 error_code) { if (stats_callback_) { stats_callback_->on_mtproto_error(); } - return Status::Error(500, PSLICE() << "Mtproto error: " << error_code); + return Status::Error(500, PSLICE() << "MTProto error: " << error_code); } if (error_code == -404) { - return Status::Error(-404, PSLICE() << "Mtproto error: " << error_code); + return Status::Error(-404, PSLICE() << "MTProto error: " << error_code); } - return Status::Error(PSLICE() << "Mtproto error: " << error_code); + return Status::Error(PSLICE() << "MTProto error: " << error_code); } Status RawConnection::on_quick_ack(uint32 quick_ack, Callback &callback) { auto it = quick_ack_to_token_.find(quick_ack); if (it == quick_ack_to_token_.end()) { - LOG(WARNING) << Status::Error(PSLICE() << "Unknown " << tag("quick_ack", quick_ack)); + LOG(WARNING) << Status::Error(PSLICE() << "Unknown quick_ack " << quick_ack); return Status::OK(); - // TODO: return Status::Error(PSLICE() << "Unknown " << tag("quick_ack", quick_ack)); + // TODO: return Status::Error(PSLICE() << "Unknown quick_ack " << quick_ack); } auto token = it->second; quick_ack_to_token_.erase(it); diff --git a/td/mtproto/RawConnection.h b/td/mtproto/RawConnection.h index 5a816cab..4864ac61 100644 --- a/td/mtproto/RawConnection.h +++ b/td/mtproto/RawConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/SessionConnection.cpp b/td/mtproto/SessionConnection.cpp index 139fa355..ea715785 100644 --- a/td/mtproto/SessionConnection.cpp +++ b/td/mtproto/SessionConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,6 +7,7 @@ #include "td/mtproto/SessionConnection.h" #include "td/mtproto/AuthData.h" +#include "td/mtproto/AuthKey.h" #include "td/mtproto/CryptoStorer.h" #include "td/mtproto/PacketStorer.h" #include "td/mtproto/Transport.h" @@ -808,8 +809,9 @@ std::pair SessionConnection::encrypted_bind(int64 perm_key, info.salt = Random::secure_int64(); info.session_id = Random::secure_int64(); - auto packet = BufferWriter{Transport::write(query_storer, auth_data_->get_main_auth_key(), &info), 0, 0}; - Transport::write(query_storer, auth_data_->get_main_auth_key(), &info, packet.as_slice()); + const AuthKey &main_auth_key = auth_data_->get_main_auth_key(); + auto packet = BufferWriter{Transport::write(query_storer, main_auth_key, &info), 0, 0}; + Transport::write(query_storer, main_auth_key, &info, packet.as_slice()); return std::make_pair(query.message_id, packet.as_buffer_slice()); } diff --git a/td/mtproto/SessionConnection.h b/td/mtproto/SessionConnection.h index b2884402..84bd6408 100644 --- a/td/mtproto/SessionConnection.h +++ b/td/mtproto/SessionConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TcpTransport.cpp b/td/mtproto/TcpTransport.cpp index 7537d0d4..93452612 100644 --- a/td/mtproto/TcpTransport.cpp +++ b/td/mtproto/TcpTransport.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TcpTransport.h b/td/mtproto/TcpTransport.h index a386a0dc..88d81da9 100644 --- a/td/mtproto/TcpTransport.h +++ b/td/mtproto/TcpTransport.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TlsInit.cpp b/td/mtproto/TlsInit.cpp index 40375b20..115512eb 100644 --- a/td/mtproto/TlsInit.cpp +++ b/td/mtproto/TlsInit.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TlsInit.h b/td/mtproto/TlsInit.h index 09b1f6b5..b93f76c2 100644 --- a/td/mtproto/TlsInit.h +++ b/td/mtproto/TlsInit.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TlsReaderByteFlow.cpp b/td/mtproto/TlsReaderByteFlow.cpp index 312c5fbe..005af6db 100644 --- a/td/mtproto/TlsReaderByteFlow.cpp +++ b/td/mtproto/TlsReaderByteFlow.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TlsReaderByteFlow.h b/td/mtproto/TlsReaderByteFlow.h index 8fe205bc..fd067400 100644 --- a/td/mtproto/TlsReaderByteFlow.h +++ b/td/mtproto/TlsReaderByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/Transport.cpp b/td/mtproto/Transport.cpp index 3f03a769..a2dadbc0 100644 --- a/td/mtproto/Transport.cpp +++ b/td/mtproto/Transport.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -337,7 +337,7 @@ size_t Transport::write_no_crypto(const Storer &storer, PacketInfo *info, Mutabl return size; } // NoCryptoHeader - as(dest.begin()) = uint64(0); + as(dest.begin()) = 0; auto real_size = storer.store(dest.ubegin() + sizeof(uint64)); CHECK(real_size == storer.size()); return size; diff --git a/td/mtproto/Transport.h b/td/mtproto/Transport.h index c4ec0fe9..5daca489 100644 --- a/td/mtproto/Transport.h +++ b/td/mtproto/Transport.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/TransportType.h b/td/mtproto/TransportType.h index b4b5a85e..19987ebe 100644 --- a/td/mtproto/TransportType.h +++ b/td/mtproto/TransportType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/crypto.cpp b/td/mtproto/crypto.cpp index 2263477e..596a9263 100644 --- a/td/mtproto/crypto.cpp +++ b/td/mtproto/crypto.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -77,12 +77,10 @@ Result RSA::from_pem(Slice pem) { } int64 RSA::get_fingerprint() const { - mtproto_api::rsa_public_key public_key; // string objects are necessary, because mtproto_api::rsa_public_key contains Slice inside string n_str = n_.to_binary(); string e_str = e_.to_binary(); - public_key.n_ = n_str; - public_key.e_ = e_str; + mtproto_api::rsa_public_key public_key(n_str, e_str); size_t size = tl_calc_length(public_key); std::vector tmp(size); size = tl_store_unsafe(public_key, tmp.data()); diff --git a/td/mtproto/crypto.h b/td/mtproto/crypto.h index 042b029b..119815ab 100644 --- a/td/mtproto/crypto.h +++ b/td/mtproto/crypto.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/utils.cpp b/td/mtproto/utils.cpp index 078385c6..40274d5b 100644 --- a/td/mtproto/utils.cpp +++ b/td/mtproto/utils.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/mtproto/utils.h b/td/mtproto/utils.h index 34903b36..d7c6cbbc 100644 --- a/td/mtproto/utils.h +++ b/td/mtproto/utils.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/AccessRights.h b/td/telegram/AccessRights.h index 2f46768c..bc7b4937 100644 --- a/td/telegram/AccessRights.h +++ b/td/telegram/AccessRights.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/AnimationsManager.cpp b/td/telegram/AnimationsManager.cpp index fb0aa1fa..39b6e1fb 100644 --- a/td/telegram/AnimationsManager.cpp +++ b/td/telegram/AnimationsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -309,8 +309,8 @@ tl_object_ptr AnimationsManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); @@ -362,7 +362,7 @@ SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -629,7 +629,7 @@ void AnimationsManager::add_saved_animation(const tl_object_ptr &&promise) { @@ -646,38 +646,31 @@ void AnimationsManager::send_save_gif_query(FileId animation_id, bool unsave, Pr ->send(animation_id, file_view.remote_location().as_input_document(), unsave); } -void AnimationsManager::add_saved_animation_inner(FileId animation_id, Promise &&promise) { - if (add_saved_animation_impl(animation_id, promise)) { - send_save_gif_query(animation_id, false, std::move(promise)); - } -} - void AnimationsManager::add_saved_animation_by_id(FileId animation_id) { // TODO log event - Promise promise; - add_saved_animation_impl(animation_id, promise); + add_saved_animation_impl(animation_id, false, Auto()); } -bool AnimationsManager::add_saved_animation_impl(FileId animation_id, Promise &promise) { +void AnimationsManager::add_saved_animation_impl(FileId animation_id, bool add_on_server, Promise &&promise) { CHECK(!td_->auth_manager_->is_bot()); auto file_view = td_->file_manager_->get_file_view(animation_id); if (file_view.empty()) { - promise.set_error(Status::Error(7, "Animation file not found")); - return false; + return promise.set_error(Status::Error(7, "Animation file not found")); } LOG(INFO) << "Add saved animation " << animation_id << " with main file " << file_view.file_id(); if (!are_saved_animations_loaded_) { - load_saved_animations(PromiseCreator::lambda([animation_id, promise = std::move(promise)](Result<> result) mutable { - if (result.is_ok()) { - send_closure(G()->animations_manager(), &AnimationsManager::add_saved_animation_inner, animation_id, - std::move(promise)); - } else { - promise.set_error(result.move_as_error()); - } - })); - return false; + load_saved_animations( + PromiseCreator::lambda([animation_id, add_on_server, promise = std::move(promise)](Result<> result) mutable { + if (result.is_ok()) { + send_closure(G()->animations_manager(), &AnimationsManager::add_saved_animation_impl, animation_id, + add_on_server, std::move(promise)); + } else { + promise.set_error(result.move_as_error()); + } + })); + return; } auto is_equal = [animation_id](FileId file_id) { @@ -692,31 +685,25 @@ bool AnimationsManager::add_saved_animation_impl(FileId animation_id, Promisemime_type != "video/mp4") { - promise.set_error(Status::Error(7, "Only MPEG4 animations can be saved")); - return false; + return promise.set_error(Status::Error(7, "Only MPEG4 animations can be saved")); } if (!file_view.has_remote_location()) { - promise.set_error(Status::Error(7, "Can save only sent animations")); - return false; + return promise.set_error(Status::Error(7, "Can save only sent animations")); } if (file_view.remote_location().is_web()) { - promise.set_error(Status::Error(7, "Can't save web animations")); - return false; + return promise.set_error(Status::Error(7, "Can't save web animations")); } if (!file_view.remote_location().is_document()) { - promise.set_error(Status::Error(7, "Can't save encrypted animations")); - return false; + return promise.set_error(Status::Error(7, "Can't save encrypted animations")); } auto it = std::find_if(saved_animation_ids_.begin(), saved_animation_ids_.end(), is_equal); @@ -735,7 +722,9 @@ bool AnimationsManager::add_saved_animation_impl(FileId animation_id, Promise &input_file, @@ -754,8 +743,7 @@ void AnimationsManager::remove_saved_animation(const tl_object_ptris_online()) { + if (td_->is_online() && !td_->auth_manager_->is_bot()) { get_saved_animations(Auto()); } } diff --git a/td/telegram/AnimationsManager.h b/td/telegram/AnimationsManager.h index 102f4409..b843897e 100644 --- a/td/telegram/AnimationsManager.h +++ b/td/telegram/AnimationsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -109,9 +109,7 @@ class AnimationsManager : public Actor { int32 get_saved_animations_hash(const char *source) const; - void add_saved_animation_inner(FileId animation_id, Promise &&promise); - - bool add_saved_animation_impl(FileId animation_id, Promise &promise); + void add_saved_animation_impl(FileId animation_id, bool add_on_server, Promise &&promise); void load_saved_animations(Promise &&promise); diff --git a/td/telegram/AnimationsManager.hpp b/td/telegram/AnimationsManager.hpp index 0853a330..7052079e 100644 --- a/td/telegram/AnimationsManager.hpp +++ b/td/telegram/AnimationsManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/AudiosManager.cpp b/td/telegram/AudiosManager.cpp index acb45a69..eeeb4cde 100644 --- a/td/telegram/AudiosManager.cpp +++ b/td/telegram/AudiosManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -194,7 +194,7 @@ SecretInputMedia AudiosManager::get_secret_input_media(FileId audio_file_id, return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -225,8 +225,8 @@ tl_object_ptr AudiosManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); diff --git a/td/telegram/AudiosManager.h b/td/telegram/AudiosManager.h index 1c764e67..eb00909d 100644 --- a/td/telegram/AudiosManager.h +++ b/td/telegram/AudiosManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/AudiosManager.hpp b/td/telegram/AudiosManager.hpp index 2b45c1a6..eae2aa2c 100644 --- a/td/telegram/AudiosManager.hpp +++ b/td/telegram/AudiosManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/AuthManager.cpp b/td/telegram/AuthManager.cpp index 4f73cef5..64ede0c1 100644 --- a/td/telegram/AuthManager.cpp +++ b/td/telegram/AuthManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -23,13 +23,14 @@ #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" #include "td/telegram/TopDialogManager.h" -#include "td/telegram/UniqueId.h" #include "td/telegram/UpdatesManager.h" #include "td/actor/PromiseFuture.h" +#include "td/utils/base64.h" #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" #include "td/utils/ScopeGuard.h" #include "td/utils/Slice.h" #include "td/utils/Time.h" @@ -78,6 +79,9 @@ void AuthManager::tear_down() { } bool AuthManager::is_bot() const { + if (net_query_id_ != 0 && net_query_type_ == NetQueryType::BotAuthentication) { + return true; + } return is_bot_ && (state_ == State::Ok || state_ == State::LoggingOut || state_ == State::DestroyingKeys || state_ == State::Closing); } @@ -96,18 +100,21 @@ bool AuthManager::is_authorized() const { tl_object_ptr AuthManager::get_authorization_state_object(State authorization_state) const { switch (authorization_state) { - case State::Ok: - return make_tl_object(); - case State::WaitCode: - return send_code_helper_.get_authorization_state_wait_code(); case State::WaitPhoneNumber: return make_tl_object(); - case State::WaitRegistration: - return make_tl_object( - terms_of_service_.get_terms_of_service_object()); + case State::WaitCode: + return send_code_helper_.get_authorization_state_wait_code(); + case State::WaitQrCodeConfirmation: + return make_tl_object("tg://login?token=" + + base64url_encode(login_token_)); case State::WaitPassword: return make_tl_object( wait_password_state_.hint_, wait_password_state_.has_recovery_, wait_password_state_.email_address_pattern_); + case State::WaitRegistration: + return make_tl_object( + terms_of_service_.get_terms_of_service_object()); + case State::Ok: + return make_tl_object(); case State::LoggingOut: case State::DestroyingKeys: return make_tl_object(); @@ -143,9 +150,9 @@ void AuthManager::check_bot_token(uint64 query_id, string bot_token) { } if (state_ != State::WaitPhoneNumber && state_ != State::Ok) { // TODO do not allow State::Ok - return on_query_error(query_id, Status::Error(8, "checkAuthenticationBotToken unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to checkAuthenticationBotToken unexpected")); } - if (!send_code_helper_.phone_number().empty()) { + if (!send_code_helper_.phone_number().empty() || was_qr_code_request_) { return on_query_error( query_id, Status::Error(8, "Cannot set bot token after authentication beginning. You need to log out first")); } @@ -173,6 +180,69 @@ void AuthManager::check_bot_token(uint64 query_id, string bot_token) { DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); } +void AuthManager::request_qr_code_authentication(uint64 query_id, vector other_user_ids) { + if (state_ != State::WaitPhoneNumber) { + if ((state_ == State::WaitCode || state_ == State::WaitPassword || state_ == State::WaitRegistration) && + net_query_id_ == 0) { + // ok + } else { + return on_query_error(query_id, Status::Error(8, "Call to requestQrCodeAuthentication unexpected")); + } + } + if (was_check_bot_token_) { + return on_query_error( + query_id, + Status::Error(8, + "Cannot request QR code authentication after bot token was entered. You need to log out first")); + } + for (auto &other_user_id : other_user_ids) { + UserId user_id(other_user_id); + if (!user_id.is_valid()) { + return on_query_error(query_id, Status::Error(400, "Invalid user_id among other user_ids")); + } + } + + other_user_ids_ = std::move(other_user_ids); + send_code_helper_ = SendCodeHelper(); + terms_of_service_ = TermsOfService(); + was_qr_code_request_ = true; + + on_new_query(query_id); + + send_export_login_token_query(); +} + +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)); +} + +void AuthManager::set_login_token_expires_at(double login_token_expires_at) { + login_token_expires_at_ = login_token_expires_at; + poll_export_login_code_timeout_.cancel_timeout(); + poll_export_login_code_timeout_.set_callback(std::move(on_update_login_token_static)); + poll_export_login_code_timeout_.set_callback_data(static_cast(G()->td().get_actor_unsafe())); + poll_export_login_code_timeout_.set_timeout_at(login_token_expires_at_); +} + +void AuthManager::on_update_login_token_static(void *td) { + static_cast(td)->auth_manager_->on_update_login_token(); +} + +void AuthManager::on_update_login_token() { + if (G()->close_flag()) { + return; + } + if (state_ != State::WaitQrCodeConfirmation) { + return; + } + + send_export_login_token_query(); +} + void AuthManager::set_phone_number(uint64 query_id, string phone_number, td_api::object_ptr settings) { if (state_ != State::WaitPhoneNumber) { @@ -180,7 +250,7 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number, net_query_id_ == 0) { // ok } else { - return on_query_error(query_id, Status::Error(8, "setAuthenticationPhoneNumber unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to setAuthenticationPhoneNumber unexpected")); } } if (was_check_bot_token_) { @@ -191,27 +261,25 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number, return on_query_error(query_id, Status::Error(8, "Phone number can't be empty")); } - auto r_send_code = send_code_helper_.send_code(phone_number, settings, api_id_, api_hash_); - if (r_send_code.is_error()) { + other_user_ids_.clear(); + was_qr_code_request_ = false; + + if (send_code_helper_.phone_number() != phone_number) { send_code_helper_ = SendCodeHelper(); terms_of_service_ = TermsOfService(); - r_send_code = send_code_helper_.send_code(phone_number, settings, api_id_, api_hash_); - if (r_send_code.is_error()) { - return on_query_error(query_id, r_send_code.move_as_error()); - } } on_new_query(query_id); - auto unique_id = UniqueId::next(); start_net_query(NetQueryType::SendCode, - G()->net_query_creator().create(unique_id, create_storer(r_send_code.move_as_ok()), DcId::main(), - NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + 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)); } void AuthManager::resend_authentication_code(uint64 query_id) { - if (state_ != State::WaitCode || was_check_bot_token_) { - return on_query_error(query_id, Status::Error(8, "resendAuthenticationCode unexpected")); + if (state_ != State::WaitCode) { + return on_query_error(query_id, Status::Error(8, "Call to resendAuthenticationCode unexpected")); } auto r_resend_code = send_code_helper_.resend_code(); @@ -228,7 +296,7 @@ void AuthManager::resend_authentication_code(uint64 query_id) { void AuthManager::check_code(uint64 query_id, string code) { if (state_ != State::WaitCode) { - return on_query_error(query_id, Status::Error(8, "checkAuthenticationCode unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to checkAuthenticationCode unexpected")); } code_ = std::move(code); @@ -242,7 +310,7 @@ void AuthManager::check_code(uint64 query_id, string code) { void AuthManager::register_user(uint64 query_id, string first_name, string last_name) { if (state_ != State::WaitRegistration) { - return on_query_error(query_id, Status::Error(8, "registerUser unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to registerUser unexpected")); } on_new_query(query_id); @@ -254,15 +322,15 @@ 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(), - code_, first_name, last_name)), - DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); + 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)); } void AuthManager::check_password(uint64 query_id, string password) { if (state_ != State::WaitPassword) { - return on_query_error(query_id, Status::Error(8, "checkAuthenticationPassword unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to checkAuthenticationPassword unexpected")); } LOG(INFO) << "Have SRP id " << wait_password_state_.srp_id_; @@ -275,7 +343,7 @@ void AuthManager::check_password(uint64 query_id, string password) { void AuthManager::request_password_recovery(uint64 query_id) { if (state_ != State::WaitPassword) { - return on_query_error(query_id, Status::Error(8, "requestAuthenticationPasswordRecovery unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to requestAuthenticationPasswordRecovery unexpected")); } on_new_query(query_id); @@ -286,7 +354,7 @@ void AuthManager::request_password_recovery(uint64 query_id) { void AuthManager::recover_password(uint64 query_id, string code) { if (state_ != State::WaitPassword) { - return on_query_error(query_id, Status::Error(8, "recoverAuthenticationPassword unexpected")); + return on_query_error(query_id, Status::Error(8, "Call to recoverAuthenticationPassword unexpected")); } on_new_query(query_id); @@ -381,36 +449,112 @@ void AuthManager::start_net_query(NetQueryType net_query_type, NetQueryPtr net_q void AuthManager::on_send_code_result(NetQueryPtr &result) { auto r_sent_code = fetch_result(result->ok()); if (r_sent_code.is_error()) { - if (r_sent_code.error().message() == CSlice("PHONE_NUMBER_BANNED")) { - LOG(PLAIN) << "Your phone number was banned for suspicious activity. If you think that this is a mistake, please " - "write to recover@telegram.org your phone number and other details to recover the account."; - } return on_query_error(r_sent_code.move_as_error()); } auto sent_code = r_sent_code.move_as_ok(); LOG(INFO) << "Receive " << to_string(sent_code); - if (terms_of_service_.get_id().empty()) { - terms_of_service_ = TermsOfService(std::move(sent_code->terms_of_service_)); - } - send_code_helper_.on_sent_code(std::move(sent_code)); update_state(State::WaitCode, true); on_query_ok(); } +void AuthManager::on_request_qr_code_result(NetQueryPtr &result, bool is_import) { + Status status; + if (result->is_ok()) { + auto r_login_token = fetch_result(result->ok()); + if (r_login_token.is_ok()) { + auto login_token = r_login_token.move_as_ok(); + + if (is_import) { + CHECK(DcId::is_valid(imported_dc_id_)); + G()->net_query_dispatcher().set_main_dc_id(imported_dc_id_); + imported_dc_id_ = -1; + } + + on_get_login_token(std::move(login_token)); + return; + } + + status = r_login_token.move_as_error(); + } else { + status = std::move(result->error()); + } + CHECK(status.is_error()); + + LOG(INFO) << "Receive " << status << " for login token " << (is_import ? "import" : "export"); + if (is_import) { + imported_dc_id_ = -1; + } + if (query_id_ != 0) { + on_query_error(std::move(status)); + } else { + login_code_retry_delay_ = clamp(2 * login_code_retry_delay_, 1, 60); + set_login_token_expires_at(Time::now() + login_code_retry_delay_); + } +} + +void AuthManager::on_get_login_token(tl_object_ptr login_token) { + LOG(INFO) << "Receive " << to_string(login_token); + + login_code_retry_delay_ = 0; + + CHECK(login_token != nullptr); + switch (login_token->get_id()) { + case telegram_api::auth_loginToken::ID: { + auto token = move_tl_object_as(login_token); + login_token_ = token->token_.as_slice().str(); + set_login_token_expires_at(Time::now() + td::max(token->expires_ - G()->server_time(), 1.0)); + update_state(State::WaitQrCodeConfirmation, true); + if (query_id_ != 0) { + on_query_ok(); + } + break; + } + case telegram_api::auth_loginTokenMigrateTo::ID: { + auto token = move_tl_object_as(login_token); + if (!DcId::is_valid(token->dc_id_)) { + LOG(ERROR) << "Receive wrong DC " << token->dc_id_; + return; + } + if (query_id_ != 0) { + on_query_ok(); + } + + imported_dc_id_ = token->dc_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)); + break; + } + case telegram_api::auth_loginTokenSuccess::ID: { + auto token = move_tl_object_as(login_token); + on_get_authorization(std::move(token->authorization_)); + break; + } + default: + UNREACHABLE(); + } +} + void AuthManager::on_get_password_result(NetQueryPtr &result) { - auto r_password = fetch_result(result->ok()); - if (r_password.is_error()) { + Result> r_password; + if (result->is_error()) { + r_password = std::move(result->error()); + } else { + r_password = fetch_result(result->ok()); + } + if (r_password.is_error() && query_id_ != 0) { return on_query_error(r_password.move_as_error()); } - auto password = r_password.move_as_ok(); + auto password = r_password.is_ok() ? r_password.move_as_ok() : nullptr; LOG(INFO) << "Receive password info: " << to_string(password); wait_password_state_ = WaitPasswordState(); - if (password->current_algo_ != nullptr) { + if (password != nullptr && password->current_algo_ != nullptr) { switch (password->current_algo_->get_id()) { case telegram_api::passwordKdfAlgoUnknown::ID: return on_query_error(Status::Error(400, "Application update is needed to log in")); @@ -431,6 +575,11 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { default: UNREACHABLE(); } + } else if (was_qr_code_request_) { + imported_dc_id_ = -1; + login_code_retry_delay_ = clamp(2 * login_code_retry_delay_, 1, 60); + set_login_token_expires_at(Time::now() + login_code_retry_delay_); + return; } else { start_net_query(NetQueryType::SignIn, G()->net_query_creator().create( @@ -440,6 +589,11 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { return; } + if (imported_dc_id_ != -1) { + G()->net_query_dispatcher().set_main_dc_id(imported_dc_id_); + imported_dc_id_ = -1; + } + if (state_ == State::WaitPassword) { LOG(INFO) << "Have SRP id " << wait_password_state_.srp_id_; auto hash = PasswordManager::get_input_check_password(password_, wait_password_state_.current_client_salt_, @@ -452,7 +606,9 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); } else { update_state(State::WaitPassword); - on_query_ok(); + if (query_id_ != 0) { + on_query_ok(); + } } } @@ -468,17 +624,15 @@ void AuthManager::on_request_password_recovery_result(NetQueryPtr &result) { on_query_ok(); } -void AuthManager::on_authentication_result(NetQueryPtr &result, bool expected_flag) { +void AuthManager::on_authentication_result(NetQueryPtr &result, bool is_from_current_query) { auto r_sign_in = fetch_result(result->ok()); if (r_sign_in.is_error()) { - if (expected_flag && query_id_ != 0) { + if (is_from_current_query && query_id_ != 0) { return on_query_error(r_sign_in.move_as_error()); } return; } - auto sign_in = r_sign_in.move_as_ok(); - CHECK(sign_in->get_id() == telegram_api::auth_authorization::ID); - on_authorization(std::move(sign_in)); + on_get_authorization(r_sign_in.move_as_ok()); } void AuthManager::on_log_out_result(NetQueryPtr &result) { @@ -549,14 +703,26 @@ void AuthManager::on_delete_account_result(NetQueryPtr &result) { } } -void AuthManager::on_authorization(tl_object_ptr auth) { +void AuthManager::on_get_authorization(tl_object_ptr auth_ptr) { if (state_ == State::Ok) { - LOG(WARNING) << "Ignore duplicated auth.authorization"; + LOG(WARNING) << "Ignore duplicated auth.Authorization"; if (query_id_ != 0) { on_query_ok(); } return; } + CHECK(auth_ptr != nullptr); + if (auth_ptr->get_id() == telegram_api::auth_authorizationSignUpRequired::ID) { + auto sign_up_required = telegram_api::move_object_as(auth_ptr); + terms_of_service_ = TermsOfService(std::move(sign_up_required->terms_of_service_)); + update_state(State::WaitRegistration); + if (query_id_ != 0) { + on_query_ok(); + } + return; + } + auto auth = telegram_api::move_object_as(auth_ptr); + G()->shared_config().set_option_integer("authorization_date", G()->unix_time()); if (was_check_bot_token_) { is_bot_ = true; @@ -566,7 +732,7 @@ void AuthManager::on_authorization(tl_object_ptrcontacts_manager_->on_get_user(std::move(auth->user_), "on_authorization", true); + td->contacts_manager_->on_get_user(std::move(auth->user_), "on_get_authorization", true); update_state(State::Ok, true); if (!td->contacts_manager_->get_my_id().is_valid()) { LOG(ERROR) << "Server doesn't send proper authorization"; @@ -581,7 +747,7 @@ void AuthManager::on_authorization(tl_object_ptrnotification_manager_->init(); send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up); - td->updates_manager_->get_difference("on_authorization"); + td->updates_manager_->get_difference("on_get_authorization"); td->on_online_updated(false, true); td->schedule_get_terms_of_service(0); if (!is_bot()) { @@ -598,33 +764,47 @@ void AuthManager::on_result(NetQueryPtr result) { result->clear(); }; NetQueryType type = NetQueryType::None; + LOG(INFO) << "Receive result of query " << result->id() << ", expecting " << net_query_id_ << " with type " + << static_cast(net_query_type_); if (result->id() == net_query_id_) { net_query_id_ = 0; type = net_query_type_; net_query_type_ = NetQueryType::None; if (result->is_error()) { - if (type == NetQueryType::SignIn && result->error().code() == 401 && - result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) { + if ((type == NetQueryType::SignIn || type == NetQueryType::RequestQrCode || type == NetQueryType::ImportQrCode) && + result->error().code() == 401 && result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) { + auto dc_id = DcId::main(); + if (type == NetQueryType::ImportQrCode) { + CHECK(DcId::is_valid(imported_dc_id_)); + dc_id = DcId::internal(imported_dc_id_); + } 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(create_storer(telegram_api::account_getPassword()), dc_id, + NetQuery::Type::Common, NetQuery::AuthFlag::Off)); return; } - if (type == NetQueryType::SignIn && result->error().message() == CSlice("PHONE_NUMBER_UNOCCUPIED")) { - code_ = "11111"; - update_state(State::WaitRegistration); - on_query_ok(); - return; + if (result->error().message() == CSlice("PHONE_NUMBER_BANNED")) { + LOG(PLAIN) + << "Your phone number was banned for suspicious activity. If you think that this is a mistake, please " + "write to recover@telegram.org your phone number and other details to recover the account."; } - if (type != NetQueryType::LogOut) { + if (type != NetQueryType::LogOut && type != NetQueryType::DeleteAccount) { if (query_id_ != 0) { if (state_ == State::WaitPhoneNumber) { + other_user_ids_.clear(); send_code_helper_ = SendCodeHelper(); terms_of_service_ = TermsOfService(); + was_qr_code_request_ = false; + was_check_bot_token_ = false; } on_query_error(std::move(result->error())); + return; + } + if (type != NetQueryType::RequestQrCode && type != NetQueryType::ImportQrCode && + type != NetQueryType::GetPassword) { + LOG(INFO) << "Ignore error for net query of type " << static_cast(net_query_type_); + return; } - return; } } } else if (result->is_ok() && result->ok_tl_constructor() == telegram_api::auth_authorization::ID) { @@ -647,6 +827,12 @@ void AuthManager::on_result(NetQueryPtr result) { case NetQueryType::SendCode: on_send_code_result(result); break; + case NetQueryType::RequestQrCode: + on_request_qr_code_result(result, false); + break; + case NetQueryType::ImportQrCode: + on_request_qr_code_result(result, true); + break; case NetQueryType::GetPassword: on_get_password_result(result); break; @@ -697,10 +883,24 @@ bool AuthManager::load_state() { return false; } if (!db_state.state_timestamp_.is_in_past()) { - LOG(INFO) << "Ignore auth_state: timestamp in future"; + LOG(INFO) << "Ignore auth_state: timestamp in the future"; return false; } - if (Timestamp::at(db_state.state_timestamp_.at() + 5 * 60).is_in_past()) { + auto state_timeout = [state = db_state.state_] { + switch (state) { + case State::WaitPassword: + case State::WaitRegistration: + return 86400; + case State::WaitCode: + case State::WaitQrCodeConfirmation: + return 5 * 60; + default: + UNREACHABLE(); + return 0; + } + }(); + + if (Timestamp::at(db_state.state_timestamp_.at() + state_timeout).is_in_past()) { LOG(INFO) << "Ignore auth_state: expired " << db_state.state_timestamp_.in(); return false; } @@ -708,11 +908,14 @@ bool AuthManager::load_state() { LOG(INFO) << "Load auth_state from database: " << tag("state", static_cast(db_state.state_)); if (db_state.state_ == State::WaitCode) { send_code_helper_ = std::move(db_state.send_code_helper_); - terms_of_service_ = std::move(db_state.terms_of_service_); + } else if (db_state.state_ == State::WaitQrCodeConfirmation) { + other_user_ids_ = std::move(db_state.other_user_ids_); + login_token_ = std::move(db_state.login_token_); + set_login_token_expires_at(db_state.login_token_expires_at_); } else if (db_state.state_ == State::WaitPassword) { wait_password_state_ = std::move(db_state.wait_password_state_); } else if (db_state.state_ == State::WaitRegistration) { - code_ = "11111"; // the code has already been checked + send_code_helper_ = std::move(db_state.send_code_helper_); terms_of_service_ = std::move(db_state.terms_of_service_); } else { UNREACHABLE(); @@ -722,7 +925,8 @@ bool AuthManager::load_state() { } void AuthManager::save_state() { - if (state_ != State::WaitCode && state_ != State::WaitPassword && state_ != State::WaitRegistration) { + if (state_ != State::WaitCode && state_ != State::WaitQrCodeConfirmation && state_ != State::WaitPassword && + state_ != State::WaitRegistration) { if (state_ != State::Closing) { G()->td_db()->get_binlog_pmc()->erase("auth_state"); } @@ -731,12 +935,15 @@ void AuthManager::save_state() { DbState db_state = [&] { if (state_ == State::WaitCode) { - return DbState::wait_code(api_id_, api_hash_, send_code_helper_, terms_of_service_); + return DbState::wait_code(api_id_, api_hash_, send_code_helper_); + } else if (state_ == State::WaitQrCodeConfirmation) { + return DbState::wait_qr_code_confirmation(api_id_, api_hash_, other_user_ids_, login_token_, + login_token_expires_at_); } else if (state_ == State::WaitPassword) { return DbState::wait_password(api_id_, api_hash_, wait_password_state_); } else { CHECK(state_ == State::WaitRegistration); - return DbState::wait_registration(api_id_, api_hash_, terms_of_service_); + return DbState::wait_registration(api_id_, api_hash_, send_code_helper_, terms_of_service_); } }(); G()->td_db()->get_binlog_pmc()->set("auth_state", log_event_store(db_state).as_slice().str()); diff --git a/td/telegram/AuthManager.h b/td/telegram/AuthManager.h index 8521f4e4..f8cf5c3e 100644 --- a/td/telegram/AuthManager.h +++ b/td/telegram/AuthManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,6 +15,7 @@ #include "td/telegram/telegram_api.h" #include "td/actor/actor.h" +#include "td/actor/Timeout.h" #include "td/utils/common.h" #include "td/utils/Status.h" @@ -37,6 +38,7 @@ class AuthManager : public NetActor { void resend_authentication_code(uint64 query_id); void check_code(uint64 query_id, string code); void register_user(uint64 query_id, string first_name, string last_name); + void request_qr_code_authentication(uint64 query_id, vector other_user_ids); void check_bot_token(uint64 query_id, string bot_token); void check_password(uint64 query_id, string password); void request_password_recovery(uint64 query_id); @@ -44,6 +46,8 @@ class AuthManager : public NetActor { void logout(uint64 query_id); void delete_account(uint64 query_id, const string &reason); + void on_update_login_token(); + void on_authorization_lost(); void on_closing(bool destroy_flag); @@ -57,6 +61,7 @@ class AuthManager : public NetActor { None, WaitPhoneNumber, WaitCode, + WaitQrCodeConfirmation, WaitPassword, WaitRegistration, Ok, @@ -69,6 +74,8 @@ class AuthManager : public NetActor { SignIn, SignUp, SendCode, + RequestQrCode, + ImportQrCode, GetPassword, CheckPassword, RequestPasswordRecovery, @@ -105,6 +112,11 @@ class AuthManager : public NetActor { // WaitCode SendCodeHelper send_code_helper_; + // WaitQrCodeConfirmation + vector other_user_ids_; + string login_token_; + double login_token_expires_at_ = 0; + // WaitPassword WaitPasswordState wait_password_state_; @@ -112,12 +124,18 @@ class AuthManager : public NetActor { TermsOfService terms_of_service_; DbState() = default; - // TODO layer 104+: remove terms_of_service - static DbState wait_code(int32 api_id, string api_hash, SendCodeHelper send_code_helper, - TermsOfService terms_of_service) { + static DbState wait_code(int32 api_id, string api_hash, SendCodeHelper send_code_helper) { DbState state(State::WaitCode, api_id, api_hash); state.send_code_helper_ = std::move(send_code_helper); - state.terms_of_service_ = std::move(terms_of_service); + return state; + } + + static DbState wait_qr_code_confirmation(int32 api_id, string api_hash, vector other_user_ids, + string login_token, double login_token_expires_at) { + DbState state(State::WaitQrCodeConfirmation, api_id, api_hash); + state.other_user_ids_ = std::move(other_user_ids); + state.login_token_ = std::move(login_token); + state.login_token_expires_at_ = login_token_expires_at; return state; } @@ -127,8 +145,10 @@ class AuthManager : public NetActor { return state; } - static DbState wait_registration(int32 api_id, string api_hash, TermsOfService terms_of_service) { + static DbState wait_registration(int32 api_id, string api_hash, SendCodeHelper send_code_helper, + TermsOfService terms_of_service) { DbState state(State::WaitRegistration, api_id, api_hash); + state.send_code_helper_ = std::move(send_code_helper); state.terms_of_service_ = std::move(terms_of_service); return state; } @@ -158,6 +178,12 @@ class AuthManager : public NetActor { SendCodeHelper send_code_helper_; string code_; + // State::WaitQrCodeConfirmation + vector other_user_ids_; + string login_token_; + double login_token_expires_at_ = 0.0; + int32 imported_dc_id_ = -1; + // State::WaitPassword string password_; @@ -166,10 +192,15 @@ class AuthManager : public NetActor { // for bots string bot_token_; + uint64 query_id_ = 0; WaitPasswordState wait_password_state_; + int32 login_code_retry_delay_ = 0; + Timeout poll_export_login_code_timeout_; + + bool was_qr_code_request_ = false; bool was_check_bot_token_ = false; bool is_bot_ = false; uint64 net_query_id_ = 0; @@ -183,15 +214,20 @@ class AuthManager : public NetActor { void on_query_ok(); void start_net_query(NetQueryType net_query_type, NetQueryPtr net_query); + static void on_update_login_token_static(void *td); + void send_export_login_token_query(); + void set_login_token_expires_at(double login_token_expires_at); void destroy_auth_keys(); void on_send_code_result(NetQueryPtr &result); + void on_request_qr_code_result(NetQueryPtr &result, bool is_import); void on_get_password_result(NetQueryPtr &result); void on_request_password_recovery_result(NetQueryPtr &result); void on_authentication_result(NetQueryPtr &result, bool expected_flag); void on_log_out_result(NetQueryPtr &result); void on_delete_account_result(NetQueryPtr &result); - void on_authorization(tl_object_ptr auth); + void on_get_login_token(tl_object_ptr login_token); + void on_get_authorization(tl_object_ptr auth_ptr); void on_result(NetQueryPtr result) override; diff --git a/td/telegram/AuthManager.hpp b/td/telegram/AuthManager.hpp index 26b78eeb..c7e24ae3 100644 --- a/td/telegram/AuthManager.hpp +++ b/td/telegram/AuthManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,7 +7,7 @@ #pragma once #include "td/telegram/AuthManager.h" - +#include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/SendCodeHelper.hpp" #include "td/telegram/Version.h" @@ -52,11 +52,15 @@ void AuthManager::DbState::store(StorerT &storer) const { bool is_pbkdf2_supported = true; bool is_srp_supported = true; bool is_wait_registration_supported = true; + bool is_wait_registration_stores_phone_number = true; + bool is_wait_qr_code_confirmation_supported = true; BEGIN_STORE_FLAGS(); STORE_FLAG(has_terms_of_service); STORE_FLAG(is_pbkdf2_supported); STORE_FLAG(is_srp_supported); STORE_FLAG(is_wait_registration_supported); + STORE_FLAG(is_wait_registration_stores_phone_number); + STORE_FLAG(is_wait_qr_code_confirmation_supported); END_STORE_FLAGS(); store(state_, storer); store(api_id_, storer); @@ -69,9 +73,14 @@ void AuthManager::DbState::store(StorerT &storer) const { if (state_ == State::WaitCode) { store(send_code_helper_, storer); + } else if (state_ == State::WaitQrCodeConfirmation) { + store(other_user_ids_, storer); + store(login_token_, storer); + store_time(login_token_expires_at_, storer); } else if (state_ == State::WaitPassword) { store(wait_password_state_, storer); } else if (state_ == State::WaitRegistration) { + store(send_code_helper_, storer); } else { UNREACHABLE(); } @@ -84,19 +93,25 @@ void AuthManager::DbState::parse(ParserT &parser) { bool is_pbkdf2_supported = false; bool is_srp_supported = false; bool is_wait_registration_supported = false; + bool is_wait_registration_stores_phone_number = false; + bool is_wait_qr_code_confirmation_supported = false; if (parser.version() >= static_cast(Version::AddTermsOfService)) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_terms_of_service); PARSE_FLAG(is_pbkdf2_supported); PARSE_FLAG(is_srp_supported); PARSE_FLAG(is_wait_registration_supported); + PARSE_FLAG(is_wait_registration_stores_phone_number); + PARSE_FLAG(is_wait_qr_code_confirmation_supported); END_PARSE_FLAGS(); } - if (!is_wait_registration_supported) { - return parser.set_error("Have no wait registration support"); + if (!is_wait_qr_code_confirmation_supported) { + return parser.set_error("Have no QR code confirmation support"); } CHECK(is_pbkdf2_supported); CHECK(is_srp_supported); + CHECK(is_wait_registration_supported); + CHECK(is_wait_registration_stores_phone_number); parse(state_, parser); parse(api_id_, parser); @@ -109,9 +124,14 @@ void AuthManager::DbState::parse(ParserT &parser) { if (state_ == State::WaitCode) { parse(send_code_helper_, parser); + } else if (state_ == State::WaitQrCodeConfirmation) { + parse(other_user_ids_, parser); + parse(login_token_, parser); + parse_time(login_token_expires_at_, parser); } else if (state_ == State::WaitPassword) { parse(wait_password_state_, parser); } else if (state_ == State::WaitRegistration) { + parse(send_code_helper_, parser); } else { parser.set_error(PSTRING() << "Unexpected " << tag("state", static_cast(state_))); } diff --git a/td/telegram/AutoDownloadSettings.cpp b/td/telegram/AutoDownloadSettings.cpp index 91476771..de2bd5f9 100644 --- a/td/telegram/AutoDownloadSettings.cpp +++ b/td/telegram/AutoDownloadSettings.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -26,8 +26,8 @@ static td_api::object_ptr convert_auto_download_se auto audio_preload_next = (flags & telegram_api::autoDownloadSettings::AUDIO_PRELOAD_NEXT_MASK) != 0; auto phonecalls_less_data = (flags & telegram_api::autoDownloadSettings::PHONECALLS_LESS_DATA_MASK) != 0; return td_api::make_object( - !disabled, settings->photo_size_max_, settings->video_size_max_, settings->file_size_max_, video_preload_large, - audio_preload_next, phonecalls_less_data); + !disabled, settings->photo_size_max_, settings->video_size_max_, settings->file_size_max_, + settings->video_upload_maxbitrate_, video_preload_large, audio_preload_next, phonecalls_less_data); } class GetAutoDownloadSettingsQuery : public Td::ResultHandler { @@ -76,7 +76,7 @@ telegram_api::object_ptr get_input_auto_down } return telegram_api::make_object( flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, settings.max_photo_file_size, - settings.max_video_file_size, settings.max_other_file_size); + settings.max_video_file_size, settings.max_other_file_size, settings.video_upload_bitrate); } class SaveAutoDownloadSettingsQuery : public Td::ResultHandler { @@ -119,6 +119,7 @@ AutoDownloadSettings get_auto_download_settings(const td_api::object_ptrmax_photo_file_size_; result.max_video_file_size = settings->max_video_file_size_; result.max_other_file_size = settings->max_other_file_size_; + result.video_upload_bitrate = settings->video_upload_bitrate_; result.is_enabled = settings->is_auto_download_enabled_; result.preload_large_videos = settings->preload_large_videos_; result.preload_next_audio = settings->preload_next_audio_; diff --git a/td/telegram/AutoDownloadSettings.h b/td/telegram/AutoDownloadSettings.h index 3ea5476f..bd143d82 100644 --- a/td/telegram/AutoDownloadSettings.h +++ b/td/telegram/AutoDownloadSettings.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,6 +22,7 @@ class AutoDownloadSettings { int32 max_photo_file_size = 0; int32 max_video_file_size = 0; int32 max_other_file_size = 0; + int32 video_upload_bitrate = 0; bool is_enabled = false; bool preload_large_videos = false; bool preload_next_audio = false; diff --git a/td/telegram/BackgroundId.h b/td/telegram/BackgroundId.h index b07a1b9f..d1fe3dbf 100644 --- a/td/telegram/BackgroundId.h +++ b/td/telegram/BackgroundId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/BackgroundManager.cpp b/td/telegram/BackgroundManager.cpp index 2eaa98fc..a313d56b 100644 --- a/td/telegram/BackgroundManager.cpp +++ b/td/telegram/BackgroundManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -35,8 +35,6 @@ #include "td/utils/Slice.h" #include "td/utils/tl_helpers.h" -#include - namespace td { class GetBackgroundQuery : public Td::ResultHandler { @@ -327,7 +325,7 @@ void BackgroundManager::start_up() { log_event_parse(logevent, logevent_string).ensure(); CHECK(logevent.background_.id.is_valid()); - bool needs_file_id = (logevent.background_.type.type != BackgroundType::Type::Solid); + bool needs_file_id = logevent.background_.type.type != BackgroundType::Type::Fill; if (logevent.background_.file_id.is_valid() != needs_file_id) { LOG(ERROR) << "Failed to load " << logevent.background_.id << " of " << logevent.background_.type; G()->td_db()->get_binlog_pmc()->erase(get_background_database_key(for_dark_theme)); @@ -362,43 +360,18 @@ void BackgroundManager::get_backgrounds(Promise &&promise) { Result BackgroundManager::get_background_url(const string &name, td_api::object_ptr background_type) const { TRY_RESULT(type, get_background_type(background_type.get())); - - vector modes; - if (type.is_blurred) { - modes.emplace_back("blur"); - } - if (type.is_moving) { - modes.emplace_back("motion"); - } - string mode = implode(modes, '+'); - - string url = PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << "bg/"; - switch (type.type) { - case BackgroundType::Type::Wallpaper: - url += name; - if (!mode.empty()) { - url += "?mode="; - url += mode; - } - return url; - case BackgroundType::Type::Pattern: - url += name; - url += "?intensity="; - url += to_string(type.intensity); - url += "&bg_color="; - url += type.get_color_hex_string(); - if (!mode.empty()) { - url += "&mode="; - url += mode; - } - return url; - case BackgroundType::Type::Solid: - url += type.get_color_hex_string(); - return url; - default: - UNREACHABLE(); - return url; + auto url = PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << "bg/"; + auto link = type.get_link(); + if (type.is_server()) { + url += name; + if (!link.empty()) { + url += '?'; + url += link; + } + } else { + url += link; } + return url; } void BackgroundManager::reload_background_from_server( @@ -417,6 +390,10 @@ void BackgroundManager::reload_background(BackgroundId background_id, int64 acce telegram_api::make_object(background_id.get(), access_hash), std::move(promise)); } +static bool is_background_name_local(Slice name) { + return name.size() <= 6 || name.find('?') <= 13u; +} + BackgroundId BackgroundManager::search_background(const string &name, Promise &&promise) { auto it = name_to_background_id_.find(name); if (it != name_to_background_id_.end()) { @@ -429,15 +406,50 @@ BackgroundId BackgroundManager::search_background(const string &name, Promise 6 || i + 7 < fill_colors.size()) { + promise.set_error(Status::Error(400, "WALLPAPER_INVALID")); + return BackgroundId(); + } + have_hyphen = true; + hyphen_pos = i; } } - int32 color = static_cast(hex_to_integer(name)); - auto background_id = add_solid_background(color); + + BackgroundFill fill; + if (have_hyphen) { + int32 top_color = static_cast(hex_to_integer(fill_colors.substr(0, hyphen_pos))); + int32 bottom_color = static_cast(hex_to_integer(fill_colors.substr(hyphen_pos + 1))); + int32 rotation_angle = 0; + + Slice prefix("rotation="); + if (begins_with(parameters, prefix)) { + rotation_angle = to_integer(parameters.substr(prefix.size())); + if (!BackgroundFill::is_valid_rotation_angle(rotation_angle)) { + rotation_angle = 0; + } + } + + fill = BackgroundFill(top_color, bottom_color, rotation_angle); + } else { + int32 color = static_cast(hex_to_integer(fill_colors)); + fill = BackgroundFill(color); + } + auto background_id = add_fill_background(fill); promise.set_value(Unit()); return background_id; } @@ -470,14 +482,14 @@ void BackgroundManager::on_load_background_from_database(string name, string val loaded_from_database_backgrounds_.insert(name); - CHECK(name.size() > 6); + CHECK(!is_background_name_local(name)); if (name_to_background_id_.count(name) == 0 && !value.empty()) { LOG(INFO) << "Successfully loaded background " << name << " of size " << value.size() << " from database"; Background background; auto status = log_event_parse(background, value); - if (status.is_error() || background.type.type == BackgroundType::Type::Solid || !background.file_id.is_valid() || + if (status.is_error() || background.type.type == BackgroundType::Type::Fill || !background.file_id.is_valid() || !background.id.is_valid()) { - LOG(ERROR) << "Can't load bacground " << name << ": " << status << ' ' << format::as_hex_dump<4>(Slice(value)); + LOG(ERROR) << "Can't load background " << name << ": " << status << ' ' << format::as_hex_dump<4>(Slice(value)); } else { if (background.name != name) { LOG(ERROR) << "Expected background " << name << ", but received " << background.name; @@ -519,17 +531,20 @@ Result BackgroundManager::prepare_input_file(const tl_object_ptr(color) + 1); +BackgroundId BackgroundManager::add_fill_background(const BackgroundFill &fill) { + return add_fill_background(fill, false, (fill.top_color & 0x808080) == 0 && (fill.bottom_color & 0x808080) == 0); +} + +BackgroundId BackgroundManager::add_fill_background(const BackgroundFill &fill, bool is_default, bool is_dark) { + BackgroundId background_id(fill.get_id()); Background background; background.id = background_id; background.is_creator = true; - background.is_default = false; - background.is_dark = (color & 0x808080) == 0; - background.type = BackgroundType(color); - background.name = background.type.get_color_hex_string(); + background.is_default = is_default; + background.is_dark = is_dark; + background.type = BackgroundType(fill); + background.name = background.type.get_link(); add_background(background); return background_id; @@ -551,14 +566,13 @@ BackgroundId BackgroundManager::set_background(const td_api::InputBackground *in } auto type = r_type.move_as_ok(); - if (type.type == BackgroundType::Type::Solid) { - auto background_id = add_solid_background(type.color); - if (set_background_id_[for_dark_theme] != background_id) { - set_background_id(background_id, type, for_dark_theme); - } + if (type.type == BackgroundType::Type::Fill) { + auto background_id = add_fill_background(type.fill); + set_background_id(background_id, type, for_dark_theme); promise.set_value(Unit()); return background_id; } + CHECK(type.is_server()); if (input_background == nullptr) { promise.set_error(Status::Error(400, "Input background must be non-empty")); @@ -596,6 +610,7 @@ BackgroundId BackgroundManager::set_background(const td_api::InputBackground *in BackgroundId BackgroundManager::set_background(BackgroundId background_id, const BackgroundType &type, bool for_dark_theme, Promise &&promise) { + LOG(INFO) << "Set " << background_id << " with " << type; auto *background = get_background(background_id); if (background == nullptr) { promise.set_error(Status::Error(400, "Background to set not found")); @@ -605,7 +620,7 @@ BackgroundId BackgroundManager::set_background(BackgroundId background_id, const promise.set_error(Status::Error(400, "Background type mismatch")); return BackgroundId(); } - if (set_background_id_[for_dark_theme] == background_id) { + if (set_background_id_[for_dark_theme] == background_id && set_background_type_[for_dark_theme] == type) { promise.set_value(Unit()); return background_id; } @@ -627,8 +642,7 @@ void BackgroundManager::on_installed_background(BackgroundId background_id, Back return promise.set_error(result.move_as_error()); } - auto it = std::find(installed_background_ids_.begin(), installed_background_ids_.end(), background_id); - if (it == installed_background_ids_.end()) { + if (!td::contains(installed_background_ids_, background_id)) { installed_background_ids_.insert(installed_background_ids_.begin(), background_id); } set_background_id(background_id, type, for_dark_theme); @@ -727,7 +741,7 @@ void BackgroundManager::do_upload_background_file(FileId file_id, const Backgrou } void BackgroundManager::on_uploaded_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme, - telegram_api::object_ptr wallpaper, + telegram_api::object_ptr wallpaper, Promise &&promise) { CHECK(wallpaper != nullptr); @@ -739,6 +753,10 @@ void BackgroundManager::on_uploaded_background_file(FileId file_id, const Backgr auto background = get_background(background_id); CHECK(background != nullptr); + if (!background->file_id.is_valid()) { + td_->file_manager_->cancel_upload(file_id); + return promise.set_error(Status::Error(500, "Receive wrong uploaded background without file")); + } LOG_STATUS(td_->file_manager_->merge(background->file_id, file_id)); set_background_id(background_id, type, for_dark_theme); promise.set_value(Unit()); @@ -756,7 +774,7 @@ void BackgroundManager::remove_background(BackgroundId background_id, Promisetype.type == BackgroundType::Type::Solid) { + if (!background->type.is_server()) { return query_promise.set_value(Unit()); } @@ -769,10 +787,7 @@ void BackgroundManager::on_removed_background(BackgroundId background_id, Result if (result.is_error()) { return promise.set_error(result.move_as_error()); } - auto it = std::find(installed_background_ids_.begin(), installed_background_ids_.end(), background_id); - if (it != installed_background_ids_.end()) { - installed_background_ids_.erase(it); - } + td::remove(installed_background_ids_, background_id); if (background_id == set_background_id_[0]) { set_background_id(BackgroundId(), BackgroundType(), false); } @@ -802,6 +817,8 @@ void BackgroundManager::on_reset_background(Result &&result, Promise } void BackgroundManager::add_background(const Background &background) { + LOG(INFO) << "Add " << background.id << " of " << background.type; + CHECK(background.id.is_valid()); auto *result = &backgrounds_[background.id]; @@ -834,7 +851,7 @@ void BackgroundManager::add_background(const Background &background) { result->name = background.name; - if (result->name.size() > 6) { + if (!is_background_name_local(result->name)) { name_to_background_id_.emplace(result->name, result->id); loaded_from_database_backgrounds_.erase(result->name); // don't needed anymore } @@ -869,7 +886,7 @@ void BackgroundManager::add_background(const Background &background) { file_id_to_background_id_.emplace(result->file_id, result->id); } else { // if file_source_id is valid, then this is a new background with result->file_id == FileId() - // then background.file_id == FileId(), then this is a solid background, which can't have file_source_id + // then background.file_id == FileId(), then this is a fill background, which can't have file_source_id CHECK(!file_source_id.is_valid()); } } @@ -898,9 +915,31 @@ string BackgroundManager::get_background_name_database_key(const string &name) { BackgroundId BackgroundManager::on_get_background(BackgroundId expected_background_id, const string &expected_background_name, - telegram_api::object_ptr wallpaper) { - CHECK(wallpaper != nullptr); + telegram_api::object_ptr wallpaper_ptr) { + CHECK(wallpaper_ptr != nullptr); + if (wallpaper_ptr->get_id() == telegram_api::wallPaperNoFile::ID) { + auto wallpaper = move_tl_object_as(wallpaper_ptr); + + auto settings = std::move(wallpaper->settings_); + if (settings == nullptr) { + LOG(ERROR) << "Receive wallPaperNoFile without settings: " << to_string(wallpaper); + return BackgroundId(); + } + + bool has_color = (settings->flags_ & telegram_api::wallPaperSettings::BACKGROUND_COLOR_MASK) != 0; + auto color = has_color ? settings->background_color_ : 0; + auto is_default = (wallpaper->flags_ & telegram_api::wallPaperNoFile::DEFAULT_MASK) != 0; + auto is_dark = (wallpaper->flags_ & telegram_api::wallPaperNoFile::DARK_MASK) != 0; + + BackgroundFill fill = BackgroundFill(color); + if ((settings->flags_ & telegram_api::wallPaperSettings::SECOND_BACKGROUND_COLOR_MASK) != 0) { + fill = BackgroundFill(color, settings->second_background_color_, settings->rotation_); + } + return add_fill_background(fill, is_default, is_dark); + } + + auto wallpaper = move_tl_object_as(wallpaper_ptr); auto id = BackgroundId(wallpaper->id_); if (!id.is_valid()) { LOG(ERROR) << "Receive " << to_string(wallpaper); @@ -909,7 +948,7 @@ BackgroundId BackgroundManager::on_get_background(BackgroundId expected_backgrou if (expected_background_id.is_valid() && id != expected_background_id) { LOG(ERROR) << "Expected " << expected_background_id << ", but receive " << to_string(wallpaper); } - if (wallpaper->slug_.size() <= 6 || (0 < wallpaper->id_ && wallpaper->id_ <= 0x1000000)) { + if (is_background_name_local(wallpaper->slug_) || BackgroundFill::is_valid_id(wallpaper->id_)) { LOG(ERROR) << "Receive " << to_string(wallpaper); return BackgroundId(); } @@ -949,7 +988,7 @@ BackgroundId BackgroundManager::on_get_background(BackgroundId expected_backgrou name_to_background_id_.emplace(expected_background_name, id); } - if (G()->parameters().use_file_db && background.name.size() > 6) { + if (G()->parameters().use_file_db && !is_background_name_local(background.name)) { LOG(INFO) << "Save " << id << " to database with name " << background.name; G()->td_db()->get_sqlite_pmc()->set(get_background_name_database_key(background.name), log_event_store(background).as_slice().str(), Auto()); @@ -1020,8 +1059,7 @@ td_api::object_ptr BackgroundManager::get_backgrounds_objec return get_background_object(background_id, for_dark_theme); }); auto background_id = set_background_id_[for_dark_theme]; - if (background_id.is_valid() && std::find(installed_background_ids_.begin(), installed_background_ids_.end(), - background_id) == installed_background_ids_.end()) { + if (background_id.is_valid() && !td::contains(installed_background_ids_, background_id)) { backgrounds.push_back(get_background_object(background_id, for_dark_theme)); } std::stable_sort(backgrounds.begin(), backgrounds.end(), diff --git a/td/telegram/BackgroundManager.h b/td/telegram/BackgroundManager.h index 0ce5cacc..d3a3096b 100644 --- a/td/telegram/BackgroundManager.h +++ b/td/telegram/BackgroundManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -55,12 +55,12 @@ class BackgroundManager : public Actor { td_api::object_ptr get_backgrounds_object(bool for_dark_theme) const; BackgroundId on_get_background(BackgroundId expected_background_id, const string &expected_background_name, - telegram_api::object_ptr wallpaper); + telegram_api::object_ptr wallpaper_ptr); FileSourceId get_background_file_source_id(BackgroundId background_id, int64 access_hash); void on_uploaded_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme, - telegram_api::object_ptr wallpaper, + telegram_api::object_ptr wallpaper, Promise &&promise); void get_current_state(vector> &updates) const; @@ -104,7 +104,9 @@ class BackgroundManager : public Actor { void send_update_selected_background(bool for_dark_theme) const; - BackgroundId add_solid_background(int32 color); + BackgroundId add_fill_background(const BackgroundFill &fill); + + BackgroundId add_fill_background(const BackgroundFill &fill, bool is_default, bool is_dark); void add_background(const Background &background); diff --git a/td/telegram/BackgroundType.cpp b/td/telegram/BackgroundType.cpp index 408ecc11..f802d309 100644 --- a/td/telegram/BackgroundType.cpp +++ b/td/telegram/BackgroundType.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,10 +7,11 @@ #include "td/telegram/BackgroundType.h" #include "td/utils/logging.h" +#include "td/utils/Slice.h" namespace td { -string BackgroundType::get_color_hex_string() const { +static string get_color_hex_string(int32 color) { string result; for (int i = 20; i >= 0; i -= 4) { result += "0123456789abcdef"[(color >> i) & 0xf]; @@ -18,27 +19,121 @@ string BackgroundType::get_color_hex_string() const { return result; } -bool operator==(const BackgroundType &lhs, const BackgroundType &rhs) { - return lhs.type == rhs.type && lhs.is_blurred == rhs.is_blurred && lhs.is_moving == rhs.is_moving && - lhs.color == rhs.color && lhs.intensity == rhs.intensity; +static BackgroundFill get_background_fill(const td_api::BackgroundFill *fill) { + CHECK(fill != nullptr); + switch (fill->get_id()) { + case td_api::backgroundFillSolid::ID: { + auto solid = static_cast(fill); + return BackgroundFill(solid->color_); + } + case td_api::backgroundFillGradient::ID: { + auto gradient = static_cast(fill); + return BackgroundFill(gradient->top_color_, gradient->bottom_color_, gradient->rotation_angle_); + } + default: + UNREACHABLE(); + return {}; + } } -StringBuilder &operator<<(StringBuilder &string_builder, const BackgroundType &type) { - switch (type.type) { +static string get_background_fill_color_hex_string(const BackgroundFill &fill, bool is_first) { + if (fill.is_solid()) { + return get_color_hex_string(fill.top_color); + } else { + string colors = PSTRING() << get_color_hex_string(fill.top_color) << '-' << get_color_hex_string(fill.bottom_color); + if (fill.rotation_angle != 0) { + colors += (PSTRING() << (is_first ? '?' : '&') << "rotation=" << fill.rotation_angle); + } + return colors; + } +} + +static bool is_valid_color(int32 color) { + return 0 <= color && color <= 0xFFFFFF; +} + +static bool is_valid_intensity(int32 intensity) { + return 0 <= intensity && intensity <= 100; +} + +int64 BackgroundFill::get_id() const { + CHECK(is_valid_color(top_color)); + CHECK(is_valid_color(bottom_color)); + CHECK(is_valid_rotation_angle(rotation_angle)); + if (is_solid()) { + return static_cast(top_color) + 1; + } + return (rotation_angle / 45) * 0x1000001000001 + (static_cast(top_color) << 24) + bottom_color + (1 << 24) + 1; +} + +bool BackgroundFill::is_valid_id(int64 id) { + return 0 < id && id < 0x8000008000008; +} + +bool operator==(const BackgroundFill &lhs, const BackgroundFill &rhs) { + return lhs.top_color == rhs.top_color && lhs.bottom_color == rhs.bottom_color && + lhs.rotation_angle == rhs.rotation_angle; +} + +string BackgroundType::get_link() const { + string mode; + if (is_blurred) { + mode = "blur"; + } + if (is_moving) { + if (!mode.empty()) { + mode += '+'; + } + mode += "motion"; + } + + switch (type) { + case BackgroundType::Type::Wallpaper: { + if (!mode.empty()) { + return PSTRING() << "mode=" << mode; + } + return string(); + } + case BackgroundType::Type::Pattern: { + string link = PSTRING() << "intensity=" << intensity + << "&bg_color=" << get_background_fill_color_hex_string(fill, false); + if (!mode.empty()) { + link += "&mode="; + link += mode; + } + return link; + } + case BackgroundType::Type::Fill: + return get_background_fill_color_hex_string(fill, true); + default: + UNREACHABLE(); + return string(); + } +} + +bool operator==(const BackgroundType &lhs, const BackgroundType &rhs) { + return lhs.type == rhs.type && lhs.is_blurred == rhs.is_blurred && lhs.is_moving == rhs.is_moving && + lhs.intensity == rhs.intensity && lhs.fill == rhs.fill; +} + +static StringBuilder &operator<<(StringBuilder &string_builder, const BackgroundType::Type &type) { + switch (type) { case BackgroundType::Type::Wallpaper: - return string_builder << "type Wallpaper[" << (type.is_blurred ? "blurred" : "") << ' ' - << (type.is_moving ? "moving" : "") << ']'; + return string_builder << "Wallpaper"; case BackgroundType::Type::Pattern: - return string_builder << "type Pattern[" << (type.is_moving ? "moving" : "") << ' ' << type.get_color_hex_string() - << ' ' << type.intensity << ']'; - case BackgroundType::Type::Solid: - return string_builder << "type Solid[" << type.get_color_hex_string() << ']'; + return string_builder << "Pattern"; + case BackgroundType::Type::Fill: + return string_builder << "Fill"; default: UNREACHABLE(); return string_builder; } } +StringBuilder &operator<<(StringBuilder &string_builder, const BackgroundType &type) { + return string_builder << "type " << type.type << '[' << type.get_link() << ']'; +} + Result get_background_type(const td_api::BackgroundType *type) { if (type == nullptr) { return Status::Error(400, "Type must not be empty"); @@ -53,22 +148,34 @@ Result get_background_type(const td_api::BackgroundType *type) { } case td_api::backgroundTypePattern::ID: { auto pattern = static_cast(type); - result = BackgroundType(pattern->is_moving_, pattern->color_, pattern->intensity_); + if (pattern->fill_ == nullptr) { + return Status::Error(400, "Fill info must not be empty"); + } + result = BackgroundType(pattern->is_moving_, get_background_fill(pattern->fill_.get()), pattern->intensity_); break; } - case td_api::backgroundTypeSolid::ID: { - auto solid = static_cast(type); - result = BackgroundType(solid->color_); + case td_api::backgroundTypeFill::ID: { + auto fill = static_cast(type); + if (fill->fill_ == nullptr) { + return Status::Error(400, "Fill info must not be empty"); + } + result = BackgroundType(get_background_fill(fill->fill_.get())); break; } default: UNREACHABLE(); } - if (result.intensity < 0 || result.intensity > 100) { + if (!is_valid_intensity(result.intensity)) { return Status::Error(400, "Wrong intensity value"); } - if (result.color < 0 || result.color > 0xFFFFFF) { - return Status::Error(400, "Wrong color value"); + if (!is_valid_color(result.fill.top_color)) { + return Status::Error(400, result.fill.is_solid() ? Slice("Wrong color value") : ("Wrong top color value")); + } + if (!is_valid_color(result.fill.bottom_color)) { + return Status::Error(400, "Wrong bottom color value"); + } + if (!BackgroundFill::is_valid_rotation_angle(result.fill.rotation_angle)) { + return Status::Error(400, "Wrong rotation angle value"); } return result; } @@ -77,42 +184,70 @@ BackgroundType get_background_type(bool is_pattern, telegram_api::object_ptr settings) { bool is_blurred = false; bool is_moving = false; - int32 color = 0; + BackgroundFill fill; int32 intensity = 0; if (settings) { auto flags = settings->flags_; is_blurred = (flags & telegram_api::wallPaperSettings::BLUR_MASK) != 0; is_moving = (flags & telegram_api::wallPaperSettings::MOTION_MASK) != 0; + + int32 color = 0; if ((flags & telegram_api::wallPaperSettings::BACKGROUND_COLOR_MASK) != 0) { color = settings->background_color_; - if (color < 0 || color > 0xFFFFFF) { + if (!is_valid_color(color)) { LOG(ERROR) << "Receive " << to_string(settings); color = 0; } } + if ((flags & telegram_api::wallPaperSettings::SECOND_BACKGROUND_COLOR_MASK) != 0) { + int32 second_color = settings->second_background_color_; + if (!is_valid_color(second_color)) { + LOG(ERROR) << "Receive " << to_string(settings); + second_color = 0; + } + + int32 rotation_angle = settings->rotation_; + if (!BackgroundFill::is_valid_rotation_angle(rotation_angle)) { + LOG(ERROR) << "Receive " << to_string(settings); + rotation_angle = 0; + } + + fill = BackgroundFill(color, second_color, rotation_angle); + } else { + fill = BackgroundFill(color); + } + if ((flags & telegram_api::wallPaperSettings::INTENSITY_MASK) != 0) { intensity = settings->intensity_; - if (intensity < 0 || intensity > 100) { + if (!is_valid_intensity(intensity)) { LOG(ERROR) << "Receive " << to_string(settings); intensity = 0; } } } if (is_pattern) { - return BackgroundType(is_moving, color, intensity); + return BackgroundType(is_moving, fill, intensity); } else { return BackgroundType(is_blurred, is_moving); } } +static td_api::object_ptr get_background_fill_object(const BackgroundFill &fill) { + if (fill.is_solid()) { + return td_api::make_object(fill.top_color); + } + return td_api::make_object(fill.top_color, fill.bottom_color, fill.rotation_angle); +} + td_api::object_ptr get_background_type_object(const BackgroundType &type) { switch (type.type) { case BackgroundType::Type::Wallpaper: return td_api::make_object(type.is_blurred, type.is_moving); case BackgroundType::Type::Pattern: - return td_api::make_object(type.is_moving, type.color, type.intensity); - case BackgroundType::Type::Solid: - return td_api::make_object(type.color); + return td_api::make_object(get_background_fill_object(type.fill), type.intensity, + type.is_moving); + case BackgroundType::Type::Fill: + return td_api::make_object(get_background_fill_object(type.fill)); default: UNREACHABLE(); return nullptr; @@ -127,22 +262,23 @@ telegram_api::object_ptr get_input_wallpaper_se if (type.is_moving) { flags |= telegram_api::wallPaperSettings::MOTION_MASK; } - if (type.color != 0) { + if (type.fill.top_color != 0 || type.fill.bottom_color != 0) { flags |= telegram_api::wallPaperSettings::BACKGROUND_COLOR_MASK; } + if (!type.fill.is_solid()) { + flags |= telegram_api::wallPaperSettings::SECOND_BACKGROUND_COLOR_MASK; + } if (type.intensity) { flags |= telegram_api::wallPaperSettings::INTENSITY_MASK; } - switch (type.type) { - case BackgroundType::Type::Wallpaper: - case BackgroundType::Type::Pattern: - return telegram_api::make_object(flags, false /*ignored*/, false /*ignored*/, - type.color, type.intensity); - case BackgroundType::Type::Solid: - default: - UNREACHABLE(); - return nullptr; + if (type.is_server()) { + return telegram_api::make_object(flags, false /*ignored*/, false /*ignored*/, + type.fill.top_color, type.fill.bottom_color, + type.intensity, type.fill.rotation_angle); } + + UNREACHABLE(); + return nullptr; } } // namespace td diff --git a/td/telegram/BackgroundType.h b/td/telegram/BackgroundType.h index a2ed240c..56efb611 100644 --- a/td/telegram/BackgroundType.h +++ b/td/telegram/BackgroundType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,25 +15,56 @@ namespace td { +struct BackgroundFill { + int32 top_color = 0; + int32 bottom_color = 0; + int32 rotation_angle = 0; + + BackgroundFill() = default; + explicit BackgroundFill(int32 solid_color) : top_color(solid_color), bottom_color(solid_color) { + } + BackgroundFill(int32 top_color, int32 bottom_color, int32 rotation_angle) + : top_color(top_color), bottom_color(bottom_color), rotation_angle(rotation_angle) { + } + + bool is_solid() const { + return top_color == bottom_color; + } + + int64 get_id() const; + + static bool is_valid_id(int64 id); + + static bool is_valid_rotation_angle(int32 rotation_angle) { + return 0 <= rotation_angle && rotation_angle < 360 && rotation_angle % 45 == 0; + } +}; + +bool operator==(const BackgroundFill &lhs, const BackgroundFill &rhs); + struct BackgroundType { - enum class Type : int32 { Wallpaper, Pattern, Solid }; - Type type = Type::Solid; + enum class Type : int32 { Wallpaper, Pattern, Fill }; + Type type = Type::Fill; bool is_blurred = false; bool is_moving = false; - int32 color = 0; int32 intensity = 0; + BackgroundFill fill; BackgroundType() = default; BackgroundType(bool is_blurred, bool is_moving) : type(Type::Wallpaper), is_blurred(is_blurred), is_moving(is_moving) { } - BackgroundType(bool is_moving, int32 color, int32 intensity) - : type(Type::Pattern), is_moving(is_moving), color(color), intensity(intensity) { + BackgroundType(bool is_moving, const BackgroundFill &fill, int32 intensity) + : type(Type::Pattern), is_moving(is_moving), intensity(intensity), fill(fill) { } - explicit BackgroundType(int32 color) : type(Type::Solid), color(color) { + explicit BackgroundType(BackgroundFill fill) : type(Type::Fill), fill(fill) { } - string get_color_hex_string() const; + bool is_server() const { + return type == Type::Wallpaper || type == Type::Pattern; + } + + string get_link() const; }; bool operator==(const BackgroundType &lhs, const BackgroundType &rhs); diff --git a/td/telegram/BackgroundType.hpp b/td/telegram/BackgroundType.hpp index 6f1aea1c..8e4df826 100644 --- a/td/telegram/BackgroundType.hpp +++ b/td/telegram/BackgroundType.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,17 +14,23 @@ namespace td { template void store(const BackgroundType &type, StorerT &storer) { - bool has_color = type.color != 0; + bool has_fill = type.fill.top_color != 0 || type.fill.bottom_color != 0; bool has_intensity = type.intensity != 0; + bool is_gradient = !type.fill.is_solid(); BEGIN_STORE_FLAGS(); STORE_FLAG(type.is_blurred); STORE_FLAG(type.is_moving); - STORE_FLAG(has_color); + STORE_FLAG(has_fill); STORE_FLAG(has_intensity); + STORE_FLAG(is_gradient); END_STORE_FLAGS(); store(type.type, storer); - if (has_color) { - store(type.color, storer); + if (has_fill) { + store(type.fill.top_color, storer); + if (is_gradient) { + store(type.fill.bottom_color, storer); + store(type.fill.rotation_angle, storer); + } } if (has_intensity) { store(type.intensity, storer); @@ -33,17 +39,25 @@ void store(const BackgroundType &type, StorerT &storer) { template void parse(BackgroundType &type, ParserT &parser) { - bool has_color; + bool has_fill; bool has_intensity; + bool is_gradient; BEGIN_PARSE_FLAGS(); PARSE_FLAG(type.is_blurred); PARSE_FLAG(type.is_moving); - PARSE_FLAG(has_color); + PARSE_FLAG(has_fill); PARSE_FLAG(has_intensity); + PARSE_FLAG(is_gradient); END_PARSE_FLAGS(); parse(type.type, parser); - if (has_color) { - parse(type.color, parser); + if (has_fill) { + parse(type.fill.top_color, parser); + if (is_gradient) { + parse(type.fill.bottom_color, parser); + parse(type.fill.rotation_angle, parser); + } else { + type.fill.bottom_color = type.fill.top_color; + } } if (has_intensity) { parse(type.intensity, parser); diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp index 63b38ee8..3ef23874 100644 --- a/td/telegram/CallActor.cpp +++ b/td/telegram/CallActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallActor.h b/td/telegram/CallActor.h index aa24c496..c13e9c2c 100644 --- a/td/telegram/CallActor.h +++ b/td/telegram/CallActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallDiscardReason.cpp b/td/telegram/CallDiscardReason.cpp index 8c3d8a1c..9b5e1b96 100644 --- a/td/telegram/CallDiscardReason.cpp +++ b/td/telegram/CallDiscardReason.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallDiscardReason.h b/td/telegram/CallDiscardReason.h index 2f1237f3..d83a9e11 100644 --- a/td/telegram/CallDiscardReason.h +++ b/td/telegram/CallDiscardReason.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallId.h b/td/telegram/CallId.h index bd7842c2..9a81d0ac 100644 --- a/td/telegram/CallId.h +++ b/td/telegram/CallId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallManager.cpp b/td/telegram/CallManager.cpp index 695cb14c..c09ee2d3 100644 --- a/td/telegram/CallManager.cpp +++ b/td/telegram/CallManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallManager.h b/td/telegram/CallManager.h index 0679e526..367984d0 100644 --- a/td/telegram/CallManager.h +++ b/td/telegram/CallManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/CallbackQueriesManager.cpp b/td/telegram/CallbackQueriesManager.cpp index 3e5eb466..7455c712 100644 --- a/td/telegram/CallbackQueriesManager.cpp +++ b/td/telegram/CallbackQueriesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -182,7 +182,7 @@ void CallbackQueriesManager::on_new_query(int32 flags, int64 callback_query_id, return; } - td_->messages_manager_->force_create_dialog(dialog_id, "on_new_callback_query"); + td_->messages_manager_->force_create_dialog(dialog_id, "on_new_callback_query", true); send_closure( G()->td(), &Td::send_update, make_tl_object( @@ -237,10 +237,14 @@ int64 CallbackQueriesManager::send_callback_query(FullMessageId full_message_id, return 0; } - if (!td_->messages_manager_->have_message(full_message_id, "send_callback_query")) { + if (!td_->messages_manager_->have_message_force(full_message_id, "send_callback_query")) { promise.set_error(Status::Error(5, "Message not found")); return 0; } + if (full_message_id.get_message_id().is_valid_scheduled()) { + promise.set_error(Status::Error(5, "Can't send callback queries from scheduled messages")); + return 0; + } if (!full_message_id.get_message_id().is_server()) { promise.set_error(Status::Error(5, "Bad message identifier")); return 0; diff --git a/td/telegram/CallbackQueriesManager.h b/td/telegram/CallbackQueriesManager.h index 7835bef1..c05bd221 100644 --- a/td/telegram/CallbackQueriesManager.h +++ b/td/telegram/CallbackQueriesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,6 +10,7 @@ #include "td/telegram/telegram_api.h" #include "td/telegram/DialogId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/MessageId.h" #include "td/telegram/UserId.h" diff --git a/td/telegram/ChannelId.h b/td/telegram/ChannelId.h index b4da6f66..f2a57249 100644 --- a/td/telegram/ChannelId.h +++ b/td/telegram/ChannelId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ChatId.h b/td/telegram/ChatId.h index 5ef93ff2..0ecce097 100644 --- a/td/telegram/ChatId.h +++ b/td/telegram/ChatId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Client.cpp b/td/telegram/Client.cpp index 4f17ed9d..b0fb29be 100644 --- a/td/telegram/Client.cpp +++ b/td/telegram/Client.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Client.h b/td/telegram/Client.h index 776c6bdf..1e232e83 100644 --- a/td/telegram/Client.h +++ b/td/telegram/Client.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ClientActor.cpp b/td/telegram/ClientActor.cpp index 2b0c2368..d570c8c9 100644 --- a/td/telegram/ClientActor.cpp +++ b/td/telegram/ClientActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ClientActor.h b/td/telegram/ClientActor.h index 3d300166..54cb7250 100644 --- a/td/telegram/ClientActor.h +++ b/td/telegram/ClientActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ClientDotNet.cpp b/td/telegram/ClientDotNet.cpp index a6b496a5..9719b429 100644 --- a/td/telegram/ClientDotNet.cpp +++ b/td/telegram/ClientDotNet.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ClientJson.cpp b/td/telegram/ClientJson.cpp index 02b7dd73..94358660 100644 --- a/td/telegram/ClientJson.cpp +++ b/td/telegram/ClientJson.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,8 +9,6 @@ #include "td/telegram/td_api.h" #include "td/telegram/td_api_json.h" -#include "td/tl/tl_json.h" - #include "td/utils/common.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" @@ -45,7 +43,7 @@ static std::pair, string> to_request(Slice } td_api::object_ptr func; - auto status = from_json(func, json_value); + auto status = from_json(func, std::move(json_value)); if (status.is_error()) { return {get_return_error_function(PSLICE() << "Failed to parse JSON object as TDLib request: " << status.error().message()), diff --git a/td/telegram/ClientJson.h b/td/telegram/ClientJson.h index 5cfa9325..3b08cd28 100644 --- a/td/telegram/ClientJson.h +++ b/td/telegram/ClientJson.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ConfigManager.cpp b/td/telegram/ConfigManager.cpp index 016ae48d..67c9d8cb 100644 --- a/td/telegram/ConfigManager.cpp +++ b/td/telegram/ConfigManager.cpp @@ -1,13 +1,15 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/ConfigManager.h" +#include "td/telegram/AuthManager.h" #include "td/telegram/ConfigShared.h" #include "td/telegram/Global.h" +#include "td/telegram/JsonValue.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/net/AuthDataShared.h" #include "td/telegram/net/ConnectionCreator.h" @@ -19,6 +21,7 @@ #include "td/telegram/net/PublicRsaKeyShared.h" #include "td/telegram/net/Session.h" #include "td/telegram/StateManager.h" +#include "td/telegram/Td.h" #include "td/telegram/TdDb.h" #include "td/telegram/telegram_api.h" @@ -52,7 +55,7 @@ #include "td/utils/tl_parsers.h" #include "td/utils/UInt.h" -#include +#include #include #include @@ -197,27 +200,40 @@ Result decode_config(Slice input) { return std::move(config); } +template static ActorOwn<> get_simple_config_impl(Promise promise, int32 scheduler_id, string url, - string host, bool prefer_ipv6) { + string host, std::vector> headers, bool prefer_ipv6, + F &&get_config, + string content = string(), string content_type = string()) { VLOG(config_recoverer) << "Request simple config from " << url; #if TD_EMSCRIPTEN // FIXME return ActorOwn<>(); #else const int timeout = 10; const int ttl = 3; + headers.emplace_back("Host", std::move(host)); + headers.emplace_back("User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/77.0.3865.90 Safari/537.36"); return ActorOwn<>(create_actor_on_scheduler( "Wget", scheduler_id, - PromiseCreator::lambda([promise = std::move(promise)](Result> r_query) mutable { + PromiseCreator::lambda([get_config = std::move(get_config), + promise = std::move(promise)](Result> r_query) mutable { promise.set_result([&]() -> Result { TRY_RESULT(http_query, std::move(r_query)); SimpleConfigResult res; res.r_http_date = HttpDate::parse_http_date(http_query->get_header("date").str()); - res.r_config = decode_config(http_query->content_); + auto r_config = get_config(*http_query); + if (r_config.is_error()) { + res.r_config = r_config.move_as_error(); + } else { + res.r_config = decode_config(r_config.ok()); + } return std::move(res); }()); }), - std::move(url), std::vector>({{"Host", std::move(host)}}), timeout, ttl, prefer_ipv6, - SslStream::VerifyPeer::Off)); + std::move(url), std::move(headers), timeout, ttl, prefer_ipv6, SslStream::VerifyPeer::Off, std::move(content), + std::move(content_type))); #endif } @@ -226,65 +242,48 @@ ActorOwn<> get_simple_config_azure(Promise promise, const Co string url = PSTRING() << "https://software-download.microsoft.com/" << (is_test ? "test" : "prod") << "v2/config.txt"; const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6"); - return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "tcdnb.azureedge.net", prefer_ipv6); + return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "tcdnb.azureedge.net", {}, + prefer_ipv6, [](auto &http_query) -> Result { return http_query.content_.str(); }); } static ActorOwn<> get_simple_config_dns(Slice address, Slice host, Promise promise, const ConfigShared *shared_config, bool is_test, int32 scheduler_id) { - VLOG(config_recoverer) << "Request simple config from DNS"; -#if TD_EMSCRIPTEN // FIXME - return ActorOwn<>(); -#else string name = shared_config == nullptr ? string() : shared_config->get_option_string("dc_txt_domain_name"); - const int timeout = 10; - const int ttl = 3; const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6"); - if (name.empty() || true) { + if (name.empty()) { name = is_test ? "tapv3.stel.com" : "apv3.stel.com"; } - return ActorOwn<>(create_actor_on_scheduler( - "Wget", scheduler_id, - PromiseCreator::lambda([promise = std::move(promise)](Result> r_query) mutable { - promise.set_result([&]() -> Result { - TRY_RESULT(http_query, std::move(r_query)); - - SimpleConfigResult res; - res.r_http_date = HttpDate::parse_http_date(http_query->get_header("date").str()); - res.r_config = [&]() -> Result { - TRY_RESULT(json, json_decode(http_query->content_)); - if (json.type() != JsonValue::Type::Object) { - return Status::Error("JSON error"); - } - auto &answer_object = json.get_object(); - TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false)); - auto &answer_array = answer.get_array(); - vector parts; - for (auto &v : answer_array) { - if (v.type() != JsonValue::Type::Object) { - return Status::Error("JSON error"); - } - auto &data_object = v.get_object(); - TRY_RESULT(part, get_json_object_string_field(data_object, "data", false)); - parts.push_back(std::move(part)); - } - if (parts.size() != 2) { - return Status::Error("Expected data in two parts"); - } - string data; - if (parts[0].size() < parts[1].size()) { - data = parts[1] + parts[0]; - } else { - data = parts[0] + parts[1]; - } - return decode_config(data); - }(); - return std::move(res); - }()); - }), - PSTRING() << "https://" << address << "?name=" << url_encode(name) << "&type=16", - std::vector>({{"Host", host.str()}, {"Accept", "application/dns-json"}}), timeout, ttl, - prefer_ipv6, SslStream::VerifyPeer::Off)); -#endif + auto get_config = [](auto &http_query) -> Result { + TRY_RESULT(json, json_decode(http_query.content_)); + if (json.type() != JsonValue::Type::Object) { + return Status::Error("Expected JSON object"); + } + auto &answer_object = json.get_object(); + TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false)); + auto &answer_array = answer.get_array(); + vector parts; + for (auto &v : answer_array) { + if (v.type() != JsonValue::Type::Object) { + return Status::Error("Expected JSON object"); + } + auto &data_object = v.get_object(); + TRY_RESULT(part, get_json_object_string_field(data_object, "data", false)); + parts.push_back(std::move(part)); + } + if (parts.size() != 2) { + return Status::Error("Expected data in two parts"); + } + string data; + if (parts[0].size() < parts[1].size()) { + data = parts[1] + parts[0]; + } else { + data = parts[0] + parts[1]; + } + return data; + }; + return get_simple_config_impl(std::move(promise), scheduler_id, + PSTRING() << "https://" << address << "?name=" << url_encode(name) << "&type=16", + host.str(), {{"Accept", "application/dns-json"}}, prefer_ipv6, std::move(get_config)); } ActorOwn<> get_simple_config_google_dns(Promise promise, const ConfigShared *shared_config, @@ -299,6 +298,80 @@ ActorOwn<> get_simple_config_mozilla_dns(Promise promise, co shared_config, is_test, scheduler_id); } +static string generate_firebase_remote_config_payload() { + unsigned char buf[17]; + Random::secure_bytes(buf, sizeof(buf)); + buf[0] = static_cast((buf[0] & 0xF0) | 0x07); + auto app_instance_id = base64url_encode(Slice(buf, sizeof(buf))); + app_instance_id.resize(22); + return PSTRING() << "{\"app_id\":\"1:560508485281:web:4ee13a6af4e84d49e67ae0\",\"app_instance_id\":\"" + << app_instance_id << "\"}"; +} + +ActorOwn<> get_simple_config_firebase_remote_config(Promise promise, + const ConfigShared *shared_config, bool is_test, + int32 scheduler_id) { + if (is_test) { + promise.set_error(Status::Error(400, "Test config is not supported")); + return ActorOwn<>(); + } + + static const string payload = generate_firebase_remote_config_payload(); + string url = + "https://firebaseremoteconfig.googleapis.com/v1/projects/peak-vista-421/namespaces/" + "firebase:fetch?key=AIzaSyC2-kAkpDsroixRXw-sTw-Wfqo4NxjMwwM"; + const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6"); + auto get_config = [](auto &http_query) -> Result { + TRY_RESULT(json, json_decode(http_query.get_arg("entries"))); + if (json.type() != JsonValue::Type::Object) { + return Status::Error("Expected JSON object"); + } + auto &entries_object = json.get_object(); + TRY_RESULT(config, get_json_object_string_field(entries_object, "ipconfigv3", false)); + return std::move(config); + }; + return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "firebaseremoteconfig.googleapis.com", + {}, prefer_ipv6, std::move(get_config), payload, "application/json"); +} + +ActorOwn<> get_simple_config_firebase_realtime(Promise promise, const ConfigShared *shared_config, + bool is_test, int32 scheduler_id) { + if (is_test) { + promise.set_error(Status::Error(400, "Test config is not supported")); + return ActorOwn<>(); + } + + string url = "https://reserve-5a846.firebaseio.com/ipconfigv3.json"; + const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6"); + auto get_config = [](auto &http_query) -> Result { + return http_query.get_arg("content").str(); + }; + return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "reserve-5a846.firebaseio.com", {}, + prefer_ipv6, std::move(get_config)); +} + +ActorOwn<> get_simple_config_firebase_firestore(Promise promise, const ConfigShared *shared_config, + bool is_test, int32 scheduler_id) { + if (is_test) { + promise.set_error(Status::Error(400, "Test config is not supported")); + return ActorOwn<>(); + } + + string url = "https://www.google.com/v1/projects/reserve-5a846/databases/(default)/documents/ipconfig/v3"; + const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6"); + auto get_config = [](auto &http_query) -> Result { + TRY_RESULT(json, json_decode(http_query.get_arg("fields"))); + if (json.type() != JsonValue::Type::Object) { + return Status::Error("Expected JSON object"); + } + TRY_RESULT(data, get_json_object_field(json.get_object(), "data", JsonValue::Type::Object, false)); + TRY_RESULT(config, get_json_object_string_field(data.get_object(), "stringValue", false)); + return std::move(config); + }; + return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "firestore.googleapis.com", {}, + prefer_ipv6, std::move(get_config)); +} + ActorOwn<> get_full_config(DcOption option, Promise promise, ActorShared<> parent) { class SessionCallback : public Session::Callback { public: @@ -399,9 +472,7 @@ ActorOwn<> get_full_config(DcOption option, Promise promise, ActorSh std::vector> auth_key_listeners_; void notify() { - auto it = std::remove_if(auth_key_listeners_.begin(), auth_key_listeners_.end(), - [&](auto &listener) { return !listener->notify(); }); - auth_key_listeners_.erase(it, auth_key_listeners_.end()); + td::remove_if(auth_key_listeners_, [&](auto &listener) { return !listener->notify(); }); } string auth_key_key() const { @@ -421,16 +492,17 @@ ActorOwn<> get_full_config(DcOption option, Promise promise, ActorSh private: void start_up() override { auto auth_data = std::make_shared(option_.get_dc_id()); - int32 int_dc_id = option_.get_dc_id().get_raw_id(); + int32 raw_dc_id = option_.get_dc_id().get_raw_id(); auto session_callback = make_unique(actor_shared(this, 1), std::move(option_)); + int32 int_dc_id = raw_dc_id; if (G()->is_test_dc()) { int_dc_id += 10000; } - session_ = - create_actor("ConfigSession", std::move(session_callback), std::move(auth_data), int_dc_id, - false /*is_main*/, true /*use_pfs*/, false /*is_cdn*/, false /*need_destroy_auth_key*/, - mtproto::AuthKey(), std::vector()); + session_ = create_actor("ConfigSession", std::move(session_callback), std::move(auth_data), raw_dc_id, + 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); @@ -719,9 +791,15 @@ class ConfigRecoverer : public Actor { send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false); }); auto get_simple_config = [&]() { - switch (simple_config_turn_ % 3) { + switch (simple_config_turn_ % 4) { case 2: return get_simple_config_azure; + case 3: + return get_simple_config_firebase_remote_config; + case 4: + return get_simple_config_firebase_realtime; + case 5: + return get_simple_config_firebase_firestore; case 0: return get_simple_config_google_dns; case 1: @@ -793,12 +871,10 @@ ConfigManager::ConfigManager(ActorShared<> parent) : parent_(std::move(parent)) } void ConfigManager::start_up() { - // TODO there are some problems when many ConfigRecoverers starts at the same time - // if (G()->parameters().use_file_db) { ref_cnt_++; config_recoverer_ = create_actor("Recoverer", actor_shared()); send_closure(config_recoverer_, &ConfigRecoverer::on_dc_options_update, load_dc_options_update()); - // } + auto expire_time = load_config_expire_time(); if (expire_time.is_in_past()) { request_config(); @@ -812,29 +888,96 @@ void ConfigManager::hangup_shared() { ref_cnt_--; try_stop(); } + void ConfigManager::hangup() { ref_cnt_--; config_recoverer_.reset(); try_stop(); } + void ConfigManager::loop() { if (expire_time_ && expire_time_.is_in_past()) { request_config(); expire_time_ = {}; } } + void ConfigManager::try_stop() { if (ref_cnt_ == 0) { stop(); } } + void ConfigManager::request_config() { + if (G()->close_flag()) { + return; + } + if (config_sent_cnt_ != 0) { return; } request_config_from_dc_impl(DcId::main()); } +void ConfigManager::get_app_config(Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get(); + if (auth_manager != nullptr && auth_manager->is_bot()) { + return promise.set_value(nullptr); + } + + 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)); + } +} + +void ConfigManager::get_content_settings(Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + auto auth_manager = G()->td().get_actor_unsafe()->auth_manager_.get(); + if (auth_manager == nullptr || !auth_manager->is_authorized() || auth_manager->is_bot()) { + return promise.set_value(Unit()); + } + + 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)); + } +} + +void ConfigManager::set_content_settings(bool ignore_sensitive_content_restrictions, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + last_set_content_settings_ = ignore_sensitive_content_restrictions; + auto &queries = set_content_settings_queries_[ignore_sensitive_content_restrictions]; + queries.push_back(std::move(promise)); + if (!is_set_content_settings_request_sent_) { + is_set_content_settings_request_sent_ = true; + int32 flags = 0; + if (ignore_sensitive_content_restrictions) { + 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*/))), + actor_shared(this, 3 + static_cast(ignore_sensitive_content_restrictions))); + } +} + void ConfigManager::on_dc_options_update(DcOptions dc_options) { save_dc_options_update(dc_options); send_closure(config_recoverer_, &ConfigRecoverer::on_dc_options_update, std::move(dc_options)); @@ -851,10 +994,105 @@ void ConfigManager::request_config_from_dc_impl(DcId dc_id) { 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)); + actor_shared(this, 0)); +} + +void ConfigManager::set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions) { + G()->shared_config().set_option_boolean("ignore_sensitive_content_restrictions", + ignore_sensitive_content_restrictions); + bool have_ignored_restriction_reasons = G()->shared_config().have_option("ignored_restriction_reasons"); + if (have_ignored_restriction_reasons != ignore_sensitive_content_restrictions) { + get_app_config(Auto()); + } } void ConfigManager::on_result(NetQueryPtr res) { + auto token = get_link_token(); + if (token == 3 || token == 4) { + is_set_content_settings_request_sent_ = false; + bool ignore_sensitive_content_restrictions = (token == 4); + auto promises = std::move(set_content_settings_queries_[ignore_sensitive_content_restrictions]); + set_content_settings_queries_[ignore_sensitive_content_restrictions].clear(); + CHECK(!promises.empty()); + auto result_ptr = fetch_result(std::move(res)); + if (result_ptr.is_error()) { + for (auto &promise : promises) { + promise.set_error(result_ptr.error().clone()); + } + } else { + if (G()->shared_config().get_option_boolean("can_ignore_sensitive_content_restrictions") && + last_set_content_settings_ == ignore_sensitive_content_restrictions) { + set_ignore_sensitive_content_restrictions(ignore_sensitive_content_restrictions); + } + + for (auto &promise : promises) { + promise.set_value(Unit()); + } + } + + if (!set_content_settings_queries_[!ignore_sensitive_content_restrictions].empty()) { + if (ignore_sensitive_content_restrictions == last_set_content_settings_) { + promises = std::move(set_content_settings_queries_[!ignore_sensitive_content_restrictions]); + set_content_settings_queries_[!ignore_sensitive_content_restrictions].clear(); + for (auto &promise : promises) { + promise.set_value(Unit()); + } + } else { + set_content_settings(!ignore_sensitive_content_restrictions, Auto()); + } + } + return; + } + if (token == 2) { + auto promises = std::move(get_content_settings_queries_); + get_content_settings_queries_.clear(); + CHECK(!promises.empty()); + auto result_ptr = fetch_result(std::move(res)); + if (result_ptr.is_error()) { + for (auto &promise : promises) { + promise.set_error(result_ptr.error().clone()); + } + return; + } + + auto result = result_ptr.move_as_ok(); + set_ignore_sensitive_content_restrictions(result->sensitive_enabled_); + G()->shared_config().set_option_boolean("can_ignore_sensitive_content_restrictions", result->sensitive_can_change_); + + for (auto &promise : promises) { + promise.set_value(Unit()); + } + return; + } + if (token == 1) { + auto promises = std::move(get_app_config_queries_); + get_app_config_queries_.clear(); + CHECK(!promises.empty()); + auto result_ptr = fetch_result(std::move(res)); + if (result_ptr.is_error()) { + for (auto &promise : promises) { + if (!promise) { + promise.set_value(nullptr); + } else { + promise.set_error(result_ptr.error().clone()); + } + } + return; + } + + auto result = result_ptr.move_as_ok(); + process_app_config(result); + for (auto &promise : promises) { + if (!promise) { + promise.set_value(nullptr); + } else { + promise.set_value(convert_json_value_object(result)); + } + } + return; + } + + CHECK(token == 0); CHECK(config_sent_cnt_ > 0); config_sent_cnt_--; auto r_config = fetch_result(std::move(res)); @@ -906,7 +1144,7 @@ void ConfigManager::process_config(tl_object_ptr config) { bool is_from_main_dc = G()->net_query_dispatcher().main_dc_id().get_value() == config->this_dc_; LOG(INFO) << to_string(config); - auto reload_in = max(60 /* at least 60 seconds*/, config->expires_ - config->date_); + auto reload_in = clamp(config->expires_ - config->date_, 60, 86400); save_config_expire(Timestamp::in(reload_in)); reload_in -= Random::fast(0, reload_in / 5); if (!is_from_main_dc) { @@ -931,6 +1169,7 @@ void ConfigManager::process_config(tl_object_ptr config) { shared_config.set_option_integer("basic_group_size_max", config->chat_size_max_); shared_config.set_option_integer("supergroup_size_max", config->megagroup_size_max_); shared_config.set_option_integer("pinned_chat_count_max", config->pinned_dialogs_count_max_); + shared_config.set_option_integer("pinned_archived_chat_count_max", config->pinned_infolder_count_max_); if (is_from_main_dc || !shared_config.have_option("expect_blocking")) { shared_config.set_option_boolean("expect_blocking", (config->flags_ & telegram_api::config::BLOCKED_MODE_MASK) != 0); @@ -1039,6 +1278,100 @@ void ConfigManager::process_config(tl_object_ptr config) { // shared_config.set_option_integer("push_chat_period_ms", config->push_chat_period_ms_); // shared_config.set_option_integer("push_chat_limit", config->push_chat_limit_); + + if (is_from_main_dc) { + get_app_config(Auto()); + if (!shared_config.have_option("can_ignore_sensitive_content_restrictions") || + !shared_config.have_option("ignore_sensitive_content_restrictions")) { + get_content_settings(Auto()); + } + } +} + +void ConfigManager::process_app_config(tl_object_ptr &config) { + CHECK(config != nullptr); + LOG(INFO) << "Receive app config " << to_string(config); + + vector> new_values; + string wallet_blockchain_name; + string wallet_config; + string ignored_restriction_reasons; + if (config->get_id() == telegram_api::jsonObject::ID) { + for (auto &key_value : static_cast(config.get())->value_) { + Slice key = key_value->key_; + telegram_api::JSONValue *value = key_value->value_.get(); + if (key == "test" || key == "wallet_enabled") { + continue; + } + if (key == "wallet_blockchain_name") { + if (value->get_id() == telegram_api::jsonString::ID) { + wallet_blockchain_name = std::move(static_cast(value)->value_); + } else { + LOG(ERROR) << "Receive unexpected wallet_blockchain_name " << to_string(*value); + } + continue; + } + if (key == "wallet_config") { + if (value->get_id() == telegram_api::jsonString::ID) { + wallet_config = std::move(static_cast(value)->value_); + } else { + LOG(ERROR) << "Receive unexpected wallet_config " << to_string(*value); + } + continue; + } + if (key == "ignore_restriction_reasons") { + if (value->get_id() == telegram_api::jsonArray::ID) { + auto reasons = std::move(static_cast(value)->value_); + for (auto &reason : reasons) { + if (reason->get_id() == telegram_api::jsonString::ID) { + Slice reason_name = static_cast(reason.get())->value_; + if (!reason_name.empty() && reason_name.find(',') == Slice::npos) { + if (!ignored_restriction_reasons.empty()) { + ignored_restriction_reasons += ','; + } + ignored_restriction_reasons.append(reason_name.begin(), reason_name.end()); + } else { + LOG(ERROR) << "Receive unexpected restriction reason " << reason_name; + } + } else { + LOG(ERROR) << "Receive unexpected restriction reason " << to_string(reason); + } + } + } else { + LOG(ERROR) << "Receive unexpected ignore_restriction_reasons " << to_string(*value); + } + continue; + } + + new_values.push_back(std::move(key_value)); + } + } else { + LOG(ERROR) << "Receive wrong app config " << to_string(config); + } + config = make_tl_object(std::move(new_values)); + + ConfigShared &shared_config = G()->shared_config(); + if (wallet_config.empty()) { + shared_config.set_option_empty("default_ton_blockchain_config"); + shared_config.set_option_empty("default_ton_blockchain_name"); + } else { + shared_config.set_option_string("default_ton_blockchain_name", wallet_blockchain_name); + shared_config.set_option_string("default_ton_blockchain_config", wallet_config); + } + + if (ignored_restriction_reasons.empty()) { + shared_config.set_option_empty("ignored_restriction_reasons"); + + if (shared_config.get_option_boolean("ignore_sensitive_content_restrictions", true)) { + get_content_settings(Auto()); + } + } else { + shared_config.set_option_string("ignored_restriction_reasons", ignored_restriction_reasons); + + if (!shared_config.get_option_boolean("can_ignore_sensitive_content_restrictions")) { + get_content_settings(Auto()); + } + } } } // namespace td diff --git a/td/telegram/ConfigManager.h b/td/telegram/ConfigManager.h index ea7d1c1b..8b5af15b 100644 --- a/td/telegram/ConfigManager.h +++ b/td/telegram/ConfigManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,11 +10,13 @@ #include "td/telegram/net/DcOptions.h" #include "td/telegram/net/NetQuery.h" +#include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" +#include "td/utils/common.h" #include "td/utils/logging.h" #include "td/utils/port/IPAddress.h" #include "td/utils/Slice.h" @@ -44,6 +46,16 @@ ActorOwn<> get_simple_config_google_dns(Promise promise, con ActorOwn<> get_simple_config_mozilla_dns(Promise promise, const ConfigShared *shared_config, bool is_test, int32 scheduler_id); +ActorOwn<> get_simple_config_firebase_remote_config(Promise promise, + const ConfigShared *shared_config, bool is_test, + int32 scheduler_id); + +ActorOwn<> get_simple_config_firebase_realtime(Promise promise, const ConfigShared *shared_config, + bool is_test, int32 scheduler_id); + +ActorOwn<> get_simple_config_firebase_firestore(Promise promise, const ConfigShared *shared_config, + bool is_test, int32 scheduler_id); + class HttpDate { static bool is_leap(int32 year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); @@ -72,6 +84,12 @@ class ConfigManager : public NetQueryCallback { void request_config(); + void get_app_config(Promise> &&promise); + + void get_content_settings(Promise &&promise); + + void set_content_settings(bool ignore_sensitive_content_restrictions, Promise &&promise); + void on_dc_options_update(DcOptions dc_options); private: @@ -81,6 +99,12 @@ class ConfigManager : public NetQueryCallback { int ref_cnt_{1}; Timestamp expire_time_; + vector>> get_app_config_queries_; + vector> get_content_settings_queries_; + vector> set_content_settings_queries_[2]; + bool is_set_content_settings_request_sent_ = false; + bool last_set_content_settings_ = false; + void start_up() override; void hangup_shared() override; void hangup() override; @@ -91,6 +115,8 @@ class ConfigManager : public NetQueryCallback { void request_config_from_dc_impl(DcId dc_id); void process_config(tl_object_ptr config); + void process_app_config(tl_object_ptr &config); + void set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions); Timestamp load_config_expire_time(); void save_config_expire(Timestamp timestamp); diff --git a/td/telegram/ConfigShared.cpp b/td/telegram/ConfigShared.cpp index bed7e039..ead1572c 100644 --- a/td/telegram/ConfigShared.cpp +++ b/td/telegram/ConfigShared.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ConfigShared.h b/td/telegram/ConfigShared.h index ab8045f9..9628bec6 100644 --- a/td/telegram/ConfigShared.h +++ b/td/telegram/ConfigShared.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -41,7 +41,6 @@ class ConfigShared { void set_option_string(Slice name, Slice value); bool have_option(Slice name) const; - string get_option(Slice name) const; std::unordered_map get_options(Slice prefix) const; std::unordered_map get_options() const; @@ -59,6 +58,8 @@ class ConfigShared { bool set_option(Slice name, Slice value); + string get_option(Slice name) const; + void on_option_updated(Slice name) const; }; diff --git a/td/telegram/Contact.cpp b/td/telegram/Contact.cpp index 04f075ad..72522980 100644 --- a/td/telegram/Contact.cpp +++ b/td/telegram/Contact.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Contact.h b/td/telegram/Contact.h index f1f230dd..e9746f94 100644 --- a/td/telegram/Contact.h +++ b/td/telegram/Contact.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index d608ac15..acd42131 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,19 +12,26 @@ #include "td/telegram/AuthManager.h" #include "td/telegram/ConfigShared.h" +#include "td/telegram/Dependencies.h" +#include "td/telegram/DeviceTokenManager.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileType.h" +#include "td/telegram/FolderId.h" #include "td/telegram/Global.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/logevent/LogEvent.h" +#include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/NotificationManager.h" +#include "td/telegram/PasswordManager.h" #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" #include "td/telegram/SecretChatActor.h" +#include "td/telegram/ServerMessageId.h" +#include "td/telegram/StickerSetId.hpp" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" @@ -40,6 +47,7 @@ #include "td/db/SqliteKeyValue.h" #include "td/db/SqliteKeyValueAsync.h" +#include "td/utils/base64.h" #include "td/utils/buffer.h" #include "td/utils/format.h" #include "td/utils/logging.h" @@ -53,6 +61,7 @@ #include #include #include +#include #include namespace td { @@ -116,11 +125,36 @@ class GetAccountTtlQuery : public Td::ResultHandler { } }; +class AcceptLoginTokenQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit AcceptLoginTokenQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &login_token) { + send_query( + G()->net_query_creator().create(create_storer(telegram_api::auth_acceptLoginToken(BufferSlice(login_token))))); + } + + 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()); + } + + LOG(DEBUG) << "Receive result for AcceptLoginTokenQuery: " << to_string(result_ptr.ok()); + promise_.set_value(ContactsManager::convert_authorization_object(result_ptr.move_as_ok())); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class GetAuthorizationsQuery : public Td::ResultHandler { Promise> promise_; - static constexpr int32 AUTHORIZATION_FLAG_IS_CURRENT = 1 << 0; - static constexpr int32 AUTHORIZATION_FLAG_IS_OFFICIAL_APPLICATION = 1 << 1; - static constexpr int32 AUTHORIZATION_FLAG_IS_PASSWORD_PENDING = 1 << 2; public: explicit GetAuthorizationsQuery(Promise> &&promise) : promise_(std::move(promise)) { @@ -139,21 +173,8 @@ class GetAuthorizationsQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetAuthorizationsQuery: " << to_string(ptr); - auto results = make_tl_object(); - results->sessions_.reserve(ptr->authorizations_.size()); - for (auto &authorization : ptr->authorizations_) { - CHECK(authorization != nullptr); - bool is_current = (authorization->flags_ & AUTHORIZATION_FLAG_IS_CURRENT) != 0; - bool is_official_application = (authorization->flags_ & AUTHORIZATION_FLAG_IS_OFFICIAL_APPLICATION) != 0; - bool is_password_pending = (authorization->flags_ & AUTHORIZATION_FLAG_IS_PASSWORD_PENDING) != 0; - - results->sessions_.push_back(make_tl_object( - authorization->hash_, is_current, is_password_pending, authorization->api_id_, authorization->app_name_, - authorization->app_version_, is_official_application, authorization->device_model_, authorization->platform_, - authorization->system_version_, authorization->date_created_, authorization->date_active_, authorization->ip_, - authorization->country_, authorization->region_)); - } - + auto results = make_tl_object( + transform(std::move(ptr->authorizations_), ContactsManager::convert_authorization_object)); std::sort(results->sessions_.begin(), results->sessions_.end(), [](const td_api::object_ptr &lhs, const td_api::object_ptr &rhs) { if (lhs->is_current_ != rhs->is_current_) { @@ -220,6 +241,7 @@ class ResetAuthorizationsQuery : public Td::ResultHandler { bool result = result_ptr.move_as_ok(); LOG_IF(WARNING, !result) << "Failed to terminate all sessions"; + send_closure(td->device_token_manager_, &DeviceTokenManager::reregister_device); promise_.set_value(Unit()); } @@ -330,51 +352,39 @@ class ResetWebAuthorizationsQuery : public Td::ResultHandler { } }; -class BlockUserQuery : public Td::ResultHandler { +class SetUserIsBlockedQuery : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + public: - void send(tl_object_ptr &&user) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_block(std::move(user))))); + explicit SetUserIsBlockedQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + 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))))); + } else { + send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_unblock(std::move(input_user))))); + } } void on_result(uint64 id, BufferSlice packet) override { + static_assert( + std::is_same::value, ""); 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(); - LOG_IF(WARNING, !result) << "Block user has failed"; + LOG_IF(WARNING, !result) << "Block/Unblock " << user_id_ << " has failed"; + + promise_.set_value(Unit()); } void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { - LOG(WARNING) << "Receive error for blockUser: " << status; - } - status.ignore(); - } -}; - -class UnblockUserQuery : public Td::ResultHandler { - public: - void send(tl_object_ptr &&user) { - send_query(G()->net_query_creator().create(create_storer(telegram_api::contacts_unblock(std::move(user))))); - } - - 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(); - LOG_IF(WARNING, !result) << "Unblock user has failed"; - } - - void on_error(uint64 id, Status status) override { - if (!G()->close_flag()) { - LOG(WARNING) << "Receive error for unblockUser: " << status; - } - status.ignore(); + promise_.set_error(std::move(status)); } }; @@ -484,6 +494,79 @@ class GetContactsStatusesQuery : public Td::ResultHandler { } }; +class AddContactQuery : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + + public: + explicit AddContactQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(UserId user_id, tl_object_ptr &&input_user, const string &first_name, + const string &last_name, const string &phone_number, bool share_phone_number) { + user_id_ = user_id; + int32 flags = 0; + 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)))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for AddContactQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + promise_.set_value(Unit()); + } + + 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"); + } +}; + +class AcceptContactQuery : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + + public: + explicit AcceptContactQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + 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))))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for AcceptContactQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + promise_.set_value(Unit()); + } + + 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"); + } +}; + class ImportContactsQuery : public Td::ResultHandler { Promise promise_; vector input_contacts_; @@ -584,14 +667,12 @@ class ImportContactsQuery : public Td::ResultHandler { class DeleteContactsQuery : public Td::ResultHandler { Promise promise_; - vector user_ids_; public: explicit DeleteContactsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(vector &&user_ids, vector> &&input_users) { - user_ids_ = std::move(user_ids); + void send(vector> &&input_users) { send_query( G()->net_query_creator().create(create_storer(telegram_api::contacts_deleteContacts(std::move(input_users))))); } @@ -602,12 +683,10 @@ class DeleteContactsQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - bool result = result_ptr.ok(); - if (!result) { - return on_error(id, Status::Error(500, "Some contacts can't be deleted")); - } + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for DeleteContactsQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); - td->contacts_manager_->on_deleted_contacts(user_ids_); promise_.set_value(Unit()); } @@ -686,6 +765,33 @@ class ResetContactsQuery : public Td::ResultHandler { } }; +class SearchDialogsNearbyQuery : public Td::ResultHandler { + Promise> promise_; + + public: + explicit SearchDialogsNearbyQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const Location &location) { + send_query(G()->net_query_creator().create( + create_storer(telegram_api::contacts_getLocated(location.get_input_geo_point())))); + } + + 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()); + } + + promise_.set_value(result_ptr.move_as_ok()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class UploadProfilePhotoQuery : public Td::ResultHandler { Promise promise_; FileId file_id_; @@ -993,13 +1099,13 @@ class UpdateChannelUsernameQuery : public Td::ResultHandler { class SetChannelStickerSetQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; - int64 sticker_set_id_; + StickerSetId sticker_set_id_; public: explicit SetChannelStickerSetQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(ChannelId channel_id, int64 sticker_set_id, + void send(ChannelId channel_id, StickerSetId sticker_set_id, telegram_api::object_ptr &&input_sticker_set) { channel_id_ = channel_id; sticker_set_id_ = sticker_set_id; @@ -1190,6 +1296,132 @@ class EditChatAboutQuery : public Td::ResultHandler { } }; +class SetDiscussionGroupQuery : public Td::ResultHandler { + Promise promise_; + ChannelId broadcast_channel_id_; + ChannelId group_channel_id_; + + public: + explicit SetDiscussionGroupQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId broadcast_channel_id, + telegram_api::object_ptr broadcast_input_channel, ChannelId group_channel_id, + 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))))); + } + + 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.move_as_ok(); + LOG_IF(INFO, !result) << "Set discussion group has failed"; + + td->contacts_manager_->on_update_channel_linked_channel_id(broadcast_channel_id_, group_channel_id_); + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (status.message() == "LINK_NOT_MODIFIED") { + return promise_.set_value(Unit()); + } + promise_.set_error(std::move(status)); + } +}; + +class EditLocationQuery : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + DialogLocation location_; + + public: + explicit EditLocationQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, const DialogLocation &location) { + channel_id_ = channel_id; + location_ = location; + + 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())))); + } + + 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.move_as_ok(); + LOG_IF(INFO, !result) << "Edit chat location has failed"; + + td->contacts_manager_->on_update_channel_location(channel_id_, location_); + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "EditLocationQuery"); + promise_.set_error(std::move(status)); + } +}; + +class ToggleSlowModeQuery : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + int32 slow_mode_delay_ = 0; + + public: + explicit ToggleSlowModeQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, int32 slow_mode_delay) { + channel_id_ = channel_id; + slow_mode_delay_ = slow_mode_delay; + + 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_toggleSlowMode(std::move(input_channel), slow_mode_delay)))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for toggleSlowMode: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + td->contacts_manager_->on_update_channel_slow_mode_delay(channel_id_, slow_mode_delay_); + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (status.message() == "CHAT_NOT_MODIFIED") { + td->contacts_manager_->on_update_channel_slow_mode_delay(channel_id_, slow_mode_delay_); + if (!td->auth_manager_->is_bot()) { + promise_.set_value(Unit()); + return; + } + } else { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "ToggleSlowModeQuery"); + } + promise_.set_error(std::move(status)); + } +}; + class ReportChannelSpamQuery : public Td::ResultHandler { Promise promise_; ChannelId channel_id_; @@ -1573,7 +1805,7 @@ class InviteToChannelQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for inviteToChannel: " << to_string(ptr); td->updates_manager_->on_get_updates(std::move(ptr)); - td->contacts_manager_->invalidate_channel_full(channel_id_, false); + td->contacts_manager_->invalidate_channel_full(channel_id_, false, false); promise_.set_value(Unit()); } @@ -1598,7 +1830,7 @@ class EditChannelAdminQuery : 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_editAdmin( - std::move(input_channel), std::move(input_user), status.get_chat_admin_rights())))); + std::move(input_channel), std::move(input_user), status.get_chat_admin_rights(), status.get_rank())))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1610,7 +1842,7 @@ class EditChannelAdminQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for editChannelAdmin: " << to_string(ptr); td->updates_manager_->on_get_updates(std::move(ptr)); - td->contacts_manager_->invalidate_channel_full(channel_id_, false); + td->contacts_manager_->invalidate_channel_full(channel_id_, false, false); promise_.set_value(Unit()); } @@ -1647,7 +1879,7 @@ class EditChannelBannedQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for editChannelBanned: " << to_string(ptr); td->updates_manager_->on_get_updates(std::move(ptr)); - td->contacts_manager_->invalidate_channel_full(channel_id_, false); + td->contacts_manager_->invalidate_channel_full(channel_id_, false, false); promise_.set_value(Unit()); } @@ -1695,6 +1927,81 @@ class LeaveChannelQuery : public Td::ResultHandler { } }; +class CanEditChannelCreatorQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit CanEditChannelCreatorQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + 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( + telegram_api::make_object(), std::move(input_user), + make_tl_object())))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(ERROR) << "Receive result for CanEditChannelCreator: " << to_string(ptr); + promise_.set_error(Status::Error(500, "Server doesn't returned error")); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + +class EditChannelCreatorQuery : public Td::ResultHandler { + Promise promise_; + ChannelId channel_id_; + + public: + explicit EditChannelCreatorQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, UserId user_id, + tl_object_ptr input_check_password) { + channel_id_ = channel_id; + auto input_channel = td->contacts_manager_->get_input_channel(channel_id); + if (input_channel == nullptr) { + return promise_.set_error(Status::Error(400, "Have no access to the chat")); + } + auto input_user = td->contacts_manager_->get_input_user(user_id); + 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))))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for editChannelCreator: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + td->contacts_manager_->invalidate_channel_full(channel_id_, false, false); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->contacts_manager_->on_get_channel_error(channel_id_, status, "EditChannelCreatorQuery"); + promise_.set_error(std::move(status)); + td->updates_manager_->get_difference("EditChannelCreatorQuery"); + } +}; + class MigrateChatQuery : public Td::ResultHandler { Promise promise_; @@ -1727,13 +2034,23 @@ class MigrateChatQuery : public Td::ResultHandler { class GetCreatedPublicChannelsQuery : public Td::ResultHandler { Promise promise_; + PublicDialogType type_; public: explicit GetCreatedPublicChannelsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getAdminedPublicChannels()))); + void send(PublicDialogType type, bool check_limit) { + type_ = type; + int32 flags = 0; + if (type_ == PublicDialogType::IsLocationBased) { + flags |= telegram_api::channels_getAdminedPublicChannels::BY_LOCATION_MASK; + } + if (check_limit) { + 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*/)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1748,13 +2065,13 @@ class GetCreatedPublicChannelsQuery : public Td::ResultHandler { switch (constructor_id) { case telegram_api::messages_chats::ID: { auto chats = move_tl_object_as(chats_ptr); - td->contacts_manager_->on_get_created_public_channels(std::move(chats->chats_)); + td->contacts_manager_->on_get_created_public_channels(type_, std::move(chats->chats_)); break; } case telegram_api::messages_chatsSlice::ID: { auto chats = move_tl_object_as(chats_ptr); LOG(ERROR) << "Receive chatsSlice in result of GetCreatedPublicChannelsQuery"; - td->contacts_manager_->on_get_created_public_channels(std::move(chats->chats_)); + td->contacts_manager_->on_get_created_public_channels(type_, std::move(chats->chats_)); break; } default: @@ -1769,6 +2086,81 @@ class GetCreatedPublicChannelsQuery : public Td::ResultHandler { } }; +class GetGroupsForDiscussionQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit GetGroupsForDiscussionQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send() { + send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getGroupsForDiscussion()))); + } + + 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 chats_ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetGroupsForDiscussionQuery " << to_string(chats_ptr); + int32 constructor_id = chats_ptr->get_id(); + switch (constructor_id) { + case telegram_api::messages_chats::ID: { + auto chats = move_tl_object_as(chats_ptr); + td->contacts_manager_->on_get_dialogs_for_discussion(std::move(chats->chats_)); + break; + } + case telegram_api::messages_chatsSlice::ID: { + auto chats = move_tl_object_as(chats_ptr); + LOG(ERROR) << "Receive chatsSlice in result of GetCreatedPublicChannelsQuery"; + td->contacts_manager_->on_get_dialogs_for_discussion(std::move(chats->chats_)); + break; + } + default: + UNREACHABLE(); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + +class GetInactiveChannelsQuery : public Td::ResultHandler { + Promise promise_; + + public: + explicit GetInactiveChannelsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send() { + send_query(G()->net_query_creator().create(create_storer(telegram_api::channels_getInactiveChannels()))); + } + + 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(); + LOG(INFO) << "Receive result for GetInactiveChannelsQuery " << to_string(result); + // TODO use result->dates_ + td->contacts_manager_->on_get_users(std::move(result->users_), "GetInactiveChannelsQuery"); + td->contacts_manager_->on_get_inactive_channels(std::move(result->chats_)); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + promise_.set_error(std::move(status)); + } +}; + class GetUsersQuery : public Td::ResultHandler { Promise promise_; @@ -1940,9 +2332,7 @@ class GetFullChatQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); td->contacts_manager_->on_get_users(std::move(ptr->users_), "GetFullChatQuery"); td->contacts_manager_->on_get_chats(std::move(ptr->chats_), "GetFullChatQuery"); - td->contacts_manager_->on_get_chat_full(std::move(ptr->full_chat_)); - - promise_.set_value(Unit()); + td->contacts_manager_->on_get_chat_full(std::move(ptr->full_chat_), std::move(promise_)); } void on_error(uint64 id, Status status) override { @@ -2027,9 +2417,7 @@ class GetFullChannelQuery : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); td->contacts_manager_->on_get_users(std::move(ptr->users_), "GetFullChannelQuery"); td->contacts_manager_->on_get_chats(std::move(ptr->chats_), "GetFullChannelQuery"); - td->contacts_manager_->on_get_chat_full(std::move(ptr->full_chat_)); - - promise_.set_value(Unit()); + td->contacts_manager_->on_get_chat_full(std::move(ptr->full_chat_), std::move(promise_)); } void on_error(uint64 id, Status status) override { @@ -2163,6 +2551,8 @@ class GetChannelAdministratorsQuery : public Td::ResultHandler { return promise_.set_error(Status::Error(3, "Supergroup not found")); } + 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( std::move(input_channel), telegram_api::make_object(), 0, @@ -2181,18 +2571,24 @@ class GetChannelAdministratorsQuery : public Td::ResultHandler { case telegram_api::channels_channelParticipants::ID: { auto participants = telegram_api::move_object_as(participants_ptr); td->contacts_manager_->on_get_users(std::move(participants->users_), "GetChannelAdministratorsQuery"); - vector administrator_user_ids; - administrator_user_ids.reserve(participants->participants_.size()); + vector administrators; + administrators.reserve(participants->participants_.size()); for (auto &participant : participants->participants_) { - UserId user_id; - downcast_call(*participant, [&user_id](auto &participant) { user_id = UserId(participant.user_id_); }); - if (user_id.is_valid()) { - administrator_user_ids.push_back(user_id); + DialogParticipant dialog_participant = + td->contacts_manager_->get_dialog_participant(channel_id_, std::move(participant)); + if (!dialog_participant.user_id.is_valid() || !dialog_participant.status.is_administrator()) { + LOG(ERROR) << "Receive " << dialog_participant.user_id << " with status " << dialog_participant.status + << " as an administrator of " << channel_id_; + continue; } + administrators.emplace_back(dialog_participant.user_id, dialog_participant.status.get_rank(), + dialog_participant.status.is_creator()); } - td->contacts_manager_->on_update_dialog_administrators(DialogId(channel_id_), std::move(administrator_user_ids), - true); + td->contacts_manager_->on_update_channel_administrator_count(channel_id_, + narrow_cast(administrators.size())); + td->contacts_manager_->on_update_dialog_administrators(DialogId(channel_id_), std::move(administrators), true); + break; } case telegram_api::channels_channelParticipantsNotModified::ID: @@ -2240,28 +2636,6 @@ class GetSupportUserQuery : public Td::ResultHandler { } }; -StringBuilder &operator<<(StringBuilder &string_builder, ContactsManager::LinkState link_state) { - switch (link_state) { - case ContactsManager::LinkState::Unknown: - string_builder << "unknown"; - break; - case ContactsManager::LinkState::None: - string_builder << "none"; - break; - case ContactsManager::LinkState::KnowsPhoneNumber: - string_builder << "knows phone number"; - break; - case ContactsManager::LinkState::Contact: - string_builder << "contact"; - break; - } - return string_builder; -} - -bool ContactsManager::UserFull::is_bot_info_expired(int32 bot_info_version) const { - return bot_info_version != -1 && (bot_info == nullptr || bot_info->version != bot_info_version); -} - bool ContactsManager::UserFull::is_expired() const { return expires_at < Time::now(); } @@ -2322,6 +2696,12 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent channel_unban_timeout_.set_callback(on_channel_unban_timeout_callback); channel_unban_timeout_.set_callback_data(static_cast(this)); + + user_nearby_timeout_.set_callback(on_user_nearby_timeout_callback); + user_nearby_timeout_.set_callback_data(static_cast(this)); + + slow_mode_delay_timeout_.set_callback(on_slow_mode_delay_timeout_callback); + slow_mode_delay_timeout_.set_callback_data(static_cast(this)); } void ContactsManager::tear_down() { @@ -2387,25 +2767,96 @@ void ContactsManager::on_channel_unban_timeout(ChannelId channel_id) { LOG_IF(ERROR, c->status.is_restricted() || c->status.is_banned()) << "Status of " << channel_id << " wasn't updated: " << c->status; } else { - c->need_send_update = true; + c->is_changed = true; } LOG(INFO) << "Update " << channel_id << " status"; c->is_status_changed = true; - invalidate_channel_full(channel_id, false); + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); update_channel(c, channel_id); // always call, because in case of failure we need to reactivate timeout } +void ContactsManager::on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long) { + if (G()->close_flag()) { + return; + } + + auto contacts_manager = static_cast(contacts_manager_ptr); + send_closure_later(contacts_manager->actor_id(contacts_manager), &ContactsManager::on_user_nearby_timeout, + UserId(narrow_cast(user_id_long))); +} + +void ContactsManager::on_user_nearby_timeout(UserId user_id) { + if (G()->close_flag()) { + return; + } + + auto u = get_user(user_id); + CHECK(u != nullptr); + + LOG(INFO) << "Remove " << user_id << " from nearby list"; + DialogId dialog_id(user_id); + for (size_t i = 0; i < users_nearby_.size(); i++) { + if (users_nearby_[i].dialog_id == dialog_id) { + users_nearby_.erase(users_nearby_.begin() + i); + send_update_users_nearby(); + return; + } + } +} + +void ContactsManager::on_slow_mode_delay_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long) { + if (G()->close_flag()) { + return; + } + + auto contacts_manager = static_cast(contacts_manager_ptr); + send_closure_later(contacts_manager->actor_id(contacts_manager), &ContactsManager::on_slow_mode_delay_timeout, + ChannelId(narrow_cast(channel_id_long))); +} + +void ContactsManager::on_slow_mode_delay_timeout(ChannelId channel_id) { + if (G()->close_flag()) { + return; + } + + on_update_channel_slow_mode_next_send_date(channel_id, 0); +} + template -void ContactsManager::store_link_state(const LinkState &link_state, StorerT &storer) { - store(static_cast(link_state), storer); +void ContactsManager::BotInfo::store(StorerT &storer) const { + using td::store; + bool has_description = !description.empty(); + bool has_commands = !commands.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_description); + STORE_FLAG(has_commands); + END_STORE_FLAGS(); + store(version, storer); + if (has_description) { + store(description, storer); + } + if (has_commands) { + store(commands, storer); + } } template -void ContactsManager::parse_link_state(LinkState &link_state, ParserT &parser) { - uint32 link_state_uint32; - parse(link_state_uint32, parser); - link_state = static_cast(static_cast(link_state_uint32)); +void ContactsManager::BotInfo::parse(ParserT &parser) { + using td::parse; + bool has_description; + bool has_commands; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_description); + PARSE_FLAG(has_commands); + END_PARSE_FLAGS(); + parse(version, parser); + if (has_description) { + parse(description, parser); + } + if (has_commands) { + parse(commands, parser); + } } template @@ -2414,10 +2865,11 @@ void ContactsManager::User::store(StorerT &storer) const { bool has_last_name = !last_name.empty(); bool has_username = !username.empty(); bool has_photo = photo.small_file_id.is_valid(); - bool is_restricted = !restriction_reason.empty(); bool has_language_code = !language_code.empty(); bool have_access_hash = access_hash != -1; bool has_cache_version = cache_version != 0; + bool has_is_contact = true; + bool has_restriction_reasons = !restriction_reasons.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(is_received); STORE_FLAG(is_verified); @@ -2430,13 +2882,17 @@ void ContactsManager::User::store(StorerT &storer) const { STORE_FLAG(has_last_name); STORE_FLAG(has_username); STORE_FLAG(has_photo); - STORE_FLAG(is_restricted); + STORE_FLAG(false); // legacy is_restricted STORE_FLAG(has_language_code); STORE_FLAG(have_access_hash); STORE_FLAG(is_support); STORE_FLAG(is_min_access_hash); STORE_FLAG(is_scam); STORE_FLAG(has_cache_version); + STORE_FLAG(has_is_contact); + STORE_FLAG(is_contact); + STORE_FLAG(is_mutual_contact); + STORE_FLAG(has_restriction_reasons); END_STORE_FLAGS(); store(first_name, storer); if (has_last_name) { @@ -2452,11 +2908,9 @@ void ContactsManager::User::store(StorerT &storer) const { if (has_photo) { store(photo, storer); } - store_link_state(inbound, storer); - store_link_state(outbound, storer); store(was_online, storer); - if (is_restricted) { - store(restriction_reason, storer); + if (has_restriction_reasons) { + store(restriction_reasons, storer); } if (is_inline_bot) { store(inline_query_placeholder, storer); @@ -2478,10 +2932,12 @@ void ContactsManager::User::parse(ParserT &parser) { bool has_last_name; bool has_username; bool has_photo; - bool is_restricted; + bool legacy_is_restricted; bool has_language_code; bool have_access_hash; bool has_cache_version; + bool has_is_contact; + bool has_restriction_reasons; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_received); PARSE_FLAG(is_verified); @@ -2494,13 +2950,17 @@ void ContactsManager::User::parse(ParserT &parser) { PARSE_FLAG(has_last_name); PARSE_FLAG(has_username); PARSE_FLAG(has_photo); - PARSE_FLAG(is_restricted); + PARSE_FLAG(legacy_is_restricted); PARSE_FLAG(has_language_code); PARSE_FLAG(have_access_hash); PARSE_FLAG(is_support); PARSE_FLAG(is_min_access_hash); PARSE_FLAG(is_scam); PARSE_FLAG(has_cache_version); + PARSE_FLAG(has_is_contact); + PARSE_FLAG(is_contact); + PARSE_FLAG(is_mutual_contact); + PARSE_FLAG(has_restriction_reasons); END_PARSE_FLAGS(); parse(first_name, parser); if (has_last_name) { @@ -2519,11 +2979,24 @@ void ContactsManager::User::parse(ParserT &parser) { if (has_photo) { parse(photo, parser); } - parse_link_state(inbound, parser); - parse_link_state(outbound, parser); + if (!has_is_contact) { + // enum class LinkState : uint8 { Unknown, None, KnowsPhoneNumber, Contact }; + + uint32 link_state_inbound; + uint32 link_state_outbound; + parse(link_state_inbound, parser); + parse(link_state_outbound, parser); + + is_contact = link_state_outbound == 3; + is_mutual_contact = is_contact && link_state_inbound == 3; + } parse(was_online, parser); - if (is_restricted) { + if (legacy_is_restricted) { + string restriction_reason; parse(restriction_reason, parser); + restriction_reasons = get_restriction_reasons(restriction_reason); + } else if (has_restriction_reasons) { + parse(restriction_reasons, parser); } if (is_inline_bot) { parse(inline_query_placeholder, parser); @@ -2537,9 +3010,53 @@ void ContactsManager::User::parse(ParserT &parser) { if (has_cache_version) { parse(cache_version, parser); } + if (first_name.empty() && last_name.empty()) { first_name = phone_number; } + if (!is_contact && is_mutual_contact) { + LOG(ERROR) << "Have invalid flag is_mutual_contact"; + is_mutual_contact = false; + cache_version = 0; + } +} + +template +void ContactsManager::UserFull::store(StorerT &storer) const { + using td::store; + bool has_about = !about.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_about); + STORE_FLAG(is_blocked); + STORE_FLAG(can_be_called); + STORE_FLAG(has_private_calls); + STORE_FLAG(can_pin_messages); + STORE_FLAG(need_phone_number_privacy_exception); + END_STORE_FLAGS(); + if (has_about) { + store(about, storer); + } + store(common_chat_count, storer); + store_time(expires_at, storer); +} + +template +void ContactsManager::UserFull::parse(ParserT &parser) { + using td::parse; + bool has_about; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_about); + PARSE_FLAG(is_blocked); + PARSE_FLAG(can_be_called); + PARSE_FLAG(has_private_calls); + PARSE_FLAG(can_pin_messages); + PARSE_FLAG(need_phone_number_privacy_exception); + END_PARSE_FLAGS(); + if (has_about) { + parse(about, parser); + } + parse(common_chat_count, parser); + parse_time(expires_at, parser); } template @@ -2636,7 +3153,7 @@ void ContactsManager::Chat::parse(ParserT &parser) { } else if (left) { status = DialogParticipantStatus::Left(); } else if (is_creator) { - status = DialogParticipantStatus::Creator(true); + status = DialogParticipantStatus::Creator(true, string()); } else if (is_administrator && !everyone_is_administrator) { status = DialogParticipantStatus::GroupAdministrator(false); } else { @@ -2656,16 +3173,58 @@ void ContactsManager::Chat::parse(ParserT &parser) { } } +template +void ContactsManager::ChatFull::store(StorerT &storer) const { + using td::store; + bool has_description = !description.empty(); + bool has_invite_link = !invite_link.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_description); + STORE_FLAG(has_invite_link); + STORE_FLAG(can_set_username); + END_STORE_FLAGS(); + store(version, storer); + store(creator_user_id, storer); + store(participants, storer); + if (has_description) { + store(description, storer); + } + if (has_invite_link) { + store(invite_link, storer); + } +} + +template +void ContactsManager::ChatFull::parse(ParserT &parser) { + using td::parse; + bool has_description; + bool has_invite_link; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_description); + PARSE_FLAG(has_invite_link); + PARSE_FLAG(can_set_username); + END_PARSE_FLAGS(); + parse(version, parser); + parse(creator_user_id, parser); + parse(participants, parser); + if (has_description) { + parse(description, parser); + } + if (has_invite_link) { + parse(invite_link, parser); + } +} + template void ContactsManager::Channel::store(StorerT &storer) const { using td::store; bool has_photo = photo.small_file_id.is_valid(); bool has_username = !username.empty(); - bool is_restricted = !restriction_reason.empty(); bool use_new_rights = true; bool has_participant_count = participant_count != 0; bool have_default_permissions = true; bool has_cache_version = cache_version != 0; + bool has_restriction_reasons = !restriction_reasons.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(false); STORE_FLAG(false); @@ -2678,12 +3237,16 @@ void ContactsManager::Channel::store(StorerT &storer) const { STORE_FLAG(is_verified); STORE_FLAG(has_photo); STORE_FLAG(has_username); - STORE_FLAG(is_restricted); + STORE_FLAG(false); STORE_FLAG(use_new_rights); STORE_FLAG(has_participant_count); STORE_FLAG(have_default_permissions); STORE_FLAG(is_scam); STORE_FLAG(has_cache_version); + STORE_FLAG(has_linked_channel); + STORE_FLAG(has_location); + STORE_FLAG(is_slow_mode_enabled); + STORE_FLAG(has_restriction_reasons); END_STORE_FLAGS(); store(status, storer); @@ -2696,8 +3259,8 @@ void ContactsManager::Channel::store(StorerT &storer) const { store(username, storer); } store(date, storer); - if (is_restricted) { - store(restriction_reason, storer); + if (has_restriction_reasons) { + store(restriction_reasons, storer); } if (has_participant_count) { store(participant_count, storer); @@ -2715,7 +3278,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { using td::parse; bool has_photo; bool has_username; - bool is_restricted; + bool legacy_is_restricted; bool left; bool kicked; bool is_creator; @@ -2726,6 +3289,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { bool has_participant_count; bool have_default_permissions; bool has_cache_version; + bool has_restriction_reasons; BEGIN_PARSE_FLAGS(); PARSE_FLAG(left); PARSE_FLAG(kicked); @@ -2738,12 +3302,16 @@ void ContactsManager::Channel::parse(ParserT &parser) { PARSE_FLAG(is_verified); PARSE_FLAG(has_photo); PARSE_FLAG(has_username); - PARSE_FLAG(is_restricted); + PARSE_FLAG(legacy_is_restricted); PARSE_FLAG(use_new_rights); PARSE_FLAG(has_participant_count); PARSE_FLAG(have_default_permissions); PARSE_FLAG(is_scam); PARSE_FLAG(has_cache_version); + PARSE_FLAG(has_linked_channel); + PARSE_FLAG(has_location); + PARSE_FLAG(is_slow_mode_enabled); + PARSE_FLAG(has_restriction_reasons); END_PARSE_FLAGS(); if (use_new_rights) { @@ -2754,7 +3322,7 @@ void ContactsManager::Channel::parse(ParserT &parser) { } else if (left) { status = DialogParticipantStatus::Left(); } else if (is_creator) { - status = DialogParticipantStatus::Creator(true); + status = DialogParticipantStatus::Creator(true, string()); } else if (can_edit || can_moderate) { status = DialogParticipantStatus::ChannelAdministrator(false, is_megagroup); } else { @@ -2770,8 +3338,12 @@ void ContactsManager::Channel::parse(ParserT &parser) { parse(username, parser); } parse(date, parser); - if (is_restricted) { + if (legacy_is_restricted) { + string restriction_reason; parse(restriction_reason, parser); + restriction_reasons = get_restriction_reasons(restriction_reason); + } else if (has_restriction_reasons) { + parse(restriction_reasons, parser); } if (has_participant_count) { parse(participant_count, parser); @@ -2789,6 +3361,166 @@ void ContactsManager::Channel::parse(ParserT &parser) { } } +template +void ContactsManager::ChannelFull::store(StorerT &storer) const { + using td::store; + bool has_description = !description.empty(); + bool has_administrator_count = administrator_count != 0; + bool has_restricted_count = restricted_count != 0; + bool has_banned_count = banned_count != 0; + bool has_invite_link = !invite_link.empty(); + bool has_sticker_set = sticker_set_id.is_valid(); + bool has_linked_channel_id = linked_channel_id.is_valid(); + bool has_migrated_from_max_message_id = migrated_from_max_message_id.is_valid(); + bool has_migrated_from_chat_id = migrated_from_chat_id.is_valid(); + bool has_location = !location.empty(); + 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; + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_description); + STORE_FLAG(has_administrator_count); + STORE_FLAG(has_restricted_count); + STORE_FLAG(has_banned_count); + STORE_FLAG(has_invite_link); + STORE_FLAG(has_sticker_set); + STORE_FLAG(has_linked_channel_id); + STORE_FLAG(has_migrated_from_max_message_id); + STORE_FLAG(has_migrated_from_chat_id); + STORE_FLAG(can_get_participants); + STORE_FLAG(can_set_username); + STORE_FLAG(can_set_sticker_set); + STORE_FLAG(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); + END_STORE_FLAGS(); + if (has_description) { + store(description, storer); + } + store(participant_count, storer); + if (has_administrator_count) { + store(administrator_count, storer); + } + if (has_restricted_count) { + store(restricted_count, storer); + } + if (has_banned_count) { + store(banned_count, storer); + } + if (has_invite_link) { + store(invite_link, storer); + } + if (has_sticker_set) { + store(sticker_set_id, storer); + } + if (has_linked_channel_id) { + store(linked_channel_id, storer); + } + if (has_location) { + store(location, storer); + } + if (has_bot_user_ids) { + store(bot_user_ids, storer); + } + if (has_migrated_from_max_message_id) { + store(migrated_from_max_message_id, storer); + } + if (has_migrated_from_chat_id) { + store(migrated_from_chat_id, storer); + } + if (is_slow_mode_enabled) { + store(slow_mode_delay, storer); + } + if (is_slow_mode_delay_active) { + store(slow_mode_next_send_date, storer); + } + store_time(expires_at, storer); +} + +template +void ContactsManager::ChannelFull::parse(ParserT &parser) { + using td::parse; + bool has_description; + bool has_administrator_count; + bool has_restricted_count; + bool has_banned_count; + bool has_invite_link; + bool has_sticker_set; + bool has_linked_channel_id; + bool has_migrated_from_max_message_id; + bool has_migrated_from_chat_id; + bool has_location; + bool has_bot_user_ids; + bool is_slow_mode_enabled; + bool is_slow_mode_delay_active; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_description); + PARSE_FLAG(has_administrator_count); + PARSE_FLAG(has_restricted_count); + PARSE_FLAG(has_banned_count); + PARSE_FLAG(has_invite_link); + PARSE_FLAG(has_sticker_set); + PARSE_FLAG(has_linked_channel_id); + PARSE_FLAG(has_migrated_from_max_message_id); + PARSE_FLAG(has_migrated_from_chat_id); + PARSE_FLAG(can_get_participants); + PARSE_FLAG(can_set_username); + PARSE_FLAG(can_set_sticker_set); + PARSE_FLAG(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); + END_PARSE_FLAGS(); + if (has_description) { + parse(description, parser); + } + parse(participant_count, parser); + if (has_administrator_count) { + parse(administrator_count, parser); + } + if (has_restricted_count) { + parse(restricted_count, parser); + } + if (has_banned_count) { + parse(banned_count, parser); + } + if (has_invite_link) { + parse(invite_link, parser); + } + if (has_sticker_set) { + parse(sticker_set_id, parser); + } + if (has_linked_channel_id) { + parse(linked_channel_id, parser); + } + if (has_location) { + parse(location, parser); + } + if (has_bot_user_ids) { + parse(bot_user_ids, parser); + } + if (has_migrated_from_max_message_id) { + parse(migrated_from_max_message_id, parser); + } + if (has_migrated_from_chat_id) { + parse(migrated_from_chat_id, parser); + } + if (is_slow_mode_enabled) { + parse(slow_mode_delay, parser); + } + if (is_slow_mode_delay_active) { + parse(slow_mode_next_send_date, parser); + } + parse_time(expires_at, parser); +} + template void ContactsManager::SecretChat::store(StorerT &storer) const { using td::store; @@ -2947,20 +3679,21 @@ tl_object_ptr ContactsManager::get_input_peer_chat(Chat bool ContactsManager::have_input_peer_channel(ChannelId channel_id, AccessRights access_rights) const { const Channel *c = get_channel(channel_id); - return have_input_peer_channel(c, access_rights); + return have_input_peer_channel(c, channel_id, access_rights); } tl_object_ptr ContactsManager::get_input_peer_channel(ChannelId channel_id, AccessRights access_rights) const { const Channel *c = get_channel(channel_id); - if (!have_input_peer_channel(c, access_rights)) { + if (!have_input_peer_channel(c, channel_id, access_rights)) { return nullptr; } return make_tl_object(channel_id.get(), c->access_hash); } -bool ContactsManager::have_input_peer_channel(const Channel *c, AccessRights access_rights) { +bool ContactsManager::have_input_peer_channel(const Channel *c, ChannelId channel_id, AccessRights access_rights, + bool from_linked) const { if (c == nullptr) { return false; } @@ -2970,8 +3703,17 @@ bool ContactsManager::have_input_peer_channel(const Channel *c, AccessRights acc if (c->status.is_banned()) { return false; } - if (!c->username.empty() && access_rights == AccessRights::Read) { - return true; + if (access_rights == AccessRights::Read) { + if (!c->username.empty() || c->has_location) { + return true; + } + if (!from_linked) { + auto linked_channel_id = get_linked_channel_id(channel_id); + if (linked_channel_id.is_valid() && + have_input_peer_channel(get_channel(linked_channel_id), linked_channel_id, access_rights, true)) { + return true; + } + } } if (!c->status.is_member()) { return false; @@ -3130,24 +3872,6 @@ int32 ContactsManager::get_secret_chat_ttl(SecretChatId secret_chat_id) const { return c->ttl; } -bool ContactsManager::default_can_report_spam_in_secret_chat(SecretChatId secret_chat_id) const { - auto c = get_secret_chat(secret_chat_id); - if (c == nullptr) { - return true; - } - if (c->is_outbound) { - return false; - } - auto u = get_user(c->user_id); - if (u == nullptr) { - return true; - } - if (u->outbound == LinkState::Contact) { - return false; - } - return true; -} - string ContactsManager::get_user_username(UserId user_id) const { if (!user_id.is_valid()) { return string(); @@ -3401,6 +4125,33 @@ void ContactsManager::get_account_ttl(Promise &&promise) const { td_->create_handler(std::move(promise))->send(); } +td_api::object_ptr ContactsManager::convert_authorization_object( + tl_object_ptr &&authorization) { + CHECK(authorization != nullptr); + bool is_current = (authorization->flags_ & telegram_api::authorization::CURRENT_MASK) != 0; + bool is_official_application = (authorization->flags_ & telegram_api::authorization::OFFICIAL_APP_MASK) != 0; + bool is_password_pending = (authorization->flags_ & telegram_api::authorization::PASSWORD_PENDING_MASK) != 0; + + return td_api::make_object( + authorization->hash_, is_current, is_password_pending, authorization->api_id_, authorization->app_name_, + authorization->app_version_, is_official_application, authorization->device_model_, authorization->platform_, + authorization->system_version_, authorization->date_created_, authorization->date_active_, authorization->ip_, + authorization->country_, authorization->region_); +} + +void ContactsManager::confirm_qr_code_authentication(string link, + Promise> &&promise) { + Slice prefix("tg://login?token="); + if (!begins_with(to_lower(link), prefix)) { + return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID")); + } + auto r_token = base64url_decode(Slice(link).substr(prefix.size())); + if (r_token.is_error()) { + return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID")); + } + td_->create_handler(std::move(promise))->send(r_token.ok()); +} + void ContactsManager::get_active_sessions(Promise> &&promise) const { td_->create_handler(std::move(promise))->send(); } @@ -3425,36 +4176,34 @@ void ContactsManager::disconnect_all_websites(Promise &&promise) const { td_->create_handler(std::move(promise))->send(); } -Status ContactsManager::block_user(UserId user_id) { +Status ContactsManager::set_user_is_blocked(UserId user_id, bool is_blocked) { if (user_id == get_my_id()) { - return Status::Error(5, "Can't block self"); + return Status::Error(5, is_blocked ? Slice("Can't block self") : Slice("Can't unblock self")); } - auto user = get_input_user(user_id); - if (user == nullptr) { + auto input_user = get_input_user(user_id); + if (input_user == nullptr) { return Status::Error(5, "User not found"); } - td_->create_handler()->send(std::move(user)); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), user_id, is_blocked](Result result) { + if (!G()->close_flag() && result.is_error()) { + send_closure(actor_id, &ContactsManager::on_set_user_is_blocked_failed, user_id, is_blocked, + result.move_as_error()); + } + }); + td_->create_handler(std::move(query_promise)) + ->send(user_id, std::move(input_user), is_blocked); - on_update_user_blocked(user_id, true); + on_update_user_is_blocked(user_id, is_blocked); return Status::OK(); } -Status ContactsManager::unblock_user(UserId user_id) { - if (user_id == get_my_id()) { - return Status::Error(5, "Can't unblock self"); - } - - auto user = get_input_user(user_id); - if (user == nullptr) { - return Status::Error(5, "User not found"); - } - - td_->create_handler()->send(std::move(user)); - - on_update_user_blocked(user_id, false); - return Status::OK(); +void ContactsManager::on_set_user_is_blocked_failed(UserId user_id, bool is_blocked, Status error) { + 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"); } bool ContactsManager::is_valid_username(const string &username) { @@ -3596,7 +4345,7 @@ int32 ContactsManager::get_contacts_hash() { CHECK(std::is_sorted(user_ids.begin(), user_ids.end())); auto my_id = get_my_id(); const User *u = get_user_force(my_id); - if (u != nullptr && u->outbound == LinkState::Contact) { + if (u != nullptr && u->is_contact) { user_ids.insert(std::upper_bound(user_ids.begin(), user_ids.end(), my_id.get()), my_id.get()); } @@ -3617,6 +4366,37 @@ void ContactsManager::reload_contacts(bool force) { } } +void ContactsManager::add_contact(td_api::object_ptr &&contact, bool share_phone_number, + Promise &&promise) { + if (contact == nullptr) { + return promise.set_error(Status::Error(400, "Added contact must be non-empty")); + } + + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + if (!are_contacts_loaded_) { + load_contacts(PromiseCreator::lambda([actor_id = actor_id(this), contact = std::move(contact), share_phone_number, + promise = std::move(promise)](Result &&) mutable { + send_closure(actor_id, &ContactsManager::add_contact, std::move(contact), share_phone_number, std::move(promise)); + })); + return; + } + + LOG(INFO) << "Add " << oneline(to_string(contact)) << " with share_phone_number = " << share_phone_number; + + UserId user_id{contact->user_id_}; + auto input_user = get_input_user(user_id); + if (input_user == nullptr) { + return promise.set_error(Status::Error(3, "User not found")); + } + + td_->create_handler(std::move(promise)) + ->send(user_id, std::move(input_user), contact->first_name_, contact->last_name_, contact->phone_number_, + share_phone_number); +} + std::pair, vector> ContactsManager::import_contacts( const vector> &contacts, int64 &random_id, Promise &&promise) { if (!are_contacts_loaded_) { @@ -3637,7 +4417,7 @@ std::pair, vector> ContactsManager::import_contacts( } for (auto &contact : contacts) { if (contact == nullptr) { - promise.set_error(Status::Error(400, "Imported contacts should not be empty")); + promise.set_error(Status::Error(400, "Imported contacts must be non-empty")); return {}; } } @@ -3667,7 +4447,7 @@ void ContactsManager::remove_contacts(vector user_ids, Promise &&p vector> input_users; for (auto &user_id : user_ids) { const User *u = get_user(user_id); - if (u != nullptr && u->outbound == LinkState::Contact) { + if (u != nullptr && u->is_contact) { auto input_user = get_input_user(user_id); if (input_user != nullptr) { to_delete_user_ids.push_back(user_id); @@ -3680,8 +4460,7 @@ void ContactsManager::remove_contacts(vector user_ids, Promise &&p return promise.set_value(Unit()); } - td_->create_handler(std::move(promise)) - ->send(std::move(to_delete_user_ids), std::move(input_users)); + td_->create_handler(std::move(promise))->send(std::move(input_users)); } void ContactsManager::remove_contacts_by_phone_number(vector user_phone_numbers, vector user_ids, @@ -3938,15 +4717,14 @@ void ContactsManager::on_update_contacts_reset() { for (auto &p : users_) { UserId user_id = p.first; User u = &p.second; - bool is_contact = u->outbound == LinkState::Contact; - if (is_contact) { + if (u->is_contact) { LOG(INFO) << "Drop contact with " << user_id; if (user_id != my_id) { CHECK(contacts_hints_.has_key(user_id.get())); } - on_update_user_links(u, user_id, LinkState::KnowsPhoneNumber, u->inbound); + on_update_user_is_contact(u, user_id, false, false); update_user(u, user_id); - CHECK(u->outbound != LinkState::Contact); + CHECK(!u->is_contact); if (user_id != my_id) { CHECK(!contacts_hints_.has_key(user_id.get())); } @@ -4011,6 +4789,170 @@ std::pair> ContactsManager::search_contacts(const string & return {narrow_cast(result.first), std::move(user_ids)}; } +void ContactsManager::share_phone_number(UserId user_id, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + if (!are_contacts_loaded_) { + load_contacts(PromiseCreator::lambda( + [actor_id = actor_id(this), user_id, promise = std::move(promise)](Result &&) mutable { + send_closure(actor_id, &ContactsManager::share_phone_number, user_id, std::move(promise)); + })); + return; + } + + LOG(INFO) << "Share phone number with " << user_id; + auto input_user = get_input_user(user_id); + if (input_user == nullptr) { + return promise.set_error(Status::Error(3, "User not found")); + } + + td_->messages_manager_->hide_dialog_action_bar(DialogId(user_id)); + + td_->create_handler(std::move(promise))->send(user_id, std::move(input_user)); +} + +void ContactsManager::search_dialogs_nearby(const Location &location, + Promise> &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid location specified")); + } + + 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); +} + +vector> ContactsManager::get_chats_nearby_object( + const vector &dialogs_nearby) { + return transform(dialogs_nearby, [](const DialogNearby &dialog_nearby) { + return td_api::make_object(dialog_nearby.dialog_id.get(), dialog_nearby.distance); + }); +} + +void ContactsManager::send_update_users_nearby() const { + send_closure(G()->td(), &Td::send_update, + td_api::make_object(get_chats_nearby_object(users_nearby_))); +} + +void ContactsManager::on_get_dialogs_nearby(Result> result, + Promise> &&promise) { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + + auto updates_ptr = result.move_as_ok(); + if (updates_ptr->get_id() != telegram_api::updates::ID) { + LOG(ERROR) << "Receive " << oneline(to_string(*updates_ptr)) << " instead of updates"; + return promise.set_error(Status::Error(500, "Receive unsupported response from the server")); + } + + auto update = telegram_api::move_object_as(updates_ptr); + LOG(INFO) << "Receive chats nearby in " << to_string(update); + + on_get_users(std::move(update->users_), "on_get_dialogs_nearby"); + on_get_chats(std::move(update->chats_), "on_get_dialogs_nearby"); + + for (auto &dialog_nearby : users_nearby_) { + user_nearby_timeout_.cancel_timeout(dialog_nearby.dialog_id.get_user_id().get()); + } + auto old_users_nearby = std::move(users_nearby_); + users_nearby_.clear(); + channels_nearby_.clear(); + for (auto &update_ptr : update->updates_) { + 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); + } + + std::sort(users_nearby_.begin(), users_nearby_.end()); + if (old_users_nearby != users_nearby_) { + send_update_users_nearby(); // for other clients connected to the same TDLib instance + } + promise.set_value(td_api::make_object(get_chats_nearby_object(users_nearby_), + get_chats_nearby_object(channels_nearby_))); +} + +void ContactsManager::on_update_peer_located(vector> &&peers, + bool from_update) { + auto now = G()->unix_time(); + bool need_update = false; + for (auto &peer_located : peers) { + DialogId dialog_id(peer_located->peer_); + int32 expires_at = peer_located->expires_; + int32 distance = peer_located->distance_; + if (distance < 0 || distance > 50000000) { + LOG(ERROR) << "Receive wrong distance to " << to_string(peer_located); + continue; + } + if (expires_at <= now) { + LOG(INFO) << "Skip expired result " << to_string(peer_located); + continue; + } + + auto dialog_type = dialog_id.get_type(); + if (dialog_type == DialogType::User) { + auto user_id = dialog_id.get_user_id(); + if (!have_user(user_id)) { + LOG(ERROR) << "Can't find " << user_id; + continue; + } + if (expires_at < now + 86400) { + user_nearby_timeout_.set_timeout_in(user_id.get(), expires_at - now + 1); + } + } else if (dialog_type == DialogType::Channel) { + auto channel_id = dialog_id.get_channel_id(); + if (!have_channel(channel_id)) { + LOG(ERROR) << "Can't find " << channel_id; + continue; + } + if (expires_at != std::numeric_limits::max()) { + LOG(ERROR) << "Receive expiring at " << expires_at << " group location in " << to_string(peer_located); + } + if (from_update) { + LOG(ERROR) << "Receive nearby " << channel_id << " from update"; + continue; + } + } else { + LOG(ERROR) << "Receive chat of wrong type in " << to_string(peer_located); + continue; + } + + td_->messages_manager_->force_create_dialog(dialog_id, "on_update_peer_located"); + + if (from_update) { + bool is_found = false; + for (auto &dialog_nearby : users_nearby_) { + if (dialog_nearby.dialog_id == dialog_id) { + if (dialog_nearby.distance != distance) { + dialog_nearby.distance = distance; + need_update = true; + } + is_found = true; + break; + } + } + if (!is_found) { + users_nearby_.emplace_back(dialog_id, distance); + need_update = true; + } + } else { + auto &dialogs_nearby = dialog_type == DialogType::User ? users_nearby_ : channels_nearby_; + dialogs_nearby.emplace_back(dialog_id, distance); + } + } + if (need_update) { + std::sort(users_nearby_.begin(), users_nearby_.end()); + send_update_users_nearby(); + } +} + void ContactsManager::set_profile_photo(const tl_object_ptr &input_photo, Promise &&promise) { auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Photo, input_photo, DialogId(get_my_id()), false, false); @@ -4023,9 +4965,9 @@ void ContactsManager::set_profile_photo(const tl_object_ptr & FileView file_view = td_->file_manager_->get_file_view(file_id); CHECK(!file_view.is_encrypted()); - if (file_view.has_remote_location() && !file_view.remote_location().is_web()) { + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web()) { td_->create_handler(std::move(promise)) - ->send(td_->file_manager_->dup_file_id(file_id), file_view.remote_location().as_input_photo()); + ->send(td_->file_manager_->dup_file_id(file_id), file_view.main_remote_location().as_input_photo()); return; } @@ -4113,8 +5055,8 @@ void ContactsManager::on_update_profile_success(int32 flags, const string &first << "Wrong last name \"" << u->last_name << "\", expected \"" << last_name << '"'; if ((flags & ACCOUNT_UPDATE_ABOUT) != 0) { - UserFull *user_full = get_user_full(my_user_id); - if (user_full != nullptr && user_full->is_inited) { + UserFull *user_full = get_user_full_force(my_user_id); + if (user_full != nullptr) { user_full->about = about; user_full->is_changed = true; update_user_full(user_full, my_user_id); @@ -4156,7 +5098,7 @@ void ContactsManager::set_channel_username(ChannelId channel_id, const string &u } if (!username.empty() && c->username.empty()) { - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full(channel_id, "set_channel_username"); if (channel_full != nullptr && !channel_full->can_set_username) { return promise.set_error(Status::Error(3, "Can't set supergroup username")); } @@ -4165,7 +5107,8 @@ void ContactsManager::set_channel_username(ChannelId channel_id, const string &u td_->create_handler(std::move(promise))->send(channel_id, username); } -void ContactsManager::set_channel_sticker_set(ChannelId channel_id, int64 sticker_set_id, Promise &&promise) { +void ContactsManager::set_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id, + Promise &&promise) { auto c = get_channel(channel_id); if (c == nullptr) { return promise.set_error(Status::Error(6, "Supergroup not found")); @@ -4178,7 +5121,7 @@ void ContactsManager::set_channel_sticker_set(ChannelId channel_id, int64 sticke } telegram_api::object_ptr input_sticker_set; - if (sticker_set_id == 0) { + if (!sticker_set_id.is_valid()) { input_sticker_set = telegram_api::make_object(); } else { input_sticker_set = td_->stickers_manager_->get_input_sticker_set(sticker_set_id); @@ -4187,7 +5130,7 @@ void ContactsManager::set_channel_sticker_set(ChannelId channel_id, int64 sticke } } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full(channel_id, "set_channel_sticker_set"); if (channel_full != nullptr && !channel_full->can_set_sticker_set) { return promise.set_error(Status::Error(3, "Can't set supergroup sticker set")); } @@ -4221,8 +5164,12 @@ void ContactsManager::toggle_channel_is_all_history_available(ChannelId channel_ return promise.set_error(Status::Error(6, "Not enough rights to toggle all supergroup history availability")); } if (get_channel_type(c) != ChannelType::Megagroup) { - return promise.set_error(Status::Error(6, "Message history can be hidden in the supergroups only")); + return promise.set_error(Status::Error(6, "Message history can be hidden in supergroups only")); } + if (c->has_linked_channel && !is_all_history_available) { + return promise.set_error(Status::Error(6, "Message history can't be hidden in discussion supergroups")); + } + // it can be toggled in public chats, but will not affect them td_->create_handler(std::move(promise)) ->send(channel_id, is_all_history_available); @@ -4242,6 +5189,140 @@ void ContactsManager::set_channel_description(ChannelId channel_id, const string td_->create_handler(std::move(promise))->send(DialogId(channel_id), new_description); } +void ContactsManager::set_channel_discussion_group(DialogId dialog_id, DialogId discussion_dialog_id, + Promise &&promise) { + if (!dialog_id.is_valid() && !discussion_dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat identifiers specified")); + } + + ChannelId broadcast_channel_id; + telegram_api::object_ptr broadcast_input_channel; + if (dialog_id.is_valid()) { + 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")); + } + + broadcast_channel_id = dialog_id.get_channel_id(); + const Channel *c = get_channel(broadcast_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")); + } + if (!c->status.is_administrator() || !c->status.can_change_info_and_settings()) { + return promise.set_error(Status::Error(400, "Not enough rights in the channel")); + } + + broadcast_input_channel = td_->contacts_manager_->get_input_channel(broadcast_channel_id); + CHECK(broadcast_input_channel != nullptr); + } else { + broadcast_input_channel = telegram_api::make_object(); + } + + ChannelId group_channel_id; + telegram_api::object_ptr group_input_channel; + if (discussion_dialog_id.is_valid()) { + if (!td_->messages_manager_->have_dialog_force(discussion_dialog_id)) { + return promise.set_error(Status::Error(400, "Discussion chat not found")); + } + if (discussion_dialog_id.get_type() != DialogType::Channel) { + return promise.set_error(Status::Error(400, "Discussion chat is not a supergroup")); + } + + group_channel_id = discussion_dialog_id.get_channel_id(); + const Channel *c = get_channel(group_channel_id); + if (c == nullptr) { + return promise.set_error(Status::Error(400, "Discussion chat info not found")); + } + + if (!c->is_megagroup) { + return promise.set_error(Status::Error(400, "Discussion chat is not a supergroup")); + } + if (!c->status.is_administrator() || !c->status.can_pin_messages()) { + return promise.set_error(Status::Error(400, "Not enough rights in the supergroup")); + } + + group_input_channel = td_->contacts_manager_->get_input_channel(group_channel_id); + CHECK(group_input_channel != nullptr); + } else { + group_input_channel = telegram_api::make_object(); + } + + td_->create_handler(std::move(promise)) + ->send(broadcast_channel_id, std::move(broadcast_input_channel), group_channel_id, + std::move(group_input_channel)); +} + +void ContactsManager::set_channel_location(DialogId dialog_id, const DialogLocation &location, + Promise &&promise) { + if (location.empty()) { + return promise.set_error(Status::Error(400, "Invalid chat location specified")); + } + + if (!dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat 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 supergroup")); + } + + 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 supergroup")); + } + if (!c->status.is_creator()) { + return promise.set_error(Status::Error(400, "Not enough rights in the supergroup")); + } + + td_->create_handler(std::move(promise))->send(channel_id, location); +} + +void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow_mode_delay, Promise &&promise) { + std::vector allowed_slow_mode_delays{0, 10, 30, 60, 300, 900, 3600}; + if (!td::contains(allowed_slow_mode_delays, slow_mode_delay)) { + return promise.set_error(Status::Error(400, "Invalid new value for slow mode delay")); + } + + if (!dialog_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid chat 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 supergroup")); + } + + 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 supergroup")); + } + if (!get_channel_permissions(c).can_restrict_members()) { + return promise.set_error(Status::Error(400, "Not enough rights in the supergroup")); + } + + td_->create_handler(std::move(promise))->send(channel_id, slow_mode_delay); +} + void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, Promise &&promise) { auto c = get_channel(channel_id); @@ -4265,6 +5346,10 @@ void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_id, vector server_message_ids; for (auto &message_id : message_ids) { + if (message_id.is_valid_scheduled()) { + return promise.set_error(Status::Error(6, "Can't report scheduled messages")); + } + if (!message_id.is_valid()) { return promise.set_error(Status::Error(6, "Message not found")); } @@ -4431,22 +5516,7 @@ void ContactsManager::change_channel_participant_status_impl(ChannelId channel_i DialogParticipantStatus status, DialogParticipantStatus old_status, Promise &&promise) { - if (td_->auth_manager_->is_bot()) { - if (status.is_restricted() && status.is_member() && !old_status.is_member()) { - // allow bots to restrict left chat members without trying to add them - status.set_is_member(false); - } - - auto new_until_date = status.get_until_date(); - if (new_until_date >= 1840000000 && status.is_restricted()) { - status = DialogParticipantStatus::Restricted( - status.is_member(), new_until_date - 300000000, status.can_send_messages(), status.can_send_media(), - status.can_send_stickers(), status.can_send_animations(), status.can_send_games(), - status.can_use_inline_bots(), status.can_add_web_page_previews(), old_status.can_send_polls(), - old_status.can_change_info_and_settings(), old_status.can_invite_users(), old_status.can_pin_messages()); - } - } - if (old_status == status) { + if (old_status == status && !old_status.is_creator()) { return promise.set_value(Unit()); } @@ -4456,23 +5526,33 @@ void ContactsManager::change_channel_participant_status_impl(ChannelId channel_i bool need_restrict = false; if (status.is_creator() || old_status.is_creator()) { if (!old_status.is_creator()) { - return promise.set_error(Status::Error(3, "Can't add creator to the chat")); + return promise.set_error(Status::Error(3, "Can't add another owner to the chat")); + } + if (!status.is_creator()) { + return promise.set_error(Status::Error(3, "Can't remove chat owner")); + } + if (status.is_member() == old_status.is_member()) { + // change rank + if (user_id != get_my_id()) { + return promise.set_error(Status::Error(3, "Not enough rights to change chat owner custom title")); + } + + auto input_user = get_input_user(user_id); + if (input_user == nullptr) { + return promise.set_error(Status::Error(3, "User not found")); + } + + td_->create_handler(std::move(promise))->send(channel_id, std::move(input_user), status); + return; + } + if (user_id != get_my_id()) { + return promise.set_error(Status::Error(3, "Not enough rights to edit chat owner membership")); } if (status.is_member()) { - // creator member -> not creator member // creator not member -> creator member - // creator not member -> not creator member - if (old_status.is_member()) { - return promise.set_error(Status::Error(3, "Can't demote chat creator")); - } need_add = true; } else { // creator member -> creator not member - // creator member -> not creator not member - // creator not member -> not creator not member - if (!old_status.is_member()) { - return promise.set_error(Status::Error(3, "Can't restrict chat creator")); - } need_restrict = true; } } else if (status.is_administrator()) { @@ -4532,6 +5612,9 @@ void ContactsManager::promote_channel_participant(ChannelId channel_id, UserId u if (!get_channel_permissions(c).can_promote_members()) { return promise.set_error(Status::Error(3, "Not enough rights")); } + + CHECK(!old_status.is_creator()); + CHECK(!status.is_creator()); } auto input_user = get_input_user(user_id); @@ -4555,7 +5638,7 @@ void ContactsManager::change_chat_participant_status(ChatId chat_id, UserId user } if (!get_chat_permissions(c).can_promote_members()) { - return promise.set_error(Status::Error(3, "Need creator rights in the group chat")); + return promise.set_error(Status::Error(3, "Need owner rights in the group chat")); } if (user_id == get_my_id()) { @@ -4578,6 +5661,111 @@ void ContactsManager::change_chat_participant_status(ChatId chat_id, UserId user ->send(chat_id, std::move(input_user), status.is_administrator()); } +void ContactsManager::can_transfer_ownership(Promise &&promise) { + auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_result) mutable { + CHECK(r_result.is_error()); + + auto error = r_result.move_as_error(); + CanTransferOwnershipResult result; + if (error.message() == "PASSWORD_HASH_INVALID") { + return promise.set_value(std::move(result)); + } + if (error.message() == "PASSWORD_MISSING") { + result.type = CanTransferOwnershipResult::Type::PasswordNeeded; + return promise.set_value(std::move(result)); + } + if (begins_with(error.message(), "PASSWORD_TOO_FRESH_")) { + result.type = CanTransferOwnershipResult::Type::PasswordTooFresh; + result.retry_after = to_integer(error.message().substr(Slice("PASSWORD_TOO_FRESH_").size())); + if (result.retry_after < 0) { + result.retry_after = 0; + } + return promise.set_value(std::move(result)); + } + if (begins_with(error.message(), "SESSION_TOO_FRESH_")) { + result.type = CanTransferOwnershipResult::Type::SessionTooFresh; + result.retry_after = to_integer(error.message().substr(Slice("SESSION_TOO_FRESH_").size())); + if (result.retry_after < 0) { + result.retry_after = 0; + } + return promise.set_value(std::move(result)); + } + promise.set_error(std::move(error)); + }); + + td_->create_handler(std::move(request_promise))->send(); +} + +td_api::object_ptr ContactsManager::get_can_transfer_ownership_result_object( + CanTransferOwnershipResult result) { + switch (result.type) { + case CanTransferOwnershipResult::Type::Ok: + return td_api::make_object(); + case CanTransferOwnershipResult::Type::PasswordNeeded: + return td_api::make_object(); + case CanTransferOwnershipResult::Type::PasswordTooFresh: + return td_api::make_object(result.retry_after); + case CanTransferOwnershipResult::Type::SessionTooFresh: + return td_api::make_object(result.retry_after); + default: + UNREACHABLE(); + return nullptr; + } +} + +void ContactsManager::transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, + Promise &&promise) { + if (!td_->messages_manager_->have_dialog_force(dialog_id)) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + if (!have_user_force(user_id)) { + return promise.set_error(Status::Error(3, "User not found")); + } + if (is_user_bot(user_id)) { + return promise.set_error(Status::Error(3, "User is a bot")); + } + if (is_user_deleted(user_id)) { + return promise.set_error(Status::Error(3, "User is deleted")); + } + if (password.empty()) { + return promise.set_error(Status::Error(400, "PASSWORD_HASH_INVALID")); + } + + switch (dialog_id.get_type()) { + case DialogType::User: + case DialogType::Chat: + case DialogType::SecretChat: + return promise.set_error(Status::Error(3, "Can't transfer chat ownership")); + case DialogType::Channel: + send_closure( + td_->password_manager_, &PasswordManager::get_input_check_password_srp, password, + PromiseCreator::lambda([actor_id = actor_id(this), channel_id = dialog_id.get_channel_id(), user_id, + promise = std::move(promise)]( + Result> result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + send_closure(actor_id, &ContactsManager::transfer_channel_ownership, channel_id, user_id, + result.move_as_ok(), std::move(promise)); + })); + break; + case DialogType::None: + default: + UNREACHABLE(); + } +} + +void ContactsManager::transfer_channel_ownership( + ChannelId channel_id, UserId user_id, tl_object_ptr input_check_password, + Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + td_->create_handler(std::move(promise)) + ->send(channel_id, user_id, std::move(input_check_password)); +} + void ContactsManager::export_chat_invite_link(ChatId chat_id, Promise &&promise) { const Chat *c = get_chat(chat_id); if (c == nullptr) { @@ -4639,7 +5827,7 @@ string ContactsManager::get_chat_invite_link(ChatId chat_id) const { string ContactsManager::get_channel_invite_link( ChannelId channel_id) { // should be non-const to update ChannelFull cache - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full(channel_id, "get_channel_invite_link"); if (channel_full == nullptr) { auto it = channel_invite_links_.find(channel_id); return it == channel_invite_links_.end() ? string() : it->second; @@ -4734,9 +5922,8 @@ void ContactsManager::restrict_channel_participant(ChannelId channel_id, UserId return; } - if (status.is_creator()) { - return promise.set_error(Status::Error(3, "Not enough rights to restrict chat creator")); - } + CHECK(!old_status.is_creator()); + CHECK(!status.is_creator()); if (!get_channel_permissions(c).can_restrict_members()) { return promise.set_error(Status::Error(3, "Not enough rights to restrict/unrestrict chat member")); @@ -4791,59 +5978,124 @@ ChannelId ContactsManager::migrate_chat_to_megagroup(ChatId chat_id, Promise ContactsManager::get_created_public_dialogs(Promise &&promise) { - if (created_public_channels_inited_) { +vector ContactsManager::get_channel_ids(vector> &&chats, + const char *source) { + vector channel_ids; + for (auto &chat : chats) { + auto channel_id = get_channel_id(chat); + if (!channel_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << channel_id << " from " << source << " in " << to_string(chat); + } else { + channel_ids.push_back(channel_id); + } + on_get_chat(std::move(chat), source); + } + return channel_ids; +} + +vector ContactsManager::get_dialog_ids(vector> &&chats, + const char *source) { + vector dialog_ids; + for (auto &chat : chats) { + auto channel_id = get_channel_id(chat); + if (!channel_id.is_valid()) { + auto chat_id = get_chat_id(chat); + if (!chat_id.is_valid()) { + LOG(ERROR) << "Receive invalid chat from " << source << " in " << to_string(chat); + } else { + dialog_ids.push_back(DialogId(chat_id)); + } + } else { + dialog_ids.push_back(DialogId(channel_id)); + } + on_get_chat(std::move(chat), source); + } + return dialog_ids; +} + +vector ContactsManager::get_created_public_dialogs(PublicDialogType type, Promise &&promise) { + int32 index = static_cast(type); + if (created_public_channels_inited_[index]) { promise.set_value(Unit()); - return transform(created_public_channels_, [&](ChannelId channel_id) { + return transform(created_public_channels_[index], [&](ChannelId channel_id) { DialogId dialog_id(channel_id); td_->messages_manager_->force_create_dialog(dialog_id, "get_created_public_dialogs"); return dialog_id; }); } - td_->create_handler(std::move(promise))->send(); + td_->create_handler(std::move(promise))->send(type, false); return {}; } -void ContactsManager::on_get_created_public_channels(vector> &&chats) { - created_public_channels_inited_ = true; - created_public_channels_.clear(); +void ContactsManager::on_get_created_public_channels(PublicDialogType type, + vector> &&chats) { + int32 index = static_cast(type); + created_public_channels_[index] = get_channel_ids(std::move(chats), "on_get_created_public_channels"); + created_public_channels_inited_[index] = true; +} - for (auto &chat : chats) { - switch (chat->get_id()) { - case telegram_api::chatEmpty::ID: - LOG(ERROR) << "Receive chatEmpty as created public channel"; - break; - case telegram_api::chat::ID: - LOG(ERROR) << "Receive chat as created public channel"; - break; - case telegram_api::chatForbidden::ID: - LOG(ERROR) << "Receive chatForbidden as created public channel"; - break; - case telegram_api::channel::ID: { - auto c = static_cast(chat.get()); - ChannelId channel_id(c->id_); - if (!channel_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << channel_id; - continue; - } - created_public_channels_.push_back(channel_id); - break; - } - case telegram_api::channelForbidden::ID: { - auto c = static_cast(chat.get()); - ChannelId channel_id(c->id_); - if (!channel_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << channel_id; - continue; - } - created_public_channels_.push_back(channel_id); - break; - } - default: - UNREACHABLE(); +void ContactsManager::check_created_public_dialogs_limit(PublicDialogType type, Promise &&promise) { + td_->create_handler(std::move(promise))->send(type, true); +} + +vector ContactsManager::get_dialogs_for_discussion(Promise &&promise) { + if (dialogs_for_discussion_inited_) { + promise.set_value(Unit()); + return transform(dialogs_for_discussion_, [&](DialogId dialog_id) { + td_->messages_manager_->force_create_dialog(dialog_id, "get_dialogs_for_discussion"); + return dialog_id; + }); + } + + td_->create_handler(std::move(promise))->send(); + return {}; +} + +void ContactsManager::on_get_dialogs_for_discussion(vector> &&chats) { + dialogs_for_discussion_inited_ = true; + dialogs_for_discussion_ = get_dialog_ids(std::move(chats), "on_get_dialogs_for_discussion"); +} + +void ContactsManager::update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable) { + if (!dialogs_for_discussion_inited_) { + return; + } + + if (is_suitable) { + if (!td::contains(dialogs_for_discussion_, dialog_id)) { + LOG(DEBUG) << "Add " << dialog_id << " to list of suitable discussion chats"; + dialogs_for_discussion_.insert(dialogs_for_discussion_.begin(), dialog_id); } - on_get_chat(std::move(chat), "on_get_created_public_channels"); + } else { + if (td::remove(dialogs_for_discussion_, dialog_id)) { + LOG(DEBUG) << "Remove " << dialog_id << " from list of suitable discussion chats"; + } + } +} + +vector ContactsManager::get_inactive_channels(Promise &&promise) { + if (inactive_channels_inited_) { + promise.set_value(Unit()); + return transform(inactive_channels_, [&](ChannelId channel_id) { + DialogId dialog_id{channel_id}; + td_->messages_manager_->force_create_dialog(dialog_id, "get_inactive_channels"); + return dialog_id; + }); + } + + td_->create_handler(std::move(promise))->send(); + return {}; +} + +void ContactsManager::on_get_inactive_channels(vector> &&chats) { + inactive_channels_inited_ = true; + inactive_channels_ = get_channel_ids(std::move(chats), "on_get_inactive_channels"); +} + +void ContactsManager::remove_inactive_channel(ChannelId channel_id) { + if (inactive_channels_inited_ && td::remove(inactive_channels_, channel_id)) { + LOG(DEBUG) << "Remove " << channel_id << " from list of inactive channels"; } } @@ -4913,9 +6165,9 @@ void ContactsManager::on_deleted_contacts(const vector &deleted_contact_ LOG(INFO) << "Drop contact with " << user_id; auto u = get_user(user_id); CHECK(u != nullptr); - on_update_user_links(u, user_id, LinkState::KnowsPhoneNumber, u->inbound); + on_update_user_is_contact(u, user_id, false, false); update_user(u, user_id); - CHECK(u->outbound != LinkState::Contact); + CHECK(!u->is_contact); CHECK(!contacts_hints_.has_key(user_id.get())); } } @@ -4955,19 +6207,18 @@ void ContactsManager::on_get_contacts(tl_object_ptroutbound == LinkState::Contact; + User *u = p.second.get(); bool should_be_contact = contact_user_ids.count(user_id) == 1; - if (is_contact != should_be_contact) { - if (is_contact) { + if (u->is_contact != should_be_contact) { + if (u->is_contact) { LOG(INFO) << "Drop contact with " << user_id; if (user_id != my_id) { LOG_CHECK(contacts_hints_.has_key(user_id.get())) << my_id << " " << user_id << " " << to_string(get_user_object(user_id, u)); } - on_update_user_links(u, user_id, LinkState::KnowsPhoneNumber, u->inbound); + on_update_user_is_contact(u, user_id, false, false); update_user(u, user_id); - CHECK(u->outbound != LinkState::Contact); + CHECK(!u->is_contact); if (user_id != my_id) { CHECK(!contacts_hints_.has_key(user_id.get())); } @@ -5069,12 +6320,6 @@ void ContactsManager::on_update_online_status_privacy() { td_->create_handler()->send(); } -void ContactsManager::on_get_contacts_link(tl_object_ptr &&link) { - UserId user_id = get_user_id(link->user_); - on_get_user(std::move(link->user_), "on_get_contacts_link"); - on_update_user_links(user_id, std::move(link->my_link_), std::move(link->foreign_link_)); -} - UserId ContactsManager::get_user_id(const tl_object_ptr &user) { CHECK(user != nullptr); switch (user->get_id()) { @@ -5175,12 +6420,12 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, if (have_access_hash) { // access_hash must be updated before photo auto access_hash = user->access_hash_; bool is_min_access_hash = !is_received && ((flags & USER_FLAG_HAS_PHONE_NUMBER) == 0); - if (u->access_hash != access_hash && (!is_min_access_hash || u->is_min_access_hash)) { + if (u->access_hash != access_hash && (!is_min_access_hash || u->is_min_access_hash || u->access_hash == -1)) { LOG(DEBUG) << "Access hash has changed for " << user_id << " from " << u->access_hash << "/" << u->is_min_access_hash << " to " << access_hash << "/" << is_min_access_hash; u->access_hash = access_hash; u->is_min_access_hash = is_min_access_hash; - u->is_changed = true; + u->need_save_to_database = true; } } if (is_received) { @@ -5190,21 +6435,9 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, if (is_received) { on_update_user_online(u, user_id, std::move(user->status_)); - LinkState out, in; - if (flags & USER_FLAG_IS_MUTUAL_CONTACT) { - out = LinkState::Contact; - in = LinkState::Contact; - } else if (flags & USER_FLAG_IS_CONTACT) { - out = LinkState::Contact; - in = LinkState::Unknown; - } else if (flags & USER_FLAG_HAS_PHONE_NUMBER) { - out = LinkState::KnowsPhoneNumber; - in = LinkState::Unknown; - } else { - out = LinkState::None; - in = LinkState::Unknown; - } - on_update_user_links(u, user_id, out, in); + auto is_contact = (flags & USER_FLAG_IS_CONTACT) != 0; + auto is_mutual_contact = (flags & USER_FLAG_IS_MUTUAL_CONTACT) != 0; + on_update_user_is_contact(u, user_id, is_contact, is_mutual_contact); } if (is_received || !u->is_received) { @@ -5217,7 +6450,7 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, bool is_deleted = (flags & USER_FLAG_IS_DELETED) != 0; bool can_join_groups = (flags & USER_FLAG_IS_PRIVATE_BOT) == 0; bool can_read_all_group_messages = (flags & USER_FLAG_IS_BOT_WITH_PRIVACY_DISABLED) != 0; - string restriction_reason = std::move(user->restriction_reason_); + auto restriction_reasons = get_restriction_reasons(std::move(user->restriction_reason_)); bool is_scam = (flags & USER_FLAG_IS_SCAM) != 0; bool is_inline_bot = (flags & USER_FLAG_IS_INLINE_BOT) != 0; string inline_query_placeholder = user->bot_inline_placeholder_; @@ -5252,7 +6485,7 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, int32 bot_info_version = has_bot_info_version ? user->bot_info_version_ : -1; if (is_verified != u->is_verified || is_support != u->is_support || is_bot != u->is_bot || can_join_groups != u->can_join_groups || can_read_all_group_messages != u->can_read_all_group_messages || - restriction_reason != u->restriction_reason || is_scam != u->is_scam || is_inline_bot != u->is_inline_bot || + restriction_reasons != u->restriction_reasons || is_scam != u->is_scam || is_inline_bot != u->is_inline_bot || inline_query_placeholder != u->inline_query_placeholder || need_location_bot != u->need_location_bot) { LOG_IF(ERROR, is_bot != u->is_bot && !is_deleted && !u->is_deleted && u->is_received) << "User.is_bot has changed for " << user_id << "/" << u->username << " from " << source << " from " @@ -5262,38 +6495,35 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, u->is_bot = is_bot; u->can_join_groups = can_join_groups; u->can_read_all_group_messages = can_read_all_group_messages; - u->restriction_reason = std::move(restriction_reason); + u->restriction_reasons = std::move(restriction_reasons); u->is_scam = is_scam; u->is_inline_bot = is_inline_bot; u->inline_query_placeholder = std::move(inline_query_placeholder); u->need_location_bot = need_location_bot; LOG(DEBUG) << "Info has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } if (u->bot_info_version != bot_info_version) { u->bot_info_version = bot_info_version; LOG(DEBUG) << "Bot info version has changed for " << user_id; - u->is_changed = true; + u->need_save_to_database = true; } if (is_received && !u->is_received) { u->is_received = true; LOG(DEBUG) << "Receive " << user_id; - u->need_send_update = true; + u->is_changed = true; } if (is_deleted != u->is_deleted) { u->is_deleted = is_deleted; - if (u->is_deleted) { - invalidate_user_full(user_id); - } - LOG(DEBUG) << "User.is_deleted has changed for " << user_id; - u->need_send_update = true; + u->is_is_deleted_changed = true; + u->is_changed = true; } bool has_language_code = (flags & USER_FLAG_HAS_LANGUAGE_CODE) != 0; @@ -5303,12 +6533,12 @@ void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, u->language_code = user->lang_code_; LOG(DEBUG) << "Language code has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } if (u->cache_version != User::CACHE_VERSION && u->is_received) { u->cache_version = User::CACHE_VERSION; - u->is_changed = true; + u->need_save_to_database = true; } update_user(u, user_id); } @@ -5366,9 +6596,14 @@ void ContactsManager::on_binlog_user_event(BinlogEvent &&event) { log_event_parse(log_event, event.data_).ensure(); auto user_id = log_event.user_id; + if (have_user(user_id)) { + LOG(ERROR) << "Skip adding already added " << user_id; + binlog_erase(G()->td_db()->get_binlog(), event.id_); + return; + } + LOG(INFO) << "Add " << user_id << " from binlog"; User *u = add_user(user_id, "on_binlog_user_event"); - LOG_CHECK(u->first_name.empty() && u->last_name.empty()) << user_id; *u = std::move(log_event.u); // users come from binlog before all other events, so just add them u->logevent_id = event.id_; @@ -5419,10 +6654,10 @@ void ContactsManager::on_save_user_to_database(UserId user_id, bool success) { CHECK(u != nullptr); LOG_CHECK(u->is_being_saved) << user_id << " " << u->is_saved << " " << u->is_status_saved << " " << load_user_from_database_queries_.count(user_id) << " " << u->is_received << " " - << u->is_deleted << " " << u->is_bot << " " << u->is_changed << " " - << u->need_send_update << " " << u->is_status_changed << " " << u->is_name_changed << " " + << u->is_deleted << " " << u->is_bot << " " << u->need_save_to_database << " " + << u->is_changed << " " << u->is_status_changed << " " << u->is_name_changed << " " << u->is_username_changed << " " << u->is_photo_changed << " " - << u->is_outbound_link_changed; + << u->is_is_contact_changed << " " << u->is_is_deleted_changed; CHECK(load_user_from_database_queries_.count(user_id) == 0); u->is_being_saved = false; @@ -5546,7 +6781,7 @@ ContactsManager::User *ContactsManager::get_user_force(UserId user_id) { flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, 777000, 1, "Telegram", string(), string(), "42777", - std::move(profile_photo), nullptr, 0, string(), string(), string()); + std::move(profile_photo), nullptr, 0, Auto(), string(), string()); on_get_user(std::move(user), "get_user_force"); u = get_user(user_id); CHECK(u != nullptr && u->is_received); @@ -5629,9 +6864,14 @@ void ContactsManager::on_binlog_chat_event(BinlogEvent &&event) { log_event_parse(log_event, event.data_).ensure(); auto chat_id = log_event.chat_id; + if (have_chat(chat_id)) { + LOG(ERROR) << "Skip adding already added " << chat_id; + binlog_erase(G()->td_db()->get_binlog(), event.id_); + return; + } + LOG(INFO) << "Add " << chat_id << " from binlog"; Chat *c = add_chat(chat_id); - CHECK(c->status.is_banned()); *c = std::move(log_event.c); // chats come from binlog before all other events, so just add them c->logevent_id = event.id_; @@ -5852,9 +7092,14 @@ void ContactsManager::on_binlog_channel_event(BinlogEvent &&event) { log_event_parse(log_event, event.data_).ensure(); auto channel_id = log_event.channel_id; + if (have_channel(channel_id)) { + LOG(ERROR) << "Skip adding already added " << channel_id; + binlog_erase(G()->td_db()->get_binlog(), event.id_); + return; + } + LOG(INFO) << "Add " << channel_id << " from binlog"; Channel *c = add_channel(channel_id, "on_binlog_channel_event"); - LOG_CHECK(c->status.is_banned()) << channel_id; *c = std::move(log_event.c); // channels come from binlog before all other events, so just add them c->logevent_id = event.id_; @@ -6069,9 +7314,14 @@ void ContactsManager::on_binlog_secret_chat_event(BinlogEvent &&event) { log_event_parse(log_event, event.data_).ensure(); auto secret_chat_id = log_event.secret_chat_id; + if (have_secret_chat(secret_chat_id)) { + LOG(ERROR) << "Skip adding already added " << secret_chat_id; + binlog_erase(G()->td_db()->get_binlog(), event.id_); + return; + } + LOG(INFO) << "Add " << secret_chat_id << " from binlog"; SecretChat *c = add_secret_chat(secret_chat_id); - CHECK(c->date == 0); *c = std::move(log_event.c); // secret chats come from binlog before all other events, so just add them c->logevent_id = event.id_; @@ -6242,41 +7492,375 @@ ContactsManager::SecretChat *ContactsManager::get_secret_chat_force(SecretChatId return get_secret_chat(secret_chat_id); } +void ContactsManager::save_user_full(const UserFull *user_full, UserId user_id) { + if (!G()->parameters().use_chat_info_db) { + return; + } + + LOG(INFO) << "Trying to save to database full " << user_id; + CHECK(user_full != nullptr); + G()->td_db()->get_sqlite_pmc()->set(get_user_full_database_key(user_id), get_user_full_database_value(user_full), + Auto()); +} + +string ContactsManager::get_user_full_database_key(UserId user_id) { + return PSTRING() << "usf" << user_id.get(); +} + +string ContactsManager::get_user_full_database_value(const UserFull *user_full) { + return log_event_store(*user_full).as_slice().str(); +} + +void ContactsManager::on_load_user_full_from_database(UserId user_id, string value) { + LOG(INFO) << "Successfully loaded full " << user_id << " of size " << value.size() << " from database"; + // G()->td_db()->get_sqlite_pmc()->erase(get_user_full_database_key(user_id), Auto()); + // return; + + if (get_user_full(user_id) != nullptr || value.empty()) { + return; + } + + UserFull *user_full = add_user_full(user_id); + auto status = log_event_parse(*user_full, value); + if (status.is_error()) { + // can't happen unless database is broken + LOG(ERROR) << "Repair broken full " << user_id << ' ' << format::as_hex_dump<4>(Slice(value)); + + // just clean all known data about the user and pretend that there was nothing in the database + users_full_.erase(user_id); + G()->td_db()->get_sqlite_pmc()->erase(get_user_full_database_key(user_id), Auto()); + return; + } + + Dependencies dependencies; + dependencies.user_ids.insert(user_id); + td_->messages_manager_->resolve_dependencies_force(dependencies); + + if (user_full->need_phone_number_privacy_exception && is_user_contact(user_id)) { + user_full->need_phone_number_privacy_exception = false; + } + get_bot_info_force(user_id, false); + + update_user_full(user_full, user_id, true); + + if (is_user_deleted(user_id)) { + drop_user_full(user_id); + } +} + +ContactsManager::UserFull *ContactsManager::get_user_full_force(UserId user_id) { + if (!user_id.is_valid()) { + return nullptr; + } + + UserFull *user_full = get_user_full(user_id); + if (user_full != nullptr) { + return user_full; + } + if (!G()->parameters().use_chat_info_db) { + return nullptr; + } + if (!unavailable_user_fulls_.insert(user_id).second) { + return nullptr; + } + + LOG(INFO) << "Trying to load full " << user_id << " from database"; + on_load_user_full_from_database(user_id, + G()->td_db()->get_sqlite_sync_pmc()->get(get_user_full_database_key(user_id))); + return get_user_full(user_id); +} + +void ContactsManager::save_bot_info(const BotInfo *bot_info, UserId user_id) { + if (!G()->parameters().use_chat_info_db) { + return; + } + + LOG(INFO) << "Trying to save to database bot info " << user_id; + CHECK(bot_info != nullptr); + G()->td_db()->get_sqlite_pmc()->set(get_bot_info_database_key(user_id), get_bot_info_database_value(bot_info), + Auto()); +} + +void ContactsManager::update_bot_info(BotInfo *bot_info, UserId user_id, bool send_update, bool from_database) { + CHECK(bot_info != nullptr); + unavailable_bot_infos_.erase(user_id); // don't needed anymore + + if (bot_info->is_changed) { + if (send_update) { + auto user_full = get_user_full(user_id); + if (user_full != nullptr) { + user_full->need_send_update = true; + update_user_full(user_full, user_id); + } + // do not send updates about all ChatFull + } + + if (!from_database) { + save_bot_info(bot_info, user_id); + } + bot_info->is_changed = false; + } +} + +string ContactsManager::get_bot_info_database_key(UserId user_id) { + return PSTRING() << "us_bot_info" << user_id.get(); +} + +string ContactsManager::get_bot_info_database_value(const BotInfo *bot_info) { + return log_event_store(*bot_info).as_slice().str(); +} + +void ContactsManager::on_load_bot_info_from_database(UserId user_id, string value, bool send_update) { + CHECK(G()->parameters().use_chat_info_db); + LOG(INFO) << "Successfully loaded bot info for " << user_id << " of size " << value.size() << " from database"; + // G()->td_db()->get_sqlite_pmc()->erase(get_bot_info_database_key(user_id), Auto()); + // return; + + if (get_bot_info(user_id) != nullptr || value.empty() || !is_user_bot(user_id)) { + return; + } + + BotInfo *bot_info = add_bot_info(user_id); + auto status = log_event_parse(*bot_info, value); + if (status.is_error()) { + // can't happen unless database is broken + LOG(ERROR) << "Repair broken bot info for " << user_id << ' ' << format::as_hex_dump<4>(Slice(value)); + + // clean all known data about the bot info and try to repair it + G()->td_db()->get_sqlite_pmc()->erase(get_bot_info_database_key(user_id), Auto()); + reload_user_full(user_id); + return; + } + + update_bot_info(bot_info, user_id, send_update, true); +} + +ContactsManager::BotInfo *ContactsManager::get_bot_info_force(UserId user_id, bool send_update) { + if (!is_user_bot(user_id)) { + return nullptr; + } + + BotInfo *bot_info = get_bot_info(user_id); + if (bot_info != nullptr) { + return bot_info; + } + if (!G()->parameters().use_chat_info_db) { + return nullptr; + } + if (!unavailable_bot_infos_.insert(user_id).second) { + return nullptr; + } + + LOG(INFO) << "Trying to load bot info for " << user_id << " from database"; + on_load_bot_info_from_database(user_id, G()->td_db()->get_sqlite_sync_pmc()->get(get_bot_info_database_key(user_id)), + send_update); + return get_bot_info(user_id); +} + +void ContactsManager::save_chat_full(const ChatFull *chat_full, ChatId chat_id) { + if (!G()->parameters().use_chat_info_db) { + return; + } + + LOG(INFO) << "Trying to save to database full " << chat_id; + CHECK(chat_full != nullptr); + G()->td_db()->get_sqlite_pmc()->set(get_chat_full_database_key(chat_id), get_chat_full_database_value(chat_full), + Auto()); +} + +string ContactsManager::get_chat_full_database_key(ChatId chat_id) { + return PSTRING() << "grf" << chat_id.get(); +} + +string ContactsManager::get_chat_full_database_value(const ChatFull *chat_full) { + return log_event_store(*chat_full).as_slice().str(); +} + +void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string value) { + LOG(INFO) << "Successfully loaded full " << chat_id << " of size " << value.size() << " from database"; + // G()->td_db()->get_sqlite_pmc()->erase(get_chat_full_database_key(chat_id), Auto()); + // return; + + if (get_chat_full(chat_id) != nullptr || value.empty()) { + return; + } + + ChatFull *chat_full = add_chat_full(chat_id); + auto status = log_event_parse(*chat_full, value); + if (status.is_error()) { + // can't happen unless database is broken + LOG(ERROR) << "Repair broken full " << chat_id << ' ' << format::as_hex_dump<4>(Slice(value)); + + // just clean all known data about the chat and pretend that there was nothing in the database + chats_full_.erase(chat_id); + G()->td_db()->get_sqlite_pmc()->erase(get_chat_full_database_key(chat_id), Auto()); + return; + } + + Dependencies dependencies; + dependencies.chat_ids.insert(chat_id); + dependencies.user_ids.insert(chat_full->creator_user_id); + for (auto &participant : chat_full->participants) { + dependencies.user_ids.insert(participant.user_id); + dependencies.user_ids.insert(participant.inviter_user_id); + } + td_->messages_manager_->resolve_dependencies_force(dependencies); + + for (auto &participant : chat_full->participants) { + get_bot_info_force(participant.user_id); + } + + update_chat_full(chat_full, chat_id, true); +} + +ContactsManager::ChatFull *ContactsManager::get_chat_full_force(ChatId chat_id) { + if (!chat_id.is_valid()) { + return nullptr; + } + + ChatFull *chat_full = get_chat_full(chat_id); + if (chat_full != nullptr) { + return chat_full; + } + if (!G()->parameters().use_chat_info_db) { + return nullptr; + } + if (!unavailable_chat_fulls_.insert(chat_id).second) { + return nullptr; + } + + LOG(INFO) << "Trying to load full " << chat_id << " from database"; + on_load_chat_full_from_database(chat_id, + G()->td_db()->get_sqlite_sync_pmc()->get(get_chat_full_database_key(chat_id))); + return get_chat_full(chat_id); +} + +void ContactsManager::save_channel_full(const ChannelFull *channel_full, ChannelId channel_id) { + if (!G()->parameters().use_chat_info_db) { + return; + } + + LOG(INFO) << "Trying to save to database full " << channel_id; + CHECK(channel_full != nullptr); + G()->td_db()->get_sqlite_pmc()->set(get_channel_full_database_key(channel_id), + get_channel_full_database_value(channel_full), Auto()); +} + +string ContactsManager::get_channel_full_database_key(ChannelId channel_id) { + return PSTRING() << "chf" << channel_id.get(); +} + +string ContactsManager::get_channel_full_database_value(const ChannelFull *channel_full) { + return log_event_store(*channel_full).as_slice().str(); +} + +void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, string value) { + LOG(INFO) << "Successfully loaded full " << channel_id << " of size " << value.size() << " from database"; + // G()->td_db()->get_sqlite_pmc()->erase(get_channel_full_database_key(channel_id), Auto()); + // return; + + if (get_channel_full(channel_id, "on_load_channel_full_from_database") != nullptr || value.empty()) { + return; + } + + ChannelFull *channel_full = add_channel_full(channel_id); + auto status = log_event_parse(*channel_full, value); + if (status.is_error()) { + // can't happen unless database is broken + LOG(ERROR) << "Repair broken full " << channel_id << ' ' << format::as_hex_dump<4>(Slice(value)); + + // just clean all known data about the channel and pretend that there was nothing in the database + channels_full_.erase(channel_id); + G()->td_db()->get_sqlite_pmc()->erase(get_channel_full_database_key(channel_id), Auto()); + return; + } + + Dependencies dependencies; + dependencies.channel_ids.insert(channel_id); + td_->messages_manager_->add_dialog_dependencies(dependencies, DialogId(channel_full->linked_channel_id)); + dependencies.chat_ids.insert(channel_full->migrated_from_chat_id); + dependencies.user_ids.insert(channel_full->bot_user_ids.begin(), channel_full->bot_user_ids.end()); + td_->messages_manager_->resolve_dependencies_force(dependencies); + + for (auto &user_id : channel_full->bot_user_ids) { + get_bot_info_force(user_id); + } + + update_channel_full(channel_full, channel_id, true); +} + +ContactsManager::ChannelFull *ContactsManager::get_channel_full_force(ChannelId channel_id) { + if (!channel_id.is_valid()) { + return nullptr; + } + + ChannelFull *channel_full = get_channel_full(channel_id, "get_channel_full_force"); + if (channel_full != nullptr) { + return channel_full; + } + if (!G()->parameters().use_chat_info_db) { + return nullptr; + } + if (!unavailable_channel_fulls_.insert(channel_id).second) { + return nullptr; + } + + LOG(INFO) << "Trying to load full " << channel_id << " from database"; + on_load_channel_full_from_database( + channel_id, G()->td_db()->get_sqlite_sync_pmc()->get(get_channel_full_database_key(channel_id))); + return get_channel_full(channel_id, "get_channel_full_force"); +} + +void ContactsManager::for_each_secret_chat_with_user(UserId user_id, std::function f) { + auto it = secret_chats_with_user_.find(user_id); + if (it != secret_chats_with_user_.end()) { + for (auto secret_chat_id : it->second) { + f(secret_chat_id); + } + } +} + void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, bool from_database) { CHECK(u != nullptr); - if (u->is_name_changed || u->is_username_changed || u->is_outbound_link_changed) { + if (u->is_name_changed || u->is_username_changed || u->is_is_contact_changed) { update_contacts_hints(u, user_id, from_database); } + 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) { + auto user_full = get_user_full(user_id); + if (user_full != nullptr && user_full->need_phone_number_privacy_exception) { + on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, false); + update_user_full(user_full, user_id); + } + } + } + if (u->is_is_deleted_changed) { + td_->messages_manager_->on_dialog_user_is_deleted_updated(DialogId(user_id), u->is_deleted); + if (u->is_deleted) { + auto user_full = get_user_full(user_id); // must not load user_full from database before sending updateUser + if (user_full != nullptr) { + drop_user_full(user_id); + } + } + } if (u->is_name_changed) { td_->messages_manager_->on_dialog_title_updated(DialogId(user_id)); - auto it = secret_chats_with_user_.find(user_id); - if (it != secret_chats_with_user_.end()) { - for (auto secret_chat_id : it->second) { - td_->messages_manager_->on_dialog_title_updated(DialogId(secret_chat_id)); - } - } + for_each_secret_chat_with_user(user_id, + [messages_manager = td_->messages_manager_.get()](SecretChatId secret_chat_id) { + messages_manager->on_dialog_title_updated(DialogId(secret_chat_id)); + }); } if (u->is_photo_changed) { td_->messages_manager_->on_dialog_photo_updated(DialogId(user_id)); - auto it = secret_chats_with_user_.find(user_id); - if (it != secret_chats_with_user_.end()) { - for (auto secret_chat_id : it->second) { - td_->messages_manager_->on_dialog_photo_updated(DialogId(secret_chat_id)); - } - } + for_each_secret_chat_with_user(user_id, + [messages_manager = td_->messages_manager_.get()](SecretChatId secret_chat_id) { + messages_manager->on_dialog_photo_updated(DialogId(secret_chat_id)); + }); add_user_photo_id(u, user_id, u->photo.id, dialog_photo_get_file_ids(u->photo)); - UserFull *user_full = get_user_full(user_id); - if (user_full != nullptr) { - user_full->photos.clear(); - if (u->photo.id == 0) { - user_full->photo_count = 0; - } else { - user_full->photo_count = -1; - } - user_full->photos_offset = user_full->photo_count; - } + drop_user_photos(user_id, u->photo.id <= 0); } if (u->is_status_changed && user_id != get_my_id()) { auto left_time = get_user_was_online(u, user_id) - G()->server_time_cached(); @@ -6292,11 +7876,19 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo if (u->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(user_id)); } + if (!td_->auth_manager_->is_bot()) { + if (u->restriction_reasons.empty()) { + restricted_user_ids_.erase(user_id); + } else { + restricted_user_ids_.insert(user_id); + } + } u->is_name_changed = false; u->is_username_changed = false; u->is_photo_changed = false; - u->is_outbound_link_changed = false; + u->is_is_contact_changed = false; + u->is_is_deleted_changed = false; u->is_default_permissions_changed = false; if (u->is_deleted) { @@ -6310,18 +7902,19 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo */ } - LOG(DEBUG) << "Update " << user_id << ": is_changed = " << u->is_changed - << ", need_send_update = " << u->need_send_update << ", is_status_changed = " << u->is_status_changed; - if (u->is_changed || u->need_send_update) { + LOG(DEBUG) << "Update " << user_id << ": need_save_to_database = " << u->need_save_to_database + << ", is_changed = " << u->is_changed << ", is_status_changed = " << u->is_status_changed; + u->need_save_to_database |= u->is_changed; + if (u->need_save_to_database) { if (!from_database) { u->is_saved = false; } - if (u->need_send_update) { - send_closure(G()->td(), &Td::send_update, make_tl_object(get_user_object(user_id, u))); - u->need_send_update = false; - u->is_status_changed = false; - } + u->need_save_to_database = false; + } + if (u->is_changed) { + send_closure(G()->td(), &Td::send_update, make_tl_object(get_user_object(user_id, u))); u->is_changed = false; + u->is_status_changed = false; } if (u->is_status_changed) { if (!from_database) { @@ -6369,22 +7962,27 @@ void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, boo if (c->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(chat_id)); } + if (c->is_is_active_changed) { + update_dialogs_for_discussion(DialogId(chat_id), c->is_active && c->status.is_creator()); + } c->is_photo_changed = false; c->is_title_changed = false; c->is_default_permissions_changed = false; + c->is_is_active_changed = false; - LOG(DEBUG) << "Update " << chat_id << ": is_changed = " << c->is_changed - << ", need_send_update = " << c->need_send_update; - if (c->is_changed || c->need_send_update) { + LOG(DEBUG) << "Update " << chat_id << ": need_save_to_database = " << c->need_save_to_database + << ", is_changed = " << c->is_changed; + c->need_save_to_database |= c->is_changed; + if (c->need_save_to_database) { if (!from_database) { c->is_saved = false; } + c->need_save_to_database = false; + } + if (c->is_changed) { + send_closure(G()->td(), &Td::send_update, + make_tl_object(get_basic_group_object(chat_id, c))); c->is_changed = false; - if (c->need_send_update) { - send_closure(G()->td(), &Td::send_update, - make_tl_object(get_basic_group_object(chat_id, c))); - c->need_send_update = false; - } } if (!from_database) { @@ -6430,17 +8028,21 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from } else { channel_unban_timeout_.cancel_timeout(channel_id.get()); } + + if (c->is_megagroup) { + update_dialogs_for_discussion(DialogId(channel_id), c->status.is_administrator() && c->status.can_pin_messages()); + } + if (!c->status.is_member()) { + remove_inactive_channel(channel_id); + } } if (c->is_username_changed) { - if (c->status.is_creator() && created_public_channels_inited_) { + if (c->status.is_creator() && created_public_channels_inited_[0]) { if (c->username.empty()) { - created_public_channels_.erase( - std::remove(created_public_channels_.begin(), created_public_channels_.end(), channel_id), - created_public_channels_.end()); + td::remove(created_public_channels_[0], channel_id); } else { - if (std::find(created_public_channels_.begin(), created_public_channels_.end(), channel_id) == - created_public_channels_.end()) { - created_public_channels_.push_back(channel_id); + if (!td::contains(created_public_channels_[0], channel_id)) { + created_public_channels_[0].push_back(channel_id); } } } @@ -6448,44 +8050,53 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from if (c->is_default_permissions_changed) { td_->messages_manager_->on_dialog_permissions_updated(DialogId(channel_id)); } + if (!td_->auth_manager_->is_bot()) { + if (c->restriction_reasons.empty()) { + restricted_channel_ids_.erase(channel_id); + } else { + restricted_channel_ids_.insert(channel_id); + } + } + c->is_photo_changed = false; c->is_title_changed = false; c->is_default_permissions_changed = false; c->is_status_changed = false; c->is_username_changed = false; - LOG(DEBUG) << "Update " << channel_id << ": is_changed = " << c->is_changed - << ", need_send_update = " << c->need_send_update; - if (c->is_changed || c->need_send_update) { + LOG(DEBUG) << "Update " << channel_id << ": need_save_to_database = " << c->need_save_to_database + << ", is_changed = " << c->is_changed; + c->need_save_to_database |= c->is_changed; + if (c->need_save_to_database) { if (!from_database) { c->is_saved = false; } + c->need_save_to_database = false; + } + if (c->is_changed) { + send_closure(G()->td(), &Td::send_update, + make_tl_object(get_supergroup_object(channel_id, c))); c->is_changed = false; - if (c->need_send_update) { - send_closure(G()->td(), &Td::send_update, - make_tl_object(get_supergroup_object(channel_id, c))); - c->need_send_update = false; - } } if (!from_database) { save_channel(c, channel_id, from_binlog); } - bool have_read_access = have_input_peer_channel(c, AccessRights::Read); + bool have_read_access = have_input_peer_channel(c, channel_id, AccessRights::Read); bool is_member = c->status.is_member(); if (c->had_read_access && !have_read_access) { send_closure_later(G()->messages_manager(), &MessagesManager::delete_dialog, DialogId(channel_id)); } else if (!from_database && c->was_member != is_member) { DialogId dialog_id(channel_id); send_closure_later(G()->messages_manager(), &MessagesManager::force_create_dialog, dialog_id, "update channel", - true); + true, true); } c->had_read_access = have_read_access; c->was_member = is_member; - if (c->cache_version != Channel::CACHE_VERSION && !c->is_repaired && have_input_peer_channel(c, AccessRights::Read) && - !G()->close_flag()) { + if (c->cache_version != Channel::CACHE_VERSION && !c->is_repaired && + have_input_peer_channel(c, channel_id, AccessRights::Read) && !G()->close_flag()) { c->is_repaired = true; LOG(INFO) << "Repairing cache of " << channel_id; @@ -6496,86 +8107,144 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from void ContactsManager::update_secret_chat(SecretChat *c, SecretChatId secret_chat_id, bool from_binlog, bool from_database) { CHECK(c != nullptr); - LOG(DEBUG) << "Update " << secret_chat_id << ": is_changed = " << c->is_changed - << ", need_send_update = " << c->need_send_update; - if (c->is_changed || c->need_send_update) { + LOG(DEBUG) << "Update " << secret_chat_id << ": need_save_to_database = " << c->need_save_to_database + << ", is_changed = " << c->is_changed; + c->need_save_to_database |= c->is_changed; + if (c->need_save_to_database) { if (!from_database) { c->is_saved = false; } - c->is_changed = false; - if (c->need_send_update) { - send_closure(G()->td(), &Td::send_update, - make_tl_object(get_secret_chat_object(secret_chat_id, c))); - c->need_send_update = false; - } + c->need_save_to_database = false; DialogId dialog_id(secret_chat_id); send_closure_later(G()->messages_manager(), &MessagesManager::force_create_dialog, dialog_id, "update secret chat", - true); + true, true); if (c->is_state_changed) { send_closure_later(G()->messages_manager(), &MessagesManager::on_update_secret_chat_state, secret_chat_id, c->state); c->is_state_changed = false; } } + if (c->is_changed) { + send_closure(G()->td(), &Td::send_update, + make_tl_object(get_secret_chat_object(secret_chat_id, c))); + c->is_changed = false; + } if (!from_database) { save_secret_chat(c, secret_chat_id, from_binlog); } } -void ContactsManager::update_user_full(UserFull *user_full, UserId user_id) { +void ContactsManager::update_user_full(UserFull *user_full, UserId user_id, bool from_database) { CHECK(user_full != nullptr); + unavailable_user_fulls_.erase(user_id); // don't needed anymore if (user_full->is_common_chat_count_changed) { td_->messages_manager_->drop_common_dialogs_cache(user_id); user_full->is_common_chat_count_changed = false; } - if (user_full->is_changed) { - user_full->is_changed = false; - if (user_full->is_inited) { - send_closure(G()->td(), &Td::send_update, - make_tl_object(get_user_id_object(user_id, "updateUserFullInfo"), - get_user_full_info_object(user_id, user_full))); + if (user_full->is_is_blocked_changed) { + td_->messages_manager_->on_dialog_user_is_blocked_updated(DialogId(user_id), user_full->is_blocked); + user_full->is_is_blocked_changed = false; + } + + user_full->need_send_update |= user_full->is_changed; + user_full->need_save_to_database |= user_full->is_changed; + user_full->is_changed = false; + if (user_full->need_send_update) { + send_closure(G()->td(), &Td::send_update, + make_tl_object(get_user_id_object(user_id, "updateUserFullInfo"), + get_user_full_info_object(user_id, user_full))); + user_full->need_send_update = false; + } + if (user_full->need_save_to_database) { + if (!from_database) { + save_user_full(user_full, user_id); } + user_full->need_save_to_database = false; } } -void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id) { +void ContactsManager::update_chat_full(ChatFull *chat_full, ChatId chat_id, bool from_database) { CHECK(chat_full != nullptr); - if (chat_full->is_changed) { - vector administrator_user_ids; + unavailable_chat_fulls_.erase(chat_id); // don't needed anymore + + chat_full->need_send_update |= chat_full->is_changed; + chat_full->need_save_to_database |= chat_full->is_changed; + chat_full->is_changed = false; + if (chat_full->need_send_update) { + vector administrators; vector bot_user_ids; for (const auto &participant : chat_full->participants) { auto user_id = participant.user_id; if (participant.status.is_administrator()) { - administrator_user_ids.push_back(user_id); + administrators.emplace_back(user_id, participant.status.get_rank(), participant.status.is_creator()); } if (is_user_bot(user_id)) { bot_user_ids.push_back(user_id); } } - on_update_dialog_administrators(DialogId(chat_id), std::move(administrator_user_ids), chat_full->version != -1); + on_update_dialog_administrators(DialogId(chat_id), std::move(administrators), chat_full->version != -1); td_->messages_manager_->on_dialog_bots_updated(DialogId(chat_id), std::move(bot_user_ids)); - chat_full->is_changed = false; send_closure( G()->td(), &Td::send_update, make_tl_object(get_basic_group_id_object(chat_id, "update_chat_full"), get_basic_group_full_info_object(chat_full))); + chat_full->need_send_update = false; + } + if (chat_full->need_save_to_database) { + if (!from_database) { + save_chat_full(chat_full, chat_id); + } + chat_full->need_save_to_database = false; } } -void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId channel_id) { +void ContactsManager::update_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool from_database) { CHECK(channel_full != nullptr); - if (channel_full->is_changed) { - if (channel_full->participant_count < channel_full->administrator_count) { - channel_full->administrator_count = channel_full->participant_count; + unavailable_channel_fulls_.erase(channel_id); // don't needed anymore + + if (channel_full->participant_count < channel_full->administrator_count) { + channel_full->administrator_count = channel_full->participant_count; + } + + if (channel_full->is_slow_mode_next_send_date_changed) { + auto now = G()->server_time(); + if (channel_full->slow_mode_next_send_date > now + 3601) { + channel_full->slow_mode_next_send_date = static_cast(now) + 3601; } - channel_full->is_changed = false; + if (channel_full->slow_mode_next_send_date <= now) { + channel_full->slow_mode_next_send_date = 0; + } + if (channel_full->slow_mode_next_send_date == 0) { + slow_mode_delay_timeout_.cancel_timeout(channel_id.get()); + } else { + slow_mode_delay_timeout_.set_timeout_in(channel_id.get(), channel_full->slow_mode_next_send_date - now + 0.002); + } + channel_full->is_slow_mode_next_send_date_changed = false; + } + + channel_full->need_send_update |= channel_full->is_changed; + channel_full->need_save_to_database |= channel_full->is_changed; + channel_full->is_changed = false; + if (channel_full->need_send_update) { + if (channel_full->linked_channel_id.is_valid()) { + td_->messages_manager_->force_create_dialog(DialogId(channel_full->linked_channel_id), "update_channel_full", + true); + } + send_closure( G()->td(), &Td::send_update, make_tl_object(get_supergroup_id_object(channel_id, "update_channel_full"), get_supergroup_full_info_object(channel_full))); + channel_full->need_send_update = false; + } + if (channel_full->need_save_to_database) { + if (!from_database) { + save_channel_full(channel_full, channel_id); + } + channel_full->need_save_to_database = false; } } @@ -6598,22 +8267,39 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u return; } - on_update_user_links(user_id, std::move(user_full->link_->my_link_), std::move(user_full->link_->foreign_link_)); td_->messages_manager_->on_update_dialog_notify_settings(DialogId(user_id), std::move(user_full->notify_settings_), "on_get_user_full"); - MessageId pinned_message_id; - if ((user_full->flags_ & USER_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { - pinned_message_id = MessageId(ServerMessageId(user_full->pinned_msg_id_)); + { + MessageId pinned_message_id; + if ((user_full->flags_ & USER_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { + pinned_message_id = MessageId(ServerMessageId(user_full->pinned_msg_id_)); + } + td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(user_id), pinned_message_id); } - td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(user_id), pinned_message_id); + { + FolderId folder_id; + if ((user_full->flags_ & USER_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(user_full->folder_id_); + } + td_->messages_manager_->on_update_dialog_folder_id(DialogId(user_id), folder_id); + } + td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( + DialogId(user_id), (user_full->flags_ & USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); - UserFull *user = &users_full_[user_id]; + UserFull *user = add_user_full(user_id); user->expires_at = Time::now() + USER_FULL_EXPIRE_TIME; - user->is_inited = true; on_update_user_full_is_blocked(user, user_id, (user_full->flags_ & USER_FULL_FLAG_IS_BLOCKED) != 0); on_update_user_full_common_chat_count(user, user_id, user_full->common_chats_count_); + on_update_user_full_need_phone_number_privacy_exception( + user, user_id, (user_full->settings_->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0); + + bool can_pin_messages = user_full->can_pin_message_; + if (user->can_pin_messages != can_pin_messages) { + user->can_pin_messages = can_pin_messages; + user->is_changed = true; + } bool can_be_called = user_full->phone_calls_available_ && !user_full->phone_calls_private_; bool has_private_calls = user_full->phone_calls_private_; @@ -6628,14 +8314,17 @@ 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) { - user->photo_count = 0; - user->photos_offset = 0; - user->photos.clear(); + drop_user_photos(user_id, true); } - if ((user_full->flags_ & USER_FULL_FLAG_HAS_BOT_INFO) != 0 && !u->is_deleted) { - on_update_user_full_bot_info(user, user_id, u->bot_info_version, std::move(user_full->bot_info_)); + if (user_full->bot_info_ != nullptr) { + if (on_update_bot_info(std::move(user_full->bot_info_), false)) { + user->need_send_update = true; + } } update_user_full(user, user_id); + + // update peer settings after UserFull is created and updated to not update twice need_phone_number_privacy_exception + td_->messages_manager_->on_get_peer_settings(DialogId(user_id), std::move(user_full->settings_)); } void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 limit, int32 total_count, @@ -6677,21 +8366,21 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim return; } - UserFull *user = &users_full_[user_id]; - user->photo_count = total_count; - CHECK(user->getting_photos_now); - user->getting_photos_now = false; + UserPhotos *user_photos = &user_photos_[user_id]; + user_photos->count = total_count; + CHECK(user_photos->getting_now); + user_photos->getting_now = false; - if (user->photos_offset == -1) { - user->photos_offset = 0; - CHECK(user->photos.empty()); + if (user_photos->offset == -1) { + user_photos->offset = 0; + CHECK(user_photos->photos.empty()); } - if (offset != narrow_cast(user->photos.size()) + user->photos_offset) { + if (offset != narrow_cast(user_photos->photos.size()) + user_photos->offset) { LOG(INFO) << "Inappropriate offset to append " << user_id << " profile photos to cache: offset = " << offset - << ", current_offset = " << user->photos_offset << ", photo_count = " << user->photos.size(); - user->photos.clear(); - user->photos_offset = offset; + << ", current_offset = " << user_photos->offset << ", photo_count = " << user_photos->photos.size(); + user_photos->photos.clear(); + user_photos->offset = offset; } for (auto &photo : photos) { @@ -6703,14 +8392,14 @@ void ContactsManager::on_get_user_photos(UserId user_id, int32 offset, int32 lim continue; } - user->photos.push_back(std::move(user_photo)); - add_user_photo_id(u, user_id, user->photos.back().id, photo_get_file_ids(user->photos.back())); + user_photos->photos.push_back(std::move(user_photo)); + add_user_photo_id(u, user_id, user_photos->photos.back().id, photo_get_file_ids(user_photos->photos.back())); } } -bool ContactsManager::on_update_bot_info(tl_object_ptr &&bot_info) { - CHECK(bot_info != nullptr); - UserId user_id(bot_info->user_id_); +bool ContactsManager::on_update_bot_info(tl_object_ptr &&new_bot_info, bool send_update) { + CHECK(new_bot_info != nullptr); + UserId user_id(new_bot_info->user_id_); if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; return false; @@ -6722,40 +8411,39 @@ bool ContactsManager::on_update_bot_info(tl_object_ptr && return false; } - if (u->is_deleted) { + if (u->is_deleted || !u->is_bot) { return false; } - UserFull *user_full = &users_full_[user_id]; - bool result = on_update_user_full_bot_info(user_full, user_id, u->bot_info_version, std::move(bot_info)); - update_user_full(user_full, user_id); - return result; + BotInfo *bot_info = add_bot_info(user_id); + if (bot_info->version > u->bot_info_version) { + LOG(WARNING) << "Ignore outdated version of BotInfo for " << user_id << " with version " << u->bot_info_version + << ", current version is " << bot_info->version; + return false; + } + if (bot_info->version == u->bot_info_version) { + LOG(DEBUG) << "Ignore already known version of BotInfo for " << user_id << " with version " << u->bot_info_version; + return false; + } + + bot_info->version = u->bot_info_version; + bot_info->description = std::move(new_bot_info->description_); + bot_info->commands = transform(std::move(new_bot_info->commands_), [](auto &&command) { + return std::make_pair(std::move(command->command_), std::move(command->description_)); + }); + bot_info->is_changed = true; + + update_bot_info(bot_info, user_id, send_update, false); + return true; } -bool ContactsManager::on_update_user_full_bot_info(UserFull *user_full, UserId user_id, int32 bot_info_version, - tl_object_ptr &&bot_info) { - CHECK(user_full != nullptr); - CHECK(bot_info != nullptr); - - if (user_full->bot_info != nullptr && user_full->bot_info->version > bot_info_version) { - LOG(WARNING) << "Ignore outdated version of BotInfo for " << user_id << " with version " << bot_info_version - << ", current version is " << user_full->bot_info->version; - return false; - } - if (user_full->bot_info != nullptr && user_full->bot_info->version == bot_info_version) { - LOG(DEBUG) << "Ignore already known version of BotInfo for " << user_id << " with version " << bot_info_version; +bool ContactsManager::is_bot_info_expired(UserId user_id, int32 bot_info_version) { + if (bot_info_version == -1) { return false; } - vector> commands; - commands.reserve(bot_info->commands_.size()); - for (auto &command : bot_info->commands_) { - commands.emplace_back(std::move(command->command_), std::move(command->description_)); - } - user_full->bot_info = - td::make_unique(bot_info_version, std::move(bot_info->description_), std::move(commands)); - user_full->is_changed = true; - return true; + auto bot_info = get_bot_info_force(user_id); + return bot_info == nullptr || bot_info->version != bot_info_version; } void ContactsManager::on_get_chat(tl_object_ptr &&chat, const char *source) { @@ -6780,14 +8468,14 @@ void ContactsManager::on_get_chats(vector> &&c } } -void ContactsManager::on_get_chat_full(tl_object_ptr &&chat_full_ptr) { +void ContactsManager::on_get_chat_full(tl_object_ptr &&chat_full_ptr, Promise &&promise) { LOG(INFO) << "Receive " << to_string(chat_full_ptr); if (chat_full_ptr->get_id() == telegram_api::chatFull::ID) { auto chat_full = move_tl_object_as(chat_full_ptr); ChatId chat_id(chat_full->id_); if (!chat_id.is_valid()) { LOG(ERROR) << "Receive invalid " << chat_id; - return; + return promise.set_value(Unit()); } { @@ -6804,20 +8492,29 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(chat_id), pinned_message_id); if (c->version > c->pinned_message_version) { c->pinned_message_version = c->version; - c->is_changed = true; + c->need_save_to_database = true; update_chat(c, chat_id); } } } + { + FolderId folder_id; + if ((chat_full->flags_ & CHAT_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(chat_full->folder_id_); + } + td_->messages_manager_->on_update_dialog_folder_id(DialogId(chat_id), folder_id); + } + td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( + DialogId(chat_id), (chat_full->flags_ & CHAT_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); - ChatFull *chat = &chats_full_[chat_id]; + ChatFull *chat = add_chat_full(chat_id); on_update_chat_full_invite_link(chat, std::move(chat_full->exported_invite_)); // Ignoring chat_full->photo for (auto &bot_info : chat_full->bot_info_) { if (on_update_bot_info(std::move(bot_info))) { - chat->is_changed = true; + chat->need_send_update = true; } } @@ -6825,6 +8522,10 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c chat->description = std::move(chat_full->about_); chat->is_changed = true; } + if (chat->can_set_username != chat_full->can_set_username_) { + chat->can_set_username = chat_full->can_set_username_; + chat->is_changed = true; + } on_get_chat_participants(std::move(chat_full->participants_), false); td_->messages_manager_->on_update_dialog_notify_settings(DialogId(chat_id), std::move(chat_full->notify_settings_), @@ -6837,7 +8538,25 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c ChannelId channel_id(channel_full->id_); if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; - return; + return promise.set_value(Unit()); + } + + if (!G()->close_flag()) { + auto channel = get_channel_full(channel_id, "on_get_channel_full"); + if (channel != nullptr) { + if (channel->repair_request_version != 0 && channel->repair_request_version < channel->speculative_version) { + LOG(INFO) << "Receive ChannelFull with request version " << channel->repair_request_version + << ", but current speculative version is " << channel->speculative_version; + + channel->repair_request_version = channel->speculative_version; + + auto input_channel = get_input_channel(channel_id); + CHECK(input_channel != nullptr); + td_->create_handler(std::move(promise))->send(channel_id, std::move(input_channel)); + return; + } + channel->repair_request_version = 0; + } } td_->messages_manager_->on_update_dialog_notify_settings( @@ -6845,9 +8564,10 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c // Ignoring channel_full->photo - if (!have_channel(channel_id)) { + auto c = get_channel(channel_id); + if (c == nullptr) { LOG(ERROR) << channel_id << " not found"; - return; + return promise.set_value(Unit()); } auto participant_count = @@ -6860,21 +8580,25 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_BANNED_COUNT) != 0 ? channel_full->kicked_count_ : 0; auto can_get_participants = (channel_full->flags_ & CHANNEL_FULL_FLAG_CAN_GET_PARTICIPANTS) != 0; 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_STICKERS) != 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; - int64 sticker_set_id = channel_full->stickerset_ == nullptr - ? 0 - : td_->stickers_manager_->on_get_sticker_set(std::move(channel_full->stickerset_), true); + 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"); + } - ChannelFull *channel = &channels_full_[channel_id]; + ChannelFull *channel = add_channel_full(channel_id); + channel->repair_request_version = 0; channel->expires_at = Time::now() + CHANNEL_FULL_EXPIRE_TIME; if (channel->description != channel_full->about_ || channel->participant_count != participant_count || 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_view_statistics != can_view_statistics || channel->sticker_set_id != sticker_set_id || - channel->is_all_history_available != is_all_history_available) { + channel->can_set_location != can_set_location || channel->can_view_statistics != can_view_statistics || + 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; channel->administrator_count = administrator_count; @@ -6883,19 +8607,17 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c 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->sticker_set_id = sticker_set_id; channel->is_all_history_available = is_all_history_available; + channel->sticker_set_id = sticker_set_id; channel->is_changed = true; - if (participant_count != 0) { - auto c = get_channel(channel_id); - if (c != nullptr && c->participant_count != participant_count) { - c->participant_count = participant_count; - c->need_send_update = true; - update_channel(c, channel_id); - } + if (participant_count != 0 && c->participant_count != participant_count) { + c->participant_count = participant_count; + c->is_changed = true; + update_channel(c, channel_id); } } @@ -6905,16 +8627,28 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_channel_max_unavailable_message_id( channel_id, MessageId(ServerMessageId(channel_full->available_min_id_))); } - td_->messages_manager_->on_read_channel_inbox( - channel_id, MessageId(ServerMessageId(channel_full->read_inbox_max_id_)), channel_full->unread_count_); + td_->messages_manager_->on_read_channel_inbox(channel_id, + MessageId(ServerMessageId(channel_full->read_inbox_max_id_)), + channel_full->unread_count_, channel_full->pts_, "ChannelFull"); on_update_channel_full_invite_link(channel, std::move(channel_full->exported_invite_)); - MessageId pinned_message_id; - if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { - pinned_message_id = MessageId(ServerMessageId(channel_full->pinned_msg_id_)); + { + MessageId pinned_message_id; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { + pinned_message_id = MessageId(ServerMessageId(channel_full->pinned_msg_id_)); + } + td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(channel_id), pinned_message_id); } - td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(channel_id), pinned_message_id); + { + FolderId folder_id; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_FOLDER_ID) != 0) { + folder_id = FolderId(channel_full->folder_id_); + } + td_->messages_manager_->on_update_dialog_folder_id(DialogId(channel_id), folder_id); + } + td_->messages_manager_->on_update_dialog_has_scheduled_server_messages( + DialogId(channel_id), (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SCHEDULED_MESSAGES) != 0); if (participant_count >= 190) { int32 online_member_count = 0; @@ -6924,9 +8658,43 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c td_->messages_manager_->on_update_dialog_online_member_count(DialogId(channel_id), online_member_count, true); } + vector bot_user_ids; for (auto &bot_info : channel_full->bot_info_) { + UserId user_id(bot_info->user_id_); + if (!is_user_bot(user_id)) { + continue; + } + + bot_user_ids.push_back(user_id); on_update_bot_info(std::move(bot_info)); } + on_update_channel_full_bot_user_ids(channel, channel_id, std::move(bot_user_ids)); + + ChannelId linked_channel_id; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_LINKED_CHANNEL_ID) != 0) { + linked_channel_id = ChannelId(channel_full->linked_chat_id_); + auto linked_channel = get_channel_force(linked_channel_id); + if (linked_channel == nullptr || c->is_megagroup == linked_channel->is_megagroup || + channel_id == linked_channel_id) { + LOG(ERROR) << "Failed to add a link between " << channel_id << " and " << linked_channel_id; + linked_channel_id = ChannelId(); + } + } + on_update_channel_full_linked_channel_id(channel, channel_id, linked_channel_id); + + on_update_channel_full_location(channel, channel_id, DialogLocation(std::move(channel_full->location_))); + + if (c->is_megagroup) { + int32 slow_mode_delay = 0; + int32 slow_mode_next_send_date = 0; + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_DELAY) != 0) { + slow_mode_delay = channel_full->slowmode_seconds_; + } + if ((channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_SLOW_MODE_NEXT_SEND_DATE) != 0) { + slow_mode_next_send_date = channel_full->slowmode_next_send_date_; + } + on_update_channel_full_slow_mode_delay(channel, channel_id, slow_mode_delay, slow_mode_next_send_date); + } ChatId migrated_from_chat_id; MessageId migrated_from_max_message_id; @@ -6945,12 +8713,13 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c update_channel_full(channel, channel_id); } + promise.set_value(Unit()); } bool ContactsManager::is_update_about_username_change_received(UserId user_id) const { const User *u = get_user(user_id); if (u != nullptr) { - return u->inbound == LinkState::Contact; + return u->is_contact; } else { return false; } @@ -6981,14 +8750,14 @@ void ContactsManager::on_update_user_name(User *u, UserId user_id, string &&firs u->last_name = std::move(last_name); u->is_name_changed = true; LOG(DEBUG) << "Name has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } td_->messages_manager_->on_dialog_username_updated(DialogId(user_id), u->username, username); if (u->username != username) { u->username = std::move(username); u->is_username_changed = true; LOG(DEBUG) << "Username has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } } @@ -7011,7 +8780,7 @@ void ContactsManager::on_update_user_phone_number(User *u, UserId user_id, strin if (u->phone_number != phone_number) { u->phone_number = std::move(phone_number); LOG(DEBUG) << "Phone number has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } } @@ -7036,16 +8805,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); - UserFull *user_full = get_user_full(user_id); - if (user_full != nullptr) { - user_full->photos.clear(); - if (is_empty) { - user_full->photo_count = 0; - } else { - user_full->photo_count = -1; - } - user_full->photos_offset = user_full->photo_count; - } + drop_user_photos(user_id, is_empty); return; } @@ -7063,7 +8823,7 @@ void ContactsManager::do_update_user_photo(User *u, UserId user_id, u->photo = new_photo; u->is_photo_changed = true; LOG(DEBUG) << "Photo has changed for " << user_id; - u->need_send_update = true; + u->is_changed = true; } } @@ -7085,6 +8845,26 @@ void ContactsManager::add_user_photo_id(User *u, UserId user_id, int64 photo_id, } } +void ContactsManager::on_update_user_is_contact(User *u, UserId user_id, bool is_contact, bool is_mutual_contact) { + UserId my_id = get_my_id(); + if (user_id == my_id) { + is_mutual_contact = is_contact; + } + if (!is_contact && is_mutual_contact) { + LOG(ERROR) << "Receive is_mutual_contact == true for non-contact " << user_id; + is_mutual_contact = false; + } + + 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); + u->is_contact = is_contact; + u->is_mutual_contact = is_mutual_contact; + u->is_changed = true; + } +} + void ContactsManager::on_update_user_online(UserId user_id, tl_object_ptr &&status) { if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; @@ -7209,15 +8989,16 @@ void ContactsManager::on_update_user_local_was_online(User *u, UserId user_id, i } } -void ContactsManager::on_update_user_blocked(UserId user_id, bool is_blocked) { - LOG(INFO) << "Receive update user blocked with " << user_id << " and is_blocked = " << is_blocked; +void ContactsManager::on_update_user_is_blocked(UserId user_id, bool is_blocked) { + LOG(INFO) << "Receive update user is blocked with " << user_id << " and is_blocked = " << is_blocked; if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; return; } - UserFull *user_full = get_user_full(user_id); + UserFull *user_full = get_user_full_force(user_id); if (user_full == nullptr) { + td_->messages_manager_->on_dialog_user_is_blocked_updated(DialogId(user_id), is_blocked); return; } on_update_user_full_is_blocked(user_full, user_id, is_blocked); @@ -7226,7 +9007,8 @@ void ContactsManager::on_update_user_blocked(UserId user_id, bool is_blocked) { void ContactsManager::on_update_user_full_is_blocked(UserFull *user_full, UserId user_id, bool is_blocked) { CHECK(user_full != nullptr); - if (user_full->is_inited && user_full->is_blocked != is_blocked) { + if (user_full->is_blocked != is_blocked) { + user_full->is_is_blocked_changed = true; user_full->is_blocked = is_blocked; user_full->is_changed = true; } @@ -7239,7 +9021,7 @@ void ContactsManager::on_update_user_common_chat_count(UserId user_id, int32 com return; } - UserFull *user_full = get_user_full(user_id); + UserFull *user_full = get_user_full_force(user_id); if (user_full == nullptr) { return; } @@ -7254,23 +9036,55 @@ void ContactsManager::on_update_user_full_common_chat_count(UserFull *user_full, LOG(ERROR) << "Receive " << common_chat_count << " as common group count with " << user_id; common_chat_count = 0; } - if (user_full->is_inited && user_full->common_chat_count != common_chat_count) { + if (user_full->common_chat_count != common_chat_count) { user_full->common_chat_count = common_chat_count; user_full->is_common_chat_count_changed = true; user_full->is_changed = true; } } +void ContactsManager::on_update_user_need_phone_number_privacy_exception(UserId user_id, + bool need_phone_number_privacy_exception) { + LOG(INFO) << "Receive " << need_phone_number_privacy_exception << " need phone number privacy exception with " + << user_id; + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << user_id; + return; + } + + UserFull *user_full = get_user_full_force(user_id); + if (user_full == nullptr) { + return; + } + on_update_user_full_need_phone_number_privacy_exception(user_full, user_id, need_phone_number_privacy_exception); + update_user_full(user_full, user_id); +} + +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 (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; + } +} + +void ContactsManager::on_ignored_restriction_reasons_changed() { + for (auto user_id : restricted_user_ids_) { + send_closure(G()->td(), &Td::send_update, + td_api::make_object(get_user_object(user_id, get_user(user_id)))); + } + for (auto channel_id : restricted_channel_ids_) { + send_closure( + G()->td(), &Td::send_update, + td_api::make_object(get_supergroup_object(channel_id, get_channel(channel_id)))); + } +} + void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promise promise) { UserId my_id = get_my_id(); - UserFull *user_full = get_user_full(my_id); - if (user_full != nullptr) { - // drop photo cache - user_full->photos.clear(); - user_full->photo_count = -1; - user_full->photos_offset = -1; - } + drop_user_photos(my_id, false); if (G()->close_flag()) { return promise.set_value(Unit()); @@ -7279,97 +9093,41 @@ void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promise &&link) { - int32 id = link->get_id(); - switch (id) { - case telegram_api::contactLinkUnknown::ID: - return LinkState::Unknown; - case telegram_api::contactLinkNone::ID: - return LinkState::None; - case telegram_api::contactLinkHasPhone::ID: - return LinkState::KnowsPhoneNumber; - case telegram_api::contactLinkContact::ID: - return LinkState::Contact; - default: - UNREACHABLE(); - } - return LinkState::Unknown; -} - -void ContactsManager::on_update_user_links(UserId user_id, tl_object_ptr &&outbound, - tl_object_ptr &&inbound) { - if (!user_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << user_id; - return; - } - - User *u = get_user_force(user_id); - if (u != nullptr) { - on_update_user_links(u, user_id, get_link_state(std::move(outbound)), get_link_state(std::move(inbound))); - update_user(u, user_id); - } else { - LOG(INFO) << "Ignore update user links about unknown " << user_id; - } -} - -void ContactsManager::on_update_user_links(User *u, UserId user_id, LinkState outbound, LinkState inbound) { - UserId my_id = get_my_id(); - if (user_id == my_id) { - if (outbound == LinkState::None && !td_->auth_manager_->is_bot()) { - outbound = LinkState::KnowsPhoneNumber; +void ContactsManager::drop_user_photos(UserId user_id, bool is_empty) { + auto it = user_photos_.find(user_id); + if (it != user_photos_.end()) { + auto user_photos = &it->second; + user_photos->photos.clear(); + if (is_empty) { + user_photos->count = 0; + } else { + user_photos->count = -1; } - inbound = outbound; - } - if (!u->phone_number.empty() && outbound == LinkState::None) { - outbound = LinkState::KnowsPhoneNumber; - } - - LOG(DEBUG) << "Update " << user_id << " links from (" << u->outbound << ", " << u->inbound << ") to (" << outbound - << ", " << inbound << ")"; - bool need_send_update = false; - if (outbound != u->outbound && outbound != LinkState::Unknown) { - need_send_update |= outbound != LinkState::None || u->outbound != LinkState::Unknown; - LOG(DEBUG) << "Set outbound link to " << outbound << ", need_send_update = " << need_send_update; - u->outbound = outbound; - u->is_outbound_link_changed = true; - u->is_changed = true; - } - if (inbound != u->inbound && inbound != LinkState::Unknown) { - need_send_update |= inbound != LinkState::None || u->inbound != LinkState::Unknown; - LOG(DEBUG) << "Set inbound link to " << inbound << ", need_send_update = " << need_send_update; - u->inbound = inbound; - u->is_changed = true; - } - if (u->inbound == LinkState::Contact && u->outbound != LinkState::Contact) { - u->inbound = LinkState::KnowsPhoneNumber; - u->is_changed = true; - need_send_update = true; - } - - if (need_send_update) { - LOG(DEBUG) << "Links have changed for " << user_id; - u->need_send_update = true; + user_photos->offset = user_photos->count; } } -void ContactsManager::invalidate_user_full(UserId user_id) { - auto user_full = get_user_full(user_id); +void ContactsManager::drop_user_full(UserId user_id) { + drop_user_photos(user_id, false); + + bot_infos_.erase(user_id); + if (G()->parameters().use_chat_info_db) { + G()->td_db()->get_sqlite_pmc()->erase(get_bot_info_database_key(user_id), Auto()); + } + + auto user_full = get_user_full_force(user_id); if (user_full == nullptr) { return; } user_full->expires_at = 0.0; - user_full->photos.clear(); - user_full->photo_count = -1; - user_full->photos_offset = -1; - user_full->is_inited = true; user_full->is_blocked = false; user_full->can_be_called = false; user_full->has_private_calls = false; + user_full->need_phone_number_privacy_exception = false; user_full->about = string(); user_full->common_chat_count = 0; - user_full->bot_info = nullptr; user_full->is_changed = true; update_user_full(user_full, user_id); @@ -7473,7 +9231,7 @@ void ContactsManager::on_get_chat_participants(tl_object_ptr(participant_ptr); new_creator_user_id = UserId(participant->user_id_); dialog_participant = {new_creator_user_id, new_creator_user_id, c->date, - DialogParticipantStatus::Creator(true)}; + DialogParticipantStatus::Creator(true, string())}; break; } case telegram_api::chatParticipantAdmin::ID: { @@ -7594,13 +9352,15 @@ DialogParticipant ContactsManager::get_dialog_participant( } case telegram_api::channelParticipantCreator::ID: { auto participant = move_tl_object_as(participant_ptr); - return {UserId(participant->user_id_), UserId(), 0, DialogParticipantStatus::Creator(true)}; + return {UserId(participant->user_id_), UserId(), 0, + DialogParticipantStatus::Creator(true, std::move(participant->rank_))}; } case telegram_api::channelParticipantAdmin::ID: { auto participant = move_tl_object_as(participant_ptr); bool can_be_edited = (participant->flags_ & telegram_api::channelParticipantAdmin::CAN_EDIT_MASK) != 0; return {UserId(participant->user_id_), UserId(participant->promoted_by_), participant->date_, - get_dialog_participant_status(can_be_edited, std::move(participant->admin_rights_))}; + get_dialog_participant_status(can_be_edited, std::move(participant->admin_rights_), + std::move(participant->rank_))}; } case telegram_api::channelParticipantBanned::ID: { auto participant = move_tl_object_as(participant_ptr); @@ -7617,7 +9377,7 @@ DialogParticipant ContactsManager::get_dialog_participant( tl_object_ptr ContactsManager::get_chat_member_object( const DialogParticipant &dialog_participant) const { UserId participant_user_id = dialog_participant.user_id; - return make_tl_object( + return td_api::make_object( get_user_id_object(participant_user_id, "chatMember.user_id"), get_user_id_object(dialog_participant.inviter_user_id, "chatMember.inviter_user_id"), dialog_participant.joined_date, dialog_participant.status.get_chat_member_status_object(), @@ -7670,12 +9430,20 @@ bool ContactsManager::on_get_channel_error(ChannelId channel_id, const Status &s telegram_api::channelForbidden update(flags, false /*ignored*/, false /*ignored*/, channel_id.get(), c->access_hash, c->title, 0); on_chat_update(update, "CHANNEL_PRIVATE"); - } else if (!c->username.empty()) { - LOG(INFO) << "Drop username of " << channel_id; - on_update_channel_username(c, channel_id, ""); - update_channel(c, channel_id); + } else { + if (!c->username.empty()) { + LOG(INFO) << "Drop username of " << channel_id; + on_update_channel_username(c, channel_id, ""); + update_channel(c, channel_id); + } + if (c->has_location) { + LOG(INFO) << "Drop location of " << channel_id; + c->has_location = false; + update_channel(c, channel_id); + } } - LOG_IF(ERROR, have_input_peer_channel(c, AccessRights::Read)) + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); + LOG_IF(ERROR, have_input_peer_channel(c, channel_id, AccessRights::Read)) << "Have read access to channel after receiving CHANNEL_PRIVATE. Channel state: " << oneline(to_string(get_supergroup_object(channel_id, c))) << ". Previous channel state: " << debug_channel_object; @@ -7690,7 +9458,12 @@ bool ContactsManager::is_user_contact(UserId user_id) const { } bool ContactsManager::is_user_contact(const User *u, UserId user_id) const { - return u != nullptr && u->outbound == LinkState::Contact && user_id != get_my_id(); + return u != nullptr && u->is_contact && user_id != get_my_id(); +} + +bool ContactsManager::is_user_blocked(UserId user_id) { + const UserFull *user_full = get_user_full_force(user_id); + return user_full != nullptr && user_full->is_blocked; } void ContactsManager::on_get_channel_participants_success( @@ -7734,41 +9507,44 @@ void ContactsManager::on_get_channel_participants_success( filter.is_recent() && total_count != 0 && total_count < max_participant_count ? total_count : -1; int32 administrator_count = filter.is_administrators() ? total_count : -1; if (is_full && (filter.is_administrators() || filter.is_bots() || filter.is_recent())) { - vector administrator_user_ids; + vector administrators; vector bot_user_ids; { - auto user_ids = transform(result, [](const DialogParticipant &participant) { return participant.user_id; }); if (filter.is_recent()) { for (const auto &participant : result) { if (participant.status.is_administrator()) { - administrator_user_ids.push_back(participant.user_id); + administrators.emplace_back(participant.user_id, participant.status.get_rank(), + participant.status.is_creator()); } if (is_user_bot(participant.user_id)) { bot_user_ids.push_back(participant.user_id); } } - administrator_count = narrow_cast(administrator_user_ids.size()); + administrator_count = narrow_cast(administrators.size()); if (get_channel_type(channel_id) == ChannelType::Megagroup && !td_->auth_manager_->is_bot()) { cached_channel_participants_[channel_id] = result; update_channel_online_member_count(channel_id, true); } } else if (filter.is_administrators()) { - administrator_user_ids = std::move(user_ids); + for (const auto &participant : result) { + administrators.emplace_back(participant.user_id, participant.status.get_rank(), + participant.status.is_creator()); + } } else if (filter.is_bots()) { - bot_user_ids = std::move(user_ids); + bot_user_ids = transform(result, [](const DialogParticipant &participant) { return participant.user_id; }); } } if (filter.is_administrators() || filter.is_recent()) { - on_update_dialog_administrators(DialogId(channel_id), std::move(administrator_user_ids), true); + on_update_dialog_administrators(DialogId(channel_id), std::move(administrators), true); } if (filter.is_bots() || filter.is_recent()) { - td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), std::move(bot_user_ids)); + on_update_channel_bot_user_ids(channel_id, std::move(bot_user_ids)); } } if (participant_count != -1 || administrator_count != -1) { - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full != nullptr) { if (participant_count != -1 && channel_full->participant_count != participant_count) { channel_full->participant_count = participant_count; @@ -7784,7 +9560,7 @@ void ContactsManager::on_get_channel_participants_success( auto c = get_channel(channel_id); if (c != nullptr && c->participant_count != participant_count) { c->participant_count = participant_count; - c->need_send_update = true; + c->is_changed = true; update_channel(c, channel_id); } } @@ -7819,6 +9595,7 @@ bool ContactsManager::speculative_add_count(int32 &count, int32 new_count) { void ContactsManager::speculative_add_channel_participants(ChannelId channel_id, const vector &added_user_ids, UserId inviter_user_id, int32 date, bool by_me) { auto it = cached_channel_participants_.find(channel_id); + auto channel_full = get_channel_full_force(channel_id); bool is_participants_cache_changed = false; int32 new_participant_count = 0; @@ -7843,10 +9620,18 @@ void ContactsManager::speculative_add_channel_participants(ChannelId channel_id, participants.emplace_back(user_id, inviter_user_id, date, DialogParticipantStatus::Member()); } } + + if (channel_full != nullptr && is_user_bot(user_id) && !td::contains(channel_full->bot_user_ids, user_id)) { + channel_full->bot_user_ids.push_back(user_id); + channel_full->need_save_to_database = true; + } } if (is_participants_cache_changed) { update_channel_online_member_count(channel_id, false); } + if (channel_full != nullptr) { + update_channel_full(channel_full, channel_id); + } if (new_participant_count == 0) { return; } @@ -7871,6 +9656,14 @@ void ContactsManager::speculative_delete_channel_participant(ChannelId channel_i } } + if (is_user_bot(deleted_user_id)) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full != nullptr && td::remove(channel_full->bot_user_ids, deleted_user_id)) { + channel_full->need_save_to_database = true; + update_channel_full(channel_full, channel_id); + } + } + speculative_add_channel_participants(channel_id, -1, by_me); } @@ -7878,51 +9671,72 @@ void ContactsManager::speculative_add_channel_participants(ChannelId channel_id, bool by_me) { if (by_me) { // Currently ignore all changes made by the current user, because they may be already counted - invalidate_channel_full(channel_id, false); // just in case + invalidate_channel_full(channel_id, false, false); // just in case return; } - auto c = get_channel(channel_id); + auto c = get_channel_force(channel_id); if (c != nullptr && c->participant_count != 0 && speculative_add_count(c->participant_count, new_participant_count)) { - c->need_send_update = true; + c->is_changed = true; update_channel(c, channel_id); } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { return; } channel_full->is_changed |= speculative_add_count(channel_full->participant_count, new_participant_count); + if (channel_full->is_changed) { + channel_full->speculative_version++; + } + update_channel_full(channel_full, channel_id); } void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId user_id, DialogParticipantStatus new_status, DialogParticipantStatus old_status) { - auto c = get_channel(channel_id); + auto c = get_channel_force(channel_id); if (c != nullptr && c->participant_count != 0 && speculative_add_count(c->participant_count, new_status.is_member() - old_status.is_member())) { - c->need_send_update = true; + c->is_changed = true; update_channel(c, channel_id); } - if (new_status.is_administrator() != old_status.is_administrator()) { + if (new_status.is_administrator() != old_status.is_administrator() || + new_status.get_rank() != old_status.get_rank()) { DialogId dialog_id(channel_id); auto administrators_it = dialog_administrators_.find(dialog_id); if (administrators_it != dialog_administrators_.end()) { - auto user_ids = administrators_it->second; - auto it = std::find(user_ids.begin(), user_ids.end(), user_id); - bool is_found = it != user_ids.end(); - - if (new_status.is_administrator() != is_found) { - if (is_found) { - user_ids.erase(it); - } else { - user_ids.push_back(user_id); + auto administrators = administrators_it->second; + if (new_status.is_administrator()) { + bool is_found = false; + for (auto &administrator : administrators) { + if (administrator.get_user_id() == user_id) { + is_found = true; + if (administrator.get_rank() != new_status.get_rank() || + administrator.is_creator() != new_status.is_creator()) { + administrator = DialogAdministrator(user_id, new_status.get_rank(), new_status.is_creator()); + on_update_dialog_administrators(dialog_id, std::move(administrators), true); + } + break; + } + } + if (!is_found) { + administrators.emplace_back(user_id, new_status.get_rank(), new_status.is_creator()); + on_update_dialog_administrators(dialog_id, std::move(administrators), true); + } + } else { + size_t i = 0; + while (i != administrators.size() && administrators[i].get_user_id() != user_id) { + i++; + } + if (i != administrators.size()) { + administrators.erase(administrators.begin() + i); + on_update_dialog_administrators(dialog_id, std::move(administrators), true); } - on_update_dialog_administrators(dialog_id, std::move(user_ids), true); } } } @@ -7949,7 +9763,7 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId } } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { return; } @@ -7963,18 +9777,41 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId channel_full->is_changed |= speculative_add_count(channel_full->banned_count, new_status.is_banned() - old_status.is_banned()); + if (channel_full->is_changed) { + channel_full->speculative_version++; + } + + if (new_status.is_member() != old_status.is_member() && is_user_bot(user_id)) { + if (new_status.is_member()) { + if (!td::contains(channel_full->bot_user_ids, user_id)) { + channel_full->bot_user_ids.push_back(user_id); + channel_full->need_save_to_database = true; + } + } else { + if (td::remove(channel_full->bot_user_ids, user_id)) { + channel_full->need_save_to_database = true; + } + } + } + update_channel_full(channel_full, channel_id); } -void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_invite_link) { - LOG(INFO) << "Invalidate channel full for " << channel_id; +void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay) { + LOG(INFO) << "Invalidate supergroup full for " << channel_id; // drop channel full cache - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full != nullptr) { channel_full->expires_at = 0.0; if (drop_invite_link) { on_update_channel_full_invite_link(channel_full, nullptr); } + if (drop_slow_mode_delay && channel_full->slow_mode_delay != 0) { + channel_full->slow_mode_delay = 0; + channel_full->slow_mode_next_send_date = 0; + channel_full->is_slow_mode_next_send_date_changed = true; + channel_full->is_changed = true; + } update_channel_full(channel_full, channel_id); } else if (drop_invite_link) { auto it = channel_invite_links_.find(channel_id); @@ -7987,12 +9824,12 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool drop_in void ContactsManager::on_get_chat_invite_link(ChatId chat_id, tl_object_ptr &&invite_link_ptr) { CHECK(chat_id.is_valid()); - if (!have_chat(chat_id)) { + if (!have_chat_force(chat_id)) { LOG(ERROR) << chat_id << " not found"; return; } - auto chat_full = get_chat_full(chat_id); + auto chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { update_invite_link(chat_invite_links_[chat_id], std::move(invite_link_ptr)); return; @@ -8017,7 +9854,7 @@ void ContactsManager::on_get_channel_invite_link(ChannelId channel_id, return; } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { update_invite_link(channel_invite_links_[channel_id], std::move(invite_link_ptr)); return; @@ -8034,6 +9871,156 @@ void ContactsManager::on_update_channel_full_invite_link( } } +void ContactsManager::remove_linked_channel_id(ChannelId channel_id) { + if (!channel_id.is_valid()) { + return; + } + + auto it = linked_channel_ids_.find(channel_id); + if (it != linked_channel_ids_.end()) { + auto linked_channel_id = it->second; + linked_channel_ids_.erase(it); + linked_channel_ids_.erase(linked_channel_id); + } +} + +ChannelId ContactsManager::get_linked_channel_id(ChannelId channel_id) const { + auto channel_full = get_channel_full(channel_id); + if (channel_full != nullptr) { + return channel_full->linked_channel_id; + } + + auto it = linked_channel_ids_.find(channel_id); + if (it != linked_channel_ids_.end()) { + return it->second; + } + + return ChannelId(); +} + +void ContactsManager::on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id, + ChannelId linked_channel_id) { + remove_linked_channel_id(channel_id); + remove_linked_channel_id(linked_channel_id); + if (channel_id.is_valid() && linked_channel_id.is_valid()) { + linked_channel_ids_[channel_id] = linked_channel_id; + linked_channel_ids_[linked_channel_id] = channel_id; + } + + if (channel_full != nullptr && channel_full->linked_channel_id != linked_channel_id) { + if (channel_full->linked_channel_id.is_valid()) { + // remove link from a previously linked channel_full + auto linked_channel = get_channel_force(channel_full->linked_channel_id); + if (linked_channel != nullptr && linked_channel->has_linked_channel) { + linked_channel->has_linked_channel = false; + linked_channel->is_changed = true; + update_channel(linked_channel, channel_full->linked_channel_id); + reload_channel(channel_full->linked_channel_id, Auto()); + } + auto linked_channel_full = get_channel_full_force(channel_full->linked_channel_id); + if (linked_channel_full != nullptr && linked_channel_full->linked_channel_id == channel_id) { + linked_channel_full->linked_channel_id = ChannelId(); + linked_channel_full->is_changed = true; + update_channel_full(linked_channel_full, channel_full->linked_channel_id); + } + } + + channel_full->linked_channel_id = linked_channel_id; + channel_full->is_changed = true; + + if (channel_full->linked_channel_id.is_valid()) { + // add link from a newly linked channel_full + auto linked_channel = get_channel_force(channel_full->linked_channel_id); + if (linked_channel != nullptr && !linked_channel->has_linked_channel) { + linked_channel->has_linked_channel = true; + linked_channel->is_changed = true; + update_channel(linked_channel, channel_full->linked_channel_id); + reload_channel(channel_full->linked_channel_id, Auto()); + } + auto linked_channel_full = get_channel_full_force(channel_full->linked_channel_id); + if (linked_channel_full != nullptr && linked_channel_full->linked_channel_id != channel_id) { + linked_channel_full->linked_channel_id = channel_id; + linked_channel_full->is_changed = true; + update_channel_full(linked_channel_full, channel_full->linked_channel_id); + } + } + } + + Channel *c = get_channel(channel_id); + CHECK(c != nullptr); + if (linked_channel_id.is_valid() != c->has_linked_channel) { + c->has_linked_channel = linked_channel_id.is_valid(); + c->is_changed = true; + update_channel(c, channel_id); + } +} + +void ContactsManager::on_update_channel_full_location(ChannelFull *channel_full, ChannelId channel_id, + const DialogLocation &location) { + if (channel_full->location != location) { + channel_full->location = location; + channel_full->is_changed = true; + } + + Channel *c = get_channel(channel_id); + CHECK(c != nullptr); + if (location.empty() == c->has_location) { + c->has_location = !location.empty(); + c->is_changed = true; + update_channel(c, channel_id); + } +} + +void ContactsManager::on_update_channel_full_slow_mode_delay(ChannelFull *channel_full, ChannelId channel_id, + int32 slow_mode_delay, int32 slow_mode_next_send_date) { + if (slow_mode_delay < 0) { + LOG(ERROR) << "Receive slow mode delay " << slow_mode_delay << " in " << channel_id; + slow_mode_delay = 0; + } + + if (channel_full->slow_mode_delay != slow_mode_delay) { + channel_full->slow_mode_delay = slow_mode_delay; + channel_full->is_changed = true; + } + on_update_channel_full_slow_mode_next_send_date(channel_full, slow_mode_next_send_date); + + Channel *c = get_channel(channel_id); + CHECK(c != nullptr); + bool is_slow_mode_enabled = slow_mode_delay != 0; + if (is_slow_mode_enabled != c->is_slow_mode_enabled) { + c->is_slow_mode_enabled = is_slow_mode_enabled; + c->is_changed = true; + update_channel(c, channel_id); + } +} + +void ContactsManager::on_update_channel_full_slow_mode_next_send_date(ChannelFull *channel_full, + int32 slow_mode_next_send_date) { + if (slow_mode_next_send_date < 0) { + LOG(ERROR) << "Receive slow mode next send date " << slow_mode_next_send_date; + slow_mode_next_send_date = 0; + } + if (channel_full->slow_mode_delay == 0 && slow_mode_next_send_date > 0) { + LOG(ERROR) << "Slow mode is disabled, but next send date is " << slow_mode_next_send_date; + slow_mode_next_send_date = 0; + } + + if (slow_mode_next_send_date != 0) { + auto now = G()->unix_time(); + if (slow_mode_next_send_date <= now) { + slow_mode_next_send_date = 0; + } + if (slow_mode_next_send_date > now + 3601) { + slow_mode_next_send_date = now + 3601; + } + } + if (channel_full->slow_mode_next_send_date != slow_mode_next_send_date) { + channel_full->slow_mode_next_send_date = slow_mode_next_send_date; + channel_full->is_slow_mode_next_send_date_changed = true; + channel_full->is_changed = true; + } +} + void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, tl_object_ptr &&chat_invite_ptr) { auto &invite_link_info = invite_link_infos_[invite_link]; @@ -8058,6 +10045,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, on_get_chat(std::move(chat_invite_already->chat_), "chatInviteAlready"); CHECK(chat_id == ChatId() || channel_id == ChannelId()); + CHECK(invite_link_info != nullptr); invite_link_info->chat_id = chat_id; invite_link_info->channel_id = channel_id; @@ -8071,6 +10059,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, } case telegram_api::chatInvite::ID: { auto chat_invite = move_tl_object_as(chat_invite_ptr); + CHECK(invite_link_info != nullptr); invite_link_info->chat_id = ChatId(); invite_link_info->channel_id = ChannelId(); invite_link_info->title = chat_invite->title_; @@ -8174,7 +10163,7 @@ void ContactsManager::invalidate_invite_link_info(const string &invite_link) { } void ContactsManager::repair_chat_participants(ChatId chat_id) { - send_get_chat_full_query(chat_id, Auto()); + send_get_chat_full_query(chat_id, Auto(), "repair_chat_participants"); } void ContactsManager::on_update_chat_add_user(ChatId chat_id, UserId inviter_user_id, UserId user_id, int32 date, @@ -8194,7 +10183,7 @@ void ContactsManager::on_update_chat_add_user(ChatId chat_id, UserId inviter_use LOG(INFO) << "Receive updateChatParticipantAdd to " << chat_id << " with " << user_id << " invited by " << inviter_user_id << " at " << date << " with version " << version; - ChatFull *chat_full = get_chat_full(chat_id); + ChatFull *chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { LOG(INFO) << "Ignoring update about members of " << chat_id; return; @@ -8230,7 +10219,7 @@ void ContactsManager::on_update_chat_add_user(ChatId chat_id, UserId inviter_use } chat_full->participants.push_back(DialogParticipant{user_id, inviter_user_id, date, user_id == chat_full->creator_user_id - ? DialogParticipantStatus::Creator(true) + ? DialogParticipantStatus::Creator(true, string()) : DialogParticipantStatus::Member()}); update_chat_online_member_count(chat_full, chat_id, false); chat_full->is_changed = true; @@ -8290,7 +10279,7 @@ void ContactsManager::on_update_chat_edit_administrator(ChatId chat_id, UserId u } c->version = version; - c->is_changed = true; + c->need_save_to_database = true; if (user_id == get_my_id() && !c->status.is_creator()) { // if chat with version was already received, then the update is already processed // so we need to call on_update_chat_status only if version > c->version @@ -8299,7 +10288,7 @@ void ContactsManager::on_update_chat_edit_administrator(ChatId chat_id, UserId u update_chat(c, chat_id); } - ChatFull *chat_full = get_chat_full(chat_id); + ChatFull *chat_full = get_chat_full_force(chat_id); if (chat_full != nullptr) { if (chat_full->version + 1 == version) { for (auto &participant : chat_full->participants) { @@ -8329,7 +10318,7 @@ void ContactsManager::on_update_chat_delete_user(ChatId chat_id, UserId user_id, LOG(INFO) << "Receive updateChatParticipantDelete from " << chat_id << " with " << user_id << " and version " << version; - ChatFull *chat_full = get_chat_full(chat_id); + ChatFull *chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { LOG(INFO) << "Ignoring update about members of " << chat_id; return; @@ -8385,7 +10374,7 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti c->default_permissions_version = -1; c->pinned_message_version = -1; - invalidate_chat_full(chat_id); + drop_chat_full(chat_id); } if (drop_invite_link) { auto it = chat_invite_links_.find(chat_id); @@ -8394,7 +10383,7 @@ void ContactsManager::on_update_chat_status(Chat *c, ChatId chat_id, DialogParti } } - c->need_send_update = true; + c->is_changed = true; } } @@ -8441,7 +10430,7 @@ void ContactsManager::on_update_chat_default_permissions(ChatId chat_id, Restric << " and default_permissions = " << default_permissions << ", but default_permissions are not changed. Current version is " << c->version; c->version = version; - c->is_changed = true; + c->need_save_to_database = true; on_update_chat_default_permissions(c, chat_id, default_permissions, version); update_chat(c, chat_id); } @@ -8455,7 +10444,7 @@ void ContactsManager::on_update_chat_default_permissions(Chat *c, ChatId chat_id c->default_permissions = default_permissions; c->default_permissions_version = version; c->is_default_permissions_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } @@ -8491,14 +10480,14 @@ void ContactsManager::on_update_chat_pinned_message(ChatId chat_id, MessageId pi repair_chat_participants(chat_id); } else if (version == c->version + 1) { c->version = version; - c->is_changed = true; + c->need_save_to_database = true; } td_->messages_manager_->on_update_dialog_pinned_message_id(DialogId(chat_id), pinned_message_id); if (version > c->pinned_message_version) { LOG(INFO) << "Change pinned message version of " << chat_id << " from " << c->pinned_message_version << " to " << version; c->pinned_message_version = version; - c->is_changed = true; + c->need_save_to_database = true; } update_chat(c, chat_id); } @@ -8529,13 +10518,13 @@ void ContactsManager::on_update_chat_participant_count(Chat *c, ChatId chat_id, c->participant_count = participant_count; c->version = version; - c->need_send_update = true; + c->is_changed = true; return; } if (version > c->version) { c->version = version; - c->is_changed = true; + c->need_save_to_database = true; } } @@ -8552,7 +10541,7 @@ void ContactsManager::on_update_chat_photo(Chat *c, ChatId chat_id, } c->photo = new_chat_photo; c->is_photo_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } @@ -8560,14 +10549,15 @@ void ContactsManager::on_update_chat_title(Chat *c, ChatId chat_id, string &&tit if (c->title != title) { c->title = std::move(title); c->is_title_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } void ContactsManager::on_update_chat_active(Chat *c, ChatId chat_id, bool is_active) { if (c->is_active != is_active) { c->is_active = is_active; - c->need_send_update = true; + c->is_is_active_changed = true; + c->is_changed = true; } } @@ -8577,7 +10567,7 @@ void ContactsManager::on_update_chat_migrated_to_channel_id(Chat *c, ChatId chat << "Upgraded supergroup ID for " << chat_id << " has changed from " << c->migrated_to_channel_id << " to " << migrated_to_channel_id; c->migrated_to_channel_id = migrated_to_channel_id; - c->need_send_update = true; + c->is_changed = true; } } @@ -8587,7 +10577,7 @@ void ContactsManager::on_update_chat_description(ChatId chat_id, string &&descri return; } - auto chat_full = get_chat_full(chat_id); + auto chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { return; } @@ -8647,8 +10637,8 @@ void ContactsManager::on_update_chat_full_participants(ChatFull *chat_full, Chat update_chat_online_member_count(chat_full, chat_id, true); } -void ContactsManager::invalidate_chat_full(ChatId chat_id) { - ChatFull *chat_full = get_chat_full(chat_id); +void ContactsManager::drop_chat_full(ChatId chat_id) { + ChatFull *chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { auto it = chat_invite_links_.find(chat_id); if (it != chat_invite_links_.end()) { @@ -8657,7 +10647,7 @@ void ContactsManager::invalidate_chat_full(ChatId chat_id) { return; } - LOG(INFO) << "Invalidate groupFull of " << chat_id; + LOG(INFO) << "Drop basicGroupFullInfo of " << chat_id; //chat_full->creator_user_id = UserId(); chat_full->participants.clear(); chat_full->version = -1; @@ -8680,7 +10670,7 @@ void ContactsManager::on_update_channel_photo(Channel *c, ChannelId channel_id, } c->photo = new_chat_photo; c->is_photo_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } @@ -8688,19 +10678,33 @@ void ContactsManager::on_update_channel_title(Channel *c, ChannelId channel_id, if (c->title != title) { c->title = std::move(title); c->is_title_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } void ContactsManager::on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status) { if (c->status != status) { LOG(INFO) << "Update " << channel_id << " status from " << c->status << " to " << status; + bool is_ownership_transferred = c->status.is_creator() != status.is_creator(); bool drop_invite_link = c->status.is_administrator() != status.is_administrator() || c->status.is_member() != status.is_member(); c->status = status; c->is_status_changed = true; - c->need_send_update = true; - invalidate_channel_full(channel_id, drop_invite_link); + c->is_changed = true; + invalidate_channel_full(channel_id, drop_invite_link, !c->is_slow_mode_enabled); + if (is_ownership_transferred) { + for (size_t i = 0; i < 2; i++) { + created_public_channels_inited_[i] = false; + created_public_channels_[i].clear(); + } + + auto input_channel = get_input_channel(channel_id); + if (input_channel != nullptr) { + send_get_channel_full_query(nullptr, channel_id, std::move(input_channel), Auto(), "update channel owner"); + } + + reload_dialog_administrators(DialogId(channel_id), 0, Auto()); + } } } @@ -8711,7 +10715,7 @@ void ContactsManager::on_update_channel_default_permissions(Channel *c, ChannelI << default_permissions; c->default_permissions = default_permissions; c->is_default_permissions_changed = true; - c->is_changed = true; + c->need_save_to_database = true; } } @@ -8735,12 +10739,12 @@ void ContactsManager::on_update_channel_username(Channel *c, ChannelId channel_i if (c->username != username) { if (c->username.empty() || username.empty()) { // moving channel from private to public can change availability of chat members - invalidate_channel_full(channel_id, true); + invalidate_channel_full(channel_id, true, !c->is_slow_mode_enabled); } c->username = std::move(username); c->is_username_changed = true; - c->need_send_update = true; + c->is_changed = true; } } @@ -8750,7 +10754,7 @@ void ContactsManager::on_update_channel_description(ChannelId channel_id, string return; } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { return; } @@ -8761,13 +10765,13 @@ void ContactsManager::on_update_channel_description(ChannelId channel_id, string } } -void ContactsManager::on_update_channel_sticker_set(ChannelId channel_id, int64 sticker_set_id) { +void ContactsManager::on_update_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id) { if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; return; } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { return; } @@ -8778,13 +10782,80 @@ void ContactsManager::on_update_channel_sticker_set(ChannelId channel_id, int64 } } +void ContactsManager::on_update_channel_linked_channel_id(ChannelId channel_id, ChannelId group_channel_id) { + if (channel_id.is_valid()) { + auto channel_full = get_channel_full_force(channel_id); + on_update_channel_full_linked_channel_id(channel_full, channel_id, group_channel_id); + if (channel_full != nullptr) { + update_channel_full(channel_full, channel_id); + } + } + if (group_channel_id.is_valid()) { + auto channel_full = get_channel_full_force(group_channel_id); + on_update_channel_full_linked_channel_id(channel_full, group_channel_id, channel_id); + if (channel_full != nullptr) { + update_channel_full(channel_full, group_channel_id); + } + } +} + +void ContactsManager::on_update_channel_location(ChannelId channel_id, const DialogLocation &location) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full != nullptr) { + on_update_channel_full_location(channel_full, channel_id, location); + update_channel_full(channel_full, channel_id); + } +} + +void ContactsManager::on_update_channel_slow_mode_delay(ChannelId channel_id, int32 slow_mode_delay) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full != nullptr) { + on_update_channel_full_slow_mode_delay(channel_full, channel_id, slow_mode_delay, 0); + update_channel_full(channel_full, channel_id); + } +} + +void ContactsManager::on_update_channel_slow_mode_next_send_date(ChannelId channel_id, int32 slow_mode_next_send_date) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full != nullptr) { + on_update_channel_full_slow_mode_next_send_date(channel_full, slow_mode_next_send_date); + update_channel_full(channel_full, channel_id); + } +} + +void ContactsManager::on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids) { + CHECK(channel_id.is_valid()); + if (!have_channel(channel_id)) { + LOG(ERROR) << channel_id << " not found"; + return; + } + + auto channel_full = get_channel_full_force(channel_id); + if (channel_full == nullptr) { + td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), std::move(bot_user_ids)); + return; + } + on_update_channel_full_bot_user_ids(channel_full, channel_id, std::move(bot_user_ids)); + update_channel_full(channel_full, channel_id); +} + +void ContactsManager::on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id, + vector &&bot_user_ids) { + CHECK(channel_full != nullptr); + if (channel_full->bot_user_ids != bot_user_ids) { + td_->messages_manager_->on_dialog_bots_updated(DialogId(channel_id), bot_user_ids); + channel_full->bot_user_ids = std::move(bot_user_ids); + channel_full->need_save_to_database = true; + } +} + void ContactsManager::on_update_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available) { if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; return; } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { return; } @@ -8869,7 +10940,7 @@ Result ContactsManager::get_bot_data(UserId user_id) const { return Status::Error(5, "Bot not found"); } - auto bot = &p->second; + auto bot = p->second.get(); if (!bot->is_bot) { return Status::Error(5, "User is not a bot"); } @@ -8889,6 +10960,11 @@ Result ContactsManager::get_bot_data(UserId user_id) const { return bot_data; } +bool ContactsManager::is_user_status_exact(UserId user_id) const { + auto u = get_user(user_id); + return u != nullptr && !u->is_deleted && !u->is_bot && u->was_online > 0; +} + 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; @@ -8899,7 +10975,7 @@ const ContactsManager::User *ContactsManager::get_user(UserId user_id) const { if (p == users_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -8908,7 +10984,7 @@ ContactsManager::User *ContactsManager::get_user(UserId user_id) { if (p == users_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -8944,7 +11020,7 @@ UserId ContactsManager::get_me(Promise &&promise) { bool ContactsManager::get_user(UserId user_id, int left_tries, Promise &&promise) { if (!user_id.is_valid()) { - promise.set_error(Status::Error(6, "Invalid user id")); + promise.set_error(Status::Error(6, "Invalid user ID")); return false; } @@ -8978,7 +11054,11 @@ bool ContactsManager::get_user(UserId user_id, int left_tries, Promise &&p ContactsManager::User *ContactsManager::add_user(UserId user_id, const char *source) { CHECK(user_id.is_valid()); - return &users_[user_id]; + auto &user_ptr = users_[user_id]; + if (user_ptr == nullptr) { + user_ptr = make_unique(); + } + return user_ptr.get(); } const ContactsManager::UserFull *ContactsManager::get_user_full(UserId user_id) const { @@ -8986,7 +11066,7 @@ const ContactsManager::UserFull *ContactsManager::get_user_full(UserId user_id) if (p == users_full_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -8995,10 +11075,20 @@ ContactsManager::UserFull *ContactsManager::get_user_full(UserId user_id) { if (p == users_full_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } +ContactsManager::UserFull *ContactsManager::add_user_full(UserId user_id) { + CHECK(user_id.is_valid()); + auto &user_full_ptr = users_full_[user_id]; + if (user_full_ptr == nullptr) { + user_full_ptr = make_unique(); + user_full_ptr->can_pin_messages = (user_id == get_my_id()); + } + return user_full_ptr.get(); +} + void ContactsManager::reload_user(UserId user_id, Promise &&promise) { if (!user_id.is_valid()) { return promise.set_error(Status::Error(6, "Invalid user id")); @@ -9017,31 +11107,31 @@ void ContactsManager::reload_user(UserId user_id, Promise &&promise) { } bool ContactsManager::get_user_full(UserId user_id, Promise &&promise) { - auto user = get_user(user_id); - if (user == nullptr) { + auto u = get_user(user_id); + if (u == nullptr) { promise.set_error(Status::Error(6, "User not found")); return false; } - auto user_full = get_user_full(user_id); - if (user_full == nullptr || !user_full->is_inited) { + auto user_full = get_user_full_force(user_id); + if (user_full == nullptr) { auto input_user = get_input_user(user_id); if (input_user == nullptr) { promise.set_error(Status::Error(6, "Can't get info about inaccessible user")); return false; } - send_get_user_full_query(user_id, std::move(input_user), std::move(promise)); + send_get_user_full_query(user_id, std::move(input_user), std::move(promise), "get_user_full"); return false; } - if (user_full->is_expired() || user_full->is_bot_info_expired(user->bot_info_version)) { + 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()) { - send_get_user_full_query(user_id, std::move(input_user), std::move(promise)); + send_get_user_full_query(user_id, std::move(input_user), std::move(promise), "get expired user_full"); return false; } else { - send_get_user_full_query(user_id, std::move(input_user), Auto()); + send_get_user_full_query(user_id, std::move(input_user), Auto(), "get expired user_full"); } } @@ -9049,8 +11139,16 @@ bool ContactsManager::get_user_full(UserId user_id, Promise &&promise) { return true; } +void ContactsManager::reload_user_full(UserId user_id) { + auto input_user = get_input_user(user_id); + if (input_user != nullptr) { + send_get_user_full_query(user_id, std::move(input_user), Auto(), "reload_user_full"); + } +} + void ContactsManager::send_get_user_full_query(UserId user_id, tl_object_ptr &&input_user, - Promise &&promise) { + Promise &&promise, const char *source) { + LOG(INFO) << "Get full " << user_id << " from " << source; auto send_query = PromiseCreator::lambda([td = td_, input_user = std::move(input_user)](Result> &&promise) mutable { if (promise.is_ok()) { @@ -9060,6 +11158,33 @@ void ContactsManager::send_get_user_full_query(UserId user_id, tl_object_ptrsecond.get(); + } +} + +ContactsManager::BotInfo *ContactsManager::get_bot_info(UserId user_id) { + auto p = bot_infos_.find(user_id); + if (p == bot_infos_.end()) { + return nullptr; + } else { + return p->second.get(); + } +} + +ContactsManager::BotInfo *ContactsManager::add_bot_info(UserId user_id) { + CHECK(user_id.is_valid()); + auto &bot_info_ptr = bot_infos_[user_id]; + if (bot_info_ptr == nullptr) { + bot_info_ptr = make_unique(); + } + return bot_info_ptr.get(); +} + std::pair> ContactsManager::get_user_profile_photos(UserId user_id, int32 offset, int32 limit, Promise &&promise) { std::pair> result; @@ -9083,32 +11208,32 @@ std::pair> ContactsManager::get_user_profile_photos return result; } - auto user_full = &users_full_[user_id]; - if (user_full->getting_photos_now) { + 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")); return result; } - if (user_full->photo_count != -1) { // know photo count - CHECK(user_full->photos_offset != -1); - result.first = user_full->photo_count; + if (user_photos->count != -1) { // know photo count + CHECK(user_photos->offset != -1); + result.first = user_photos->count; - if (offset >= user_full->photo_count) { + if (offset >= user_photos->count) { // offset if too big promise.set_value(Unit()); return result; } - if (limit > user_full->photo_count - offset) { - limit = user_full->photo_count - offset; + if (limit > user_photos->count - offset) { + limit = user_photos->count - offset; } - int32 cache_begin = user_full->photos_offset; - int32 cache_end = cache_begin + narrow_cast(user_full->photos.size()); + int32 cache_begin = user_photos->offset; + int32 cache_end = cache_begin + narrow_cast(user_photos->photos.size()); if (cache_begin <= offset && offset + limit <= cache_end) { // answer query from cache for (int i = 0; i < limit; i++) { - result.second.push_back(&user_full->photos[i + offset - cache_begin]); + result.second.push_back(&user_photos->photos[i + offset - cache_begin]); } promise.set_value(Unit()); return result; @@ -9121,7 +11246,7 @@ std::pair> ContactsManager::get_user_profile_photos } } - user_full->getting_photos_now = true; + user_photos->getting_now = true; if (limit < MAX_GET_PROFILE_PHOTOS / 5) { limit = MAX_GET_PROFILE_PHOTOS / 5; // make limit reasonable @@ -9168,7 +11293,7 @@ const ContactsManager::Chat *ContactsManager::get_chat(ChatId chat_id) const { if (p == chats_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -9177,25 +11302,23 @@ ContactsManager::Chat *ContactsManager::get_chat(ChatId chat_id) { if (p == chats_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } ContactsManager::Chat *ContactsManager::add_chat(ChatId chat_id) { - auto c = get_chat(chat_id); - if (c != nullptr) { - return c; - } - CHECK(chat_id.is_valid()); - c = &chats_[chat_id]; - auto it = chat_photo_file_source_ids_.find(chat_id); - if (it != chat_photo_file_source_ids_.end()) { - VLOG(file_references) << "Move " << it->second << " inside of " << chat_id; - c->photo_source_id = it->second; - chat_photo_file_source_ids_.erase(it); + auto &chat_ptr = chats_[chat_id]; + if (chat_ptr == nullptr) { + chat_ptr = make_unique(); + auto it = chat_photo_file_source_ids_.find(chat_id); + if (it != chat_photo_file_source_ids_.end()) { + VLOG(file_references) << "Move " << it->second << " inside of " << chat_id; + chat_ptr->photo_source_id = it->second; + chat_photo_file_source_ids_.erase(it); + } } - return c; + return chat_ptr.get(); } bool ContactsManager::get_chat(ChatId chat_id, int left_tries, Promise &&promise) { @@ -9238,7 +11361,7 @@ const ContactsManager::ChatFull *ContactsManager::get_chat_full(ChatId chat_id) if (p == chats_full_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -9247,11 +11370,20 @@ ContactsManager::ChatFull *ContactsManager::get_chat_full(ChatId chat_id) { if (p == chats_full_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } -bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id) const { +ContactsManager::ChatFull *ContactsManager::add_chat_full(ChatId chat_id) { + CHECK(chat_id.is_valid()); + auto &chat_full_ptr = chats_full_[chat_id]; + if (chat_full_ptr == nullptr) { + chat_full_ptr = make_unique(); + } + return chat_full_ptr.get(); +} + +bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id) { CHECK(c != nullptr); CHECK(chat_full != nullptr); if (!c->is_active && chat_full->version == -1) { @@ -9265,15 +11397,10 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha } for (const auto &participant : chat_full->participants) { - auto user = get_user(participant.user_id); - if (user != nullptr && user->bot_info_version != -1) { - auto user_full = get_user_full(participant.user_id); - if (user_full == nullptr || user_full->is_bot_info_expired(user->bot_info_version)) { - LOG(INFO) << "Have outdated botInfo for " << participant.user_id << " with version " - << (user_full && user_full->bot_info ? user_full->bot_info->version : -123456789) - << ", but current version is " << user->bot_info_version; - return true; - } + auto u = get_user(participant.user_id); + if (u != nullptr && is_bot_info_expired(participant.user_id, u->bot_info_version)) { + LOG(INFO) << "Have outdated botInfo for " << participant.user_id << ", expected version " << u->bot_info_version; + return true; } } @@ -9281,26 +11408,26 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha } bool ContactsManager::get_chat_full(ChatId chat_id, Promise &&promise) { - auto chat = get_chat(chat_id); - if (chat == nullptr) { + auto c = get_chat(chat_id); + if (c == nullptr) { promise.set_error(Status::Error(6, "Group not found")); return false; } - auto chat_full = get_chat_full(chat_id); + auto chat_full = get_chat_full_force(chat_id); if (chat_full == nullptr) { LOG(INFO) << "Full " << chat_id << " not found"; - send_get_chat_full_query(chat_id, std::move(promise)); + send_get_chat_full_query(chat_id, std::move(promise), "get_chat_full"); return false; } - if (is_chat_full_outdated(chat_full, chat, chat_id)) { + if (is_chat_full_outdated(chat_full, c, chat_id)) { LOG(INFO) << "Have outdated full " << chat_id; if (td_->auth_manager_->is_bot()) { - send_get_chat_full_query(chat_id, std::move(promise)); + send_get_chat_full_query(chat_id, std::move(promise), "get expired chat_full"); return false; } else { - send_get_chat_full_query(chat_id, Auto()); + send_get_chat_full_query(chat_id, Auto(), "get expired chat_full"); } } @@ -9308,7 +11435,8 @@ bool ContactsManager::get_chat_full(ChatId chat_id, Promise &&promise) { return true; } -void ContactsManager::send_get_chat_full_query(ChatId chat_id, Promise &&promise) { +void ContactsManager::send_get_chat_full_query(ChatId chat_id, Promise &&promise, const char *source) { + LOG(INFO) << "Get full " << chat_id << " from " << source; auto send_query = PromiseCreator::lambda([td = td_, chat_id](Result> &&promise) { if (promise.is_ok()) { td->create_handler(promise.move_as_ok())->send(chat_id); @@ -9373,6 +11501,14 @@ FileSourceId ContactsManager::get_chat_photo_file_source_id(ChatId chat_id) { return source_id; } +bool ContactsManager::is_channel_public(ChannelId channel_id) const { + return is_channel_public(get_channel(channel_id)); +} + +bool ContactsManager::is_channel_public(const Channel *c) { + return c != nullptr && (!c->username.empty() || c->has_location); +} + ChannelType ContactsManager::get_channel_type(ChannelId channel_id) const { auto c = get_channel(channel_id); if (c == nullptr) { @@ -9455,6 +11591,14 @@ FileSourceId ContactsManager::get_channel_photo_file_source_id(ChannelId channel return source_id; } +int32 ContactsManager::get_channel_slow_mode_delay(ChannelId channel_id) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full == nullptr) { + return 0; + } + return channel_full->slow_mode_delay; +} + bool ContactsManager::have_channel(ChannelId channel_id) const { return channels_.count(channel_id) > 0; } @@ -9468,7 +11612,7 @@ const ContactsManager::Channel *ContactsManager::get_channel(ChannelId channel_i if (p == channels_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } @@ -9477,25 +11621,23 @@ ContactsManager::Channel *ContactsManager::get_channel(ChannelId channel_id) { if (p == channels_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } ContactsManager::Channel *ContactsManager::add_channel(ChannelId channel_id, const char *source) { - auto c = get_channel(channel_id); - if (c != nullptr) { - return c; - } - CHECK(channel_id.is_valid()); - c = &channels_[channel_id]; - auto it = channel_photo_file_source_ids_.find(channel_id); - if (it != channel_photo_file_source_ids_.end()) { - VLOG(file_references) << "Move " << it->second << " inside of " << channel_id; - c->photo_source_id = it->second; - channel_photo_file_source_ids_.erase(it); + auto &channel_ptr = channels_[channel_id]; + if (channel_ptr == nullptr) { + channel_ptr = make_unique(); + auto it = channel_photo_file_source_ids_.find(channel_id); + if (it != channel_photo_file_source_ids_.end()) { + VLOG(file_references) << "Move " << it->second << " inside of " << channel_id; + channel_ptr->photo_source_id = it->second; + channel_photo_file_source_ids_.erase(it); + } } - return c; + return channel_ptr.get(); } bool ContactsManager::get_channel(ChannelId channel_id, int left_tries, Promise &&promise) { @@ -9545,28 +11687,37 @@ const ContactsManager::ChannelFull *ContactsManager::get_channel_full(ChannelId if (p == channels_full_.end()) { return nullptr; } else { - return &p->second; + return p->second.get(); } } -ContactsManager::ChannelFull *ContactsManager::get_channel_full(ChannelId channel_id) { +ContactsManager::ChannelFull *ContactsManager::get_channel_full(ChannelId channel_id, const char *source) { auto p = channels_full_.find(channel_id); if (p == channels_full_.end()) { return nullptr; } - auto channel_full = &p->second; + auto channel_full = p->second.get(); if (channel_full->is_expired() && !td_->auth_manager_->is_bot()) { auto input_channel = get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_get_channel_full_query(channel_id, std::move(input_channel), Auto()); + send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), Auto(), source); } return channel_full; } +ContactsManager::ChannelFull *ContactsManager::add_channel_full(ChannelId channel_id) { + CHECK(channel_id.is_valid()); + auto &channel_full_ptr = channels_full_[channel_id]; + if (channel_full_ptr == nullptr) { + channel_full_ptr = make_unique(); + } + return channel_full_ptr.get(); +} + bool ContactsManager::get_channel_full(ChannelId channel_id, Promise &&promise) { - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr) { auto input_channel = get_input_channel(channel_id); if (input_channel == nullptr) { @@ -9574,18 +11725,19 @@ bool ContactsManager::get_channel_full(ChannelId channel_id, Promise &&pro return false; } - send_get_channel_full_query(channel_id, std::move(input_channel), std::move(promise)); + send_get_channel_full_query(nullptr, channel_id, std::move(input_channel), std::move(promise), "get channel_full"); return false; } if (channel_full->is_expired()) { if (td_->auth_manager_->is_bot()) { auto input_channel = get_input_channel(channel_id); CHECK(input_channel != nullptr); - send_get_channel_full_query(channel_id, std::move(input_channel), std::move(promise)); + send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), std::move(promise), + "get expired channel_full"); return false; } else { - // request has already been sent in get_channel_full - // send_get_channel_full_query(channel_id, std::move(input_channel), Auto()); + // request has already been sent in get_channel_full_force + // send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), Auto(), "get expired channel_full"); } } @@ -9593,9 +11745,22 @@ bool ContactsManager::get_channel_full(ChannelId channel_id, Promise &&pro return true; } -void ContactsManager::send_get_channel_full_query(ChannelId channel_id, +void ContactsManager::send_get_channel_full_query(ChannelFull *channel_full, ChannelId channel_id, tl_object_ptr &&input_channel, - Promise &&promise) { + Promise &&promise, const char *source) { + if (channel_full != nullptr) { + if (!promise) { + if (channel_full->repair_request_version != 0) { + LOG(INFO) << "Skip get full " << channel_id << " request from " << source; + return; + } + channel_full->repair_request_version = channel_full->speculative_version; + } else { + channel_full->repair_request_version = std::numeric_limits::max(); + } + } + + LOG(INFO) << "Get full " << channel_id << " from " << source; auto send_query = PromiseCreator::lambda( [td = td_, channel_id, input_channel = std::move(input_channel)](Result> &&promise) mutable { if (promise.is_ok()) { @@ -9611,7 +11776,11 @@ bool ContactsManager::have_secret_chat(SecretChatId secret_chat_id) const { ContactsManager::SecretChat *ContactsManager::add_secret_chat(SecretChatId secret_chat_id) { CHECK(secret_chat_id.is_valid()); - return &secret_chats_[secret_chat_id]; + auto &secret_chat_ptr = secret_chats_[secret_chat_id]; + if (secret_chat_ptr == nullptr) { + secret_chat_ptr = make_unique(); + } + return secret_chat_ptr.get(); } const ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secret_chat_id) const { @@ -9619,7 +11788,7 @@ const ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId if (it == secret_chats_.end()) { return nullptr; } - return &it->second; + return it->second.get(); } ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secret_chat_id) { @@ -9627,7 +11796,7 @@ ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secre if (it == secret_chats_.end()) { return nullptr; } - return &it->second; + return it->second.get(); } bool ContactsManager::get_secret_chat(SecretChatId secret_chat_id, bool force, Promise &&promise) { @@ -9658,44 +11827,43 @@ void ContactsManager::on_update_secret_chat(SecretChatId secret_chat_id, int64 a auto *secret_chat = add_secret_chat(secret_chat_id); if (access_hash != secret_chat->access_hash) { secret_chat->access_hash = access_hash; - secret_chat->is_changed = true; + secret_chat->need_save_to_database = true; } if (user_id.is_valid() && user_id != secret_chat->user_id) { if (secret_chat->user_id.is_valid()) { LOG(ERROR) << "Secret chat user has changed from " << secret_chat->user_id << " to " << user_id; auto &old_secret_chat_ids = secret_chats_with_user_[secret_chat->user_id]; - old_secret_chat_ids.erase(std::remove(old_secret_chat_ids.begin(), old_secret_chat_ids.end(), secret_chat_id), - old_secret_chat_ids.end()); + td::remove(old_secret_chat_ids, secret_chat_id); } secret_chat->user_id = user_id; secret_chats_with_user_[secret_chat->user_id].push_back(secret_chat_id); - secret_chat->need_send_update = true; + secret_chat->is_changed = true; } if (state != SecretChatState::Unknown && state != secret_chat->state) { secret_chat->state = state; - secret_chat->need_send_update = true; + secret_chat->is_changed = true; secret_chat->is_state_changed = true; } if (is_outbound != secret_chat->is_outbound) { secret_chat->is_outbound = is_outbound; - secret_chat->need_send_update = true; + secret_chat->is_changed = true; } if (ttl != -1 && ttl != secret_chat->ttl) { secret_chat->ttl = ttl; - secret_chat->need_send_update = true; + secret_chat->is_changed = true; } if (date != 0 && date != secret_chat->date) { secret_chat->date = date; - secret_chat->is_changed = true; + secret_chat->need_save_to_database = true; } if (!key_hash.empty() && key_hash != secret_chat->key_hash) { secret_chat->key_hash = std::move(key_hash); - secret_chat->need_send_update = true; + secret_chat->is_changed = true; } if (layer != 0 && layer != secret_chat->layer) { secret_chat->layer = layer; - secret_chat->need_send_update = true; + secret_chat->is_changed = true; } update_secret_chat(secret_chat, secret_chat_id); @@ -9793,7 +11961,7 @@ std::pair> ContactsManager::search_chat_partici DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, UserId user_id, int64 &random_id, bool force, Promise &&promise) { - LOG(INFO) << "Trying to get " << user_id << " as member of " << channel_id; + LOG(INFO) << "Trying to get " << user_id << " as member of " << channel_id << " with random_id " << random_id; if (random_id != 0) { // request has already been sent before auto it = received_channel_participant_.find(random_id); @@ -9811,14 +11979,13 @@ DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, } if (!td_->auth_manager_->is_bot() && is_user_bot(user_id)) { - // get BotInfo through UserFull - auto user = get_user(user_id); - auto user_full = get_user_full(user_id); - if (user_full == nullptr || user_full->is_bot_info_expired(user->bot_info_version)) { + auto u = get_user(user_id); + CHECK(u != nullptr); + if (is_bot_info_expired(user_id, u->bot_info_version)) { if (force) { - LOG(ERROR) << "Can't find cached UserFull"; + LOG(ERROR) << "Can't find cached BotInfo"; } else { - send_get_user_full_query(user_id, std::move(input_user), std::move(promise)); + send_get_user_full_query(user_id, std::move(input_user), std::move(promise), "get_channel_participant"); return DialogParticipant(); } } @@ -9829,11 +11996,13 @@ DialogParticipant ContactsManager::get_channel_participant(ChannelId channel_id, } while (random_id == 0 || received_channel_participant_.find(random_id) != received_channel_participant_.end()); received_channel_participant_[random_id]; // reserve place for result - LOG(DEBUG) << "Get info about " << user_id << " membership in the " << channel_id; + LOG(DEBUG) << "Get info about " << user_id << " membership in the " << channel_id << " with random_id " << random_id; auto on_result_promise = PromiseCreator::lambda( [this, random_id, promise = std::move(promise)](Result r_dialog_participant) mutable { // ResultHandlers are cleared before managers, so it is safe to capture this + LOG(INFO) << "Receive a member of a channel with random_id " << random_id; + auto it = received_channel_participant_.find(random_id); CHECK(it != received_channel_participant_.end()); @@ -9897,7 +12066,7 @@ std::pair> ContactsManager::get_channel_partici return result; } - auto channel_full = get_channel_full(channel_id); + auto channel_full = get_channel_full_force(channel_id); if (channel_full == nullptr || (!force && channel_full->is_expired())) { if (force) { LOG(ERROR) << "Can't find cached ChannelFull"; @@ -9906,7 +12075,8 @@ std::pair> ContactsManager::get_channel_partici if (input_channel == nullptr) { promise.set_error(Status::Error(6, "Supergroup not found")); } else { - send_get_channel_full_query(channel_id, std::move(input_channel), std::move(promise)); + send_get_channel_full_query(channel_full, channel_id, std::move(input_channel), std::move(promise), + "get_channel_participants"); } return result; } @@ -9936,13 +12106,15 @@ void ContactsManager::send_get_channel_participants_query(ChannelId channel_id, ->send(channel_id, std::move(filter), offset, limit, random_id); } -vector ContactsManager::get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise) { +vector ContactsManager::get_dialog_administrators(DialogId dialog_id, int left_tries, + Promise &&promise) { auto it = dialog_administrators_.find(dialog_id); if (it != dialog_administrators_.end()) { promise.set_value(Unit()); if (left_tries >= 2) { - auto hash = - get_vector_hash(transform(it->second, [](UserId user_id) { return static_cast(user_id.get()); })); + auto hash = get_vector_hash(transform(it->second, [](const DialogAdministrator &administrator) { + return static_cast(administrator.get_user_id().get()); + })); reload_dialog_administrators(dialog_id, hash, Auto()); // update administrators cache } return it->second; @@ -9964,7 +12136,7 @@ vector ContactsManager::get_dialog_administrators(DialogId dialog_id, in } string ContactsManager::get_dialog_administrators_database_key(DialogId dialog_id) { - return PSTRING() << "admin" << (-dialog_id.get()); + return PSTRING() << "adm" << (-dialog_id.get()); } void ContactsManager::load_dialog_administrators(DialogId dialog_id, Promise &&promise) { @@ -9988,50 +12160,63 @@ void ContactsManager::on_load_dialog_administrators_from_database(DialogId dialo return; } - vector user_ids; - log_event_parse(user_ids, value).ensure(); + vector administrators; + log_event_parse(administrators, value).ensure(); - LOG(INFO) << "Successfully loaded " << user_ids.size() << " administrators in " << dialog_id << " from database"; + LOG(INFO) << "Successfully loaded " << administrators.size() << " administrators in " << dialog_id + << " from database"; MultiPromiseActorSafe load_users_multipromise{"LoadUsersMultiPromiseActor"}; load_users_multipromise.add_promise( - PromiseCreator::lambda([dialog_id, user_ids, promise = std::move(promise)](Result<> result) mutable { + PromiseCreator::lambda([dialog_id, administrators, promise = std::move(promise)](Result<> result) mutable { send_closure(G()->contacts_manager(), &ContactsManager::on_load_administrator_users_finished, dialog_id, - std::move(user_ids), std::move(result), std::move(promise)); + std::move(administrators), std::move(result), std::move(promise)); })); auto lock_promise = load_users_multipromise.get_promise(); - for (auto user_id : user_ids) { - get_user(user_id, 3, load_users_multipromise.get_promise()); + for (auto &administrator : administrators) { + get_user(administrator.get_user_id(), 3, load_users_multipromise.get_promise()); } lock_promise.set_value(Unit()); } -void ContactsManager::on_load_administrator_users_finished(DialogId dialog_id, vector user_ids, Result<> result, +void ContactsManager::on_load_administrator_users_finished(DialogId dialog_id, + vector administrators, Result<> result, Promise promise) { if (result.is_ok()) { - dialog_administrators_.emplace(dialog_id, std::move(user_ids)); + dialog_administrators_.emplace(dialog_id, std::move(administrators)); } promise.set_value(Unit()); } -void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector administrator_user_ids, +void ContactsManager::on_update_channel_administrator_count(ChannelId channel_id, int32 administrator_count) { + auto channel_full = get_channel_full_force(channel_id); + if (channel_full != nullptr && channel_full->administrator_count != administrator_count) { + channel_full->administrator_count = administrator_count; + channel_full->is_changed = true; + update_channel_full(channel_full, channel_id); + } +} + +void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector &&administrators, bool have_access) { - LOG(INFO) << "Update administrators in " << dialog_id << " to " << format::as_array(administrator_user_ids); + LOG(INFO) << "Update administrators in " << dialog_id << " to " << format::as_array(administrators); if (have_access) { - std::sort(administrator_user_ids.begin(), administrator_user_ids.end(), - [](UserId lhs, UserId rhs) { return lhs.get() < rhs.get(); }); + std::sort(administrators.begin(), administrators.end(), + [](const DialogAdministrator &lhs, const DialogAdministrator &rhs) { + return lhs.get_user_id().get() < rhs.get_user_id().get(); + }); auto it = dialog_administrators_.find(dialog_id); if (it != dialog_administrators_.end()) { - if (it->second == administrator_user_ids) { + if (it->second == administrators) { return; } - it->second = std::move(administrator_user_ids); + it->second = std::move(administrators); } else { - it = dialog_administrators_.emplace(dialog_id, std::move(administrator_user_ids)).first; + it = dialog_administrators_.emplace(dialog_id, std::move(administrators)).first; } if (G()->parameters().use_chat_info_db) { @@ -10090,9 +12275,9 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc } if (is_creator) { - return DialogParticipantStatus::Creator(!has_left); + return DialogParticipantStatus::Creator(!has_left, string()); } else if (chat.admin_rights_ != nullptr) { - return get_dialog_participant_status(false, std::move(chat.admin_rights_)); + return get_dialog_participant_status(false, std::move(chat.admin_rights_), string()); } else if (was_kicked) { return DialogParticipantStatus::Banned(0); } else if (has_left) { @@ -10114,7 +12299,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc case telegram_api::inputChannel::ID: { auto input_channel = move_tl_object_as(chat.migrated_to_); migrated_to_channel_id = ChannelId(input_channel->channel_id_); - if (!have_channel(migrated_to_channel_id)) { + if (!have_channel_force(migrated_to_channel_id)) { if (!migrated_to_channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << migrated_to_channel_id << debug_str; } else { @@ -10151,7 +12336,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc LOG_IF(ERROR, c->date != 0) << "Chat creation date has changed from " << c->date << " to " << chat.date_ << debug_str; c->date = chat.date_; - c->is_changed = true; + c->need_save_to_database = true; } on_update_chat_status(c, chat_id, std::move(status)); on_update_chat_default_permissions(c, chat_id, get_restricted_rights(std::move(chat.default_banned_rights_)), @@ -10162,7 +12347,7 @@ void ContactsManager::on_chat_update(telegram_api::chat &chat, const char *sourc LOG_IF(INFO, !is_active && !migrated_to_channel_id.is_valid()) << chat_id << " is deactivated in " << debug_str; if (c->cache_version != Chat::CACHE_VERSION) { c->cache_version = Chat::CACHE_VERSION; - c->is_changed = true; + c->need_save_to_database = true; } update_chat(c, chat_id); } @@ -10181,7 +12366,7 @@ void ContactsManager::on_chat_update(telegram_api::chatForbidden &chat, const ch on_update_chat_photo(c, chat_id, nullptr); if (c->date != 0) { c->date = 0; // removed in 38-th layer - c->is_changed = true; + c->need_save_to_database = true; } on_update_chat_status(c, chat_id, DialogParticipantStatus::Banned(0)); if (is_uninited) { @@ -10192,7 +12377,7 @@ void ContactsManager::on_chat_update(telegram_api::chatForbidden &chat, const ch } if (c->cache_version != Chat::CACHE_VERSION) { c->cache_version = Chat::CACHE_VERSION; - c->is_changed = true; + c->need_save_to_database = true; } update_chat(c, chat_id); } @@ -10218,10 +12403,13 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool has_access_hash = (channel.flags_ & CHANNEL_FLAG_HAS_ACCESS_HASH) != 0; auto access_hash = has_access_hash ? channel.access_hash_ : 0; + bool has_linked_channel = (channel.flags_ & CHANNEL_FLAG_HAS_LINKED_CHAT) != 0; + bool has_location = (channel.flags_ & CHANNEL_FLAG_HAS_LOCATION) != 0; bool sign_messages = (channel.flags_ & CHANNEL_FLAG_SIGN_MESSAGES) != 0; + bool is_slow_mode_enabled = (channel.flags_ & CHANNEL_FLAG_IS_SLOW_MODE_ENABLED) != 0; bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0; bool is_verified = (channel.flags_ & CHANNEL_FLAG_IS_VERIFIED) != 0; - string restriction_reason = std::move(channel.restriction_reason_); + auto restriction_reasons = get_restriction_reasons(std::move(channel.restriction_reason_)); bool is_scam = (channel.flags_ & CHANNEL_FLAG_IS_SCAM) != 0; int32 participant_count = (channel.flags_ & CHANNEL_FLAG_HAS_PARTICIPANT_COUNT) != 0 ? channel.participants_count_ : 0; @@ -10236,6 +12424,9 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char if (is_megagroup) { LOG_IF(ERROR, sign_messages) << "Need to sign messages in the supergroup " << channel_id << " from " << source; sign_messages = true; + } else { + LOG_IF(ERROR, is_slow_mode_enabled) << "Slow mode enabled in the " << channel_id << " from " << source; + is_slow_mode_enabled = false; } DialogParticipantStatus status = [&]() { @@ -10243,9 +12434,9 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char bool is_creator = (channel.flags_ & CHANNEL_FLAG_USER_IS_CREATOR) != 0; if (is_creator) { - return DialogParticipantStatus::Creator(!has_left); + return DialogParticipantStatus::Creator(!has_left, string()); } else if (channel.admin_rights_ != nullptr) { - return get_dialog_participant_status(false, std::move(channel.admin_rights_)); + return get_dialog_participant_status(false, std::move(channel.admin_rights_), string()); } else if (channel.banned_rights_ != nullptr) { return get_dialog_participant_status(!has_left, std::move(channel.banned_rights_)); } else if (has_left) { @@ -10270,8 +12461,8 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char c->is_megagroup = is_megagroup; c->is_verified = is_verified; - c->need_send_update = true; - invalidate_channel_full(channel_id, false); + c->is_changed = true; + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); } update_channel(c, channel_id); @@ -10285,18 +12476,23 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char return; } + if (status.is_creator()) { + // to correctly calculate is_ownership_transferred in on_update_channel_status + get_channel_force(channel_id); + } + Channel *c = add_channel(channel_id, "on_channel"); if (c->status.is_banned()) { // possibly uninited channel min_channels_.erase(channel_id); } if (c->access_hash != access_hash) { c->access_hash = access_hash; - c->is_changed = true; + c->need_save_to_database = true; } on_update_channel_title(c, channel_id, std::move(channel.title_)); if (c->date != channel.date_) { c->date = channel.date_; - c->need_send_update = true; + c->is_changed = true; } on_update_channel_photo(c, channel_id, std::move(channel.photo_)); on_update_channel_status(c, channel_id, std::move(status)); @@ -10306,24 +12502,28 @@ void ContactsManager::on_chat_update(telegram_api::channel &channel, const char if (participant_count != 0 && participant_count != c->participant_count) { c->participant_count = participant_count; - c->need_send_update = true; + c->is_changed = true; } - if (c->sign_messages != sign_messages || c->is_megagroup != is_megagroup || c->is_verified != is_verified || - c->restriction_reason != restriction_reason || c->is_scam != is_scam) { + if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || + c->sign_messages != sign_messages || c->is_megagroup != is_megagroup || c->is_verified != is_verified || + c->restriction_reasons != restriction_reasons || c->is_scam != is_scam) { + c->has_linked_channel = has_linked_channel; + c->has_location = has_location; c->sign_messages = sign_messages; + c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; c->is_verified = is_verified; - c->restriction_reason = std::move(restriction_reason); + c->restriction_reasons = std::move(restriction_reasons); c->is_scam = is_scam; - c->need_send_update = true; - invalidate_channel_full(channel_id, false); + c->is_changed = true; + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); } if (c->cache_version != Channel::CACHE_VERSION) { c->cache_version = Channel::CACHE_VERSION; - c->is_changed = true; + c->need_save_to_database = true; } update_channel(c, channel_id); } @@ -10351,13 +12551,13 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co } if (c->access_hash != channel.access_hash_) { c->access_hash = channel.access_hash_; - c->is_changed = true; + c->need_save_to_database = true; } on_update_channel_title(c, channel_id, std::move(channel.title_)); on_update_channel_photo(c, channel_id, nullptr); if (c->date != 0) { c->date = 0; - c->need_send_update = true; + c->is_changed = true; } int32 unban_date = (channel.flags_ & CHANNEL_FLAG_HAS_UNBAN_DATE) != 0 ? channel.until_date_ : 0; on_update_channel_status(c, channel_id, DialogParticipantStatus::Banned(unban_date)); @@ -10365,10 +12565,12 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co tl_object_ptr banned_rights; // == nullptr on_update_channel_default_permissions(c, channel_id, get_restricted_rights(banned_rights)); + bool has_linked_channel = false; + bool has_location = false; bool sign_messages = false; + bool is_slow_mode_enabled = false; bool is_megagroup = (channel.flags_ & CHANNEL_FLAG_IS_MEGAGROUP) != 0; bool is_verified = false; - string restriction_reason; bool is_scam = false; { @@ -10384,24 +12586,29 @@ void ContactsManager::on_chat_update(telegram_api::channelForbidden &channel, co if (c->participant_count != 0) { c->participant_count = 0; - c->need_send_update = true; + c->is_changed = true; } - if (c->sign_messages != sign_messages || c->is_megagroup != is_megagroup || c->is_verified != is_verified || - c->restriction_reason != restriction_reason || c->is_scam != is_scam) { + if (c->has_linked_channel != has_linked_channel || c->has_location != has_location || + c->sign_messages != sign_messages || c->is_slow_mode_enabled != is_slow_mode_enabled || + c->is_megagroup != is_megagroup || c->is_verified != is_verified || !c->restriction_reasons.empty() || + c->is_scam != is_scam) { + c->has_linked_channel = has_linked_channel; + c->has_location = has_location; c->sign_messages = sign_messages; + c->is_slow_mode_enabled = is_slow_mode_enabled; c->is_megagroup = is_megagroup; c->is_verified = is_verified; - c->restriction_reason = std::move(restriction_reason); + c->restriction_reasons.clear(); c->is_scam = is_scam; - c->need_send_update = true; - invalidate_channel_full(channel_id, false); + c->is_changed = true; + invalidate_channel_full(channel_id, false, !c->is_slow_mode_enabled); } if (c->cache_version != Channel::CACHE_VERSION) { c->cache_version = Channel::CACHE_VERSION; - c->is_changed = true; + c->need_save_to_database = true; } update_channel(c, channel_id); } @@ -10418,14 +12625,14 @@ void ContactsManager::on_upload_profile_photo(FileId file_id, tl_object_ptrfile_manager_->get_file_view(file_id); if (file_view.has_remote_location() && input_file == nullptr) { - if (file_view.remote_location().is_web()) { + if (file_view.main_remote_location().is_web()) { // TODO reupload promise.set_error(Status::Error(400, "Can't use web photo as profile photo")); return; } td_->create_handler(std::move(promise)) - ->send(file_id, file_view.remote_location().as_input_photo()); + ->send(file_id, file_view.main_remote_location().as_input_photo()); return; } CHECK(input_file != nullptr); @@ -10480,9 +12687,8 @@ int32 ContactsManager::get_user_id_object(UserId user_id, const char *source) co send_closure(G()->td(), &Td::send_update, td_api::make_object(td_api::make_object( user_id.get(), "", "", "", "", td_api::make_object(), - get_profile_photo_object(td_->file_manager_.get(), nullptr), - get_link_state_object(LinkState::Unknown), get_link_state_object(LinkState::Unknown), false, false, - "", false, false, td_api::make_object(), ""))); + get_profile_photo_object(td_->file_manager_.get(), nullptr), false, false, false, false, "", false, + false, td_api::make_object(), ""))); } return user_id.get(); } @@ -10507,8 +12713,8 @@ tl_object_ptr ContactsManager::get_user_object(UserId user_id, con return make_tl_object( user_id.get(), u->first_name, u->last_name, u->username, u->phone_number, get_user_status_object(user_id, u), - get_profile_photo_object(td_->file_manager_.get(), &u->photo), get_link_state_object(u->outbound), - get_link_state_object(u->inbound), u->is_verified, u->is_support, u->restriction_reason, u->is_scam, + get_profile_photo_object(td_->file_manager_.get(), &u->photo), u->is_contact, u->is_mutual_contact, + u->is_verified, u->is_support, get_restriction_reason_description(u->restriction_reasons), u->is_scam, u->is_received, std::move(type), u->language_code); } @@ -10532,10 +12738,11 @@ tl_object_ptr ContactsManager::get_user_full_info_object(U const UserFull *user_full) const { CHECK(user_full != nullptr); bool is_bot = is_user_bot(user_id); - return make_tl_object(user_full->is_blocked, user_full->can_be_called, - user_full->has_private_calls, is_bot ? string() : user_full->about, - is_bot ? user_full->about : string(), user_full->common_chat_count, - get_bot_info_object(user_full->bot_info.get())); + return make_tl_object( + user_full->is_blocked, user_full->can_be_called, user_full->has_private_calls, + user_full->need_phone_number_privacy_exception, is_bot ? string() : user_full->about, + is_bot ? user_full->about : string(), user_full->common_chat_count, + is_bot ? get_bot_info_object(user_id) : nullptr); } int32 ContactsManager::get_basic_group_id_object(ChatId chat_id, const char *source) const { @@ -10553,21 +12760,20 @@ tl_object_ptr ContactsManager::get_basic_group_object(ChatId return get_basic_group_object(chat_id, get_chat(chat_id)); } -tl_object_ptr ContactsManager::get_basic_group_object(ChatId chat_id, const Chat *chat) { - if (chat == nullptr) { +tl_object_ptr ContactsManager::get_basic_group_object(ChatId chat_id, const Chat *c) { + if (c == nullptr) { return nullptr; } - if (chat->migrated_to_channel_id.is_valid()) { - get_channel_force(chat->migrated_to_channel_id); + if (c->migrated_to_channel_id.is_valid()) { + get_channel_force(c->migrated_to_channel_id); } - return get_basic_group_object_const(chat_id, chat); + return get_basic_group_object_const(chat_id, c); } -tl_object_ptr ContactsManager::get_basic_group_object_const(ChatId chat_id, - const Chat *chat) const { +tl_object_ptr ContactsManager::get_basic_group_object_const(ChatId chat_id, const Chat *c) const { return make_tl_object( - chat_id.get(), chat->participant_count, get_chat_status(chat).get_chat_member_status_object(), chat->is_active, - get_supergroup_id_object(chat->migrated_to_channel_id, "get_basic_group_object")); + chat_id.get(), c->participant_count, get_chat_status(c).get_chat_member_status_object(), c->is_active, + get_supergroup_id_object(c->migrated_to_channel_id, "get_basic_group_object")); } tl_object_ptr ContactsManager::get_basic_group_full_info_object(ChatId chat_id) const { @@ -10591,7 +12797,7 @@ int32 ContactsManager::get_supergroup_id_object(ChannelId channel_id, const char send_closure(G()->td(), &Td::send_update, td_api::make_object(td_api::make_object( channel_id.get(), string(), 0, DialogParticipantStatus::Banned(0).get_chat_member_status_object(), - 0, false, true, false, "", false))); + 0, false, false, false, false, true, false, "", false))); } return channel_id.get(); } @@ -10600,15 +12806,14 @@ tl_object_ptr ContactsManager::get_supergroup_object(Channel return get_supergroup_object(channel_id, get_channel(channel_id)); } -tl_object_ptr ContactsManager::get_supergroup_object(ChannelId channel_id, - const Channel *channel) const { - if (channel == nullptr) { +tl_object_ptr ContactsManager::get_supergroup_object(ChannelId channel_id, const Channel *c) const { + if (c == nullptr) { return nullptr; } - return make_tl_object(channel_id.get(), channel->username, channel->date, - get_channel_status(channel).get_chat_member_status_object(), - channel->participant_count, channel->sign_messages, !channel->is_megagroup, - channel->is_verified, channel->restriction_reason, channel->is_scam); + return td_api::make_object( + channel_id.get(), c->username, c->date, get_channel_status(c).get_chat_member_status_object(), + c->participant_count, c->has_linked_channel, c->has_location, c->sign_messages, c->is_slow_mode_enabled, + !c->is_megagroup, c->is_verified, get_restriction_reason_description(c->restriction_reasons), c->is_scam); } tl_object_ptr ContactsManager::get_supergroup_full_info_object(ChannelId channel_id) const { @@ -10618,11 +12823,17 @@ tl_object_ptr ContactsManager::get_supergroup_full_i tl_object_ptr ContactsManager::get_supergroup_full_info_object( const ChannelFull *channel_full) const { CHECK(channel_full != nullptr); - return make_tl_object( + double slow_mode_delay_expires_in = 0; + if (channel_full->slow_mode_next_send_date != 0) { + slow_mode_delay_expires_in = max(channel_full->slow_mode_next_send_date - G()->server_time(), 1e-3); + } + return td_api::make_object( channel_full->description, channel_full->participant_count, channel_full->administrator_count, - channel_full->restricted_count, channel_full->banned_count, channel_full->can_get_participants, - channel_full->can_set_username, channel_full->can_set_sticker_set, channel_full->can_view_statistics, - channel_full->is_all_history_available, channel_full->sticker_set_id, channel_full->invite_link, + 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->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()); } @@ -10676,40 +12887,16 @@ tl_object_ptr ContactsManager::get_secret_chat_object_const( secret_chat->key_hash, secret_chat->layer); } -tl_object_ptr ContactsManager::get_link_state_object(LinkState link) { - switch (link) { - case LinkState::Unknown: - case LinkState::None: - return make_tl_object(); - case LinkState::KnowsPhoneNumber: - return make_tl_object(); - case LinkState::Contact: - return make_tl_object(); - default: - UNREACHABLE(); - } - return make_tl_object(); -} - -tl_object_ptr ContactsManager::get_bot_info_object(const BotInfo *bot_info) { +td_api::object_ptr ContactsManager::get_bot_info_object(UserId user_id) const { + auto bot_info = get_bot_info(user_id); if (bot_info == nullptr) { return nullptr; } - vector> commands; - for (auto &command : bot_info->commands) { - commands.push_back(make_tl_object(command.first, command.second)); - } - - return make_tl_object(bot_info->description, std::move(commands)); -} - -tl_object_ptr ContactsManager::get_bot_info_object(UserId user_id) const { - auto user_full = get_user_full(user_id); - if (user_full == nullptr || user_full->bot_info == nullptr) { - return nullptr; - } - return get_bot_info_object(user_full->bot_info.get()); + auto commands = transform(bot_info->commands, [](auto &command) { + return td_api::make_object(command.first, command.second); + }); + return td_api::make_object(bot_info->description, std::move(commands)); } tl_object_ptr ContactsManager::get_chat_invite_link_info_object( @@ -10758,7 +12945,7 @@ tl_object_ptr ContactsManager::get_chat_invite_link_ if (c != nullptr) { title = c->title; photo = &c->photo; - is_public = !c->username.empty(); + is_public = is_channel_public(c); is_megagroup = c->is_megagroup; participant_count = c->participant_count; } else { @@ -10801,39 +12988,39 @@ UserId ContactsManager::get_support_user(Promise &&promise) { } void ContactsManager::after_get_difference() { + if (td_->auth_manager_->is_bot()) { + return; + } get_user(get_my_id(), 3, Promise()); } void ContactsManager::get_current_state(vector> &updates) const { for (auto &it : users_) { - updates.push_back(td_api::make_object(get_user_object(it.first, &it.second))); + updates.push_back(td_api::make_object(get_user_object(it.first, it.second.get()))); } for (auto &it : channels_) { - updates.push_back(td_api::make_object(get_supergroup_object(it.first, &it.second))); + updates.push_back(td_api::make_object(get_supergroup_object(it.first, it.second.get()))); } for (auto &it : chats_) { // chat object can contain channel_id, so it must be sent after channels updates.push_back( - td_api::make_object(get_basic_group_object_const(it.first, &it.second))); + td_api::make_object(get_basic_group_object_const(it.first, it.second.get()))); } for (auto &it : secret_chats_) { // secret chat object contains user_id, so it must be sent after users updates.push_back( - td_api::make_object(get_secret_chat_object_const(it.first, &it.second))); + td_api::make_object(get_secret_chat_object_const(it.first, it.second.get()))); } for (auto &it : users_full_) { - if (!it.second.is_inited) { - continue; - } - updates.push_back(td_api::make_object(get_user_id_object(it.first, "get_current_state"), - get_user_full_info_object(it.first, &it.second))); + updates.push_back(td_api::make_object( + get_user_id_object(it.first, "get_current_state"), get_user_full_info_object(it.first, it.second.get()))); } for (auto &it : channels_full_) { updates.push_back(td_api::make_object( - get_supergroup_id_object(it.first, "get_current_state"), get_supergroup_full_info_object(&it.second))); + get_supergroup_id_object(it.first, "get_current_state"), get_supergroup_full_info_object(it.second.get()))); } for (auto &it : chats_full_) { updates.push_back(td_api::make_object( - get_basic_group_id_object(it.first, "get_current_state"), get_basic_group_full_info_object(&it.second))); + get_basic_group_id_object(it.first, "get_current_state"), get_basic_group_full_info_object(it.second.get()))); } } diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 293c8156..f76ded91 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,14 +13,20 @@ #include "td/telegram/ChannelId.h" #include "td/telegram/ChatId.h" #include "td/telegram/Contact.h" +#include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" +#include "td/telegram/Location.h" #include "td/telegram/MessageId.h" #include "td/telegram/Photo.h" +#include "td/telegram/PublicDialogType.h" #include "td/telegram/QueryCombiner.h" +#include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatId.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" @@ -58,6 +64,12 @@ enum class ChannelType : uint8 { Broadcast, Megagroup, Unknown }; enum class CheckDialogUsernameResult : uint8 { Ok, Invalid, Occupied, PublicDialogsTooMuch, PublicGroupsUnavailable }; +struct CanTransferOwnershipResult { + enum class Type : uint8 { Ok, PasswordNeeded, PasswordTooFresh, SessionTooFresh }; + Type type = Type::Ok; + int32 retry_after = 0; +}; + class ContactsManager : public Actor { public: ContactsManager(Td *td, ActorShared<> parent); @@ -105,6 +117,8 @@ class ContactsManager : public Actor { bool is_update_about_username_change_received(UserId user_id) const; + void for_each_secret_chat_with_user(UserId user_id, std::function f); + string get_user_username(UserId user_id) const; string get_channel_username(ChannelId channel_id) const; string get_secret_chat_username(SecretChatId secret_chat_id) const; @@ -116,8 +130,6 @@ class ContactsManager : public Actor { SecretChatState get_secret_chat_state(SecretChatId secret_chat_id) const; int32 get_secret_chat_layer(SecretChatId secret_chat_id) const; - bool default_can_report_spam_in_secret_chat(SecretChatId secret_chat_id) const; - void on_imported_contacts(int64 random_id, vector imported_contact_user_ids, vector unimported_contact_invites); @@ -131,8 +143,6 @@ class ContactsManager : public Actor { void reload_contacts(bool force); - void on_get_contacts_link(tl_object_ptr &&link); - void on_get_user(tl_object_ptr &&user, const char *source, bool is_me = false, bool expect_support = false); void on_get_users(vector> &&users, const char *source); @@ -150,7 +160,7 @@ class ContactsManager : public Actor { void on_get_chat(tl_object_ptr &&chat, const char *source); void on_get_chats(vector> &&chats, const char *source); - void on_get_chat_full(tl_object_ptr &&chat_full); + 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); @@ -159,13 +169,14 @@ class ContactsManager : public Actor { void on_update_user_photo(UserId user_id, tl_object_ptr &&photo_ptr); void on_update_user_online(UserId user_id, tl_object_ptr &&status); void on_update_user_local_was_online(UserId user_id, int32 local_was_online); - void on_update_user_links(UserId user_id, tl_object_ptr &&outbound, - tl_object_ptr &&inbound); - void on_update_user_blocked(UserId user_id, bool is_blocked); + void on_update_user_is_blocked(UserId user_id, bool is_blocked); void on_update_user_common_chat_count(UserId user_id, int32 common_chat_count); + void on_update_user_need_phone_number_privacy_exception(UserId user_id, bool need_phone_number_privacy_exception); void on_delete_profile_photo(int64 profile_photo_id, Promise promise); + void on_ignored_restriction_reasons_changed(); + void on_get_chat_participants(tl_object_ptr &&participants, bool from_update); void on_update_chat_add_user(ChatId chat_id, UserId inviter_user_id, UserId user_id, int32 date, int32 version); void on_update_chat_description(ChatId chat_id, string &&description); @@ -176,18 +187,26 @@ class ContactsManager : public Actor { void on_update_channel_username(ChannelId channel_id, string &&username); void on_update_channel_description(ChannelId channel_id, string &&description); - void on_update_channel_sticker_set(ChannelId channel_id, int64 sticker_set_id); + void on_update_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id); + void on_update_channel_linked_channel_id(ChannelId channel_id, ChannelId group_channel_id); + void on_update_channel_location(ChannelId channel_id, const DialogLocation &location); + void on_update_channel_slow_mode_delay(ChannelId channel_id, int32 slow_mode_delay); + void on_update_channel_slow_mode_next_send_date(ChannelId channel_id, int32 slow_mode_next_send_date); void on_update_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available); 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_dialog_administrators(DialogId dialog_id, vector administrator_user_ids, bool have_access); + void on_update_peer_located(vector> &&peers, bool from_update); + + void on_update_dialog_administrators(DialogId dialog_id, vector &&administrators, + bool have_access); void speculative_add_channel_participants(ChannelId channel_id, const vector &added_user_ids, UserId inviter_user_id, int32 date, bool by_me); void speculative_delete_channel_participant(ChannelId channel_id, UserId deleted_user_id, bool by_me); - void invalidate_channel_full(ChannelId channel_id, bool drop_invite_link); + void invalidate_channel_full(ChannelId channel_id, bool drop_invite_link, bool drop_slow_mode_delay); bool on_get_channel_error(ChannelId channel_id, const Status &status, const string &source); @@ -210,7 +229,13 @@ class ContactsManager : public Actor { void invalidate_invite_link_info(const string &invite_link); - void on_get_created_public_channels(vector> &&chats); + void on_get_created_public_channels(PublicDialogType type, vector> &&chats); + + void on_get_dialogs_for_discussion(vector> &&chats); + + void on_get_inactive_channels(vector> &&chats); + + void remove_inactive_channel(ChannelId channel_id); UserId get_my_id() const; @@ -239,6 +264,11 @@ class ContactsManager : public Actor { void set_account_ttl(int32 account_ttl, Promise &&promise) const; void get_account_ttl(Promise &&promise) const; + static td_api::object_ptr convert_authorization_object( + tl_object_ptr &&authorization); + + void confirm_qr_code_authentication(string link, Promise> &&promise); + void get_active_sessions(Promise> &&promise) const; void terminate_session(int64 session_id, Promise &&promise) const; void terminate_all_other_sessions(Promise &&promise) const; @@ -247,9 +277,7 @@ class ContactsManager : public Actor { void disconnect_website(int64 authorizations_id, Promise &&promise) const; void disconnect_all_websites(Promise &&promise) const; - Status block_user(UserId user_id); - - Status unblock_user(UserId user_id); + Status set_user_is_blocked(UserId user_id, bool is_blocked); int64 get_blocked_users(int32 offset, int32 limit, Promise &&promise); @@ -260,6 +288,8 @@ class ContactsManager : public Actor { tl_object_ptr get_blocked_users_object(int64 random_id); + void add_contact(td_api::object_ptr &&contact, bool share_phone_number, Promise &&promise); + std::pair, vector> import_contacts(const vector> &contacts, int64 &random_id, Promise &&promise); @@ -279,6 +309,10 @@ class ContactsManager : public Actor { void on_update_contacts_reset(); + void share_phone_number(UserId user_id, Promise &&promise); + + void search_dialogs_nearby(const Location &location, Promise> &&promise); + void set_profile_photo(const tl_object_ptr &input_photo, Promise &&promise); void delete_profile_photo(int64 profile_photo_id, Promise &&promise); @@ -295,7 +329,7 @@ class ContactsManager : public Actor { void set_channel_username(ChannelId channel_id, const string &username, Promise &&promise); - void set_channel_sticker_set(ChannelId channel_id, int64 sticker_set_id, Promise &&promise); + void set_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id, Promise &&promise); void toggle_channel_sign_messages(ChannelId channel_id, bool sign_messages, Promise &&promise); @@ -304,6 +338,12 @@ class ContactsManager : public Actor { void set_channel_description(ChannelId channel_id, const string &description, Promise &&promise); + void set_channel_discussion_group(DialogId dialog_id, DialogId discussion_dialog_id, Promise &&promise); + + void set_channel_location(DialogId dialog_id, const DialogLocation &location, Promise &&promise); + + void set_channel_slow_mode_delay(DialogId dialog_id, int32 slow_mode_delay, Promise &&promise); + void report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, Promise &&promise); @@ -322,6 +362,13 @@ class ContactsManager : public Actor { void change_channel_participant_status(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, Promise &&promise); + void can_transfer_ownership(Promise &&promise); + + static td_api::object_ptr get_can_transfer_ownership_result_object( + CanTransferOwnershipResult result); + + void transfer_dialog_ownership(DialogId dialog_id, UserId user_id, const string &password, Promise &&promise); + void export_chat_invite_link(ChatId chat_id, Promise &&promise); void export_channel_invite_link(ChannelId channel_id, Promise &&promise); @@ -336,15 +383,25 @@ class ContactsManager : public Actor { ChannelId migrate_chat_to_megagroup(ChatId chat_id, Promise &promise); - vector get_created_public_dialogs(Promise &&promise); + vector get_created_public_dialogs(PublicDialogType type, Promise &&promise); + + void check_created_public_dialogs_limit(PublicDialogType type, Promise &&promise); + + vector get_dialogs_for_discussion(Promise &&promise); + + vector get_inactive_channels(Promise &&promise); bool is_user_contact(UserId user_id) const; + bool is_user_blocked(UserId user_id); + bool is_user_deleted(UserId user_id) const; bool is_user_bot(UserId user_id) const; Result get_bot_data(UserId user_id) const TD_WARN_UNUSED_RESULT; + bool is_user_status_exact(UserId user_id) const; + bool can_report_user(UserId user_id) const; bool have_user(UserId user_id) const; @@ -358,6 +415,7 @@ class ContactsManager : public Actor { 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); + void reload_user_full(UserId user_id); std::pair> get_user_profile_photos(UserId user_id, int32 offset, int32 limit, Promise &&promise); @@ -383,6 +441,8 @@ class ContactsManager : public Actor { void reload_channel(ChannelId chnanel_id, Promise &&promise); bool get_channel_full(ChannelId channel_id, Promise &&promise); + bool is_channel_public(ChannelId channel_id) const; + bool have_secret_chat(SecretChatId secret_chat_id) const; bool have_secret_chat_force(SecretChatId secret_chat_id); bool get_secret_chat(SecretChatId secret_chat_id, bool force, Promise &&promise); @@ -395,6 +455,7 @@ class ContactsManager : public Actor { int32 get_channel_participant_count(ChannelId channel_id) const; bool get_channel_sign_messages(ChannelId channel_id) const; FileSourceId get_channel_photo_file_source_id(ChannelId channel_id); + int32 get_channel_slow_mode_delay(ChannelId channel_id); std::pair> search_among_users(const vector &user_ids, const string &query, int32 limit); @@ -418,7 +479,7 @@ class ContactsManager : public Actor { DialogParticipant get_dialog_participant(ChannelId channel_id, tl_object_ptr &&participant_ptr) const; - vector get_dialog_administrators(DialogId chat_id, int left_tries, Promise &&promise); + vector get_dialog_administrators(DialogId chat_id, int left_tries, Promise &&promise); int32 get_user_id_object(UserId user_id, const char *source) const; @@ -454,8 +515,6 @@ class ContactsManager : public Actor { tl_object_ptr get_chat_member_object(const DialogParticipant &dialog_participant) const; - tl_object_ptr get_bot_info_object(UserId user_id) const; - tl_object_ptr get_chat_invite_link_info_object(const string &invite_link) const; UserId get_support_user(Promise &&promise); @@ -467,10 +526,6 @@ class ContactsManager : public Actor { void get_current_state(vector> &updates) const; private: - enum class LinkState : uint8 { Unknown, None, KnowsPhoneNumber, Contact }; - - friend StringBuilder &operator<<(StringBuilder &string_builder, LinkState link_state); - struct User { string first_name; string last_name; @@ -480,7 +535,7 @@ class ContactsManager : public Actor { ProfilePhoto photo; - string restriction_reason; + vector restriction_reasons; string inline_query_placeholder; int32 bot_info_version = -1; @@ -489,9 +544,6 @@ class ContactsManager : public Actor { string language_code; - LinkState outbound = LinkState::Unknown; - LinkState inbound = LinkState::Unknown; - std::unordered_set photo_ids; std::unordered_map online_member_dialogs; // id -> time @@ -510,6 +562,8 @@ class ContactsManager : public Actor { bool is_inline_bot = false; bool need_location_bot = false; bool is_scam = false; + bool is_contact = false; + bool is_mutual_contact = false; bool is_photo_inited = false; @@ -518,10 +572,11 @@ class ContactsManager : public Actor { bool is_name_changed = true; bool is_username_changed = true; bool is_photo_changed = true; - bool is_outbound_link_changed = true; + bool is_is_contact_changed = true; + bool is_is_deleted_changed = true; bool is_default_permissions_changed = true; - bool is_changed = true; // have new changes not sent to the database except changes visible to the client - bool need_send_update = true; // have new changes not sent to the client + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database bool is_status_changed = true; bool is_online_status_changed = true; // whether online/offline has changed @@ -542,38 +597,49 @@ class ContactsManager : public Actor { int32 version = -1; string description; vector> commands; + bool is_changed = true; - BotInfo(int32 version, string description, vector> &&commands) - : version(version), description(std::move(description)), commands(std::move(commands)) { - } + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; - // do not forget to update invalidate_user_full and on_get_user_full - struct UserFull { + struct UserPhotos { vector photos; - int32 photo_count = -1; - int32 photos_offset = -1; - - unique_ptr bot_info; + int32 count = -1; + int32 offset = -1; + bool getting_now = false; + }; + // do not forget to update drop_user_full and on_get_user_full + struct UserFull { string about; int32 common_chat_count = 0; - bool getting_photos_now = false; - - bool is_inited = false; // photos and bot_info may be inited regardless this flag bool is_blocked = false; bool can_be_called = false; bool has_private_calls = false; + bool can_pin_messages = false; + bool need_phone_number_privacy_exception = false; + bool is_is_blocked_changed = true; bool is_common_chat_count_changed = true; - bool is_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database double expires_at = 0.0; - bool is_bot_info_expired(int32 bot_info_version) const; bool is_expired() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; struct Chat { @@ -598,8 +664,9 @@ class ContactsManager : public Actor { bool is_title_changed = true; bool is_photo_changed = true; bool is_default_permissions_changed = true; - bool is_changed = true; // have new changes not sent to the database except changes visible to the client - bool need_send_update = true; // have new changes not sent to the client + bool is_is_active_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database bool is_repaired = false; // whether cached value is rechecked @@ -623,7 +690,17 @@ class ContactsManager : public Actor { string invite_link; - bool is_changed = true; + bool can_set_username = false; + + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; struct Channel { @@ -632,16 +709,19 @@ class ContactsManager : public Actor { DialogPhoto photo; FileSourceId photo_source_id; string username; - string restriction_reason; + vector restriction_reasons; DialogParticipantStatus status = DialogParticipantStatus::Banned(0); RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false}; int32 date = 0; int32 participant_count = 0; - static constexpr uint32 CACHE_VERSION = 1; + static constexpr uint32 CACHE_VERSION = 4; uint32 cache_version = 0; + bool has_linked_channel = false; + bool has_location = false; bool sign_messages = false; + bool is_slow_mode_enabled = false; bool is_megagroup = false; bool is_verified = false; @@ -654,8 +734,8 @@ class ContactsManager : public Actor { bool is_status_changed = true; bool had_read_access = true; bool was_member = false; - bool is_changed = true; // have new changes not sent to the database except changes visible to the client - bool need_send_update = true; // have new changes not sent to the client + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database bool is_repaired = false; // whether cached value is rechecked @@ -678,21 +758,43 @@ class ContactsManager : public Actor { int32 banned_count = 0; string invite_link; - int64 sticker_set_id = 0; // do not forget to store along with access hash + uint32 speculative_version = 1; + uint32 repair_request_version = 0; + + StickerSetId sticker_set_id; + + ChannelId linked_channel_id; + + DialogLocation location; + + int32 slow_mode_delay = 0; + int32 slow_mode_next_send_date = 0; MessageId migrated_from_max_message_id; ChatId migrated_from_chat_id; + vector bot_user_ids; + bool can_get_participants = false; 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_changed = true; + bool is_slow_mode_next_send_date_changed = true; + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_send_update = true; // have new changes that need only to be sent to the client + bool need_save_to_database = true; // have new changes that need only to be saved to the database double expires_at = 0.0; bool is_expired() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); }; struct SecretChat { @@ -707,8 +809,8 @@ class ContactsManager : public Actor { bool is_outbound = false; bool is_state_changed = true; - bool is_changed = true; // have new changes not sent to the database except changes visible to the client - bool need_send_update = true; // have new changes not sent to the client + bool is_changed = true; // have new changes that need to be sent to the client and database + bool need_save_to_database = true; // have new changes that need only to be saved to the database bool is_saved = false; // is current secret chat version being saved/is saved to the database bool is_being_saved = false; // is current secret chat being saved to the database @@ -735,6 +837,26 @@ class ContactsManager : public Actor { bool is_megagroup = false; }; + struct DialogNearby { + DialogId dialog_id; + int32 distance; + + DialogNearby(DialogId dialog_id, int32 distance) : dialog_id(dialog_id), distance(distance) { + } + + bool operator<(const DialogNearby &other) const { + return distance < other.distance || (distance == other.distance && dialog_id.get() < other.dialog_id.get()); + } + + bool operator==(const DialogNearby &other) const { + return distance == other.distance && dialog_id == other.dialog_id; + } + + bool operator!=(const DialogNearby &other) const { + return !(*this == other); + } + }; + class UserLogEvent; class ChatLogEvent; class ChannelLogEvent; @@ -776,6 +898,8 @@ class ContactsManager : public Actor { static constexpr int32 USER_FULL_FLAG_HAS_BOT_INFO = 1 << 3; static constexpr int32 USER_FULL_FLAG_HAS_PINNED_MESSAGE = 1 << 6; static constexpr int32 USER_FULL_FLAG_CAN_PIN_MESSAGE = 1 << 7; + static constexpr int32 USER_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; + static constexpr int32 USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 12; static constexpr int32 CHAT_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHAT_FLAG_USER_WAS_KICKED = 1 << 1; @@ -786,6 +910,8 @@ class ContactsManager : public Actor { static constexpr int32 CHAT_FLAG_WAS_MIGRATED = 1 << 6; static constexpr int32 CHAT_FULL_FLAG_HAS_PINNED_MESSAGE = 1 << 6; + static constexpr int32 CHAT_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 8; + static constexpr int32 CHAT_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; static constexpr int32 CHANNEL_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHANNEL_FLAG_USER_HAS_LEFT = 1 << 2; @@ -803,6 +929,9 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FLAG_HAS_UNBAN_DATE = 1 << 16; static constexpr int32 CHANNEL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 17; static constexpr int32 CHANNEL_FLAG_IS_SCAM = 1 << 19; + static constexpr int32 CHANNEL_FLAG_HAS_LINKED_CHAT = 1 << 20; + static constexpr int32 CHANNEL_FLAG_HAS_LOCATION = 1 << 21; + static constexpr int32 CHANNEL_FLAG_IS_SLOW_MODE_ENABLED = 1 << 22; static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1; @@ -811,12 +940,19 @@ class ContactsManager : public Actor { static constexpr int32 CHANNEL_FULL_FLAG_MIGRATED_FROM = 1 << 4; static constexpr int32 CHANNEL_FULL_FLAG_HAS_PINNED_MESSAGE = 1 << 5; static constexpr int32 CHANNEL_FULL_FLAG_CAN_SET_USERNAME = 1 << 6; - static constexpr int32 CHANNEL_FULL_FLAG_CAN_SET_STICKERS = 1 << 7; + static constexpr int32 CHANNEL_FULL_FLAG_CAN_SET_STICKER_SET = 1 << 7; static constexpr int32 CHANNEL_FULL_FLAG_HAS_STICKER_SET = 1 << 8; static constexpr int32 CHANNEL_FULL_FLAG_HAS_AVAILABLE_MIN_MESSAGE_ID = 1 << 9; static constexpr int32 CHANNEL_FULL_FLAG_IS_ALL_HISTORY_HIDDEN = 1 << 10; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_FOLDER_ID = 1 << 11; static constexpr int32 CHANNEL_FULL_FLAG_CAN_VIEW_STATISTICS = 1 << 12; static constexpr int32 CHANNEL_FULL_FLAG_HAS_ONLINE_MEMBER_COUNT = 1 << 13; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_LINKED_CHANNEL_ID = 1 << 14; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_LOCATION = 1 << 15; + static constexpr int32 CHANNEL_FULL_FLAG_CAN_SET_LOCATION = 1 << 16; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_SLOW_MODE_DELAY = 1 << 17; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_SLOW_MODE_NEXT_SEND_DATE = 1 << 18; + static constexpr int32 CHANNEL_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 19; static constexpr int32 CHAT_INVITE_FLAG_IS_CHANNEL = 1 << 0; static constexpr int32 CHAT_INVITE_FLAG_IS_BROADCAST = 1 << 1; @@ -835,7 +971,8 @@ class ContactsManager : public Actor { static bool have_input_peer_user(const User *u, AccessRights access_rights); static bool have_input_peer_chat(const Chat *c, AccessRights access_rights); - static bool have_input_peer_channel(const Channel *c, AccessRights access_rights); + bool have_input_peer_channel(const Channel *c, ChannelId channel_id, AccessRights access_rights, + bool from_linked = false) const; static bool have_input_encrypted_peer(const SecretChat *secret_chat, AccessRights access_rights); const User *get_user(UserId user_id) const; @@ -847,9 +984,18 @@ class ContactsManager : public Actor { const UserFull *get_user_full(UserId user_id) const; UserFull *get_user_full(UserId user_id); + UserFull *get_user_full_force(UserId user_id); + + UserFull *add_user_full(UserId user_id); void send_get_user_full_query(UserId user_id, tl_object_ptr &&input_user, - Promise &&promise); + Promise &&promise, const char *source); + + const BotInfo *get_bot_info(UserId user_id) const; + BotInfo *get_bot_info(UserId user_id); + BotInfo *get_bot_info_force(UserId user_id, bool send_update = true); + + BotInfo *add_bot_info(UserId user_id); const Chat *get_chat(ChatId chat_id) const; Chat *get_chat(ChatId chat_id); @@ -859,8 +1005,11 @@ class ContactsManager : public Actor { const ChatFull *get_chat_full(ChatId chat_id) const; ChatFull *get_chat_full(ChatId chat_id); + ChatFull *get_chat_full_force(ChatId chat_id); - void send_get_chat_full_query(ChatId chat_id, Promise &&promise); + ChatFull *add_chat_full(ChatId chat_id); + + void send_get_chat_full_query(ChatId chat_id, Promise &&promise, const char *source); const Channel *get_channel(ChannelId channel_id) const; Channel *get_channel(ChannelId channel_id); @@ -869,10 +1018,14 @@ class ContactsManager : public Actor { Channel *add_channel(ChannelId channel_id, const char *source); const ChannelFull *get_channel_full(ChannelId channel_id) const; - ChannelFull *get_channel_full(ChannelId channel_id); + ChannelFull *get_channel_full(ChannelId channel_id, const char *source); + ChannelFull *get_channel_full_force(ChannelId channel_id); - void send_get_channel_full_query(ChannelId channel_id, tl_object_ptr &&input_channel, - Promise &&promise); + ChannelFull *add_channel_full(ChannelId channel_id); + + void send_get_channel_full_query(ChannelFull *channel_full, ChannelId channel_id, + tl_object_ptr &&input_channel, Promise &&promise, + const char *source); const SecretChat *get_secret_chat(SecretChatId secret_chat_id) const; SecretChat *get_secret_chat(SecretChatId secret_chat_id); @@ -890,19 +1043,18 @@ class ContactsManager : public Actor { void set_my_id(UserId my_id); - static LinkState get_link_state(tl_object_ptr &&link); - static bool is_valid_username(const string &username); - bool on_update_bot_info(tl_object_ptr &&bot_info); + bool on_update_bot_info(tl_object_ptr &&new_bot_info, bool send_update = true); + bool is_bot_info_expired(UserId user_id, int32 bot_info_version); void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, string &&username); void on_update_user_phone_number(User *u, UserId user_id, string &&phone_number); void on_update_user_photo(User *u, UserId user_id, tl_object_ptr &&photo, const char *source); + void on_update_user_is_contact(User *u, UserId user_id, bool is_contact, bool is_mutual_contact); void on_update_user_online(User *u, UserId user_id, tl_object_ptr &&status); void on_update_user_local_was_online(User *u, UserId user_id, int32 local_was_online); - void on_update_user_links(User *u, UserId user_id, LinkState outbound, LinkState inbound); void do_update_user_photo(User *u, UserId user_id, tl_object_ptr &&photo, const char *source); @@ -911,9 +1063,12 @@ class ContactsManager : public Actor { void on_update_user_full_is_blocked(UserFull *user_full, UserId user_id, bool is_blocked); void on_update_user_full_common_chat_count(UserFull *user_full, UserId user_id, int32 common_chat_count); - bool on_update_user_full_bot_info(UserFull *user_full, UserId user_id, int32 bot_info_version, - tl_object_ptr &&bot_info); - void invalidate_user_full(UserId user_id); + 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_full(UserId user_id); + + void on_set_user_is_blocked_failed(UserId user_id, bool is_blocked, Status error); void on_update_chat_status(Chat *c, ChatId chat_id, DialogParticipantStatus status); void on_update_chat_default_permissions(Chat *c, ChatId chat_id, RestrictedRights default_permissions, int32 version); @@ -937,8 +1092,21 @@ class ContactsManager : public Actor { void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status); void on_update_channel_default_permissions(Channel *c, ChannelId channel_id, RestrictedRights default_permissions); + void on_update_channel_bot_user_ids(ChannelId channel_id, vector &&bot_user_ids); + void on_update_channel_full_invite_link(ChannelFull *channel_full, tl_object_ptr &&invite_link_ptr); + void on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id, + ChannelId linked_channel_id); + void on_update_channel_full_location(ChannelFull *channel_full, ChannelId channel_id, const DialogLocation &location); + void on_update_channel_full_slow_mode_delay(ChannelFull *channel_full, ChannelId channel_id, int32 slow_mode_delay, + int32 slow_mode_next_send_date); + void on_update_channel_full_slow_mode_next_send_date(ChannelFull *channel_full, int32 slow_mode_next_send_date); + void on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id, + vector &&bot_user_ids); + + void remove_linked_channel_id(ChannelId channel_id); + ChannelId get_linked_channel_id(ChannelId channel_id) const; static bool speculative_add_count(int32 &count, int32 new_count); @@ -947,7 +1115,7 @@ class ContactsManager : public Actor { void speculative_add_channel_user(ChannelId channel_id, UserId user_id, DialogParticipantStatus new_status, DialogParticipantStatus old_status); - void invalidate_chat_full(ChatId chat_id); + void drop_chat_full(ChatId chat_id); void update_user_online_member_count(User *u); void update_chat_online_member_count(const ChatFull *chat_full, ChatId chat_id, bool is_from_server); @@ -1001,17 +1169,39 @@ class ContactsManager : public Actor { void load_secret_chat_from_database_impl(SecretChatId secret_chat_id, Promise promise); void on_load_secret_chat_from_database(SecretChatId secret_chat_id, string value); + void save_user_full(const UserFull *user_full, UserId user_id); + static string get_user_full_database_key(UserId user_id); + static string get_user_full_database_value(const UserFull *user_full); + void on_load_user_full_from_database(UserId user_id, string value); + + void save_bot_info(const BotInfo *bot_info, UserId user_id); + static string get_bot_info_database_key(UserId user_id); + static string get_bot_info_database_value(const BotInfo *bot_info); + void on_load_bot_info_from_database(UserId user_id, string value, bool send_update); + + void save_chat_full(const ChatFull *chat_full, ChatId chat_id); + static string get_chat_full_database_key(ChatId chat_id); + static string get_chat_full_database_value(const ChatFull *chat_full); + void on_load_chat_full_from_database(ChatId chat_id, string value); + + void save_channel_full(const ChannelFull *channel_full, ChannelId channel_id); + static string get_channel_full_database_key(ChannelId channel_id); + static string get_channel_full_database_value(const ChannelFull *channel_full); + void on_load_channel_full_from_database(ChannelId channel_id, string value); + void update_user(User *u, UserId user_id, bool from_binlog = false, bool from_database = false); void update_chat(Chat *c, ChatId chat_id, bool from_binlog = false, bool from_database = false); void update_channel(Channel *c, ChannelId channel_id, bool from_binlog = false, bool from_database = false); void update_secret_chat(SecretChat *c, SecretChatId secret_chat_id, bool from_binlog = false, bool from_database = false); - void update_user_full(UserFull *user_full, UserId user_id); - void update_chat_full(ChatFull *chat_full, ChatId chat_id); - void update_channel_full(ChannelFull *channel_full, ChannelId channel_id); + void update_user_full(UserFull *user_full, UserId user_id, bool from_database = false); + void update_chat_full(ChatFull *chat_full, ChatId chat_id, bool from_database = false); + void update_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool from_database = false); - bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id) const; + void update_bot_info(BotInfo *bot_info, UserId user_id, bool send_update, bool from_database); + + bool is_chat_full_outdated(const ChatFull *chat_full, const Chat *c, ChatId chat_id); bool is_user_contact(const User *u, UserId user_id) const; @@ -1040,6 +1230,16 @@ class ContactsManager : public Actor { void on_clear_imported_contacts(vector &&contacts, vector contacts_unique_id, std::pair, vector> &&to_add, Promise &&promise); + static vector> get_chats_nearby_object( + const vector &dialogs_nearby); + + void send_update_users_nearby() const; + + void on_get_dialogs_nearby(Result> result, + Promise> &&promise); + + static bool is_channel_public(const Channel *c); + static bool is_valid_invite_link(const string &invite_link); bool update_invite_link(string &invite_link, tl_object_ptr &&invite_link_ptr); @@ -1054,28 +1254,26 @@ class ContactsManager : public Actor { void on_load_dialog_administrators_from_database(DialogId dialog_id, string value, Promise &&promise); - void on_load_administrator_users_finished(DialogId dialog_id, vector user_ids, Result<> result, - Promise promise); + void on_load_administrator_users_finished(DialogId dialog_id, vector administrators, + Result<> result, Promise promise); void reload_dialog_administrators(DialogId dialog_id, int32 hash, Promise &&promise); tl_object_ptr get_user_status_object(UserId user_id, const User *u) const; - static tl_object_ptr get_link_state_object(LinkState link); - - static tl_object_ptr get_bot_info_object(const BotInfo *bot_info); + td_api::object_ptr get_bot_info_object(UserId user_id) const; tl_object_ptr get_user_object(UserId user_id, const User *u) const; tl_object_ptr get_user_full_info_object(UserId user_id, const UserFull *user_full) const; - tl_object_ptr get_basic_group_object(ChatId chat_id, const Chat *chat); + tl_object_ptr get_basic_group_object(ChatId chat_id, const Chat *c); - tl_object_ptr get_basic_group_object_const(ChatId chat_id, const Chat *chat) const; + tl_object_ptr get_basic_group_object_const(ChatId chat_id, const Chat *c) const; tl_object_ptr get_basic_group_full_info_object(const ChatFull *chat_full) const; - tl_object_ptr get_supergroup_object(ChannelId channel_id, const Channel *channel) const; + tl_object_ptr get_supergroup_object(ChannelId channel_id, const Channel *c) const; tl_object_ptr get_supergroup_full_info_object(const ChannelFull *channel_full) const; @@ -1086,6 +1284,12 @@ class ContactsManager : public Actor { tl_object_ptr get_secret_chat_object_const(SecretChatId secret_chat_id, const SecretChat *secret_chat) const; + vector get_channel_ids(vector> &&chats, const char *source); + + vector get_dialog_ids(vector> &&chats, const char *source); + + void update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable); + void delete_chat_participant(ChatId chat_id, UserId user_id, Promise &&promise); void change_channel_participant_status_impl(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, @@ -1097,17 +1301,23 @@ class ContactsManager : public Actor { void restrict_channel_participant(ChannelId channel_id, UserId user_id, DialogParticipantStatus status, DialogParticipantStatus old_status, Promise &&promise); + void transfer_channel_ownership(ChannelId channel_id, UserId user_id, + tl_object_ptr input_check_password, + 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); + static void on_user_nearby_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); + + static void on_slow_mode_delay_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long); + void on_user_online_timeout(UserId user_id); - template - static void store_link_state(const LinkState &link_state, StorerT &storer); + void on_user_nearby_timeout(UserId user_id); - template - static void parse_link_state(LinkState &link_state, ParserT &parser); + void on_slow_mode_delay_timeout(ChannelId channel_id); void tear_down() override; @@ -1117,8 +1327,10 @@ class ContactsManager : public Actor { UserId support_user_id_; int32 my_was_online_local_ = 0; - std::unordered_map users_; - std::unordered_map users_full_; + std::unordered_map, UserIdHash> users_; + std::unordered_map, UserIdHash> users_full_; + std::unordered_map, UserIdHash> bot_infos_; + std::unordered_map user_photos_; mutable std::unordered_set unknown_users_; std::unordered_map, UserIdHash> pending_user_photos_; struct UserIdPhotoIdHash { @@ -1128,18 +1340,18 @@ class ContactsManager : public Actor { }; std::unordered_map, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_; - std::unordered_map chats_; - std::unordered_map chats_full_; + std::unordered_map, ChatIdHash> chats_; + std::unordered_map, ChatIdHash> chats_full_; mutable std::unordered_set unknown_chats_; std::unordered_map chat_photo_file_source_ids_; std::unordered_set min_channels_; - std::unordered_map channels_; - std::unordered_map channels_full_; + std::unordered_map, ChannelIdHash> channels_; + std::unordered_map, ChannelIdHash> channels_full_; mutable std::unordered_set unknown_channels_; std::unordered_map channel_photo_file_source_ids_; - std::unordered_map secret_chats_; + std::unordered_map, SecretChatIdHash> secret_chats_; mutable std::unordered_set unknown_secret_chats_; std::unordered_map, UserIdHash> secret_chats_with_user_; @@ -1148,17 +1360,27 @@ class ContactsManager : public Actor { std::unordered_map channel_invite_links_; // in-memory cache for invite links std::unordered_map> invite_link_infos_; - bool created_public_channels_inited_ = false; - vector created_public_channels_; + bool created_public_channels_inited_[2] = {false, false}; + vector created_public_channels_[2]; + + bool dialogs_for_discussion_inited_ = false; + vector dialogs_for_discussion_; + + bool inactive_channels_inited_ = false; + vector inactive_channels_; std::unordered_map>, UserIdHash> load_user_from_database_queries_; std::unordered_set loaded_from_database_users_; + std::unordered_set unavailable_user_fulls_; + std::unordered_set unavailable_bot_infos_; std::unordered_map>, ChatIdHash> load_chat_from_database_queries_; std::unordered_set loaded_from_database_chats_; + std::unordered_set unavailable_chat_fulls_; std::unordered_map>, ChannelIdHash> load_channel_from_database_queries_; std::unordered_set loaded_from_database_channels_; + std::unordered_set unavailable_channel_fulls_; std::unordered_map>, SecretChatIdHash> load_secret_chat_from_database_queries_; std::unordered_set loaded_from_database_secret_chats_; @@ -1167,7 +1389,7 @@ class ContactsManager : public Actor { QueryCombiner get_chat_full_queries_{"GetChatFullCombiner", 2.0}; QueryCombiner get_channel_full_queries_{"GetChannelFullCombiner", 2.0}; - std::unordered_map, DialogIdHash> dialog_administrators_; + std::unordered_map, DialogIdHash> dialog_administrators_; class UploadProfilePhotoCallback; std::shared_ptr upload_profile_photo_callback_; @@ -1201,6 +1423,14 @@ class ContactsManager : public Actor { bool are_imported_contacts_changing_ = false; bool need_clear_imported_contacts_ = false; + vector users_nearby_; + vector channels_nearby_; + + std::unordered_map linked_channel_ids_; + + std::unordered_set restricted_user_ids_; + std::unordered_set restricted_channel_ids_; + vector next_all_imported_contacts_; vector imported_contacts_unique_id_; vector imported_contacts_pos_; @@ -1210,6 +1440,8 @@ class ContactsManager : public Actor { MultiTimeout user_online_timeout_{"UserOnlineTimeout"}; MultiTimeout channel_unban_timeout_{"ChannelUnbanTimeout"}; + MultiTimeout user_nearby_timeout_{"UserNearbyTimeout"}; + MultiTimeout slow_mode_delay_timeout_{"SlowModeDelayTimeout"}; }; } // namespace td diff --git a/td/telegram/DelayDispatcher.cpp b/td/telegram/DelayDispatcher.cpp index 428410a3..69ce597f 100644 --- a/td/telegram/DelayDispatcher.cpp +++ b/td/telegram/DelayDispatcher.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DelayDispatcher.h b/td/telegram/DelayDispatcher.h index 563bb82e..dba3ec08 100644 --- a/td/telegram/DelayDispatcher.h +++ b/td/telegram/DelayDispatcher.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Dependencies.h b/td/telegram/Dependencies.h index 6bce2cf3..64cc1adf 100644 --- a/td/telegram/Dependencies.h +++ b/td/telegram/Dependencies.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DeviceTokenManager.cpp b/td/telegram/DeviceTokenManager.cpp index d6b7093a..4806ff19 100644 --- a/td/telegram/DeviceTokenManager.cpp +++ b/td/telegram/DeviceTokenManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -37,6 +37,7 @@ void DeviceTokenManager::TokenInfo::store(StorerT &storer) const { bool is_sync = state == State::Sync; bool is_unregister = state == State::Unregister; bool is_register = state == State::Register; + CHECK(state != State::Reregister); BEGIN_STORE_FLAGS(); STORE_FLAG(has_other_user_ids); STORE_FLAG(is_sync); @@ -88,21 +89,24 @@ void DeviceTokenManager::TokenInfo::parse(ParserT &parser) { } } -StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManager::TokenInfo &token_info) { - switch (token_info.state) { +StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManager::TokenInfo::State &state) { + switch (state) { case DeviceTokenManager::TokenInfo::State::Sync: - string_builder << "Synchronized"; - break; + return string_builder << "Synchronized"; case DeviceTokenManager::TokenInfo::State::Unregister: - string_builder << "Unregister"; - break; + return string_builder << "Unregister"; case DeviceTokenManager::TokenInfo::State::Register: - string_builder << "Register"; - break; + return string_builder << "Register"; + case DeviceTokenManager::TokenInfo::State::Reregister: + return string_builder << "Reregister"; default: UNREACHABLE(); + return string_builder; } - string_builder << " token \"" << format::escaped(token_info.token) << "\""; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManager::TokenInfo &token_info) { + string_builder << token_info.state << " token \"" << format::escaped(token_info.token) << "\""; if (!token_info.other_user_ids.empty()) { string_builder << ", with other users " << token_info.other_user_ids; } @@ -110,7 +114,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManage string_builder << ", sandboxed"; } if (token_info.encrypt) { - string_builder << ", encrypted"; + string_builder << ", encrypted with ID " << token_info.encryption_key_id; } return string_builder; } @@ -268,6 +272,16 @@ void DeviceTokenManager::register_device(tl_object_ptr devi save_info(token_type); } +void DeviceTokenManager::reregister_device() { + for (int32 token_type = 1; token_type < TokenType::SIZE; token_type++) { + auto &token = tokens_[token_type]; + if (token.state == TokenInfo::State::Sync && !token.token.empty()) { + token.state = TokenInfo::State::Reregister; + } + } + loop(); +} + vector> DeviceTokenManager::get_encryption_keys() const { vector> result; for (int32 token_type = 1; token_type < TokenType::SIZE; token_type++) { @@ -317,9 +331,9 @@ void DeviceTokenManager::start_up() { } token.token = serialized.substr(1); } - LOG(INFO) << "GET device token " << token_type << "--->" << token; - if (token.state == TokenInfo::State::Sync) { - token.state = TokenInfo::State::Register; + LOG(INFO) << "Have device token " << token_type << "--->" << token; + if (token.state == TokenInfo::State::Sync && !token.token.empty()) { + token.state = TokenInfo::State::Reregister; } } loop(); @@ -343,7 +357,7 @@ void DeviceTokenManager::dec_sync_cnt() { } void DeviceTokenManager::loop() { - if (sync_cnt_ != 0) { + if (sync_cnt_ != 0 || G()->close_flag()) { return; } for (int32 token_type = 1; token_type < TokenType::SIZE; token_type++) { @@ -361,8 +375,10 @@ void DeviceTokenManager::loop() { net_query = G()->net_query_creator().create( create_storer(telegram_api::account_unregisterDevice(token_type, info.token, std::move(other_user_ids)))); } else { - net_query = G()->net_query_creator().create(create_storer(telegram_api::account_registerDevice( - token_type, info.token, info.is_app_sandbox, BufferSlice(info.encryption_key), std::move(other_user_ids)))); + int32 flags = telegram_api::account_registerDevice::NO_MUTED_MASK; + net_query = G()->net_query_creator().create(create_storer( + telegram_api::account_registerDevice(flags, false /*ignored*/, token_type, info.token, info.is_app_sandbox, + 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)); @@ -378,12 +394,12 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { return; } info.net_query_id = 0; + CHECK(info.state != TokenInfo::State::Sync); + static_assert(std::is_same::value, ""); auto r_flag = fetch_result(std::move(net_query)); - - info.net_query_id = 0; if (r_flag.is_ok() && r_flag.ok()) { if (info.promise) { int64 push_token_id = 0; @@ -401,22 +417,24 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { } info.state = TokenInfo::State::Sync; } else { - if (info.promise) { - if (r_flag.is_error()) { - info.promise.set_error(r_flag.error().clone()); - } else { - info.promise.set_error(Status::Error(5, "Got false as result of server request")); + if (r_flag.is_error()) { + if (!G()->close_flag()) { + LOG(ERROR) << "Failed to " << info.state << " device: " << r_flag.error(); } + info.promise.set_error(r_flag.move_as_error()); + } else { + info.promise.set_error(Status::Error(5, "Got false as result of registerDevice server request")); } - if (info.state == TokenInfo::State::Register) { + if (info.state == TokenInfo::State::Reregister) { + // keep trying to reregister the token + return loop(); + } else if (info.state == TokenInfo::State::Register) { info.state = TokenInfo::State::Unregister; } else { + CHECK(info.state == TokenInfo::State::Unregister); info.state = TokenInfo::State::Sync; info.token.clear(); } - if (r_flag.is_error() && !G()->close_flag()) { - LOG(ERROR) << r_flag.error(); - } } save_info(token_type); } diff --git a/td/telegram/DeviceTokenManager.h b/td/telegram/DeviceTokenManager.h index e03e338e..8d5a78c2 100644 --- a/td/telegram/DeviceTokenManager.h +++ b/td/telegram/DeviceTokenManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -29,6 +29,8 @@ class DeviceTokenManager : public NetQueryCallback { void register_device(tl_object_ptr device_token_ptr, vector other_user_ids, Promise> promise); + void reregister_device(); + vector> get_encryption_keys() const; private: @@ -49,7 +51,7 @@ class DeviceTokenManager : public NetQueryCallback { SIZE }; struct TokenInfo { - enum class State : int32 { Sync, Unregister, Register }; + enum class State : int32 { Sync, Unregister, Register, Reregister }; State state = State::Sync; string token; uint64 net_query_id = 0; @@ -67,6 +69,8 @@ class DeviceTokenManager : public NetQueryCallback { void parse(ParserT &parser); }; + friend StringBuilder &operator<<(StringBuilder &string_builder, const TokenInfo::State &state); + friend StringBuilder &operator<<(StringBuilder &string_builder, const TokenInfo &token_info); std::array tokens_; diff --git a/td/telegram/DhCache.cpp b/td/telegram/DhCache.cpp index 7f202a7c..2f5bba8f 100644 --- a/td/telegram/DhCache.cpp +++ b/td/telegram/DhCache.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DhCache.h b/td/telegram/DhCache.h index 29cd26db..e8f38052 100644 --- a/td/telegram/DhCache.h +++ b/td/telegram/DhCache.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DhConfig.h b/td/telegram/DhConfig.h index e6409272..6d84bc73 100644 --- a/td/telegram/DhConfig.h +++ b/td/telegram/DhConfig.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DialogAdministrator.cpp b/td/telegram/DialogAdministrator.cpp new file mode 100644 index 00000000..76dcccbf --- /dev/null +++ b/td/telegram/DialogAdministrator.cpp @@ -0,0 +1,26 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/DialogAdministrator.h" + +#include "td/telegram/ContactsManager.h" + +namespace td { + +td_api::object_ptr DialogAdministrator::get_chat_administrator_object( + const ContactsManager *contacts_manager) const { + CHECK(contacts_manager != nullptr); + CHECK(user_id_.is_valid()); + return td_api::make_object( + contacts_manager->get_user_id_object(user_id_, "get_chat_administrator_object"), rank_, is_creator_); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogAdministrator &administrator) { + return string_builder << "DialogAdministrator[" << administrator.user_id_ << ", title = " << administrator.rank_ + << ", is_owner = " << administrator.is_creator_ << "]"; +} + +} // namespace td diff --git a/td/telegram/DialogAdministrator.h b/td/telegram/DialogAdministrator.h new file mode 100644 index 00000000..96003d66 --- /dev/null +++ b/td/telegram/DialogAdministrator.h @@ -0,0 +1,89 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" +#include "td/telegram/UserId.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class ContactsManager; + +class DialogAdministrator { + UserId user_id_; + string rank_; + bool is_creator_ = false; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogAdministrator &location); + + public: + DialogAdministrator() = default; + + DialogAdministrator(UserId user_id, const string &rank, bool is_creator) + : user_id_(user_id), rank_(rank), is_creator_(is_creator) { + } + + td_api::object_ptr get_chat_administrator_object( + const ContactsManager *contacts_manager) const; + + UserId get_user_id() const { + return user_id_; + } + + const string &get_rank() const { + return rank_; + } + + bool is_creator() const { + return is_creator_; + } + + template + void store(StorerT &storer) const { + using td::store; + bool has_rank = !rank_.empty(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_rank); + STORE_FLAG(is_creator_); + END_STORE_FLAGS(); + store(user_id_, storer); + if (has_rank) { + store(rank_, storer); + } + } + + template + void parse(ParserT &parser) { + using td::parse; + bool has_rank; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_rank); + PARSE_FLAG(is_creator_); + END_PARSE_FLAGS(); + parse(user_id_, parser); + if (has_rank) { + parse(rank_, parser); + } + } +}; + +inline bool operator==(const DialogAdministrator &lhs, const DialogAdministrator &rhs) { + return lhs.get_user_id() == rhs.get_user_id() && lhs.get_rank() == rhs.get_rank() && + lhs.is_creator() == rhs.is_creator(); +} + +inline bool operator!=(const DialogAdministrator &lhs, const DialogAdministrator &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogAdministrator &administrator); + +} // namespace td diff --git a/td/telegram/DialogDate.h b/td/telegram/DialogDate.h index 2c8c9a03..d03df06d 100644 --- a/td/telegram/DialogDate.h +++ b/td/telegram/DialogDate.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/MessageId.h" +#include "td/telegram/ServerMessageId.h" #include "td/utils/StringBuilder.h" diff --git a/td/telegram/DialogDb.cpp b/td/telegram/DialogDb.cpp index 1277caac..b5c7d4ac 100644 --- a/td/telegram/DialogDb.cpp +++ b/td/telegram/DialogDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -37,32 +37,46 @@ Status init_dialog_db(SqliteDb &db, int32 version, bool &was_created) { TRY_STATUS(drop_dialog_db(db, version)); version = 0; } - auto create_notification_group_table = [&]() { + + 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 = [&]() { + 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]() { + 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"); + }; + if (version == 0) { LOG(INFO) << "Create new dialog database"; was_created = true; TRY_STATUS( - db.exec("CREATE TABLE IF NOT EXISTS dialogs (dialog_id INT8 PRIMARY KEY, dialog_order INT8, data BLOB)")); - TRY_STATUS(db.exec("CREATE INDEX IF NOT EXISTS dialog_by_dialog_order ON dialogs (dialog_order, dialog_id)")); + db.exec("CREATE TABLE IF NOT EXISTS dialogs (dialog_id INT8 PRIMARY KEY, dialog_order INT8, data BLOB, " + "folder_id INT4)")); TRY_STATUS(create_notification_group_table()); TRY_STATUS(create_last_notification_date_index()); + TRY_STATUS(add_dialogs_in_folder_index()); version = current_db_version(); } if (version < static_cast(DbVersion::AddNotificationsSupport)) { TRY_STATUS(create_notification_group_table()); TRY_STATUS(create_last_notification_date_index()); } + if (version < static_cast(DbVersion::AddFolders)) { + 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")); + } return Status::OK(); } @@ -89,36 +103,33 @@ class DialogDbImpl : public DialogDbSyncInterface { } Status init() { - TRY_RESULT(add_dialog_stmt, db_.get_statement("INSERT OR REPLACE INTO dialogs VALUES(?1, ?2, ?3)")); - TRY_RESULT(add_notification_group_stmt, - db_.get_statement("INSERT OR REPLACE INTO notification_groups VALUES(?1, ?2, ?3)")); - TRY_RESULT(delete_notification_group_stmt, - db_.get_statement("DELETE FROM notification_groups WHERE notification_group_id = ?1")); - TRY_RESULT(get_dialog_stmt, db_.get_statement("SELECT data FROM dialogs WHERE dialog_id = ?1")); - TRY_RESULT(get_dialogs_stmt, db_.get_statement("SELECT data, dialog_id, dialog_order FROM dialogs WHERE " - "dialog_order < ?1 OR (dialog_order = ?1 AND dialog_id < ?2) ORDER " - "BY dialog_order DESC, dialog_id DESC LIMIT ?3")); - TRY_RESULT( - get_notification_groups_by_last_notification_date_stmt, + TRY_RESULT_ASSIGN(add_dialog_stmt_, db_.get_statement("INSERT OR REPLACE INTO dialogs VALUES(?1, ?2, ?3, ?4)")); + TRY_RESULT_ASSIGN(add_notification_group_stmt_, + db_.get_statement("INSERT OR REPLACE INTO notification_groups VALUES(?1, ?2, ?3)")); + TRY_RESULT_ASSIGN(delete_notification_group_stmt_, + db_.get_statement("DELETE FROM notification_groups WHERE notification_group_id = ?1")); + TRY_RESULT_ASSIGN(get_dialog_stmt_, db_.get_statement("SELECT data FROM dialogs WHERE dialog_id = ?1")); + TRY_RESULT_ASSIGN( + get_dialogs_stmt_, + db_.get_statement("SELECT data, dialog_id, dialog_order FROM dialogs WHERE " + "folder_id == ?1 AND (dialog_order < ?2 OR (dialog_order = ?2 AND dialog_id < ?3)) ORDER " + "BY dialog_order DESC, dialog_id DESC LIMIT ?4")); + TRY_RESULT_ASSIGN( + get_notification_groups_by_last_notification_date_stmt_, db_.get_statement("SELECT notification_group_id, dialog_id, last_notification_date FROM notification_groups " "WHERE last_notification_date < ?1 OR (last_notification_date = ?1 " "AND (dialog_id < ?2 OR (dialog_id = ?2 AND notification_group_id < ?3))) ORDER BY " "last_notification_date DESC, dialog_id DESC LIMIT ?4")); // "WHERE (last_notification_date, dialog_id, notification_group_id) < (?1, ?2, ?3) ORDER BY " // "last_notification_date DESC, dialog_id DESC, notification_group_id DESC LIMIT ?4")); - TRY_RESULT( - get_notification_group_stmt, + TRY_RESULT_ASSIGN( + get_notification_group_stmt_, db_.get_statement( "SELECT dialog_id, last_notification_date FROM notification_groups WHERE notification_group_id = ?1")); - - add_dialog_stmt_ = std::move(add_dialog_stmt); - add_notification_group_stmt_ = std::move(add_notification_group_stmt); - delete_notification_group_stmt_ = std::move(delete_notification_group_stmt); - get_dialog_stmt_ = std::move(get_dialog_stmt); - get_dialogs_stmt_ = std::move(get_dialogs_stmt); - get_notification_groups_by_last_notification_date_stmt_ = - std::move(get_notification_groups_by_last_notification_date_stmt); - get_notification_group_stmt_ = std::move(get_notification_group_stmt); + 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")); // LOG(ERROR) << get_dialog_stmt_.explain().ok(); // LOG(ERROR) << get_dialogs_stmt_.explain().ok(); @@ -129,7 +140,7 @@ class DialogDbImpl : public DialogDbSyncInterface { return Status::OK(); } - Status add_dialog(DialogId dialog_id, int64 order, BufferSlice data, + Status add_dialog(DialogId dialog_id, FolderId folder_id, int64 order, BufferSlice data, vector notification_groups) override { SCOPE_EXIT { add_dialog_stmt_.reset(); @@ -137,6 +148,11 @@ class DialogDbImpl : public DialogDbSyncInterface { add_dialog_stmt_.bind_int64(1, dialog_id.get()).ensure(); add_dialog_stmt_.bind_int64(2, order).ensure(); add_dialog_stmt_.bind_blob(3, data.as_slice()).ensure(); + if (order > 0) { + add_dialog_stmt_.bind_int32(4, folder_id.get()).ensure(); + } else { + add_dialog_stmt_.bind_null(4).ensure(); + } TRY_STATUS(add_dialog_stmt_.step()); @@ -190,27 +206,39 @@ class DialogDbImpl : public DialogDbSyncInterface { get_last_notification_date(get_notification_group_stmt_, 1)); } - Result> get_dialogs(int64 order, DialogId dialog_id, int32 limit) override { + Result get_secret_chat_count(FolderId folder_id) override { + SCOPE_EXIT { + get_secret_chat_count_stmt_.reset(); + }; + get_secret_chat_count_stmt_.bind_int32(1, folder_id.get()).ensure(); + TRY_STATUS(get_secret_chat_count_stmt_.step()); + CHECK(get_secret_chat_count_stmt_.has_row()); + return get_secret_chat_count_stmt_.view_int32(0); + } + + Result get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, + int32 limit) override { SCOPE_EXIT { get_dialogs_stmt_.reset(); }; - get_dialogs_stmt_.bind_int64(1, order).ensure(); - get_dialogs_stmt_.bind_int64(2, dialog_id.get()).ensure(); - get_dialogs_stmt_.bind_int32(3, limit).ensure(); + get_dialogs_stmt_.bind_int32(1, folder_id.get()).ensure(); + get_dialogs_stmt_.bind_int64(2, order).ensure(); + get_dialogs_stmt_.bind_int64(3, dialog_id.get()).ensure(); + get_dialogs_stmt_.bind_int32(4, limit).ensure(); - vector dialogs; + DialogDbGetDialogsResult result; TRY_STATUS(get_dialogs_stmt_.step()); while (get_dialogs_stmt_.has_row()) { BufferSlice data(get_dialogs_stmt_.view_blob(0)); - auto loaded_dialog_id = get_dialogs_stmt_.view_int64(1); - auto loaded_dialog_order = get_dialogs_stmt_.view_int64(2); - LOG(INFO) << "Load chat " << loaded_dialog_id << " with order " << loaded_dialog_order; - dialogs.emplace_back(std::move(data)); + result.next_dialog_id = DialogId(get_dialogs_stmt_.view_int64(1)); + result.next_order = get_dialogs_stmt_.view_int64(2); + LOG(INFO) << "Load " << result.next_dialog_id << " with order " << result.next_order; + result.dialogs.emplace_back(std::move(data)); TRY_STATUS(get_dialogs_stmt_.step()); } - return std::move(dialogs); + return std::move(result); } Result> get_notification_groups_by_last_notification_date( NotificationGroupKey notification_group_key, int32 limit) override { @@ -251,6 +279,7 @@ class DialogDbImpl : public DialogDbSyncInterface { SqliteStatement get_dialogs_stmt_; SqliteStatement get_notification_groups_by_last_notification_date_stmt_; SqliteStatement get_notification_group_stmt_; + SqliteStatement get_secret_chat_count_stmt_; static int32 get_last_notification_date(SqliteStatement &stmt, int id) { if (stmt.view_datatype(id) == SqliteStatement::Datatype::Null) { @@ -285,9 +314,9 @@ class DialogDbAsync : public DialogDbAsyncInterface { impl_ = create_actor_on_scheduler("DialogDbActor", scheduler_id, std::move(sync_db)); } - void add_dialog(DialogId dialog_id, int64 order, BufferSlice data, vector notification_groups, - Promise<> promise) override { - send_closure(impl_, &Impl::add_dialog, dialog_id, order, std::move(data), std::move(notification_groups), + void add_dialog(DialogId dialog_id, FolderId folder_id, int64 order, BufferSlice data, + vector notification_groups, Promise<> promise) override { + send_closure(impl_, &Impl::add_dialog, dialog_id, folder_id, order, std::move(data), std::move(notification_groups), std::move(promise)); } @@ -302,12 +331,19 @@ class DialogDbAsync : public DialogDbAsyncInterface { send_closure(impl_, &Impl::get_notification_group, notification_group_id, std::move(promise)); } + void get_secret_chat_count(FolderId folder_id, Promise promise) override { + send_closure(impl_, &Impl::get_secret_chat_count, folder_id, std::move(promise)); + } + void get_dialog(DialogId dialog_id, Promise promise) override { send_closure_later(impl_, &Impl::get_dialog, dialog_id, std::move(promise)); } - void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) override { - send_closure_later(impl_, &Impl::get_dialogs, order, dialog_id, limit, std::move(promise)); + + void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise promise) override { + send_closure_later(impl_, &Impl::get_dialogs, folder_id, order, dialog_id, limit, std::move(promise)); } + void close(Promise<> promise) override { send_closure_later(impl_, &Impl::close, std::move(promise)); } @@ -317,17 +353,20 @@ class DialogDbAsync : public DialogDbAsyncInterface { public: explicit Impl(std::shared_ptr sync_db_safe) : sync_db_safe_(std::move(sync_db_safe)) { } - void add_dialog(DialogId dialog_id, int64 order, BufferSlice data, vector notification_groups, - Promise<> promise) { + + void add_dialog(DialogId dialog_id, FolderId folder_id, int64 order, BufferSlice data, + vector notification_groups, Promise<> promise) { add_write_query([=, promise = std::move(promise), data = std::move(data), notification_groups = std::move(notification_groups)](Unit) mutable { - this->on_write_result(std::move(promise), - sync_db_->add_dialog(dialog_id, order, std::move(data), std::move(notification_groups))); + this->on_write_result(std::move(promise), sync_db_->add_dialog(dialog_id, folder_id, order, std::move(data), + std::move(notification_groups))); }); } + void on_write_result(Promise<> promise, Status status) { pending_write_results_.emplace_back(std::move(promise), std::move(status)); } + void get_notification_groups_by_last_notification_date(NotificationGroupKey notification_group_key, int32 limit, Promise> promise) { add_read_query(); @@ -338,14 +377,23 @@ class DialogDbAsync : public DialogDbAsyncInterface { add_read_query(); promise.set_result(sync_db_->get_notification_group(notification_group_id)); } + + void get_secret_chat_count(FolderId folder_id, Promise promise) { + add_read_query(); + promise.set_result(sync_db_->get_secret_chat_count(folder_id)); + } + void get_dialog(DialogId dialog_id, Promise promise) { add_read_query(); promise.set_result(sync_db_->get_dialog(dialog_id)); } - void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) { + + void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise promise) { add_read_query(); - promise.set_result(sync_db_->get_dialogs(order, dialog_id, limit)); + promise.set_result(sync_db_->get_dialogs(folder_id, order, dialog_id, limit)); } + void close(Promise<> promise) { do_flush(); sync_db_safe_.reset(); @@ -365,6 +413,7 @@ class DialogDbAsync : public DialogDbAsyncInterface { std::vector, Status>> pending_write_results_; vector> pending_writes_; double wakeup_at_ = 0; + template void add_write_query(F &&f) { pending_writes_.push_back(PromiseCreator::lambda(std::forward(f), PromiseCreator::Ignore())); @@ -378,9 +427,11 @@ class DialogDbAsync : public DialogDbAsyncInterface { set_timeout_at(wakeup_at_); } } + void add_read_query() { do_flush(); } + void do_flush() { if (pending_writes_.empty()) { return; @@ -397,6 +448,7 @@ class DialogDbAsync : public DialogDbAsyncInterface { pending_write_results_.clear(); cancel_timeout(); } + void timeout_expired() override { do_flush(); } diff --git a/td/telegram/DialogDb.h b/td/telegram/DialogDb.h index ede45031..3212c76f 100644 --- a/td/telegram/DialogDb.h +++ b/td/telegram/DialogDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,6 +7,7 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/FolderId.h" #include "td/telegram/NotificationGroupId.h" #include "td/telegram/NotificationGroupKey.h" @@ -24,6 +25,12 @@ namespace td { class SqliteConnectionSafe; class SqliteDb; +struct DialogDbGetDialogsResult { + vector dialogs; + int64 next_order = 0; + DialogId next_dialog_id; +}; + class DialogDbSyncInterface { public: DialogDbSyncInterface() = default; @@ -31,18 +38,21 @@ class DialogDbSyncInterface { DialogDbSyncInterface &operator=(const DialogDbSyncInterface &) = delete; virtual ~DialogDbSyncInterface() = default; - virtual Status add_dialog(DialogId dialog_id, int64 order, BufferSlice data, + virtual Status add_dialog(DialogId dialog_id, FolderId folder_id, int64 order, BufferSlice data, vector notification_groups) = 0; virtual Result get_dialog(DialogId dialog_id) = 0; - virtual Result> get_dialogs(int64 order, DialogId dialog_id, int32 limit) = 0; + virtual Result get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, + int32 limit) = 0; virtual Result> get_notification_groups_by_last_notification_date( NotificationGroupKey notification_group_key, int32 limit) = 0; virtual Result get_notification_group(NotificationGroupId notification_group_id) = 0; + virtual Result get_secret_chat_count(FolderId folder_id) = 0; + virtual Status begin_transaction() = 0; virtual Status commit_transaction() = 0; }; @@ -64,12 +74,13 @@ class DialogDbAsyncInterface { DialogDbAsyncInterface &operator=(const DialogDbAsyncInterface &) = delete; virtual ~DialogDbAsyncInterface() = default; - virtual void add_dialog(DialogId dialog_id, int64 order, BufferSlice data, + virtual void add_dialog(DialogId dialog_id, FolderId folder_id, int64 order, BufferSlice data, vector notification_groups, Promise<> promise) = 0; virtual void get_dialog(DialogId dialog_id, Promise promise) = 0; - virtual void get_dialogs(int64 order, DialogId dialog_id, int32 limit, Promise> promise) = 0; + virtual void get_dialogs(FolderId folder_id, int64 order, DialogId dialog_id, int32 limit, + Promise promise) = 0; virtual void get_notification_groups_by_last_notification_date(NotificationGroupKey notification_group_key, int32 limit, @@ -78,6 +89,8 @@ class DialogDbAsyncInterface { virtual void get_notification_group(NotificationGroupId notification_group_id, Promise promise) = 0; + virtual void get_secret_chat_count(FolderId folder_id, Promise promise) = 0; + virtual void close(Promise<> promise) = 0; }; diff --git a/td/telegram/DialogId.cpp b/td/telegram/DialogId.cpp index 0ccf068b..6fc4646e 100644 --- a/td/telegram/DialogId.cpp +++ b/td/telegram/DialogId.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -103,8 +103,20 @@ DialogId::DialogId(SecretChatId chat_id) { } } -DialogId::DialogId(const tl_object_ptr &dialog_peer) { - id = get_peer_id(dialog_peer->peer_); +DialogId::DialogId(const tl_object_ptr &dialog_peer) { + CHECK(dialog_peer != nullptr); + switch (dialog_peer->get_id()) { + case telegram_api::dialogPeer::ID: + id = get_peer_id(static_cast(dialog_peer.get())->peer_); + break; + case telegram_api::dialogPeerFolder::ID: + LOG(ERROR) << "Receive unsupported " << to_string(dialog_peer); + id = 0; + break; + default: + id = 0; + UNREACHABLE(); + } } DialogId::DialogId(const tl_object_ptr &peer) : id(get_peer_id(peer)) { diff --git a/td/telegram/DialogId.h b/td/telegram/DialogId.h index 8fc1f591..89882437 100644 --- a/td/telegram/DialogId.h +++ b/td/telegram/DialogId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -45,7 +45,7 @@ class DialogId { template ::value>> DialogId(T dialog_id) = delete; - explicit DialogId(const tl_object_ptr &dialog_peer); + explicit DialogId(const tl_object_ptr &dialog_peer); explicit DialogId(const tl_object_ptr &peer); explicit DialogId(UserId user_id); explicit DialogId(ChatId chat_id); diff --git a/td/telegram/DialogLocation.cpp b/td/telegram/DialogLocation.cpp new file mode 100644 index 00000000..586f73d0 --- /dev/null +++ b/td/telegram/DialogLocation.cpp @@ -0,0 +1,63 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/DialogLocation.h" + +#include "td/telegram/misc.h" + +namespace td { + +DialogLocation::DialogLocation(telegram_api::object_ptr &&channel_location_ptr) { + if (channel_location_ptr != nullptr && channel_location_ptr->get_id() == telegram_api::channelLocation::ID) { + auto channel_location = static_cast(channel_location_ptr.get()); + location_ = Location(channel_location->geo_point_); + address_ = std::move(channel_location->address_); + } +} + +DialogLocation::DialogLocation(td_api::object_ptr &&chat_location) { + if (chat_location != nullptr) { + location_ = Location(chat_location->location_); + address_ = std::move(chat_location->address_); + if (!clean_input_string(address_)) { + address_.clear(); + } + } +} + +bool DialogLocation::empty() const { + return location_.empty(); +} + +td_api::object_ptr DialogLocation::get_chat_location_object() const { + if (empty()) { + return nullptr; + } + return td_api::make_object(location_.get_location_object(), address_); +} + +telegram_api::object_ptr DialogLocation::get_input_geo_point() const { + return location_.get_input_geo_point(); +} + +const string &DialogLocation::get_address() const { + return address_; +} + +bool operator==(const DialogLocation &lhs, const DialogLocation &rhs) { + return lhs.location_ == rhs.location_ && lhs.address_ == rhs.address_; +} + +bool operator!=(const DialogLocation &lhs, const DialogLocation &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogLocation &location) { + return string_builder << "DialogLocation[location = " << location.location_ << ", address = " << location.address_ + << "]"; +} + +} // namespace td diff --git a/td/telegram/DialogLocation.h b/td/telegram/DialogLocation.h new file mode 100644 index 00000000..540ee86a --- /dev/null +++ b/td/telegram/DialogLocation.h @@ -0,0 +1,63 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/Location.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class DialogLocation { + Location location_; + string address_; + + friend bool operator==(const DialogLocation &lhs, const DialogLocation &rhs); + friend bool operator!=(const DialogLocation &lhs, const DialogLocation &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogLocation &location); + + public: + DialogLocation() = default; + + explicit DialogLocation(telegram_api::object_ptr &&channel_location_ptr); + + explicit DialogLocation(td_api::object_ptr &&chat_location); + + bool empty() const; + + td_api::object_ptr get_chat_location_object() const; + + telegram_api::object_ptr get_input_geo_point() const; + + const string &get_address() const; + + template + void store(StorerT &storer) const { + using td::store; + store(location_, storer); + store(address_, storer); + } + + template + void parse(ParserT &parser) { + using td::parse; + parse(location_, parser); + parse(address_, parser); + } +}; + +bool operator==(const DialogLocation &lhs, const DialogLocation &rhs); +bool operator!=(const DialogLocation &lhs, const DialogLocation &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const DialogLocation &location); + +} // namespace td diff --git a/td/telegram/DialogParticipant.cpp b/td/telegram/DialogParticipant.cpp index bf76a35c..732a0de4 100644 --- a/td/telegram/DialogParticipant.cpp +++ b/td/telegram/DialogParticipant.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,6 +7,7 @@ #include "td/telegram/DialogParticipant.h" #include "td/telegram/Global.h" +#include "td/telegram/misc.h" #include "td/utils/common.h" #include "td/utils/logging.h" @@ -15,6 +16,10 @@ namespace td { +DialogParticipantStatus::DialogParticipantStatus(Type type, uint32 flags, int32 until_date, string rank) + : type_(type), flags_(flags), until_date_(until_date), rank_(strip_empty_characters(std::move(rank), 16)) { +} + int32 DialogParticipantStatus::fix_until_date(int32 date) { if (date == std::numeric_limits::max() || date < 0) { return 0; @@ -22,12 +27,13 @@ int32 DialogParticipantStatus::fix_until_date(int32 date) { return date; } -DialogParticipantStatus DialogParticipantStatus::Creator(bool is_member) { +DialogParticipantStatus DialogParticipantStatus::Creator(bool is_member, string rank) { return DialogParticipantStatus(Type::Creator, - ALL_ADMINISTRATOR_RIGHTS | ALL_PERMISSION_RIGHTS | (is_member ? IS_MEMBER : 0), 0); + ALL_ADMINISTRATOR_RIGHTS | ALL_PERMISSION_RIGHTS | (is_member ? IS_MEMBER : 0), 0, + std::move(rank)); } -DialogParticipantStatus DialogParticipantStatus::Administrator(bool can_be_edited, bool can_change_info, +DialogParticipantStatus DialogParticipantStatus::Administrator(string rank, bool can_be_edited, bool can_change_info, bool can_post_messages, bool can_edit_messages, bool can_delete_messages, bool can_invite_users, bool can_restrict_members, bool can_pin_messages, @@ -44,11 +50,11 @@ DialogParticipantStatus DialogParticipantStatus::Administrator(bool can_be_edite if (flags == 0 || flags == CAN_BE_EDITED) { return Member(); } - return DialogParticipantStatus(Type::Administrator, IS_MEMBER | ALL_RESTRICTED_RIGHTS | flags, 0); + return DialogParticipantStatus(Type::Administrator, IS_MEMBER | ALL_RESTRICTED_RIGHTS | flags, 0, std::move(rank)); } DialogParticipantStatus DialogParticipantStatus::Member() { - return DialogParticipantStatus(Type::Member, IS_MEMBER | ALL_PERMISSION_RIGHTS, 0); + return DialogParticipantStatus(Type::Member, IS_MEMBER | ALL_PERMISSION_RIGHTS, 0, string()); } DialogParticipantStatus DialogParticipantStatus::Restricted( @@ -70,26 +76,26 @@ DialogParticipantStatus DialogParticipantStatus::Restricted( if (flags == (IS_MEMBER | ALL_PERMISSION_RIGHTS)) { return Member(); } - return DialogParticipantStatus(Type::Restricted, flags, fix_until_date(restricted_until_date)); + return DialogParticipantStatus(Type::Restricted, flags, fix_until_date(restricted_until_date), string()); } DialogParticipantStatus DialogParticipantStatus::Left() { - return DialogParticipantStatus(Type::Left, ALL_PERMISSION_RIGHTS, 0); + return DialogParticipantStatus(Type::Left, ALL_PERMISSION_RIGHTS, 0, string()); } DialogParticipantStatus DialogParticipantStatus::Banned(int32 banned_until_date) { - return DialogParticipantStatus(Type::Banned, 0, fix_until_date(banned_until_date)); + return DialogParticipantStatus(Type::Banned, 0, fix_until_date(banned_until_date), string()); } DialogParticipantStatus DialogParticipantStatus::GroupAdministrator(bool is_creator) { - return DialogParticipantStatus::Administrator(is_creator, true, false, false, true, true, true, true, false); + return Administrator(string(), is_creator, true, false, false, true, true, true, true, false); } DialogParticipantStatus DialogParticipantStatus::ChannelAdministrator(bool is_creator, bool is_megagroup) { if (is_megagroup) { - return DialogParticipantStatus::Administrator(is_creator, true, false, false, true, true, true, true, false); + return Administrator(string(), is_creator, true, false, false, true, true, true, true, false); } else { - return DialogParticipantStatus::Administrator(is_creator, false, true, true, true, false, true, false, false); + return Administrator(string(), is_creator, false, true, true, true, false, true, false, false); } } @@ -102,20 +108,20 @@ RestrictedRights DialogParticipantStatus::get_restricted_rights() const { tl_object_ptr DialogParticipantStatus::get_chat_member_status_object() const { switch (type_) { case Type::Creator: - return make_tl_object(is_member()); + return td_api::make_object(rank_, is_member()); case Type::Administrator: - return make_tl_object( - can_be_edited(), can_change_info_and_settings(), can_post_messages(), can_edit_messages(), + return td_api::make_object( + rank_, can_be_edited(), can_change_info_and_settings(), can_post_messages(), can_edit_messages(), can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), can_promote_members()); case Type::Member: - return make_tl_object(); + return td_api::make_object(); case Type::Restricted: - return make_tl_object(is_member(), until_date_, - get_restricted_rights().get_chat_permissions_object()); + return td_api::make_object( + is_member(), until_date_, get_restricted_rights().get_chat_permissions_object()); case Type::Left: - return make_tl_object(); + return td_api::make_object(); case Type::Banned: - return make_tl_object(until_date_); + return td_api::make_object(until_date_); default: UNREACHABLE(); return nullptr; @@ -225,14 +231,14 @@ DialogParticipantStatus DialogParticipantStatus::apply_restrictions(RestrictedRi } break; case Type::Banned: - // banned can do nothing, even restirctions allows them to do that + // banned can do nothing, even restrictions allows them to do that break; default: UNREACHABLE(); break; } - return DialogParticipantStatus(type_, flags, 0); + return DialogParticipantStatus(type_, flags, 0, string()); } void DialogParticipantStatus::update_restrictions() const { @@ -254,7 +260,8 @@ void DialogParticipantStatus::update_restrictions() const { } bool operator==(const DialogParticipantStatus &lhs, const DialogParticipantStatus &rhs) { - return lhs.type_ == rhs.type_ && lhs.flags_ == rhs.flags_ && lhs.until_date_ == rhs.until_date_; + return lhs.type_ == rhs.type_ && lhs.flags_ == rhs.flags_ && lhs.until_date_ == rhs.until_date_ && + lhs.rank_ == rhs.rank_; } bool operator!=(const DialogParticipantStatus &lhs, const DialogParticipantStatus &rhs) { @@ -268,6 +275,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant if (!status.is_member()) { string_builder << "-non-member"; } + if (!status.rank_.empty()) { + string_builder << " [" << status.rank_ << "]"; + } return string_builder; case DialogParticipantStatus::Type::Administrator: string_builder << "Administrator: "; @@ -295,6 +305,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant if (status.can_promote_members()) { string_builder << "(promote)"; } + if (!status.rank_.empty()) { + string_builder << " [" << status.rank_ << "]"; + } return string_builder; case DialogParticipantStatus::Type::Member: return string_builder << "Member"; @@ -364,14 +377,14 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr(status.get()); - return DialogParticipantStatus::Creator(st->is_member_); + return DialogParticipantStatus::Creator(st->is_member_, st->custom_title_); } case td_api::chatMemberStatusAdministrator::ID: { auto st = static_cast(status.get()); - return DialogParticipantStatus::Administrator(st->can_be_edited_, st->can_change_info_, st->can_post_messages_, - st->can_edit_messages_, st->can_delete_messages_, - st->can_invite_users_, st->can_restrict_members_, - st->can_pin_messages_, st->can_promote_members_); + return DialogParticipantStatus::Administrator( + st->custom_title_, st->can_be_edited_, st->can_change_info_, st->can_post_messages_, st->can_edit_messages_, + st->can_delete_messages_, st->can_invite_users_, st->can_restrict_members_, st->can_pin_messages_, + st->can_promote_members_); } case td_api::chatMemberStatusMember::ID: return DialogParticipantStatus::Member(); @@ -401,8 +414,9 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr &admin_rights) { +DialogParticipantStatus get_dialog_participant_status(bool can_be_edited, + const tl_object_ptr &admin_rights, + string rank) { bool can_change_info = (admin_rights->flags_ & telegram_api::chatAdminRights::CHANGE_INFO_MASK) != 0; bool can_post_messages = (admin_rights->flags_ & telegram_api::chatAdminRights::POST_MESSAGES_MASK) != 0; bool can_edit_messages = (admin_rights->flags_ & telegram_api::chatAdminRights::EDIT_MESSAGES_MASK) != 0; @@ -411,9 +425,9 @@ DialogParticipantStatus get_dialog_participant_status( bool can_restrict_members = (admin_rights->flags_ & telegram_api::chatAdminRights::BAN_USERS_MASK) != 0; bool can_pin_messages = (admin_rights->flags_ & telegram_api::chatAdminRights::PIN_MESSAGES_MASK) != 0; bool can_promote_members = (admin_rights->flags_ & telegram_api::chatAdminRights::ADD_ADMINS_MASK) != 0; - return DialogParticipantStatus::Administrator(can_be_edited, can_change_info, can_post_messages, can_edit_messages, - can_delete_messages, can_invite_users, can_restrict_members, - can_pin_messages, can_promote_members); + return DialogParticipantStatus::Administrator(std::move(rank), can_be_edited, can_change_info, can_post_messages, + can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members); } DialogParticipantStatus get_dialog_participant_status( diff --git a/td/telegram/DialogParticipant.h b/td/telegram/DialogParticipant.h index 286c23aa..d75acf8b 100644 --- a/td/telegram/DialogParticipant.h +++ b/td/telegram/DialogParticipant.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -135,7 +135,8 @@ class DialogParticipantStatus { static constexpr uint32 IS_MEMBER = 1 << 27; - // bits 28-31 reserved for Type and until_date flag + static constexpr uint32 HAS_RANK = 1u << 14; + // bits 28-30 reserved for Type static constexpr int TYPE_SHIFT = 28; static constexpr uint32 HAS_UNTIL_DATE = 1u << 31; @@ -157,19 +158,18 @@ class DialogParticipantStatus { mutable Type type_; mutable uint32 flags_; mutable int32 until_date_; // restricted and banned only + string rank_; // creator and administrator only static int32 fix_until_date(int32 date); - DialogParticipantStatus(Type type, uint32 flags, int32 until_date) - : type_(type), flags_(flags), until_date_(until_date) { - } + DialogParticipantStatus(Type type, uint32 flags, int32 until_date, string rank); public: - static DialogParticipantStatus Creator(bool is_member); + static DialogParticipantStatus Creator(bool is_member, string rank); - static DialogParticipantStatus Administrator(bool can_be_edited, bool can_change_info, bool can_post_messages, - bool can_edit_messages, bool can_delete_messages, bool can_invite_users, - bool can_restrict_members, bool can_pin_messages, + static DialogParticipantStatus Administrator(string rank, bool can_be_edited, bool can_change_info, + bool can_post_messages, bool can_edit_messages, bool can_delete_messages, + bool can_invite_users, bool can_restrict_members, bool can_pin_messages, bool can_promote_members); static DialogParticipantStatus Member(); @@ -308,16 +308,26 @@ class DialogParticipantStatus { return until_date_; } + const string &get_rank() const { + return rank_; + } + template void store(StorerT &storer) const { uint32 stored_flags = flags_ | (static_cast(type_) << TYPE_SHIFT); if (until_date_ > 0) { stored_flags |= HAS_UNTIL_DATE; } + if (!rank_.empty()) { + stored_flags |= HAS_RANK; + } td::store(stored_flags, storer); if (until_date_ > 0) { td::store(until_date_, storer); } + if (!rank_.empty()) { + td::store(rank_, storer); + } } template @@ -328,6 +338,10 @@ class DialogParticipantStatus { td::parse(until_date_, parser); stored_flags &= ~HAS_UNTIL_DATE; } + if ((stored_flags & HAS_RANK) != 0) { + td::parse(rank_, parser); + stored_flags &= ~HAS_RANK; + } type_ = static_cast(stored_flags >> TYPE_SHIFT); flags_ = stored_flags & ((1 << TYPE_SHIFT) - 1); } @@ -354,6 +368,22 @@ struct DialogParticipant { DialogParticipant(UserId user_id, UserId inviter_user_id, int32 joined_date, DialogParticipantStatus status) : user_id(user_id), inviter_user_id(inviter_user_id), joined_date(joined_date), status(status) { } + + template + void store(StorerT &storer) const { + td::store(user_id, storer); + td::store(inviter_user_id, storer); + td::store(joined_date, storer); + td::store(status, storer); + } + + template + void parse(ParserT &parser) { + td::parse(user_id, parser); + td::parse(inviter_user_id, parser); + td::parse(joined_date, parser); + td::parse(status, parser); + } }; StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant &dialog_participant); @@ -407,7 +437,8 @@ DialogParticipantsFilter get_dialog_participants_filter(const tl_object_ptr &status); DialogParticipantStatus get_dialog_participant_status(bool can_be_edited, - const tl_object_ptr &admin_rights); + const tl_object_ptr &admin_rights, + string rank); DialogParticipantStatus get_dialog_participant_status( bool is_member, const tl_object_ptr &banned_rights); diff --git a/td/telegram/Document.cpp b/td/telegram/Document.cpp index f1373c0d..df0a2ac3 100644 --- a/td/telegram/Document.cpp +++ b/td/telegram/Document.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Document.h b/td/telegram/Document.h index 37eb26e5..ad09baa4 100644 --- a/td/telegram/Document.h +++ b/td/telegram/Document.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Document.hpp b/td/telegram/Document.hpp index 403dbb8f..a304cc3b 100644 --- a/td/telegram/Document.hpp +++ b/td/telegram/Document.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DocumentsManager.cpp b/td/telegram/DocumentsManager.cpp index 51ee96b1..87edd124 100644 --- a/td/telegram/DocumentsManager.cpp +++ b/td/telegram/DocumentsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -523,7 +523,7 @@ SecretInputMedia DocumentsManager::get_secret_input_media(FileId document_file_i return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -550,8 +550,8 @@ tl_object_ptr DocumentsManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); diff --git a/td/telegram/DocumentsManager.h b/td/telegram/DocumentsManager.h index 232b1d9a..761881a5 100644 --- a/td/telegram/DocumentsManager.h +++ b/td/telegram/DocumentsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DocumentsManager.hpp b/td/telegram/DocumentsManager.hpp index 4900c1b1..e697952b 100644 --- a/td/telegram/DocumentsManager.hpp +++ b/td/telegram/DocumentsManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DraftMessage.cpp b/td/telegram/DraftMessage.cpp index ca8f987e..badc2a77 100644 --- a/td/telegram/DraftMessage.cpp +++ b/td/telegram/DraftMessage.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/telegram/MessageEntity.h" #include "td/telegram/misc.h" +#include "td/telegram/ServerMessageId.h" #include "td/utils/logging.h" diff --git a/td/telegram/DraftMessage.h b/td/telegram/DraftMessage.h index 472ab260..a148cf63 100644 --- a/td/telegram/DraftMessage.h +++ b/td/telegram/DraftMessage.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/DraftMessage.hpp b/td/telegram/DraftMessage.hpp index c9545360..f00f745a 100644 --- a/td/telegram/DraftMessage.hpp +++ b/td/telegram/DraftMessage.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/FileReferenceManager.cpp b/td/telegram/FileReferenceManager.cpp index db2fef24..01ebd169 100644 --- a/td/telegram/FileReferenceManager.cpp +++ b/td/telegram/FileReferenceManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +12,7 @@ #include "td/telegram/files/FileManager.h" #include "td/telegram/Global.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/StickersManager.h" #include "td/telegram/WebPagesManager.h" @@ -19,6 +20,7 @@ #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/overloaded.h" +#include "td/utils/Time.h" namespace td { @@ -354,7 +356,7 @@ void FileReferenceManager::reload_photo(PhotoSizeSource source, Promise pr break; case PhotoSizeSource::Type::StickerSetThumbnail: send_closure(G()->stickers_manager(), &StickersManager::reload_sticker_set, - source.sticker_set_thumbnail().sticker_set_id, + StickerSetId(source.sticker_set_thumbnail().sticker_set_id), source.sticker_set_thumbnail().sticker_set_access_hash, std::move(promise)); break; default: diff --git a/td/telegram/FileReferenceManager.h b/td/telegram/FileReferenceManager.h index 94cbd3e3..f7d2d4ed 100644 --- a/td/telegram/FileReferenceManager.h +++ b/td/telegram/FileReferenceManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,7 +14,7 @@ #include "td/telegram/ChatId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" -#include "td/telegram/MessageId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/PhotoSizeSource.h" #include "td/telegram/SetWithPosition.h" #include "td/telegram/UserId.h" diff --git a/td/telegram/FileReferenceManager.hpp b/td/telegram/FileReferenceManager.hpp index 4409c41f..204cf664 100644 --- a/td/telegram/FileReferenceManager.hpp +++ b/td/telegram/FileReferenceManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,7 +13,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileSourceId.h" -#include "td/telegram/MessageId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" diff --git a/td/telegram/FolderId.h b/td/telegram/FolderId.h new file mode 100644 index 00000000..b0b28994 --- /dev/null +++ b/td/telegram/FolderId.h @@ -0,0 +1,78 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +#include +#include + +namespace td { + +class FolderId { + int32 id = 0; + + public: + FolderId() = default; + + explicit FolderId(int32 folder_id) : id(folder_id) { + } + template ::value>> + FolderId(T folder_id) = delete; + + explicit FolderId(const td_api::object_ptr &chat_list) { + if (chat_list != nullptr && chat_list->get_id() == td_api::chatListArchive::ID) { + id = 1; + } else { + CHECK(id == 0); + } + } + + int32 get() const { + return id; + } + + bool operator==(const FolderId &other) const { + return id == other.id; + } + + bool operator!=(const FolderId &other) const { + return id != other.id; + } + + template + void store(StorerT &storer) const { + storer.store_int(id); + } + + template + void parse(ParserT &parser) { + id = parser.fetch_int(); + } + + static FolderId main() { + return FolderId(); + } + static FolderId archive() { + return FolderId(1); + } +}; + +struct FolderIdHash { + std::size_t operator()(FolderId folder_id) const { + return std::hash()(folder_id.get()); + } +}; + +inline StringBuilder &operator<<(StringBuilder &string_builder, FolderId folder_id) { + return string_builder << "folder " << folder_id.get(); +} + +} // namespace td diff --git a/td/telegram/FullMessageId.h b/td/telegram/FullMessageId.h new file mode 100644 index 00000000..525d4130 --- /dev/null +++ b/td/telegram/FullMessageId.h @@ -0,0 +1,68 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/DialogId.h" +#include "td/telegram/MessageId.h" + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +struct FullMessageId { + private: + DialogId dialog_id; + MessageId message_id; + + public: + FullMessageId() : dialog_id(), message_id() { + } + + FullMessageId(DialogId dialog_id, MessageId message_id) : dialog_id(dialog_id), message_id(message_id) { + } + + bool operator==(const FullMessageId &other) const { + return dialog_id == other.dialog_id && message_id == other.message_id; + } + + bool operator!=(const FullMessageId &other) const { + return !(*this == other); + } + + DialogId get_dialog_id() const { + return dialog_id; + } + MessageId get_message_id() const { + return message_id; + } + + template + void store(StorerT &storer) const { + dialog_id.store(storer); + message_id.store(storer); + } + + template + void parse(ParserT &parser) { + dialog_id.parse(parser); + message_id.parse(parser); + } +}; + +struct FullMessageIdHash { + std::size_t operator()(FullMessageId full_message_id) const { + return DialogIdHash()(full_message_id.get_dialog_id()) * 2023654985u + + MessageIdHash()(full_message_id.get_message_id()); + } +}; + +inline StringBuilder &operator<<(StringBuilder &string_builder, FullMessageId full_message_id) { + return string_builder << full_message_id.get_message_id() << " in " << full_message_id.get_dialog_id(); +} + +} // namespace td diff --git a/td/telegram/Game.cpp b/td/telegram/Game.cpp index d7743fd3..1498db19 100644 --- a/td/telegram/Game.cpp +++ b/td/telegram/Game.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Game.h b/td/telegram/Game.h index 628d0b39..d6e011b0 100644 --- a/td/telegram/Game.h +++ b/td/telegram/Game.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Game.hpp b/td/telegram/Game.hpp index 0e6f9a3d..18189a05 100644 --- a/td/telegram/Game.hpp +++ b/td/telegram/Game.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Global.cpp b/td/telegram/Global.cpp index 2fbfa7d6..b8507547 100644 --- a/td/telegram/Global.cpp +++ b/td/telegram/Global.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -61,6 +61,28 @@ void Global::set_mtproto_header(unique_ptr mtproto_header) { mtproto_header_ = std::move(mtproto_header); } +struct ServerTimeDiff { + double diff; + double system_time; + + template + void store(StorerT &storer) const { + using td::store; + store(diff, storer); + store(system_time, storer); + } + template + void parse(ParserT &parser) { + using td::parse; + parse(diff, parser); + if (parser.get_left_len() != 0) { + parse(system_time, parser); + } else { + system_time = 0; + } + } +}; + Status Global::init(const TdParameters ¶meters, ActorId td, unique_ptr td_db_ptr) { parameters_ = parameters; @@ -70,38 +92,68 @@ Status Global::init(const TdParameters ¶meters, ActorId td, unique_ptrget_binlog_pmc()->get("server_time_difference"); - auto default_time_difference = Clocks::system() - Time::now(); - if (save_diff_str.empty()) { + string saved_diff_str = td_db()->get_binlog_pmc()->get("server_time_difference"); + auto system_time = Clocks::system(); + auto default_time_difference = system_time - Time::now(); + if (saved_diff_str.empty()) { server_time_difference_ = default_time_difference; - server_time_difference_was_updated_ = false; } else { - double save_diff; - unserialize(save_diff, save_diff_str).ensure(); - double diff = save_diff + default_time_difference; + ServerTimeDiff saved_diff; + unserialize(saved_diff, saved_diff_str).ensure(); + + double diff = saved_diff.diff + default_time_difference; + if (saved_diff.system_time > system_time) { + double time_backwards_fix = saved_diff.system_time - system_time; + if (time_backwards_fix > 60) { + LOG(WARNING) << "Fix system time which went backwards: " << format::as_time(time_backwards_fix) << " " + << tag("saved_system_time", saved_diff.system_time) << tag("system_time", system_time); + } + diff += time_backwards_fix; + } LOG(DEBUG) << "LOAD: " << tag("server_time_difference", diff); server_time_difference_ = diff; - server_time_difference_was_updated_ = false; } + server_time_difference_was_updated_ = false; dns_time_difference_ = default_time_difference; dns_time_difference_was_updated_ = false; return Status::OK(); } +int32 Global::to_unix_time(double server_time) { + LOG_CHECK(1.0 <= server_time && server_time <= 2140000000.0) << server_time << " " << Clocks::system(); + return static_cast(server_time); +} + void Global::update_server_time_difference(double diff) { if (!server_time_difference_was_updated_ || server_time_difference_ < diff) { server_time_difference_ = diff; server_time_difference_was_updated_ = true; - - // diff = server_time - Time::now - // save_diff = server_time - Clocks::system - double save_diff = diff + Time::now() - Clocks::system(); - auto str = serialize(save_diff); - td_db()->get_binlog_pmc()->set("server_time_difference", str); + do_save_server_time_difference(); } } +void Global::save_server_time() { + auto t = Time::now(); + if (server_time_difference_was_updated_ && system_time_saved_at_.load(std::memory_order_relaxed) + 10 < t) { + system_time_saved_at_ = t; + do_save_server_time_difference(); + } +} + +void Global::do_save_server_time_difference() { + LOG(INFO) << "Save server time difference"; + // diff = server_time - Time::now + // fixed_diff = server_time - Clocks::system + double system_time = Clocks::system(); + double fixed_diff = server_time_difference_ + Time::now() - system_time; + + ServerTimeDiff diff; + diff.diff = fixed_diff; + diff.system_time = system_time; + td_db()->get_binlog_pmc()->set("server_time_difference", serialize(diff)); +} + void Global::update_dns_time_difference(double diff) { dns_time_difference_ = diff; dns_time_difference_was_updated_ = true; diff --git a/td/telegram/Global.h b/td/telegram/Global.h index a70b592c..e4fe2309 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -124,9 +124,6 @@ class Global : public ActorContext { return *shared_config_; } - double from_server_time(double date) const { - return date - get_server_time_difference(); - } double to_server_time(double now) const { return now + get_server_time_difference(); } @@ -137,14 +134,16 @@ class Global : public ActorContext { return to_server_time(Time::now_cached()); } int32 unix_time() const { - return static_cast(server_time()); + return to_unix_time(server_time()); } int32 unix_time_cached() const { - return static_cast(server_time_cached()); + return to_unix_time(server_time_cached()); } void update_server_time_difference(double diff); + void save_server_time(); + double get_server_time_difference() const { return server_time_difference_.load(std::memory_order_relaxed); } @@ -407,6 +406,7 @@ class Global : public ActorContext { std::atomic dns_time_difference_{0.0}; std::atomic dns_time_difference_was_updated_{false}; std::atomic close_flag_{false}; + std::atomic system_time_saved_at_{-1e10}; std::vector> net_stats_file_callbacks_; @@ -423,6 +423,10 @@ class Global : public ActorContext { std::unordered_map location_access_hashes_; + static int32 to_unix_time(double server_time); + + void do_save_server_time_difference(); + void do_close(Promise<> on_finish, bool destroy_flag); }; diff --git a/td/telegram/HashtagHints.cpp b/td/telegram/HashtagHints.cpp index 3880e95c..d587c682 100644 --- a/td/telegram/HashtagHints.cpp +++ b/td/telegram/HashtagHints.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +13,7 @@ #include "td/utils/logging.h" #include "td/utils/tl_helpers.h" +#include "td/utils/utf8.h" #include @@ -73,6 +74,11 @@ string HashtagHints::get_key() const { } void HashtagHints::hashtag_used_impl(const string &hashtag) { + if (!check_utf8(hashtag)) { + LOG(ERROR) << "Trying to add invalid UTF-8 hashtag \"" << hashtag << '"'; + return; + } + // TODO: may be it should be optimized a little auto key = std::hash()(hashtag); hints_.add(key, hashtag); @@ -87,7 +93,7 @@ void HashtagHints::from_db(Result data, bool dummy) { std::vector hashtags; auto status = unserialize(hashtags, data.ok()); if (status.is_error()) { - LOG(ERROR) << status; + LOG(ERROR) << "Failed to unserialize hashtag hints: " << status; return; } diff --git a/td/telegram/HashtagHints.h b/td/telegram/HashtagHints.h index a39d6262..f425e49d 100644 --- a/td/telegram/HashtagHints.h +++ b/td/telegram/HashtagHints.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index 53313759..c07a31ef 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -34,6 +34,7 @@ #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" +#include "td/telegram/Venue.h" #include "td/telegram/VideosManager.h" #include "td/telegram/VoiceNotesManager.h" @@ -179,6 +180,9 @@ void InlineQueriesManager::on_drop_inline_query_result_timeout_callback(void *in } void InlineQueriesManager::after_get_difference() { + if (td_->auth_manager_->is_bot()) { + return; + } if (recently_used_bots_loaded_ < 2) { Promise promise; load_recently_used_bots(promise); @@ -319,6 +323,18 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe return promise.set_error(Status::Error(400, "Method can be used by bots only")); } + if (!switch_pm_text.empty()) { + if (switch_pm_parameter.empty()) { + return promise.set_error(Status::Error(400, "Can't use empty switch_pm_parameter")); + } + if (switch_pm_parameter.size() > 64) { + return promise.set_error(Status::Error(400, "Too long switch_pm_parameter specified")); + } + if (!is_base64url(switch_pm_parameter)) { + return promise.set_error(Status::Error(400, "Unallowed characters in switch_pm_parameter are used")); + } + } + vector> results; bool is_gallery = false; @@ -650,19 +666,19 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe if (file_view.is_encrypted()) { return promise.set_error(Status::Error(400, "Can't send encrypted file")); } - if (file_view.remote_location().is_web()) { + if (file_view.main_remote_location().is_web()) { return promise.set_error(Status::Error(400, "Can't send web file")); } if (file_type == FileType::Photo) { auto result = make_tl_object( - id, type, file_view.remote_location().as_input_photo(), std::move(inline_message)); + id, type, file_view.main_remote_location().as_input_photo(), std::move(inline_message)); results.push_back(std::move(result)); continue; } auto result = make_tl_object( - flags, id, type, title, description, file_view.remote_location().as_input_document(), + flags, id, type, title, description, file_view.main_remote_location().as_input_document(), std::move(inline_message)); results.push_back(std::move(result)); continue; @@ -836,8 +852,8 @@ td_api::object_ptr copy(const td_api::localFile &obj) { } template <> td_api::object_ptr copy(const td_api::remoteFile &obj) { - return td_api::make_object(obj.id_, obj.is_uploading_active_, obj.is_uploading_completed_, - obj.uploaded_size_); + return td_api::make_object(obj.id_, obj.unique_id_, obj.is_uploading_active_, + obj.is_uploading_completed_, obj.uploaded_size_); } template <> @@ -1712,12 +1728,10 @@ bool InlineQueriesManager::update_bot_usage(UserId bot_user_id) { } void InlineQueriesManager::remove_recent_inline_bot(UserId bot_user_id, Promise &&promise) { - auto it = std::find(recently_used_bot_user_ids_.begin(), recently_used_bot_user_ids_.end(), bot_user_id); - if (it != recently_used_bot_user_ids_.end()) { - recently_used_bot_user_ids_.erase(it); + if (td::remove(recently_used_bot_user_ids_, bot_user_id)) { save_recently_used_bots(); } - return promise.set_value(Unit()); + promise.set_value(Unit()); } } // namespace td diff --git a/td/telegram/InlineQueriesManager.h b/td/telegram/InlineQueriesManager.h index 5640074a..07ad9f69 100644 --- a/td/telegram/InlineQueriesManager.h +++ b/td/telegram/InlineQueriesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/InputMessageText.cpp b/td/telegram/InputMessageText.cpp index 7bdb3d11..a6f12085 100644 --- a/td/telegram/InputMessageText.cpp +++ b/td/telegram/InputMessageText.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/InputMessageText.h b/td/telegram/InputMessageText.h index 06143475..9952b1eb 100644 --- a/td/telegram/InputMessageText.h +++ b/td/telegram/InputMessageText.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/InputMessageText.hpp b/td/telegram/InputMessageText.hpp index 540c81d6..9e39d9eb 100644 --- a/td/telegram/InputMessageText.hpp +++ b/td/telegram/InputMessageText.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/JsonValue.cpp b/td/telegram/JsonValue.cpp index dfd82805..a1e9fdf8 100644 --- a/td/telegram/JsonValue.cpp +++ b/td/telegram/JsonValue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -148,7 +148,7 @@ class JsonableJsonValue : public Jsonable { *scope << JsonNull(); break; case td_api::jsonValueBoolean::ID: - *scope << static_cast(json_value_)->value_; + *scope << JsonBool(static_cast(json_value_)->value_); break; case td_api::jsonValueNumber::ID: *scope << static_cast(json_value_)->value_; @@ -177,7 +177,7 @@ class JsonableJsonValue : public Jsonable { if (!check_utf8(member->key_)) { LOG(ERROR) << "Have incorrect UTF-8 object key " << member->key_; } else { - object << ctie(member->key_, JsonableJsonValue(member->value_.get())); + object(member->key_, JsonableJsonValue(member->value_.get())); } } } diff --git a/td/telegram/JsonValue.h b/td/telegram/JsonValue.h index 3ed0459b..b54c5326 100644 --- a/td/telegram/JsonValue.h +++ b/td/telegram/JsonValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index db848a00..25375701 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,6 +19,7 @@ #include "td/telegram/td_api.hpp" #include "td/telegram/telegram_api.h" +#include "td/db/DbKey.h" #include "td/db/SqliteDb.h" #include "td/db/SqliteKeyValue.h" diff --git a/td/telegram/LanguagePackManager.h b/td/telegram/LanguagePackManager.h index 182fbe19..311f7940 100644 --- a/td/telegram/LanguagePackManager.h +++ b/td/telegram/LanguagePackManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Location.cpp b/td/telegram/Location.cpp index 154121c0..6110622a 100644 --- a/td/telegram/Location.cpp +++ b/td/telegram/Location.cpp @@ -1,19 +1,11 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/Location.h" -#include "td/telegram/Global.h" -#include "td/telegram/misc.h" -#include "td/telegram/secret_api.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" - -#include "td/utils/common.h" - #include namespace td { @@ -114,82 +106,6 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Location &locatio << "]"; } -Venue::Venue(const tl_object_ptr &geo_point_ptr, string title, string address, string provider, - string id, string type) - : location_(geo_point_ptr) - , title_(std::move(title)) - , address_(std::move(address)) - , provider_(std::move(provider)) - , id_(std::move(id)) - , type_(std::move(type)) { -} - -Venue::Venue(Location location, string title, string address, string provider, string id, string type) - : location_(location) - , title_(std::move(title)) - , address_(std::move(address)) - , provider_(std::move(provider)) - , id_(std::move(id)) - , type_(std::move(type)) { -} - -Venue::Venue(const tl_object_ptr &venue) - : location_(venue->location_) - , title_(venue->title_) - , address_(venue->address_) - , provider_(venue->provider_) - , id_(venue->id_) - , type_(venue->type_) { -} - -bool Venue::empty() const { - return location_.empty(); -} - -Location &Venue::location() { - return location_; -} - -const Location &Venue::location() const { - return location_; -} - -tl_object_ptr Venue::get_venue_object() const { - return make_tl_object(location_.get_location_object(), title_, address_, provider_, id_, type_); -} - -tl_object_ptr Venue::get_input_media_venue() const { - return make_tl_object(location_.get_input_geo_point(), title_, address_, provider_, - id_, type_); -} - -SecretInputMedia Venue::get_secret_input_media_venue() const { - return SecretInputMedia{nullptr, - make_tl_object( - location_.get_latitude(), location_.get_longitude(), title_, address_, provider_, id_)}; -} - -tl_object_ptr Venue::get_input_bot_inline_message_media_venue( - int32 flags, tl_object_ptr &&reply_markup) const { - return make_tl_object( - flags, location_.get_input_geo_point(), title_, address_, provider_, id_, type_, std::move(reply_markup)); -} - -bool operator==(const Venue &lhs, const Venue &rhs) { - return lhs.location_ == rhs.location_ && lhs.title_ == rhs.title_ && lhs.address_ == rhs.address_ && - lhs.provider_ == rhs.provider_ && lhs.id_ == rhs.id_ && lhs.type_ == rhs.type_; -} - -bool operator!=(const Venue &lhs, const Venue &rhs) { - return !(lhs == rhs); -} - -StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue) { - return string_builder << "Venue[location = " << venue.location_ << ", title = " << venue.title_ - << ", address = " << venue.address_ << ", provider = " << venue.provider_ - << ", id = " << venue.id_ << ", type = " << venue.type_ << "]"; -} - Result> process_input_message_location( tl_object_ptr &&input_message_content) { CHECK(input_message_content != nullptr); @@ -212,37 +128,4 @@ Result> process_input_message_location( return std::make_pair(std::move(location), period); } -Result process_input_message_venue(tl_object_ptr &&input_message_content) { - CHECK(input_message_content != nullptr); - CHECK(input_message_content->get_id() == td_api::inputMessageVenue::ID); - auto venue = std::move(static_cast(input_message_content.get())->venue_); - - if (venue == nullptr) { - return Status::Error(400, "Venue can't be empty"); - } - - if (!clean_input_string(venue->title_)) { - return Status::Error(400, "Venue title must be encoded in UTF-8"); - } - if (!clean_input_string(venue->address_)) { - return Status::Error(400, "Venue address must be encoded in UTF-8"); - } - if (!clean_input_string(venue->provider_)) { - return Status::Error(400, "Venue provider must be encoded in UTF-8"); - } - if (!clean_input_string(venue->id_)) { - return Status::Error(400, "Venue identifier must be encoded in UTF-8"); - } - if (!clean_input_string(venue->type_)) { - return Status::Error(400, "Venue type must be encoded in UTF-8"); - } - - Venue result(venue); - if (result.empty()) { - return Status::Error(400, "Wrong venue location specified"); - } - - return result; -} - } // namespace td diff --git a/td/telegram/Location.h b/td/telegram/Location.h index 9d7ecbec..160d4650 100644 --- a/td/telegram/Location.h +++ b/td/telegram/Location.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #include "td/telegram/Global.h" #include "td/telegram/SecretInputMedia.h" -#include "td/telegram/Version.h" #include "td/telegram/secret_api.h" #include "td/telegram/td_api.h" @@ -112,79 +111,7 @@ bool operator!=(const Location &lhs, const Location &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const Location &location); -class Venue { - Location location_; - string title_; - string address_; - string provider_; - string id_; - string type_; - - friend bool operator==(const Venue &lhs, const Venue &rhs); - friend bool operator!=(const Venue &lhs, const Venue &rhs); - - friend StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue); - - public: - Venue() = default; - - Venue(const tl_object_ptr &geo_point_ptr, string title, string address, string provider, - string id, string type); - - Venue(Location location, string title, string address, string provider, string id, string type); - - explicit Venue(const tl_object_ptr &venue); - - bool empty() const; - - Location &location(); - - const Location &location() const; - - tl_object_ptr get_venue_object() const; - - tl_object_ptr get_input_media_venue() const; - - SecretInputMedia get_secret_input_media_venue() const; - - // TODO very strange function - tl_object_ptr get_input_bot_inline_message_media_venue( - int32 flags, tl_object_ptr &&reply_markup) const; - - template - void store(StorerT &storer) const { - using td::store; - store(location_, storer); - store(title_, storer); - store(address_, storer); - store(provider_, storer); - store(id_, storer); - store(type_, storer); - } - - template - void parse(ParserT &parser) { - using td::parse; - parse(location_, parser); - parse(title_, parser); - parse(address_, parser); - parse(provider_, parser); - parse(id_, parser); - if (parser.version() >= static_cast(Version::AddVenueType)) { - parse(type_, parser); - } - } -}; - -bool operator==(const Venue &lhs, const Venue &rhs); -bool operator!=(const Venue &lhs, const Venue &rhs); - -StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue); - Result> process_input_message_location( td_api::object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; -Result process_input_message_venue(td_api::object_ptr &&input_message_content) - TD_WARN_UNUSED_RESULT; - } // namespace td diff --git a/td/telegram/Log.cpp b/td/telegram/Log.cpp index 657efdee..ff203459 100644 --- a/td/telegram/Log.cpp +++ b/td/telegram/Log.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Log.h b/td/telegram/Log.h index 17f1b696..87ece943 100644 --- a/td/telegram/Log.h +++ b/td/telegram/Log.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/LogDotNet.cpp b/td/telegram/LogDotNet.cpp index 55c4e73d..975cb5f6 100644 --- a/td/telegram/LogDotNet.cpp +++ b/td/telegram/LogDotNet.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Logging.cpp b/td/telegram/Logging.cpp index 900073ea..41040be8 100644 --- a/td/telegram/Logging.cpp +++ b/td/telegram/Logging.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Logging.h b/td/telegram/Logging.h index 43a2bc52..d2f12dc4 100644 --- a/td/telegram/Logging.h +++ b/td/telegram/Logging.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index a7f89ebf..9abc10de 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -46,10 +46,12 @@ #include "td/telegram/secret_api.hpp" #include "td/telegram/SecureValue.h" #include "td/telegram/SecureValue.hpp" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/StickersManager.h" #include "td/telegram/StickersManager.hpp" #include "td/telegram/Td.h" #include "td/telegram/UserId.h" +#include "td/telegram/Venue.h" #include "td/telegram/Version.h" #include "td/telegram/VideoNotesManager.h" #include "td/telegram/VideoNotesManager.hpp" @@ -72,7 +74,6 @@ #include "td/utils/tl_helpers.h" #include "td/utils/utf8.h" -#include #include namespace td { @@ -942,7 +943,7 @@ static void store(const MessageContent *content, StorerT &storer) { store(m->invoice_payload, storer); } if (has_order_info) { - store(*m->order_info, storer); + store(m->order_info, storer); } if (has_telegram_payment_charge_id) { store(m->telegram_payment_charge_id, storer); @@ -1274,8 +1275,7 @@ static void parse(unique_ptr &content, ParserT &parser) { parse(m->invoice_payload, parser); } if (has_order_info) { - m->order_info = make_unique(); - parse(*m->order_info, parser); + parse(m->order_info, parser); } if (has_telegram_payment_charge_id) { parse(m->telegram_payment_charge_id, parser); @@ -2774,13 +2774,39 @@ static bool need_message_text_changed_warning(const MessageText *old_content, co // server has deleted first entity and ltrim the message return false; } - for (auto &entity : new_content->text.entities) { - if (entity.type == MessageEntity::Type::PhoneNumber) { - // TODO remove after find_phone_numbers is implemented - return false; + return true; +} + +static bool need_message_entities_changed_warning(const vector &old_entities, + const vector &new_entities) { + size_t old_pos = 0; + size_t new_pos = 0; + // compare entities, skipping some known to be different + while (old_pos < old_entities.size() || new_pos < new_entities.size()) { + // TODO remove after find_phone_numbers is implemented + while (new_pos < new_entities.size() && new_entities[new_pos].type == MessageEntity::Type::PhoneNumber) { + new_pos++; + } + + if (old_pos < old_entities.size() && new_pos < new_entities.size() && + old_entities[old_pos] == new_entities[new_pos]) { + old_pos++; + new_pos++; + continue; + } + + if (old_pos < old_entities.size() && old_entities[old_pos].type == MessageEntity::Type::MentionName) { + // server could delete sime MentionName entities + old_pos++; + continue; + } + + if (old_pos < old_entities.size() || new_pos < new_entities.size()) { + return true; } } - return true; + + return false; } void merge_message_contents(Td *td, const MessageContent *old_content, MessageContent *new_content, @@ -2804,7 +2830,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo if (old_->text.entities != new_->text.entities) { const int32 MAX_CUSTOM_ENTITIES_COUNT = 100; // server-side limit if (need_message_changed_warning && need_message_text_changed_warning(old_, new_) && - old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT) { + old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT && + need_message_entities_changed_warning(old_->text.entities, new_->text.entities)) { LOG(WARNING) << "Entities has changed from " << to_string(get_message_content_object(old_content, td, -1, false)) << ". New content is " << to_string(get_message_content_object(new_content, td, -1, false)); @@ -2958,18 +2985,23 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo } } + LOG(DEBUG) << "Merge photos " << old_photo->photos << " and " << new_photo->photos + << " with new photos size = " << new_photos_size << ", need_merge = " << need_merge + << ", need_update = " << need_update; if (need_merge && new_photos_size != 0) { FileId old_file_id = get_message_content_upload_file_id(old_content); FileView old_file_view = td->file_manager_->get_file_view(old_file_id); FileId new_file_id = new_photo->photos[0].file_id; FileView new_file_view = td->file_manager_->get_file_view(new_file_id); CHECK(new_file_view.has_remote_location()); + + LOG(DEBUG) << "Trying to merge old file " << old_file_id << " and new file " << new_file_id; if (new_file_view.remote_location().is_web()) { LOG(ERROR) << "Have remote web photo location"; } else if (!old_file_view.has_remote_location() || - old_file_view.remote_location().get_file_reference() != + old_file_view.main_remote_location().get_file_reference() != new_file_view.remote_location().get_file_reference() || - old_file_view.remote_location().get_access_hash() != + old_file_view.main_remote_location().get_access_hash() != new_file_view.remote_location().get_access_hash()) { FileId file_id = td->file_manager_->register_remote( FullRemoteFileLocation({FileType::Photo, 'i'}, new_file_view.remote_location().get_id(), @@ -3355,6 +3387,9 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File } void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id) { + if (full_message_id.get_message_id().is_scheduled()) { + return; + } switch (content->get_type()) { case MessageContentType::Poll: return td->poll_manager_->register_poll(static_cast(content)->poll_id, full_message_id); @@ -3364,6 +3399,9 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage } void unregister_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id) { + if (full_message_id.get_message_id().is_scheduled()) { + return; + } switch (content->get_type()) { case MessageContentType::Poll: return td->poll_manager_->unregister_poll(static_cast(content)->poll_id, full_message_id); @@ -4844,7 +4882,7 @@ bool need_delay_message_content_notification(const MessageContent *content, User return true; case MessageContentType::ChatAddUsers: { auto &added_user_ids = static_cast(content)->user_ids; - return std::find(added_user_ids.begin(), added_user_ids.end(), my_user_id) == added_user_ids.end(); + return !td::contains(added_user_ids, my_user_id); } case MessageContentType::ChatDeleteUser: return static_cast(content)->user_id != my_user_id; @@ -5025,7 +5063,7 @@ void on_sent_message_content(Td *td, const MessageContent *content) { } } -int64 add_sticker_set(Td *td, tl_object_ptr &&input_sticker_set) { +StickerSetId add_sticker_set(Td *td, tl_object_ptr &&input_sticker_set) { return td->stickers_manager_->add_sticker_set(std::move(input_sticker_set)); } diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index 5d8644ac..9b08ddd6 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,12 +9,14 @@ #include "td/telegram/Dependencies.h" #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageId.h" #include "td/telegram/Photo.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/secret_api.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserId.h" @@ -252,6 +254,6 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC void on_sent_message_content(Td *td, const MessageContent *content); -int64 add_sticker_set(Td *td, tl_object_ptr &&input_sticker_set); +StickerSetId add_sticker_set(Td *td, tl_object_ptr &&input_sticker_set); } // namespace td diff --git a/td/telegram/MessageEntity.cpp b/td/telegram/MessageEntity.cpp index bae48340..a257b95b 100644 --- a/td/telegram/MessageEntity.cpp +++ b/td/telegram/MessageEntity.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/misc.h" +#include "td/telegram/SecretChatActor.h" #include "td/utils/format.h" #include "td/utils/logging.h" @@ -22,62 +23,57 @@ namespace td { -StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity) { - bool has_argument = false; - string_builder << '['; - switch (message_entity.type) { +int MessageEntity::get_type_priority(Type type) { + static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0}; + return types[static_cast(type)]; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Type &message_entity_type) { + switch (message_entity_type) { case MessageEntity::Type::Mention: - string_builder << "Mention"; - break; + return string_builder << "Mention"; case MessageEntity::Type::Hashtag: - string_builder << "Hashtag"; - break; + return string_builder << "Hashtag"; case MessageEntity::Type::BotCommand: - string_builder << "BotCommand"; - break; + return string_builder << "BotCommand"; case MessageEntity::Type::Url: - string_builder << "Url"; - break; + return string_builder << "Url"; case MessageEntity::Type::EmailAddress: - string_builder << "EmailAddress"; - break; + return string_builder << "EmailAddress"; case MessageEntity::Type::Bold: - string_builder << "Bold"; - break; + return string_builder << "Bold"; case MessageEntity::Type::Italic: - string_builder << "Italic"; - break; + return string_builder << "Italic"; + case MessageEntity::Type::Underline: + return string_builder << "Underline"; + case MessageEntity::Type::Strikethrough: + return string_builder << "Strikethrough"; + case MessageEntity::Type::BlockQuote: + return string_builder << "BlockQuote"; case MessageEntity::Type::Code: - string_builder << "Code"; - break; + return string_builder << "Code"; case MessageEntity::Type::Pre: - string_builder << "Pre"; - break; + return string_builder << "Pre"; case MessageEntity::Type::PreCode: - string_builder << "PreCode"; - has_argument = true; - break; + return string_builder << "PreCode"; case MessageEntity::Type::TextUrl: - string_builder << "TextUrl"; - has_argument = true; - break; + return string_builder << "TextUrl"; case MessageEntity::Type::MentionName: - string_builder << "MentionName"; - break; + return string_builder << "MentionName"; case MessageEntity::Type::Cashtag: - string_builder << "Cashtag"; - break; + return string_builder << "Cashtag"; case MessageEntity::Type::PhoneNumber: - string_builder << "PhoneNumber"; - break; + return string_builder << "PhoneNumber"; default: UNREACHABLE(); - string_builder << "Impossible"; - break; + return string_builder << "Impossible"; } +} - string_builder << ", offset = " << message_entity.offset << ", length = " << message_entity.length; - if (has_argument) { +StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity) { + string_builder << '[' << message_entity.type << ", offset = " << message_entity.offset + << ", length = " << message_entity.length; + if (!message_entity.argument.empty()) { string_builder << ", argument = \"" << message_entity.argument << "\""; } if (message_entity.user_id.is_valid()) { @@ -103,6 +99,12 @@ tl_object_ptr MessageEntity::get_text_entity_type_object return make_tl_object(); case MessageEntity::Type::Italic: return make_tl_object(); + case MessageEntity::Type::Underline: + return make_tl_object(); + case MessageEntity::Type::Strikethrough: + return make_tl_object(); + case MessageEntity::Type::BlockQuote: + return nullptr; case MessageEntity::Type::Code: return make_tl_object(); case MessageEntity::Type::Pre: @@ -132,7 +134,10 @@ vector> get_text_entities_object(const vector< result.reserve(entities.size()); for (auto &entity : entities) { - result.push_back(entity.get_text_entity_object()); + auto entity_object = entity.get_text_entity_object(); + if (entity_object->type_ != nullptr) { + result.push_back(std::move(entity_object)); + } } return result; @@ -983,15 +988,13 @@ const std::unordered_set &get_valid_short_usernames() { vector find_mentions(Slice str) { auto mentions = match_mentions(str); - mentions.erase(std::remove_if(mentions.begin(), mentions.end(), - [](Slice mention) { - mention.remove_prefix(1); - if (mention.size() >= 5) { - return false; - } - return get_valid_short_usernames().count(mention) == 0; - }), - mentions.end()); + td::remove_if(mentions, [](Slice mention) { + mention.remove_prefix(1); + if (mention.size() >= 5) { + return false; + } + return get_valid_short_usernames().count(mention) == 0; + }); return mentions; } @@ -1024,18 +1027,59 @@ vector> find_urls(Slice str) { return result; } -// sorts entities, removes intersecting and empty entities -static void fix_entities(vector &entities) { - if (entities.empty()) { - return; +// 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; + } + + while (!nested_entities_stack.empty() && + entities[i].offset >= nested_entities_stack.back()->offset + nested_entities_stack.back()->length) { + // remove non-intersecting entities from the stack + 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; + } + 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) { + // Pre and Code can't contain nested entities + continue; + } + } + + if (i != left_entities) { + entities[left_entities] = std::move(entities[i]); + } + nested_entities_stack.push_back(&entities[left_entities++]); } - std::sort(entities.begin(), entities.end()); + entities.erase(entities.begin() + left_entities, entities.end()); +} +// removes all intersecting entities, including nested +// entities must be pre-sorted and pre-validated +static void remove_intersecting_entities(vector &entities) { int32 last_entity_end = 0; size_t left_entities = 0; for (size_t i = 0; i < entities.size(); i++) { - if (entities[i].length > 0 && entities[i].offset >= last_entity_end) { + CHECK(entities[i].length > 0); + if (entities[i].offset >= last_entity_end) { last_entity_end = entities[i].offset + entities[i].length; if (i != left_entities) { entities[left_entities] = std::move(entities[i]); @@ -1046,6 +1090,17 @@ static void fix_entities(vector &entities) { entities.erase(entities.begin() + left_entities, entities.end()); } +static void fix_entities(vector &entities) { + if (entities.empty()) { + // fast path + return; + } + + std::sort(entities.begin(), entities.end()); + + remove_unallowed_entities(entities); +} + vector find_entities(Slice text, bool skip_bot_commands, bool only_urls) { vector entities; @@ -1081,7 +1136,6 @@ vector find_entities(Slice text, bool skip_bot_commands, bool onl auto urls = find_urls(text); for (auto &url : urls) { - // TODO better find messageEntityUrl auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url; if (only_urls && type != MessageEntity::Type::Url) { continue; @@ -1095,9 +1149,11 @@ vector find_entities(Slice text, bool skip_bot_commands, bool onl return entities; } - fix_entities(entities); + std::sort(entities.begin(), entities.end()); - // fix offsets to utf16 offsets + remove_intersecting_entities(entities); + + // fix offsets to UTF-16 offsets const unsigned char *begin = text.ubegin(); const unsigned char *ptr = begin; const unsigned char *end = text.uend(); @@ -1117,7 +1173,7 @@ vector find_entities(Slice text, bool skip_bot_commands, bool onl while (ptr != end && cnt > 0) { unsigned char c = ptr[0]; utf16_pos += 1 + (c >= 0xf0); - ptr = next_utf8_unsafe(ptr, nullptr, "match_urls 8"); + ptr = next_utf8_unsafe(ptr, nullptr, "find_entities"); pos = static_cast(ptr - begin); if (entity_begin == pos) { @@ -1192,6 +1248,12 @@ string get_first_url(Slice text, const vector &entities) { break; case MessageEntity::Type::Italic: break; + case MessageEntity::Type::Underline: + break; + case MessageEntity::Type::Strikethrough: + break; + case MessageEntity::Type::BlockQuote: + break; case MessageEntity::Type::Code: break; case MessageEntity::Type::Pre: @@ -1295,7 +1357,7 @@ Result> parse_markdown(string &text) { i += 2; is_pre = true; size_t language_end = i; - while (language_end < size && !is_space(text[language_end]) && text[language_end] != '`') { + while (!is_space(text[language_end]) && text[language_end] != '`') { language_end++; } if (i != language_end && language_end < size && text[language_end] != '`') { @@ -1312,11 +1374,11 @@ Result> parse_markdown(string &text) { } } - int32 utf16_entity_length = 0; + int32 entity_offset = utf16_offset; while (i < size && (text[i] != end_character || (is_pre && !(text[i + 1] == '`' && text[i + 2] == '`')))) { auto cur_ch = static_cast(text[i]); if (is_utf8_character_first_code_unit(cur_ch)) { - utf16_entity_length += 1 + (cur_ch >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + utf16_offset += 1 + (cur_ch >= 0xf0); // >= 4 bytes in symbol => surrogaite pair } result.push_back(text[i++]); } @@ -1324,13 +1386,14 @@ Result> parse_markdown(string &text) { return Status::Error(400, PSLICE() << "Can't find end of the entity starting at byte offset " << begin_pos); } - if (utf16_entity_length > 0) { + if (entity_offset != utf16_offset) { + auto entity_length = utf16_offset - entity_offset; switch (c) { case '_': - entities.emplace_back(MessageEntity::Type::Italic, utf16_offset, utf16_entity_length); + entities.emplace_back(MessageEntity::Type::Italic, entity_offset, entity_length); break; case '*': - entities.emplace_back(MessageEntity::Type::Bold, utf16_offset, utf16_entity_length); + entities.emplace_back(MessageEntity::Type::Bold, entity_offset, entity_length); break; case '[': { string url; @@ -1345,12 +1408,11 @@ Result> parse_markdown(string &text) { } auto user_id = get_link_user_id(url); if (user_id.is_valid()) { - entities.emplace_back(utf16_offset, utf16_entity_length, user_id); + entities.emplace_back(entity_offset, entity_length, user_id); } else { auto r_url = check_url(url); if (r_url.is_ok()) { - entities.emplace_back(MessageEntity::Type::TextUrl, utf16_offset, utf16_entity_length, - r_url.move_as_ok()); + entities.emplace_back(MessageEntity::Type::TextUrl, entity_offset, entity_length, r_url.move_as_ok()); } } break; @@ -1358,18 +1420,17 @@ Result> parse_markdown(string &text) { case '`': if (is_pre) { if (language.empty()) { - entities.emplace_back(MessageEntity::Type::Pre, utf16_offset, utf16_entity_length); + entities.emplace_back(MessageEntity::Type::Pre, entity_offset, entity_length); } else { - entities.emplace_back(MessageEntity::Type::PreCode, utf16_offset, utf16_entity_length, language); + entities.emplace_back(MessageEntity::Type::PreCode, entity_offset, entity_length, language); } } else { - entities.emplace_back(MessageEntity::Type::Code, utf16_offset, utf16_entity_length); + entities.emplace_back(MessageEntity::Type::Code, entity_offset, entity_length); } break; default: UNREACHABLE(); } - utf16_offset += utf16_entity_length; } if (is_pre) { i += 2; @@ -1379,7 +1440,224 @@ Result> parse_markdown(string &text) { return entities; } -static uint32 decode_html_entity(const string &text, size_t &pos) { +static Result> do_parse_markdown_v2(CSlice text, string &result) { + vector entities; + int32 utf16_offset = 0; + + struct EntityInfo { + MessageEntity::Type type; + string argument; + int32 entity_offset; + size_t entity_byte_offset; + size_t entity_begin_pos; + + EntityInfo(MessageEntity::Type type, string argument, int32 entity_offset, size_t entity_byte_offset, + size_t entity_begin_pos) + : type(type) + , argument(std::move(argument)) + , entity_offset(entity_offset) + , entity_byte_offset(entity_byte_offset) + , entity_begin_pos(entity_begin_pos) { + } + }; + std::vector nested_entities; + + for (size_t i = 0; i < text.size(); i++) { + auto c = static_cast(text[i]); + if (c == '\\' && text[i + 1] > 0 && text[i + 1] <= 126) { + i++; + utf16_offset += 1; + result += text[i]; + continue; + } + + Slice reserved_characters("_*[]()~`>#+-=|{}.!"); + if (!nested_entities.empty()) { + switch (nested_entities.back().type) { + case MessageEntity::Type::Code: + case MessageEntity::Type::Pre: + case MessageEntity::Type::PreCode: + reserved_characters = Slice("`"); + break; + default: + break; + } + } + + if (reserved_characters.find(text[i]) == Slice::npos) { + 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; + } + + bool is_end_of_an_entity = false; + if (!nested_entities.empty()) { + is_end_of_an_entity = [&] { + switch (nested_entities.back().type) { + case MessageEntity::Type::Bold: + return c == '*'; + case MessageEntity::Type::Italic: + return c == '_' && text[i + 1] != '_'; + case MessageEntity::Type::Code: + return c == '`'; + case MessageEntity::Type::Pre: + case MessageEntity::Type::PreCode: + return c == '`' && text[i + 1] == '`' && text[i + 2] == '`'; + case MessageEntity::Type::TextUrl: + return c == ']'; + case MessageEntity::Type::Underline: + return c == '_' && text[i + 1] == '_'; + case MessageEntity::Type::Strikethrough: + return c == '~'; + default: + UNREACHABLE(); + return false; + } + }(); + } + + if (!is_end_of_an_entity) { + // begin of an entity + MessageEntity::Type type; + string argument; + auto entity_byte_offset = i; + switch (c) { + case '_': + if (text[i + 1] == '_') { + type = MessageEntity::Type::Underline; + i++; + } else { + type = MessageEntity::Type::Italic; + } + break; + case '*': + type = MessageEntity::Type::Bold; + break; + case '~': + type = MessageEntity::Type::Strikethrough; + break; + case '[': + type = MessageEntity::Type::TextUrl; + break; + case '`': + if (text[i + 1] == '`' && text[i + 2] == '`') { + i += 3; + type = MessageEntity::Type::Pre; + size_t language_end = i; + while (!is_space(text[language_end]) && text[language_end] != '`') { + language_end++; + } + if (i != language_end && language_end < text.size() && text[language_end] != '`') { + type = MessageEntity::Type::PreCode; + argument = text.substr(i, language_end - i).str(); + i = language_end; + } + // skip one new line in the beginning of the text + if (text[i] == '\n' || text[i] == '\r') { + if ((text[i + 1] == '\n' || text[i + 1] == '\r') && text[i] != text[i + 1]) { + i += 2; + } else { + i++; + } + } + + i--; + } else { + type = MessageEntity::Type::Code; + } + break; + default: + return Status::Error( + 400, PSLICE() << "Character '" << text[i] << "' is reserved and must be escaped with the preceding '\\'"); + } + nested_entities.emplace_back(type, std::move(argument), utf16_offset, entity_byte_offset, result.size()); + } else { + // end of an entity + auto type = nested_entities.back().type; + auto argument = std::move(nested_entities.back().argument); + UserId user_id; + bool skip_entity = utf16_offset == nested_entities.back().entity_offset; + switch (type) { + case MessageEntity::Type::Bold: + case MessageEntity::Type::Italic: + case MessageEntity::Type::Code: + case MessageEntity::Type::Strikethrough: + break; + case MessageEntity::Type::Underline: + i++; + break; + case MessageEntity::Type::Pre: + case MessageEntity::Type::PreCode: + i += 2; + break; + case MessageEntity::Type::TextUrl: { + string url; + if (text[i + 1] != '(') { + // use text as a url + url = result.substr(nested_entities.back().entity_begin_pos); + } else { + i += 2; + auto url_begin_pos = i; + while (i < text.size() && text[i] != ')') { + if (text[i] == '\\' && text[i + 1] > 0 && text[i + 1] <= 126) { + url += text[i + 1]; + i += 2; + continue; + } + url += text[i++]; + } + if (text[i] != ')') { + return Status::Error(400, PSLICE() << "Can't find end of a URL at byte offset " << url_begin_pos); + } + } + user_id = get_link_user_id(url); + if (!user_id.is_valid()) { + auto r_url = check_url(url); + if (r_url.is_error()) { + skip_entity = true; + } else { + argument = r_url.move_as_ok(); + } + } + break; + } + default: + UNREACHABLE(); + return false; + } + + if (!skip_entity) { + auto entity_offset = nested_entities.back().entity_offset; + auto entity_length = utf16_offset - entity_offset; + if (user_id.is_valid()) { + entities.emplace_back(entity_offset, entity_length, user_id); + } else { + entities.emplace_back(type, entity_offset, entity_length, std::move(argument)); + } + } + nested_entities.pop_back(); + } + } + if (!nested_entities.empty()) { + return Status::Error(400, PSLICE() << "Can't find end of " << nested_entities.back().type + << " entity at byte offset " << nested_entities.back().entity_byte_offset); + } + + std::sort(entities.begin(), entities.end()); + + return entities; +} + +Result> parse_markdown_v2(string &text) { + string result; + TRY_RESULT(entities, do_parse_markdown_v2(text, result)); + text = result; + return entities; +} + +static uint32 decode_html_entity(CSlice text, size_t &pos) { auto c = static_cast(text[pos]); if (c != '&') { return 0; @@ -1409,14 +1687,14 @@ static uint32 decode_html_entity(const string &text, size_t &pos) { while (is_alpha(text[end_pos])) { end_pos++; } - string entity(text, pos + 1, end_pos - pos - 1); - if (entity == "lt") { + Slice entity = text.substr(pos + 1, end_pos - pos - 1); + if (entity == Slice("lt")) { res = static_cast('<'); - } else if (entity == "gt") { + } else if (entity == Slice("gt")) { res = static_cast('>'); - } else if (entity == "amp") { + } else if (entity == Slice("amp")) { res = static_cast('&'); - } else if (entity == "quot") { + } else if (entity == Slice("quot")) { res = static_cast('"'); } else { // unsupported literal entity @@ -1432,12 +1710,26 @@ static uint32 decode_html_entity(const string &text, size_t &pos) { return res; } -Result> parse_html(string &text) { - string result; +static Result> do_parse_html(CSlice text, string &result) { vector entities; - size_t size = text.size(); int32 utf16_offset = 0; - for (size_t i = 0; i < size; i++) { + + struct EntityInfo { + string tag_name; + string argument; + int32 entity_offset; + size_t entity_begin_pos; + + EntityInfo(string tag_name, string argument, int32 entity_offset, size_t entity_begin_pos) + : tag_name(std::move(tag_name)) + , argument(std::move(argument)) + , entity_offset(entity_offset) + , entity_begin_pos(entity_begin_pos) { + } + }; + std::vector nested_entities; + + for (size_t i = 0; i < text.size(); i++) { auto c = static_cast(text[i]); if (c == '&') { auto ch = decode_html_entity(text, i); @@ -1456,172 +1748,196 @@ Result> parse_html(string &text) { continue; } - // we are at begin of the entity - size_t begin_pos = i++; - if (text[i] == '/') { - return Status::Error(400, PSLICE() << "Unexpected end tag at byte offset " << begin_pos); - } - while (!is_space(text[i]) && text[i] != '>') { - i++; - } - if (text[i] == 0) { - return Status::Error(400, PSLICE() << "Unclosed start tag at byte offset " << begin_pos); - } - - string tag_name(text, begin_pos + 1, i - begin_pos - 1); - to_lower_inplace(tag_name); - if (tag_name != "em" && tag_name != "strong" && tag_name != "a" && tag_name != "b" && tag_name != "i" && - tag_name != "pre" && tag_name != "code") { - return Status::Error(400, - PSLICE() << "Unsupported start tag \"" << tag_name << "\" at byte offset " << begin_pos); - } - - string url; - // string language; TODO PreCode support - while (text[i] != '>') { - while (text[i] != 0 && is_space(text[i])) { - i++; - } - if (text[i] == '>') { - break; - } - auto attribute_begin_pos = i; - while (!is_space(text[i]) && text[i] != '=') { - i++; - } - string attribute_name(text, attribute_begin_pos, i - attribute_begin_pos); - if (attribute_name.empty()) { - return Status::Error(400, PSLICE() << "Expected equal sign in declaration of attribute of the tag \"" - << tag_name << "\" at byte offset " << begin_pos); - } - while (text[i] != 0 && is_space(text[i])) { - i++; - } - if (text[i] != '=') { - return Status::Error(400, PSLICE() << "Expected equal sign in declaration of attribute of the tag \"" - << tag_name << "\" at byte offset " << begin_pos); - } - i++; - while (text[i] != 0 && is_space(text[i])) { + auto begin_pos = i++; + if (text[i] != '/') { + // begin of an entity + while (!is_space(text[i]) && text[i] != '>') { i++; } if (text[i] == 0) { return Status::Error(400, PSLICE() << "Unclosed start tag at byte offset " << begin_pos); } - string attribute_value; - if (text[i] != '\'' && text[i] != '"') { - // A name token (a sequence of letters, digits, periods, or hyphens). Name tokens are not case sensitive. - auto token_begin_pos = i; - while (is_alnum(text[i]) || text[i] == '.' || text[i] == '-') { + string tag_name = to_lower(text.substr(begin_pos + 1, i - begin_pos - 1)); + if (tag_name != "a" && tag_name != "b" && tag_name != "strong" && tag_name != "i" && tag_name != "em" && + tag_name != "s" && tag_name != "strike" && tag_name != "del" && tag_name != "u" && tag_name != "ins" && + tag_name != "pre" && tag_name != "code") { + return Status::Error(400, PSLICE() + << "Unsupported start tag \"" << tag_name << "\" at byte offset " << begin_pos); + } + + string argument; + while (text[i] != '>') { + while (text[i] != 0 && is_space(text[i])) { i++; } - attribute_value.assign(text, token_begin_pos, i - token_begin_pos); - to_lower_inplace(attribute_value); - - if (!is_space(text[i]) && text[i] != '>') { - return Status::Error(400, PSLICE() << "Unexpected end of name token at byte offset " << token_begin_pos); + if (text[i] == '>') { + break; } - } else { - // A string literal - char end_character = text[i++]; - while (text[i] != end_character && text[i] != 0) { - if (text[i] == '&') { - auto ch = decode_html_entity(text, i); - if (ch != 0) { - append_utf8_character(attribute_value, ch); - continue; + auto attribute_begin_pos = i; + while (!is_space(text[i]) && text[i] != '=') { + i++; + } + Slice attribute_name = text.substr(attribute_begin_pos, i - attribute_begin_pos); + if (attribute_name.empty()) { + return Status::Error( + 400, PSLICE() << "Empty attribute name in the tag \"" << tag_name << "\" at byte offset " << begin_pos); + } + while (text[i] != 0 && is_space(text[i])) { + i++; + } + if (text[i] != '=') { + return Status::Error(400, PSLICE() << "Expected equal sign in declaration of an attribute of the tag \"" + << tag_name << "\" at byte offset " << begin_pos); + } + i++; + while (text[i] != 0 && is_space(text[i])) { + i++; + } + if (text[i] == 0) { + return Status::Error(400, PSLICE() + << "Unclosed start tag \"" << tag_name << "\" at byte offset " << begin_pos); + } + + string attribute_value; + if (text[i] != '\'' && text[i] != '"') { + // A name token (a sequence of letters, digits, periods, or hyphens). Name tokens are not case sensitive. + auto token_begin_pos = i; + while (is_alnum(text[i]) || text[i] == '.' || text[i] == '-') { + i++; + } + attribute_value = to_lower(text.substr(token_begin_pos, i - token_begin_pos)); + + if (!is_space(text[i]) && text[i] != '>') { + return Status::Error(400, PSLICE() << "Unexpected end of name token at byte offset " << token_begin_pos); + } + } else { + // A string literal + char end_character = text[i++]; + while (text[i] != end_character && text[i] != 0) { + if (text[i] == '&') { + auto ch = decode_html_entity(text, i); + if (ch != 0) { + append_utf8_character(attribute_value, ch); + continue; + } + } + attribute_value.push_back(text[i++]); + } + if (text[i] == end_character) { + i++; + } + } + if (text[i] == 0) { + return Status::Error(400, PSLICE() << "Unclosed start tag at byte offset " << begin_pos); + } + + if (tag_name == "a" && attribute_name == Slice("href")) { + argument = std::move(attribute_value); + } + if (tag_name == "code" && attribute_name == Slice("class") && begins_with(attribute_value, "language-")) { + argument = attribute_value.substr(9); + } + } + + nested_entities.emplace_back(std::move(tag_name), std::move(argument), utf16_offset, result.size()); + } else { + // end of an entity + if (nested_entities.empty()) { + return Status::Error(400, PSLICE() << "Unexpected end tag at byte offset " << begin_pos); + } + + while (!is_space(text[i]) && text[i] != '>') { + i++; + } + Slice end_tag_name = text.substr(begin_pos + 2, i - begin_pos - 2); + while (is_space(text[i]) && text[i] != 0) { + i++; + } + if (text[i] != '>') { + return Status::Error(400, PSLICE() << "Unclosed end tag at byte offset " << begin_pos); + } + + string tag_name = std::move(nested_entities.back().tag_name); + if (!end_tag_name.empty() && end_tag_name != tag_name) { + return Status::Error(400, PSLICE() << "Unmatched end tag at byte offset " << begin_pos << ", expected \"\", found \"\""); + } + + if (utf16_offset > nested_entities.back().entity_offset) { + auto entity_offset = nested_entities.back().entity_offset; + auto entity_length = utf16_offset - entity_offset; + if (tag_name == "i" || tag_name == "em") { + entities.emplace_back(MessageEntity::Type::Italic, entity_offset, entity_length); + } else if (tag_name == "b" || tag_name == "strong") { + entities.emplace_back(MessageEntity::Type::Bold, entity_offset, entity_length); + } else if (tag_name == "s" || tag_name == "strike" || tag_name == "del") { + entities.emplace_back(MessageEntity::Type::Strikethrough, entity_offset, entity_length); + } else if (tag_name == "u" || tag_name == "ins") { + entities.emplace_back(MessageEntity::Type::Underline, entity_offset, entity_length); + } else if (tag_name == "a") { + auto url = std::move(nested_entities.back().argument); + if (url.empty()) { + url = result.substr(nested_entities.back().entity_begin_pos); + } + auto user_id = get_link_user_id(url); + if (user_id.is_valid()) { + entities.emplace_back(entity_offset, entity_length, user_id); + } else { + auto r_url = check_url(url); + if (r_url.is_ok()) { + entities.emplace_back(MessageEntity::Type::TextUrl, entity_offset, entity_length, r_url.move_as_ok()); } } - attribute_value.push_back(text[i++]); - } - if (text[i] == end_character) { - i++; - } - } - if (text[i] == 0) { - return Status::Error(400, PSLICE() << "Unclosed start tag at byte offset " << begin_pos); - } - - if (tag_name == "a" && attribute_name == "href") { - url = attribute_value; - } - } - i++; - - int32 utf16_entity_length = 0; - size_t entity_begin_pos = result.size(); - while (text[i] != 0 && text[i] != '<') { - auto cur_ch = static_cast(text[i]); - if (cur_ch == '&') { - auto ch = decode_html_entity(text, i); - if (ch != 0) { - utf16_entity_length += 1 + (ch > 0xffff); - append_utf8_character(result, ch); - continue; - } - } - if (is_utf8_character_first_code_unit(cur_ch)) { - utf16_entity_length += 1 + (cur_ch >= 0xf0); // >= 4 bytes in symbol => surrogaite pair - } - result.push_back(text[i++]); - } - if (text[i] == 0) { - return Status::Error(400, - PSLICE() << "Can't find end tag corresponding to start tag at byte offset " << begin_pos); - } - - auto end_tag_begin_pos = i++; - if (text[i] != '/') { - return Status::Error(400, PSLICE() << "Expected end tag at byte offset " << end_tag_begin_pos); - } - while (!is_space(text[i]) && text[i] != '>') { - i++; - } - string end_tag_name(text, end_tag_begin_pos + 2, i - end_tag_begin_pos - 2); - while (is_space(text[i]) && text[i] != 0) { - i++; - } - if (text[i] != '>') { - return Status::Error(400, PSLICE() << "Unclosed end tag at byte offset " << end_tag_begin_pos); - } - if (!end_tag_name.empty() && end_tag_name != tag_name) { - return Status::Error(400, PSLICE() << "Unmatched end tag at byte offset " << end_tag_begin_pos - << ", expected \"\", found\"\""); - } - - if (utf16_entity_length > 0) { - if (tag_name == "i" || tag_name == "em") { - entities.emplace_back(MessageEntity::Type::Italic, utf16_offset, utf16_entity_length); - } else if (tag_name == "b" || tag_name == "strong") { - entities.emplace_back(MessageEntity::Type::Bold, utf16_offset, utf16_entity_length); - } else if (tag_name == "a") { - if (url.empty()) { - url = result.substr(entity_begin_pos); - } - auto user_id = get_link_user_id(url); - if (user_id.is_valid()) { - entities.emplace_back(utf16_offset, utf16_entity_length, user_id); - } else { - auto r_url = check_url(url); - if (r_url.is_ok()) { - entities.emplace_back(MessageEntity::Type::TextUrl, utf16_offset, utf16_entity_length, r_url.move_as_ok()); + } else if (tag_name == "pre") { + if (!entities.empty() && entities.back().type == MessageEntity::Type::Code && + entities.back().offset == entity_offset && entities.back().length == entity_length && + !entities.back().argument.empty()) { + entities.back().type = MessageEntity::Type::PreCode; + } else { + entities.emplace_back(MessageEntity::Type::Pre, entity_offset, entity_length); } + } else if (tag_name == "code") { + if (!entities.empty() && entities.back().type == MessageEntity::Type::Pre && + entities.back().offset == entity_offset && entities.back().length == entity_length && + !nested_entities.back().argument.empty()) { + entities.back().type = MessageEntity::Type::PreCode; + entities.back().argument = std::move(nested_entities.back().argument); + } else { + entities.emplace_back(MessageEntity::Type::Code, entity_offset, entity_length, + nested_entities.back().argument); + } + } else { + UNREACHABLE(); } - } else if (tag_name == "pre") { - entities.emplace_back(MessageEntity::Type::Pre, utf16_offset, utf16_entity_length); - } else if (tag_name == "code") { - entities.emplace_back(MessageEntity::Type::Code, utf16_offset, utf16_entity_length); } - utf16_offset += utf16_entity_length; + nested_entities.pop_back(); } } + if (!nested_entities.empty()) { + return Status::Error( + 400, PSLICE() << "Can't find end tag corresponding to start tag " << nested_entities.back().tag_name); + } + + for (auto &entity : entities) { + if (entity.type == MessageEntity::Type::Code && !entity.argument.empty()) { + entity.argument.clear(); + } + } + + std::sort(entities.begin(), entities.end()); + + return entities; +} + +Result> parse_html(string &text) { + string result; + TRY_RESULT(entities, do_parse_html(text, result)); if (!check_utf8(result)) { return Status::Error(400, "Text contains invalid Unicode characters after decoding HTML entities, check for unmatched " "surrogate code units"); } - text = result; return entities; } @@ -1646,6 +1962,15 @@ vector> get_input_message_entities(co case MessageEntity::Type::Italic: result.push_back(make_tl_object(entity.offset, entity.length)); break; + case MessageEntity::Type::Underline: + result.push_back(make_tl_object(entity.offset, entity.length)); + break; + case MessageEntity::Type::Strikethrough: + result.push_back(make_tl_object(entity.offset, entity.length)); + break; + case MessageEntity::Type::BlockQuote: + result.push_back(make_tl_object(entity.offset, entity.length)); + break; case MessageEntity::Type::Code: result.push_back(make_tl_object(entity.offset, entity.length)); break; @@ -1684,7 +2009,7 @@ vector> get_input_message_entities(co } vector> get_input_secret_message_entities( - const vector &entities) { + const vector &entities, int32 layer) { vector> result; for (auto &entity : entities) { switch (entity.type) { @@ -1710,6 +2035,21 @@ vector> get_input_secret_message_entiti case MessageEntity::Type::Italic: result.push_back(make_tl_object(entity.offset, entity.length)); break; + case MessageEntity::Type::Underline: + if (layer >= SecretChatActor::NEW_ENTITIES_LAYER) { + result.push_back(make_tl_object(entity.offset, entity.length)); + } + break; + case MessageEntity::Type::Strikethrough: + if (layer >= SecretChatActor::NEW_ENTITIES_LAYER) { + result.push_back(make_tl_object(entity.offset, entity.length)); + } + break; + case MessageEntity::Type::BlockQuote: + if (layer >= SecretChatActor::NEW_ENTITIES_LAYER) { + result.push_back(make_tl_object(entity.offset, entity.length)); + } + break; case MessageEntity::Type::Code: result.push_back(make_tl_object(entity.offset, entity.length)); break; @@ -1758,6 +2098,12 @@ Result> get_message_entities(const ContactsManager *contac case td_api::textEntityTypeItalic::ID: entities.emplace_back(MessageEntity::Type::Italic, entity->offset_, entity->length_); break; + case td_api::textEntityTypeUnderline::ID: + entities.emplace_back(MessageEntity::Type::Underline, entity->offset_, entity->length_); + break; + case td_api::textEntityTypeStrikethrough::ID: + entities.emplace_back(MessageEntity::Type::Strikethrough, entity->offset_, entity->length_); + break; case td_api::textEntityTypeCode::ID: entities.emplace_back(MessageEntity::Type::Code, entity->offset_, entity->length_); break; @@ -1856,6 +2202,21 @@ vector get_message_entities(const ContactsManager *contacts_manag entities.emplace_back(MessageEntity::Type::Italic, entity_italic->offset_, entity_italic->length_); break; } + case telegram_api::messageEntityUnderline::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::Underline, entity_bold->offset_, entity_bold->length_); + break; + } + case telegram_api::messageEntityStrike::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::Strikethrough, entity_bold->offset_, entity_bold->length_); + break; + } + case telegram_api::messageEntityBlockquote::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::BlockQuote, entity_bold->offset_, entity_bold->length_); + break; + } case telegram_api::messageEntityCode::ID: { auto entity_code = static_cast(entity.get()); entities.emplace_back(MessageEntity::Type::Code, entity_code->offset_, entity_code->length_); @@ -1952,6 +2313,21 @@ vector get_message_entities(vectoroffset_, entity_italic->length_); break; } + case secret_api::messageEntityUnderline::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::Underline, entity_bold->offset_, entity_bold->length_); + break; + } + case secret_api::messageEntityStrike::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::Strikethrough, entity_bold->offset_, entity_bold->length_); + break; + } + case secret_api::messageEntityBlockquote::ID: { + auto entity_bold = static_cast(entity.get()); + entities.emplace_back(MessageEntity::Type::BlockQuote, entity_bold->offset_, entity_bold->length_); + break; + } case secret_api::messageEntityCode::ID: { auto entity_code = static_cast(entity.get()); entities.emplace_back(MessageEntity::Type::Code, entity_code->offset_, entity_code->length_); @@ -1996,27 +2372,24 @@ vector get_message_entities(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"); - } +// like clean_input_string but also fixes entities +// entities must be sorted, can be nested, but must not intersect each other +static Result clean_input_string_with_entities(const string &text, vector &entities) { + struct EntityInfo { + MessageEntity *entity; + int32 utf16_skipped_before; - fix_entities(entities); - - bool in_entity = false; - bool have_space_in_entity = false; - bool have_non_whitespace_in_entity = false; + EntityInfo(MessageEntity *entity, int32 utf16_skipped_before) + : entity(entity), utf16_skipped_before(utf16_skipped_before) { + } + }; + vector nested_entities_stack; size_t current_entity = 0; - int32 skipped_before_current_entity = 0; - size_t left_entities = 0; // will remove entities containing whitespaces only int32 utf16_offset = 0; int32 utf16_skipped = 0; size_t text_size = text.size(); - size_t last_non_whitespace_pos = text_size + 1; - int32 last_non_whitespace_utf16_offset = 0; string result; result.reserve(text_size); @@ -2024,40 +2397,30 @@ Status fix_formatted_text(string &text, vector &entities, bool al auto c = static_cast(text[pos]); bool is_utf8_character_begin = is_utf8_character_first_code_unit(c); if (is_utf8_character_begin) { - if (in_entity) { - CHECK(current_entity < entities.size()); - if (utf16_offset >= entities[current_entity].offset + entities[current_entity].length) { - if (utf16_offset != entities[current_entity].offset + entities[current_entity].length) { - CHECK(utf16_offset == entities[current_entity].offset + entities[current_entity].length + 1); - return Status::Error(16, PSLICE() << "Entity beginning at UTF-16 offset " << entities[current_entity].offset - << " ends in a middle of a UTF-16 symbol at byte offset " << pos); - } - entities[current_entity].offset -= skipped_before_current_entity; - entities[current_entity].length -= utf16_skipped - skipped_before_current_entity; - in_entity = false; - - auto entity_type = entities[current_entity].type; - auto have_hidden_data = - entity_type == MessageEntity::Type::TextUrl || entity_type == MessageEntity::Type::MentionName; - if (have_non_whitespace_in_entity || (have_space_in_entity && have_hidden_data)) { - // TODO check entities for validness, for example, that mentions, hashtags, cashtags and URLs are valid - if (current_entity != left_entities) { - entities[left_entities] = std::move(entities[current_entity]); - } - left_entities++; - } - current_entity++; + while (!nested_entities_stack.empty()) { + auto *entity = nested_entities_stack.back().entity; + auto entity_end = entity->offset + entity->length; + if (utf16_offset < entity_end) { + break; } + + if (utf16_offset != entity_end) { + CHECK(utf16_offset == entity_end + 1); + return Status::Error(400, PSLICE() << "Entity beginning at UTF-16 offset " << entity->offset + << " ends in a middle of a UTF-16 symbol at byte offset " << pos); + } + + auto skipped_before_current_entity = nested_entities_stack.back().utf16_skipped_before; + entity->offset -= skipped_before_current_entity; + entity->length -= utf16_skipped - skipped_before_current_entity; + nested_entities_stack.pop_back(); } - if (!in_entity && current_entity < entities.size() && utf16_offset >= entities[current_entity].offset) { + while (current_entity < entities.size() && utf16_offset >= entities[current_entity].offset) { if (utf16_offset != entities[current_entity].offset) { CHECK(utf16_offset == entities[current_entity].offset + 1); - return Status::Error(16, PSLICE() << "Entity begins in a middle of a UTF-16 symbol at byte offset " << pos); + return Status::Error(400, PSLICE() << "Entity begins in a middle of a UTF-16 symbol at byte offset " << pos); } - in_entity = true; - have_space_in_entity = false; - have_non_whitespace_in_entity = false; - skipped_before_current_entity = utf16_skipped; + nested_entities_stack.emplace_back(&entities[current_entity++], utf16_skipped); } } if (pos == text_size) { @@ -2099,7 +2462,6 @@ Status fix_formatted_text(string &text, vector &entities, bool al case 30: case 31: case 32: - have_space_in_entity = true; result.push_back(' '); utf16_offset++; break; @@ -2134,18 +2496,111 @@ Status fix_formatted_text(string &text, vector &entities, bool al } result.push_back(text[pos]); - - if (c != '\n') { - have_non_whitespace_in_entity = true; - last_non_whitespace_pos = result.size(); - last_non_whitespace_utf16_offset = utf16_offset - utf16_skipped; - } break; } } - entities.erase(entities.begin() + left_entities, entities.end()); - if (last_non_whitespace_pos == text_size + 1) { + if (current_entity != entities.size()) { + return Status::Error(400, PSLICE() << "Entity begins after the end of the text at UTF-16 offset " + << entities[current_entity].offset); + } + if (!nested_entities_stack.empty()) { + auto *entity = nested_entities_stack.back().entity; + return Status::Error(400, PSLICE() << "Entity beginning at UTF-16 offset " << entity->offset + << " ends after the end of the text at UTF-16 offset " + << entity->offset + entity->length); + } + + return result; +} + +// removes entities containing whitespaces only +static std::pair remove_invalid_entities(const string &text, vector &entities) { + vector nested_entities_stack; + size_t current_entity = 0; + + size_t last_non_whitespace_pos = text.size(); + + int32 utf16_offset = 0; + int32 last_space_utf16_offset = -1; + int32 last_non_whitespace_utf16_offset = -1; + + for (size_t pos = 0; pos <= text.size(); pos++) { + while (current_entity < entities.size() && utf16_offset >= entities[current_entity].offset && + entities[current_entity].length == 0) { + nested_entities_stack.push_back(&entities[current_entity++]); + } + while (!nested_entities_stack.empty()) { + auto *entity = nested_entities_stack.back(); + auto entity_end = entity->offset + entity->length; + if (utf16_offset < entity_end) { + break; + } + + auto have_hidden_data = + entity->type == MessageEntity::Type::TextUrl || entity->type == MessageEntity::Type::MentionName; + 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 + // keep entity + } else { + entity->length = 0; + } + + nested_entities_stack.pop_back(); + } + while (current_entity < entities.size() && utf16_offset >= entities[current_entity].offset) { + nested_entities_stack.push_back(&entities[current_entity++]); + } + + if (pos == text.size()) { + break; + } + + auto c = static_cast(text[pos]); + switch (c) { + case '\n': + break; + case 32: + last_space_utf16_offset = utf16_offset; + break; + default: + while (!is_utf8_character_first_code_unit(static_cast(text[pos + 1]))) { + pos++; + } + utf16_offset += (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair + last_non_whitespace_pos = pos; + last_non_whitespace_utf16_offset = utf16_offset; + break; + } + + utf16_offset++; + } + CHECK(nested_entities_stack.empty()); + CHECK(current_entity == entities.size()); + + td::remove_if(entities, [](const auto &entity) { return entity.length == 0; }); + + return {last_non_whitespace_pos, last_non_whitespace_utf16_offset}; +} + +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"); + } + + fix_entities(entities); + + TRY_RESULT(result, clean_input_string_with_entities(text, entities)); + + // now entities are still sorted by offset and length, but not type, + // because some characters could be deleted and some entities bacame to end together + + size_t last_non_whitespace_pos; + int32 last_non_whitespace_utf16_offset; + std::tie(last_non_whitespace_pos, last_non_whitespace_utf16_offset) = remove_invalid_entities(result, entities); + if (last_non_whitespace_utf16_offset == -1) { if (allow_empty) { text.clear(); entities.clear(); @@ -2154,19 +2609,24 @@ 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 + } + if (for_draft) { text = std::move(result); } else { // rtrim - result.resize(last_non_whitespace_pos); - while (!entities.empty() && entities.back().offset >= last_non_whitespace_utf16_offset) { + CHECK(last_non_whitespace_pos < result.size()); + 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.pop_back(); } for (auto &entity : entities) { - if (entity.offset + entity.length > last_non_whitespace_utf16_offset) { - entity.length = last_non_whitespace_utf16_offset - entity.offset; + if (entity.offset + entity.length > last_non_whitespace_utf16_offset + 1) { + entity.length = last_non_whitespace_utf16_offset + 1 - entity.offset; CHECK(entity.length > 0); } } @@ -2202,22 +2662,16 @@ Status fix_formatted_text(string &text, vector &entities, bool al new_size--; } text.resize(new_size); - while (!entities.empty() && entities.back().offset + entities.back().length > 8192) { - entities.pop_back(); - } + + td::remove_if(entities, [text_utf16_length = narrow_cast(utf8_utf16_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)); } - for (auto it = entities.begin(); it != entities.end(); ++it) { - CHECK(it->length > 0); - if (it + 1 != entities.end()) { - CHECK(it->offset + it->length <= (it + 1)->offset); - } - } - // TODO MAX_MESSAGE_LENGTH and MAX_CAPTION_LENGTH return Status::OK(); @@ -2227,11 +2681,14 @@ FormattedText get_message_text(const ContactsManager *contacts_manager, string m vector> &&server_entities, bool skip_new_entities, int32 send_date, 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 > 1497000000) { // approximate fix date - LOG(ERROR) << "Receive error " << status << " while parsing message from " << source << " with content \"" - << message_text << "\" sent at " << send_date << " with entities " << format::as_array(entities); + 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); } if (!clean_input_string(message_text)) { message_text.clear(); @@ -2305,7 +2762,7 @@ bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId di return !contacts_manager->is_user_bot(dialog_id.get_user_id()); case DialogType::SecretChat: { auto user_id = contacts_manager->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); - return !contacts_manager->is_user_bot(user_id); + return !user_id.is_valid() || !contacts_manager->is_user_bot(user_id); } case DialogType::Chat: case DialogType::Channel: diff --git a/td/telegram/MessageEntity.h b/td/telegram/MessageEntity.h index 591ec65b..666302c7 100644 --- a/td/telegram/MessageEntity.h +++ b/td/telegram/MessageEntity.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,7 +19,6 @@ #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" -#include #include #include @@ -28,8 +27,6 @@ namespace td { class ContactsManager; class MessageEntity { - tl_object_ptr get_text_entity_type_object() const; - public: enum class Type : int32 { Mention, @@ -45,7 +42,10 @@ class MessageEntity { TextUrl, MentionName, Cashtag, - PhoneNumber + PhoneNumber, + Underline, + Strikethrough, + BlockQuote }; Type type; int32 offset; @@ -70,7 +70,15 @@ class MessageEntity { } bool operator<(const MessageEntity &other) const { - return std::tie(offset, length, type) < std::tie(other.offset, other.length, other.type); + if (offset != other.offset) { + return offset < other.offset; + } + if (length != other.length) { + return length > other.length; + } + auto priority = get_type_priority(type); + auto other_priority = get_type_priority(other.type); + return priority < other_priority; } bool operator!=(const MessageEntity &rhs) const { @@ -82,8 +90,15 @@ class MessageEntity { template void parse(ParserT &parser); + + private: + tl_object_ptr get_text_entity_type_object() const; + + static int get_type_priority(Type type); }; +StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Type &message_entity_type); + StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity); struct FormattedText { @@ -127,6 +142,8 @@ string get_first_url(Slice text, const vector &entities); Result> parse_markdown(string &text); +Result> parse_markdown_v2(string &text); + Result> parse_html(string &text); vector> get_input_message_entities(const ContactsManager *contacts_manager, @@ -138,7 +155,7 @@ vector> get_input_message_entities(co const char *source); vector> get_input_secret_message_entities( - const vector &entities); + const vector &entities, int32 layer); vector get_message_entities(const ContactsManager *contacts_manager, vector> &&server_entities, diff --git a/td/telegram/MessageEntity.hpp b/td/telegram/MessageEntity.hpp index 8767261a..01d298cc 100644 --- a/td/telegram/MessageEntity.hpp +++ b/td/telegram/MessageEntity.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/MessageId.cpp b/td/telegram/MessageId.cpp new file mode 100644 index 00000000..83765c1b --- /dev/null +++ b/td/telegram/MessageId.cpp @@ -0,0 +1,156 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/MessageId.h" + +#include "td/utils/logging.h" +#include "td/utils/misc.h" + +namespace td { + +MessageId::MessageId(ScheduledServerMessageId server_message_id, int32 send_date, bool force) { + if (send_date <= (1 << 30)) { + LOG(ERROR) << "Scheduled message send date " << send_date << " is in the past"; + return; + } + if (!server_message_id.is_valid() && !force) { + LOG(ERROR) << "Scheduled message ID " << server_message_id.get() << " is invalid"; + return; + } + id = (static_cast(send_date - (1 << 30)) << 21) | (server_message_id.get() << 3) | SCHEDULED_MASK; +} + +bool MessageId::is_valid() const { + if (id <= 0 || id > max().get()) { + return false; + } + if ((id & FULL_TYPE_MASK) == 0) { + return true; + } + int32 type = (id & TYPE_MASK); + return type == TYPE_YET_UNSENT || type == TYPE_LOCAL; +} + +bool MessageId::is_valid_scheduled() const { + if (id <= 0 || id > max().get()) { + return false; + } + int32 type = (id & TYPE_MASK); + return type == SCHEDULED_MASK || type == (SCHEDULED_MASK | TYPE_YET_UNSENT) || type == (SCHEDULED_MASK | TYPE_LOCAL); +} + +MessageType MessageId::get_type() const { + if (id <= 0 || id > max().get()) { + return MessageType::None; + } + + if (is_scheduled()) { + switch (id & TYPE_MASK) { + case SCHEDULED_MASK | TYPE_YET_UNSENT: + return MessageType::YetUnsent; + case SCHEDULED_MASK | TYPE_LOCAL: + return MessageType::Local; + case SCHEDULED_MASK: + return MessageType::Server; + default: + return MessageType::None; + } + } + + if ((id & FULL_TYPE_MASK) == 0) { + return MessageType::Server; + } + switch (id & TYPE_MASK) { + case TYPE_YET_UNSENT: + return MessageType::YetUnsent; + case TYPE_LOCAL: + return MessageType::Local; + default: + return MessageType::None; + } +} + +ServerMessageId MessageId::get_server_message_id_force() const { + CHECK(!is_scheduled()); + return ServerMessageId(narrow_cast(id >> SERVER_ID_SHIFT)); +} + +MessageId MessageId::get_next_message_id(MessageType type) const { + if (is_scheduled()) { + CHECK(is_valid_scheduled()); + auto current_type = get_type(); + if (static_cast(current_type) < static_cast(type)) { + return MessageId(id - static_cast(current_type) + static_cast(type)); + } + int64 base_id = (id & ~TYPE_MASK) + TYPE_MASK + 1 + SCHEDULED_MASK; + switch (type) { + case MessageType::Server: + return MessageId(base_id); + case MessageType::YetUnsent: + return MessageId(base_id + TYPE_YET_UNSENT); + case MessageType::Local: + return MessageId(base_id + TYPE_LOCAL); + case MessageType::None: + default: + UNREACHABLE(); + return MessageId(); + } + } + + switch (type) { + case MessageType::Server: + if (is_server()) { + return MessageId(ServerMessageId(get_server_message_id().get() + 1)); + } + return get_next_server_message_id(); + case MessageType::YetUnsent: + return MessageId(((id + TYPE_MASK + 1 - TYPE_YET_UNSENT) & ~TYPE_MASK) + TYPE_YET_UNSENT); + case MessageType::Local: + return MessageId(((id + TYPE_MASK + 1 - TYPE_LOCAL) & ~TYPE_MASK) + TYPE_LOCAL); + case MessageType::None: + default: + UNREACHABLE(); + return MessageId(); + } +} + +StringBuilder &operator<<(StringBuilder &string_builder, MessageId message_id) { + if (message_id.is_scheduled()) { + string_builder << "scheduled "; + + if (!message_id.is_valid_scheduled()) { + return string_builder << "invalid message " << message_id.get(); + } + if (message_id.is_scheduled_server()) { + return string_builder << "server message " << message_id.get_scheduled_server_message_id_force().get(); + } + if (message_id.is_local()) { + return string_builder << "local message " << message_id.get_scheduled_server_message_id_force().get(); + } + if (message_id.is_yet_unsent()) { + return string_builder << "yet unsent message " << message_id.get_scheduled_server_message_id_force().get(); + } + return string_builder << "bugged message " << message_id.get(); + } + + if (!message_id.is_valid()) { + return string_builder << "invalid message " << message_id.get(); + } + if (message_id.is_server()) { + return string_builder << "server message " << message_id.get_server_message_id_force().get(); + } + if (message_id.is_local()) { + return string_builder << "local message " << message_id.get_server_message_id_force().get() << '.' + << (message_id.get() & MessageId::FULL_TYPE_MASK); + } + if (message_id.is_yet_unsent()) { + return string_builder << "yet unsent message " << message_id.get_server_message_id_force().get() << '.' + << (message_id.get() & MessageId::FULL_TYPE_MASK); + } + return string_builder << "bugged message " << message_id.get(); +} + +} // namespace td diff --git a/td/telegram/MessageId.h b/td/telegram/MessageId.h index e4347d3e..1d4392e7 100644 --- a/td/telegram/MessageId.h +++ b/td/telegram/MessageId.h @@ -1,17 +1,16 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once -#include "td/telegram/DialogId.h" +#include "td/telegram/ScheduledServerMessageId.h" +#include "td/telegram/ServerMessageId.h" #include "td/utils/common.h" -#include "td/utils/misc.h" #include "td/utils/StringBuilder.h" -#include "td/utils/tl_helpers.h" #include #include @@ -19,114 +18,79 @@ namespace td { -class ServerMessageId { - int32 id = 0; - - public: - ServerMessageId() = default; - - explicit ServerMessageId(int32 message_id) : id(message_id) { - } - template ::value>> - ServerMessageId(T message_id) = delete; - - bool is_valid() const { - return id > 0; - } - - int32 get() const { - return id; - } - - bool operator==(const ServerMessageId &other) const { - return id == other.id; - } - - bool operator!=(const ServerMessageId &other) const { - return id != other.id; - } - - template - void store(StorerT &storer) const { - storer.store_int(id); - } - - template - void parse(ParserT &parser) { - id = parser.fetch_int(); - } -}; - -enum class MessageType : int32 { None, Server, Local, YetUnsent }; +enum class MessageType : int32 { None, Server, YetUnsent, Local }; class MessageId { int64 id = 0; - public: static constexpr int32 SERVER_ID_SHIFT = 20; + static constexpr int32 SHORT_TYPE_MASK = (1 << 2) - 1; static constexpr int32 TYPE_MASK = (1 << 3) - 1; static constexpr int32 FULL_TYPE_MASK = (1 << SERVER_ID_SHIFT) - 1; + static constexpr int32 SCHEDULED_MASK = 4; static constexpr int32 TYPE_YET_UNSENT = 1; static constexpr int32 TYPE_LOCAL = 2; + friend StringBuilder &operator<<(StringBuilder &string_builder, MessageId message_id); + + // ordinary message ID layout + // |-------31--------|---17---|1|--2-| + // |server_message_id|local_id|0|type| + + // scheduled message ID layout + // |-------30-------|----18---|1|--2-| + // |send_date-2**30 |server_id|1|type| + + ServerMessageId get_server_message_id_force() const; + + ScheduledServerMessageId get_scheduled_server_message_id_force() const { + CHECK(is_scheduled()); + return ScheduledServerMessageId(static_cast((id >> 3) & ((1 << 18) - 1))); + } + + public: MessageId() = default; explicit MessageId(ServerMessageId server_message_id) : id(static_cast(server_message_id.get()) << SERVER_ID_SHIFT) { } + MessageId(ScheduledServerMessageId server_message_id, int32 send_date, bool force = false); + explicit constexpr MessageId(int64 message_id) : id(message_id) { } template ::value>> MessageId(T message_id) = delete; static constexpr MessageId min() { - return MessageId(static_cast(MessageId::TYPE_LOCAL)); + return MessageId(static_cast(MessageId::TYPE_YET_UNSENT)); } static constexpr MessageId max() { return MessageId(static_cast(std::numeric_limits::max()) << SERVER_ID_SHIFT); } - bool is_valid() const { - if (id <= 0 || id > max().get()) { - return false; - } - if ((id & FULL_TYPE_MASK) == 0) { - return true; - } - int32 type = (id & TYPE_MASK); - return type == TYPE_YET_UNSENT || type == TYPE_LOCAL; - } + bool is_valid() const; + + bool is_valid_scheduled() const; int64 get() const { return id; } - MessageType get_type() const { - if (id <= 0 || id > max().get()) { - return MessageType::None; - } - if ((id & FULL_TYPE_MASK) == 0) { - return MessageType::Server; - } - switch (id & TYPE_MASK) { - case TYPE_YET_UNSENT: - return MessageType::YetUnsent; - case TYPE_LOCAL: - return MessageType::Local; - default: - return MessageType::None; - } + MessageType get_type() const; + + bool is_scheduled() const { + return (id & SCHEDULED_MASK) != 0; } bool is_yet_unsent() const { - CHECK(is_valid()); - return (id & TYPE_MASK) == TYPE_YET_UNSENT; + CHECK(is_valid() || is_scheduled()); + return (id & SHORT_TYPE_MASK) == TYPE_YET_UNSENT; } bool is_local() const { - CHECK(is_valid()); - return (id & TYPE_MASK) == TYPE_LOCAL; + CHECK(is_valid() || is_scheduled()); + return (id & SHORT_TYPE_MASK) == TYPE_LOCAL; } bool is_server() const { @@ -134,21 +98,44 @@ class MessageId { return (id & FULL_TYPE_MASK) == 0; } - ServerMessageId get_server_message_id() const { - CHECK(id == 0 || is_server()); - return ServerMessageId(narrow_cast(id >> SERVER_ID_SHIFT)); + bool is_scheduled_server() const { + CHECK(is_valid_scheduled()); + return (id & SHORT_TYPE_MASK) == 0; } - // returns greatest server message id not bigger than this message id + bool is_any_server() const { + return is_scheduled() ? is_scheduled_server() : is_server(); + } + + ServerMessageId get_server_message_id() const { + CHECK(id == 0 || is_server()); + return get_server_message_id_force(); + } + + // returns greatest server message identifier not bigger than this message identifier MessageId get_prev_server_message_id() const { + CHECK(!is_scheduled()); return MessageId(id & ~FULL_TYPE_MASK); } - // returns smallest server message id not less than this message id + // returns smallest server message identifier not less than this message identifier MessageId get_next_server_message_id() const { + CHECK(!is_scheduled()); return MessageId((id + FULL_TYPE_MASK) & ~FULL_TYPE_MASK); } + MessageId get_next_message_id(MessageType type) const; + + ScheduledServerMessageId get_scheduled_server_message_id() const { + CHECK(is_scheduled_server()); + return get_scheduled_server_message_id_force(); + } + + int32 get_scheduled_message_date() const { + CHECK(is_valid_scheduled()); + return static_cast(id >> 21) + (1 << 30); + } + bool operator==(const MessageId &other) const { return id == other.id; } @@ -157,6 +144,23 @@ class MessageId { return id != other.id; } + friend bool operator<(const MessageId &lhs, const MessageId &rhs) { + CHECK(lhs.is_scheduled() == rhs.is_scheduled()); + return lhs.id < rhs.id; + } + + friend bool operator>(const MessageId &lhs, const MessageId &rhs) { + return rhs < lhs; + } + + friend bool operator<=(const MessageId &lhs, const MessageId &rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const MessageId &lhs, const MessageId &rhs) { + return !(lhs < rhs); + } + template void store(StorerT &storer) const { storer.store_long(id); @@ -174,75 +178,4 @@ struct MessageIdHash { } }; -inline StringBuilder &operator<<(StringBuilder &string_builder, MessageId message_id) { - if (!message_id.is_valid()) { - return string_builder << "invalid message " << message_id.get(); - } - if (message_id.is_server()) { - return string_builder << "server message " << (message_id.get() >> MessageId::SERVER_ID_SHIFT); - } - if (message_id.is_local()) { - return string_builder << "local message " << (message_id.get() >> MessageId::SERVER_ID_SHIFT) << '.' - << (message_id.get() & MessageId::FULL_TYPE_MASK); - } - if (message_id.is_yet_unsent()) { - return string_builder << "yet unsent message " << (message_id.get() >> MessageId::SERVER_ID_SHIFT) << '.' - << (message_id.get() & MessageId::FULL_TYPE_MASK); - } - return string_builder << "bugged message " << message_id.get(); -} - -struct FullMessageId { - private: - DialogId dialog_id; - MessageId message_id; - - public: - FullMessageId() : dialog_id(), message_id() { - } - - FullMessageId(DialogId dialog_id, MessageId message_id) : dialog_id(dialog_id), message_id(message_id) { - } - - bool operator==(const FullMessageId &other) const { - return dialog_id == other.dialog_id && message_id == other.message_id; - } - - bool operator!=(const FullMessageId &other) const { - return !(*this == other); - } - - DialogId get_dialog_id() const { - return dialog_id; - } - MessageId get_message_id() const { - return message_id; - } - - template - void store(StorerT &storer) const { - using ::td::store; - store(dialog_id, storer); - store(message_id, storer); - } - - template - void parse(ParserT &parser) { - using ::td::parse; - parse(dialog_id, parser); - parse(message_id, parser); - } -}; - -struct FullMessageIdHash { - std::size_t operator()(FullMessageId full_message_id) const { - return DialogIdHash()(full_message_id.get_dialog_id()) * 2023654985u + - MessageIdHash()(full_message_id.get_message_id()); - } -}; - -inline StringBuilder &operator<<(StringBuilder &string_builder, FullMessageId full_message_id) { - return string_builder << full_message_id.get_message_id() << " in " << full_message_id.get_dialog_id(); -} - } // namespace td diff --git a/td/telegram/MessagesDb.cpp b/td/telegram/MessagesDb.cpp index 1352ac57..b46d35bd 100644 --- a/td/telegram/MessagesDb.cpp +++ b/td/telegram/MessagesDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,49 +53,59 @@ Status init_messages_db(SqliteDb &db, int32 version) { } auto add_media_indices = [&db](int begin, int end) { -// for (int i = begin; i < end; i++) { -// TRY_STATUS(db.exec(PSLICE() << "CREATE INDEX IF NOT EXISTS message_index_" << i -// << " ON messages (dialog_id, message_id) WHERE (index_mask & " << (1 << i) -// << ") != 0")); -// } + // for (int i = begin; i < end; i++) { + // TRY_STATUS(db.exec(PSLICE() << "CREATE INDEX IF NOT EXISTS message_index_" << i + // << " ON messages (dialog_id, message_id) WHERE (index_mask & " << (1 << i) + // << ") != 0")); + // } return Status::OK(); }; auto add_fts = [&db] { - TRY_STATUS( - db.exec("CREATE INDEX IF NOT EXISTS message_by_search_id ON messages " - "(search_id) WHERE search_id IS NOT NULL")); + // TRY_STATUS( + // db.exec("CREATE INDEX IF NOT EXISTS message_by_search_id ON messages " + // "(search_id) WHERE search_id IS NOT NULL")); + + // TRY_STATUS( + // db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(text, content='messages', " + // "content_rowid='search_id', tokenize = \"unicode61 remove_diacritics 0 tokenchars '\a'\")")); + // TRY_STATUS(db.exec( + // "CREATE TRIGGER IF NOT EXISTS trigger_fts_delete BEFORE DELETE ON messages WHEN OLD.search_id IS NOT NULL" + // " BEGIN INSERT INTO messages_fts(messages_fts, rowid, text) VALUES(\'delete\', OLD.search_id, OLD.text); END")); + // TRY_STATUS(db.exec( + // "CREATE TRIGGER IF NOT EXISTS trigger_fts_insert AFTER INSERT ON messages WHEN NEW.search_id IS NOT NULL" + // " BEGIN INSERT INTO messages_fts(rowid, text) VALUES(NEW.search_id, NEW.text); END")); + // //TRY_STATUS(db.exec( + // //"CREATE TRIGGER IF NOT EXISTS trigger_fts_update AFTER UPDATE ON messages WHEN NEW.search_id IS NOT NULL OR " + // //"OLD.search_id IS NOT NULL" + // //" BEGIN " + // //"INSERT INTO messages_fts(messages_fts, rowid, text) VALUES(\'delete\', OLD.search_id, OLD.text); " + // //"INSERT INTO messages_fts(rowid, text) VALUES(NEW.search_id, NEW.text); " + // //" END")); -// TRY_STATUS( -// db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(text, content='messages', " -// "content_rowid='search_id', tokenize = \"unicode61 remove_diacritics 0 tokenchars '\a'\")")); -// TRY_STATUS(db.exec( -// "CREATE TRIGGER IF NOT EXISTS trigger_fts_delete BEFORE DELETE ON messages WHEN OLD.search_id IS NOT NULL" -// " BEGIN INSERT INTO messages_fts(messages_fts, rowid, text) VALUES(\'delete\', OLD.search_id, OLD.text); END")); -// TRY_STATUS(db.exec( -// "CREATE TRIGGER IF NOT EXISTS trigger_fts_insert AFTER INSERT ON messages WHEN NEW.search_id IS NOT NULL" -// " BEGIN INSERT INTO messages_fts(rowid, text) VALUES(NEW.search_id, NEW.text); END")); -// TRY_STATUS(db.exec( -// "CREATE TRIGGER IF NOT EXISTS trigger_fts_update AFTER UPDATE ON messages WHEN NEW.search_id IS NOT NULL OR " -// "OLD.search_id IS NOT NULL" -// " BEGIN " -// "INSERT INTO messages_fts(messages_fts, rowid, text) VALUES(\'delete\', OLD.search_id, OLD.text); " -// "INSERT INTO messages_fts(rowid, text) VALUES(NEW.search_id, NEW.text); " -// " END")); return Status::OK(); }; auto add_call_index = [&db]() { -// for (int i = static_cast(SearchMessagesFilter::Call) - 1; -// i < static_cast(SearchMessagesFilter::MissedCall); i++) { -// TRY_STATUS(db.exec(PSLICE() << "CREATE INDEX IF NOT EXISTS full_message_index_" << i -// << " ON messages (unique_message_id) WHERE (index_mask & " << (1 << i) << ") != 0")); -// } + // for (int i = static_cast(SearchMessagesFilter::Call) - 1; + // i < static_cast(SearchMessagesFilter::MissedCall); i++) { + // TRY_STATUS(db.exec(PSLICE() << "CREATE INDEX IF NOT EXISTS full_message_index_" << i + // << " ON messages (unique_message_id) WHERE (index_mask & " << (1 << i) << ") != 0")); + // } return Status::OK(); }; 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]() { TRY_STATUS( - db.exec("CREATE INDEX IF NOT EXISTS message_by_notification_id ON messages (dialog_id, notification_id) " - "WHERE notification_id IS NOT NULL")); + 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))")); + + TRY_STATUS( + db.exec("CREATE INDEX IF NOT EXISTS message_by_server_message_id ON scheduled_messages " + "(dialog_id, server_message_id) WHERE server_message_id IS NOT NULL")); return Status::OK(); }; @@ -127,6 +137,8 @@ Status init_messages_db(SqliteDb &db, int32 version) { TRY_STATUS(add_notification_id_index()); + TRY_STATUS(add_scheduled_messages_table()); + version = current_db_version(); } if (version < static_cast(DbVersion::MessagesDbMediaIndex)) { @@ -148,6 +160,9 @@ Status init_messages_db(SqliteDb &db, int32 version) { TRY_STATUS(db.exec("ALTER TABLE messages ADD COLUMN notification_id INT4")); TRY_STATUS(add_notification_id_index()); } + if (version < static_cast(DbVersion::AddScheduledMessages)) { + TRY_STATUS(add_scheduled_messages_table()); + } return Status::OK(); } @@ -169,86 +184,85 @@ class MessagesDbImpl : public MessagesDbSyncInterface { db_memory_ = SqliteDb::open_with_key(":memory:", DbKey::empty()).move_as_ok(); TRY_STATUS(init_messages_db(db_memory_, db_.user_version().move_as_ok())); - TRY_RESULT( - add_message_stmt, + TRY_RESULT_ASSIGN( + add_message_stmt_, db_memory_.get_statement("INSERT OR REPLACE INTO messages VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)")); - TRY_RESULT(delete_message_stmt, db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND message_id = ?2")); - TRY_RESULT(delete_all_dialog_messages_stmt, - db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND message_id <= ?2")); - TRY_RESULT(delete_dialog_messages_from_user_stmt, - db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND sender_user_id == ?2")); + TRY_RESULT_ASSIGN(delete_message_stmt_, + db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND message_id = ?2")); + TRY_RESULT_ASSIGN(delete_all_dialog_messages_stmt_, + db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND message_id <= ?2")); + TRY_RESULT_ASSIGN(delete_dialog_messages_from_user_stmt_, + db_memory_.get_statement("DELETE FROM messages WHERE dialog_id = ?1 AND sender_user_id == ?2")); - TRY_RESULT(get_message_stmt, - db_memory_.get_statement("SELECT data FROM messages WHERE dialog_id = ?1 AND message_id = ?2")); - TRY_RESULT(get_message_by_random_id_stmt, - db_memory_.get_statement("SELECT data FROM messages WHERE dialog_id = ?1 AND random_id = ?2")); - TRY_RESULT(get_message_by_unique_message_id_stmt, - db_memory_.get_statement("SELECT dialog_id, data FROM messages WHERE unique_message_id = ?1")); + TRY_RESULT_ASSIGN(get_message_stmt_, + db_memory_.get_statement("SELECT data FROM messages WHERE dialog_id = ?1 AND message_id = ?2")); + TRY_RESULT_ASSIGN(get_message_by_random_id_stmt_, + db_memory_.get_statement("SELECT data FROM messages WHERE dialog_id = ?1 AND random_id = ?2")); + TRY_RESULT_ASSIGN(get_message_by_unique_message_id_stmt_, + db_memory_.get_statement("SELECT dialog_id, data FROM messages WHERE unique_message_id = ?1")); - TRY_RESULT(get_expiring_messages_stmt, - db_memory_.get_statement("SELECT dialog_id, data FROM messages WHERE ?1 < ttl_expires_at AND ttl_expires_at <= " - "?2")); - TRY_RESULT(get_expiring_messages_helper_stmt, - db_memory_.get_statement("SELECT MAX(ttl_expires_at), COUNT(*) FROM (SELECT ttl_expires_at FROM messages WHERE " - "?1 < ttl_expires_at LIMIT ?2) AS T")); + TRY_RESULT_ASSIGN( + get_expiring_messages_stmt_, + db_memory_.get_statement("SELECT dialog_id, data FROM messages WHERE ?1 < ttl_expires_at AND ttl_expires_at <= ?2")); + TRY_RESULT_ASSIGN(get_expiring_messages_helper_stmt_, + db_memory_.get_statement("SELECT MAX(ttl_expires_at), COUNT(*) FROM (SELECT ttl_expires_at FROM " + "messages WHERE ?1 < ttl_expires_at LIMIT ?2) AS T")); - TRY_RESULT(get_messages_asc_stmt, - db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 AND " - "message_id > ?2 ORDER BY message_id ASC LIMIT ?3")); - TRY_RESULT(get_messages_desc_stmt, db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 " - "AND message_id < ?2 ORDER BY message_id DESC LIMIT ?3")); - TRY_RESULT(get_messages_from_notification_id_stmt, - db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 " - "AND notification_id < ?2 ORDER BY notification_id DESC LIMIT ?3")); -// TRY_RESULT( -// get_messages_fts_stmt, -// db_.get_statement("SELECT dialog_id, data, search_id FROM messages WHERE search_id IN (SELECT rowid FROM " -// "messages_fts WHERE messages_fts MATCH ?1 AND rowid < ?2 ORDER BY rowid DESC LIMIT " -// "?3) ORDER BY search_id DESC")); + TRY_RESULT_ASSIGN(get_messages_stmt_.asc_stmt_, + db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 AND message_id > " + "?2 ORDER BY message_id ASC LIMIT ?3")); + TRY_RESULT_ASSIGN(get_messages_stmt_.desc_stmt_, + db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 AND message_id < " + "?2 ORDER BY message_id DESC LIMIT ?3")); + TRY_RESULT_ASSIGN(get_scheduled_messages_stmt_, + db_memory_.get_statement("SELECT data, message_id FROM scheduled_messages WHERE dialog_id = ?1 AND " + "message_id < ?2 ORDER BY message_id DESC LIMIT ?3")); + TRY_RESULT_ASSIGN(get_messages_from_notification_id_stmt_, + db_memory_.get_statement("SELECT data, message_id FROM messages WHERE dialog_id = ?1 AND " + "notification_id < ?2 ORDER BY notification_id DESC LIMIT ?3")); + // TRY_RESULT_ASSIGN( + // get_messages_fts_stmt_, + // db_memory_.get_statement( + // "SELECT dialog_id, data, search_id FROM messages WHERE search_id IN (SELECT rowid FROM messages_fts WHERE " + // "messages_fts MATCH ?1 AND rowid < ?2 ORDER BY rowid DESC LIMIT ?3) ORDER BY search_id DESC")); for (int32 i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { - TRY_RESULT(get_messages_from_index_desc_stmt, - db_memory_.get_statement(PSLICE() << "SELECT data, message_id FROM messages WHERE dialog_id = ?1 " - "AND message_id < ?2 AND (index_mask & " - << (1 << i) << ") != 0 ORDER BY message_id DESC LIMIT ?3")); - get_messages_from_index_stmts_[i].desc_stmt_ = std::move(get_messages_from_index_desc_stmt); + TRY_RESULT_ASSIGN(get_messages_from_index_stmts_[i].desc_stmt_, + db_memory_.get_statement(PSLICE() << "SELECT data, message_id FROM messages WHERE dialog_id = ?1 " + "AND message_id < ?2 AND (index_mask & " + << (1 << i) << ") != 0 ORDER BY message_id DESC LIMIT ?3")); - TRY_RESULT(get_messages_from_index_asc_stmt, - db_memory_.get_statement(PSLICE() << "SELECT data, message_id FROM messages WHERE dialog_id = ?1 " - "AND message_id > ?2 AND (index_mask & " - << (1 << i) << ") != 0 ORDER BY message_id ASC LIMIT ?3")); - get_messages_from_index_stmts_[i].asc_stmt_ = std::move(get_messages_from_index_asc_stmt); + TRY_RESULT_ASSIGN(get_messages_from_index_stmts_[i].asc_stmt_, + db_memory_.get_statement(PSLICE() << "SELECT data, message_id FROM messages WHERE dialog_id = ?1 " + "AND message_id > ?2 AND (index_mask & " + << (1 << i) << ") != 0 ORDER BY message_id ASC LIMIT ?3")); - // LOG(ERROR) << get_messages_from_index_stmts_[i].explain().ok(); + // LOG(ERROR) << get_messages_from_index_stmts_[i].desc_stmt_.explain().ok(); + // LOG(ERROR) << get_messages_from_index_stmts_[i].asc_stmt_.explain().ok(); } for (int i = static_cast(SearchMessagesFilter::Call) - 1, pos = 0; i < static_cast(SearchMessagesFilter::MissedCall); i++, pos++) { - TRY_RESULT(get_messages_from_index_stmt, - db_memory_.get_statement(PSLICE() << "SELECT dialog_id, data FROM messages " - "WHERE unique_message_id < ?1 AND (index_mask & " - << (1 << i) << ") != 0 ORDER BY unique_message_id DESC LIMIT ?2")); - get_calls_stmts_[pos] = std::move(get_messages_from_index_stmt); - // LOG(ERROR) << get_messages_from_index_stmts_[i].explain().ok(); + TRY_RESULT_ASSIGN( + get_calls_stmts_[pos], + db_memory_.get_statement( + PSLICE() << "SELECT dialog_id, data FROM messages WHERE unique_message_id < ?1 AND (index_mask & " + << (1 << i) << ") != 0 ORDER BY unique_message_id DESC LIMIT ?2")); } - add_message_stmt_ = std::move(add_message_stmt); - delete_message_stmt_ = std::move(delete_message_stmt); - delete_all_dialog_messages_stmt_ = std::move(delete_all_dialog_messages_stmt); - delete_dialog_messages_from_user_stmt_ = std::move(delete_dialog_messages_from_user_stmt); - - get_message_stmt_ = std::move(get_message_stmt); - get_message_by_random_id_stmt_ = std::move(get_message_by_random_id_stmt); - get_message_by_unique_message_id_stmt_ = std::move(get_message_by_unique_message_id_stmt); - - get_expiring_messages_stmt_ = std::move(get_expiring_messages_stmt); - get_expiring_messages_helper_stmt_ = std::move(get_expiring_messages_helper_stmt); - - get_messages_stmt_.asc_stmt_ = std::move(get_messages_asc_stmt); - get_messages_stmt_.desc_stmt_ = std::move(get_messages_desc_stmt); - get_messages_from_notification_id_stmt_ = std::move(get_messages_from_notification_id_stmt); - -// get_messages_fts_stmt_ = std::move(get_messages_fts_stmt); + TRY_RESULT_ASSIGN(add_scheduled_message_stmt_, + db_memory_.get_statement("INSERT OR REPLACE INTO scheduled_messages VALUES(?1, ?2, ?3, ?4)")); + TRY_RESULT_ASSIGN( + get_scheduled_message_stmt_, + db_memory_.get_statement("SELECT data FROM scheduled_messages WHERE dialog_id = ?1 AND message_id = ?2")); + TRY_RESULT_ASSIGN( + get_scheduled_server_message_stmt_, + db_memory_.get_statement("SELECT data FROM scheduled_messages WHERE dialog_id = ?1 AND server_message_id = ?2")); + TRY_RESULT_ASSIGN(delete_scheduled_message_stmt_, + db_memory_.get_statement("DELETE FROM scheduled_messages WHERE dialog_id = ?1 AND message_id = ?2")); + TRY_RESULT_ASSIGN( + delete_scheduled_server_message_stmt_, + db_memory_.get_statement("DELETE FROM scheduled_messages WHERE dialog_id = ?1 AND server_message_id = ?2")); // LOG(ERROR) << get_message_stmt_.explain().ok(); // LOG(ERROR) << get_messages_from_notification_id_stmt.explain().ok(); @@ -258,8 +272,6 @@ class MessagesDbImpl : public MessagesDbSyncInterface { // LOG(ERROR) << get_expiring_messages_stmt_.explain().ok(); // LOG(ERROR) << get_expiring_messages_helper_stmt_.explain().ok(); - // LOG(ERROR) << get_messages_asc_stmt_.explain().ok(); - // LOG(ERROR) << get_messages_desc_stmt_.explain().ok(); // LOG(FATAL) << "EXPLAINED"; return Status::OK(); @@ -350,21 +362,57 @@ class MessagesDbImpl : public MessagesDbSyncInterface { return Status::OK(); } - Status delete_message(FullMessageId full_message_id) override { + Status add_scheduled_message(FullMessageId full_message_id, BufferSlice data) override { + LOG(INFO) << "Add " << full_message_id << " to database"; auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); CHECK(dialog_id.is_valid()); - CHECK(message_id.is_valid()); + CHECK(message_id.is_valid_scheduled()); SCOPE_EXIT { - delete_message_stmt_.reset(); + add_scheduled_message_stmt_.reset(); }; - delete_message_stmt_.bind_int64(1, dialog_id.get()).ensure(); - delete_message_stmt_.bind_int64(2, message_id.get()).ensure(); - delete_message_stmt_.step().ensure(); + add_scheduled_message_stmt_.bind_int64(1, dialog_id.get()).ensure(); + add_scheduled_message_stmt_.bind_int64(2, message_id.get()).ensure(); + + if (message_id.is_scheduled_server()) { + add_scheduled_message_stmt_.bind_int32(3, message_id.get_scheduled_server_message_id().get()).ensure(); + } else { + add_scheduled_message_stmt_.bind_null(3).ensure(); + } + + add_scheduled_message_stmt_.bind_blob(4, data.as_slice()).ensure(); + + add_scheduled_message_stmt_.step().ensure(); + + return Status::OK(); + } + + Status delete_message(FullMessageId full_message_id) override { + LOG(INFO) << "Delete " << full_message_id << " from database"; + auto dialog_id = full_message_id.get_dialog_id(); + auto message_id = full_message_id.get_message_id(); + CHECK(dialog_id.is_valid()); + CHECK(message_id.is_valid() || message_id.is_valid_scheduled()); + bool is_scheduled = message_id.is_scheduled(); + bool is_scheduled_server = is_scheduled && message_id.is_scheduled_server(); + auto &stmt = is_scheduled + ? (is_scheduled_server ? delete_scheduled_server_message_stmt_ : delete_scheduled_message_stmt_) + : delete_message_stmt_; + SCOPE_EXIT { + stmt.reset(); + }; + stmt.bind_int64(1, dialog_id.get()).ensure(); + if (is_scheduled_server) { + stmt.bind_int32(2, message_id.get_scheduled_server_message_id().get()).ensure(); + } else { + stmt.bind_int64(2, message_id.get()).ensure(); + } + stmt.step().ensure(); return Status::OK(); } Status delete_all_dialog_messages(DialogId dialog_id, MessageId from_message_id) override { + LOG(INFO) << "Delete all messages in " << dialog_id << " up to " << from_message_id << " from database"; CHECK(dialog_id.is_valid()); CHECK(from_message_id.is_valid()); SCOPE_EXIT { @@ -380,6 +428,7 @@ class MessagesDbImpl : public MessagesDbSyncInterface { } Status delete_dialog_messages_from_user(DialogId dialog_id, UserId sender_user_id) override { + LOG(INFO) << "Delete all messages in " << dialog_id << " sent by " << sender_user_id << " from database"; CHECK(dialog_id.is_valid()); CHECK(sender_user_id.is_valid()); SCOPE_EXIT { @@ -395,18 +444,26 @@ class MessagesDbImpl : public MessagesDbSyncInterface { auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); CHECK(dialog_id.is_valid()); - CHECK(message_id.is_valid()); - + CHECK(message_id.is_valid() || message_id.is_valid_scheduled()); + bool is_scheduled = message_id.is_scheduled(); + bool is_scheduled_server = is_scheduled && message_id.is_scheduled_server(); + auto &stmt = is_scheduled ? (is_scheduled_server ? get_scheduled_server_message_stmt_ : get_scheduled_message_stmt_) + : get_message_stmt_; SCOPE_EXIT { - get_message_stmt_.reset(); + stmt.reset(); }; - get_message_stmt_.bind_int64(1, dialog_id.get()).ensure(); - get_message_stmt_.bind_int64(2, message_id.get()).ensure(); - get_message_stmt_.step().ensure(); - if (!get_message_stmt_.has_row()) { + + stmt.bind_int64(1, dialog_id.get()).ensure(); + if (is_scheduled_server) { + stmt.bind_int32(2, message_id.get_scheduled_server_message_id().get()).ensure(); + } else { + stmt.bind_int64(2, message_id.get()).ensure(); + } + stmt.step().ensure(); + if (!stmt.has_row()) { return Status::Error("Not found"); } - return BufferSlice(get_message_stmt_.view_blob(0)); + return BufferSlice(stmt.view_blob(0)); } Result> get_message_by_unique_message_id( @@ -444,8 +501,7 @@ class MessagesDbImpl : public MessagesDbSyncInterface { int64 left_message_id = first_message_id.get(); int64 right_message_id = last_message_id.get(); LOG_CHECK(left_message_id <= right_message_id) << first_message_id << " " << last_message_id; - TRY_RESULT(first_messages, - get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id.get(), left_message_id - 1, 1)); + TRY_RESULT(first_messages, get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id, left_message_id - 1, 1)); if (!first_messages.empty()) { MessageId real_first_message_id; int32 real_first_message_date; @@ -457,7 +513,7 @@ class MessagesDbImpl : public MessagesDbSyncInterface { MessageId prev_found_message_id; while (left_message_id <= right_message_id) { auto middle_message_id = left_message_id + ((right_message_id - left_message_id) >> 1); - TRY_RESULT(messages, get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id.get(), middle_message_id, 1)); + TRY_RESULT(messages, get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id, middle_message_id, 1)); MessageId message_id; int32 message_date = std::numeric_limits::max(); @@ -473,7 +529,7 @@ class MessagesDbImpl : public MessagesDbSyncInterface { if (prev_found_message_id == message_id) { // we may be very close to the result, let's check TRY_RESULT(left_messages, - get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id.get(), left_message_id - 1, 2)); + get_messages_inner(get_messages_stmt_.asc_stmt_, dialog_id, left_message_id - 1, 2)); CHECK(!left_messages.empty()); if (left_messages.size() == 1) { // only one message has left, result is found @@ -547,6 +603,10 @@ class MessagesDbImpl : public MessagesDbSyncInterface { return get_messages_impl(get_messages_stmt_, query.dialog_id, query.from_message_id, query.offset, query.limit); } + Result> get_scheduled_messages(DialogId dialog_id, int32 limit) override { + return get_messages_inner(get_scheduled_messages_stmt_, dialog_id, std::numeric_limits::max(), limit); + } + Result> get_messages_from_notification_id(DialogId dialog_id, NotificationId from_notification_id, int32 limit) override { auto &stmt = get_messages_from_notification_id_stmt_; @@ -617,58 +677,57 @@ class MessagesDbImpl : public MessagesDbSyncInterface { } Result get_messages_fts(MessagesDbFtsQuery query) override { -// SCOPE_EXIT { -// get_messages_fts_stmt_.reset(); -// }; + // SCOPE_EXIT { + // get_messages_fts_stmt_.reset(); + // }; -// LOG(INFO) << tag("query", query.query) << query.dialog_id << tag("index_mask", query.index_mask) -// << tag("from_search_id", query.from_search_id) << tag("limit", query.limit); -// string words = prepare_query(query.query); -// LOG(INFO) << tag("from", query.query) << tag("to", words); -// -// dialog_id kludge -// if (query.dialog_id.is_valid()) { -// words += PSTRING() << " \"\a" << query.dialog_id.get() << "\""; -// } -// -// index_mask kludge -// if (query.index_mask != 0) { -// int index_i = -1; -// for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { -// if (query.index_mask == (1 << i)) { -// index_i = i; -// break; -// } -// } -// if (index_i == -1) { -// return Status::Error("Union of index types is not supported"); -// } -// words += PSTRING() << " \"\a\a" << index_i << "\""; -// } -// -// auto &stmt = get_messages_fts_stmt_; -// stmt.bind_string(1, words).ensure(); -// if (query.from_search_id == 0) { -// query.from_search_id = std::numeric_limits::max(); -// } -// stmt.bind_int64(2, query.from_search_id).ensure(); -// stmt.bind_int32(3, query.limit).ensure(); -// MessagesDbFtsResult result; -// auto status = stmt.step(); -// if (status.is_error()) { -// LOG(ERROR) << status; -// return std::move(result); -// } -// while (stmt.has_row()) { -// auto dialog_id = stmt.view_int64(0); -// auto data_slice = stmt.view_blob(1); -// auto search_id = stmt.view_int64(2); -// result.next_search_id = search_id; -// result.messages.push_back(MessagesDbMessage{DialogId(dialog_id), BufferSlice(data_slice)}); -// stmt.step().ensure(); -// } - - MessagesDbFtsResult result; + // LOG(INFO) << tag("query", query.query) << query.dialog_id << tag("index_mask", query.index_mask) + // << tag("from_search_id", query.from_search_id) << tag("limit", query.limit); + // string words = prepare_query(query.query); + // LOG(INFO) << tag("from", query.query) << tag("to", words); + + // // dialog_id kludge + // if (query.dialog_id.is_valid()) { + // words += PSTRING() << " \"\a" << query.dialog_id.get() << "\""; + // } + + // // index_mask kludge + // if (query.index_mask != 0) { + // int index_i = -1; + // for (int i = 0; i < MESSAGES_DB_INDEX_COUNT; i++) { + // if (query.index_mask == (1 << i)) { + // index_i = i; + // break; + // } + // } + // if (index_i == -1) { + // return Status::Error("Union of index types is not supported"); + // } + // words += PSTRING() << " \"\a\a" << index_i << "\""; + // } + + // auto &stmt = get_messages_fts_stmt_; + // stmt.bind_string(1, words).ensure(); + // if (query.from_search_id == 0) { + // query.from_search_id = std::numeric_limits::max(); + // } + // stmt.bind_int64(2, query.from_search_id).ensure(); + // stmt.bind_int32(3, query.limit).ensure(); + // MessagesDbFtsResult result; + // auto status = stmt.step(); + // if (status.is_error()) { + // LOG(ERROR) << status; + // return std::move(result); + // } + // while (stmt.has_row()) { + // auto dialog_id = stmt.view_int64(0); + // auto data_slice = stmt.view_blob(1); + // auto search_id = stmt.view_int64(2); + // result.next_search_id = search_id; + // result.messages.push_back(MessagesDbMessage{DialogId(dialog_id), BufferSlice(data_slice)}); + // stmt.step().ensure(); + // } + MessagesDbFtsResult result; return std::move(result); } @@ -762,12 +821,19 @@ class MessagesDbImpl : public MessagesDbSyncInterface { SqliteStatement desc_stmt_; }; GetMessagesStmt get_messages_stmt_; + SqliteStatement get_scheduled_messages_stmt_; SqliteStatement get_messages_from_notification_id_stmt_; std::array get_messages_from_index_stmts_; std::array get_calls_stmts_; -// SqliteStatement get_messages_fts_stmt_; +// SqliteStatement get_messages_fts_stmt_; + + SqliteStatement add_scheduled_message_stmt_; + SqliteStatement get_scheduled_message_stmt_; + SqliteStatement get_scheduled_server_message_stmt_; + SqliteStatement delete_scheduled_message_stmt_; + SqliteStatement delete_scheduled_server_message_stmt_; Result> get_messages_impl(GetMessagesStmt &stmt, DialogId dialog_id, MessageId from_message_id, int32 offset, int32 limit) { @@ -798,16 +864,14 @@ class MessagesDbImpl : public MessagesDbSyncInterface { left_cnt++; } - TRY_RESULT(left_tmp, get_messages_inner(stmt.desc_stmt_, dialog_id.get(), left_message_id, left_cnt)); - left = std::move(left_tmp); + TRY_RESULT_ASSIGN(left, get_messages_inner(stmt.desc_stmt_, dialog_id, left_message_id, left_cnt)); if (right_cnt == 1 && !left.empty() && false /*get_message_id(left[0].as_slice()) == message_id*/) { right_cnt = 0; } } if (right_cnt != 0) { - TRY_RESULT(right_tmp, get_messages_inner(stmt.asc_stmt_, dialog_id.get(), right_message_id, right_cnt)); - right = std::move(right_tmp); + TRY_RESULT_ASSIGN(right, get_messages_inner(stmt.asc_stmt_, dialog_id, right_message_id, right_cnt)); std::reverse(right.begin(), right.end()); } if (left.empty()) { @@ -823,24 +887,24 @@ class MessagesDbImpl : public MessagesDbSyncInterface { return std::move(right); } - Result> get_messages_inner(SqliteStatement &stmt, int64 dialog_id, int64 from_message_id, + Result> get_messages_inner(SqliteStatement &stmt, DialogId dialog_id, int64 from_message_id, int32 limit) { SCOPE_EXIT { stmt.reset(); }; - stmt.bind_int64(1, dialog_id).ensure(); + stmt.bind_int64(1, dialog_id.get()).ensure(); stmt.bind_int64(2, from_message_id).ensure(); stmt.bind_int32(3, limit).ensure(); - LOG(INFO) << "Begin to load " << limit << " messages in " << DialogId(dialog_id) << " from " - << MessageId(from_message_id) << " from database"; + LOG(INFO) << "Begin to load " << limit << " messages in " << dialog_id << " from " << MessageId(from_message_id) + << " from database"; std::vector result; stmt.step().ensure(); while (stmt.has_row()) { auto data_slice = stmt.view_blob(0); result.emplace_back(data_slice); auto message_id = stmt.view_int64(1); - LOG(INFO) << "Loaded " << MessageId(message_id) << " in " << DialogId(dialog_id) << " from database"; + LOG(INFO) << "Loaded " << MessageId(message_id) << " in " << dialog_id << " from database"; stmt.step().ensure(); } return std::move(result); @@ -900,6 +964,9 @@ class MessagesDbAsync : public MessagesDbAsyncInterface { ttl_expires_at, index_mask, search_id, std::move(text), notification_id, std::move(data), std::move(promise)); } + void add_scheduled_message(FullMessageId full_message_id, BufferSlice data, Promise<> promise) override { + send_closure_later(impl_, &Impl::add_scheduled_message, full_message_id, std::move(data), std::move(promise)); + } void delete_message(FullMessageId full_message_id, Promise<> promise) override { send_closure_later(impl_, &Impl::delete_message, full_message_id, std::move(promise)); @@ -930,6 +997,9 @@ class MessagesDbAsync : public MessagesDbAsyncInterface { void get_messages(MessagesDbMessagesQuery query, Promise> promise) override { send_closure_later(impl_, &Impl::get_messages, std::move(query), std::move(promise)); } + void get_scheduled_messages(DialogId dialog_id, int32 limit, Promise> promise) override { + send_closure_later(impl_, &Impl::get_scheduled_messages, dialog_id, limit, std::move(promise)); + } void get_messages_from_notification_id(DialogId dialog_id, NotificationId from_notification_id, int32 limit, Promise> promise) override { send_closure_later(impl_, &Impl::get_messages_from_notification_id, dialog_id, from_notification_id, limit, @@ -970,6 +1040,11 @@ class MessagesDbAsync : public MessagesDbAsyncInterface { index_mask, search_id, std::move(text), notification_id, std::move(data))); }); } + void add_scheduled_message(FullMessageId full_message_id, BufferSlice data, Promise<> promise) { + add_write_query([this, full_message_id, promise = std::move(promise), data = std::move(data)](Unit) mutable { + this->on_write_result(std::move(promise), sync_db_->add_scheduled_message(full_message_id, std::move(data))); + }); + } void delete_message(FullMessageId full_message_id, Promise<> promise) { add_write_query([=, promise = std::move(promise)](Unit) mutable { @@ -1011,6 +1086,10 @@ class MessagesDbAsync : public MessagesDbAsyncInterface { add_read_query(); promise.set_result(sync_db_->get_messages(std::move(query))); } + void get_scheduled_messages(DialogId dialog_id, int32 limit, Promise> promise) { + add_read_query(); + promise.set_result(sync_db_->get_scheduled_messages(dialog_id, limit)); + } void get_messages_from_notification_id(DialogId dialog_id, NotificationId from_notification_id, int32 limit, Promise> promise) { add_read_query(); diff --git a/td/telegram/MessagesDb.h b/td/telegram/MessagesDb.h index c838c707..f352f66a 100644 --- a/td/telegram/MessagesDb.h +++ b/td/telegram/MessagesDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,8 +7,10 @@ #pragma once #include "td/telegram/DialogId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/MessageId.h" #include "td/telegram/NotificationId.h" +#include "td/telegram/ServerMessageId.h" #include "td/actor/PromiseFuture.h" @@ -89,6 +91,7 @@ class MessagesDbSyncInterface { virtual Status add_message(FullMessageId full_message_id, ServerMessageId unique_message_id, UserId sender_user_id, int64 random_id, int32 ttl_expires_at, int32 index_mask, int64 search_id, string text, NotificationId notification_id, BufferSlice data) = 0; + virtual Status add_scheduled_message(FullMessageId full_message_id, BufferSlice data) = 0; virtual Status delete_message(FullMessageId full_message_id) = 0; virtual Status delete_all_dialog_messages(DialogId dialog_id, MessageId from_message_id) = 0; @@ -102,6 +105,7 @@ class MessagesDbSyncInterface { MessageId last_message_id, int32 date) = 0; virtual Result> get_messages(MessagesDbMessagesQuery query) = 0; + virtual Result> get_scheduled_messages(DialogId dialog_id, int32 limit) = 0; virtual Result> get_messages_from_notification_id(DialogId dialog_id, NotificationId from_notification_id, int32 limit) = 0; @@ -135,6 +139,7 @@ class MessagesDbAsyncInterface { virtual void add_message(FullMessageId full_message_id, ServerMessageId unique_message_id, UserId sender_user_id, int64 random_id, int32 ttl_expires_at, int32 index_mask, int64 search_id, string text, NotificationId notification_id, BufferSlice data, Promise<> promise) = 0; + virtual void add_scheduled_message(FullMessageId full_message_id, BufferSlice data, Promise<> promise) = 0; virtual void delete_message(FullMessageId full_message_id, Promise<> promise) = 0; virtual void delete_all_dialog_messages(DialogId dialog_id, MessageId from_message_id, Promise<> promise) = 0; @@ -148,6 +153,7 @@ class MessagesDbAsyncInterface { int32 date, Promise promise) = 0; virtual void get_messages(MessagesDbMessagesQuery query, Promise> promise) = 0; + virtual void get_scheduled_messages(DialogId dialog_id, int32 limit, Promise> promise) = 0; virtual void get_messages_from_notification_id(DialogId dialog_id, NotificationId from_notification_id, int32 limit, Promise> promise) = 0; diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 0931eace..55c49a1d 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +11,7 @@ #include "td/telegram/ConfigShared.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogDb.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/DraftMessage.h" #include "td/telegram/DraftMessage.hpp" #include "td/telegram/FileReferenceManager.h" @@ -22,6 +23,7 @@ #include "td/telegram/HashtagHints.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/InputMessageText.h" +#include "td/telegram/Location.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/logevent/LogEventHelper.h" #include "td/telegram/MessageContent.h" @@ -116,6 +118,29 @@ 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()))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetAllDraftsQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + } + + void on_error(uint64 id, Status status) override { + LOG(ERROR) << "Receive error for GetAllDraftsQuery: " << status; + status.ignore(); + } +}; + class GetDialogQuery : public Td::ResultHandler { DialogId dialog_id_; @@ -138,7 +163,7 @@ class GetDialogQuery : public Td::ResultHandler { td->contacts_manager_->on_get_users(std::move(result->users_), "GetDialogQuery"); td->contacts_manager_->on_get_chats(std::move(result->chats_), "GetDialogQuery"); td->messages_manager_->on_get_dialogs( - std::move(result->dialogs_), -1, std::move(result->messages_), + FolderId(), std::move(result->dialogs_), -1, std::move(result->messages_), PromiseCreator::lambda([td = td, dialog_id = dialog_id_](Result<> result) { if (result.is_ok()) { td->messages_manager_->on_get_dialog_query_finished(dialog_id, Status::OK()); @@ -159,14 +184,17 @@ class GetDialogQuery : public Td::ResultHandler { }; class GetPinnedDialogsActor : public NetActorOnce { + FolderId folder_id_; Promise promise_; public: explicit GetPinnedDialogsActor(Promise &&promise) : promise_(std::move(promise)) { } - NetQueryRef send(uint64 sequence_id) { - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_getPinnedDialogs())); + 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 result = query.get_weak(); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); @@ -185,7 +213,7 @@ class GetPinnedDialogsActor : public NetActorOnce { td->contacts_manager_->on_get_users(std::move(result->users_), "GetPinnedDialogsActor"); td->contacts_manager_->on_get_chats(std::move(result->chats_), "GetPinnedDialogsActor"); std::reverse(result->dialogs_.begin(), result->dialogs_.end()); - td->messages_manager_->on_get_dialogs(std::move(result->dialogs_), -2, std::move(result->messages_), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(result->dialogs_), -2, std::move(result->messages_), std::move(promise_)); } @@ -242,7 +270,8 @@ class GetMessagesQuery : public Td::ResultHandler { auto info = td->messages_manager_->on_get_messages(result_ptr.move_as_ok(), "GetMessagesQuery"); LOG_IF(ERROR, info.is_channel_messages) << "Receive channel messages in GetMessagesQuery"; - td->messages_manager_->on_get_messages(std::move(info.messages), info.is_channel_messages, "GetMessagesQuery"); + td->messages_manager_->on_get_messages(std::move(info.messages), info.is_channel_messages, false, + "GetMessagesQuery"); promise_.set_value(Unit()); } @@ -280,7 +309,7 @@ class GetChannelMessagesQuery : public Td::ResultHandler { auto info = td->messages_manager_->on_get_messages(result_ptr.move_as_ok(), "GetChannelMessagesQuery"); LOG_IF(ERROR, !info.is_channel_messages) << "Receive ordinary messages in GetChannelMessagesQuery"; - td->messages_manager_->on_get_messages(std::move(info.messages), info.is_channel_messages, + td->messages_manager_->on_get_messages(std::move(info.messages), info.is_channel_messages, false, "GetChannelMessagesQuery"); promise_.set_value(Unit()); @@ -296,6 +325,46 @@ class GetChannelMessagesQuery : public Td::ResultHandler { } }; +class GetScheduledMessagesQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit GetScheduledMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, tl_object_ptr &&input_peer, vector &&message_ids) { + 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))))); + } + + 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 info = td->messages_manager_->on_get_messages(result_ptr.move_as_ok(), "GetScheduledMessagesQuery"); + LOG_IF(ERROR, info.is_channel_messages != (dialog_id_.get_type() == DialogType::Channel)) + << "Receive wrong messages constructor in GetScheduledMessagesQuery"; + td->messages_manager_->on_get_messages(std::move(info.messages), info.is_channel_messages, true, + "GetScheduledMessagesQuery"); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (status.message() == "MESSAGE_IDS_EMPTY") { + promise_.set_value(Unit()); + return; + } + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetScheduledMessagesQuery"); + promise_.set_error(std::move(status)); + } +}; + class UpdateDialogPinnedMessageQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -397,22 +466,26 @@ class ExportChannelMessageLinkQuery : public Td::ResultHandler { }; class GetDialogListActor : public NetActorOnce { + FolderId folder_id_; Promise promise_; public: explicit GetDialogListActor(Promise &&promise) : promise_(std::move(promise)) { } - void send(int32 offset_date, ServerMessageId offset_message_id, DialogId offset_dialog_id, int32 limit, - uint64 sequence_id) { + void send(FolderId folder_id, int32 offset_date, ServerMessageId offset_message_id, DialogId offset_dialog_id, + int32 limit, uint64 sequence_id) { + folder_id_ = folder_id; auto input_peer = td->messages_manager_->get_input_peer(offset_dialog_id, AccessRights::Read); if (input_peer == nullptr) { input_peer = make_tl_object(); } - int32 flags = telegram_api::messages_getDialogs::EXCLUDE_PINNED_MASK; - auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_getDialogs( - flags, false /*ignored*/, offset_date, offset_message_id.get(), std::move(input_peer), limit, 0))); + 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))); send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, std::move(query), actor_shared(this), sequence_id); } @@ -430,7 +503,7 @@ class GetDialogListActor : public NetActorOnce { auto dialogs = move_tl_object_as(ptr); td->contacts_manager_->on_get_users(std::move(dialogs->users_), "GetDialogListActor"); td->contacts_manager_->on_get_chats(std::move(dialogs->chats_), "GetDialogListActor"); - td->messages_manager_->on_get_dialogs(std::move(dialogs->dialogs_), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(dialogs->dialogs_), narrow_cast(dialogs->dialogs_.size()), std::move(dialogs->messages_), std::move(promise_)); break; @@ -439,7 +512,7 @@ class GetDialogListActor : public NetActorOnce { auto dialogs = move_tl_object_as(ptr); td->contacts_manager_->on_get_users(std::move(dialogs->users_), "GetDialogListActor"); td->contacts_manager_->on_get_chats(std::move(dialogs->chats_), "GetDialogListActor"); - td->messages_manager_->on_get_dialogs(std::move(dialogs->dialogs_), max(dialogs->count_, 0), + td->messages_manager_->on_get_dialogs(folder_id_, std::move(dialogs->dialogs_), max(dialogs->count_, 0), std::move(dialogs->messages_), std::move(promise_)); break; } @@ -517,8 +590,7 @@ class GetCommonDialogsQuery : public Td::ResultHandler { auto chats_ptr = result_ptr.move_as_ok(); LOG(INFO) << "Receive result for GetCommonDialogsQuery: " << to_string(chats_ptr); - int32 constructor_id = chats_ptr->get_id(); - switch (constructor_id) { + switch (chats_ptr->get_id()) { case telegram_api::messages_chats::ID: { auto chats = move_tl_object_as(chats_ptr); td->messages_manager_->on_get_common_dialogs(user_id_, offset_chat_id_, std::move(chats->chats_), @@ -582,17 +654,22 @@ class CreateChannelQuery : public Td::ResultHandler { explicit CreateChannelQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(const string &title, bool is_megagroup, const string &about, int64 random_id) { + void send(const string &title, bool is_megagroup, const string &about, const DialogLocation &location, + int64 random_id) { int32 flags = 0; if (is_megagroup) { flags |= telegram_api::channels_createChannel::MEGAGROUP_MASK; } else { flags |= telegram_api::channels_createChannel::BROADCAST_MASK; } + if (!location.empty()) { + flags |= telegram_api::channels_createChannel::GEO_POINT_MASK; + } random_id_ = random_id; - send_query(G()->net_query_creator().create(create_storer( - telegram_api::channels_createChannel(flags, false /*ignored*/, false /*ignored*/, title, about)))); + 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())))); } void on_result(uint64 id, BufferSlice packet) override { @@ -941,22 +1018,25 @@ class ToggleDialogPinQuery : public Td::ResultHandler { if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "ToggleDialogPinQuery")) { LOG(ERROR) << "Receive error for ToggleDialogPinQuery: " << status; } - td->messages_manager_->on_update_dialog_is_pinned(dialog_id_, !is_pinned_); + td->messages_manager_->set_dialog_is_pinned(dialog_id_, !is_pinned_); promise_.set_error(std::move(status)); } }; class ReorderPinnedDialogsQuery : public Td::ResultHandler { + FolderId folder_id_; Promise promise_; public: explicit ReorderPinnedDialogsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(const vector &dialog_ids) { + 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( - flags, true /*ignored*/, td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read))))); + flags, true /*ignored*/, folder_id.get(), + td->messages_manager_->get_input_dialog_peers(dialog_ids, AccessRights::Read))))); } void on_result(uint64 id, BufferSlice packet) override { @@ -978,7 +1058,7 @@ class ReorderPinnedDialogsQuery : public Td::ResultHandler { if (!G()->close_flag()) { LOG(ERROR) << "Receive error for ReorderPinnedDialogsQuery: " << status; } - td->messages_manager_->on_update_pinned_dialogs(); + td->messages_manager_->on_update_pinned_dialogs(folder_id_); promise_.set_error(std::move(status)); } }; @@ -1434,8 +1514,8 @@ class SearchMessagesGlobalQuery : public Td::ResultHandler { explicit SearchMessagesGlobalQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(const string &query, int32 offset_date, DialogId offset_dialog_id, MessageId offset_message_id, int32 limit, - int64 random_id) { + void send(FolderId folder_id, bool ignore_folder_id, const string &query, int32 offset_date, + DialogId offset_dialog_id, MessageId offset_message_id, int32 limit, int64 random_id) { query_ = query; offset_date_ = offset_date; offset_dialog_id_ = offset_dialog_id; @@ -1448,8 +1528,13 @@ class SearchMessagesGlobalQuery : public Td::ResultHandler { input_peer = make_tl_object(); } - send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_searchGlobal( - query, offset_date_, std::move(input_peer), offset_message_id.get_server_message_id().get(), limit)))); + int32 flags = 0; + if (!ignore_folder_id) { + flags |= telegram_api::messages_searchGlobal::FOLDER_ID_MASK; + } + send_query(G()->net_query_creator().create(create_storer( + telegram_api::messages_searchGlobal(flags, folder_id.get(), query, offset_date_, std::move(input_peer), + offset_message_id.get_server_message_id().get(), limit)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -1472,6 +1557,48 @@ class SearchMessagesGlobalQuery : public Td::ResultHandler { } }; +class GetAllScheduledMessagesQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + uint32 generation_; + + public: + explicit GetAllScheduledMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, int32 hash, uint32 generation) { + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + dialog_id_ = dialog_id; + generation_ = generation; + + send_query(G()->net_query_creator().create( + create_storer(telegram_api::messages_getScheduledHistory(std::move(input_peer), hash)))); + } + + 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()); + } + + if (result_ptr.ok()->get_id() == telegram_api::messages_messagesNotModified::ID) { + td->messages_manager_->on_get_scheduled_server_messages(dialog_id_, generation_, Auto(), true); + } else { + auto info = td->messages_manager_->on_get_messages(result_ptr.move_as_ok(), "GetAllScheduledMessagesQuery"); + td->messages_manager_->on_get_scheduled_server_messages(dialog_id_, generation_, std::move(info.messages), false); + } + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetAllScheduledMessagesQuery"); + promise_.set_error(std::move(status)); + } +}; + class GetRecentLocationsQuery : public Td::ResultHandler { Promise promise_; DialogId dialog_id_; @@ -1800,7 +1927,7 @@ class SendMessageActor : public NetActorOnce { DialogId dialog_id_; public: - void send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, + void send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, int32 schedule_date, tl_object_ptr &&reply_markup, vector> &&entities, const string &text, int64 random_id, NetQueryRef *send_query_ref, uint64 sequence_dispatcher_id) { @@ -1821,7 +1948,7 @@ class SendMessageActor : public NetActorOnce { auto query = G()->net_query_creator().create(create_storer(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)))); + std::move(entities), schedule_date))); if (G()->shared_config().get_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_id](Unit) { @@ -1932,8 +2059,8 @@ class SendInlineBotResultQuery : public Td::ResultHandler { DialogId dialog_id_; public: - NetQueryRef send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, int64 random_id, int64 query_id, - const string &result_id) { + NetQueryRef send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, int32 schedule_date, int64 random_id, + int64 query_id, const string &result_id) { random_id_ = random_id; dialog_id_ = dialog_id; @@ -1942,7 +2069,7 @@ class SendInlineBotResultQuery : public Td::ResultHandler { auto query = G()->net_query_creator().create(create_storer(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))); + 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; @@ -1978,8 +2105,9 @@ class SendMultiMediaActor : public NetActorOnce { DialogId dialog_id_; public: - void send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, vector &&file_ids, - vector> &&input_single_media, uint64 sequence_dispatcher_id) { + void send(int32 flags, DialogId dialog_id, MessageId reply_to_message_id, int32 schedule_date, + vector &&file_ids, vector> &&input_single_media, + uint64 sequence_dispatcher_id) { for (auto &single_media : input_single_media) { random_ids_.push_back(single_media->random_id_); CHECK(FileManager::extract_was_uploaded(single_media->media_) == false); @@ -1998,16 +2126,8 @@ class SendMultiMediaActor : public NetActorOnce { auto query = G()->net_query_creator().create(create_storer(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)))); - if (G()->shared_config().get_option_boolean("use_quick_ack")) { - query->quick_ack_promise_ = PromiseCreator::lambda( - [random_ids = random_ids_](Unit) { - for (auto random_id : random_ids) { - send_closure(G()->messages_manager(), &MessagesManager::on_send_message_get_quick_ack, random_id); - } - }, - PromiseCreator::Ignore()); - } + 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, std::move(query), actor_shared(this), sequence_dispatcher_id); @@ -2095,7 +2215,7 @@ class SendMediaActor : public NetActorOnce { public: void send(FileId file_id, FileId thumbnail_file_id, int32 flags, DialogId dialog_id, MessageId reply_to_message_id, - tl_object_ptr &&reply_markup, + int32 schedule_date, tl_object_ptr &&reply_markup, vector> &&entities, const string &text, tl_object_ptr &&input_media, int64 random_id, NetQueryRef *send_query_ref, uint64 sequence_dispatcher_id) { @@ -2120,10 +2240,10 @@ class SendMediaActor : public NetActorOnce { telegram_api::messages_sendMedia request(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), reply_to_message_id.get_server_message_id().get(), std::move(input_media), text, random_id, std::move(reply_markup), - std::move(entities)); + std::move(entities), schedule_date); LOG(INFO) << "Send media: " << to_string(request); auto query = G()->net_query_creator().create(create_storer(request)); - if (G()->shared_config().get_option_boolean("use_quick_ack")) { + if (G()->shared_config().get_option_boolean("use_quick_ack") && was_uploaded_) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_id](Unit) { send_closure(G()->messages_manager(), &MessagesManager::on_send_message_get_quick_ack, random_id); @@ -2273,6 +2393,55 @@ class UploadMediaQuery : public Td::ResultHandler { } }; +class SendScheduledMessageActor : public NetActorOnce { + Promise promise_; + DialogId dialog_id_; + + public: + explicit SendScheduledMessageActor(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, MessageId message_id, uint64 sequence_dispatcher_id) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit); + if (input_peer == nullptr) { + on_error(0, Status::Error(400, "Can't access the chat")); + stop(); + return; + } + + LOG(DEBUG) << "Send " << FullMessageId{dialog_id, message_id}; + + 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}))); + + query->debug("send to MessagesManager::MultiSequenceDispatcher"); + send_closure(td->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback, + std::move(query), actor_shared(this), sequence_dispatcher_id); + } + + void on_result(uint64 id, BufferSlice packet) override { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(id, result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for SendScheduledMessage: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + LOG(INFO) << "Receive error for SendScheduledMessage: " << status; + td->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendScheduledMessageActor"); + promise_.set_error(std::move(status)); + } +}; + class EditMessageActor : public NetActorOnce { Promise promise_; DialogId dialog_id_; @@ -2284,7 +2453,8 @@ class EditMessageActor : public NetActorOnce { void send(int32 flags, DialogId dialog_id, MessageId message_id, const string &text, vector> &&entities, tl_object_ptr &&input_media, - tl_object_ptr &&reply_markup, uint64 sequence_dispatcher_id) { + tl_object_ptr &&reply_markup, int32 schedule_date, + uint64 sequence_dispatcher_id) { dialog_id_ = dialog_id; if (false && input_media != nullptr) { @@ -2312,11 +2482,16 @@ class EditMessageActor : public NetActorOnce { if (input_media != nullptr) { flags |= telegram_api::messages_editMessage::MEDIA_MASK; } + if (schedule_date != 0) { + flags |= telegram_api::messages_editMessage::SCHEDULE_DATE_MASK; + } LOG(DEBUG) << "Edit message with flags " << flags; + 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( - flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), text, - std::move(input_media), std::move(reply_markup), std::move(entities)))); + flags, false /*ignored*/, std::move(input_peer), server_message_id, text, std::move(input_media), + 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, @@ -2595,7 +2770,7 @@ class ForwardMessagesActor : public NetActorOnce { } void send(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id, const vector &message_ids, - vector &&random_ids, uint64 sequence_dispatcher_id) { + vector &&random_ids, int32 schedule_date, uint64 sequence_dispatcher_id) { LOG(INFO) << "Forward " << format::as_array(message_ids) << " from " << from_dialog_id << " to " << to_dialog_id; random_ids_ = random_ids; @@ -2617,7 +2792,8 @@ class ForwardMessagesActor : public NetActorOnce { auto query = G()->net_query_creator().create(create_storer(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)))); + MessagesManager::get_server_message_ids(message_ids), std::move(random_ids), std::move(to_input_peer), + schedule_date))); if (G()->shared_config().get_option_boolean("use_quick_ack")) { query->quick_ack_promise_ = PromiseCreator::lambda( [random_ids = random_ids_](Unit) { @@ -2894,6 +3070,47 @@ class DeleteChannelMessagesQuery : public Td::ResultHandler { } }; +class DeleteScheduledMessagesQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit DeleteScheduledMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, vector &&message_ids) { + dialog_id_ = dialog_id; + LOG(INFO) << "Send deleteScheduledMessagesQuery to delete " << format::as_array(message_ids); + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + 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))))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for DeleteScheduledMessagesQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "DeleteScheduledMessagesQuery")) { + LOG(ERROR) << "Receive error for delete scheduled messages: " << status; + } + promise_.set_error(std::move(status)); + } +}; + class GetDialogNotifySettingsQuery : public Td::ResultHandler { DialogId dialog_id_; @@ -3164,13 +3381,9 @@ class ResetNotifySettingsQuery : public Td::ResultHandler { }; class GetPeerSettingsQuery : public Td::ResultHandler { - Promise promise_; DialogId dialog_id_; public: - explicit GetPeerSettingsQuery(Promise &&promise) : promise_(std::move(promise)) { - } - void send(DialogId dialog_id) { dialog_id_ = dialog_id; @@ -3188,14 +3401,11 @@ class GetPeerSettingsQuery : public Td::ResultHandler { } td->messages_manager_->on_get_peer_settings(dialog_id_, result_ptr.move_as_ok()); - - promise_.set_value(Unit()); } void on_error(uint64 id, Status status) override { LOG(INFO) << "Receive error for get peer settings: " << status; td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetPeerSettingsQuery"); - promise_.set_error(std::move(status)); } }; @@ -3211,28 +3421,33 @@ class UpdatePeerSettingsQuery : public Td::ResultHandler { dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); - CHECK(input_peer != nullptr); + if (input_peer == nullptr) { + return promise_.set_value(Unit()); + } if (is_spam_dialog) { send_query( G()->net_query_creator().create(create_storer(telegram_api::messages_reportSpam(std::move(input_peer))))); } else { - send_query( - G()->net_query_creator().create(create_storer(telegram_api::messages_hideReportSpam(std::move(input_peer))))); + send_query(G()->net_query_creator().create( + create_storer(telegram_api::messages_hidePeerSettingsBar(std::move(input_peer))))); } } void on_result(uint64 id, BufferSlice packet) override { static_assert(std::is_same::value, + telegram_api::messages_hidePeerSettingsBar::ReturnType>::value, ""); auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(id, result_ptr.move_as_error()); } - td->messages_manager_->on_get_peer_settings(dialog_id_, - make_tl_object(0, false /*ignored*/)); + td->messages_manager_->on_get_peer_settings( + dialog_id_, + make_tl_object(0, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, false /*ignored*/), + true); promise_.set_value(Unit()); } @@ -3240,6 +3455,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"); promise_.set_error(std::move(status)); } }; @@ -3269,8 +3485,11 @@ class ReportEncryptedSpamQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->messages_manager_->on_get_peer_settings(dialog_id_, - make_tl_object(0, false /*ignored*/)); + td->messages_manager_->on_get_peer_settings( + dialog_id_, + make_tl_object(0, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, false /*ignored*/), + true); promise_.set_value(Unit()); } @@ -3278,6 +3497,9 @@ 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( + DialogId(td->contacts_manager_->get_secret_chat_user_id(dialog_id_.get_secret_chat_id())), + "ReportEncryptedSpamQuery"); promise_.set_error(std::move(status)); } }; @@ -3326,6 +3548,52 @@ 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"); + promise_.set_error(std::move(status)); + } +}; + +class EditPeerFoldersQuery : public Td::ResultHandler { + Promise promise_; + DialogId dialog_id_; + + public: + explicit EditPeerFoldersQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(DialogId dialog_id, FolderId folder_id) { + dialog_id_ = dialog_id; + + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + + 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))))); + } + + 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 ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for EditPeerFoldersQuery: " << to_string(ptr); + td->updates_manager_->on_get_updates(std::move(ptr)); + promise_.set_value(Unit()); + } + + void on_error(uint64 id, Status status) override { + if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "EditPeerFoldersQuery")) { + LOG(INFO) << "Receive error for EditPeerFoldersQuery: " << status; + } + + // trying to repair folder ID for this dialog + td->messages_manager_->get_dialog_info_full(dialog_id_, Auto()); + promise_.set_error(std::move(status)); } }; @@ -3339,7 +3607,6 @@ class GetStatsUrlQuery : public Td::ResultHandler { } void send(DialogId dialog_id, const string ¶meters, bool is_dark) { - // TODO use parameters and is_dark dialog_id_ = dialog_id; auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); @@ -3367,6 +3634,120 @@ class GetStatsUrlQuery : public Td::ResultHandler { } }; +class RequestUrlAuthQuery : public Td::ResultHandler { + Promise> promise_; + string url_; + DialogId dialog_id_; + + public: + explicit RequestUrlAuthQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(string url, DialogId dialog_id, MessageId message_id, int32 button_id) { + url_ = std::move(url); + 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)))); + } + + 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(); + LOG(INFO) << "Receive " << to_string(result); + switch (result->get_id()) { + case telegram_api::urlAuthResultRequest::ID: { + auto request = telegram_api::move_object_as(result); + UserId bot_user_id = ContactsManager::get_user_id(request->bot_); + if (!bot_user_id.is_valid()) { + return on_error(id, Status::Error(500, "Receive invalid bot_user_id")); + } + td->contacts_manager_->on_get_user(std::move(request->bot_), "RequestUrlAuthQuery"); + bool request_write_access = + (request->flags_ & telegram_api::urlAuthResultRequest::REQUEST_WRITE_ACCESS_MASK) != 0; + promise_.set_value(td_api::make_object( + url_, request->domain_, td->contacts_manager_->get_user_id_object(bot_user_id, "RequestUrlAuthQuery"), + request_write_access)); + break; + } + case telegram_api::urlAuthResultAccepted::ID: { + auto accepted = telegram_api::move_object_as(result); + promise_.set_value(td_api::make_object(accepted->url_, true)); + break; + } + case telegram_api::urlAuthResultDefault::ID: + promise_.set_value(td_api::make_object(url_, false)); + break; + } + } + + void on_error(uint64 id, Status status) override { + if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "RequestUrlAuthQuery")) { + LOG(INFO) << "RequestUrlAuthQuery returned " << status; + } + promise_.set_value(td_api::make_object(url_, false)); + } +}; + +class AcceptUrlAuthQuery : public Td::ResultHandler { + Promise> promise_; + string url_; + DialogId dialog_id_; + + public: + explicit AcceptUrlAuthQuery(Promise> &&promise) : promise_(std::move(promise)) { + } + + void send(string url, DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access) { + url_ = std::move(url); + dialog_id_ = dialog_id; + auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); + CHECK(input_peer != nullptr); + int32 flags = 0; + 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)))); + } + + 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(); + LOG(INFO) << "Receive " << to_string(result); + switch (result->get_id()) { + case telegram_api::urlAuthResultRequest::ID: + LOG(ERROR) << "Receive unexpected " << to_string(result); + return on_error(id, Status::Error(500, "Receive unexpected urlAuthResultRequest")); + case telegram_api::urlAuthResultAccepted::ID: { + auto accepted = telegram_api::move_object_as(result); + promise_.set_value(td_api::make_object(accepted->url_)); + break; + } + case telegram_api::urlAuthResultDefault::ID: + promise_.set_value(td_api::make_object(url_)); + break; + } + } + + void on_error(uint64 id, Status status) override { + if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "AcceptUrlAuthQuery")) { + LOG(INFO) << "AcceptUrlAuthQuery returned " << status; + } + promise_.set_error(std::move(status)); + } +}; + class GetChannelDifferenceQuery : public Td::ResultHandler { DialogId dialog_id_; int32 pts_; @@ -3573,6 +3954,7 @@ void MessagesManager::Message::store(StorerT &storer) const { bool has_send_error_code = send_error_code != 0; bool has_real_forward_from = real_forward_from_dialog_id.is_valid() && real_forward_from_message_id.is_valid(); bool has_legacy_layer = legacy_layer != 0; + bool has_restriction_reasons = !restriction_reasons.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(is_channel_post); STORE_FLAG(is_outgoing); @@ -3616,6 +3998,10 @@ void MessagesManager::Message::store(StorerT &storer) const { STORE_FLAG(is_bot_start_message); STORE_FLAG(has_real_forward_from); STORE_FLAG(has_legacy_layer); + STORE_FLAG(hide_edit_date); + STORE_FLAG(has_restriction_reasons); + STORE_FLAG(is_from_scheduled); + STORE_FLAG(is_copy); END_STORE_FLAGS(); } @@ -3688,9 +4074,12 @@ void MessagesManager::Message::store(StorerT &storer) const { if (has_legacy_layer) { store(legacy_layer, storer); } + if (has_restriction_reasons) { + store(restriction_reasons, storer); + } store_message_content(content.get(), storer); if (has_reply_markup) { - store(*reply_markup, storer); + store(reply_markup, storer); } } @@ -3719,6 +4108,7 @@ void MessagesManager::Message::parse(ParserT &parser) { bool has_send_error_code = false; bool has_real_forward_from = false; bool has_legacy_layer = false; + bool has_restriction_reasons = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(is_channel_post); PARSE_FLAG(is_outgoing); @@ -3762,6 +4152,10 @@ void MessagesManager::Message::parse(ParserT &parser) { PARSE_FLAG(is_bot_start_message); PARSE_FLAG(has_real_forward_from); PARSE_FLAG(has_legacy_layer); + PARSE_FLAG(hide_edit_date); + PARSE_FLAG(has_restriction_reasons); + PARSE_FLAG(is_from_scheduled); + PARSE_FLAG(is_copy); END_PARSE_FLAGS(); } @@ -3775,6 +4169,7 @@ void MessagesManager::Message::parse(ParserT &parser) { parse(edit_date, parser); } if (has_send_date) { + CHECK(message_id.is_valid() || message_id.is_valid_scheduled()); CHECK(message_id.is_yet_unsent()); parse(send_date, parser); } else if (message_id.is_valid() && message_id.is_yet_unsent()) { @@ -3839,10 +4234,12 @@ void MessagesManager::Message::parse(ParserT &parser) { if (has_legacy_layer) { parse(legacy_layer, parser); } + if (has_restriction_reasons) { + parse(restriction_reasons, parser); + } parse_message_content(content, parser); if (has_reply_markup) { - reply_markup = make_unique(); - parse(*reply_markup, parser); + parse(reply_markup, parser); } is_content_secret |= is_secret_message_content(ttl, content->get_type()); // repair is_content_secret for old messages @@ -3900,7 +4297,9 @@ void MessagesManager::Dialog::store(StorerT &storer) const { bool has_pinned_message_id = pinned_message_id.is_valid(); bool has_flags2 = true; bool has_max_notification_message_id = - max_notification_message_id.is_valid() && max_notification_message_id.get() > last_new_message_id.get(); + max_notification_message_id.is_valid() && max_notification_message_id > last_new_message_id; + bool has_folder_id = folder_id != FolderId(); + bool has_pending_read_channel_inbox = pending_read_channel_inbox_pts != 0; BEGIN_STORE_FLAGS(); STORE_FLAG(has_draft_message); STORE_FLAG(has_last_database_message); @@ -3939,6 +4338,16 @@ void MessagesManager::Dialog::store(StorerT &storer) const { if (has_flags2) { BEGIN_STORE_FLAGS(); STORE_FLAG(has_max_notification_message_id); + STORE_FLAG(has_folder_id); + STORE_FLAG(is_folder_id_inited); + STORE_FLAG(has_pending_read_channel_inbox); + STORE_FLAG(know_action_bar); + STORE_FLAG(can_add_contact); + STORE_FLAG(can_block_user); + STORE_FLAG(can_share_phone_number); + STORE_FLAG(can_report_location); + STORE_FLAG(has_scheduled_server_messages); + STORE_FLAG(has_scheduled_database_messages); END_STORE_FLAGS(); } @@ -3952,7 +4361,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const { store(reply_markup_message_id, storer); store(notification_settings, storer); if (has_draft_message) { - store(*draft_message, storer); + store(draft_message, storer); } store(last_clear_history_date, storer); store(order, storer); @@ -4015,6 +4424,14 @@ void MessagesManager::Dialog::store(StorerT &storer) const { if (has_max_notification_message_id) { store(max_notification_message_id, storer); } + if (has_folder_id) { + store(folder_id, storer); + } + if (has_pending_read_channel_inbox) { + store(pending_read_channel_inbox_pts, storer); + store(pending_read_channel_inbox_max_message_id, storer); + store(pending_read_channel_inbox_server_unread_count, storer); + } } // do not forget to resolve dialog dependencies including dependencies of last_message @@ -4041,6 +4458,8 @@ void MessagesManager::Dialog::parse(ParserT &parser) { bool has_pinned_message_id; bool has_flags2; bool has_max_notification_message_id = false; + bool has_folder_id = false; + bool has_pending_read_channel_inbox = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_draft_message); PARSE_FLAG(has_last_database_message); @@ -4079,7 +4498,24 @@ void MessagesManager::Dialog::parse(ParserT &parser) { if (has_flags2) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_max_notification_message_id); + PARSE_FLAG(has_folder_id); + PARSE_FLAG(is_folder_id_inited); + PARSE_FLAG(has_pending_read_channel_inbox); + PARSE_FLAG(know_action_bar); + PARSE_FLAG(can_add_contact); + PARSE_FLAG(can_block_user); + PARSE_FLAG(can_share_phone_number); + PARSE_FLAG(can_report_location); + PARSE_FLAG(has_scheduled_server_messages); + PARSE_FLAG(has_scheduled_database_messages); END_PARSE_FLAGS(); + } else { + is_folder_id_inited = false; + know_action_bar = false; + can_add_contact = false; + can_block_user = false; + can_share_phone_number = false; + can_report_location = false; } parse(last_new_message_id, parser); @@ -4098,14 +4534,12 @@ void MessagesManager::Dialog::parse(ParserT &parser) { parse(reply_markup_message_id, parser); parse(notification_settings, parser); if (has_draft_message) { - draft_message = make_unique(); - parse(*draft_message, parser); + parse(draft_message, parser); } parse(last_clear_history_date, parser); parse(order, parser); if (has_last_database_message) { - messages = make_unique(); - parse(*messages, parser); + parse(messages, parser); } if (has_first_database_message_id) { parse(first_database_message_id, parser); @@ -4186,6 +4620,14 @@ void MessagesManager::Dialog::parse(ParserT &parser) { if (has_max_notification_message_id) { parse(max_notification_message_id, parser); } + if (has_folder_id) { + parse(folder_id, parser); + } + if (has_pending_read_channel_inbox) { + parse(pending_read_channel_inbox_pts, parser); + parse(pending_read_channel_inbox_max_message_id, parser); + parse(pending_read_channel_inbox_server_unread_count, parser); + } } template @@ -4266,6 +4708,9 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent pending_message_views_timeout_.set_callback(on_pending_message_views_timeout_callback); pending_message_views_timeout_.set_callback_data(static_cast(this)); + pending_message_live_location_view_timeout_.set_callback(on_pending_message_live_location_view_timeout_callback); + pending_message_live_location_view_timeout_.set_callback_data(static_cast(this)); + pending_draft_message_timeout_.set_callback(on_pending_draft_message_timeout_callback); pending_draft_message_timeout_.set_callback_data(static_cast(this)); @@ -4290,6 +4735,9 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent update_dialog_online_member_count_timeout_.set_callback(on_update_dialog_online_member_count_timeout_callback); update_dialog_online_member_count_timeout_.set_callback_data(static_cast(this)); + preload_dialog_list_timeout_.set_callback(on_preload_dialog_list_timeout_callback); + preload_dialog_list_timeout_.set_callback_data(static_cast(this)); + sequence_dispatcher_ = create_actor("multi sequence dispatcher"); } @@ -4315,6 +4763,17 @@ void MessagesManager::on_pending_message_views_timeout_callback(void *messages_m DialogId(dialog_id_int)); } +void MessagesManager::on_pending_message_live_location_view_timeout_callback(void *messages_manager_ptr, + int64 task_id) { + if (G()->close_flag()) { + return; + } + + auto messages_manager = static_cast(messages_manager_ptr); + send_closure_later(messages_manager->actor_id(messages_manager), + &MessagesManager::view_message_live_location_on_server, task_id); +} + void MessagesManager::on_pending_draft_message_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int) { if (G()->close_flag()) { return; @@ -4401,6 +4860,16 @@ void MessagesManager::on_update_dialog_online_member_count_timeout_callback(void &MessagesManager::on_update_dialog_online_member_count_timeout, DialogId(dialog_id_int)); } +void MessagesManager::on_preload_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int) { + if (G()->close_flag()) { + return; + } + + auto messages_manager = static_cast(messages_manager_ptr); + send_closure_later(messages_manager->actor_id(messages_manager), &MessagesManager::preload_dialog_list, + FolderId(narrow_cast(folder_id_int))); +} + BufferSlice MessagesManager::get_dialog_database_value(const Dialog *d) { // can't use log_event_store, because it tries to parse stored Dialog LogEventStorerCalcLength storer_calc_length; @@ -4432,8 +4901,8 @@ void MessagesManager::save_dialog_to_database(DialogId dialog_id) { add_group_key(d->message_notification_group); add_group_key(d->mention_notification_group); G()->td_db()->get_dialog_db_async()->add_dialog( - dialog_id, d->order, get_dialog_database_value(d), std::move(changed_group_keys), - PromiseCreator::lambda([dialog_id, can_reuse_notification_group](Result<> result) { + dialog_id, d->folder_id, d->is_folder_id_inited ? d->order : 0, get_dialog_database_value(d), + std::move(changed_group_keys), PromiseCreator::lambda([dialog_id, can_reuse_notification_group](Result<> result) { send_closure(G()->messages_manager(), &MessagesManager::on_save_dialog_to_database, dialog_id, can_reuse_notification_group, result.is_ok()); })); @@ -4524,7 +4993,7 @@ void MessagesManager::update_message_count_by_index(Dialog *d, int diff, int32 i int32 MessagesManager::get_message_index_mask(DialogId dialog_id, const Message *m) const { CHECK(m != nullptr); - if (m->message_id.is_yet_unsent() || m->is_failed_to_send) { + if (m->message_id.is_scheduled() || m->message_id.is_yet_unsent() || m->is_failed_to_send) { return 0; } bool is_secret = dialog_id.get_type() == DialogType::SecretChat; @@ -4559,6 +5028,11 @@ vector MessagesManager::get_server_message_ids(const vector &m return transform(message_ids, [](MessageId message_id) { return message_id.get_server_message_id().get(); }); } +vector MessagesManager::get_scheduled_server_message_ids(const vector &message_ids) { + return transform(message_ids, + [](MessageId message_id) { return message_id.get_scheduled_server_message_id().get(); }); +} + tl_object_ptr MessagesManager::get_input_message(MessageId message_id) { return make_tl_object(message_id.get_server_message_id().get()); } @@ -4603,7 +5077,7 @@ vector> MessagesManager::get_input_peers( return input_peers; } -tl_object_ptr MessagesManager::get_input_dialog_peer(DialogId dialog_id, +tl_object_ptr MessagesManager::get_input_dialog_peer(DialogId dialog_id, AccessRights access_rights) const { switch (dialog_id.get_type()) { case DialogType::User: @@ -4619,9 +5093,9 @@ tl_object_ptr MessagesManager::get_input_dialog_p } } -vector> MessagesManager::get_input_dialog_peers( +vector> MessagesManager::get_input_dialog_peers( const vector &dialog_ids, AccessRights access_rights) const { - vector> input_dialog_peers; + vector> input_dialog_peers; input_dialog_peers.reserve(dialog_ids.size()); for (auto &dialog_id : dialog_ids) { auto input_dialog_peer = get_input_dialog_peer(dialog_id, access_rights); @@ -4760,11 +5234,11 @@ void MessagesManager::skip_old_pending_update(tl_object_ptrget_id() == telegram_api::updateNewMessage::ID) { auto update_new_message = static_cast(update.get()); - auto full_message_id = get_full_message_id(update_new_message->message_); + auto full_message_id = get_full_message_id(update_new_message->message_, false); if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) { if (new_pts == old_pts) { // otherwise message can be already deleted // apply sent message anyway - on_get_message(std::move(update_new_message->message_), true, false, true, true, + on_get_message(std::move(update_new_message->message_), true, false, false, true, true, "updateNewMessage with an awaited message"); return; } else { @@ -4988,8 +5462,7 @@ void MessagesManager::on_update_service_notification(tl_object_ptrdialog_id; CHECK(dialog_id.get_type() == DialogType::User); auto new_message = make_unique(); - new_message->message_id = get_next_local_message_id(d); - new_message->random_y = get_random_y(new_message->message_id); + set_message_id(new_message, get_next_local_message_id(d)); new_message->sender_user_id = dialog_id.get_user_id(); new_message->date = date; new_message->ttl = ttl; @@ -5062,7 +5535,7 @@ void MessagesManager::on_update_edit_channel_message(tl_object_ptrmax_id_)), -1, "updateReadChannelInbox"); + FolderId folder_id; + if ((update->flags_ & telegram_api::updateReadChannelInbox::FOLDER_ID_MASK) != 0) { + folder_id = FolderId(update->folder_id_); + } + on_update_dialog_folder_id(DialogId(channel_id), folder_id); + on_read_channel_inbox(channel_id, MessageId(ServerMessageId(update->max_id_)), update->still_unread_count_, + update->pts_, "updateReadChannelInbox"); } void MessagesManager::on_update_read_channel_outbox(tl_object_ptr &&update) { @@ -5179,13 +5657,14 @@ void MessagesManager::on_update_message_views(FullMessageId full_message_id, int Message *m = get_message_force(d, message_id, "on_update_message_views"); if (m == nullptr) { LOG(INFO) << "Ignore updateMessageViews about unknown " << full_message_id; - if (message_id.get() > d->last_new_message_id.get() && dialog_id.get_type() == DialogType::Channel) { + if (!message_id.is_scheduled() && message_id > d->last_new_message_id && + dialog_id.get_type() == DialogType::Channel) { get_channel_difference(dialog_id, d->pts, true, "on_update_message_views"); } return; } - if (update_message_views(full_message_id.get_dialog_id(), m, views)) { + if (update_message_views(dialog_id, m, views)) { on_message_changed(d, m, true, "on_update_message_views"); } } @@ -5228,6 +5707,43 @@ bool MessagesManager::update_message_views(DialogId dialog_id, Message *m, int32 return false; } +void MessagesManager::on_update_live_location_viewed(FullMessageId full_message_id) { + LOG(DEBUG) << "Live location was viewed in " << full_message_id; + if (!are_active_live_location_messages_loaded_) { + get_active_live_location_messages(PromiseCreator::lambda([actor_id = actor_id(this), full_message_id](Unit result) { + send_closure(actor_id, &MessagesManager::on_update_live_location_viewed, full_message_id); + })); + return; + } + + auto active_live_location_message_ids = get_active_live_location_messages(Auto()); + if (!td::contains(active_live_location_message_ids, full_message_id)) { + LOG(DEBUG) << "Can't find " << full_message_id << " in " << active_live_location_message_ids; + return; + } + + send_update_message_live_location_viewed(full_message_id); +} + +void MessagesManager::on_update_some_live_location_viewed(Promise &&promise) { + LOG(DEBUG) << "Some live location was viewed"; + if (!are_active_live_location_messages_loaded_) { + get_active_live_location_messages( + PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](Unit result) mutable { + send_closure(actor_id, &MessagesManager::on_update_some_live_location_viewed, std::move(promise)); + })); + return; + } + + // update all live locations, because it is unknown, which exactly was viewed + auto active_live_location_message_ids = get_active_live_location_messages(Auto()); + for (auto full_message_id : active_live_location_message_ids) { + send_update_message_live_location_viewed(full_message_id); + } + + promise.set_value(Unit()); +} + void MessagesManager::on_update_message_content(FullMessageId full_message_id) { const Dialog *d = get_dialog(full_message_id.get_dialog_id()); CHECK(d != nullptr); @@ -5242,6 +5758,7 @@ void MessagesManager::on_update_message_content(FullMessageId full_message_id) { bool MessagesManager::update_message_contains_unread_mention(Dialog *d, Message *m, bool contains_unread_mention, const char *source) { LOG_CHECK(m != nullptr) << source; + CHECK(!m->message_id.is_scheduled()); if (!contains_unread_mention && m->contains_unread_mention) { remove_message_notification_id(d, m, true, true); // should be called before contains_unread_mention is updated @@ -5267,28 +5784,50 @@ bool MessagesManager::update_message_contains_unread_mention(Dialog *d, Message return false; } -void MessagesManager::on_read_channel_inbox(ChannelId channel_id, MessageId max_message_id, int32 server_unread_count) { - DialogId dialog_id(channel_id); - if (max_message_id.is_valid() || server_unread_count > 0) { - /* - // dropping unread count can make things worse, so don't drop it - if (server_unread_count > 0 && G()->parameters().use_message_db) { - const Dialog *d = get_dialog_force(dialog_id); - if (d == nullptr) { - return; - } +void MessagesManager::on_read_channel_inbox(ChannelId channel_id, MessageId max_message_id, int32 server_unread_count, + int32 pts, const char *source) { + if (td_->auth_manager_->is_bot()) { + return; + } - if (d->is_last_read_inbox_message_id_inited) { - server_unread_count = -1; - } + CHECK(!max_message_id.is_scheduled()); + if (!max_message_id.is_valid() && server_unread_count <= 0) { + return; + } + + DialogId dialog_id(channel_id); + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + LOG(INFO) << "Receive read inbox in unknown " << dialog_id << " from " << source; + return; + } + + /* + // dropping unread count can make things worse, so don't drop it + if (server_unread_count > 0 && G()->parameters().use_message_db && d->is_last_read_inbox_message_id_inited) { + server_unread_count = -1; + } + */ + + if (d->pts == pts) { + read_history_inbox(dialog_id, max_message_id, server_unread_count, source); + } else if (d->pts > pts) { + // outdated update, need to repair server_unread_count from the server + repair_channel_server_unread_count(d); + } else { + // update from the future, keep it until it can be applied + if (pts >= d->pending_read_channel_inbox_pts) { + d->pending_read_channel_inbox_pts = pts; + d->pending_read_channel_inbox_max_message_id = max_message_id; + d->pending_read_channel_inbox_server_unread_count = server_unread_count; + on_dialog_updated(dialog_id, "on_read_channel_inbox"); } - */ - read_history_inbox(dialog_id, max_message_id, server_unread_count, "on_read_channel_inbox"); } } void MessagesManager::on_read_channel_outbox(ChannelId channel_id, MessageId max_message_id) { DialogId dialog_id(channel_id); + CHECK(!max_message_id.is_scheduled()); if (max_message_id.is_valid()) { read_history_outbox(dialog_id, max_message_id); } @@ -5302,6 +5841,7 @@ void MessagesManager::on_update_channel_max_unavailable_message_id(ChannelId cha } DialogId dialog_id(channel_id); + CHECK(!max_unavailable_message_id.is_scheduled()); if (!max_unavailable_message_id.is_valid() && max_unavailable_message_id != MessageId()) { LOG(ERROR) << "Receive wrong max_unavailable_message_id: " << max_unavailable_message_id; max_unavailable_message_id = MessageId(); @@ -5336,6 +5876,43 @@ void MessagesManager::on_update_dialog_online_member_count(DialogId dialog_id, i "on_update_channel_online_member_count"); } +void MessagesManager::on_update_delete_scheduled_messages(DialogId dialog_id, + vector &&server_message_ids) { + if (td_->auth_manager_->is_bot()) { + // just in case + return; + } + + if (!dialog_id.is_valid()) { + LOG(ERROR) << "Receive deleted scheduled messages in invalid " << dialog_id; + return; + } + + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + LOG(INFO) << "Skip updateDeleteScheduledMessages in unknown " << dialog_id; + return; + } + + vector deleted_message_ids; + for (auto server_message_id : server_message_ids) { + if (!server_message_id.is_valid()) { + LOG(ERROR) << "Incoming update tries to delete scheduled message " << server_message_id.get(); + continue; + } + + auto message = do_delete_scheduled_message(d, MessageId(server_message_id, std::numeric_limits::max()), true, + "on_update_delete_scheduled_messages"); + if (message != nullptr) { + deleted_message_ids.push_back(message->message_id.get()); + } + } + + send_update_delete_messages(dialog_id, std::move(deleted_message_ids), true, false); + + send_update_chat_has_scheduled_messages(d); +} + void MessagesManager::on_update_include_sponsored_dialog_to_unread_count() { if (td_->auth_manager_->is_bot()) { // just in case @@ -5362,14 +5939,17 @@ void MessagesManager::on_update_include_sponsored_dialog_to_unread_count() { return; } + auto folder_id = FolderId::main(); + auto &list = get_dialog_list(folder_id); const Dialog *d = get_dialog(sponsored_dialog_id_); CHECK(d != nullptr); auto unread_count = d->server_unread_count + d->local_unread_count; - if (unread_count != 0 && is_message_unread_count_inited_) { - send_update_unread_message_count(d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); + if (unread_count != 0 && list.is_message_unread_count_inited_) { + send_update_unread_message_count(folder_id, d->dialog_id, true, + "on_update_include_sponsored_dialog_to_unread_count"); } - if ((unread_count != 0 || d->is_marked_as_unread) && is_dialog_unread_count_inited_) { - send_update_unread_chat_count(d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); + if ((unread_count != 0 || d->is_marked_as_unread) && list.is_dialog_unread_count_inited_) { + send_update_unread_chat_count(folder_id, d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count"); } } @@ -5537,7 +6117,7 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, UserId user_id, void MessagesManager::cancel_user_dialog_action(DialogId dialog_id, const Message *m) { CHECK(m != nullptr); if (m->forward_info != nullptr || m->had_forward_info || m->via_bot_user_id.is_valid() || m->hide_via_bot || - m->is_channel_post) { + m->is_channel_post || m->message_id.is_scheduled()) { return; } @@ -5598,11 +6178,11 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p if (update->get_id() == telegram_api::updateNewChannelMessage::ID) { auto update_new_channel_message = static_cast(update.get()); - auto message_id = get_message_id(update_new_channel_message->message_); + auto message_id = get_message_id(update_new_channel_message->message_, false); FullMessageId full_message_id(dialog_id, message_id); if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) { // apply sent channel message - on_get_message(std::move(update_new_channel_message->message_), true, true, true, true, + on_get_message(std::move(update_new_channel_message->message_), true, true, false, true, true, "updateNewChannelMessage with an awaited message"); return; } @@ -5680,8 +6260,8 @@ void MessagesManager::process_update(tl_object_ptr &&updat break; case telegram_api::updateNewMessage::ID: LOG(INFO) << "Process updateNewMessage"; - on_get_message(std::move(move_tl_object_as(update)->message_), true, false, true, - true, "updateNewMessage"); + on_get_message(std::move(move_tl_object_as(update)->message_), true, false, false, + true, true, "updateNewMessage"); break; case updateSentMessage::ID: { auto send_message_success_update = move_tl_object_as(update); @@ -5701,7 +6281,7 @@ void MessagesManager::process_update(tl_object_ptr &&updat case telegram_api::updateEditMessage::ID: { auto full_message_id = on_get_message(std::move(move_tl_object_as(update)->message_), false, false, - false, false, "updateEditMessage"); + false, false, false, "updateEditMessage"); LOG(INFO) << "Process updateEditMessage"; on_message_edited(full_message_id); break; @@ -5719,8 +6299,14 @@ void MessagesManager::process_update(tl_object_ptr &&updat case telegram_api::updateReadHistoryInbox::ID: { auto read_update = move_tl_object_as(update); LOG(INFO) << "Process updateReadHistoryInbox"; - read_history_inbox(DialogId(read_update->peer_), MessageId(ServerMessageId(read_update->max_id_)), -1, - "updateReadHistoryInbox"); + DialogId dialog_id(read_update->peer_); + FolderId folder_id; + if ((read_update->flags_ & telegram_api::updateReadHistoryInbox::FOLDER_ID_MASK) != 0) { + folder_id = FolderId(read_update->folder_id_); + } + on_update_dialog_folder_id(dialog_id, folder_id); + read_history_inbox(dialog_id, MessageId(ServerMessageId(read_update->max_id_)), + -1 /*read_update->still_unread_count*/, "updateReadHistoryInbox"); break; } case telegram_api::updateReadHistoryOutbox::ID: { @@ -5750,7 +6336,7 @@ void MessagesManager::process_channel_update(tl_object_ptr case telegram_api::updateNewChannelMessage::ID: LOG(INFO) << "Process updateNewChannelMessage"; on_get_message(std::move(move_tl_object_as(update)->message_), true, true, - true, true, "updateNewChannelMessage"); + false, true, true, "updateNewChannelMessage"); break; case telegram_api::updateDeleteChannelMessages::ID: { auto delete_channel_messages_update = move_tl_object_as(update); @@ -5774,7 +6360,7 @@ void MessagesManager::process_channel_update(tl_object_ptr LOG(INFO) << "Process updateEditChannelMessage"; auto full_message_id = on_get_message(std::move(move_tl_object_as(update)->message_), false, - true, false, false, "updateEditChannelMessage"); + true, false, false, false, "updateEditChannelMessage"); on_message_edited(full_message_id); break; } @@ -5794,11 +6380,8 @@ void MessagesManager::on_message_edited(FullMessageId full_message_id) { CHECK(m != nullptr); if (td_->auth_manager_->is_bot()) { send_update_message_edited(dialog_id, m); - } else { - if (m->forward_info == nullptr && !m->had_forward_info && (m->is_outgoing || dialog_id == get_my_dialog_id())) { - update_used_hashtags(dialog_id, m); - } } + update_used_hashtags(dialog_id, m); } void MessagesManager::process_pending_updates() { @@ -5970,8 +6553,9 @@ void MessagesManager::update_dialog_unmute_timeout(Dialog *d, bool old_use_defau dialog_unmute_timeout_.cancel_timeout(d->dialog_id.get()); } + auto &list = get_dialog_list(d->folder_id); if (old_mute_until != -1 && need_unread_counter(d->order) && - (is_message_unread_count_inited_ || is_dialog_unread_count_inited_)) { + (list.is_message_unread_count_inited_ || list.is_dialog_unread_count_inited_)) { auto unread_count = d->server_unread_count + d->local_unread_count; if (unread_count != 0 || d->is_marked_as_unread) { if (old_use_default || new_use_default) { @@ -5984,18 +6568,18 @@ void MessagesManager::update_dialog_unmute_timeout(Dialog *d, bool old_use_defau } } if ((old_mute_until != 0) != (new_mute_until != 0)) { - if (unread_count != 0 && is_message_unread_count_inited_) { + if (unread_count != 0 && list.is_message_unread_count_inited_) { int32 delta = old_mute_until != 0 ? -unread_count : unread_count; - unread_message_muted_count_ += delta; - send_update_unread_message_count(d->dialog_id, true, "update_dialog_unmute_timeout"); + list.unread_message_muted_count_ += delta; + send_update_unread_message_count(d->folder_id, d->dialog_id, true, "update_dialog_unmute_timeout"); } - if (is_dialog_unread_count_inited_) { + if (list.is_dialog_unread_count_inited_) { int32 delta = old_mute_until != 0 ? -1 : 1; - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } - send_update_unread_chat_count(d->dialog_id, true, "update_dialog_unmute_timeout"); + send_update_unread_chat_count(d->folder_id, d->dialog_id, true, "update_dialog_unmute_timeout"); } } } @@ -6016,44 +6600,50 @@ void MessagesManager::update_scope_unmute_timeout(NotificationSettingsScope scop dialog_unmute_timeout_.cancel_timeout(static_cast(scope) + 1); } - if (old_mute_until != -1 && (is_message_unread_count_inited_ || is_dialog_unread_count_inited_)) { + if (old_mute_until != -1 && !td_->auth_manager_->is_bot() && G()->parameters().use_message_db) { auto was_muted = old_mute_until != 0; auto is_muted = new_mute_until != 0; if (was_muted != is_muted) { - int32 delta = 0; - int32 total_count = 0; - int32 marked_count = 0; + std::unordered_map delta; + std::unordered_map total_count; + std::unordered_map marked_count; + std::unordered_set folder_ids; for (auto &dialog : dialogs_) { Dialog *d = dialog.second.get(); if (need_unread_counter(d->order) && d->notification_settings.use_default_mute_until && get_dialog_notification_setting_scope(d->dialog_id) == scope) { int32 unread_count = d->server_unread_count + d->local_unread_count; if (unread_count != 0) { - delta += unread_count; - total_count++; + delta[d->folder_id] += unread_count; + total_count[d->folder_id]++; + folder_ids.insert(d->folder_id); } else if (d->is_marked_as_unread) { - total_count++; - marked_count++; + total_count[d->folder_id]++; + marked_count[d->folder_id]++; + folder_ids.insert(d->folder_id); } } } - if (delta != 0 && is_message_unread_count_inited_) { - if (was_muted) { - unread_message_muted_count_ -= delta; - } else { - unread_message_muted_count_ += delta; + for (auto folder_id : folder_ids) { + auto &list = get_dialog_list(folder_id); + if (delta[folder_id] != 0 && list.is_message_unread_count_inited_) { + if (was_muted) { + list.unread_message_muted_count_ -= delta[folder_id]; + } else { + list.unread_message_muted_count_ += delta[folder_id]; + } + send_update_unread_message_count(folder_id, DialogId(), true, "update_scope_unmute_timeout"); } - send_update_unread_message_count(DialogId(), true, "update_scope_unmute_timeout"); - } - if (total_count != 0 && is_dialog_unread_count_inited_) { - if (was_muted) { - unread_dialog_muted_count_ -= total_count; - unread_dialog_muted_marked_count_ -= marked_count; - } else { - unread_dialog_muted_count_ += total_count; - unread_dialog_muted_marked_count_ += marked_count; + if (total_count[folder_id] != 0 && list.is_dialog_unread_count_inited_) { + if (was_muted) { + list.unread_dialog_muted_count_ -= total_count[folder_id]; + list.unread_dialog_muted_marked_count_ -= marked_count[folder_id]; + } else { + list.unread_dialog_muted_count_ += total_count[folder_id]; + list.unread_dialog_muted_marked_count_ += marked_count[folder_id]; + } + send_update_unread_chat_count(folder_id, DialogId(), true, "update_scope_unmute_timeout"); } - send_update_unread_chat_count(DialogId(), true, "update_scope_unmute_timeout"); } } } @@ -6172,41 +6762,57 @@ bool MessagesManager::update_dialog_silent_send_message(Dialog *d, bool silent_s return true; } -bool MessagesManager::get_dialog_report_spam_state(DialogId dialog_id, Promise &&promise) { - Dialog *d = get_dialog_force(dialog_id); - if (d == nullptr) { - promise.set_error(Status::Error(3, "Chat not found")); - return false; - } - - if (!have_input_peer(dialog_id, AccessRights::Read)) { - promise.set_error(Status::Error(3, "Can't access the chat")); - return false; - } - - if (d->know_can_report_spam) { - promise.set_value(Unit()); - return d->can_report_spam; +void MessagesManager::repair_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; switch (dialog_id.get_type()) { case DialogType::User: + td_->contacts_manager_->reload_user_full(dialog_id.get_user_id()); + return; case DialogType::Chat: case DialogType::Channel: - td_->create_handler(std::move(promise))->send(dialog_id); - return false; + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return; + } + + return td_->create_handler()->send(dialog_id); case DialogType::SecretChat: - promise.set_value(Unit()); - return false; case DialogType::None: default: UNREACHABLE(); - return false; } } -void MessagesManager::change_dialog_report_spam_state(DialogId dialog_id, bool is_spam_dialog, - Promise &&promise) { +void MessagesManager::hide_dialog_action_bar(DialogId dialog_id) { + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return; + } + hide_dialog_action_bar(d); +} + +void MessagesManager::hide_dialog_action_bar(Dialog *d) { + CHECK(d->dialog_id.get_type() != DialogType::SecretChat); + if (!d->know_can_report_spam) { + return; + } + if (!d->can_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number && + !d->can_report_location) { + return; + } + + d->can_report_spam = false; + d->can_add_contact = false; + d->can_block_user = false; + d->can_share_phone_number = false; + d->can_report_location = false; + send_update_chat_action_bar(d); +} + +void MessagesManager::remove_dialog_action_bar(DialogId dialog_id, Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return promise.set_error(Status::Error(3, "Chat not found")); @@ -6216,14 +6822,29 @@ void MessagesManager::change_dialog_report_spam_state(DialogId dialog_id, bool i return promise.set_error(Status::Error(3, "Can't access the chat")); } - if (!d->know_can_report_spam || !d->can_report_spam) { - return promise.set_error(Status::Error(3, "Can't update chat report spam state")); + if (dialog_id.get_type() == DialogType::SecretChat) { + dialog_id = DialogId(td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())); + d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(3, "Chat with the user not found")); + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(3, "Can't access the chat")); + } } - d->can_report_spam = false; - on_dialog_updated(dialog_id, "change_dialog_report_spam_state"); + if (!d->know_can_report_spam) { + return promise.set_error(Status::Error(3, "Can't update chat action bar")); + } - change_dialog_report_spam_state_on_server(dialog_id, is_spam_dialog, 0, std::move(promise)); + if (!d->can_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number && + !d->can_report_location) { + return promise.set_value(Unit()); + } + + hide_dialog_action_bar(d); + + change_dialog_report_spam_state_on_server(dialog_id, false, 0, std::move(promise)); } class MessagesManager::ChangeDialogReportSpamStateOnServerLogEvent { @@ -6269,8 +6890,11 @@ void MessagesManager::change_dialog_report_spam_state_on_server(DialogId dialog_ if (is_spam_dialog) { return td_->create_handler(std::move(promise))->send(dialog_id); } else { - promise.set_value(Unit()); - return; + auto user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (!user_id.is_valid()) { + return promise.set_error(Status::Error(400, "Peer user not found")); + } + return td_->create_handler(std::move(promise))->send(DialogId(user_id), false); } case DialogType::None: default: @@ -6280,6 +6904,7 @@ void MessagesManager::change_dialog_report_spam_state_on_server(DialogId dialog_ } bool MessagesManager::can_report_dialog(DialogId dialog_id) const { + // doesn't include possibility of report from action bar switch (dialog_id.get_type()) { case DialogType::User: return td_->contacts_manager_->can_report_user(dialog_id.get_user_id()); @@ -6311,12 +6936,43 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptrcan_report_spam; + if (reason->get_id() == td_api::chatReportReasonSpam::ID && message_ids.empty()) { + // report from action bar + if (dialog_id.get_type() == DialogType::SecretChat) { + auto user_dialog_id = DialogId(td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())); + user_d = get_dialog_force(user_dialog_id); + if (user_d == nullptr) { + return promise.set_error(Status::Error(3, "Chat with the user not found")); + } + + is_dialog_spam_report = user_d->know_can_report_spam; + can_report_spam = user_d->can_report_spam; + } else { + is_dialog_spam_report = d->know_can_report_spam; + } + } + + if (is_dialog_spam_report && can_report_spam) { + hide_dialog_action_bar(user_d); + return change_dialog_report_spam_state_on_server(dialog_id, true, 0, std::move(promise)); + } + if (!can_report_dialog(dialog_id)) { + if (is_dialog_spam_report) { + return promise.set_value(Unit()); + } + return promise.set_error(Status::Error(3, "Chat can't be reported")); } vector server_message_ids; for (auto message_id : message_ids) { + if (message_id.is_scheduled()) { + return promise.set_error(Status::Error(3, "Can't report scheduled messages")); + } if (message_id.is_valid() && message_id.is_server()) { server_message_ids.push_back(message_id); } @@ -6339,6 +6995,12 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr(); break; + case td_api::chatReportReasonUnrelatedLocation::ID: + report_reason = make_tl_object(); + if (dialog_id.get_type() == DialogType::Channel) { + hide_dialog_action_bar(d); + } + break; case td_api::chatReportReasonCustom::ID: { auto other_reason = static_cast(reason.get()); auto text = other_reason->text_; @@ -6359,16 +7021,125 @@ void MessagesManager::report_dialog(DialogId dialog_id, const tl_object_ptr &&peer_settings) { + tl_object_ptr &&peer_settings, + bool ignore_privacy_exception) { CHECK(peer_settings != nullptr); + if (dialog_id.get_type() == DialogType::User && !ignore_privacy_exception) { + auto need_phone_number_privacy_exception = + (peer_settings->flags_ & telegram_api::peerSettings::NEED_CONTACTS_EXCEPTION_MASK) != 0; + td_->contacts_manager_->on_update_user_need_phone_number_privacy_exception(dialog_id.get_user_id(), + need_phone_number_privacy_exception); + } + Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { return; } + auto can_report_spam = (peer_settings->flags_ & telegram_api::peerSettings::REPORT_SPAM_MASK) != 0; + auto can_add_contact = (peer_settings->flags_ & telegram_api::peerSettings::ADD_CONTACT_MASK) != 0; + auto can_block_user = (peer_settings->flags_ & telegram_api::peerSettings::BLOCK_CONTACT_MASK) != 0; + auto can_share_phone_number = (peer_settings->flags_ & telegram_api::peerSettings::SHARE_CONTACT_MASK) != 0; + auto can_report_location = (peer_settings->flags_ & telegram_api::peerSettings::REPORT_GEO_MASK) != 0; + if (d->can_report_spam == can_report_spam && d->can_add_contact == can_add_contact && + d->can_block_user == can_block_user && d->can_share_phone_number == can_share_phone_number && + d->can_report_location == can_report_location) { + if (!d->know_action_bar || !d->know_can_report_spam) { + d->know_can_report_spam = true; + d->know_action_bar = true; + on_dialog_updated(d->dialog_id, "on_get_peer_settings"); + } + return; + } + d->know_can_report_spam = true; - d->can_report_spam = (peer_settings->flags_ & telegram_api::peerSettings::REPORT_SPAM_MASK) != 0; - on_dialog_updated(dialog_id, "can_report_spam"); + d->know_action_bar = true; + d->can_report_spam = can_report_spam; + d->can_add_contact = can_add_contact; + d->can_block_user = can_block_user; + d->can_share_phone_number = can_share_phone_number; + d->can_report_location = can_report_location; + + fix_dialog_action_bar(d); + + send_update_chat_action_bar(d); +} + +void MessagesManager::fix_dialog_action_bar(Dialog *d) { + CHECK(d != nullptr); + if (!d->know_action_bar) { + return; + } + + if (d->can_report_location) { + if (d->dialog_id.get_type() != DialogType::Channel) { + LOG(ERROR) << "Receive can_report_location in " << d->dialog_id; + d->can_report_location = false; + } else if (d->can_report_spam || d->can_add_contact || d->can_block_user || d->can_share_phone_number) { + LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" << d->can_block_user + << "/" << d->can_share_phone_number << "/" << d->can_report_location; + d->can_report_spam = false; + d->can_add_contact = false; + d->can_block_user = false; + d->can_share_phone_number = false; + } + } + if (d->dialog_id.get_type() == DialogType::User) { + auto user_id = d->dialog_id.get_user_id(); + bool is_me = user_id == td_->contacts_manager_->get_my_id(); + bool is_contact = td_->contacts_manager_->is_user_contact(user_id); + bool is_blocked = td_->contacts_manager_->is_user_blocked(user_id); + bool is_deleted = td_->contacts_manager_->is_user_deleted(user_id); + if (is_me || is_blocked) { + d->can_report_spam = false; + } + if (is_me || is_blocked || is_deleted) { + d->can_share_phone_number = false; + } + if (is_me || is_blocked || is_deleted || is_contact) { + d->can_block_user = false; + d->can_add_contact = false; + } + } + if (d->can_share_phone_number) { + CHECK(!d->can_report_location); + if (d->dialog_id.get_type() != DialogType::User) { + LOG(ERROR) << "Receive can_share_phone_number in " << d->dialog_id; + d->can_share_phone_number = false; + } else if (d->can_report_spam || d->can_add_contact || d->can_block_user) { + LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" << d->can_block_user + << "/" << d->can_share_phone_number; + d->can_report_spam = false; + d->can_add_contact = false; + d->can_block_user = false; + } + } + if (d->can_block_user) { + CHECK(!d->can_report_location); + CHECK(!d->can_share_phone_number); + if (d->dialog_id.get_type() != DialogType::User) { + LOG(ERROR) << "Receive can_block_user in " << d->dialog_id; + d->can_block_user = false; + } else if (!d->can_report_spam || !d->can_add_contact) { + LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" + << d->can_block_user; + d->can_report_spam = true; + d->can_add_contact = true; + } + } + if (d->can_add_contact) { + CHECK(!d->can_report_location); + CHECK(!d->can_share_phone_number); + if (d->dialog_id.get_type() != DialogType::User) { + LOG(ERROR) << "Receive can_add_contact in " << d->dialog_id; + d->can_add_contact = false; + } else if (d->can_report_spam != d->can_block_user) { + LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" + << d->can_block_user; + d->can_report_spam = false; + d->can_block_user = false; + } + } } void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string ¶meters, bool is_dark, @@ -6388,6 +7159,67 @@ void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string td_->create_handler(std::move(promise))->send(dialog_id, parameters, is_dark); } +Result MessagesManager::get_login_button_url(DialogId dialog_id, MessageId message_id, int32 button_id) { + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return Status::Error(3, "Chat not found"); + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return Status::Error(3, "Can't access the chat"); + } + + auto m = get_message_force(d, message_id, "get_login_button_url"); + if (m == nullptr) { + return Status::Error(5, "Message not found"); + } + if (m->reply_markup == nullptr || m->reply_markup->type != ReplyMarkup::Type::InlineKeyboard) { + return Status::Error(5, "Message has no inline keyboard"); + } + if (message_id.is_scheduled()) { + return Status::Error(5, "Can't use login buttons from scheduled messages"); + } + if (!message_id.is_server()) { + // it shouldn't have UrlAuth buttons anyway + return Status::Error(5, "Message is not server"); + } + if (dialog_id.get_type() == DialogType::SecretChat) { + // secret chat messages can't have reply markup, so this shouldn't happen now + return Status::Error(5, "Message is in a secret chat"); + } + + for (auto &row : m->reply_markup->inline_keyboard) { + for (auto &button : row) { + if (button.type == InlineKeyboardButton::Type::UrlAuth && button.id == button_id) { + return button.data; + } + } + } + + return Status::Error(5, "Button not found"); +} + +void MessagesManager::get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id, + Promise> &&promise) { + auto r_url = get_login_button_url(dialog_id, message_id, button_id); + if (r_url.is_error()) { + return promise.set_error(r_url.move_as_error()); + } + + td_->create_handler(std::move(promise)) + ->send(r_url.move_as_ok(), dialog_id, message_id, button_id); +} + +void MessagesManager::get_login_url(DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access, + Promise> &&promise) { + auto r_url = get_login_button_url(dialog_id, message_id, button_id); + if (r_url.is_error()) { + return promise.set_error(r_url.move_as_error()); + } + + td_->create_handler(std::move(promise)) + ->send(r_url.move_as_ok(), dialog_id, message_id, button_id, allow_write_access); +} + void MessagesManager::load_secret_thumbnail(FileId thumbnail_file_id) { class Callback : public FileManager::DownloadCallback { public: @@ -6450,7 +7282,7 @@ void MessagesManager::on_upload_media(FileId file_id, tl_object_ptrmessage_id.is_server(); + bool is_edit = m->message_id.is_any_server(); auto dialog_id = full_message_id.get_dialog_id(); auto can_send_status = can_send_message(dialog_id); if (!is_edit && can_send_status.is_error()) { @@ -6504,7 +7336,7 @@ void MessagesManager::do_send_media(DialogId dialog_id, Message *m, FileId file_ << ", ttl = " << m->ttl; MessageContent *content = nullptr; - if (m->message_id.is_server()) { + if (m->message_id.is_any_server()) { content = m->edited_content.get(); if (content == nullptr) { LOG(ERROR) << "Message has no edited content"; @@ -6528,6 +7360,7 @@ void MessagesManager::do_send_secret_media(DialogId dialog_id, Message *m, FileI BufferSlice thumbnail) { CHECK(dialog_id.get_type() == DialogType::SecretChat); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); CHECK(m->message_id.is_yet_unsent()); bool have_input_file = input_encrypted_file != nullptr; @@ -6560,7 +7393,7 @@ void MessagesManager::on_upload_media_error(FileId file_id, Status status) { being_uploaded_files_.erase(it); - bool is_edit = full_message_id.get_message_id().is_server(); + bool is_edit = full_message_id.get_message_id().is_any_server(); if (is_edit) { fail_edit_message_media(full_message_id, Status::Error(status.code() > 0 ? status.code() : 500, status.message())); } else { @@ -6644,7 +7477,7 @@ void MessagesManager::on_upload_thumbnail(FileId thumbnail_file_id, return; } - bool is_edit = m->message_id.is_server(); + bool is_edit = m->message_id.is_any_server(); if (thumbnail_input_file == nullptr) { delete_message_content_thumbnail(is_edit ? m->edited_content.get() : m->content.get(), td_); @@ -6681,12 +7514,12 @@ void MessagesManager::on_upload_dialog_photo(FileId file_id, tl_object_ptrfile_manager_->get_file_view(file_id); CHECK(!file_view.is_encrypted()); if (input_file == nullptr && file_view.has_remote_location()) { - if (file_view.remote_location().is_web()) { + if (file_view.main_remote_location().is_web()) { // TODO reupload promise.set_error(Status::Error(400, "Can't use web photo as profile photo")); return; } - auto input_photo = file_view.remote_location().as_input_photo(); + auto input_photo = file_view.main_remote_location().as_input_photo(); input_chat_photo = make_tl_object(std::move(input_photo)); } else { input_chat_photo = make_tl_object(std::move(input_file)); @@ -6720,6 +7553,9 @@ void MessagesManager::on_upload_dialog_photo_error(FileId file_id, Status status void MessagesManager::before_get_difference() { running_get_difference_ = true; + // scheduled messages are not returned in getDifference, so we must always reget them after it + scheduled_messages_sync_generation_++; + postponed_pts_updates_.insert(std::make_move_iterator(pending_updates_.begin()), std::make_move_iterator(pending_updates_.end())); @@ -6752,7 +7588,8 @@ void MessagesManager::after_get_difference() { if (!pending_on_get_dialogs_.empty()) { LOG(INFO) << "Apply postponed results of getDialogs"; for (auto &res : pending_on_get_dialogs_) { - on_get_dialogs(std::move(res.dialogs), res.total_count, std::move(res.messages), std::move(res.promise)); + on_get_dialogs(res.folder_id, std::move(res.dialogs), res.total_count, std::move(res.messages), + std::move(res.promise)); } pending_on_get_dialogs_.clear(); } @@ -6764,13 +7601,13 @@ void MessagesManager::after_get_difference() { send_update_chat_read_inbox(get_dialog(dialog_id), false, "after_get_difference"); } } - if (have_postponed_unread_message_count_update_) { - LOG(INFO) << "Send postponed unread message count update"; - send_update_unread_message_count(DialogId(), false, "after_get_difference"); + while (!postponed_unread_message_count_updates_.empty()) { + auto folder_id = *postponed_unread_message_count_updates_.begin(); + send_update_unread_message_count(folder_id, DialogId(), false, "after_get_difference"); } - if (have_postponed_unread_chat_count_update_) { - LOG(INFO) << "Send postponed unread chat count update"; - send_update_unread_chat_count(DialogId(), false, "after_get_difference"); + while (!postponed_unread_chat_count_updates_.empty()) { + auto folder_id = *postponed_unread_chat_count_updates_.begin(); + send_update_unread_chat_count(folder_id, DialogId(), false, "after_get_difference"); } vector update_message_ids_to_delete; @@ -6778,6 +7615,8 @@ void MessagesManager::after_get_difference() { // this is impossible for ordinary chats because updates coming during getDifference have already been applied auto full_message_id = it.first; auto dialog_id = full_message_id.get_dialog_id(); + auto message_id = full_message_id.get_message_id(); + CHECK(message_id.is_valid()); switch (dialog_id.get_type()) { case DialogType::Channel: // get channel difference may prevent updates from being applied @@ -6787,7 +7626,7 @@ void MessagesManager::after_get_difference() { // fallthrough case DialogType::User: case DialogType::Chat: { - if (!have_message({dialog_id, it.second}, "after get difference")) { + if (!have_message_force({dialog_id, it.second}, "after get difference")) { // The sent message has already been deleted by the user or sent to inaccessible channel. // The sent message may never be received, but we will need updateMessageId in case the message is received // to delete it from the server and to not add to the chat. @@ -6807,7 +7646,7 @@ void MessagesManager::after_get_difference() { if (dialog_id.get_type() != DialogType::Channel) { dump_debug_message_op(get_dialog(dialog_id)); } - if (full_message_id.get_message_id().get() <= d->last_new_message_id.get()) { + if (message_id.is_scheduled() || message_id <= d->last_new_message_id) { get_message_from_server(it.first, PromiseCreator::lambda([this, full_message_id](Result result) { if (result.is_error()) { LOG(WARNING) @@ -6841,7 +7680,15 @@ void MessagesManager::after_get_difference() { load_notification_settings(); - // TODO resend some messages + if (!td_->auth_manager_->is_bot()) { + auto folder_id = FolderId::archive(); + auto &list = get_dialog_list(folder_id); + if (!list.is_dialog_unread_count_inited_) { + get_dialogs(folder_id, MIN_DIALOG_DATE, 1, false, PromiseCreator::lambda([folder_id](Unit) { + LOG(INFO) << "Inited total chat count in " << folder_id; + })); + } + } } MessagesManager::MessagesInfo MessagesManager::on_get_messages( @@ -6896,10 +7743,10 @@ MessagesManager::MessagesInfo MessagesManager::on_get_messages( } void MessagesManager::on_get_messages(vector> &&messages, bool is_channel_message, - const char *source) { + bool is_scheduled, const char *source) { LOG(DEBUG) << "Receive " << messages.size() << " messages"; for (auto &message : messages) { - on_get_message(std::move(message), false, is_channel_message, false, false, source); + on_get_message(std::move(message), false, is_channel_message, is_scheduled, false, false, source); } } @@ -6909,6 +7756,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ << dialog_id << " from " << from_message_id << " with offset " << offset << " and limit " << limit; CHECK(-limit < offset && offset <= 0); CHECK(offset < 0 || from_the_end); + CHECK(!from_message_id.is_scheduled()); // it is likely that there is no more history messages on the server bool have_full_history = from_the_end && narrow_cast(messages.size()) < limit; @@ -6935,8 +7783,8 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ // check that messages are received in decreasing message_id order MessageId cur_message_id = MessageId::max(); for (const auto &message : messages) { - MessageId message_id = get_message_id(message); - if (message_id.get() >= cur_message_id.get()) { + MessageId message_id = get_message_id(message, false); + if (message_id >= cur_message_id) { string error = PSTRING() << "Receive messages in the wrong order in history of " << dialog_id << " from " << from_message_id << " with offset " << offset << ", limit " << limit << ", from_the_end = " << from_the_end << ": "; @@ -6957,14 +7805,14 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ // be aware that any subset of the returned messages may be already deleted and returned as MessageEmpty bool is_channel_message = dialog_id.get_type() == DialogType::Channel; MessageId first_added_message_id; - MessageId last_received_message_id = get_message_id(messages[0]); + MessageId last_received_message_id = get_message_id(messages[0], false); MessageId last_added_message_id; bool have_next = false; if (narrow_cast(messages.size()) < limit + offset && d != nullptr) { - MessageId first_received_message_id = get_message_id(messages.back()); - if (first_received_message_id.get() >= from_message_id.get() && d->first_database_message_id.is_valid() && - first_received_message_id.get() >= d->first_database_message_id.get()) { + MessageId first_received_message_id = get_message_id(messages.back(), false); + if (first_received_message_id >= from_message_id && d->first_database_message_id.is_valid() && + first_received_message_id >= d->first_database_message_id) { // it is likely that there is no more history messages on the server have_full_history = true; } @@ -6984,20 +7832,20 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ } for (auto &message : messages) { - if (!have_next && from_the_end && d != nullptr && get_message_id(message).get() < d->last_message_id.get()) { + if (!have_next && from_the_end && d != nullptr && get_message_id(message, false) < d->last_message_id) { // last message in the dialog should be attached to the next message if there is some have_next = true; } auto message_dialog_id = get_message_dialog_id(message); if (message_dialog_id != dialog_id) { - LOG(ERROR) << "Receive " << get_message_id(message) << " in wrong " << message_dialog_id << " instead of " + LOG(ERROR) << "Receive " << get_message_id(message, false) << " in wrong " << message_dialog_id << " instead of " << dialog_id << ": " << oneline(to_string(message)); continue; } auto full_message_id = - on_get_message(std::move(message), false, is_channel_message, false, have_next, "get history"); + on_get_message(std::move(message), false, is_channel_message, false, false, have_next, "get history"); auto message_id = full_message_id.get_message_id(); if (message_id.is_valid()) { if (!last_added_message_id.is_valid()) { @@ -7036,20 +7884,19 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ on_dialog_updated(dialog_id, "set have_full_history 2"); } - // LOG_IF(ERROR, d->first_message_id.is_valid() && d->first_message_id.get() > first_received_message_id.get()) + // LOG_IF(ERROR, d->first_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; bool need_update_database_message_ids = - last_added_message_id.is_valid() && - (from_the_end || (last_added_message_id.get() >= d->first_database_message_id.get() && - d->last_database_message_id.get() >= first_added_message_id.get())); + last_added_message_id.is_valid() && (from_the_end || (last_added_message_id >= d->first_database_message_id && + d->last_database_message_id >= first_added_message_id)); if (from_the_end) { if (!d->last_new_message_id.is_valid()) { set_dialog_last_new_message_id( d, last_added_message_id.is_valid() ? last_added_message_id : last_received_message_id, "on_get_history"); } if (last_added_message_id.is_valid()) { - if (last_added_message_id.get() > d->last_message_id.get()) { + if (last_added_message_id > d->last_message_id) { CHECK(d->last_new_message_id.is_valid()); set_dialog_last_message_id(d, last_added_message_id, "on_get_history"); send_update_chat_last_message(d, "on_get_history"); @@ -7090,8 +7937,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ if (*it != nullptr && ((*it)->message_id == d->first_database_message_id || (*it)->have_next)) { while (*it != nullptr) { auto message_id = (*it)->message_id; - if ((message_id.is_server() || message_id.is_local()) && - message_id.get() < d->first_database_message_id.get()) { + if ((message_id.is_server() || message_id.is_local()) && message_id < d->first_database_message_id) { set_dialog_first_database_message_id(d, message_id, "on_get_history 2"); try_restore_dialog_reply_markup(d, *it); is_dialog_updated = true; @@ -7105,8 +7951,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ if (*it != nullptr && ((*it)->message_id == d->last_database_message_id || (*it)->have_next)) { while (*it != nullptr) { auto message_id = (*it)->message_id; - if ((message_id.is_server() || message_id.is_local()) && - message_id.get() > d->last_database_message_id.get()) { + if ((message_id.is_server() || message_id.is_local()) && message_id > d->last_database_message_id) { set_dialog_last_database_message_id(d, message_id, "on_get_history 2"); is_dialog_updated = true; } @@ -7127,8 +7972,7 @@ void MessagesManager::on_get_history(DialogId dialog_id, MessageId from_message_ CHECK(d->last_database_message_id.is_valid()); for (auto &first_message_id : d->first_database_message_id_by_index) { - if (first_added_message_id.get() < first_message_id.get() && - first_message_id.get() <= last_added_message_id.get()) { + if (first_added_message_id < first_message_id && first_message_id <= last_added_message_id) { first_message_id = first_added_message_id; } } @@ -7206,13 +8050,14 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c auto &result = it->second.second; CHECK(result.empty()); for (auto &message : messages) { - auto new_message = on_get_message(std::move(message), false, false, false, false, "search call messages"); - if (new_message != FullMessageId()) { - result.push_back(new_message); + auto new_full_message_id = + on_get_message(std::move(message), false, false, false, false, false, "search call messages"); + if (new_full_message_id != FullMessageId()) { + result.push_back(new_full_message_id); } - auto message_id = new_message.get_message_id(); - if (message_id.get() < first_added_message_id.get() || !first_added_message_id.is_valid()) { + auto message_id = new_full_message_id.get_message_id(); + if (message_id < first_added_message_id || !first_added_message_id.is_valid()) { first_added_message_id = message_id; } } @@ -7228,15 +8073,12 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c auto &old_first_db_message_id = calls_db_state_.first_calls_database_message_id_by_index[search_calls_filter_index(filter)]; - bool from_the_end = !from_message_id.is_valid() || from_message_id.get() >= MessageId::max().get(); - LOG(INFO) << "Have from_the_end = " << from_the_end - << ", old_first_db_message_id = " << old_first_db_message_id.get() - << ", first_added_message_id = " << first_added_message_id.get() - << ", from_message_id = " << from_message_id.get(); - if ((from_the_end || - (old_first_db_message_id.is_valid() && old_first_db_message_id.get() <= from_message_id.get())) && - (!old_first_db_message_id.is_valid() || first_added_message_id.get() < old_first_db_message_id.get())) { - LOG(INFO) << "Update calls database first message id to " << first_added_message_id; + bool from_the_end = !from_message_id.is_valid() || from_message_id >= MessageId::max(); + LOG(INFO) << "Have from_the_end = " << from_the_end << ", old_first_db_message_id = " << old_first_db_message_id + << ", first_added_message_id = " << first_added_message_id << ", from_message_id = " << from_message_id; + if ((from_the_end || (old_first_db_message_id.is_valid() && old_first_db_message_id <= from_message_id)) && + (!old_first_db_message_id.is_valid() || first_added_message_id < old_first_db_message_id)) { + LOG(INFO) << "Update calls database first message to " << first_added_message_id; old_first_db_message_id = first_added_message_id; update_state = true; } @@ -7262,28 +8104,27 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); for (auto &message : messages) { - auto new_message = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, false, - false, "SearchMessagesQuery"); - if (new_message == FullMessageId()) { + auto new_full_message_id = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, + false, false, false, "SearchMessagesQuery"); + if (new_full_message_id == FullMessageId()) { total_count--; continue; } - if (new_message.get_dialog_id() != dialog_id) { - LOG(ERROR) << "Receive " << new_message << " instead of a message in " << dialog_id; + if (new_full_message_id.get_dialog_id() != dialog_id) { + LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << dialog_id; total_count--; continue; } - auto message_id = new_message.get_message_id(); - if (filter == SearchMessagesFilter::UnreadMention && - message_id.get() <= d->last_read_all_mentions_message_id.get()) { + auto message_id = new_full_message_id.get_message_id(); + if (filter == SearchMessagesFilter::UnreadMention && message_id <= d->last_read_all_mentions_message_id) { total_count--; continue; } // TODO check that messages are returned in decreasing message_id order - if (message_id.get() < first_added_message_id.get() || !first_added_message_id.is_valid()) { + if (message_id < first_added_message_id || !first_added_message_id.is_valid()) { first_added_message_id = message_id; } result.push_back(message_id); @@ -7310,11 +8151,10 @@ void MessagesManager::on_get_dialog_messages_search_result(DialogId dialog_id, c auto &old_first_db_message_id = d->first_database_message_id_by_index[search_messages_filter_index(filter)]; bool from_the_end = !from_message_id.is_valid() || - (d->last_message_id != MessageId() && from_message_id.get() > d->last_message_id.get()) || - from_message_id.get() >= MessageId::max().get(); - if ((from_the_end || - (old_first_db_message_id.is_valid() && old_first_db_message_id.get() <= from_message_id.get())) && - (!old_first_db_message_id.is_valid() || first_added_message_id.get() < old_first_db_message_id.get())) { + (d->last_message_id != MessageId() && from_message_id > d->last_message_id) || + from_message_id >= MessageId::max(); + if ((from_the_end || (old_first_db_message_id.is_valid() && old_first_db_message_id <= from_message_id)) && + (!old_first_db_message_id.is_valid() || first_added_message_id < old_first_db_message_id)) { old_first_db_message_id = first_added_message_id; update_dialog = true; } @@ -7350,11 +8190,11 @@ void MessagesManager::on_get_messages_search_result(const string &query, int32 o CHECK(result.empty()); for (auto &message : messages) { auto dialog_id = get_message_dialog_id(message); - auto new_message = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, false, - false, "search messages"); - if (new_message != FullMessageId()) { - CHECK(dialog_id == new_message.get_dialog_id()); - result.push_back(new_message); + auto new_full_message_id = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, + false, false, false, "search messages"); + if (new_full_message_id != FullMessageId()) { + CHECK(dialog_id == new_full_message_id.get_dialog_id()); + result.push_back(new_full_message_id); } else { total_count--; } @@ -7373,6 +8213,60 @@ void MessagesManager::on_failed_messages_search(int64 random_id) { found_messages_.erase(it); } +void MessagesManager::on_get_scheduled_server_messages(DialogId dialog_id, uint32 generation, + vector> &&messages, + bool is_not_modified) { + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + if (generation < d->scheduled_messages_sync_generation) { + return; + } + d->scheduled_messages_sync_generation = generation; + + if (is_not_modified) { + return; + } + + vector old_message_ids; + find_old_messages(d->scheduled_messages.get(), + MessageId(ScheduledServerMessageId(), std::numeric_limits::max(), true), old_message_ids); + std::unordered_map old_server_message_ids; + for (auto &message_id : old_message_ids) { + if (message_id.is_scheduled_server()) { + old_server_message_ids[message_id.get_scheduled_server_message_id()] = message_id; + } + } + + bool is_channel_message = dialog_id.get_type() == DialogType::Channel; + for (auto &message : messages) { + auto message_dialog_id = get_message_dialog_id(message); + if (message_dialog_id != dialog_id) { + if (dialog_id.is_valid()) { + LOG(ERROR) << "Receive " << get_message_id(message, true) << " in wrong " << message_dialog_id << " instead of " + << dialog_id << ": " << oneline(to_string(message)); + } + continue; + } + + auto full_message_id = on_get_message(std::move(message), false, 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()); + } + } + + for (auto it : old_server_message_ids) { + auto message_id = it.second; + auto message = do_delete_scheduled_message(d, message_id, true, "on_get_scheduled_server_messages"); + CHECK(message != nullptr); + send_update_delete_messages(dialog_id, {message->message_id.get()}, true, false); + } + + send_update_chat_has_scheduled_messages(d); +} + void MessagesManager::on_get_recent_locations(DialogId dialog_id, int32 limit, int64 random_id, int32 total_count, vector> &&messages) { LOG(INFO) << "Receive " << messages.size() << " recent locations in " << dialog_id; @@ -7382,21 +8276,24 @@ void MessagesManager::on_get_recent_locations(DialogId dialog_id, int32 limit, i auto &result = it->second.second; CHECK(result.empty()); for (auto &message : messages) { - auto new_message = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, false, - false, "get recent locations"); - if (new_message != FullMessageId()) { - if (new_message.get_dialog_id() != dialog_id) { - LOG(ERROR) << "Receive " << new_message << " instead of a message in " << dialog_id; + auto new_full_message_id = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, + false, false, false, "get recent locations"); + if (new_full_message_id != FullMessageId()) { + if (new_full_message_id.get_dialog_id() != dialog_id) { + LOG(ERROR) << "Receive " << new_full_message_id << " instead of a message in " << dialog_id; + total_count--; continue; } - auto m = get_message(new_message); + auto m = get_message(new_full_message_id); + CHECK(m != nullptr); if (m->content->get_type() != MessageContentType::LiveLocation) { LOG(ERROR) << "Receive a message of wrong type " << m->content->get_type() << " in on_get_recent_locations in " << dialog_id; + total_count--; continue; } - result.push_back(new_message.get_message_id()); + result.push_back(m->message_id); } else { total_count--; } @@ -7426,16 +8323,16 @@ void MessagesManager::delete_messages_from_updates(const vector &mess Dialog *d = get_dialog_by_message_id(message_id); if (d != nullptr) { - auto m = delete_message(d, message_id, true, &need_update_dialog_pos[d->dialog_id], "updates"); - CHECK(m != nullptr); - LOG_CHECK(m->message_id == message_id) << message_id << " " << m->message_id << " " << d->dialog_id; - deleted_message_ids[d->dialog_id].push_back(message_id.get()); + auto message = delete_message(d, message_id, true, &need_update_dialog_pos[d->dialog_id], "updates"); + CHECK(message != nullptr); + LOG_CHECK(message->message_id == message_id) << message_id << " " << message->message_id << " " << d->dialog_id; + deleted_message_ids[d->dialog_id].push_back(message->message_id.get()); } if (last_clear_history_message_id_to_dialog_id_.count(message_id)) { d = get_dialog(last_clear_history_message_id_to_dialog_id_[message_id]); CHECK(d != nullptr); - auto m = delete_message(d, message_id, true, &need_update_dialog_pos[d->dialog_id], "updates"); - CHECK(m == nullptr); + auto message = delete_message(d, message_id, true, &need_update_dialog_pos[d->dialog_id], "updates"); + CHECK(message == nullptr); } } for (auto &it : need_update_dialog_pos) { @@ -7469,8 +8366,8 @@ void MessagesManager::delete_dialog_messages_from_updates(DialogId dialog_id, co continue; } - delete_message(d, message_id, true, &need_update_dialog_pos, "updates"); - deleted_message_ids.push_back(message_id.get()); + auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "updates"); + deleted_message_ids.push_back(message == nullptr ? message_id.get() : message->message_id.get()); } if (need_update_dialog_pos) { send_update_chat_last_message(d, "delete_dialog_messages_from_updates"); @@ -7492,6 +8389,9 @@ bool MessagesManager::can_forward_message(DialogId from_dialog_id, const Message if (m->ttl > 0) { return false; } + if (m->message_id.is_scheduled()) { + return false; + } switch (from_dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: @@ -7516,6 +8416,12 @@ bool MessagesManager::can_delete_channel_message(DialogParticipantStatus status, if (m->message_id.is_local() || m->message_id.is_yet_unsent()) { return true; } + if (m->message_id.is_scheduled()) { + if (m->is_channel_post) { + return status.can_post_messages(); + } + return true; + } if (is_bot && G()->unix_time_cached() >= m->date + 2 * 86400) { // bots can't delete messages older than 2 days @@ -7548,7 +8454,7 @@ bool MessagesManager::can_delete_channel_message(DialogParticipantStatus status, bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) const { if (m == nullptr) { - return false; + return true; } if (m->message_id.is_local()) { return false; @@ -7556,6 +8462,9 @@ bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) c if (dialog_id == get_my_dialog_id()) { return false; } + if (m->message_id.is_scheduled()) { + return false; + } if (m->message_id.is_yet_unsent()) { return true; } @@ -7611,14 +8520,26 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector message_ids; message_ids.reserve(input_message_ids.size()); vector deleted_server_message_ids; + + vector deleted_scheduled_server_message_ids; for (auto message_id : input_message_ids) { - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { return promise.set_error(Status::Error(6, "Invalid message identifier")); } + message_id = get_persistent_message_id(d, message_id); message_ids.push_back(message_id); - if (get_message_force(d, message_id, "delete_messages") != nullptr && (message_id.is_server() || is_secret)) { - deleted_server_message_ids.push_back(message_id); + auto message = get_message_force(d, message_id, "delete_messages"); + if (message != nullptr) { + if (message->message_id.is_scheduled()) { + if (message->message_id.is_scheduled_server()) { + deleted_scheduled_server_message_ids.push_back(message->message_id); + } + } else { + if (message->message_id.is_server() || is_secret) { + deleted_server_message_ids.push_back(message->message_id); + } + } } } @@ -7628,7 +8549,8 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector deleted_message_ids; @@ -7667,6 +8596,18 @@ void MessagesManager::delete_messages(DialogId dialog_id, const vector message_ids, bool revoke, uint64 logevent_id, Promise &&promise) { if (message_ids.empty()) { - promise.set_value(Unit()); - return; + return promise.set_value(Unit()); } LOG(INFO) << (revoke ? "Revoke " : "Delete ") << format::as_array(message_ids) << " in " << dialog_id << " from server"; @@ -7752,6 +8692,48 @@ void MessagesManager::delete_messages_from_server(DialogId dialog_id, vector message_ids_; + + template + void store(StorerT &storer) const { + td::store(dialog_id_, storer); + td::store(message_ids_, storer); + } + + template + void parse(ParserT &parser) { + td::parse(dialog_id_, parser); + td::parse(message_ids_, parser); + } +}; + +uint64 MessagesManager::save_delete_scheduled_messages_from_server_logevent(DialogId dialog_id, + const vector &message_ids) { + DeleteScheduledMessagesFromServerLogEvent logevent{dialog_id, message_ids}; + auto storer = LogEventStorerImpl(logevent); + return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::DeleteScheduledMessagesFromServer, storer); +} + +void MessagesManager::delete_scheduled_messages_from_server(DialogId dialog_id, vector message_ids, + uint64 logevent_id, Promise &&promise) { + if (message_ids.empty()) { + return promise.set_value(Unit()); + } + LOG(INFO) << "Delete " << format::as_array(message_ids) << " in " << dialog_id << " from server"; + + if (logevent_id == 0 && G()->parameters().use_message_db) { + logevent_id = save_delete_scheduled_messages_from_server_logevent(dialog_id, message_ids); + } + + auto new_promise = get_erase_logevent_promise(logevent_id, std::move(promise)); + promise = std::move(new_promise); // to prevent self-move + + td_->create_handler(std::move(promise))->send(dialog_id, std::move(message_ids)); +} + void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke, Promise &&promise) { LOG(INFO) << "Receive deleteChatHistory request to delete all messages in " << dialog_id @@ -7777,7 +8759,7 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from if (is_broadcast_channel(dialog_id)) { return promise.set_error(Status::Error(3, "Can't delete chat history in a channel")); } - if (!get_dialog_username(dialog_id).empty()) { + if (td_->contacts_manager_->is_channel_public(dialog_id.get_channel_id())) { return promise.set_error(Status::Error(3, "Can't delete chat history in a public supergroup")); } break; @@ -7883,59 +8865,57 @@ void MessagesManager::delete_dialog_history_from_server(DialogId dialog_id, Mess } } -void MessagesManager::find_messages_from_user(const unique_ptr &m, UserId user_id, - vector &message_ids) { +void MessagesManager::find_messages_from_user(const Message *m, UserId user_id, vector &message_ids) { if (m == nullptr) { return; } - find_messages_from_user(m->left, user_id, message_ids); + find_messages_from_user(m->left.get(), user_id, message_ids); if (m->sender_user_id == user_id) { message_ids.push_back(m->message_id); } - find_messages_from_user(m->right, user_id, message_ids); + find_messages_from_user(m->right.get(), user_id, message_ids); } -void MessagesManager::find_unread_mentions(const unique_ptr &m, vector &message_ids) { +void MessagesManager::find_unread_mentions(const Message *m, vector &message_ids) { if (m == nullptr) { return; } - find_unread_mentions(m->left, message_ids); + find_unread_mentions(m->left.get(), message_ids); if (m->contains_unread_mention) { message_ids.push_back(m->message_id); } - find_unread_mentions(m->right, message_ids); + find_unread_mentions(m->right.get(), message_ids); } -void MessagesManager::find_old_messages(const unique_ptr &m, MessageId max_message_id, - vector &message_ids) { +void MessagesManager::find_old_messages(const Message *m, MessageId max_message_id, vector &message_ids) { if (m == nullptr) { return; } - find_old_messages(m->left, max_message_id, message_ids); + find_old_messages(m->left.get(), max_message_id, message_ids); - if (m->message_id.get() <= max_message_id.get()) { + if (m->message_id <= max_message_id) { message_ids.push_back(m->message_id); - find_old_messages(m->right, max_message_id, message_ids); + find_old_messages(m->right.get(), max_message_id, message_ids); } } -void MessagesManager::find_unloadable_messages(const Dialog *d, int32 unload_before_date, const unique_ptr &m, +void MessagesManager::find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m, vector &message_ids, int32 &left_to_unload) const { if (m == nullptr) { return; } - find_unloadable_messages(d, unload_before_date, m->left, message_ids, left_to_unload); + find_unloadable_messages(d, unload_before_date, m->left.get(), message_ids, left_to_unload); - if (can_unload_message(d, m.get())) { + if (can_unload_message(d, m)) { if (m->last_access_date <= unload_before_date) { message_ids.push_back(m->message_id); } else { @@ -7943,7 +8923,7 @@ void MessagesManager::find_unloadable_messages(const Dialog *d, int32 unload_bef } } - find_unloadable_messages(d, unload_before_date, m->right, message_ids, left_to_unload); + find_unloadable_messages(d, unload_before_date, m->right.get(), message_ids, left_to_unload); } void MessagesManager::delete_dialog_messages_from_user(DialogId dialog_id, UserId user_id, Promise &&promise) { @@ -8001,7 +8981,7 @@ void MessagesManager::delete_dialog_messages_from_user(DialogId dialog_id, UserI } vector message_ids; - find_messages_from_user(d->messages, user_id, message_ids); + find_messages_from_user(d->messages.get(), user_id, message_ids); vector deleted_message_ids; bool need_update_dialog_pos = false; @@ -8011,9 +8991,9 @@ void MessagesManager::delete_dialog_messages_from_user(DialogId dialog_id, UserI CHECK(m->sender_user_id == user_id); CHECK(m->message_id == message_id); if (can_delete_channel_message(channel_status, m, is_bot)) { - deleted_message_ids.push_back(message_id.get()); auto p = delete_message(d, message_id, true, &need_update_dialog_pos, "delete messages from user"); CHECK(p.get() == m); + deleted_message_ids.push_back(p->message_id.get()); } } @@ -8078,7 +9058,7 @@ void MessagesManager::unload_dialog(DialogId dialog_id) { vector to_unload_message_ids; int32 left_to_unload = 0; - find_unloadable_messages(d, G()->unix_time_cached() - get_unload_dialog_delay() + 2, d->messages, + find_unloadable_messages(d, G()->unix_time_cached() - get_unload_dialog_delay() + 2, d->messages.get(), to_unload_message_ids, left_to_unload); vector unloaded_message_ids; @@ -8103,10 +9083,11 @@ void MessagesManager::unload_dialog(DialogId dialog_id) { } } -void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dialog_list, bool is_permanent) { +void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dialog_list, bool is_permanently_deleted) { CHECK(d != nullptr); LOG(INFO) << "Delete all messages in " << d->dialog_id - << " with remove_from_dialog_list = " << remove_from_dialog_list << " and is_permanent = " << is_permanent; + << " with remove_from_dialog_list = " << remove_from_dialog_list + << " and is_permanently_deleted = " << is_permanently_deleted; if (is_debug_message_op_enabled()) { d->debug_message_op.emplace_back(Dialog::MessageOp::DeleteAll, MessageId(), MessageContentType::None, remove_from_dialog_list, false, false, ""); @@ -8145,9 +9126,9 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia } vector deleted_message_ids; - do_delete_all_dialog_messages(d, d->messages, deleted_message_ids); + do_delete_all_dialog_messages(d, d->messages, is_permanently_deleted, deleted_message_ids); delete_all_dialog_messages_from_database(d, MessageId::max(), "delete_all_dialog_messages"); - if (is_permanent) { + if (is_permanently_deleted) { for (auto id : deleted_message_ids) { d->deleted_message_ids.insert(MessageId{id}); } @@ -8179,7 +9160,7 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia on_dialog_updated(d->dialog_id, "delete_all_dialog_messages"); - send_update_delete_messages(d->dialog_id, std::move(deleted_message_ids), is_permanent, false); + send_update_delete_messages(d->dialog_id, std::move(deleted_message_ids), is_permanently_deleted, false); } void MessagesManager::delete_dialog(DialogId dialog_id) { @@ -8222,13 +9203,13 @@ void MessagesManager::read_all_dialog_mentions(DialogId dialog_id, Promise return promise.set_value(Unit()); } - if (d->last_new_message_id.get() > d->last_read_all_mentions_message_id.get()) { + if (d->last_new_message_id > d->last_read_all_mentions_message_id) { d->last_read_all_mentions_message_id = d->last_new_message_id; on_dialog_updated(dialog_id, "read_all_dialog_mentions"); } vector message_ids; - find_unread_mentions(d->messages, message_ids); + find_unread_mentions(d->messages.get(), message_ids); LOG(INFO) << "Found " << message_ids.size() << " messages with unread mentions in memory"; bool is_update_sent = false; @@ -8237,6 +9218,7 @@ void MessagesManager::read_all_dialog_mentions(DialogId dialog_id, Promise CHECK(m != nullptr); CHECK(m->contains_unread_mention); CHECK(m->message_id == message_id); + CHECK(m->message_id.is_valid()); remove_message_notification_id(d, m, true, false); // should be called before contains_unread_mention is updated m->contains_unread_mention = false; @@ -8317,13 +9299,14 @@ void MessagesManager::read_channel_message_content_from_updates(Dialog *d, Messa Message *m = get_message_force(d, message_id, "read_channel_message_content_from_updates"); if (m != nullptr) { read_message_content(d, m, false, "read_channel_message_content_from_updates"); - } else if (message_id.get() > d->last_new_message_id.get()) { + } else if (message_id > d->last_new_message_id) { get_channel_difference(d->dialog_id, d->pts, true, "read_channel_message_content_from_updates"); } } bool MessagesManager::read_message_content(Dialog *d, Message *m, bool is_local_read, const char *source) { LOG_CHECK(m != nullptr) << source; + CHECK(!m->message_id.is_scheduled()); bool is_mention_read = update_message_contains_unread_mention(d, m, false, "read_message_content"); bool is_content_read = update_opened_message_content(m->content.get()) | ttl_on_open(d, m, Time::now(), is_local_read); @@ -8341,15 +9324,24 @@ bool MessagesManager::read_message_content(Dialog *d, Message *m, bool is_local_ return false; } -int32 MessagesManager::calc_new_unread_count_from_last_unread(Dialog *d, MessageId max_message_id, MessageType type) { +bool MessagesManager::has_incoming_notification(DialogId dialog_id, const Message *m) const { + if (m->is_from_scheduled) { + return true; + } + return !m->is_outgoing && dialog_id != get_my_dialog_id(); +} + +int32 MessagesManager::calc_new_unread_count_from_last_unread(Dialog *d, MessageId max_message_id, + MessageType type) const { + CHECK(!max_message_id.is_scheduled()); MessagesConstIterator it(d, max_message_id); if (*it == nullptr || (*it)->message_id != max_message_id) { return -1; } int32 unread_count = type == MessageType::Server ? d->server_unread_count : d->local_unread_count; - while (*it != nullptr && (*it)->message_id.get() > d->last_read_inbox_message_id.get()) { - if (!(*it)->is_outgoing && (*it)->message_id.get_type() == type) { + while (*it != nullptr && (*it)->message_id > d->last_read_inbox_message_id) { + if (has_incoming_notification(d->dialog_id, *it) && (*it)->message_id.get_type() == type) { unread_count--; } --it; @@ -8363,11 +9355,12 @@ int32 MessagesManager::calc_new_unread_count_from_last_unread(Dialog *d, Message } int32 MessagesManager::calc_new_unread_count_from_the_end(Dialog *d, MessageId max_message_id, MessageType type, - int32 hint_unread_count) { + int32 hint_unread_count) const { + CHECK(!max_message_id.is_scheduled()); int32 unread_count = 0; MessagesConstIterator it(d, MessageId::max()); - while (*it != nullptr && (*it)->message_id.get() > max_message_id.get()) { - if (!(*it)->is_outgoing && (*it)->message_id.get_type() == type) { + while (*it != nullptr && (*it)->message_id > max_message_id) { + if (has_incoming_notification(d->dialog_id, *it) && (*it)->message_id.get_type() == type) { unread_count++; } --it; @@ -8400,7 +9393,8 @@ int32 MessagesManager::calc_new_unread_count_from_the_end(Dialog *d, MessageId m } int32 MessagesManager::calc_new_unread_count(Dialog *d, MessageId max_message_id, MessageType type, - int32 hint_unread_count) { + int32 hint_unread_count) const { + CHECK(!max_message_id.is_scheduled()); if (d->is_empty) { return 0; } @@ -8441,7 +9435,7 @@ void MessagesManager::repair_channel_server_unread_count(Dialog *d) { if (td_->auth_manager_->is_bot()) { return; } - if (d->last_read_inbox_message_id.get() >= d->last_new_message_id.get()) { + if (d->last_read_inbox_message_id >= d->last_new_message_id) { // all messages are already read return; } @@ -8451,11 +9445,14 @@ void MessagesManager::repair_channel_server_unread_count(Dialog *d) { } LOG(INFO) << "Reload ChannelFull for " << d->dialog_id << " to repair unread message counts"; - td_->contacts_manager_->get_channel_full(d->dialog_id.get_channel_id(), Auto()); + // TODO logevent? + td_->contacts_manager_->get_channel_full(d->dialog_id.get_channel_id(), Promise()); } void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_message_id, int32 unread_count, const char *source) { + CHECK(!max_message_id.is_scheduled()); + if (td_->auth_manager_->is_bot()) { return; } @@ -8467,7 +9464,7 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa LOG(ERROR) << "Receive read inbox update in " << dialog_id << " up to " << max_message_id << " from " << source; return; } - if (d->is_last_read_inbox_message_id_inited && max_message_id.get() <= d->last_read_inbox_message_id.get()) { + 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; return; @@ -8479,14 +9476,17 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa return; } - if (max_message_id != MessageId() && unread_count > 0 && max_message_id.get() >= d->last_new_message_id.get() && - max_message_id.get() >= d->last_message_id.get() && max_message_id.get() >= d->last_database_message_id.get()) { - LOG(INFO) << "Have unknown " << unread_count << " unread messages in " << dialog_id; + if (max_message_id != MessageId() && unread_count > 0 && max_message_id >= d->last_new_message_id && + max_message_id >= d->last_message_id && max_message_id >= d->last_database_message_id) { + LOG(ERROR) << "Have unknown " << unread_count << " unread messages up to " << max_message_id << " in " + << dialog_id << " with last_new_message_id = " << d->last_new_message_id + << ", last_message_id = " << d->last_message_id + << ", last_database_message_id = " << d->last_database_message_id; unread_count = 0; } - LOG_IF(INFO, d->last_new_message_id.is_valid() && max_message_id.get() > d->last_new_message_id.get() && - max_message_id.get() > d->max_notification_message_id.get() && max_message_id.is_server() && + LOG_IF(INFO, d->last_new_message_id.is_valid() && max_message_id > d->last_new_message_id && + max_message_id > d->max_notification_message_id && max_message_id.is_server() && dialog_id.get_type() != DialogType::Channel && !running_get_difference_) << "Receive read inbox update up to unknown " << max_message_id << " in " << dialog_id << " from " << source << ". Last new is " << d->last_new_message_id << ", unread_count = " << unread_count @@ -8496,17 +9496,14 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa ttl_read_history(d, false, max_message_id, d->last_read_inbox_message_id, Time::now()); } - if (max_message_id.get() > d->last_new_message_id.get() && dialog_id.get_type() == DialogType::Channel) { + if (max_message_id > d->last_new_message_id && dialog_id.get_type() == DialogType::Channel) { LOG(INFO) << "Schedule getDifference in " << dialog_id.get_channel_id(); channel_get_difference_retry_timeout_.add_timeout_in(dialog_id.get(), 0.001); } - bool is_saved_messages = dialog_id == get_my_dialog_id(); - int32 server_unread_count = - is_saved_messages ? 0 : calc_new_unread_count(d, max_message_id, MessageType::Server, unread_count); - int32 local_unread_count = d->local_unread_count == 0 || is_saved_messages - ? 0 - : calc_new_unread_count(d, max_message_id, MessageType::Local, -1); + int32 server_unread_count = calc_new_unread_count(d, max_message_id, MessageType::Server, unread_count); + int32 local_unread_count = + d->local_unread_count == 0 ? 0 : calc_new_unread_count(d, max_message_id, MessageType::Local, -1); if (server_unread_count < 0) { server_unread_count = unread_count >= 0 ? unread_count : d->server_unread_count; @@ -8532,6 +9529,8 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa } void MessagesManager::read_history_outbox(DialogId dialog_id, MessageId max_message_id, int32 read_date) { + CHECK(!max_message_id.is_scheduled()); + if (td_->auth_manager_->is_bot()) { return; } @@ -8542,7 +9541,7 @@ void MessagesManager::read_history_outbox(DialogId dialog_id, MessageId max_mess LOG(ERROR) << "Receive read outbox update in " << dialog_id << " with " << max_message_id; return; } - if (max_message_id.get() <= d->last_read_outbox_message_id.get()) { + if (max_message_id <= d->last_read_outbox_message_id) { LOG(INFO) << "Receive read outbox update up to " << max_message_id << ", but all messages have already been read up to " << d->last_read_outbox_message_id; return; @@ -8554,7 +9553,7 @@ void MessagesManager::read_history_outbox(DialogId dialog_id, MessageId max_mess } // it is impossible for just sent outgoing messages because updates are ordered by pts - LOG_IF(INFO, d->last_new_message_id.is_valid() && max_message_id.get() > d->last_new_message_id.get() && + LOG_IF(INFO, d->last_new_message_id.is_valid() && max_message_id > d->last_new_message_id && dialog_id.get_type() != DialogType::Channel) << "Receive read outbox update about unknown " << max_message_id << " in " << dialog_id << " with last new " << d->last_new_message_id << ". Possible only for deleted outgoing message"; @@ -8580,28 +9579,96 @@ bool MessagesManager::need_unread_counter(int64 dialog_order) { return dialog_order != DEFAULT_ORDER; } -void MessagesManager::recalc_unread_count() { - if (td_->auth_manager_->is_bot() || !need_unread_count_recalc_) { +int32 MessagesManager::get_dialog_total_count(const DialogList &list) { + 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_); + } + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { + return list.in_memory_dialog_total_count_; + } + return list.in_memory_dialog_total_count_ + 1; +} + +void MessagesManager::repair_server_dialog_total_count(FolderId folder_id) { + if (td_->auth_manager_->is_bot()) { return; } - LOG(INFO) << "Recalculate unread counts"; - need_unread_count_recalc_ = false; - is_message_unread_count_inited_ = true; - is_dialog_unread_count_inited_ = true; - int32 total_count = 0; - int32 muted_count = 0; + send_closure(td_->create_net_actor(Promise()), &GetDialogListActor::send, folder_id, + 2147483647, ServerMessageId(), DialogId(), 1, + get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); +} + +void MessagesManager::repair_secret_chat_total_count(FolderId folder_id) { + if (td_->auth_manager_->is_bot()) { + return; + } + + if (G()->parameters().use_message_db) { + // race-prone + G()->td_db()->get_dialog_db_async()->get_secret_chat_count( + folder_id, PromiseCreator::lambda([actor_id = actor_id(this), folder_id](Result result) { + if (result.is_error()) { + return; + } + send_closure(actor_id, &MessagesManager::on_get_secret_chat_total_count, folder_id, result.move_as_ok()); + })); + } else { + int32 total_count = 0; + auto &list = get_dialog_list(folder_id); + for (const auto &dialog_date : list.ordered_server_dialogs_) { + auto dialog_id = dialog_date.get_dialog_id(); + if (dialog_id.get_type() == DialogType::SecretChat && dialog_date.get_order() != DEFAULT_ORDER) { + total_count++; + } + } + on_get_secret_chat_total_count(folder_id, total_count); + } +} + +void MessagesManager::on_get_secret_chat_total_count(FolderId folder_id, int32 total_count) { + CHECK(!td_->auth_manager_->is_bot()); + auto &list = get_dialog_list(folder_id); + CHECK(total_count >= 0); + if (list.secret_chat_total_count_ != total_count) { + auto old_dialog_total_count = get_dialog_total_count(list); + list.secret_chat_total_count_ = total_count; + if (list.is_dialog_unread_count_inited_ && old_dialog_total_count != get_dialog_total_count(list)) { + send_update_unread_chat_count(folder_id, DialogId(), true, "on_get_secret_chat_total_count"); + } + } +} + +void MessagesManager::recalc_unread_count(FolderId folder_id) { + if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { + return; + } + + auto &list = get_dialog_list(folder_id); + if (!list.need_unread_count_recalc_) { + return; + } + LOG(INFO) << "Recalculate unread counts in " << folder_id; + list.need_unread_count_recalc_ = false; + list.is_message_unread_count_inited_ = true; + list.is_dialog_unread_count_inited_ = true; + + int32 message_total_count = 0; + int32 message_muted_count = 0; int32 dialog_total_count = 0; int32 dialog_muted_count = 0; int32 dialog_marked_count = 0; int32 dialog_muted_marked_count = 0; - for (auto &dialog_date : ordered_server_dialogs_) { + int32 server_dialog_total_count = 0; + int32 secret_chat_total_count = 0; + for (const auto &dialog_date : list.ordered_server_dialogs_) { auto dialog_id = dialog_date.get_dialog_id(); Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); int unread_count = d->server_unread_count + d->local_unread_count; if (need_unread_counter(d->order) && (unread_count > 0 || d->is_marked_as_unread)) { - total_count += unread_count; + message_total_count += unread_count; dialog_total_count++; if (unread_count == 0 && d->is_marked_as_unread) { dialog_marked_count++; @@ -8609,34 +9676,60 @@ void MessagesManager::recalc_unread_count() { LOG(DEBUG) << "Have " << unread_count << " messages in " << dialog_id; if (is_dialog_muted(d)) { - muted_count += unread_count; + message_muted_count += unread_count; dialog_muted_count++; if (unread_count == 0 && d->is_marked_as_unread) { dialog_muted_marked_count++; } } } + if (d->order != DEFAULT_ORDER) { + if (dialog_id.get_type() == DialogType::SecretChat) { + secret_chat_total_count++; + } else { + server_dialog_total_count++; + } + } } - if (unread_message_total_count_ != total_count || unread_message_muted_count_ != muted_count) { - unread_message_total_count_ = total_count; - unread_message_muted_count_ = muted_count; - send_update_unread_message_count(DialogId(), true, "recalc_unread_count"); + if (list.unread_message_total_count_ != message_total_count || + list.unread_message_muted_count_ != message_muted_count) { + list.unread_message_total_count_ = message_total_count; + list.unread_message_muted_count_ = message_muted_count; + send_update_unread_message_count(folder_id, DialogId(), true, "recalc_unread_count"); } - if (unread_dialog_total_count_ != dialog_total_count || unread_dialog_muted_count_ != dialog_muted_count || - unread_dialog_marked_count_ != dialog_marked_count || - unread_dialog_muted_marked_count_ != dialog_muted_marked_count) { - unread_dialog_total_count_ = dialog_total_count; - unread_dialog_muted_count_ = dialog_muted_count; - unread_dialog_marked_count_ = dialog_marked_count; - unread_dialog_muted_marked_count_ = dialog_muted_marked_count; - send_update_unread_chat_count(DialogId(), true, "recalc_unread_count"); + + auto old_dialog_total_count = get_dialog_total_count(list); + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { + if (server_dialog_total_count != list.server_dialog_total_count_ || + secret_chat_total_count != list.secret_chat_total_count_) { + list.server_dialog_total_count_ = server_dialog_total_count; + list.secret_chat_total_count_ = secret_chat_total_count; + } + } else { + repair_server_dialog_total_count(folder_id); + + if (list.secret_chat_total_count_ == -1) { + repair_secret_chat_total_count(folder_id); + } + } + if (list.unread_dialog_total_count_ != dialog_total_count || list.unread_dialog_muted_count_ != dialog_muted_count || + list.unread_dialog_marked_count_ != dialog_marked_count || + list.unread_dialog_muted_marked_count_ != dialog_muted_marked_count || + old_dialog_total_count != get_dialog_total_count(list)) { + list.unread_dialog_total_count_ = dialog_total_count; + list.unread_dialog_muted_count_ = dialog_muted_count; + list.unread_dialog_marked_count_ = dialog_marked_count; + list.unread_dialog_muted_marked_count_ = dialog_muted_marked_count; + send_update_unread_chat_count(folder_id, DialogId(), true, "recalc_unread_count"); } } void MessagesManager::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) { + CHECK(!message_id.is_scheduled()); + if (td_->auth_manager_->is_bot()) { return; } @@ -8655,28 +9748,29 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId d->local_unread_count = local_unread_count; int32 new_unread_count = d->server_unread_count + d->local_unread_count; int32 delta = new_unread_count - old_unread_count; - if (delta != 0 && need_unread_counter(d->order) && is_message_unread_count_inited_) { - unread_message_total_count_ += delta; + auto &list = get_dialog_list(d->folder_id); + if (delta != 0 && need_unread_counter(d->order) && list.is_message_unread_count_inited_) { + list.unread_message_total_count_ += delta; if (is_dialog_muted(d)) { - unread_message_muted_count_ += delta; + list.unread_message_muted_count_ += delta; } - send_update_unread_message_count(d->dialog_id, force_update, source); + send_update_unread_message_count(d->folder_id, d->dialog_id, force_update, source); } delta = static_cast(new_unread_count != 0) - static_cast(old_unread_count != 0); - if (delta != 0 && need_unread_counter(d->order) && is_dialog_unread_count_inited_) { + if (delta != 0 && need_unread_counter(d->order) && list.is_dialog_unread_count_inited_) { if (d->is_marked_as_unread) { - unread_dialog_marked_count_ -= delta; + list.unread_dialog_marked_count_ -= delta; } else { - unread_dialog_total_count_ += delta; + list.unread_dialog_total_count_ += delta; } if (is_dialog_muted(d)) { if (d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ -= delta; + list.unread_dialog_muted_marked_count_ -= delta; } else { - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; } } - send_update_unread_chat_count(d->dialog_id, force_update, source); + 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) { @@ -8692,7 +9786,7 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId } if (!d->pending_new_message_notifications.empty()) { for (auto &it : d->pending_new_message_notifications) { - if (it.second.get() <= message_id.get()) { + if (it.second <= message_id) { it.first = DialogId(); } } @@ -8710,7 +9804,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.get() <= d->last_read_inbox_message_id.get()) { + 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); } @@ -8720,6 +9814,8 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId } void MessagesManager::set_dialog_last_read_outbox_message_id(Dialog *d, MessageId message_id) { + CHECK(!message_id.is_scheduled()); + if (td_->auth_manager_->is_bot()) { return; } @@ -8734,13 +9830,15 @@ void MessagesManager::set_dialog_last_read_outbox_message_id(Dialog *d, MessageI void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, MessageId max_unavailable_message_id, bool from_update, const char *source) { + CHECK(!max_unavailable_message_id.is_scheduled()); + Dialog *d = get_dialog_force(dialog_id); if (d != nullptr) { - if (max_unavailable_message_id.get() > d->last_new_message_id.get() && from_update) { + if (max_unavailable_message_id > d->last_new_message_id && from_update) { if (d->last_new_message_id.is_valid()) { if (!td_->auth_manager_->is_bot()) { - LOG(ERROR) << "Tried to set " << dialog_id << " max unavailable message id to " << max_unavailable_message_id - << " from " << source << ", but last new message id is " << d->last_new_message_id; + LOG(ERROR) << "Tried to set " << dialog_id << " max unavailable message to " << max_unavailable_message_id + << " from " << source << ", but last new message is " << d->last_new_message_id; } max_unavailable_message_id = d->last_new_message_id; } else if (max_unavailable_message_id.is_valid() && max_unavailable_message_id.is_server()) { @@ -8757,12 +9855,12 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, << " from " << source; return; } - LOG(INFO) << "Set max unavailable message id to " << max_unavailable_message_id << " in " << dialog_id << " from " + LOG(INFO) << "Set max unavailable message to " << max_unavailable_message_id << " in " << dialog_id << " from " << source; on_dialog_updated(dialog_id, "set_dialog_max_unavailable_message_id"); - if (d->max_unavailable_message_id.get() > max_unavailable_message_id.get()) { + if (d->max_unavailable_message_id > max_unavailable_message_id) { d->max_unavailable_message_id = max_unavailable_message_id; return; } @@ -8770,7 +9868,7 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, d->max_unavailable_message_id = max_unavailable_message_id; vector message_ids; - find_old_messages(d->messages, max_unavailable_message_id, message_ids); + find_old_messages(d->messages.get(), max_unavailable_message_id, message_ids); vector deleted_message_ids; bool need_update_dialog_pos = false; @@ -8781,12 +9879,12 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, auto m = get_message(d, message_id); CHECK(m != nullptr); - CHECK(m->message_id.get() <= max_unavailable_message_id.get()); + CHECK(m->message_id <= max_unavailable_message_id); CHECK(m->message_id == message_id); - deleted_message_ids.push_back(message_id.get()); auto p = delete_message(d, message_id, !from_update, &need_update_dialog_pos, "set_dialog_max_unavailable_message_id"); CHECK(p.get() == m); + deleted_message_ids.push_back(p->message_id.get()); } if (need_update_dialog_pos) { @@ -8799,7 +9897,7 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id, read_history_inbox(dialog_id, max_unavailable_message_id, -1, "set_dialog_max_unavailable_message_id"); } } else { - LOG(INFO) << "Receive max unavailable message identifier in unknown " << dialog_id << " from " << source; + LOG(INFO) << "Receive max unavailable message in unknown " << dialog_id << " from " << source; } } @@ -8865,20 +9963,21 @@ void MessagesManager::on_update_dialog_online_member_count_timeout(DialogId dial } } -MessageId MessagesManager::get_message_id(const tl_object_ptr &message_ptr) { - int32 constructor_id = message_ptr->get_id(); - switch (constructor_id) { +MessageId MessagesManager::get_message_id(const tl_object_ptr &message_ptr, bool is_scheduled) { + switch (message_ptr->get_id()) { case telegram_api::messageEmpty::ID: { auto message = static_cast(message_ptr.get()); - return MessageId(ServerMessageId(message->id_)); + return is_scheduled ? MessageId() : MessageId(ServerMessageId(message->id_)); } case telegram_api::message::ID: { auto message = static_cast(message_ptr.get()); - return MessageId(ServerMessageId(message->id_)); + return is_scheduled ? MessageId(ScheduledServerMessageId(message->id_), message->date_) + : MessageId(ServerMessageId(message->id_)); } case telegram_api::messageService::ID: { auto message = static_cast(message_ptr.get()); - return MessageId(ServerMessageId(message->id_)); + return is_scheduled ? MessageId(ScheduledServerMessageId(message->id_), message->date_) + : MessageId(ServerMessageId(message->id_)); } default: UNREACHABLE(); @@ -8887,25 +9986,14 @@ MessageId MessagesManager::get_message_id(const tl_object_ptr &message_ptr) const { - return get_full_message_id(message_ptr).get_dialog_id(); -} - -FullMessageId MessagesManager::get_full_message_id(const tl_object_ptr &message_ptr) const { - int32 constructor_id = message_ptr->get_id(); DialogId dialog_id; - MessageId message_id; UserId sender_user_id; - switch (constructor_id) { - case telegram_api::messageEmpty::ID: { - auto message = static_cast(message_ptr.get()); - LOG(INFO) << "Receive MessageEmpty"; - message_id = MessageId(ServerMessageId(message->id_)); - break; - } + switch (message_ptr->get_id()) { + case telegram_api::messageEmpty::ID: + return DialogId(); case telegram_api::message::ID: { auto message = static_cast(message_ptr.get()); dialog_id = DialogId(message->to_id_); - message_id = MessageId(ServerMessageId(message->id_)); if (message->flags_ & MESSAGE_FLAG_HAS_FROM_ID) { sender_user_id = UserId(message->from_id_); } @@ -8914,7 +10002,6 @@ FullMessageId MessagesManager::get_full_message_id(const tl_object_ptr(message_ptr.get()); dialog_id = DialogId(message->to_id_); - message_id = MessageId(ServerMessageId(message->id_)); if (message->flags_ & MESSAGE_FLAG_HAS_FROM_ID) { sender_user_id = UserId(message->from_id_); } @@ -8927,14 +10014,18 @@ FullMessageId MessagesManager::get_full_message_id(const tl_object_ptr &message_ptr, + bool is_scheduled) const { + return {get_message_dialog_id(message_ptr), get_message_id(message_ptr, is_scheduled)}; } int32 MessagesManager::get_message_date(const tl_object_ptr &message_ptr) { - int32 constructor_id = message_ptr->get_id(); - switch (constructor_id) { + switch (message_ptr->get_id()) { case telegram_api::messageEmpty::ID: return 0; case telegram_api::message::ID: { @@ -8953,6 +10044,9 @@ int32 MessagesManager::get_message_date(const tl_object_ptrdialog_id, is_outgoing, @@ -8964,10 +10058,13 @@ void MessagesManager::ttl_read_history(Dialog *d, bool is_outgoing, MessageId fr void MessagesManager::ttl_read_history_impl(DialogId dialog_id, bool is_outgoing, MessageId from_message_id, MessageId till_message_id, double view_date) { + CHECK(!from_message_id.is_scheduled()); + CHECK(!till_message_id.is_scheduled()); + auto *d = get_dialog(dialog_id); CHECK(d != nullptr); auto now = Time::now(); - for (auto it = MessagesIterator(d, from_message_id); *it && (*it)->message_id.get() >= till_message_id.get(); --it) { + for (auto it = MessagesIterator(d, from_message_id); *it && (*it)->message_id >= till_message_id; --it) { auto *m = *it; if (m->is_outgoing == is_outgoing) { ttl_on_view(d, m, view_date, now); @@ -8976,8 +10073,8 @@ void MessagesManager::ttl_read_history_impl(DialogId dialog_id, bool is_outgoing } void MessagesManager::ttl_on_view(const Dialog *d, Message *m, double view_date, double now) { - if (m->ttl > 0 && m->ttl_expires_at == 0 && !m->message_id.is_yet_unsent() && !m->is_failed_to_send && - !m->is_content_secret) { + if (m->ttl > 0 && m->ttl_expires_at == 0 && !m->message_id.is_scheduled() && !m->message_id.is_yet_unsent() && + !m->is_failed_to_send && !m->is_content_secret) { m->ttl_expires_at = m->ttl + view_date; ttl_register_message(d->dialog_id, m, now); on_message_changed(d, m, true, "ttl_on_view"); @@ -8985,6 +10082,7 @@ void MessagesManager::ttl_on_view(const Dialog *d, Message *m, double view_date, } bool MessagesManager::ttl_on_open(Dialog *d, Message *m, double now, bool is_local_read) { + CHECK(!m->message_id.is_scheduled()); if (m->ttl > 0 && m->ttl_expires_at == 0) { if (!is_local_read && d->dialog_id.get_type() != DialogType::SecretChat) { on_message_ttl_expired(d, m); @@ -9001,6 +10099,8 @@ void MessagesManager::ttl_register_message(DialogId dialog_id, const Message *m, if (m->ttl_expires_at == 0) { return; } + CHECK(!m->message_id.is_scheduled()); + auto it_flag = ttl_nodes_.insert(TtlNode(dialog_id, m->message_id)); CHECK(it_flag.second); auto it = it_flag.first; @@ -9013,6 +10113,7 @@ void MessagesManager::ttl_unregister_message(DialogId dialog_id, const Message * if (m->ttl_expires_at == 0) { return; } + CHECK(!m->message_id.is_scheduled()); TtlNode ttl_node(dialog_id, m->message_id); auto it = ttl_nodes_.find(ttl_node); @@ -9080,6 +10181,7 @@ void MessagesManager::on_message_ttl_expired(Dialog *d, Message *m) { void MessagesManager::on_message_ttl_expired_impl(Dialog *d, Message *m) { CHECK(d != nullptr); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); CHECK(m->ttl > 0); CHECK(d->dialog_id.get_type() != DialogType::SecretChat); delete_message_files(d->dialog_id, m); @@ -9135,21 +10237,35 @@ void MessagesManager::init() { G()->shared_config().get_option_boolean("include_sponsored_chat_to_unread_count"); if (G()->parameters().use_message_db) { - auto last_database_server_dialog_date_string = G()->td_db()->get_binlog_pmc()->get("last_server_dialog_date"); - if (!last_database_server_dialog_date_string.empty()) { + // erase old keys + G()->td_db()->get_binlog_pmc()->erase("last_server_dialog_date"); + G()->td_db()->get_binlog_pmc()->erase("unread_message_count"); + G()->td_db()->get_binlog_pmc()->erase("unread_dialog_count"); + + auto last_database_server_dialog_dates = G()->td_db()->get_binlog_pmc()->prefix_get("last_server_dialog_date"); + for (auto &it : last_database_server_dialog_dates) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("last_server_dialog_date").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } + string order_str; string dialog_id_str; - std::tie(order_str, dialog_id_str) = split(last_database_server_dialog_date_string); + std::tie(order_str, dialog_id_str) = split(it.second); auto r_order = to_integer_safe(order_str); auto r_dialog_id = to_integer_safe(dialog_id_str); if (r_order.is_error() || r_dialog_id.is_error()) { - LOG(ERROR) << "Can't parse " << last_database_server_dialog_date_string; + LOG(ERROR) << "Can't parse " << it.second; } else { - last_database_server_dialog_date_ = DialogDate(r_order.ok(), DialogId(r_dialog_id.ok())); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.last_database_server_dialog_date_ = DialogDate(r_order.ok(), DialogId(r_dialog_id.ok())); + LOG(INFO) << "Loaded last_database_server_dialog_date_ " << list.last_database_server_dialog_date_ << " in " + << folder_id; } } - LOG(INFO) << "Load last_database_server_dialog_date_ = " << last_database_server_dialog_date_; auto sponsored_dialog_id_string = G()->td_db()->get_binlog_pmc()->get("sponsored_dialog_id"); if (sponsored_dialog_id_string.empty()) { @@ -9178,45 +10294,70 @@ void MessagesManager::init() { } } - auto unread_message_count_string = G()->td_db()->get_binlog_pmc()->get("unread_message_count"); - if (!unread_message_count_string.empty()) { + auto unread_message_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_message_count"); + for (auto &it : unread_message_counts) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("unread_message_count").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } string total_count; string muted_count; - std::tie(total_count, muted_count) = split(unread_message_count_string); + std::tie(total_count, muted_count) = split(it.second); auto r_total_count = to_integer_safe(total_count); auto r_muted_count = to_integer_safe(muted_count); if (r_total_count.is_error() || r_muted_count.is_error()) { - LOG(ERROR) << "Can't parse " << unread_message_count_string; + LOG(ERROR) << "Can't parse " << it.second; } else { - unread_message_total_count_ = r_total_count.ok(); - unread_message_muted_count_ = r_muted_count.ok(); - is_message_unread_count_inited_ = true; - send_update_unread_message_count(DialogId(), true, "load unread_message_count"); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.unread_message_total_count_ = r_total_count.ok(); + list.unread_message_muted_count_ = r_muted_count.ok(); + list.is_message_unread_count_inited_ = true; + send_update_unread_message_count(folder_id, DialogId(), true, "load unread_message_count"); } } - auto unread_dialog_count_string = G()->td_db()->get_binlog_pmc()->get("unread_dialog_count"); - if (!unread_dialog_count_string.empty()) { - auto counts = - transform(full_split(unread_dialog_count_string), [](Slice str) { return to_integer_safe(str); }); - if (counts.size() != 4 || std::any_of(counts.begin(), counts.end(), [](auto &c) { return c.is_error(); })) { - LOG(ERROR) << "Can't parse " << unread_dialog_count_string; + auto unread_dialog_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_dialog_count"); + for (auto &it : unread_dialog_counts) { + auto r_folder_id = to_integer_safe(Slice(it.first).substr(Slice("unread_dialog_count").size())); + if (r_folder_id.is_error()) { + LOG(ERROR) << "Can't parse folder ID from " << it.first; + continue; + } + + auto counts = transform(full_split(it.second), [](Slice str) { return to_integer_safe(str); }); + if ((counts.size() != 4 && counts.size() != 6) || + std::any_of(counts.begin(), counts.end(), [](auto &c) { return c.is_error(); })) { + LOG(ERROR) << "Can't parse " << it.second; } else { - unread_dialog_total_count_ = counts[0].ok(); - unread_dialog_muted_count_ = counts[1].ok(); - unread_dialog_marked_count_ = counts[2].ok(); - unread_dialog_muted_marked_count_ = counts[3].ok(); - is_dialog_unread_count_inited_ = true; - send_update_unread_chat_count(DialogId(), true, "load unread_dialog_count"); + FolderId folder_id(r_folder_id.ok()); + auto &list = get_dialog_list(folder_id); + list.unread_dialog_total_count_ = counts[0].ok(); + list.unread_dialog_muted_count_ = counts[1].ok(); + list.unread_dialog_marked_count_ = counts[2].ok(); + list.unread_dialog_muted_marked_count_ = counts[3].ok(); + if (counts.size() == 6) { + list.server_dialog_total_count_ = counts[4].ok(); + list.secret_chat_total_count_ = counts[5].ok(); + } + if (list.server_dialog_total_count_ == -1) { + repair_server_dialog_total_count(folder_id); + } + if (list.secret_chat_total_count_ == -1) { + repair_secret_chat_total_count(folder_id); + } + list.is_dialog_unread_count_inited_ = true; + send_update_unread_chat_count(folder_id, DialogId(), true, "load unread_dialog_count"); } } ttl_db_loop_start(G()->server_time()); } else { - G()->td_db()->get_binlog_pmc()->erase("last_server_dialog_date"); - G()->td_db()->get_binlog_pmc()->erase("unread_message_count"); - G()->td_db()->get_binlog_pmc()->erase("unread_dialog_count"); + G()->td_db()->get_binlog_pmc()->erase_by_prefix("last_server_dialog_date"); + G()->td_db()->get_binlog_pmc()->erase_by_prefix("unread_message_count"); + G()->td_db()->get_binlog_pmc()->erase_by_prefix("unread_dialog_count"); G()->td_db()->get_binlog_pmc()->erase("promoted_dialog_id"); G()->td_db()->get_binlog_pmc()->erase("sponsored_dialog_id"); } @@ -9319,7 +10460,7 @@ void MessagesManager::init() { } log_string.remove_prefix(log_string.find(' ') + 1); - auto message_id = MessageId((static_cast(server_message_id) << MessageId::SERVER_ID_SHIFT) + add); + auto message_id = MessageId(MessageId(ServerMessageId(server_message_id)).get() + add); auto content_type = log_string.substr(0, log_string.find(' ')); log_string.remove_prefix(log_string.find(' ') + 1); @@ -9343,8 +10484,7 @@ void MessagesManager::init() { if (op == "MessageOpAdd") { auto m = make_unique(); - m->random_y = get_random_y(message_id); - m->message_id = message_id; + set_message_id(m, message_id); m->date = G()->unix_time(); m->content = create_text_message_content("text", {}, {}); @@ -9449,7 +10589,7 @@ void MessagesManager::ttl_db_on_result(Resultserver_time()); } @@ -9560,6 +10700,7 @@ void MessagesManager::delete_secret_chat_history(SecretChatId secret_chat_id, Me Promise<> promise) { LOG(DEBUG) << "On delete history in " << secret_chat_id << " up to " << last_message_id; CHECK(secret_chat_id.is_valid()); + CHECK(!last_message_id.is_scheduled()); DialogId dialog_id(secret_chat_id); if (!have_dialog_force(dialog_id)) { @@ -9646,7 +10787,7 @@ void MessagesManager::open_secret_message(SecretChatId secret_chat_id, int64 ran } Message *m = get_message(d, message_id); CHECK(m != nullptr); - if (message_id.is_yet_unsent() || m->is_failed_to_send || !m->is_outgoing) { + if (m->message_id.is_yet_unsent() || m->is_failed_to_send || !m->is_outgoing) { LOG(ERROR) << "Peer has opened wrong " << message_id << " in " << dialog_id; return; } @@ -9875,25 +11016,25 @@ void MessagesManager::fix_message_info_dialog_id(MessageInfo &message_info) cons } message_info.dialog_id = DialogId(sender_user_id); - LOG_IF(ERROR, (message_info.flags & MESSAGE_FLAG_IS_OUT) != 0) + LOG_IF(ERROR, !message_info.message_id.is_scheduled() && (message_info.flags & MESSAGE_FLAG_IS_OUT) != 0) << "Receive message out flag for incoming " << message_info.message_id << " in " << message_info.dialog_id; } MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( - tl_object_ptr message_ptr, const char *source) const { + tl_object_ptr message_ptr, bool is_scheduled, const char *source) const { LOG(DEBUG) << "Receive from " << source << " " << to_string(message_ptr); LOG_CHECK(message_ptr != nullptr) << source; - int32 constructor_id = message_ptr->get_id(); MessageInfo message_info; - switch (constructor_id) { + message_info.message_id = get_message_id(message_ptr, is_scheduled); + switch (message_ptr->get_id()) { case telegram_api::messageEmpty::ID: + message_info.message_id = MessageId(); break; case telegram_api::message::ID: { auto message = move_tl_object_as(message_ptr); message_info.dialog_id = DialogId(message->to_id_); - message_info.message_id = MessageId(ServerMessageId(message->id_)); if (message->flags_ & MESSAGE_FLAG_HAS_FROM_ID) { message_info.sender_user_id = UserId(message->from_id_); } @@ -9923,15 +11064,21 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( if (is_message_auto_read(message_info.dialog_id, (message->flags_ & MESSAGE_FLAG_IS_OUT) != 0)) { is_content_read = true; } + if (is_scheduled) { + is_content_read = false; + } + auto new_source = PSTRING() << FullMessageId(message_info.dialog_id, message_info.message_id) << " from " + << source; message_info.content = get_message_content( 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, - "parse_telegram_api_message"), + 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 = message->flags_ & MESSAGE_FLAG_HAS_REPLY_MARKUP ? std::move(message->reply_markup_) : nullptr; + message_info.restriction_reasons = get_restriction_reasons(std::move(message->restriction_reason_)); message_info.author_signature = std::move(message->post_author_); break; } @@ -9939,7 +11086,6 @@ MessagesManager::MessageInfo MessagesManager::parse_telegram_api_message( auto message = move_tl_object_as(message_ptr); message_info.dialog_id = DialogId(message->to_id_); - message_info.message_id = MessageId(ServerMessageId(message->id_)); if (message->flags_ & MESSAGE_FLAG_HAS_FROM_ID) { message_info.sender_user_id = UserId(message->from_id_); } @@ -9963,13 +11109,13 @@ std::pair> MessagesManager::creat bool is_channel_message) { DialogId dialog_id = message_info.dialog_id; MessageId message_id = message_info.message_id; - if (!message_id.is_valid() || !dialog_id.is_valid()) { + if ((!message_id.is_valid() && !message_id.is_valid_scheduled()) || !dialog_id.is_valid()) { if (message_id != MessageId() || dialog_id != DialogId()) { LOG(ERROR) << "Receive " << message_id << " in " << dialog_id; } return {DialogId(), nullptr}; } - if (message_id.is_yet_unsent()) { + if (message_id.is_yet_unsent() || message_id.is_local()) { LOG(ERROR) << "Receive " << message_id; return {DialogId(), nullptr}; } @@ -9986,6 +11132,9 @@ std::pair> MessagesManager::creat sender_user_id = UserId(); } } + if (message_id.is_scheduled()) { + is_channel_message = (dialog_type == DialogType::Channel); + } int32 flags = message_info.flags; if (flags & @@ -9993,7 +11142,8 @@ std::pair> MessagesManager::creat MESSAGE_FLAG_HAS_UNREAD_CONTENT | MESSAGE_FLAG_HAS_REPLY_MARKUP | MESSAGE_FLAG_HAS_ENTITIES | MESSAGE_FLAG_HAS_FROM_ID | MESSAGE_FLAG_HAS_MEDIA | MESSAGE_FLAG_HAS_VIEWS | MESSAGE_FLAG_IS_SENT_VIA_BOT | MESSAGE_FLAG_IS_SILENT | MESSAGE_FLAG_IS_POST | MESSAGE_FLAG_HAS_EDIT_DATE | MESSAGE_FLAG_HAS_AUTHOR_SIGNATURE | - MESSAGE_FLAG_HAS_MEDIA_ALBUM_ID | MESSAGE_FLAG_IS_LEGACY)) { + MESSAGE_FLAG_HAS_MEDIA_ALBUM_ID | MESSAGE_FLAG_IS_FROM_SCHEDULED | MESSAGE_FLAG_IS_LEGACY | + MESSAGE_FLAG_HIDE_EDIT_DATE | MESSAGE_FLAG_IS_RESTRICTED)) { LOG(ERROR) << "Unsupported message flags = " << flags << " received"; } @@ -10001,9 +11151,11 @@ std::pair> MessagesManager::creat bool is_silent = (flags & MESSAGE_FLAG_IS_SILENT) != 0; bool is_channel_post = (flags & MESSAGE_FLAG_IS_POST) != 0; bool is_legacy = (flags & MESSAGE_FLAG_IS_LEGACY) != 0; + bool hide_edit_date = (flags & MESSAGE_FLAG_HIDE_EDIT_DATE) != 0; + bool is_from_scheduled = (flags & MESSAGE_FLAG_IS_FROM_SCHEDULED) != 0; - LOG_IF(ERROR, is_channel_message && dialog_type != DialogType::Channel) - << "is_channel_message is true for message received in the " << dialog_id; + LOG_IF(ERROR, is_channel_message != (dialog_type == DialogType::Channel)) + << "is_channel_message is wrong for message received in the " << dialog_id; LOG_IF(ERROR, is_channel_post && !is_broadcast_channel(dialog_id)) << "is_channel_post is true for message received in the " << dialog_id; @@ -10014,10 +11166,11 @@ std::pair> MessagesManager::creat CHECK(sender_user_id == my_id); } - if (sender_user_id.is_valid() && (sender_user_id == my_id && dialog_id != my_dialog_id) != is_outgoing) { + bool supposed_to_be_outgoing = sender_user_id == my_id && !(dialog_id == my_dialog_id && !message_id.is_scheduled()); + if (sender_user_id.is_valid() && supposed_to_be_outgoing != is_outgoing) { LOG(ERROR) << "Receive wrong message out flag: me is " << my_id << ", message is from " << sender_user_id << ", flags = " << flags << " for " << message_id << " in " << dialog_id; - is_outgoing = !is_outgoing; + is_outgoing = supposed_to_be_outgoing; if (dialog_type == DialogType::Channel && !running_get_difference_ && !running_get_channel_difference(dialog_id) && get_channel_difference_to_logevent_id_.count(dialog_id) == 0) { @@ -10031,9 +11184,11 @@ std::pair> MessagesManager::creat } MessageId reply_to_message_id = message_info.reply_to_message_id; - if (reply_to_message_id != MessageId() && - (!reply_to_message_id.is_valid() || reply_to_message_id.get() >= message_id.get())) { - if (!reply_to_message_id.is_valid() || reply_to_message_id.get() - message_id.get() <= 2000000000) { + CHECK(!reply_to_message_id.is_scheduled()); + if (!message_id.is_scheduled() && reply_to_message_id != MessageId() && + (!reply_to_message_id.is_valid() || reply_to_message_id >= message_id)) { + if (!reply_to_message_id.is_valid() || + reply_to_message_id.get() - message_id.get() <= MessageId(ServerMessageId(2000000000)).get()) { LOG(ERROR) << "Receive reply to wrong " << reply_to_message_id << " in " << message_id; } reply_to_message_id = MessageId(); @@ -10056,6 +11211,10 @@ std::pair> MessagesManager::creat edit_date = 0; } + if (hide_edit_date && td_->auth_manager_->is_bot()) { + 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 @@ -10077,8 +11236,7 @@ std::pair> MessagesManager::creat LOG(INFO) << "Receive " << message_id << " in " << dialog_id << " from " << sender_user_id; auto message = make_unique(); - message->random_y = get_random_y(message_id); - message->message_id = message_id; + set_message_id(message, message_id); message->sender_user_id = sender_user_id; message->date = date; message->ttl = ttl; @@ -10087,6 +11245,7 @@ std::pair> MessagesManager::creat message->forward_info = get_message_forward_info(std::move(message_info.forward_header)); message->reply_to_message_id = reply_to_message_id; message->via_bot_user_id = via_bot_user_id; + message->restriction_reasons = std::move(message_info.restriction_reasons); message->author_signature = std::move(message_info.author_signature); message->is_outgoing = is_outgoing; message->is_channel_post = is_channel_post; @@ -10094,10 +11253,13 @@ std::pair> MessagesManager::creat !is_outgoing && dialog_type != DialogType::User && ((flags & MESSAGE_FLAG_HAS_MENTION) != 0 || content_type == MessageContentType::PinMessage); message->contains_unread_mention = - message_id.is_server() && message->contains_mention && (flags & MESSAGE_FLAG_HAS_UNREAD_CONTENT) != 0 && + !message_id.is_scheduled() && message_id.is_server() && message->contains_mention && + (flags & MESSAGE_FLAG_HAS_UNREAD_CONTENT) != 0 && (dialog_type == DialogType::Chat || (dialog_type == DialogType::Channel && !is_broadcast_channel(dialog_id))); message->disable_notification = is_silent; message->is_content_secret = is_content_secret; + message->hide_edit_date = hide_edit_date; + message->is_from_scheduled = is_from_scheduled; message->views = views; message->legacy_layer = (is_legacy ? MTPROTO_LAYER : 0); message->content = std::move(message_info.content); @@ -10133,11 +11295,31 @@ std::pair> MessagesManager::creat return {dialog_id, std::move(message)}; } +MessageId MessagesManager::find_old_message_id(DialogId dialog_id, MessageId message_id) const { + if (message_id.is_scheduled()) { + CHECK(message_id.is_scheduled_server()); + auto dialog_it = update_scheduled_message_ids_.find(dialog_id); + if (dialog_it != update_scheduled_message_ids_.end()) { + auto it = dialog_it->second.find(message_id.get_scheduled_server_message_id()); + if (it != dialog_it->second.end()) { + return it->second; + } + } + } else { + CHECK(message_id.is_server()); + auto it = update_message_ids_.find(FullMessageId(dialog_id, message_id)); + if (it != update_message_ids_.end()) { + return it->second; + } + } + return MessageId(); +} + FullMessageId MessagesManager::on_get_message(tl_object_ptr message_ptr, bool from_update, - bool is_channel_message, bool have_previous, bool have_next, - const char *source) { - return on_get_message(parse_telegram_api_message(std::move(message_ptr), source), from_update, is_channel_message, - have_previous, have_next, source); + bool is_channel_message, bool is_scheduled, bool have_previous, + bool have_next, const char *source) { + return on_get_message(parse_telegram_api_message(std::move(message_ptr), is_scheduled, source), from_update, + is_channel_message, have_previous, have_next, source); } FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message, @@ -10153,30 +11335,31 @@ 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; + bool need_update = from_update || message_id.is_scheduled(); bool need_update_dialog_pos = false; - FullMessageId full_message_id(dialog_id, message_id); - auto it = update_message_ids_.find(full_message_id); - if (it != update_message_ids_.end()) { + 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}; + if (old_message_id.is_valid() || old_message_id.is_valid_scheduled()) { Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - if (!from_update) { - if (message_id.get() <= d->last_new_message_id.get()) { + if (!from_update && !message_id.is_scheduled()) { + if (message_id <= d->last_new_message_id) { if (get_message_force(d, message_id, "receive missed unsent message not from update") != nullptr) { - LOG(ERROR) << "New " << it->second << "/" << message_id << " in " << dialog_id << " from " << source + LOG(ERROR) << "New " << old_message_id << "/" << message_id << " in " << dialog_id << " from " << source << " has id less than last_new_message_id = " << d->last_new_message_id; return FullMessageId(); } // if there is no message yet, then it is likely was missed because of a server bug and is being repaired via // get_message_from_server from after_get_difference // TODO move to INFO - LOG(ERROR) << "Receive " << it->second << "/" << message_id << " in " << dialog_id << " from " << source + LOG(ERROR) << "Receive " << old_message_id << "/" << message_id << " in " << dialog_id << " from " << source << " with id less than last_new_message_id = " << d->last_new_message_id << " and trying to add it anyway"; } else { - LOG(ERROR) << "Ignore " << it->second << "/" << message_id << " received not through update from " << source + LOG(ERROR) << "Ignore " << old_message_id << "/" << message_id << " received not through update from " << source << ": " << oneline(to_string(get_message_object(dialog_id, new_message.get()))); // TODO move to INFO dump_debug_message_op(d, 3); // TODO remove @@ -10187,9 +11370,16 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f } } - MessageId old_message_id = it->second; - - update_message_ids_.erase(it); + if (message_id.is_scheduled()) { + auto dialog_it = update_scheduled_message_ids_.find(dialog_id); + CHECK(dialog_it != update_scheduled_message_ids_.end()); + dialog_it->second.erase(message_id.get_scheduled_server_message_id()); + if (dialog_it->second.empty()) { + update_scheduled_message_ids_.erase(dialog_it); + } + } else { + update_message_ids_.erase(FullMessageId(dialog_id, message_id)); + } if (!new_message->is_outgoing && dialog_id != get_my_dialog_id()) { // sent message is not from me @@ -10203,27 +11393,28 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f // 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->message_id << " from server"; - delete_messages_from_server(dialog_id, {new_message->message_id}, true, 0, Auto()); + delete_message_from_server(dialog_id, new_message->message_id, true); return FullMessageId(); } need_update = false; - new_message->message_id = old_message_id; - new_message->random_y = get_random_y(new_message->message_id); + set_message_id(new_message, old_message_id); + new_message->from_database = false; new_message->have_previous = false; new_message->have_next = false; - update_message(d, old_message, std::move(new_message), &need_update_dialog_pos); + update_message(d, old_message.get(), std::move(new_message), &need_update_dialog_pos); new_message = std::move(old_message); - new_message->message_id = message_id; - new_message->random_y = get_random_y(new_message->message_id); + set_message_id(new_message, message_id); send_update_message_send_succeeded(d, old_message_id, new_message.get()); - try_add_active_live_location(dialog_id, new_message.get()); + if (!message_id.is_scheduled()) { + need_add_active_live_location = true; - // add_message_to_dialog will not update counts, because need_update == false - update_message_count_by_index(d, +1, new_message.get()); + // add_message_to_dialog will not update counts, because need_update == false + update_message_count_by_index(d, +1, new_message.get()); + } if (!from_update) { new_message->have_previous = have_previous; @@ -10247,6 +11438,10 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f CHECK(d != nullptr); + if (need_add_active_live_location) { + try_add_active_live_location(dialog_id, m); + } + auto pcc_it = pending_created_dialogs_.find(dialog_id); if (from_update && pcc_it != pending_created_dialogs_.end()) { pcc_it->second.set_value(Unit()); @@ -10262,17 +11457,21 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f auto p = delete_message(d, message_id, false, &need_update_dialog_pos, "get a message in inaccessible chat"); CHECK(p.get() == m); // CHECK(d->messages == nullptr); - send_update_delete_messages(dialog_id, {message_id.get()}, false, false); + send_update_delete_messages(dialog_id, {p->message_id.get()}, false, false); // don't need to update dialog pos return FullMessageId(); } + send_update_chat_has_scheduled_messages(d); + if (need_update_dialog_pos) { send_update_chat_last_message(d, "on_get_message"); } - if (need_update && m->reply_markup != nullptr && m->reply_markup->type != ReplyMarkup::Type::InlineKeyboard && - m->reply_markup->is_personal && !td_->auth_manager_->is_bot()) { + // set dialog reply markup only after updateNewMessage and updateChatLastMessage are sent + if (need_update && m->reply_markup != nullptr && !m->message_id.is_scheduled() && + m->reply_markup->type != ReplyMarkup::Type::InlineKeyboard && m->reply_markup->is_personal && + !td_->auth_manager_->is_bot()) { set_dialog_reply_markup(d, message_id); } @@ -10280,6 +11479,8 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f } void MessagesManager::set_dialog_last_message_id(Dialog *d, MessageId last_message_id, const char *source) { + CHECK(!last_message_id.is_scheduled()); + LOG(INFO) << "Set " << d->dialog_id << " last message to " << last_message_id << " from " << source; d->last_message_id = last_message_id; @@ -10301,6 +11502,8 @@ void MessagesManager::set_dialog_last_message_id(Dialog *d, MessageId last_messa void MessagesManager::set_dialog_first_database_message_id(Dialog *d, MessageId first_database_message_id, const char *source) { + CHECK(!first_database_message_id.is_scheduled()); + LOG(INFO) << "Set " << d->dialog_id << " first database message to " << first_database_message_id << " from " << source; d->first_database_message_id = first_database_message_id; @@ -10309,6 +11512,8 @@ void MessagesManager::set_dialog_first_database_message_id(Dialog *d, MessageId void MessagesManager::set_dialog_last_database_message_id(Dialog *d, MessageId last_database_message_id, const char *source, bool is_loaded_from_database) { + CHECK(!last_database_message_id.is_scheduled()); + LOG(INFO) << "Set " << d->dialog_id << " last database message to " << last_database_message_id << " from " << source; d->debug_set_dialog_last_database_message_id = source; d->last_database_message_id = last_database_message_id; @@ -10318,7 +11523,9 @@ void MessagesManager::set_dialog_last_database_message_id(Dialog *d, MessageId l } void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_new_message_id, const char *source) { - LOG_CHECK(last_new_message_id.get() > d->last_new_message_id.get()) + CHECK(!last_new_message_id.is_scheduled()); + + LOG_CHECK(last_new_message_id > d->last_new_message_id) << last_new_message_id << " " << d->last_new_message_id << " " << source; CHECK(d->dialog_id.get_type() == DialogType::SecretChat || last_new_message_id.is_server()); if (!d->last_new_message_id.is_valid()) { @@ -10336,7 +11543,7 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n vector to_delete_message_ids; while (*it != nullptr) { auto message_id = (*it)->message_id; - if (message_id.get() <= last_new_message_id.get()) { + if (message_id <= last_new_message_id) { break; } if (!message_id.is_yet_unsent()) { @@ -10351,9 +11558,9 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n vector deleted_message_ids; bool need_update_dialog_pos = false; for (auto message_id : to_delete_message_ids) { - if (delete_message(d, message_id, false, &need_update_dialog_pos, "set_dialog_last_new_message_id") != - nullptr) { - deleted_message_ids.push_back(message_id.get()); + auto message = delete_message(d, message_id, false, &need_update_dialog_pos, "set_dialog_last_new_message_id"); + if (message != nullptr) { + deleted_message_ids.push_back(message->message_id.get()); } } if (need_update_dialog_pos) { @@ -10378,6 +11585,8 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n void MessagesManager::set_dialog_last_clear_history_date(Dialog *d, int32 date, MessageId last_clear_history_message_id, const char *source, bool is_loaded_from_database) { + CHECK(!last_clear_history_message_id.is_scheduled()); + LOG(INFO) << "Set " << d->dialog_id << " last clear history date to " << date << " of " << last_clear_history_message_id << " from " << source; if (d->last_clear_history_message_id.is_valid()) { @@ -10469,6 +11678,10 @@ void MessagesManager::set_dialog_is_empty(Dialog *d, const char *source) { void MessagesManager::set_dialog_is_pinned(DialogId dialog_id, bool is_pinned) { Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + 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"); } @@ -10483,15 +11696,15 @@ void MessagesManager::set_dialog_is_pinned(Dialog *d, bool is_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); - DialogDate dialog_date(d->order, d->dialog_id); send_closure(G()->td(), &Td::send_update, - make_tl_object(d->dialog_id.get(), is_pinned, - dialog_date <= last_dialog_date_ ? d->order : 0)); + make_tl_object(d->dialog_id.get(), is_pinned, get_dialog_public_order(d))); } - // there is no need to call update_dialog_pos otherwise, it will be called by the caller + // there is no need to call update_dialog_pos, it will be called by the caller } void MessagesManager::set_dialog_reply_markup(Dialog *d, MessageId message_id) { + CHECK(!message_id.is_scheduled()); + if (d->reply_markup_message_id != message_id) { on_dialog_updated(d->dialog_id, "set_dialog_reply_markup"); } @@ -10511,6 +11724,7 @@ void MessagesManager::try_restore_dialog_reply_markup(Dialog *d, const Message * return; } + CHECK(!m->message_id.is_scheduled()); if (m->had_reply_markup) { LOG(INFO) << "Restore deleted reply markup in " << d->dialog_id; set_dialog_reply_markup(d, MessageId()); @@ -10523,6 +11737,7 @@ void MessagesManager::try_restore_dialog_reply_markup(Dialog *d, const Message * void MessagesManager::set_dialog_pinned_message_notification(Dialog *d, MessageId message_id) { CHECK(d != nullptr); + CHECK(!message_id.is_scheduled()); auto old_message_id = d->pinned_message_notification_message_id; if (old_message_id == message_id) { return; @@ -10564,11 +11779,12 @@ void MessagesManager::remove_dialog_mention_notifications(Dialog *d) { vector message_ids; std::unordered_set removed_notification_ids_set; - find_unread_mentions(d->messages, message_ids); + find_unread_mentions(d->messages.get(), message_ids); VLOG(notifications) << "Found unread mentions in " << message_ids; for (auto &message_id : message_ids) { auto m = get_message(d, message_id); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); if (m->notification_id.is_valid() && is_message_notification_active(d, m) && is_from_mention_notification_group(d, m)) { removed_notification_ids_set.insert(m->notification_id); @@ -10578,6 +11794,7 @@ void MessagesManager::remove_dialog_mention_notifications(Dialog *d) { message_ids = td_->notification_manager_->get_notification_group_message_ids(notification_group_id); VLOG(notifications) << "Found active mention notifications in " << message_ids; for (auto &message_id : message_ids) { + CHECK(!message_id.is_scheduled()); if (message_id != d->pinned_message_notification_message_id) { auto m = get_message_force(d, message_id, "remove_dialog_mention_notifications"); if (m != nullptr && m->notification_id.is_valid() && is_message_notification_active(d, m)) { @@ -10633,6 +11850,7 @@ void MessagesManager::on_update_sent_text_message(int64 random_id, // message has already been deleted return; } + full_message_id = FullMessageId(dialog_id, m->message_id); if (m->content->get_type() != MessageContentType::Text) { LOG(ERROR) << "Text message content has been already changed to " << m->content->get_type(); @@ -10678,8 +11896,7 @@ void MessagesManager::on_update_message_web_page(FullMessageId full_message_id, // dialog can be not yet added return; } - auto message_id = full_message_id.get_message_id(); - Message *m = get_message(d, message_id); + Message *m = get_message(d, full_message_id.get_message_id()); if (m == nullptr) { // message can be already deleted return; @@ -10703,37 +11920,64 @@ void MessagesManager::on_update_message_web_page(FullMessageId full_message_id, return; } - send_update_message_content(dialog_id, message_id, content, m->date, m->is_content_secret, + send_update_message_content(dialog_id, m->message_id, content, m->date, m->is_content_secret, "on_update_message_web_page"); } -void MessagesManager::on_get_dialogs(vector> &&dialogs, int32 total_count, - vector> &&messages, Promise &&promise) { +void MessagesManager::on_get_dialogs(FolderId folder_id, vector> &&dialog_folders, + int32 total_count, vector> &&messages, + Promise &&promise) { if (td_->updates_manager_->running_get_difference()) { LOG(INFO) << "Postpone result of getDialogs"; - pending_on_get_dialogs_.push_back( - PendingOnGetDialogs{std::move(dialogs), total_count, std::move(messages), std::move(promise)}); + pending_on_get_dialogs_.push_back(PendingOnGetDialogs{folder_id, std::move(dialog_folders), total_count, + std::move(messages), std::move(promise)}); return; } bool from_dialog_list = total_count >= 0; bool from_get_dialog = total_count == -1; bool from_pinned_dialog_list = total_count == -2; - if (from_get_dialog && dialogs.size() == 1) { - DialogId dialog_id(dialogs[0]->peer_); + if (from_get_dialog && dialog_folders.size() == 1 && dialog_folders[0]->get_id() == telegram_api::dialog::ID) { + DialogId dialog_id(static_cast(dialog_folders[0].get())->peer_); if (running_get_channel_difference(dialog_id)) { LOG(INFO) << "Postpone result of channels getDialogs for " << dialog_id; pending_channel_on_get_dialogs_.emplace( - dialog_id, PendingOnGetDialogs{std::move(dialogs), total_count, std::move(messages), std::move(promise)}); + dialog_id, PendingOnGetDialogs{folder_id, std::move(dialog_folders), total_count, std::move(messages), + std::move(promise)}); return; } } + vector> dialogs; + for (auto &dialog_folder : dialog_folders) { + switch (dialog_folder->get_id()) { + case telegram_api::dialog::ID: + dialogs.push_back(telegram_api::move_object_as(dialog_folder)); + break; + case telegram_api::dialogFolder::ID: { + auto folder = telegram_api::move_object_as(dialog_folder); + if (from_pinned_dialog_list) { + // TODO update unread_muted_peers_count:int unread_unmuted_peers_count:int + // unread_muted_messages_count:int unread_unmuted_messages_count:int + FolderId folder_folder_id(folder->folder_->id_); + if (folder_folder_id == FolderId::archive()) { + // archive is expected in pinned dialogs list + break; + } + } + LOG(ERROR) << "Receive unexpected " << to_string(folder); + break; + } + default: + UNREACHABLE(); + } + } + LOG(INFO) << "Receive " << dialogs.size() << " dialogs out of " << total_count << " in result of GetDialogsQuery"; std::unordered_map full_message_id_to_dialog_date; std::unordered_map, FullMessageIdHash> full_message_id_to_message; for (auto &message : messages) { - auto full_message_id = get_full_message_id(message); + auto full_message_id = get_full_message_id(message, false); if (from_dialog_list) { auto message_date = get_message_date(message); int64 order = get_dialog_order(full_message_id.get_message_id(), message_date); @@ -10784,6 +12028,12 @@ void MessagesManager::on_get_dialogs(vector> LOG(ERROR) << "Last " << last_message_id << " in " << dialog_id << " not found"; return promise.set_error(Status::Error(500, "Wrong query result returned: last message not found")); } + FolderId dialog_folder_id((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0); + if (dialog_folder_id != folder_id) { + LOG(ERROR) << "Receive " << dialog_id << " in " << dialog_folder_id << " instead of " << folder_id; + continue; + } + DialogDate dialog_date = it->second; CHECK(dialog_date.get_dialog_id() == dialog_id); @@ -10797,23 +12047,32 @@ void MessagesManager::on_get_dialogs(vector> } } + auto &list = get_dialog_list(folder_id); if (from_dialog_list) { if (dialogs.empty()) { - // if there is no more dialogs on the server + // if there are no more dialogs on the server max_dialog_date = MAX_DIALOG_DATE; } - if (last_server_dialog_date_ < max_dialog_date) { - last_server_dialog_date_ = max_dialog_date; - update_last_dialog_date(); - } else { - LOG(ERROR) << "Last server dialog date didn't increased"; + if (list.last_server_dialog_date_ < max_dialog_date) { + list.last_server_dialog_date_ = max_dialog_date; + update_last_dialog_date(folder_id); + } else if (promise) { + LOG(ERROR) << "Last server dialog date didn't increased from " << list.last_server_dialog_date_ << " to " + << max_dialog_date << " after receiving " << dialogs.size() << " chats from " << total_count << " in " + << folder_id << ". Know about order of " << list.ordered_dialogs_.size() + << " chats, last_dialog_date = " << list.last_dialog_date_ + << ", last_loaded_database_dialog_date = " << list.last_loaded_database_dialog_date_; + } + if (total_count < narrow_cast(dialogs.size())) { + LOG(ERROR) << "Receive chat total_count = " << total_count << ", but " << dialogs.size() << " chats"; + total_count = narrow_cast(dialogs.size()); } } if (from_pinned_dialog_list) { max_dialog_date = DialogDate(get_dialog_order(MessageId(), MIN_PINNED_DIALOG_DATE - 1), DialogId()); - if (last_server_dialog_date_ < max_dialog_date) { - last_server_dialog_date_ = max_dialog_date; - update_last_dialog_date(); + if (list.last_server_dialog_date_ < max_dialog_date) { + list.last_server_dialog_date_ = max_dialog_date; + update_last_dialog_date(folder_id); } } @@ -10822,11 +12081,12 @@ void MessagesManager::on_get_dialogs(vector> MessageId last_message_id(ServerMessageId(dialog->top_message_)); if (!last_message_id.is_valid() && from_dialog_list) { // skip dialogs without messages + total_count--; continue; } DialogId dialog_id(dialog->peer_); - if (std::find(added_dialog_ids.begin(), added_dialog_ids.end(), dialog_id) != added_dialog_ids.end()) { + if (td::contains(added_dialog_ids, dialog_id)) { LOG(ERROR) << "Receive " << dialog_id << " twice in result of getChats with total_count = " << total_count; continue; } @@ -10843,6 +12103,8 @@ void MessagesManager::on_get_dialogs(vector> } bool is_new = d->last_new_message_id == MessageId(); + set_dialog_folder_id(d, FolderId((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0)); + on_update_dialog_notify_settings(dialog_id, std::move(dialog->notify_settings_), "on_get_dialogs"); if (!d->notification_settings.is_synchronized) { LOG(ERROR) << "Failed to synchronize settings in " << dialog_id; @@ -10886,11 +12148,10 @@ void MessagesManager::on_get_dialogs(vector> LOG(ERROR) << "Last " << full_message_id << " not found"; } else { auto added_full_message_id = - on_get_message(std::move(last_message), false, has_pts, false, false, "get chats"); + on_get_message(std::move(last_message), false, has_pts, false, false, false, "get chats"); CHECK(d->last_new_message_id == MessageId()); set_dialog_last_new_message_id(d, last_message_id, "on_get_dialogs"); - if (d->last_new_message_id.get() > d->last_message_id.get() && - added_full_message_id.get_message_id().is_valid()) { + if (d->last_new_message_id > d->last_message_id && added_full_message_id.get_message_id().is_valid()) { CHECK(added_full_message_id.get_message_id() == d->last_new_message_id); set_dialog_last_message_id(d, d->last_new_message_id, "on_get_dialogs"); send_update_chat_last_message(d, "on_get_dialogs"); @@ -10929,7 +12190,7 @@ void MessagesManager::on_get_dialogs(vector> on_dialog_updated(dialog_id, "repair dialog server unread count"); } if (d->server_unread_count != dialog->unread_count_ || - d->last_read_inbox_message_id.get() < read_inbox_max_message_id.get()) { + 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"); } @@ -10940,7 +12201,7 @@ void MessagesManager::on_get_dialogs(vector> } if (!G()->parameters().use_message_db || is_new || !d->is_last_read_outbox_message_id_inited) { - if (d->last_read_outbox_message_id.get() < read_outbox_max_message_id.get()) { + if (d->last_read_outbox_message_id < read_outbox_max_message_id) { set_dialog_last_read_outbox_message_id(d, read_outbox_max_message_id); } if (!d->is_last_read_outbox_message_id_inited) { @@ -10960,8 +12221,18 @@ void MessagesManager::on_get_dialogs(vector> } being_added_dialog_id_ = DialogId(); } + if (from_dialog_list) { + CHECK(total_count >= 0); + if (list.server_dialog_total_count_ != total_count) { + auto old_dialog_total_count = get_dialog_total_count(list); + list.server_dialog_total_count_ = total_count; + if (list.is_dialog_unread_count_inited_ && old_dialog_total_count != get_dialog_total_count(list)) { + send_update_unread_chat_count(folder_id, DialogId(), true, "on_get_dialogs"); + } + } + } if (from_pinned_dialog_list) { - auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialogs()); + auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialogs(folder_id)); std::reverse(pinned_dialog_ids.begin(), pinned_dialog_ids.end()); if (pinned_dialog_ids != added_dialog_ids) { LOG(INFO) << "Repair pinned dialogs order from " << format::as_array(pinned_dialog_ids) << " to " @@ -11039,6 +12310,9 @@ bool MessagesManager::is_message_unload_enabled() const { } bool MessagesManager::can_unload_message(const Dialog *d, const Message *m) const { + CHECK(d != nullptr); + CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); // don't want to unload messages from opened dialogs // don't want to unload messages to which there are replies in yet unsent messages // don't want to unload messages with pending web pages @@ -11055,6 +12329,8 @@ bool MessagesManager::can_unload_message(const Dialog *d, const Message *m) cons } void MessagesManager::unload_message(Dialog *d, MessageId message_id) { + CHECK(d != nullptr); + CHECK(message_id.is_valid()); bool need_update_dialog_pos = false; auto m = do_delete_message(d, message_id, false, true, &need_update_dialog_pos, "unload_message"); CHECK(!need_update_dialog_pos); @@ -11069,8 +12345,9 @@ unique_ptr MessagesManager::delete_message(Dialog *d, void MessagesManager::add_random_id_to_message_id_correspondence(Dialog *d, int64 random_id, MessageId message_id) { CHECK(d != nullptr); CHECK(d->dialog_id.get_type() == DialogType::SecretChat); + CHECK(message_id.is_valid()); auto it = d->random_id_to_message_id.find(random_id); - if (it == d->random_id_to_message_id.end() || it->second.get() < message_id.get()) { + if (it == d->random_id_to_message_id.end() || it->second < message_id) { LOG(INFO) << "Add correspondence from random_id " << random_id << " to " << message_id << " in " << d->dialog_id; d->random_id_to_message_id[random_id] = message_id; } @@ -11079,6 +12356,7 @@ void MessagesManager::add_random_id_to_message_id_correspondence(Dialog *d, int6 void MessagesManager::delete_random_id_to_message_id_correspondence(Dialog *d, int64 random_id, MessageId message_id) { CHECK(d != nullptr); CHECK(d->dialog_id.get_type() == DialogType::SecretChat); + CHECK(message_id.is_valid()); auto it = d->random_id_to_message_id.find(random_id); if (it != d->random_id_to_message_id.end() && it->second == message_id) { LOG(INFO) << "Delete correspondence from random_id " << random_id << " to " << message_id << " in " << d->dialog_id; @@ -11089,15 +12367,17 @@ void MessagesManager::delete_random_id_to_message_id_correspondence(Dialog *d, i void MessagesManager::add_notification_id_to_message_id_correspondence(Dialog *d, NotificationId notification_id, MessageId message_id) { CHECK(d != nullptr); + CHECK(notification_id.is_valid()); + CHECK(message_id.is_valid()); auto it = d->notification_id_to_message_id.find(notification_id); if (it == d->notification_id_to_message_id.end()) { VLOG(notifications) << "Add correspondence from " << notification_id << " to " << message_id << " in " << d->dialog_id; d->notification_id_to_message_id.emplace(notification_id, message_id); - } else if (it->second.get() != message_id.get()) { + } else if (it->second != message_id) { LOG(ERROR) << "Have duplicated " << notification_id << " in " << d->dialog_id << " in " << message_id << " and " << it->second; - if (it->second.get() < message_id.get()) { + if (it->second < message_id) { it->second = message_id; } } @@ -11106,6 +12386,8 @@ void MessagesManager::add_notification_id_to_message_id_correspondence(Dialog *d void MessagesManager::delete_notification_id_to_message_id_correspondence(Dialog *d, NotificationId notification_id, MessageId message_id) { CHECK(d != nullptr); + CHECK(notification_id.is_valid()); + CHECK(message_id.is_valid()); auto it = d->notification_id_to_message_id.find(notification_id); if (it != d->notification_id_to_message_id.end() && it->second == message_id) { VLOG(notifications) << "Delete correspondence from " << notification_id << " to " << message_id << " in " @@ -11120,6 +12402,7 @@ void MessagesManager::remove_message_notification_id(Dialog *d, Message *m, bool bool ignore_pinned_message_notification_removal) { CHECK(d != nullptr); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); if (!m->notification_id.is_valid()) { return; } @@ -11181,6 +12464,8 @@ void MessagesManager::remove_new_secret_chat_notification(Dialog *d, bool is_per } void MessagesManager::fix_dialog_last_notification_id(Dialog *d, bool from_mentions, MessageId message_id) { + CHECK(d != nullptr); + CHECK(!message_id.is_scheduled()); MessagesConstIterator it(d, message_id); auto &group_info = from_mentions ? d->mention_notification_group : d->message_notification_group; VLOG(notifications) << "Trying to fix last notification id in " << group_info.group_id << " from " << d->dialog_id @@ -11248,13 +12533,18 @@ unique_ptr MessagesManager::do_delete_message(Dialog * bool only_from_memory, bool *need_update_dialog_pos, const char *source) { + CHECK(d != nullptr); if (!message_id.is_valid()) { + if (message_id.is_valid_scheduled()) { + return do_delete_scheduled_message(d, message_id, is_permanently_deleted, source); + } + LOG(ERROR) << "Trying to delete " << message_id << " in " << d->dialog_id << " from " << source; return nullptr; } FullMessageId full_message_id(d->dialog_id, message_id); - unique_ptr *v = find_message(&d->messages, message_id); + unique_ptr *v = treap_find_message(&d->messages, message_id); if (*v == nullptr) { LOG(INFO) << message_id << " is not found in " << d->dialog_id << " to be deleted from " << source; if (only_from_memory) { @@ -11275,7 +12565,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * can't do this because the message may be never received in the dialog, unread count will became negative // if last_read_inbox_message_id is not known, we can't be sure whether unread_count should be decreased or not if (message_id.is_valid() && !message_id.is_yet_unsent() && d->is_last_read_inbox_message_id_inited && - message_id.get() > d->last_read_inbox_message_id.get() && !td_->auth_manager_->is_bot()) { + message_id > d->last_read_inbox_message_id && !td_->auth_manager_->is_bot()) { int32 server_unread_count = d->server_unread_count; int32 local_unread_count = d->local_unread_count; int32 &unread_count = message_id.is_server() ? server_unread_count : local_unread_count; @@ -11292,7 +12582,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * */ return nullptr; } - v = find_message(&d->messages, message_id); + v = treap_find_message(&d->messages, message_id); CHECK(*v != nullptr); } @@ -11360,7 +12650,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * if (*it != nullptr) { if (!(*it)->message_id.is_yet_unsent() && (*it)->message_id != d->last_database_message_id) { set_dialog_last_database_message_id(d, (*it)->message_id, "do_delete_message"); - if (d->last_database_message_id.get() < d->first_database_message_id.get()) { + 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; set_dialog_first_database_message_id(d, d->last_database_message_id, "do_delete_message 2"); @@ -11399,7 +12689,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * } } } - if (only_from_memory && message_id.get() >= d->suffix_load_first_message_id_.get()) { + if (only_from_memory && message_id >= d->suffix_load_first_message_id_) { d->suffix_load_first_message_id_ = MessageId(); d->suffix_load_done_ = false; } @@ -11430,35 +12720,12 @@ unique_ptr MessagesManager::do_delete_message(Dialog * } } - unique_ptr result = std::move(*v); - unique_ptr left = std::move(result->left); - unique_ptr right = std::move(result->right); - - LOG_CHECK(result->message_id == message_id) << result->message_id << " " << message_id << " " << source; - - while (left != nullptr || right != nullptr) { - if (left == nullptr || (right != nullptr && right->random_y > left->random_y)) { - *v = std::move(right); - v = &((*v)->left); - right = std::move(*v); - } else { - *v = std::move(left); - v = &((*v)->right); - left = std::move(*v); - } - } - CHECK(*v == nullptr); + auto result = treap_delete_message(v); d->being_deleted_message_id = MessageId(); d->debug_being_deleted_message_id_source = ""; if (!only_from_memory) { - if (message_id.is_yet_unsent()) { - cancel_send_message_query(d->dialog_id, result); - } else { - cancel_edit_message_media(d->dialog_id, result.get(), "Message was deleted"); - } - if (need_get_history && !td_->auth_manager_->is_bot() && have_input_peer(d->dialog_id, AccessRights::Read)) { get_history_from_the_end(d->dialog_id, true, false, Auto()); } @@ -11467,9 +12734,8 @@ unique_ptr MessagesManager::do_delete_message(Dialog * set_dialog_reply_markup(d, MessageId()); } // if last_read_inbox_message_id is not known, we can't be sure whether unread_count should be decreased or not - if (!result->is_outgoing && message_id.get() > d->last_read_inbox_message_id.get() && - d->dialog_id != get_my_dialog_id() && d->is_last_read_inbox_message_id_inited && - !td_->auth_manager_->is_bot()) { + if (has_incoming_notification(d->dialog_id, result.get()) && message_id > d->last_read_inbox_message_id && + d->is_last_read_inbox_message_id_inited && !td_->auth_manager_->is_bot()) { int32 server_unread_count = d->server_unread_count; int32 local_unread_count = d->local_unread_count; int32 &unread_count = message_id.is_server() ? server_unread_count : local_unread_count; @@ -11501,16 +12767,23 @@ unique_ptr MessagesManager::do_delete_message(Dialog * update_message_count_by_index(d, -1, result.get()); } - on_message_deleted(d, result.get(), source); + on_message_deleted(d, result.get(), is_permanently_deleted, source); return result; } -void MessagesManager::on_message_deleted(Dialog *d, Message *m, const char *source) { +void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanently_deleted, const char *source) { + // also called for unloaded messages + + cancel_send_deleted_message(d->dialog_id, m, is_permanently_deleted); + + CHECK(m->message_id.is_valid()); switch (d->dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: - message_id_to_dialog_id_.erase(m->message_id); + if (m->message_id.is_server()) { + message_id_to_dialog_id_.erase(m->message_id); + } break; case DialogType::Channel: // nothing to do @@ -11529,11 +12802,55 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, const char *sour } } -void MessagesManager::do_delete_all_dialog_messages(Dialog *d, unique_ptr &m, - vector &deleted_message_ids) { - if (m == nullptr) { +unique_ptr MessagesManager::do_delete_scheduled_message(Dialog *d, MessageId message_id, + bool is_permanently_deleted, + const char *source) { + CHECK(d != nullptr); + CHECK(message_id.is_valid_scheduled()); + + unique_ptr *v = treap_find_message(&d->scheduled_messages, message_id); + if (*v == nullptr) { + LOG(INFO) << message_id << " is not found in " << d->dialog_id << " to be deleted from " << source; + auto message = get_message_force(d, message_id, "do_delete_scheduled_message"); + if (message == nullptr) { + // currently there may be a race between add_message_to_database and get_message_force, + // so delete a message from database just in case + delete_message_from_database(d, message_id, nullptr, is_permanently_deleted); + return nullptr; + } + + message_id = message->message_id; + v = treap_find_message(&d->scheduled_messages, message_id); + CHECK(*v != nullptr); + } + + const Message *m = v->get(); + CHECK(m->message_id == message_id); + + LOG(INFO) << "Deleting " << FullMessageId{d->dialog_id, message_id} << " from " << source; + + delete_message_from_database(d, message_id, m, is_permanently_deleted); + + remove_message_file_sources(d->dialog_id, m); + + auto result = treap_delete_message(v); + + if (message_id.is_scheduled_server()) { + size_t erased = d->scheduled_message_date.erase(message_id.get_scheduled_server_message_id()); + CHECK(erased != 0); + } + + cancel_send_deleted_message(d->dialog_id, result.get(), is_permanently_deleted); + + return result; +} + +void MessagesManager::do_delete_all_dialog_messages(Dialog *d, unique_ptr &message, + bool is_permanently_deleted, vector &deleted_message_ids) { + if (message == nullptr) { return; } + const Message *m = message.get(); MessageId message_id = m->message_id; if (is_debug_message_op_enabled()) { @@ -11544,21 +12861,15 @@ void MessagesManager::do_delete_all_dialog_messages(Dialog *d, unique_ptrright, deleted_message_ids); - do_delete_all_dialog_messages(d, m->left, deleted_message_ids); + do_delete_all_dialog_messages(d, message->right, is_permanently_deleted, deleted_message_ids); + do_delete_all_dialog_messages(d, message->left, is_permanently_deleted, deleted_message_ids); - delete_active_live_location(d->dialog_id, m.get()); - remove_message_file_sources(d->dialog_id, m.get()); + delete_active_live_location(d->dialog_id, m); + remove_message_file_sources(d->dialog_id, m); - if (message_id.is_yet_unsent()) { - cancel_send_message_query(d->dialog_id, m); - } else { - cancel_edit_message_media(d->dialog_id, m.get(), "Message was deleted"); - } + on_message_deleted(d, message.get(), is_permanently_deleted, "do_delete_all_dialog_messages"); - on_message_deleted(d, m.get(), "do_delete_all_dialog_messages"); - - m = nullptr; + message = nullptr; } bool MessagesManager::have_dialog(DialogId dialog_id) const { @@ -11646,11 +12957,14 @@ bool MessagesManager::load_dialog(DialogId dialog_id, int left_tries, Promise MessagesManager::get_dialogs(DialogDate offset, int32 limit, bool force, Promise &&promise) { - LOG(INFO) << "Get chats with offset " << offset << " and limit " << limit << ". Know about order of " - << ordered_dialogs_.size() << " chat(s). last_dialog_date_ = " << last_dialog_date_ - << ", last_server_dialog_date_ = " << last_server_dialog_date_ - << ", last_loaded_database_dialog_date_ = " << last_loaded_database_dialog_date_; +vector MessagesManager::get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, + Promise &&promise) { + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Get chats in " << folder_id << " with offset " << offset << " and limit " << limit + << ". Know about order of " << list.ordered_dialogs_.size() + << " chat(s). last_dialog_date = " << list.last_dialog_date_ + << ", last_server_dialog_date = " << list.last_server_dialog_date_ + << ", last_loaded_database_dialog_date = " << list.last_loaded_database_dialog_date_; vector result; if (limit <= 0) { @@ -11662,8 +12976,8 @@ vector MessagesManager::get_dialogs(DialogDate offset, int32 limit, bo limit = MAX_GET_DIALOGS; } - auto it = ordered_dialogs_.upper_bound(offset); - auto end = ordered_dialogs_.end(); + auto it = list.ordered_dialogs_.upper_bound(offset); + auto end = list.ordered_dialogs_.end(); while (it != end && limit-- > 0) { result.push_back(it->get_dialog_id()); ++it; @@ -11674,184 +12988,193 @@ vector MessagesManager::get_dialogs(DialogDate offset, int32 limit, bo return result; } - load_dialog_list(limit, false, std::move(promise)); + load_dialog_list(folder_id, limit, false, std::move(promise)); return result; } -void MessagesManager::load_dialog_list(int32 limit, bool only_local, Promise &&promise) { - if (last_dialog_date_ == MAX_DIALOG_DATE) { +void MessagesManager::load_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise &&promise) { + auto &list = get_dialog_list(folder_id); + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { return promise.set_value(Unit()); } - bool use_database = - G()->parameters().use_message_db && last_loaded_database_dialog_date_ < last_database_server_dialog_date_; + bool use_database = G()->parameters().use_message_db && + list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_; if (only_local && !use_database) { return promise.set_value(Unit()); } - LOG(INFO) << "Load dialog list with limit " << limit; - auto &multipromise = load_dialog_list_multipromise_; + LOG(INFO) << "Load dialog list in " << folder_id << " with limit " << limit; + auto &multipromise = list.load_dialog_list_multipromise_; multipromise.add_promise(std::move(promise)); if (multipromise.promise_count() != 1) { // queries have already been sent, just wait for the result - if (use_database && load_dialog_list_limit_max_ != 0) { - load_dialog_list_limit_max_ = max(load_dialog_list_limit_max_, limit); + if (use_database && list.load_dialog_list_limit_max_ != 0) { + list.load_dialog_list_limit_max_ = max(list.load_dialog_list_limit_max_, limit); } return; } bool is_query_sent = false; if (use_database) { - load_dialog_list_from_database(limit, multipromise.get_promise()); + load_dialog_list_from_database(folder_id, limit, multipromise.get_promise()); is_query_sent = true; } else { - LOG(INFO) << "Get dialogs from " << last_server_dialog_date_; - reload_pinned_dialogs(multipromise.get_promise()); - if (last_dialog_date_ == last_server_dialog_date_) { + LOG(INFO) << "Get dialogs from " << list.last_server_dialog_date_; + reload_pinned_dialogs(folder_id, multipromise.get_promise()); + if (list.last_dialog_date_ == list.last_server_dialog_date_) { send_closure(td_->create_net_actor(multipromise.get_promise()), &GetDialogListActor::send, - last_server_dialog_date_.get_date(), - last_server_dialog_date_.get_message_id().get_next_server_message_id().get_server_message_id(), - last_server_dialog_date_.get_dialog_id(), int32{MAX_GET_DIALOGS}, + folder_id, list.last_server_dialog_date_.get_date(), + list.last_server_dialog_date_.get_message_id().get_next_server_message_id().get_server_message_id(), + list.last_server_dialog_date_.get_dialog_id(), int32{MAX_GET_DIALOGS}, get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); is_query_sent = true; } + if (folder_id == FolderId::main() && list.last_server_dialog_date_ == MIN_DIALOG_DATE) { + // do not pass promise to not wait for drafts before showing chat list + td_->create_handler()->send(); + } } CHECK(is_query_sent); } -void MessagesManager::load_dialog_list_from_database(int32 limit, Promise &&promise) { - LOG(INFO) << "Load " << limit << " dialogs from database from " << last_loaded_database_dialog_date_ - << ", last database server dialog date = " << last_database_server_dialog_date_; +void MessagesManager::load_dialog_list_from_database(FolderId folder_id, int32 limit, Promise &&promise) { + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Load " << limit << " chats in " << folder_id << " from database from " + << list.last_loaded_database_dialog_date_ + << ", last database server dialog date = " << list.last_database_server_dialog_date_; - CHECK(load_dialog_list_limit_max_ == 0); - load_dialog_list_limit_max_ = limit; + CHECK(list.load_dialog_list_limit_max_ == 0); + list.load_dialog_list_limit_max_ = limit; G()->td_db()->get_dialog_db_async()->get_dialogs( - last_loaded_database_dialog_date_.get_order(), last_loaded_database_dialog_date_.get_dialog_id(), limit, - PromiseCreator::lambda( - [actor_id = actor_id(this), limit, promise = std::move(promise)](vector result) mutable { - send_closure(actor_id, &MessagesManager::on_get_dialogs_from_database, limit, std::move(result), - std::move(promise)); - })); + folder_id, list.last_loaded_database_dialog_date_.get_order(), + list.last_loaded_database_dialog_date_.get_dialog_id(), limit, + PromiseCreator::lambda([actor_id = actor_id(this), folder_id, limit, + promise = std::move(promise)](DialogDbGetDialogsResult result) mutable { + send_closure(actor_id, &MessagesManager::on_get_dialogs_from_database, folder_id, limit, std::move(result), + std::move(promise)); + })); } -void MessagesManager::on_get_dialogs_from_database(int32 limit, vector &&dialogs, +void MessagesManager::on_get_dialogs_from_database(FolderId folder_id, int32 limit, DialogDbGetDialogsResult &&dialogs, Promise &&promise) { - LOG(INFO) << "Receive " << dialogs.size() << " from expected " << limit - << " dialogs in result of GetDialogsFromDatabase"; + auto &list = get_dialog_list(folder_id); + LOG(INFO) << "Receive " << dialogs.dialogs.size() << " from expected " << limit << " chats in " << folder_id + << " in from database with next order " << dialogs.next_order << " and next " << dialogs.next_dialog_id; int32 new_get_dialogs_limit = 0; - int32 have_more_dialogs_in_database = (limit == static_cast(dialogs.size())); - if (have_more_dialogs_in_database && limit < load_dialog_list_limit_max_) { - new_get_dialogs_limit = load_dialog_list_limit_max_ - limit; + int32 have_more_dialogs_in_database = (limit == static_cast(dialogs.dialogs.size())); + if (have_more_dialogs_in_database && limit < list.load_dialog_list_limit_max_) { + new_get_dialogs_limit = list.load_dialog_list_limit_max_ - limit; } - load_dialog_list_limit_max_ = 0; + list.load_dialog_list_limit_max_ = 0; - DialogDate max_dialog_date = MIN_DIALOG_DATE; - for (auto &dialog : dialogs) { + size_t dialogs_skipped = 0; + for (auto &dialog : dialogs.dialogs) { Dialog *d = on_load_dialog_from_database(DialogId(), std::move(dialog)); if (d == nullptr) { + dialogs_skipped++; + continue; + } + if (d->folder_id != folder_id) { + LOG(WARNING) << "Skip " << d->dialog_id << " received from database, because it is in " << d->folder_id + << " instead of " << folder_id; + dialogs_skipped++; continue; } - DialogDate dialog_date(d->order, d->dialog_id); - if (max_dialog_date < dialog_date) { - max_dialog_date = dialog_date; - } - LOG(INFO) << "Chat " << dialog_date << " is loaded from database"; + LOG(INFO) << "Chat " << d->dialog_id << " with order " << d->order << " is loaded from database"; } + DialogDate max_dialog_date(dialogs.next_order, dialogs.next_dialog_id); if (!have_more_dialogs_in_database) { - last_loaded_database_dialog_date_ = MAX_DIALOG_DATE; - LOG(INFO) << "Set last loaded database dialog date to " << last_loaded_database_dialog_date_; - last_server_dialog_date_ = max(last_server_dialog_date_, last_database_server_dialog_date_); - LOG(INFO) << "Set last server dialog date to " << last_server_dialog_date_; - update_last_dialog_date(); - } else if (last_loaded_database_dialog_date_ < max_dialog_date) { - last_loaded_database_dialog_date_ = min(max_dialog_date, last_database_server_dialog_date_); - LOG(INFO) << "Set last loaded database dialog date to " << last_loaded_database_dialog_date_; - last_server_dialog_date_ = max(last_server_dialog_date_, last_loaded_database_dialog_date_); - LOG(INFO) << "Set last server dialog date to " << last_server_dialog_date_; - update_last_dialog_date(); + list.last_loaded_database_dialog_date_ = MAX_DIALOG_DATE; + LOG(INFO) << "Set last loaded database dialog date to " << list.last_loaded_database_dialog_date_; + list.last_server_dialog_date_ = max(list.last_server_dialog_date_, list.last_database_server_dialog_date_); + LOG(INFO) << "Set last server dialog date to " << list.last_server_dialog_date_; + update_last_dialog_date(folder_id); + } else if (list.last_loaded_database_dialog_date_ < max_dialog_date) { + list.last_loaded_database_dialog_date_ = min(max_dialog_date, list.last_database_server_dialog_date_); + LOG(INFO) << "Set last loaded database dialog date to " << list.last_loaded_database_dialog_date_; + list.last_server_dialog_date_ = max(list.last_server_dialog_date_, list.last_loaded_database_dialog_date_); + LOG(INFO) << "Set last server dialog date to " << list.last_server_dialog_date_; + update_last_dialog_date(folder_id); } else { - LOG(ERROR) << "Last loaded database dialog date didn't increased"; + LOG(ERROR) << "Last loaded database dialog date didn't increased, skipped " << dialogs_skipped << " chats out of " + << dialogs.dialogs.size(); } - if (!(last_loaded_database_dialog_date_ < last_database_server_dialog_date_)) { + if (!(list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_)) { // have_more_dialogs_in_database = false; new_get_dialogs_limit = 0; } if (new_get_dialogs_limit == 0) { - if (!preload_dialog_list_timeout_.has_timeout()) { - LOG(INFO) << "Schedule chat list preload"; - preload_dialog_list_timeout_.set_callback(std::move(MessagesManager::preload_dialog_list)); - preload_dialog_list_timeout_.set_callback_data(static_cast(this)); - } - preload_dialog_list_timeout_.set_timeout_in(0.2); - + preload_dialog_list_timeout_.add_timeout_in(folder_id.get(), 0.2); promise.set_value(Unit()); } else { - load_dialog_list_from_database(new_get_dialogs_limit, std::move(promise)); + load_dialog_list_from_database(folder_id, new_get_dialogs_limit, std::move(promise)); } } -void MessagesManager::preload_dialog_list(void *messages_manager_void) { +void MessagesManager::preload_dialog_list(FolderId folder_id) { if (G()->close_flag()) { - LOG(INFO) << "Skip chat list preload, because of closing"; + LOG(INFO) << "Skip chat list preload because of closing"; return; } - CHECK(messages_manager_void != nullptr); - auto messages_manager = static_cast(messages_manager_void); - + auto &list = get_dialog_list(folder_id); CHECK(G()->parameters().use_message_db); - if (messages_manager->load_dialog_list_multipromise_.promise_count() != 0) { + if (list.load_dialog_list_multipromise_.promise_count() != 0) { LOG(INFO) << "Skip chat list preload, because there is a pending load chat list request"; return; } - if (messages_manager->ordered_dialogs_.size() > MAX_PRELOADED_DIALOGS) { + if (list.ordered_dialogs_.size() > MAX_PRELOADED_DIALOGS) { // do nothing if there are more than MAX_PRELOADED_DIALOGS dialogs already loaded - messages_manager->recalc_unread_count(); + recalc_unread_count(folder_id); return; } - if (messages_manager->last_loaded_database_dialog_date_ < messages_manager->last_database_server_dialog_date_) { + if (list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_) { // if there are some dialogs in database, preload some of them - messages_manager->load_dialog_list(20, true, Auto()); - } else if (messages_manager->last_dialog_date_ != MAX_DIALOG_DATE) { + load_dialog_list(folder_id, 20, true, Auto()); + } else if (list.last_dialog_date_ != MAX_DIALOG_DATE) { // otherwise load more dialogs from the server - messages_manager->load_dialog_list(MAX_GET_DIALOGS, false, - PromiseCreator::lambda([messages_manager](Result result) { - if (result.is_ok()) { - messages_manager->recalc_unread_count(); - } - })); + load_dialog_list(folder_id, MAX_GET_DIALOGS, false, + PromiseCreator::lambda([actor_id = actor_id(this), folder_id](Result result) { + if (result.is_ok()) { + send_closure(actor_id, &MessagesManager::recalc_unread_count, folder_id); + } + })); } else { - messages_manager->recalc_unread_count(); + recalc_unread_count(folder_id); } } -vector MessagesManager::get_pinned_dialogs() const { +vector MessagesManager::get_pinned_dialogs(FolderId folder_id) const { vector result; - auto it = ordered_dialogs_.begin(); - auto end = ordered_dialogs_.end(); - while (it != end && it->get_date() >= MIN_PINNED_DIALOG_DATE) { - if (it->get_order() != SPONSORED_DIALOG_ORDER) { - result.push_back(it->get_dialog_id()); + auto *list = get_dialog_list(folder_id); + if (list != nullptr) { + for (const DialogDate &dialog_date : list->ordered_dialogs_) { + 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()); + } } - ++it; } return result; } -void MessagesManager::reload_pinned_dialogs(Promise &&promise) { +void MessagesManager::reload_pinned_dialogs(FolderId folder_id, Promise &&promise) { if (G()->close_flag()) { return promise.set_error(Status::Error(500, "Request aborted")); } send_closure(td_->create_net_actor(std::move(promise)), &GetPinnedDialogsActor::send, - get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); + folder_id, get_sequence_dispatcher_id(DialogId(), MessageContentType::None)); } vector MessagesManager::search_public_dialogs(const string &query, Promise &&promise) { @@ -12151,7 +13474,7 @@ void MessagesManager::on_get_common_dialogs(UserId user_id, int32 offset_chat_id CHECK(dialog_id.is_valid()); td_->contacts_manager_->on_get_chat(std::move(chat), "on_get_common_dialogs"); - if (std::find(result.begin(), result.end(), dialog_id) == result.end()) { + if (!td::contains(result, dialog_id)) { force_create_dialog(dialog_id, "get common dialogs"); result.push_back(dialog_id); } @@ -12161,7 +13484,7 @@ void MessagesManager::on_get_common_dialogs(UserId user_id, int32 offset_chat_id } } -bool MessagesManager::have_message(FullMessageId full_message_id, const char *source) { +bool MessagesManager::have_message_force(FullMessageId full_message_id, const char *source) { return get_message_force(full_message_id, source) != nullptr; } @@ -12204,10 +13527,10 @@ MessageId MessagesManager::get_replied_message_id(const Message *m) { void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message_id, Promise &&promise, tl_object_ptr input_message) { LOG(INFO) << "Get " << message_id << " in " << d->dialog_id << " using " << to_string(input_message); + auto dialog_type = d->dialog_id.get_type(); auto m = get_message_force(d, message_id, "get_message_force_from_server"); if (m == nullptr && message_id.is_valid() && message_id.is_server()) { - auto dialog_type = d->dialog_id.get_type(); - if (d->last_new_message_id != MessageId() && message_id.get() > d->last_new_message_id.get()) { + if (d->last_new_message_id != MessageId() && message_id > d->last_new_message_id) { // message will not be added to the dialog anyway if (dialog_type == DialogType::Channel) { // so we try to force channel difference first @@ -12226,6 +13549,11 @@ void MessagesManager::get_message_force_from_server(Dialog *d, MessageId message if (d->deleted_message_ids.count(message_id) == 0 && dialog_type != DialogType::SecretChat) { return get_message_from_server({d->dialog_id, message_id}, std::move(promise), std::move(input_message)); } + } else if (m == nullptr && message_id.is_valid_scheduled() && message_id.is_scheduled_server()) { + if (d->deleted_scheduled_server_message_ids.count(message_id.get_scheduled_server_message_id()) == 0 && + dialog_type != DialogType::SecretChat && input_message == nullptr) { + return get_message_from_server({d->dialog_id, message_id}, std::move(promise)); + } } promise.set_value(Unit()); @@ -12260,8 +13588,8 @@ MessageId MessagesManager::get_replied_message(DialogId dialog_id, MessageId mes } tl_object_ptr input_message; - if (message_id.is_server()) { - input_message = make_tl_object(message_id.get_server_message_id().get()); + if (m->message_id.is_valid() && m->message_id.is_server()) { + input_message = make_tl_object(m->message_id.get_server_message_id().get()); } auto replied_message_id = get_replied_message_id(m); get_message_force_from_server(d, replied_message_id, std::move(promise), std::move(input_message)); @@ -12269,6 +13597,26 @@ MessageId MessagesManager::get_replied_message(DialogId dialog_id, MessageId mes return replied_message_id; } +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)); + return; + case DialogType::Chat: + td_->contacts_manager_->get_chat_full(dialog_id.get_chat_id(), std::move(promise)); + return; + case DialogType::Channel: + td_->contacts_manager_->get_channel_full(dialog_id.get_channel_id(), std::move(promise)); + return; + case DialogType::SecretChat: + return promise.set_value(Unit()); + case DialogType::None: + default: + UNREACHABLE(); + return promise.set_error(Status::Error(500, "Wrong chat type")); + } +} + MessageId MessagesManager::get_dialog_pinned_message(DialogId dialog_id, Promise &&promise) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -12279,37 +13627,20 @@ MessageId MessagesManager::get_dialog_pinned_message(DialogId dialog_id, Promise LOG(INFO) << "Get pinned message in " << dialog_id << " with " << (d->is_pinned_message_id_inited ? "inited" : "unknown") << " pinned " << d->pinned_message_id; - Promise empty_promise; - auto &get_pinned_message_id_promise = d->is_pinned_message_id_inited ? empty_promise : promise; - switch (dialog_id.get_type()) { - case DialogType::User: - td_->contacts_manager_->get_user_full(dialog_id.get_user_id(), std::move(get_pinned_message_id_promise)); - break; - case DialogType::Chat: - td_->contacts_manager_->get_chat_full(dialog_id.get_chat_id(), std::move(get_pinned_message_id_promise)); - break; - case DialogType::Channel: { - td_->contacts_manager_->get_channel_full(dialog_id.get_channel_id(), std::move(get_pinned_message_id_promise)); - break; - } - case DialogType::SecretChat: - get_pinned_message_id_promise.set_value(Unit()); - return MessageId(); - case DialogType::None: - default: - UNREACHABLE(); - get_pinned_message_id_promise.set_error(Status::Error(500, "Wrong chat type")); - } if (!d->is_pinned_message_id_inited) { - // promise was already consumed + get_dialog_info_full(dialog_id, std::move(promise)); return MessageId(); } - tl_object_ptr input_message; - if (dialog_id.get_type() == DialogType::Channel) { - input_message = make_tl_object(); + get_dialog_info_full(dialog_id, Auto()); + + if (d->pinned_message_id.is_valid()) { + tl_object_ptr input_message; + if (dialog_id.get_type() == DialogType::Channel) { + input_message = make_tl_object(); + } + get_message_force_from_server(d, d->pinned_message_id, std::move(promise), std::move(input_message)); } - get_message_force_from_server(d, d->pinned_message_id, std::move(promise), std::move(input_message)); return d->pinned_message_id; } @@ -12324,13 +13655,13 @@ bool MessagesManager::get_messages(DialogId dialog_id, const vector & bool is_secret = dialog_id.get_type() == DialogType::SecretChat; vector missed_message_ids; for (auto message_id : message_ids) { - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { promise.set_error(Status::Error(6, "Invalid message identifier")); return false; } auto *m = get_message_force(d, message_id, "get_messages"); - if (m == nullptr && message_id.is_server() && !is_secret) { + if (m == nullptr && message_id.is_any_server() && !is_secret) { missed_message_ids.emplace_back(dialog_id, message_id); continue; } @@ -12366,10 +13697,14 @@ void MessagesManager::get_messages_from_server(vector &&message_i vector> ordinary_message_ids; std::unordered_map>, ChannelIdHash> channel_message_ids; + std::unordered_map, DialogIdHash> scheduled_message_ids; for (auto &full_message_id : message_ids) { auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); if (!message_id.is_valid() || !message_id.is_server()) { + if (message_id.is_valid_scheduled()) { + scheduled_message_ids[dialog_id].push_back(message_id.get_scheduled_server_message_id().get()); + } continue; } @@ -12393,12 +13728,25 @@ void MessagesManager::get_messages_from_server(vector &&message_i } } - // TODO MultiPromise - size_t query_count = !ordinary_message_ids.empty() + channel_message_ids.size(); - LOG_IF(ERROR, query_count > 1 && promise) << "Promise will be called after first query returns"; + MultiPromiseActorSafe mpas{"GetMessagesFromServerMultiPromiseActor"}; + mpas.add_promise(std::move(promise)); + auto lock = mpas.get_promise(); if (!ordinary_message_ids.empty()) { - td_->create_handler(std::move(promise))->send(std::move(ordinary_message_ids)); + td_->create_handler(mpas.get_promise())->send(std::move(ordinary_message_ids)); + } + + for (auto &it : scheduled_message_ids) { + auto dialog_id = it.first; + have_dialog_force(dialog_id); + auto input_peer = get_input_peer(dialog_id, AccessRights::Read); + if (input_peer == nullptr) { + LOG(ERROR) << "Can't find info about " << dialog_id << " to get a message from it"; + mpas.get_promise().set_error(Status::Error(6, "Can't access the chat")); + continue; + } + td_->create_handler(mpas.get_promise()) + ->send(dialog_id, std::move(input_peer), std::move(it.second)); } for (auto &it : channel_message_ids) { @@ -12406,18 +13754,22 @@ void MessagesManager::get_messages_from_server(vector &&message_i auto input_channel = td_->contacts_manager_->get_input_channel(it.first); if (input_channel == nullptr) { LOG(ERROR) << "Can't find info about " << it.first << " to get a message from it"; - promise.set_error(Status::Error(6, "Can't access the chat")); + mpas.get_promise().set_error(Status::Error(6, "Can't access the chat")); continue; } - td_->create_handler(std::move(promise)) + td_->create_handler(mpas.get_promise()) ->send(it.first, std::move(input_channel), std::move(it.second)); } + lock.set_value(Unit()); } bool MessagesManager::is_message_edited_recently(FullMessageId full_message_id, int32 seconds) { if (seconds < 0) { return false; } + if (!full_message_id.get_message_id().is_valid()) { + return false; + } auto m = get_message_force(full_message_id, "is_message_edited_recently"); if (m == nullptr) { @@ -12442,21 +13794,24 @@ std::pair MessagesManager::get_public_message_link(FullMessageId if (dialog_id.get_type() != DialogType::Channel || td_->contacts_manager_->get_channel_username(dialog_id.get_channel_id()).empty()) { promise.set_error(Status::Error( - 6, "Public message links are available only for messages in public supergroups and channel chats")); + 6, "Public message links are available only for messages in supergroups and channel chats with a username")); return {}; } - auto message_id = full_message_id.get_message_id(); - auto *m = get_message_force(d, message_id, "get_public_message_link"); + auto *m = get_message_force(d, full_message_id.get_message_id(), "get_public_message_link"); if (m == nullptr) { promise.set_error(Status::Error(6, "Message not found")); return {}; } - if (message_id.is_yet_unsent()) { + if (m->message_id.is_yet_unsent()) { promise.set_error(Status::Error(6, "Message is yet unsent")); return {}; } - if (!message_id.is_server()) { + if (m->message_id.is_scheduled()) { + promise.set_error(Status::Error(6, "Message is scheduled")); + return {}; + } + if (!m->message_id.is_server()) { promise.set_error(Status::Error(6, "Message is local")); return {}; } @@ -12464,7 +13819,7 @@ std::pair MessagesManager::get_public_message_link(FullMessageId auto it = public_message_links_[for_group].find(full_message_id); if (it == public_message_links_[for_group].end()) { td_->create_handler(std::move(promise)) - ->send(dialog_id.get_channel_id(), message_id, for_group, false); + ->send(dialog_id.get_channel_id(), m->message_id, for_group, false); return {}; } @@ -12495,23 +13850,26 @@ string MessagesManager::get_message_link(FullMessageId full_message_id, Promise< return {}; } - auto message_id = full_message_id.get_message_id(); - auto *m = get_message_force(d, message_id, "get_message_link"); + auto *m = get_message_force(d, full_message_id.get_message_id(), "get_message_link"); if (m == nullptr) { promise.set_error(Status::Error(6, "Message not found")); return {}; } - if (!message_id.is_server()) { + if (m->message_id.is_scheduled()) { + promise.set_error(Status::Error(6, "Message is scheduled")); + return {}; + } + if (!m->message_id.is_server()) { promise.set_error(Status::Error(6, "Message is local")); return {}; } td_->create_handler(Promise()) - ->send(dialog_id.get_channel_id(), message_id, false, true); + ->send(dialog_id.get_channel_id(), m->message_id, false, true); promise.set_value(Unit()); return PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << "c/" - << dialog_id.get_channel_id().get() << "/" << message_id.get_server_message_id().get(); + << dialog_id.get_channel_id().get() << "/" << m->message_id.get_server_message_id().get(); } Result MessagesManager::get_message_link_info(Slice url) { @@ -12590,7 +13948,7 @@ Result MessagesManager::get_message_link_info( if (begins_with(cur_t_me_url, "http://") || begins_with(cur_t_me_url, "https://")) { Slice t_me_url = cur_t_me_url; t_me_url = t_me_url.substr(url[4] == 's' ? 8 : 7); - if (std::find(t_me_urls.begin(), t_me_urls.end(), t_me_url) == t_me_urls.end()) { + if (!td::contains(t_me_urls, t_me_url)) { t_me_urls.push_back(t_me_url); } } @@ -12726,8 +14084,11 @@ Status MessagesManager::delete_dialog_reply_markup(DialogId dialog_id, MessageId if (td_->auth_manager_->is_bot()) { return Status::Error(6, "Bots can't delete chat reply markup"); } + if (message_id.is_scheduled()) { + return Status::Error(6, "Wrong message identifier specified"); + } if (!message_id.is_valid()) { - return Status::Error(6, "Invalid message id specified"); + return Status::Error(6, "Invalid message identifier specified"); } Dialog *d = get_dialog_force(dialog_id); @@ -12882,19 +14243,22 @@ void MessagesManager::clear_all_draft_messages(bool exclude_secret_chats, Promis td_->create_handler(std::move(promise))->send(); } -int32 MessagesManager::get_pinned_dialogs_limit() { - int32 limit = G()->shared_config().get_option_integer("pinned_chat_count_max"); +int32 MessagesManager::get_pinned_dialogs_limit(FolderId folder_id) { + Slice key{"pinned_chat_count_max"}; + int32 default_limit = 5; + if (folder_id != FolderId::main()) { + key = Slice("pinned_archived_chat_count_max"); + default_limit = 100; + } + int32 limit = clamp(G()->shared_config().get_option_integer(key), 0, 1000000); if (limit <= 0) { - const int32 DEFAULT_PINNED_DIALOGS_LIMIT = 5; - return DEFAULT_PINNED_DIALOGS_LIMIT; + return default_limit; } return limit; } vector MessagesManager::remove_secret_chat_dialog_ids(vector dialog_ids) { - dialog_ids.erase(std::remove_if(dialog_ids.begin(), dialog_ids.end(), - [](DialogId dialog_id) { return dialog_id.get_type() == DialogType::SecretChat; }), - dialog_ids.end()); + td::remove_if(dialog_ids, [](DialogId dialog_id) { return dialog_id.get_type() == DialogType::SecretChat; }); return dialog_ids; } @@ -12917,7 +14281,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn } if (is_pinned) { - auto pinned_dialog_ids = get_pinned_dialogs(); + auto pinned_dialog_ids = get_pinned_dialogs(d->folder_id); auto pinned_dialog_count = pinned_dialog_ids.size(); auto secret_pinned_dialog_count = std::count_if(pinned_dialog_ids.begin(), pinned_dialog_ids.end(), @@ -12926,7 +14290,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn ? secret_pinned_dialog_count : pinned_dialog_count - secret_pinned_dialog_count; - if (dialog_count >= static_cast(get_pinned_dialogs_limit())) { + if (dialog_count >= static_cast(get_pinned_dialogs_limit(d->folder_id))) { return Status::Error(400, "Maximum number of pinned chats exceeded"); } } @@ -12981,14 +14345,14 @@ void MessagesManager::toggle_dialog_is_pinned_on_server(DialogId dialog_id, bool td_->create_handler(get_erase_logevent_promise(logevent_id))->send(dialog_id, is_pinned); } -Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { +Status MessagesManager::set_pinned_dialogs(FolderId folder_id, vector dialog_ids) { if (td_->auth_manager_->is_bot()) { return Status::Error(6, "Bots can't reorder pinned chats"); } int32 dialog_count = 0; int32 secret_dialog_count = 0; - auto dialog_count_limit = get_pinned_dialogs_limit(); + auto dialog_count_limit = get_pinned_dialogs_limit(folder_id); for (auto dialog_id : dialog_ids) { Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { @@ -13012,12 +14376,12 @@ Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { return Status::Error(400, "Duplicate chats in the list of pinned chats"); } - auto pinned_dialog_ids = get_pinned_dialogs(); + auto pinned_dialog_ids = get_pinned_dialogs(folder_id); if (pinned_dialog_ids == dialog_ids) { return Status::OK(); } - LOG(INFO) << "Reorder pinned chats order from " << format::as_array(pinned_dialog_ids) << " to " - << format::as_array(dialog_ids); + LOG(INFO) << "Reorder pinned chats order in " << folder_id << " from " << format::as_array(pinned_dialog_ids) + << " to " << format::as_array(dialog_ids); auto server_old_dialog_ids = remove_secret_chat_dialog_ids(pinned_dialog_ids); auto server_new_dialog_ids = remove_secret_chat_dialog_ids(dialog_ids); @@ -13046,38 +14410,47 @@ Status MessagesManager::set_pinned_dialogs(vector dialog_ids) { } if (server_old_dialog_ids != server_new_dialog_ids) { - reorder_pinned_dialogs_on_server(server_new_dialog_ids, 0); + reorder_pinned_dialogs_on_server(folder_id, server_new_dialog_ids, 0); } return Status::OK(); } class MessagesManager::ReorderPinnedDialogsOnServerLogEvent { public: + FolderId folder_id_; vector dialog_ids_; template void store(StorerT &storer) const { + td::store(folder_id_, storer); td::store(dialog_ids_, storer); } template void parse(ParserT &parser) { + if (parser.version() >= static_cast(Version::AddFolders)) { + td::parse(folder_id_, parser); + } else { + folder_id_ = FolderId(); + } td::parse(dialog_ids_, parser); } }; -uint64 MessagesManager::save_reorder_pinned_dialogs_on_server_logevent(const vector &dialog_ids) { - ReorderPinnedDialogsOnServerLogEvent logevent{dialog_ids}; +uint64 MessagesManager::save_reorder_pinned_dialogs_on_server_logevent(FolderId folder_id, + const vector &dialog_ids) { + ReorderPinnedDialogsOnServerLogEvent logevent{folder_id, dialog_ids}; auto storer = LogEventStorerImpl(logevent); return binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::ReorderPinnedDialogsOnServer, storer); } -void MessagesManager::reorder_pinned_dialogs_on_server(const vector &dialog_ids, uint64 logevent_id) { +void MessagesManager::reorder_pinned_dialogs_on_server(FolderId folder_id, const vector &dialog_ids, + uint64 logevent_id) { if (logevent_id == 0 && G()->parameters().use_message_db) { - logevent_id = save_reorder_pinned_dialogs_on_server_logevent(dialog_ids); + logevent_id = save_reorder_pinned_dialogs_on_server_logevent(folder_id, dialog_ids); } - td_->create_handler(get_erase_logevent_promise(logevent_id))->send(dialog_ids); + td_->create_handler(get_erase_logevent_promise(logevent_id))->send(folder_id, dialog_ids); } Status MessagesManager::toggle_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread) { @@ -13361,9 +14734,10 @@ DialogId MessagesManager::create_new_group_chat(const vector &user_ids, } DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_megagroup, const string &description, - int64 &random_id, Promise &&promise) { + const DialogLocation &location, int64 &random_id, + Promise &&promise) { LOG(INFO) << "Trying to create " << (is_megagroup ? "supergroup" : "broadcast") << " with title \"" << title - << "\" and description \"" << description << "\""; + << "\", description \"" << description << "\" and " << location; if (random_id != 0) { // request has already been sent before @@ -13395,7 +14769,7 @@ DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_m created_dialogs_[random_id]; // reserve place for result td_->create_handler(std::move(promise)) - ->send(new_title, is_megagroup, strip_empty_characters(description, MAX_DESCRIPTION_LENGTH), random_id); + ->send(new_title, is_megagroup, strip_empty_characters(description, MAX_DESCRIPTION_LENGTH), location, random_id); return DialogId(); } @@ -13476,7 +14850,7 @@ Status MessagesManager::view_messages(DialogId dialog_id, const vector read_content_message_ids; for (auto message_id : message_ids) { + if (!message_id.is_valid()) { + continue; + } + auto *m = get_message_force(d, message_id, "view_messages"); if (m != nullptr) { - if (message_id.is_server() && m->views > 0) { - d->pending_viewed_message_ids.insert(message_id); + if (m->message_id.is_server() && m->views > 0) { + d->pending_viewed_message_ids.insert(m->message_id); } - if (!message_id.is_yet_unsent() && message_id.get() > max_message_id.get()) { - max_message_id = message_id; + if (!m->message_id.is_yet_unsent() && m->message_id > max_message_id) { + max_message_id = m->message_id; } - if (need_read) { - auto message_content_type = m->content->get_type(); - if (message_content_type != MessageContentType::VoiceNote && - message_content_type != MessageContentType::VideoNote && - update_message_contains_unread_mention(d, m, false, "view_messages")) { - CHECK(message_id.is_server()); - read_content_message_ids.push_back(message_id); - on_message_changed(d, m, true, "view_messages"); - } + auto message_content_type = m->content->get_type(); + if (message_content_type == MessageContentType::LiveLocation) { + on_message_live_location_viewed(d, m); } - } else if (!message_id.is_yet_unsent() && message_id.get() > max_message_id.get() && - message_id.get() <= d->max_notification_message_id.get()) { + + if (need_read && message_content_type != MessageContentType::VoiceNote && + message_content_type != MessageContentType::VideoNote && + update_message_contains_unread_mention(d, m, false, "view_messages")) { + CHECK(m->message_id.is_server()); + read_content_message_ids.push_back(m->message_id); + on_message_changed(d, m, true, "view_messages"); + } + } else if (!message_id.is_yet_unsent() && message_id > max_message_id && + message_id <= d->max_notification_message_id) { max_message_id = message_id; } } @@ -13518,10 +14898,10 @@ Status MessagesManager::view_messages(DialogId dialog_id, const vectorincrement_view_counter |= d->is_opened; } if (!read_content_message_ids.empty()) { - read_message_contents_on_server(dialog_id, std::move(read_content_message_ids), 0); + read_message_contents_on_server(dialog_id, std::move(read_content_message_ids), 0, Auto()); } - if (need_read && max_message_id.get() > d->last_read_inbox_message_id.get()) { + if (need_read && max_message_id > 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"); @@ -13532,7 +14912,7 @@ Status MessagesManager::view_messages(DialogId dialog_id, const vector prev_last_read_inbox_message_id.get()) { + if (last_read_message_id > prev_last_read_inbox_message_id) { read_history_on_server(d, last_read_message_id); } } @@ -13551,19 +14931,22 @@ Status MessagesManager::open_message_content(FullMessageId full_message_id) { return Status::Error(3, "Chat not found"); } - auto message_id = full_message_id.get_message_id(); - auto *m = get_message_force(d, message_id, "open_message_content"); + auto *m = get_message_force(d, full_message_id.get_message_id(), "open_message_content"); if (m == nullptr) { return Status::Error(4, "Message not found"); } - if (message_id.is_yet_unsent() || m->is_outgoing) { + if (m->message_id.is_scheduled() || m->message_id.is_yet_unsent() || m->is_outgoing) { return Status::OK(); } if (read_message_content(d, m, true, "open_message_content") && - (message_id.is_server() || dialog_id.get_type() == DialogType::SecretChat)) { - read_message_contents_on_server(dialog_id, {message_id}, 0); + (m->message_id.is_server() || dialog_id.get_type() == DialogType::SecretChat)) { + read_message_contents_on_server(dialog_id, {m->message_id}, 0, Auto()); + } + + if (m->content->get_type() == MessageContentType::LiveLocation) { + on_message_live_location_viewed(d, m); } return Status::OK(); @@ -13595,16 +14978,18 @@ uint64 MessagesManager::save_read_message_contents_on_server_logevent(DialogId d } void MessagesManager::read_message_contents_on_server(DialogId dialog_id, vector message_ids, - uint64 logevent_id) { + uint64 logevent_id, Promise &&promise, bool skip_logevent) { CHECK(!message_ids.empty()); LOG(INFO) << "Read contents of " << format::as_array(message_ids) << " in " << dialog_id << " on server"; - if (logevent_id == 0 && G()->parameters().use_message_db) { + if (logevent_id == 0 && G()->parameters().use_message_db && !skip_logevent) { logevent_id = save_read_message_contents_on_server_logevent(dialog_id, message_ids); } - auto promise = get_erase_logevent_promise(logevent_id); + auto new_promise = get_erase_logevent_promise(logevent_id, std::move(promise)); + promise = std::move(new_promise); // to prevent self-move + switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: @@ -13614,16 +14999,17 @@ void MessagesManager::read_message_contents_on_server(DialogId dialog_id, vector td_->create_handler(std::move(promise)) ->send(dialog_id.get_channel_id(), std::move(message_ids)); break; - case DialogType::SecretChat: + case DialogType::SecretChat: { CHECK(message_ids.size() == 1); - for (auto message_id : message_ids) { - auto m = get_message_force({dialog_id, message_id}, "read_message_contents_on_server"); - if (m != nullptr) { - send_closure(G()->secret_chats_manager(), &SecretChatsManager::send_open_message, - dialog_id.get_secret_chat_id(), m->random_id, std::move(promise)); - } + auto m = get_message_force({dialog_id, message_ids[0]}, "read_message_contents_on_server"); + if (m != nullptr) { + send_closure(G()->secret_chats_manager(), &SecretChatsManager::send_open_message, + dialog_id.get_secret_chat_id(), m->random_id, std::move(promise)); + } else { + promise.set_error(Status::Error(400, "Message not found")); } break; + } case DialogType::None: default: UNREACHABLE(); @@ -13637,14 +15023,14 @@ void MessagesManager::open_dialog(Dialog *d) { } d->is_opened = true; - auto min_message_id = MessageId(ServerMessageId(1)).get(); - if (d->last_message_id == MessageId() && d->last_read_outbox_message_id.get() < min_message_id && - d->messages != nullptr && d->messages->message_id.get() < min_message_id) { + auto min_message_id = MessageId(ServerMessageId(1)); + if (d->last_message_id == MessageId() && d->last_read_outbox_message_id < min_message_id && d->messages != nullptr && + d->messages->message_id < min_message_id) { Message *m = d->messages.get(); while (m->right != nullptr) { m = m->right.get(); } - if (m->message_id.get() < min_message_id) { + if (m->message_id < min_message_id) { read_history_inbox(dialog_id, m->message_id, -1, "open_dialog"); } } @@ -13663,6 +15049,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"); break; case DialogType::Channel: if (!is_broadcast_channel(dialog_id)) { @@ -13675,9 +15062,16 @@ void MessagesManager::open_dialog(Dialog *d) { } } get_channel_difference(dialog_id, d->pts, true, "open_dialog"); + repair_dialog_action_bar(dialog_id, "open_dialog"); break; - case DialogType::SecretChat: + case DialogType::SecretChat: { + // to repair dialog action bar + auto user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (user_id.is_valid()) { + td_->contacts_manager_->reload_user_full(user_id); + } break; + } case DialogType::None: default: UNREACHABLE(); @@ -13693,6 +15087,20 @@ void MessagesManager::open_dialog(Dialog *d) { send_update_chat_online_member_count(dialog_id, info.online_member_count); } } + + if (d->has_scheduled_database_messages && !d->is_has_scheduled_database_messages_checked) { + CHECK(G()->parameters().use_message_db); + + LOG(INFO) << "Send check has_scheduled_database_messages request"; + d->is_has_scheduled_database_messages_checked = true; + G()->td_db()->get_messages_db_async()->get_scheduled_messages( + dialog_id, 1, + PromiseCreator::lambda([dialog_id, actor_id = actor_id(this)](std::vector messages) { + if (messages.empty()) { + send_closure(actor_id, &MessagesManager::set_dialog_has_scheduled_database_messages, dialog_id, false); + } + })); + } } } @@ -13731,6 +15139,13 @@ void MessagesManager::close_dialog(Dialog *d) { pending_unload_dialog_timeout_.set_timeout_in(dialog_id.get(), get_unload_dialog_delay()); } + for (auto &it : d->pending_viewed_live_locations) { + auto live_location_task_id = it.second; + auto erased_count = viewed_live_location_tasks_.erase(live_location_task_id); + CHECK(erased_count > 0); + } + d->pending_viewed_live_locations.clear(); + switch (dialog_id.get_type()) { case DialogType::User: break; @@ -13756,25 +15171,25 @@ void MessagesManager::close_dialog(Dialog *d) { } } -tl_object_ptr MessagesManager::get_chat_type_object(DialogId dialog_id) const { +td_api::object_ptr MessagesManager::get_chat_type_object(DialogId dialog_id) const { switch (dialog_id.get_type()) { case DialogType::User: - return make_tl_object( + return td_api::make_object( td_->contacts_manager_->get_user_id_object(dialog_id.get_user_id(), "chatTypePrivate")); case DialogType::Chat: - return make_tl_object( + return td_api::make_object( td_->contacts_manager_->get_basic_group_id_object(dialog_id.get_chat_id(), "chatTypeBasicGroup")); case DialogType::Channel: { auto channel_id = dialog_id.get_channel_id(); auto channel_type = td_->contacts_manager_->get_channel_type(channel_id); - return make_tl_object( + return td_api::make_object( td_->contacts_manager_->get_supergroup_id_object(channel_id, "chatTypeSupergroup"), channel_type != ChannelType::Megagroup); } case DialogType::SecretChat: { auto secret_chat_id = dialog_id.get_secret_chat_id(); auto user_id = td_->contacts_manager_->get_secret_chat_user_id(secret_chat_id); - return make_tl_object( + return td_api::make_object( td_->contacts_manager_->get_secret_chat_id_object(secret_chat_id, "chatTypeSecret"), td_->contacts_manager_->get_user_id_object(user_id, "chatTypeSecret")); } @@ -13785,7 +15200,74 @@ tl_object_ptr MessagesManager::get_chat_type_object(DialogId d } } -tl_object_ptr MessagesManager::get_chat_object(const Dialog *d) const { +td_api::object_ptr MessagesManager::get_chat_list_object(const Dialog *d) { + if (d->order == DEFAULT_ORDER) { + return nullptr; + } + if (d->folder_id != FolderId::main() && d->folder_id != FolderId::archive()) { + LOG(ERROR) << "Have " << d->dialog_id << " in unknown " << d->folder_id; + } + return get_chat_list_object(d->folder_id); +} + +td_api::object_ptr MessagesManager::get_chat_list_object(FolderId folder_id) { + if (folder_id == FolderId::archive()) { + return td_api::make_object(); + } + if (folder_id == FolderId::main()) { + return td_api::make_object(); + } + return td_api::make_object(); +} + +td_api::object_ptr MessagesManager::get_chat_action_bar_object(const Dialog *d) const { + CHECK(d != nullptr); + if (d->dialog_id.get_type() == DialogType::SecretChat) { + auto user_id = td_->contacts_manager_->get_secret_chat_user_id(d->dialog_id.get_secret_chat_id()); + if (!user_id.is_valid()) { + return nullptr; + } + const Dialog *user_d = get_dialog(DialogId(user_id)); + if (user_d == nullptr) { + return nullptr; + } + return get_chat_action_bar_object(user_d); + } + + if (!d->know_action_bar) { + if (d->know_can_report_spam && d->dialog_id.get_type() != DialogType::SecretChat && d->can_report_spam) { + return td_api::make_object(); + } + return nullptr; + } + + if (d->can_report_location) { + CHECK(d->dialog_id.get_type() == DialogType::Channel); + CHECK(!d->can_share_phone_number && !d->can_block_user && !d->can_add_contact && !d->can_report_spam); + return td_api::make_object(); + } + if (d->can_share_phone_number) { + CHECK(d->dialog_id.get_type() == DialogType::User); + CHECK(!d->can_block_user && !d->can_add_contact && !d->can_report_spam); + return td_api::make_object(); + } + if (d->can_block_user) { + CHECK(d->dialog_id.get_type() == DialogType::User); + CHECK(d->can_report_spam && d->can_add_contact); + return td_api::make_object(); + } + if (d->can_add_contact) { + CHECK(d->dialog_id.get_type() == DialogType::User); + CHECK(!d->can_report_spam); + return td_api::make_object(); + } + if (d->can_report_spam) { + return td_api::make_object(); + } + return nullptr; +} + +td_api::object_ptr MessagesManager::get_chat_object(const Dialog *d) const { CHECK(d != nullptr); bool can_delete_for_self = false; @@ -13806,7 +15288,7 @@ tl_object_ptr MessagesManager::get_chat_object(const Dialog *d) co break; case DialogType::Channel: if (is_broadcast_channel(d->dialog_id) || - !td_->contacts_manager_->get_channel_username(d->dialog_id.get_channel_id()).empty()) { + td_->contacts_manager_->is_channel_public(d->dialog_id.get_channel_id())) { // deleteChatHistory can't be used in channels and public supergroups } else { // private supergroups can be deleted for self @@ -13829,18 +15311,20 @@ tl_object_ptr MessagesManager::get_chat_object(const Dialog *d) co } } + bool has_scheduled_messages = + d->has_scheduled_server_messages || d->has_scheduled_database_messages || d->scheduled_messages != nullptr; return make_tl_object( - d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_dialog_title(d->dialog_id), + 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)), - DialogDate(d->order, d->dialog_id) <= last_dialog_date_ ? d->order : 0, d->pinned_order != DEFAULT_ORDER, - d->is_marked_as_unread, d->order == SPONSORED_DIALOG_ORDER, 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), d->pinned_message_id.get(), - d->reply_markup_message_id.get(), get_draft_message_object(d->draft_message), d->client_data); + 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, + 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); } tl_object_ptr MessagesManager::get_chat_object(DialogId dialog_id) const { @@ -13903,36 +15387,65 @@ int32 MessagesManager::get_scope_mute_until(DialogId dialog_id) const { vector MessagesManager::get_dialog_notification_settings_exceptions(NotificationSettingsScope scope, bool filter_scope, bool compare_sound, bool force, Promise &&promise) { - if (last_dialog_date_ == MAX_DIALOG_DATE || force) { - vector result; - auto my_dialog_id = get_my_dialog_id(); - for (const auto &it : ordered_server_dialogs_) { - auto dialog_id = it.get_dialog_id(); - if (filter_scope && get_dialog_notification_setting_scope(dialog_id) != scope) { - continue; - } - if (dialog_id == my_dialog_id) { - continue; - } + bool have_all_dialogs = true; + bool have_main_list = false; + bool have_archive_list = false; + for (auto &list : dialog_lists_) { + if (list.second.last_dialog_date_ != MAX_DIALOG_DATE) { + have_all_dialogs = false; + } + have_main_list |= list.first == FolderId::main(); + have_archive_list |= list.first == FolderId::archive(); + } + if (!have_main_list || !have_archive_list) { + have_all_dialogs = false; + } - Dialog *d = get_dialog(dialog_id); - CHECK(d != nullptr); - if (d->order == DEFAULT_ORDER) { - break; + if (have_all_dialogs || force) { + vector ordered_dialogs; + auto my_dialog_id = get_my_dialog_id(); + for (auto &list : dialog_lists_) { + for (const auto &it : list.second.ordered_server_dialogs_) { + auto dialog_id = it.get_dialog_id(); + if (filter_scope && get_dialog_notification_setting_scope(dialog_id) != scope) { + continue; + } + if (dialog_id == my_dialog_id) { + continue; + } + + Dialog *d = get_dialog(dialog_id); + CHECK(d != nullptr); + if (d->order == DEFAULT_ORDER) { + break; + } + if (are_default_dialog_notification_settings(d->notification_settings, compare_sound)) { + continue; + } + if (is_dialog_message_notification_disabled(dialog_id, std::numeric_limits::max())) { + continue; + } + ordered_dialogs.push_back(DialogDate(d->order, dialog_id)); } - if (are_default_dialog_notification_settings(d->notification_settings, compare_sound)) { - continue; - } - if (is_dialog_message_notification_disabled(dialog_id, std::numeric_limits::max())) { - continue; - } - result.push_back(dialog_id); + } + std::sort(ordered_dialogs.begin(), ordered_dialogs.end()); + + vector result; + for (auto &it : ordered_dialogs) { + CHECK(result.empty() || result.back() != it.get_dialog_id()); + result.push_back(it.get_dialog_id()); } promise.set_value(Unit()); return result; } - load_dialog_list(MAX_GET_DIALOGS, true, Auto()); + load_dialog_list(FolderId::main(), MAX_GET_DIALOGS, true, Auto()); + load_dialog_list(FolderId::archive(), MAX_GET_DIALOGS, true, Auto()); + for (auto &list : dialog_lists_) { + if (list.first != FolderId::main() && list.first != FolderId::archive()) { + load_dialog_list(list.first, MAX_GET_DIALOGS, true, Auto()); + } + } td_->create_handler(std::move(promise))->send(scope, filter_scope, compare_sound); return {}; @@ -14008,6 +15521,9 @@ Status MessagesManager::set_dialog_notification_settings( if (current_settings == nullptr) { return Status::Error(6, "Wrong chat identifier specified"); } + if (dialog_id == get_my_dialog_id()) { + return Status::Error(6, "Notification settings of the Saved Messages chat can't be changed"); + } TRY_RESULT(new_settings, ::td::get_dialog_notification_settings(std::move(notification_settings), current_settings->silent_send_message)); @@ -14160,8 +15676,8 @@ tl_object_ptr MessagesManager::get_dialog_history(DialogId dia MessagesConstIterator p(d, from_message_id); LOG(DEBUG) << "Iterator points to " << (*p ? (*p)->message_id : MessageId()); - bool from_the_end = (d->last_message_id != MessageId() && from_message_id.get() > d->last_message_id.get()) || - from_message_id.get() >= MessageId::max().get(); + bool from_the_end = (d->last_message_id != MessageId() && from_message_id > d->last_message_id) || + from_message_id >= MessageId::max(); if (from_the_end) { limit += offset; @@ -14178,16 +15694,15 @@ tl_object_ptr MessagesManager::get_dialog_history(DialogId dia while (cur->left != nullptr) { cur = cur->left.get(); } - CHECK(cur->message_id.get() > from_message_id.get()); + CHECK(cur->message_id > from_message_id); from_message_id = cur->message_id; p = MessagesConstIterator(d, from_message_id); } else { have_a_gap = true; } } else if ((*p)->message_id != from_message_id) { - CHECK((*p)->message_id.get() < from_message_id.get()); - if (!(*p)->have_next && - (d->last_message_id == MessageId() || (*p)->message_id.get() < d->last_message_id.get())) { + CHECK((*p)->message_id < from_message_id); + if (!(*p)->have_next && (d->last_message_id == MessageId() || (*p)->message_id < d->last_message_id)) { have_a_gap = true; } } @@ -14212,7 +15727,7 @@ tl_object_ptr MessagesManager::get_dialog_history(DialogId dia } } - if (offset < 0 && ((d->last_message_id != MessageId() && from_message_id.get() >= d->last_message_id.get()) || + if (offset < 0 && ((d->last_message_id != MessageId() && from_message_id >= d->last_message_id) || (!have_a_gap && left_tries == 0))) { CHECK(!have_a_gap); limit += offset; @@ -14259,7 +15774,7 @@ tl_object_ptr MessagesManager::get_dialog_history(DialogId dia } LOG(INFO) << "Return " << messages.size() << " messages in result to getChatHistory"; - promise.set_value(Unit()); // can send some messages + promise.set_value(Unit()); // can return some messages return get_messages_object(-1, std::move(messages)); // TODO return real total_count of messages in the dialog } @@ -14304,6 +15819,9 @@ void MessagesManager::read_history_on_server(Dialog *d, MessageId max_message_id return; } + CHECK(d != nullptr); + CHECK(!max_message_id.is_scheduled()); + auto dialog_id = d->dialog_id; LOG(INFO) << "Read history in " << dialog_id << " on server up to " << max_message_id; @@ -14366,7 +15884,7 @@ void MessagesManager::read_history_on_server_impl(DialogId dialog_id, MessageId if (dialog_id.get_type() != DialogType::SecretChat) { message_id = message_id.get_prev_server_message_id(); } - if (message_id.get() > max_message_id.get()) { + if (message_id > max_message_id) { max_message_id = message_id; } @@ -14381,6 +15899,10 @@ void MessagesManager::read_history_on_server_impl(DialogId dialog_id, MessageId }); } + if (!max_message_id.is_valid()) { + return promise.set_value(Unit()); + } + LOG(INFO) << "Send read history request in " << dialog_id << " up to " << max_message_id; switch (dialog_id.get_type()) { case DialogType::User: @@ -14401,7 +15923,7 @@ void MessagesManager::read_history_on_server_impl(DialogId dialog_id, MessageId } if (date == 0) { LOG(ERROR) << "Don't know last read inbox message date in " << dialog_id; - break; + return promise.set_value(Unit()); } send_closure(G()->secret_chats_manager(), &SecretChatsManager::send_read_history, secret_chat_id, date, std::move(promise)); @@ -14511,8 +16033,7 @@ std::pair> MessagesManager::search_dialog_messages( } LOG(INFO) << "Search messages in " << dialog_id << " from " << fixed_from_message_id << ", have up to " << first_db_message_id << ", message_count = " << message_count; - if ((first_db_message_id.get() < fixed_from_message_id.get() || - (first_db_message_id.get() == fixed_from_message_id.get() && offset < 0)) && + if ((first_db_message_id < fixed_from_message_id || (first_db_message_id == fixed_from_message_id && offset < 0)) && message_count != -1) { LOG(INFO) << "Search messages in database in " << dialog_id << " from " << fixed_from_message_id << " and with limit " << limit; @@ -14614,7 +16135,7 @@ std::pair> MessagesManager::search_call_messages(Me CHECK(fixed_from_message_id.is_valid() && fixed_from_message_id.is_server()); LOG(INFO) << "Search call messages from " << fixed_from_message_id << ", have up to " << first_db_message_id << ", message_count = " << message_count; - if (first_db_message_id.get() < fixed_from_message_id.get() && message_count != -1) { + if (first_db_message_id < fixed_from_message_id && message_count != -1) { LOG(INFO) << "Search messages in database from " << fixed_from_message_id << " and with limit " << limit; MessagesDbCallsQuery db_query; @@ -14714,6 +16235,7 @@ vector MessagesManager::get_active_live_location_messages(Promise auto m = get_message(full_message_id); CHECK(m != nullptr); CHECK(m->content->get_type() == MessageContentType::LiveLocation); + CHECK(!m->message_id.is_scheduled()); if (m->is_failed_to_send) { continue; @@ -14734,6 +16256,10 @@ void MessagesManager::on_load_active_live_location_full_message_ids_from_databas if (value.empty()) { LOG(INFO) << "Active live location messages aren't found in the database"; on_load_active_live_location_messages_finished(); + + if (!active_live_location_full_message_ids_.empty()) { + save_active_live_locations(); + } return; } @@ -14775,7 +16301,11 @@ void MessagesManager::on_load_active_live_location_messages_finished() { void MessagesManager::try_add_active_live_location(DialogId dialog_id, const Message *m) { CHECK(m != nullptr); - if (m->content->get_type() != MessageContentType::LiveLocation || m->is_failed_to_send) { + if (td_->auth_manager_->is_bot()) { + return; + } + if (m->content->get_type() != MessageContentType::LiveLocation || m->message_id.is_scheduled() || + m->message_id.is_local() || m->via_bot_user_id.is_valid() || m->forward_info != nullptr) { return; } @@ -14788,14 +16318,24 @@ void MessagesManager::try_add_active_live_location(DialogId dialog_id, const Mes } void MessagesManager::add_active_live_location(FullMessageId full_message_id) { + if (td_->auth_manager_->is_bot()) { + return; + } if (!active_live_location_full_message_ids_.insert(full_message_id).second) { return; } // TODO add timer for live location expiration + if (!G()->parameters().use_message_db) { + return; + } + if (are_active_live_location_messages_loaded_) { save_active_live_locations(); + } else { + // load active live locations and save after that + get_active_live_location_messages(Auto()); } } @@ -14814,11 +16354,105 @@ void MessagesManager::save_active_live_locations() { } } +void MessagesManager::on_message_live_location_viewed(Dialog *d, const Message *m) { + CHECK(d != nullptr); + CHECK(m != nullptr); + CHECK(m->content->get_type() == MessageContentType::LiveLocation); + CHECK(!m->message_id.is_scheduled()); + + if (td_->auth_manager_->is_bot()) { + // just in case + return; + } + + switch (d->dialog_id.get_type()) { + case DialogType::User: + case DialogType::Chat: + case DialogType::Channel: + // ok + break; + case DialogType::SecretChat: + return; + default: + UNREACHABLE(); + return; + } + if (!d->is_opened) { + return; + } + + if (m->is_outgoing || !m->message_id.is_server() || m->via_bot_user_id.is_valid() || !m->sender_user_id.is_valid() || + td_->contacts_manager_->is_user_bot(m->sender_user_id) || m->forward_info != nullptr) { + return; + } + + auto live_period = get_message_content_live_location_period(m->content.get()); + if (live_period <= G()->unix_time() - m->date + 1) { + // live location is expired + return; + } + + auto &live_location_task_id = d->pending_viewed_live_locations[m->message_id]; + if (live_location_task_id != 0) { + return; + } + + live_location_task_id = ++viewed_live_location_task_id_; + auto &full_message_id = viewed_live_location_tasks_[live_location_task_id]; + full_message_id = FullMessageId(d->dialog_id, m->message_id); + view_message_live_location_on_server_impl(live_location_task_id, full_message_id); +} + +void MessagesManager::view_message_live_location_on_server(int64 task_id) { + if (G()->close_flag()) { + return; + } + + auto it = viewed_live_location_tasks_.find(task_id); + if (it == viewed_live_location_tasks_.end()) { + return; + } + + auto full_message_id = it->second; + Dialog *d = get_dialog(full_message_id.get_dialog_id()); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "view_message_live_location_on_server"); + if (m == nullptr || get_message_content_live_location_period(m->content.get()) <= G()->unix_time() - m->date + 1) { + // the message was deleted or live location is expired + viewed_live_location_tasks_.erase(it); + auto erased_count = d->pending_viewed_live_locations.erase(full_message_id.get_message_id()); + CHECK(erased_count > 0); + return; + } + + view_message_live_location_on_server_impl(task_id, full_message_id); +} + +void MessagesManager::view_message_live_location_on_server_impl(int64 task_id, FullMessageId full_message_id) { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), task_id](Unit result) { + send_closure(actor_id, &MessagesManager::on_message_live_location_viewed_on_server, task_id); + }); + read_message_contents_on_server(full_message_id.get_dialog_id(), {full_message_id.get_message_id()}, 0, + std::move(promise), true); +} + +void MessagesManager::on_message_live_location_viewed_on_server(int64 task_id) { + if (G()->close_flag()) { + return; + } + + auto it = viewed_live_location_tasks_.find(task_id); + if (it == viewed_live_location_tasks_.end()) { + return; + } + + pending_message_live_location_view_timeout_.add_timeout_in(task_id, LIVE_LOCATION_VIEW_PERIOD); +} + FileSourceId MessagesManager::get_message_file_source_id(FullMessageId full_message_id) { auto dialog_id = full_message_id.get_dialog_id(); auto message_id = full_message_id.get_message_id(); - if (!dialog_id.is_valid() || !message_id.is_valid() || dialog_id.get_type() == DialogType::SecretChat || - !message_id.is_server()) { + if (!dialog_id.is_valid() || !(message_id.is_valid() || message_id.is_valid_scheduled()) || + dialog_id.get_type() == DialogType::SecretChat || !message_id.is_any_server()) { return FileSourceId(); } @@ -14859,11 +16493,33 @@ void MessagesManager::remove_message_file_sources(DialogId dialog_id, const Mess } } +void MessagesManager::change_message_files(DialogId dialog_id, const Message *m, const vector &old_file_ids) { + auto new_file_ids = get_message_content_file_ids(m->content.get(), td_); + if (new_file_ids == old_file_ids) { + return; + } + + FullMessageId full_message_id{dialog_id, m->message_id}; + if (need_delete_message_files(dialog_id, m)) { + for (auto file_id : old_file_ids) { + if (!td::contains(new_file_ids, file_id) && need_delete_file(full_message_id, file_id)) { + send_closure(G()->file_manager(), &FileManager::delete_file, file_id, Promise<>(), "change_message_files"); + } + } + } + + auto file_source_id = get_message_file_source_id(full_message_id); + if (file_source_id.is_valid()) { + td_->file_manager_->change_files_source(file_source_id, old_file_ids, new_file_ids); + } +} + MessageId MessagesManager::get_first_database_message_id_by_index(const Dialog *d, SearchMessagesFilter filter) { CHECK(d != nullptr); auto message_id = filter == SearchMessagesFilter::Empty ? d->first_database_message_id : d->first_database_message_id_by_index[search_messages_filter_index(filter)]; + CHECK(!message_id.is_scheduled()); if (!message_id.is_valid()) { if (d->dialog_id.get_type() == DialogType::SecretChat) { LOG(ERROR) << "Invalid first_database_message_id_by_index in " << d->dialog_id; @@ -14886,6 +16542,8 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo } return promise.set_value(Unit()); } + CHECK(!from_message_id.is_scheduled()); + CHECK(!first_db_message_id.is_scheduled()); auto messages = r_messages.move_as_ok(); @@ -14898,11 +16556,12 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo res.reserve(messages.size()); for (auto &message : messages) { - auto m = on_get_message_from_database(dialog_id, d, message, "on_search_dialog_messages_db_result"); - if (m != nullptr && first_db_message_id.get() <= m->message_id.get()) { + auto m = on_get_message_from_database(dialog_id, d, message, false, "on_search_dialog_messages_db_result"); + if (m != nullptr && first_db_message_id <= m->message_id) { if (filter_type == SearchMessagesFilter::UnreadMention && !m->contains_unread_mention) { // skip already read by d->last_read_all_mentions_message_id mentions } else { + CHECK(!m->message_id.is_scheduled()); res.push_back(m->message_id); } } @@ -14911,7 +16570,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo auto &message_count = d->message_count_by_index[search_messages_filter_index(filter_type)]; int32 result_size = narrow_cast(res.size()); bool from_the_end = - from_message_id == MessageId::max() || (offset < 0 && (result_size == 0 || res[0].get() < from_message_id.get())); + from_message_id == MessageId::max() || (offset < 0 && (result_size == 0 || res[0] < from_message_id)); if (message_count < result_size || (message_count > result_size && from_the_end && first_db_message_id == MessageId::min() && result_size < limit + offset)) { LOG(INFO) << "Fix found message count in " << dialog_id << " from " << message_count << " to " << result_size; @@ -15003,7 +16662,7 @@ void MessagesManager::on_messages_db_fts_result(Result resu res.reserve(fts_result.messages.size()); for (auto &message : fts_result.messages) { - auto m = on_get_message_from_database(message.dialog_id, get_dialog_force(message.dialog_id), message.data, + auto m = on_get_message_from_database(message.dialog_id, get_dialog_force(message.dialog_id), message.data, false, "on_messages_db_fts_result"); if (m != nullptr) { res.push_back(FullMessageId(message.dialog_id, m->message_id)); @@ -15018,6 +16677,7 @@ void MessagesManager::on_messages_db_fts_result(Result resu void MessagesManager::on_messages_db_calls_result(Result result, int64 random_id, MessageId first_db_message_id, SearchMessagesFilter filter, Promise<> &&promise) { + CHECK(!first_db_message_id.is_scheduled()); if (result.is_error()) { found_call_messages_.erase(random_id); return promise.set_error(result.move_as_error()); @@ -15030,10 +16690,10 @@ void MessagesManager::on_messages_db_calls_result(Result res.reserve(calls_result.messages.size()); for (auto &message : calls_result.messages) { - auto m = on_get_message_from_database(message.dialog_id, get_dialog_force(message.dialog_id), message.data, + auto m = on_get_message_from_database(message.dialog_id, get_dialog_force(message.dialog_id), message.data, false, "on_messages_db_calls_result"); - if (m != nullptr && first_db_message_id.get() <= m->message_id.get()) { + if (m != nullptr && first_db_message_id <= m->message_id) { res.push_back(FullMessageId(message.dialog_id, m->message_id)); } } @@ -15047,7 +16707,8 @@ void MessagesManager::on_messages_db_calls_result(Result promise.set_value(Unit()); } -std::pair> MessagesManager::search_messages(const string &query, int32 offset_date, +std::pair> MessagesManager::search_messages(FolderId folder_id, bool ignore_folder_id, + const string &query, int32 offset_date, DialogId offset_dialog_id, MessageId offset_message_id, int32 limit, int64 &random_id, Promise &&promise) { @@ -15074,6 +16735,10 @@ std::pair> MessagesManager::search_messages(const s offset_date = std::numeric_limits::max(); } if (!offset_message_id.is_valid()) { + if (offset_message_id.is_valid_scheduled()) { + promise.set_error(Status::Error(3, "Parameter offset_message_id can't be a scheduled message identifier")); + return result; + } offset_message_id = MessageId(); } if (offset_message_id != MessageId() && !offset_message_id.is_server()) { @@ -15096,7 +16761,7 @@ std::pair> MessagesManager::search_messages(const s << offset_dialog_id << ", " << offset_message_id << " and with limit " << limit; td_->create_handler(std::move(promise)) - ->send(query, offset_date, offset_dialog_id, offset_message_id, limit, random_id); + ->send(folder_id, ignore_folder_id, query, offset_date, offset_dialog_id, offset_message_id, limit, random_id); return result; } @@ -15123,7 +16788,7 @@ int64 MessagesManager::get_dialog_message_by_date(DialogId dialog_id, int32 date get_dialog_message_by_date_results_.find(random_id) != get_dialog_message_by_date_results_.end()); get_dialog_message_by_date_results_[random_id]; // reserve place for result - auto message_id = find_message_by_date(d->messages, date); + auto message_id = find_message_by_date(d->messages.get(), date); if (message_id.is_valid() && (message_id == d->last_message_id || get_message(d, message_id)->have_next)) { get_dialog_message_by_date_results_[random_id] = {dialog_id, message_id}; promise.set_value(Unit()); @@ -15145,16 +16810,16 @@ int64 MessagesManager::get_dialog_message_by_date(DialogId dialog_id, int32 date return random_id; } -MessageId MessagesManager::find_message_by_date(const unique_ptr &m, int32 date) { +MessageId MessagesManager::find_message_by_date(const Message *m, int32 date) { if (m == nullptr) { return MessageId(); } if (m->date > date) { - return find_message_by_date(m->left, date); + return find_message_by_date(m->left.get(), date); } - auto message_id = find_message_by_date(m->right, date); + auto message_id = find_message_by_date(m->right.get(), date); if (message_id.is_valid()) { return message_id; } @@ -15167,9 +16832,10 @@ void MessagesManager::on_get_dialog_message_by_date_from_database(DialogId dialo Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); if (result.is_ok()) { - Message *m = on_get_message_from_database(dialog_id, d, result.ok(), "on_get_dialog_message_by_date_from_database"); + Message *m = + on_get_message_from_database(dialog_id, d, result.ok(), false, "on_get_dialog_message_by_date_from_database"); if (m != nullptr) { - auto message_id = find_message_by_date(d->messages, date); + auto message_id = find_message_by_date(d->messages.get(), date); if (!message_id.is_valid()) { LOG(ERROR) << "Failed to find " << m->message_id << " in " << dialog_id << " by date " << date; message_id = m->message_id; @@ -15193,7 +16859,7 @@ void MessagesManager::get_dialog_message_by_date_from_server(const Dialog *d, in return promise.set_value(Unit()); } - auto message_id = find_message_by_date(d->messages, date); + auto message_id = find_message_by_date(d->messages.get(), date); if (message_id.is_valid()) { get_dialog_message_by_date_results_[random_id] = {d->dialog_id, message_id}; } @@ -15224,11 +16890,11 @@ void MessagesManager::on_get_dialog_message_by_date_success(DialogId dialog_id, } if (message_date != 0 && message_date <= date) { result = on_get_message(std::move(message), false, dialog_id.get_type() == DialogType::Channel, false, false, - "on_get_dialog_message_by_date_success"); + false, "on_get_dialog_message_by_date_success"); if (result != FullMessageId()) { const Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - auto message_id = find_message_by_date(d->messages, date); + auto message_id = find_message_by_date(d->messages.get(), date); if (!message_id.is_valid()) { LOG(ERROR) << "Failed to find " << result.get_message_id() << " in " << dialog_id << " by date " << date; message_id = result.get_message_id(); @@ -15317,6 +16983,8 @@ int32 MessagesManager::get_dialog_message_count(DialogId dialog_id, } void MessagesManager::preload_newer_messages(const Dialog *d, MessageId max_message_id) { + CHECK(d != nullptr); + CHECK(max_message_id.is_valid()); if (td_->auth_manager_->is_bot()) { return; } @@ -15329,7 +16997,7 @@ void MessagesManager::preload_newer_messages(const Dialog *d, MessageId max_mess max_message_id = (*p)->message_id; } } - if (limit > 0 && (d->last_message_id == MessageId() || max_message_id.get() < d->last_message_id.get())) { + if (limit > 0 && (d->last_message_id == MessageId() || max_message_id < d->last_message_id)) { // need to preload some new messages LOG(INFO) << "Preloading newer after " << max_message_id; load_messages(d->dialog_id, max_message_id, -MAX_GET_HISTORY + 1, MAX_GET_HISTORY, 3, false, Promise()); @@ -15337,6 +17005,8 @@ void MessagesManager::preload_newer_messages(const Dialog *d, MessageId max_mess } void MessagesManager::preload_older_messages(const Dialog *d, MessageId min_message_id) { + CHECK(d != nullptr); + CHECK(min_message_id.is_valid()); if (td_->auth_manager_->is_bot()) { return; } @@ -15360,16 +17030,19 @@ void MessagesManager::preload_older_messages(const Dialog *d, MessageId min_mess } } -unique_ptr MessagesManager::parse_message(DialogId dialog_id, const BufferSlice &value) { +unique_ptr MessagesManager::parse_message(DialogId dialog_id, const BufferSlice &value, + bool is_scheduled) { LOG(INFO) << "Loaded message of size " << value.size() << " from database"; auto m = make_unique(); auto status = log_event_parse(*m, value.as_slice()); - if (status.is_error() || !m->message_id.is_valid()) { + bool is_message_id_valid = is_scheduled ? m->message_id.is_valid_scheduled() : m->message_id.is_valid(); + if (status.is_error() || !is_message_id_valid) { // can't happen unless database is broken, but has been seen in the wild LOG(ERROR) << "Receive invalid message from database: " << m->message_id << ' ' << status << ' ' << format::as_hex_dump<4>(value.as_slice()); - if (dialog_id.get_type() != DialogType::SecretChat && m->message_id.is_valid() && m->message_id.is_server()) { + if (!is_scheduled && dialog_id.get_type() != DialogType::SecretChat && m->message_id.is_valid() && + m->message_id.is_server()) { // trying to repair the message get_message_from_server({dialog_id, m->message_id}, Auto()); } @@ -15384,6 +17057,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId vector &&messages, Promise &&promise) { CHECK(-limit < offset && offset <= 0); CHECK(offset < 0 || from_the_end); + CHECK(!from_message_id.is_scheduled()); if (!have_input_peer(dialog_id, AccessRights::Read)) { LOG(WARNING) << "Ignore result of get_history_from_database in " << dialog_id; @@ -15426,7 +17100,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId if (!d->first_database_message_id.is_valid() && !d->have_full_history) { break; } - auto message = parse_message(dialog_id, std::move(message_slice)); + auto message = parse_message(dialog_id, std::move(message_slice), false); if (message == nullptr) { if (d->have_full_history) { d->have_full_history = false; @@ -15434,7 +17108,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } break; } - if (message->message_id.get() >= debug_cur_message_id.get()) { + if (message->message_id >= debug_cur_message_id) { // TODO move to ERROR LOG(FATAL) << "Receive message " << message->message_id << " after " << debug_cur_message_id << " from database in the history of " << dialog_id << " from " << from_message_id << " with offset " @@ -15443,7 +17117,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId } debug_cur_message_id = message->message_id; - if (message->message_id.get() < d->first_database_message_id.get()) { + if (message->message_id < d->first_database_message_id) { if (d->have_full_history) { LOG(ERROR) << "Have full history in the " << dialog_id << " and receive " << message->message_id << " from database, but first database message is " << d->first_database_message_id; @@ -15451,9 +17125,8 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId break; } } - if (!have_next && - (from_the_end || (is_first && offset < -1 && message->message_id.get() <= from_message_id.get())) && - message->message_id.get() < d->last_message_id.get()) { + if (!have_next && (from_the_end || (is_first && offset < -1 && message->message_id <= from_message_id)) && + message->message_id < d->last_message_id) { // last message in the dialog must be attached to the next local message have_next = true; } @@ -15483,7 +17156,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId added_new_message = true; } if (next_message != nullptr && !next_message->have_previous) { - LOG_CHECK(m->message_id.get() < next_message->message_id.get()) + LOG_CHECK(m->message_id < next_message->message_id) << m->message_id << ' ' << next_message->message_id << ' ' << debug_cur_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 << ' ' @@ -15517,7 +17190,7 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId if (from_the_end && last_added_message_id.is_valid()) { // CHECK(d->first_database_message_id.is_valid()); - // CHECK(last_added_message_id.get() >= d->first_database_message_id.get()); + // CHECK(last_added_message_id >= d->first_database_message_id); if ((had_full_history || d->have_full_history) && !d->last_new_message_id.is_valid() && (last_added_message_id.is_server() || d->dialog_id.get_type() == DialogType::SecretChat)) { LOG(ERROR) << "Trying to hard fix " << d->dialog_id << " last new message to " << last_added_message_id @@ -15525,15 +17198,14 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId d->last_new_message_id = last_added_message_id; on_dialog_updated(d->dialog_id, "on_get_history_from_database 3"); } - if (last_added_message_id.get() > d->last_message_id.get() && d->last_new_message_id.is_valid()) { + if (last_added_message_id > d->last_message_id && d->last_new_message_id.is_valid()) { set_dialog_last_message_id(d, last_added_message_id, "on_get_history_from_database 4"); need_update_dialog_pos = true; } - if (last_added_message_id.get() != d->last_database_message_id.get() && d->last_new_message_id.is_valid()) { + if (last_added_message_id != d->last_database_message_id && d->last_new_message_id.is_valid()) { auto debug_last_database_message_id = d->last_database_message_id; set_dialog_last_database_message_id(d, last_added_message_id, "on_get_history_from_database 5"); - if (last_added_message_id.get() < d->first_database_message_id.get() || - !d->first_database_message_id.is_valid()) { + if (last_added_message_id < d->first_database_message_id || !d->first_database_message_id.is_valid()) { CHECK(next_message != nullptr); LOG_CHECK(had_full_history || d->have_full_history) << had_full_history << ' ' << d->have_full_history << ' ' << next_message->message_id << ' ' @@ -15541,8 +17213,8 @@ void MessagesManager::on_get_history_from_database(DialogId dialog_id, MessageId << ' ' << d->last_database_message_id << ' ' << debug_last_database_message_id << ' ' << dialog_id << ' ' << d->last_new_message_id << ' ' << debug_last_new_message_id << ' ' << d->last_message_id << ' ' << debug_last_message_id; - CHECK(next_message->message_id.get() <= d->last_database_message_id.get()); - LOG(ERROR) << "Fix first database message id in " << dialog_id << " from " << d->first_database_message_id + CHECK(next_message->message_id <= d->last_database_message_id); + LOG(ERROR) << "Fix first database message in " << dialog_id << " from " << d->first_database_message_id << " to " << next_message->message_id; set_dialog_first_database_message_id(d, next_message->message_id, "on_get_history_from_database 6"); } @@ -15593,6 +17265,7 @@ void MessagesManager::get_history_from_the_end(DialogId dialog_id, bool from_dat void MessagesManager::get_history(DialogId dialog_id, MessageId from_message_id, int32 offset, int32 limit, bool from_database, bool only_local, Promise &&promise) { CHECK(dialog_id.is_valid()); + CHECK(from_message_id.is_valid()); if (!have_input_peer(dialog_id, AccessRights::Read)) { // can't get history in dialogs without read access return promise.set_value(Unit()); @@ -15663,6 +17336,195 @@ 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) { + if (G()->close_flag()) { + promise.set_error(Status::Error(500, "Request aborted")); + return {}; + } + + const Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + promise.set_error(Status::Error(6, "Chat not found")); + return {}; + } + if (!have_input_peer(dialog_id, AccessRights::Read)) { + promise.set_error(Status::Error(5, "Can't access the chat")); + return {}; + } + if (is_broadcast_channel(dialog_id) && + !td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()).can_post_messages()) { + promise.set_error(Status::Error(3, "Not enough rights to get scheduled messages")); + return {}; + } + + if (!d->has_loaded_scheduled_messages_from_database) { + load_dialog_scheduled_messages(dialog_id, true, 0, std::move(promise)); + return {}; + } + + vector message_ids; + find_old_messages(d->scheduled_messages.get(), + MessageId(ScheduledServerMessageId(), std::numeric_limits::max(), true), message_ids); + std::reverse(message_ids.begin(), message_ids.end()); + + if (G()->parameters().use_message_db) { + bool has_scheduled_database_messages = false; + for (auto &message_id : message_ids) { + CHECK(message_id.is_valid_scheduled()); + if (!message_id.is_yet_unsent()) { + has_scheduled_database_messages = true; + break; + } + } + set_dialog_has_scheduled_database_messages(d->dialog_id, has_scheduled_database_messages); + } + + if (d->scheduled_messages_sync_generation != scheduled_messages_sync_generation_) { + vector numbers; + for (auto &message_id : message_ids) { + if (!message_id.is_scheduled_server()) { + continue; + } + + numbers.push_back(message_id.get_scheduled_server_message_id().get()); + const Message *m = get_message(d, message_id); + CHECK(m != nullptr); + CHECK(m->message_id.get_scheduled_server_message_id() == message_id.get_scheduled_server_message_id()); + numbers.push_back(m->edit_date); + numbers.push_back(m->date); + } + auto hash = get_vector_hash(numbers); + + if (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()); + } + + promise.set_value(Unit()); + return message_ids; +} + +void MessagesManager::load_dialog_scheduled_messages(DialogId dialog_id, bool from_database, int32 hash, + Promise &&promise) { + if (G()->parameters().use_message_db && from_database) { + LOG(INFO) << "Load scheduled messages from database in " << dialog_id; + auto &queries = load_scheduled_messages_from_database_queries_[dialog_id]; + queries.push_back(std::move(promise)); + if (queries.size() == 1) { + G()->td_db()->get_messages_db_async()->get_scheduled_messages( + dialog_id, 1000, + PromiseCreator::lambda([dialog_id, actor_id = actor_id(this)](std::vector messages) { + send_closure(actor_id, &MessagesManager::on_get_scheduled_messages_from_database, dialog_id, + std::move(messages)); + })); + } + } else { + td_->create_handler(std::move(promise)) + ->send(dialog_id, hash, scheduled_messages_sync_generation_); + } +} + +void MessagesManager::on_get_scheduled_messages_from_database(DialogId dialog_id, vector &&messages) { + auto d = get_dialog(dialog_id); + CHECK(d != nullptr); + d->has_loaded_scheduled_messages_from_database = true; + + LOG(INFO) << "Receive " << messages.size() << " scheduled messages from database in " << dialog_id; + + Dependencies dependencies; + vector added_message_ids; + for (auto &message_slice : messages) { + auto message = parse_message(dialog_id, std::move(message_slice), true); + if (message == nullptr) { + continue; + } + message->from_database = true; + + if (get_message(d, message->message_id) != nullptr) { + continue; + } + + auto web_page_id = get_message_content_web_page_id(message->content.get()); + if (web_page_id.is_valid()) { + td_->web_pages_manager_->have_web_page_force(web_page_id); + } + bool need_update = false; + Message *m = add_scheduled_message_to_dialog(d, std::move(message), false, &need_update, + "on_get_scheduled_messages_from_database"); + if (m != nullptr) { + add_message_dependencies(dependencies, dialog_id, m); + added_message_ids.push_back(m->message_id); + } + } + resolve_dependencies_force(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); + + auto it = load_scheduled_messages_from_database_queries_.find(dialog_id); + CHECK(it != load_scheduled_messages_from_database_queries_.end()); + CHECK(!it->second.empty()); + auto promises = std::move(it->second); + load_scheduled_messages_from_database_queries_.erase(it); + + for (auto &promise : promises) { + promise.set_value(Unit()); + } +} + +Result MessagesManager::get_message_schedule_date( + td_api::object_ptr &&scheduling_state) { + if (scheduling_state == nullptr) { + return 0; + } + + switch (scheduling_state->get_id()) { + case td_api::messageSchedulingStateSendWhenOnline::ID: + return static_cast(SCHEDULE_WHEN_ONLINE_DATE); + case td_api::messageSchedulingStateSendAtDate::ID: { + auto send_at_date = td_api::move_object_as(scheduling_state); + auto send_date = send_at_date->send_date_; + if (send_date <= 0) { + return Status::Error(400, "Invalid send date specified"); + } + if (send_date <= G()->unix_time() + 10) { + return 0; + } + if (send_date - G()->unix_time() > 367 * 86400) { + return Status::Error(400, "Send date is too far in the future"); + } + return send_date; + } + default: + UNREACHABLE(); + return 0; + } +} + +tl_object_ptr MessagesManager::get_message_sending_state_object(const Message *m) { + CHECK(m != nullptr); + if (m->message_id.is_yet_unsent()) { + return td_api::make_object(); + } + if (m->is_failed_to_send) { + return td_api::make_object( + m->send_error_code, m->send_error_message, can_resend_message(m), max(m->try_resend_at - Time::now(), 0.0)); + } + return nullptr; +} + +tl_object_ptr MessagesManager::get_message_scheduling_state_object(int32 send_date) { + if (send_date == SCHEDULE_WHEN_ONLINE_DATE) { + return td_api::make_object(); + } + return td_api::make_object(send_date); +} + tl_object_ptr MessagesManager::get_message_object(FullMessageId full_message_id) { return get_message_object(full_message_id.get_dialog_id(), get_message_force(full_message_id, "get_message_object")); } @@ -15673,14 +17535,7 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial return nullptr; } - // TODO get_message_sending_state_object - tl_object_ptr sending_state; - if (m->is_failed_to_send) { - sending_state = make_tl_object( - m->send_error_code, m->send_error_message, can_resend_message(m), max(m->try_resend_at - Time::now(), 0.0)); - } else if (m->message_id.is_yet_unsent()) { - sending_state = make_tl_object(); - } + auto sending_state = get_message_sending_state_object(m); if (for_event_log) { CHECK(m->message_id.is_server()); @@ -15695,6 +17550,7 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial can_delete = can_delete_channel_message(dialog_status, m, is_bot); } + 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); @@ -15717,6 +17573,9 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial if (for_event_log) { can_delete_for_self = false; can_delete_for_all_users = false; + } else if (is_scheduled) { + can_delete_for_self = (dialog_id == my_dialog_id); + can_delete_for_all_users = !can_delete_for_self; } bool is_outgoing = m->is_outgoing; @@ -15725,7 +17584,7 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial // a forwarded message is outgoing, only if it doesn't have from_dialog_id and its sender isn't hidden // i.e. a message is incoming only if it's a forwarded message with known from_dialog_id or with a hidden sender auto forward_info = m->forward_info.get(); - is_outgoing = forward_info == nullptr || + is_outgoing = is_scheduled || forward_info == nullptr || (!forward_info->from_dialog_id.is_valid() && !is_forward_info_sender_hidden(forward_info)); } @@ -15740,19 +17599,23 @@ tl_object_ptr MessagesManager::get_message_object(DialogId dial } else { 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_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(); bool contains_unread_mention = for_event_log ? false : m->contains_unread_mention; auto live_location_date = m->is_failed_to_send ? 0 : m->date; + auto date = is_scheduled ? 0 : m->date; + auto edit_date = m->hide_edit_date ? 0 : m->edit_date; return make_tl_object( m->message_id.get(), td_->contacts_manager_->get_user_id_object(m->sender_user_id, "sender_user_id"), - dialog_id.get(), std::move(sending_state), is_outgoing, can_be_edited, can_be_forwarded, can_delete_for_self, - can_delete_for_all_users, m->is_channel_post, contains_unread_mention, m->date, m->edit_date, - get_message_forward_info_object(m->forward_info), reply_to_message_id, ttl, ttl_expires_in, + dialog_id.get(), std::move(sending_state), std::move(scheduling_state), is_outgoing, can_be_edited, + can_be_forwarded, can_delete_for_self, can_delete_for_all_users, m->is_channel_post, contains_unread_mention, + date, edit_date, get_message_forward_info_object(m->forward_info), reply_to_message_id, ttl, ttl_expires_in, td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id"), m->author_signature, m->views, - media_album_id, get_message_content_object(m->content.get(), td_, live_location_date, m->is_content_secret), + media_album_id, get_restriction_reason_description(m->restriction_reasons), + get_message_content_object(m->content.get(), td_, live_location_date, m->is_content_secret), get_reply_markup_object(m->reply_markup)); } @@ -15781,43 +17644,45 @@ tl_object_ptr MessagesManager::get_messages_object( return td_api::make_object(total_count, std::move(messages)); } -MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, MessageId reply_to_message_id, - bool disable_notification, bool from_background, - unique_ptr &&content, - bool *need_update_dialog_pos, - unique_ptr forward_info) { +MessagesManager::Message *MessagesManager::get_message_to_send( + Dialog *d, MessageId reply_to_message_id, const SendMessageOptions &options, unique_ptr &&content, + bool *need_update_dialog_pos, unique_ptr forward_info, bool is_copy) { CHECK(d != nullptr); - MessageId message_id = get_next_yet_unsent_message_id(d); + CHECK(!reply_to_message_id.is_scheduled()); + + bool is_scheduled = options.schedule_date != 0; DialogId dialog_id = d->dialog_id; + MessageId message_id = is_scheduled ? get_next_yet_unsent_scheduled_message_id(d, options.schedule_date) + : get_next_yet_unsent_message_id(d); LOG(INFO) << "Create " << message_id << " in " << dialog_id; auto dialog_type = dialog_id.get_type(); auto my_id = td_->contacts_manager_->get_my_id(); auto m = make_unique(); - m->random_y = get_random_y(message_id); - m->message_id = message_id; + set_message_id(m, message_id); bool is_channel_post = is_broadcast_channel(dialog_id); if (is_channel_post) { // sender of the post can be hidden - if (td_->contacts_manager_->get_channel_sign_messages(dialog_id.get_channel_id())) { + if (!is_scheduled && td_->contacts_manager_->get_channel_sign_messages(dialog_id.get_channel_id())) { m->author_signature = td_->contacts_manager_->get_user_title(my_id); } } else { m->sender_user_id = my_id; } - m->date = G()->unix_time(); - m->send_date = m->date; + m->send_date = G()->unix_time(); + m->date = is_scheduled ? options.schedule_date : m->send_date; m->reply_to_message_id = reply_to_message_id; m->is_channel_post = is_channel_post; - m->is_outgoing = dialog_id != DialogId(my_id); - m->from_background = from_background; + m->is_outgoing = is_scheduled || dialog_id != DialogId(my_id); + m->from_background = options.from_background; m->views = is_channel_post ? 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() || disable_notification) { - m->disable_notification = disable_notification; + if (td_->auth_manager_->is_bot() || options.disable_notification) { + m->disable_notification = options.disable_notification; } else { auto notification_settings = get_dialog_notification_settings(dialog_id, true); CHECK(notification_settings != nullptr); @@ -15825,6 +17690,7 @@ MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, Messag } if (dialog_type == DialogType::SecretChat) { + CHECK(!is_scheduled); m->ttl = td_->contacts_manager_->get_secret_chat_ttl(dialog_id.get_secret_chat_id()); if (is_service_message_content(m->content->get_type())) { m->ttl = 0; @@ -15852,6 +17718,7 @@ MessagesManager::Message *MessagesManager::get_message_to_send(Dialog *d, Messag 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); return result; } @@ -16093,7 +17960,7 @@ Status MessagesManager::can_send_message_content(DialogId dialog_id, const Messa } MessageId MessagesManager::get_persistent_message_id(const Dialog *d, MessageId message_id) const { - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { return MessageId(); } if (message_id.is_yet_unsent()) { @@ -16114,12 +17981,12 @@ MessageId MessagesManager::get_reply_to_message_id(Dialog *d, MessageId message_ return MessageId(); } message_id = get_persistent_message_id(d, message_id); - const Message *reply_to_message = get_message_force(d, message_id, "get_reply_to_message_id"); - if (reply_to_message == nullptr || message_id.is_yet_unsent() || - (message_id.is_local() && d->dialog_id.get_type() != DialogType::SecretChat)) { + const Message *m = get_message_force(d, message_id, "get_reply_to_message_id"); + if (m == nullptr || m->message_id.is_yet_unsent() || + (m->message_id.is_local() && d->dialog_id.get_type() != DialogType::SecretChat)) { if (message_id.is_server() && d->dialog_id.get_type() != DialogType::SecretChat && - message_id.get() > d->last_new_message_id.get() && message_id.get() <= d->max_notification_message_id.get()) { - // allow to reply yer unreceived server message + message_id > d->last_new_message_id && message_id <= d->max_notification_message_id) { + // allow to reply yet unreceived server message return message_id; } @@ -16127,7 +17994,7 @@ MessageId MessagesManager::get_reply_to_message_id(Dialog *d, MessageId message_ // TODO replies to yet unsent messages can be allowed with special handling of them on application restart return MessageId(); } - return message_id; + return m->message_id; } vector MessagesManager::get_message_file_ids(const Message *m) const { @@ -16154,9 +18021,10 @@ void MessagesManager::cancel_upload_file(FileId file_id) { send_closure_later(G()->file_manager(), &FileManager::cancel_upload, file_id); } -void MessagesManager::cancel_send_message_query(DialogId dialog_id, unique_ptr &m) { +void MessagesManager::cancel_send_message_query(DialogId dialog_id, Message *m) { CHECK(m != nullptr); CHECK(m->content != nullptr); + CHECK(m->message_id.is_valid() || m->message_id.is_valid_scheduled()); CHECK(m->message_id.is_yet_unsent()); LOG(INFO) << "Cancel send message query for " << m->message_id; @@ -16191,24 +18059,35 @@ void MessagesManager::cancel_send_message_query(DialogId dialog_id, unique_ptrmessage_id, Status::OK()); } - if (G()->parameters().use_file_db) { // ResourceManager::Mode::Baseline + if (!m->message_id.is_scheduled() && G()->parameters().use_file_db && + !m->is_copy) { // ResourceManager::Mode::Baseline auto queue_id = get_sequence_dispatcher_id(dialog_id, m->content->get_type()); if (queue_id & 1) { auto queue_it = yet_unsent_media_queues_.find(queue_id); if (queue_it != yet_unsent_media_queues_.end()) { auto &queue = queue_it->second; LOG(INFO) << "Delete " << m->message_id << " from queue " << queue_id; - queue.erase(m->message_id.get()); - if (queue.empty()) { - yet_unsent_media_queues_.erase(queue_it); - } else { - on_yet_unsent_media_queue_updated(dialog_id); + if (queue.erase(m->message_id.get()) != 0) { + if (queue.empty()) { + yet_unsent_media_queues_.erase(queue_it); + } else { + on_yet_unsent_media_queue_updated(dialog_id); + } } } } } } +void MessagesManager::cancel_send_deleted_message(DialogId dialog_id, Message *m, bool is_permanently_deleted) { + CHECK(m != nullptr); + if (m->message_id.is_yet_unsent()) { + cancel_send_message_query(dialog_id, m); + } else if (is_permanently_deleted || !m->message_id.is_scheduled()) { + cancel_edit_message_media(dialog_id, m, "Message was deleted"); + } +} + bool MessagesManager::is_message_auto_read(DialogId dialog_id, bool is_outgoing) const { switch (dialog_id.get_type()) { case DialogType::User: { @@ -16328,14 +18207,12 @@ class MessagesManager::SendMessageLogEvent { template void parse(ParserT &parser) { td::parse(dialog_id, parser); - CHECK(m_out == nullptr); - m_out = make_unique(); - td::parse(*m_out, parser); + td::parse(m_out, parser); } }; Result MessagesManager::send_message(DialogId dialog_id, MessageId reply_to_message_id, - bool disable_notification, bool from_background, + tl_object_ptr &&options, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content) { if (input_message_content == nullptr) { @@ -16346,8 +18223,8 @@ Result MessagesManager::send_message(DialogId dialog_id, MessageId re if (input_message_content->get_id() == td_api::inputMessageForwarded::ID) { auto input_message = static_cast(input_message_content.get()); return forward_message(dialog_id, DialogId(input_message->from_chat_id_), MessageId(input_message->message_id_), - disable_notification, from_background, input_message->in_game_share_, - input_message->send_copy_, input_message->remove_caption_); + std::move(options), input_message->in_game_share_, input_message->send_copy_, + input_message->remove_caption_); } Dialog *d = get_dialog_force(dialog_id); @@ -16358,13 +18235,15 @@ Result MessagesManager::send_message(DialogId dialog_id, MessageId re TRY_STATUS(can_send_message(dialog_id)); TRY_RESULT(message_reply_markup, get_dialog_reply_markup(dialog_id, std::move(reply_markup))); TRY_RESULT(message_content, process_input_message_content(dialog_id, std::move(input_message_content))); + TRY_RESULT(send_message_options, process_send_message_options(dialog_id, std::move(options))); + TRY_STATUS(can_use_send_message_options(send_message_options, message_content)); // there must be no errors after get_message_to_send call bool need_update_dialog_pos = false; - Message *m = get_message_to_send( - d, get_reply_to_message_id(d, reply_to_message_id), disable_notification, from_background, - dup_message_content(td_, dialog_id, message_content.content.get(), false), &need_update_dialog_pos); + Message *m = get_message_to_send(d, get_reply_to_message_id(d, reply_to_message_id), send_message_options, + dup_message_content(td_, dialog_id, message_content.content.get(), false), + &need_update_dialog_pos, nullptr, message_content.via_bot_user_id.is_valid()); m->reply_markup = std::move(message_reply_markup); m->via_bot_user_id = message_content.via_bot_user_id; m->disable_web_page_preview = message_content.disable_web_page_preview; @@ -16447,6 +18326,59 @@ Result MessagesManager::process_input_message_content( return std::move(content); } +Result MessagesManager::process_send_message_options( + DialogId dialog_id, tl_object_ptr &&options) const { + SendMessageOptions result; + if (options != nullptr) { + result.disable_notification = options->disable_notification_; + result.from_background = options->from_background_; + TRY_RESULT_ASSIGN(result.schedule_date, get_message_schedule_date(std::move(options->scheduling_state_))); + } + + auto dialog_type = dialog_id.get_type(); + bool is_secret = dialog_type == DialogType::SecretChat; + if (result.disable_notification && is_secret) { + return Status::Error(400, "Can't send messages with silent notifications to secret chats"); + } + if (result.schedule_date != 0) { + if (is_secret) { + return Status::Error(400, "Can't schedule messages in secret chats"); + } + if (td_->auth_manager_->is_bot()) { + return Status::Error(400, "Bots can't send scheduled messages"); + } + } + if (result.schedule_date == SCHEDULE_WHEN_ONLINE_DATE) { + if (dialog_type != DialogType::User) { + return Status::Error(400, "Messages can be scheduled till online only in private chats"); + } + if (dialog_id == get_my_dialog_id()) { + return Status::Error(400, "Can't scheduled till online messages in chat with self"); + } + } + + return result; +} + +Status MessagesManager::can_use_send_message_options(const SendMessageOptions &options, + const unique_ptr &content, int32 ttl) { + if (options.schedule_date != 0) { + if (ttl > 0) { + return Status::Error(400, "Can't send scheduled self-destructing messages"); + } + if (content->get_type() == MessageContentType::LiveLocation) { + return Status::Error(400, "Can't send scheduled live location messages"); + } + } + + return Status::OK(); +} + +Status MessagesManager::can_use_send_message_options(const SendMessageOptions &options, + const InputMessageContent &content) { + return can_use_send_message_options(options, content.content, content.ttl); +} + int64 MessagesManager::generate_new_media_album_id() { int64 media_album_id = 0; do { @@ -16456,7 +18388,7 @@ int64 MessagesManager::generate_new_media_album_id() { } Result> MessagesManager::send_message_group( - DialogId dialog_id, MessageId reply_to_message_id, bool disable_notification, bool from_background, + DialogId dialog_id, MessageId reply_to_message_id, tl_object_ptr &&options, vector> &&input_message_contents) { if (input_message_contents.size() > MAX_GROUPED_MESSAGES) { return Status::Error(4, "Too much messages to send as an album"); @@ -16471,10 +18403,12 @@ Result> MessagesManager::send_message_group( } TRY_STATUS(can_send_message(dialog_id)); + TRY_RESULT(send_message_options, process_send_message_options(dialog_id, std::move(options))); vector, int32>> message_contents; for (auto &input_message_content : input_message_contents) { TRY_RESULT(message_content, process_input_message_content(dialog_id, std::move(input_message_content))); + TRY_STATUS(can_use_send_message_options(send_message_options, message_content)); if (!is_allowed_media_group_content(message_content.content->get_type())) { return Status::Error(5, "Wrong message content type"); } @@ -16494,7 +18428,7 @@ Result> MessagesManager::send_message_group( vector result; bool need_update_dialog_pos = false; for (auto &message_content : message_contents) { - Message *m = get_message_to_send(d, reply_to_message_id, disable_notification, from_background, + Message *m = get_message_to_send(d, reply_to_message_id, send_message_options, dup_message_content(td_, dialog_id, message_content.first.get(), false), &need_update_dialog_pos); result.push_back(m->message_id); @@ -16532,7 +18466,7 @@ void MessagesManager::save_send_message_logevent(DialogId dialog_id, const Messa } void MessagesManager::do_send_message(DialogId dialog_id, const Message *m, vector bad_parts) { - bool is_edit = m->message_id.is_server(); + bool is_edit = m->message_id.is_any_server(); LOG(INFO) << "Do " << (is_edit ? "edit" : "send") << ' ' << FullMessageId(dialog_id, m->message_id); bool is_secret = dialog_id.get_type() == DialogType::SecretChat; @@ -16549,20 +18483,22 @@ void MessagesManager::do_send_message(DialogId dialog_id, const Message *m, vect CHECK(content != nullptr); auto content_type = content->get_type(); if (content_type == MessageContentType::Text) { + CHECK(!is_edit); const FormattedText *message_text = get_message_content_text(content); CHECK(message_text != nullptr); int64 random_id = begin_send_message(dialog_id, m); if (is_secret) { + CHECK(!m->message_id.is_scheduled()); auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id()); send_closure(td_->create_net_actor(), &SendSecretMessageActor::send, dialog_id, m->reply_to_random_id, m->ttl, message_text->text, get_secret_input_media(content, td_, nullptr, BufferSlice(), layer), - get_input_secret_message_entities(message_text->entities), m->via_bot_user_id, m->media_album_id, - random_id); + get_input_secret_message_entities(message_text->entities, layer), m->via_bot_user_id, + m->media_album_id, random_id); } else { send_closure(td_->create_net_actor(), &SendMessageActor::send, get_message_flags(m), dialog_id, - m->reply_to_message_id, get_input_reply_markup(m->reply_markup), + m->reply_to_message_id, get_message_schedule_date(m), get_input_reply_markup(m->reply_markup), get_input_message_entities(td_->contacts_manager_.get(), message_text->entities, "do_send_message"), message_text->text, random_id, &m->send_query_ref, get_sequence_dispatcher_id(dialog_id, content_type)); @@ -16575,6 +18511,7 @@ void MessagesManager::do_send_message(DialogId dialog_id, const Message *m, vect FileId thumbnail_file_id = get_message_content_thumbnail_file_id(content, td_); LOG(DEBUG) << "Need to send file " << file_id << " with thumbnail " << thumbnail_file_id; if (is_secret) { + CHECK(!is_edit); auto layer = td_->contacts_manager_->get_secret_chat_layer(dialog_id.get_secret_chat_id()); auto secret_input_media = get_secret_input_media(content, td_, nullptr, BufferSlice(), layer); if (secret_input_media.empty()) { @@ -16615,25 +18552,26 @@ void MessagesManager::on_message_media_uploaded(DialogId dialog_id, const Messag CHECK(input_media != nullptr); auto message_id = m->message_id; - if (message_id.is_server()) { + if (message_id.is_any_server()) { const FormattedText *caption = get_message_content_caption(m->edited_content.get()); auto input_reply_markup = get_input_reply_markup(m->edited_reply_markup); bool was_uploaded = FileManager::extract_was_uploaded(input_media); bool was_thumbnail_uploaded = FileManager::extract_was_thumbnail_uploaded(input_media); LOG(INFO) << "Edit media from " << message_id << " in " << dialog_id; + auto schedule_date = get_message_schedule_date(m); auto promise = PromiseCreator::lambda( - [actor_id = actor_id(this), dialog_id, message_id, file_id, thumbnail_file_id, generation = m->edit_generation, - was_uploaded, was_thumbnail_uploaded, + [actor_id = actor_id(this), dialog_id, message_id, file_id, thumbnail_file_id, schedule_date, + generation = m->edit_generation, was_uploaded, was_thumbnail_uploaded, file_reference = FileManager::extract_file_reference(input_media)](Result result) mutable { send_closure(actor_id, &MessagesManager::on_message_media_edited, dialog_id, message_id, file_id, - thumbnail_file_id, was_uploaded, was_thumbnail_uploaded, std::move(file_reference), generation, - std::move(result)); + thumbnail_file_id, was_uploaded, was_thumbnail_uploaded, std::move(file_reference), + schedule_date, generation, std::move(result)); }); send_closure(td_->create_net_actor(std::move(promise)), &EditMessageActor::send, 1 << 11, dialog_id, message_id, caption == nullptr ? "" : caption->text, get_input_message_entities(td_->contacts_manager_.get(), caption, "edit_message_media"), - std::move(input_media), std::move(input_reply_markup), + std::move(input_media), std::move(input_reply_markup), schedule_date, get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); return; } @@ -16655,11 +18593,13 @@ void MessagesManager::on_message_media_uploaded(DialogId dialog_id, const Messag LOG(INFO) << "Send 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); - send_closure(td_->create_net_actor(), &SendMediaActor::send, file_id, thumbnail_file_id, - get_message_flags(m), dialog_id, m->reply_to_message_id, get_input_reply_markup(m->reply_markup), - get_input_message_entities(td_->contacts_manager_.get(), caption, "on_message_media_uploaded"), - caption == nullptr ? "" : caption->text, std::move(input_media), random_id, &m->send_query_ref, - get_sequence_dispatcher_id(dialog_id, m->content->get_type())); + send_closure( + td_->create_net_actor(), &SendMediaActor::send, file_id, thumbnail_file_id, + get_message_flags(m), dialog_id, m->reply_to_message_id, get_message_schedule_date(m), + get_input_reply_markup(m->reply_markup), + get_input_message_entities(td_->contacts_manager_.get(), caption, "on_message_media_uploaded"), + caption == nullptr ? "" : caption->text, std::move(input_media), random_id, &m->send_query_ref, + get_sequence_dispatcher_id(dialog_id, m->is_copy ? MessageContentType::None : m->content->get_type())); })); } else { switch (input_media->get_id()) { @@ -16691,6 +18631,7 @@ void MessagesManager::on_secret_message_media_uploaded(DialogId dialog_id, const SecretInputMedia &&secret_input_media, FileId file_id, FileId thumbnail_file_id) { CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); CHECK(!secret_input_media.empty()); /* if (m->media_album_id != 0) { @@ -16736,6 +18677,7 @@ void MessagesManager::on_upload_message_media_success(DialogId dialog_id, Messag Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); + CHECK(message_id.is_valid() || message_id.is_valid_scheduled()); CHECK(message_id.is_yet_unsent()); Message *m = get_message(d, message_id); if (m == nullptr) { @@ -16764,7 +18706,7 @@ void MessagesManager::on_upload_message_media_success(DialogId dialog_id, Messag } send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id, dialog_id, - message_id, std::move(result)); + m->message_id, std::move(result)); } void MessagesManager::on_upload_message_media_file_part_missing(DialogId dialog_id, MessageId message_id, @@ -16816,7 +18758,7 @@ void MessagesManager::on_upload_message_media_fail(DialogId dialog_id, MessageId CHECK(dialog_id.get_type() != DialogType::SecretChat); send_closure_later(actor_id(this), &MessagesManager::on_upload_message_media_finished, m->media_album_id, dialog_id, - message_id, std::move(error)); + m->message_id, std::move(error)); } void MessagesManager::on_upload_message_media_finished(int64 media_album_id, DialogId dialog_id, MessageId message_id, @@ -16888,6 +18830,8 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { vector> input_single_media; MessageId reply_to_message_id; int32 flags = 0; + int32 schedule_date = 0; + bool is_copy = false; for (size_t i = 0; i < request.message_ids.size(); i++) { auto *m = get_message(d, request.message_ids[i]); if (m == nullptr) { @@ -16898,6 +18842,8 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { reply_to_message_id = m->reply_to_message_id; flags = get_message_flags(m); + schedule_date = get_message_schedule_date(m); + is_copy = m->is_copy; file_ids.push_back(get_message_content_any_file_id(m->content.get())); random_ids.push_back(begin_send_message(dialog_id, m)); @@ -16959,15 +18905,15 @@ void MessagesManager::do_send_message_group(int64 media_album_id) { LOG(INFO) << "Media group " << media_album_id << " from " << dialog_id << " is empty"; } send_closure(td_->create_net_actor(), &SendMultiMediaActor::send, flags, dialog_id, - reply_to_message_id, std::move(file_ids), std::move(input_single_media), - get_sequence_dispatcher_id(dialog_id, MessageContentType::Photo)); + reply_to_message_id, schedule_date, std::move(file_ids), std::move(input_single_media), + get_sequence_dispatcher_id(dialog_id, is_copy ? MessageContentType::None : MessageContentType::Photo)); } void MessagesManager::on_media_message_ready_to_send(DialogId dialog_id, MessageId message_id, Promise &&promise) { LOG(INFO) << "Ready to send " << message_id << " to " << dialog_id; CHECK(promise); - if (!G()->parameters().use_file_db) { // ResourceManager::Mode::Greedy + if (!G()->parameters().use_file_db || message_id.is_scheduled()) { // ResourceManager::Mode::Greedy auto m = get_message({dialog_id, message_id}); if (m != nullptr) { promise.set_value(std::move(m)); @@ -17099,7 +19045,7 @@ Result MessagesManager::send_bot_start_message(UserId bot_user_id, Di vector text_entities; text_entities.emplace_back(MessageEntity::Type::BotCommand, 0, narrow_cast(text.size())); bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, MessageId(), false, false, + Message *m = get_message_to_send(d, MessageId(), SendMessageOptions(), create_text_message_content(text, std::move(text_entities), WebPageId()), &need_update_dialog_pos); m->is_bot_start_message = true; @@ -17135,9 +19081,7 @@ class MessagesManager::SendBotStartMessageLogEvent { td::parse(bot_user_id, parser); td::parse(dialog_id, parser); td::parse(parameter, parser); - CHECK(m_out == nullptr); - m_out = make_unique(); - td::parse(*m_out, parser); + td::parse(m_out, parser); } }; @@ -17181,7 +19125,7 @@ void MessagesManager::do_send_bot_start_message(UserId bot_user_id, DialogId dia } Result MessagesManager::send_inline_query_result_message(DialogId dialog_id, MessageId reply_to_message_id, - bool disable_notification, bool from_background, + tl_object_ptr &&options, int64 query_id, const string &result_id, bool hide_via_bot) { LOG(INFO) << "Begin to send inline query result message to " << dialog_id << " in reply to " << reply_to_message_id; @@ -17192,6 +19136,7 @@ Result MessagesManager::send_inline_query_result_message(DialogId dia } TRY_STATUS(can_send_message(dialog_id)); + TRY_RESULT(send_message_options, process_send_message_options(dialog_id, std::move(options))); bool to_secret = false; switch (dialog_id.get_type()) { case DialogType::User: @@ -17219,12 +19164,13 @@ Result MessagesManager::send_inline_query_result_message(DialogId dia return Status::Error(5, "Inline query result not found"); } + TRY_STATUS(can_use_send_message_options(send_message_options, content->message_content, 0)); TRY_STATUS(can_send_message_content(dialog_id, content->message_content.get(), false, true)); bool need_update_dialog_pos = false; - Message *m = get_message_to_send( - d, get_reply_to_message_id(d, reply_to_message_id), disable_notification, from_background, - dup_message_content(td_, dialog_id, content->message_content.get(), false), &need_update_dialog_pos); + Message *m = get_message_to_send(d, get_reply_to_message_id(d, reply_to_message_id), send_message_options, + dup_message_content(td_, dialog_id, content->message_content.get(), false), + &need_update_dialog_pos, nullptr, true); m->hide_via_bot = hide_via_bot; if (!hide_via_bot) { m->via_bot_user_id = td_->inline_queries_manager_->get_inline_bot_user_id(query_id); @@ -17274,9 +19220,7 @@ class MessagesManager::SendInlineQueryResultMessageLogEvent { td::parse(dialog_id, parser); td::parse(query_id, parser); td::parse(result_id, parser); - CHECK(m_out == nullptr); - m_out = make_unique(); - td::parse(*m_out, parser); + td::parse(m_out, parser); } }; @@ -17308,8 +19252,8 @@ void MessagesManager::do_send_inline_query_result_message(DialogId dialog_id, co if (!m->via_bot_user_id.is_valid() || m->hide_via_bot) { flags |= telegram_api::messages_sendInlineBotResult::HIDE_VIA_MASK; } - m->send_query_ref = td_->create_handler()->send(flags, dialog_id, m->reply_to_message_id, - random_id, query_id, result_id); + m->send_query_ref = td_->create_handler()->send( + flags, dialog_id, m->reply_to_message_id, get_message_schedule_date(m), random_id, query_id, result_id); } bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, bool is_editing, @@ -17327,7 +19271,6 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo return false; } - bool is_bot = td_->auth_manager_->is_bot(); if (m->had_reply_markup) { return false; } @@ -17336,23 +19279,24 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo } auto my_id = td_->contacts_manager_->get_my_id(); - if (m->via_bot_user_id.is_valid() && m->via_bot_user_id != my_id) { + if (m->via_bot_user_id.is_valid() && (m->via_bot_user_id != my_id || m->message_id.is_scheduled())) { return false; } + bool is_bot = td_->auth_manager_->is_bot(); auto content_type = m->content->get_type(); DialogId my_dialog_id(my_id); bool has_edit_time_limit = !(is_bot && m->is_outgoing) && dialog_id != my_dialog_id && content_type != MessageContentType::Poll && - content_type != MessageContentType::LiveLocation; + content_type != MessageContentType::LiveLocation && !m->message_id.is_scheduled(); switch (dialog_id.get_type()) { case DialogType::User: - if (!m->is_outgoing && dialog_id != my_dialog_id) { + if (!m->is_outgoing && dialog_id != my_dialog_id && !m->via_bot_user_id.is_valid()) { return false; } break; case DialogType::Chat: - if (!m->is_outgoing) { + if (!m->is_outgoing && !m->via_bot_user_id.is_valid()) { return false; } break; @@ -17365,8 +19309,14 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo auto channel_id = dialog_id.get_channel_id(); auto channel_status = td_->contacts_manager_->get_channel_permissions(channel_id); if (m->is_channel_post) { - if (!channel_status.can_edit_messages() && !(channel_status.can_post_messages() && m->is_outgoing)) { - return false; + if (m->message_id.is_scheduled()) { + if (!channel_status.can_post_messages()) { + return false; + } + } else { + if (!channel_status.can_edit_messages() && !(channel_status.can_post_messages() && m->is_outgoing)) { + return false; + } } if (is_bot && only_reply_markup) { has_edit_time_limit = false; @@ -17419,6 +19369,9 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo // there is no caption to edit, but bot can edit inline reply_markup return true; } + if (m->message_id.is_scheduled()) { + return false; + } return !get_message_content_poll_is_closed(td_, m->content.get()); } case MessageContentType::Contact: @@ -17463,7 +19416,8 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo } bool MessagesManager::can_resend_message(const Message *m) { - if (m->send_error_code != 429 && m->send_error_message != "Message is too old to be re-sent automatically") { + if (m->send_error_code != 429 && m->send_error_message != "Message is too old to be re-sent automatically" && + m->send_error_message != "SCHEDULE_TOO_MUCH") { return false; } if (m->is_bot_start_message) { @@ -17498,6 +19452,16 @@ bool MessagesManager::is_broadcast_channel(DialogId dialog_id) const { return td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) == ChannelType::Broadcast; } +int32 MessagesManager::get_message_schedule_date(const Message *m) { + if (!m->message_id.is_scheduled()) { + return 0; + } + if (m->edited_schedule_date != 0) { + return m->edited_schedule_date; + } + return m->date; +} + void MessagesManager::edit_message_text(FullMessageId full_message_id, tl_object_ptr &&reply_markup, tl_object_ptr &&input_message_content, @@ -17521,8 +19485,7 @@ void MessagesManager::edit_message_text(FullMessageId full_message_id, return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "edit_message_text"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_text"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -17556,9 +19519,10 @@ void MessagesManager::edit_message_text(FullMessageId full_message_id, send_closure( td_->create_net_actor(std::move(promise)), &EditMessageActor::send, flags, dialog_id, - message_id, input_message_text.text.text, + m->message_id, input_message_text.text.text, get_input_message_entities(td_->contacts_manager_.get(), input_message_text.text.entities, "edit_message_text"), - nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + nullptr, std::move(input_reply_markup), get_message_schedule_date(m), + get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); } void MessagesManager::edit_message_live_location(FullMessageId full_message_id, @@ -17576,8 +19540,7 @@ void MessagesManager::edit_message_live_location(FullMessageId full_message_id, return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "edit_message_live_location"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_live_location"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -17590,6 +19553,10 @@ void MessagesManager::edit_message_live_location(FullMessageId full_message_id, if (old_message_content_type != MessageContentType::LiveLocation) { return promise.set_error(Status::Error(5, "There is no live location in the message to edit")); } + if (m->message_id.is_scheduled()) { + LOG(ERROR) << "Have " << full_message_id << " with live location"; + return promise.set_error(Status::Error(5, "Can't edit live location in scheduled message")); + } Location location(input_location); if (location.empty() && input_location != nullptr) { @@ -17610,8 +19577,9 @@ void MessagesManager::edit_message_live_location(FullMessageId full_message_id, auto input_media = telegram_api::make_object(flags, false /*ignored*/, location.get_input_geo_point(), 0); send_closure(td_->create_net_actor(std::move(promise)), &EditMessageActor::send, 0, dialog_id, - message_id, string(), vector>(), std::move(input_media), - std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + m->message_id, string(), vector>(), std::move(input_media), + std::move(input_reply_markup), get_message_schedule_date(m), + get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); } void MessagesManager::cancel_edit_message_media(DialogId dialog_id, Message *m, Slice error_message) { @@ -17629,8 +19597,9 @@ void MessagesManager::cancel_edit_message_media(DialogId dialog_id, Message *m, void MessagesManager::on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id, bool was_uploaded, bool was_thumbnail_uploaded, - string file_reference, uint64 generation, Result &&result) { - CHECK(message_id.is_server()); + string file_reference, int32 schedule_date, uint64 generation, + Result &&result) { + CHECK(message_id.is_any_server()); auto m = get_message({dialog_id, message_id}); if (m == nullptr || m->edit_generation != generation) { // message is already deleted or was edited again @@ -17679,10 +19648,13 @@ void MessagesManager::on_message_media_edited(DialogId dialog_id, MessageId mess cancel_upload_message_content_files(m->edited_content.get()); if (dialog_id.get_type() != DialogType::SecretChat) { - get_message_from_server({dialog_id, message_id}, Auto()); + get_message_from_server({dialog_id, m->message_id}, Auto()); } } + if (m->edited_schedule_date == schedule_date) { + m->edited_schedule_date = 0; + } m->edited_content = nullptr; m->edited_reply_markup = nullptr; m->edit_generation = 0; @@ -17720,8 +19692,7 @@ void MessagesManager::edit_message_media(FullMessageId full_message_id, return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - Message *m = get_message_force(d, message_id, "edit_message_media"); + Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_media"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -17729,7 +19700,7 @@ void MessagesManager::edit_message_media(FullMessageId full_message_id, if (!can_edit_message(dialog_id, m, true)) { return promise.set_error(Status::Error(5, "Message can't be edited")); } - CHECK(message_id.is_server()); + CHECK(m->message_id.is_any_server()); MessageContentType old_message_content_type = m->content->get_type(); if (old_message_content_type != MessageContentType::Animation && @@ -17787,8 +19758,7 @@ void MessagesManager::edit_message_caption(FullMessageId full_message_id, return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "edit_message_caption"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_caption"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -17816,9 +19786,10 @@ void MessagesManager::edit_message_caption(FullMessageId full_message_id, auto input_reply_markup = get_input_reply_markup(r_new_reply_markup.ok()); send_closure(td_->create_net_actor(std::move(promise)), &EditMessageActor::send, 1 << 11, dialog_id, - message_id, caption.text, + m->message_id, caption.text, get_input_message_entities(td_->contacts_manager_.get(), caption.entities, "edit_message_caption"), - nullptr, std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + nullptr, std::move(input_reply_markup), get_message_schedule_date(m), + get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); } void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id, @@ -17839,8 +19810,7 @@ void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id, return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "edit_message_reply_markup"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_reply_markup"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -17856,8 +19826,9 @@ void MessagesManager::edit_message_reply_markup(FullMessageId full_message_id, } auto input_reply_markup = get_input_reply_markup(r_new_reply_markup.ok()); send_closure(td_->create_net_actor(std::move(promise)), &EditMessageActor::send, 0, dialog_id, - message_id, string(), vector>(), nullptr, - std::move(input_reply_markup), get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + m->message_id, string(), vector>(), nullptr, + std::move(input_reply_markup), get_message_schedule_date(m), + get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); } void MessagesManager::edit_inline_message_text(const string &inline_message_id, @@ -18043,6 +20014,54 @@ void MessagesManager::edit_inline_message_reply_markup(const string &inline_mess nullptr, get_input_reply_markup(r_new_reply_markup.ok())); } +void MessagesManager::edit_message_scheduling_state( + FullMessageId full_message_id, td_api::object_ptr &&scheduling_state, + Promise &&promise) { + auto r_schedule_date = get_message_schedule_date(std::move(scheduling_state)); + if (r_schedule_date.is_error()) { + return promise.set_error(r_schedule_date.move_as_error()); + } + auto schedule_date = r_schedule_date.move_as_ok(); + + LOG(INFO) << "Begin to reschedule " << full_message_id << " to " << schedule_date; + + auto dialog_id = full_message_id.get_dialog_id(); + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(5, "Chat not found")); + } + + if (!have_input_peer(dialog_id, AccessRights::Edit)) { + return promise.set_error(Status::Error(5, "Can't access the chat")); + } + + Message *m = get_message_force(d, full_message_id.get_message_id(), "edit_message_scheduling_state"); + if (m == nullptr) { + return promise.set_error(Status::Error(5, "Message not found")); + } + + if (!m->message_id.is_scheduled()) { + return promise.set_error(Status::Error(5, "Message is not scheduled")); + } + if (!m->message_id.is_scheduled_server()) { + return promise.set_error(Status::Error(5, "Can't reschedule the message")); + } + + if (get_message_schedule_date(m) == schedule_date) { + return promise.set_value(Unit()); + } + m->edited_schedule_date = schedule_date; + + if (schedule_date > 0) { + send_closure(td_->create_net_actor(std::move(promise)), &EditMessageActor::send, 0, dialog_id, + m->message_id, string(), vector>(), nullptr, nullptr, + schedule_date, get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + } else { + send_closure(td_->create_net_actor(std::move(promise)), &SendScheduledMessageActor::send, + dialog_id, m->message_id, get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); + } +} + int32 MessagesManager::get_message_flags(const Message *m) { int32 flags = 0; if (m->reply_to_message_id.is_valid()) { @@ -18063,6 +20082,9 @@ int32 MessagesManager::get_message_flags(const Message *m) { if (m->clear_draft) { flags |= SEND_MESSAGE_FLAG_CLEAR_DRAFT; } + if (m->message_id.is_scheduled()) { + flags |= SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE; + } return flags; } @@ -18070,6 +20092,9 @@ bool MessagesManager::can_set_game_score(DialogId dialog_id, const Message *m) c if (m == nullptr) { return false; } + if (m->message_id.is_scheduled()) { + return false; + } if (m->message_id.is_yet_unsent()) { return false; } @@ -18145,8 +20170,7 @@ void MessagesManager::set_game_score(FullMessageId full_message_id, bool edit_me return promise.set_error(Status::Error(5, "Can't access the chat")); } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "set_game_score"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "set_game_score"); if (m == nullptr) { return promise.set_error(Status::Error(5, "Message not found")); } @@ -18161,7 +20185,7 @@ void MessagesManager::set_game_score(FullMessageId full_message_id, bool edit_me } send_closure(td_->create_net_actor(std::move(promise)), &SetGameScoreActor::send, dialog_id, - message_id, edit_message, std::move(input_user), score, force, + m->message_id, edit_message, std::move(input_user), score, force, get_sequence_dispatcher_id(dialog_id, MessageContentType::None)); } @@ -18204,13 +20228,12 @@ int64 MessagesManager::get_game_high_scores(FullMessageId full_message_id, UserI return 0; } - auto message_id = full_message_id.get_message_id(); - const Message *m = get_message_force(d, message_id, "get_game_high_scores"); + const Message *m = get_message_force(d, full_message_id.get_message_id(), "get_game_high_scores"); if (m == nullptr) { promise.set_error(Status::Error(5, "Message not found")); return 0; } - if (!message_id.is_server()) { + if (m->message_id.is_scheduled() || !m->message_id.is_server()) { promise.set_error(Status::Error(5, "Wrong message identifier specified")); return 0; } @@ -18228,7 +20251,7 @@ int64 MessagesManager::get_game_high_scores(FullMessageId full_message_id, UserI game_high_scores_[random_id]; // reserve place for result td_->create_handler(std::move(promise)) - ->send(dialog_id, message_id, std::move(input_user), random_id); + ->send(dialog_id, m->message_id, std::move(input_user), random_id); return random_id; } @@ -18375,7 +20398,8 @@ unique_ptr MessagesManager::get_message_for if (!channel_id.is_valid()) { if (sender_user_id.is_valid()) { if (message_id.is_valid()) { - LOG(ERROR) << "Receive non-empty message id in message forward header: " << oneline(to_string(forward_header)); + LOG(ERROR) << "Receive non-empty message identifier in message forward header: " + << oneline(to_string(forward_header)); message_id = MessageId(); } } else if (sender_name.empty()) { @@ -18385,14 +20409,14 @@ unique_ptr MessagesManager::get_message_for } else { LOG_IF(ERROR, td_->contacts_manager_->have_min_channel(channel_id)) << "Receive forward from min channel"; dialog_id = DialogId(channel_id); - force_create_dialog(dialog_id, "message forward info"); + force_create_dialog(dialog_id, "message forward info", true); if (sender_user_id.is_valid()) { LOG(ERROR) << "Receive valid sender user id in message forward header: " << oneline(to_string(forward_header)); sender_user_id = UserId(); } } if (from_dialog_id.is_valid()) { - force_create_dialog(from_dialog_id, "message forward from info"); + force_create_dialog(from_dialog_id, "message forward from info", true); } return td::make_unique(sender_user_id, forward_header->date_, dialog_id, message_id, @@ -18492,8 +20516,7 @@ class MessagesManager::ForwardMessagesLogEvent { int32 size = parser.fetch_int(); messages_out.resize(size); for (auto &m_out : messages_out) { - m_out = make_unique(); - td::parse(*m_out, parser); + td::parse(m_out, parser); } } }; @@ -18518,6 +20541,8 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d logevent_id = save_forward_messages_logevent(to_dialog_id, from_dialog_id, messages, message_ids); } + auto schedule_date = get_message_schedule_date(messages[0]); + int32 flags = 0; if (messages[0]->disable_notification) { flags |= SEND_MESSAGE_FLAG_DISABLE_NOTIFICATION; @@ -18531,19 +20556,22 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d if (messages[0]->in_game_share) { flags |= SEND_MESSAGE_FLAG_WITH_MY_SCORE; } + if (schedule_date != 0) { + flags |= SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE; + } vector random_ids = transform(messages, [this, to_dialog_id](const Message *m) { return begin_send_message(to_dialog_id, m); }); send_closure(td_->create_net_actor(get_erase_logevent_promise(logevent_id)), &ForwardMessagesActor::send, flags, to_dialog_id, from_dialog_id, message_ids, std::move(random_ids), - get_sequence_dispatcher_id(to_dialog_id, MessageContentType::None)); + schedule_date, get_sequence_dispatcher_id(to_dialog_id, MessageContentType::None)); } Result MessagesManager::forward_message(DialogId to_dialog_id, DialogId from_dialog_id, MessageId message_id, - bool disable_notification, bool from_background, bool in_game_share, - bool send_copy, bool remove_caption) { - TRY_RESULT(result, forward_messages(to_dialog_id, from_dialog_id, {message_id}, disable_notification, from_background, - in_game_share, false, send_copy, remove_caption)); + tl_object_ptr &&options, + bool in_game_share, bool send_copy, bool remove_caption) { + TRY_RESULT(result, forward_messages(to_dialog_id, from_dialog_id, {message_id}, std::move(options), in_game_share, + false, send_copy, remove_caption)); CHECK(result.size() == 1); auto sent_message_id = result[0]; if (sent_message_id == MessageId()) { @@ -18553,9 +20581,10 @@ Result MessagesManager::forward_message(DialogId to_dialog_id, Dialog } Result> MessagesManager::forward_messages(DialogId to_dialog_id, DialogId from_dialog_id, - vector message_ids, bool disable_notification, - bool from_background, bool in_game_share, bool as_album, - bool send_copy, bool remove_caption) { + vector message_ids, + tl_object_ptr &&options, + bool in_game_share, bool as_album, bool send_copy, + bool remove_caption) { if (message_ids.size() > 100) { // TODO replace with const from config or implement mass-forward return Status::Error(4, "Too much messages to forward"); } @@ -18580,11 +20609,16 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i } TRY_STATUS(can_send_message(to_dialog_id)); + TRY_RESULT(send_message_options, process_send_message_options(to_dialog_id, std::move(options))); for (auto message_id : message_ids) { + if (message_id.is_valid_scheduled()) { + return Status::Error(5, "Can't forward scheduled messages"); + } if (!message_id.is_valid()) { return Status::Error(5, "Invalid message identifier"); } + CHECK(!message_id.is_scheduled()); } bool to_secret = to_dialog_id.get_type() == DialogType::SecretChat; @@ -18611,6 +20645,7 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i continue; } CHECK(message_id.is_valid()); + CHECK(message_id == forwarded_message->message_id); if (!can_forward_message(from_dialog_id, forwarded_message)) { LOG(INFO) << "Can't forward " << message_id; @@ -18631,6 +20666,12 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i continue; } + auto can_use_options_status = can_use_send_message_options(send_message_options, content, 0); + if (can_use_options_status.is_error()) { + LOG(INFO) << "Can't forward " << message_id << ": " << can_send_status.message(); + continue; + } + auto content_type = content->get_type(); bool is_game = content_type == MessageContentType::Game; if (need_copy) { @@ -18679,7 +20720,7 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i } } - Message *m = get_message_to_send(to_dialog, MessageId(), disable_notification, from_background, std::move(content), + Message *m = get_message_to_send(to_dialog, MessageId(), send_message_options, std::move(content), &need_update_dialog_pos, std::move(forward_info)); m->real_forward_from_dialog_id = from_dialog_id; m->real_forward_from_message_id = message_id; @@ -18777,8 +20818,8 @@ Result> MessagesManager::forward_messages(DialogId to_dialog_i } for (auto &copied_message : copied_messages) { - Message *m = get_message_to_send(to_dialog, MessageId(), disable_notification, from_background, - std::move(copied_message.content), &need_update_dialog_pos); + Message *m = get_message_to_send(to_dialog, MessageId(), send_message_options, std::move(copied_message.content), + &need_update_dialog_pos, nullptr, true); m->disable_web_page_preview = copied_message.disable_web_page_preview; m->media_album_id = media_album_id; @@ -18825,10 +20866,15 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v if (m->try_resend_at > Time::now()) { return Status::Error(400, "Message can't be re-sent yet"); } - if (m->message_id.get() <= last_message_id.get()) { - return Status::Error(400, "Message identifiers must be in a strictly increasing order"); + if (last_message_id != MessageId()) { + if (m->message_id.is_scheduled() != last_message_id.is_scheduled()) { + return Status::Error(400, "Messages must be all scheduled or ordinary"); + } + if (m->message_id <= last_message_id) { + return Status::Error(400, "Message identifiers must be in a strictly increasing order"); + } } - last_message_id = message_id; + last_message_id = m->message_id; } vector> new_contents(message_ids.size()); @@ -18840,20 +20886,20 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v unique_ptr content = dup_message_content(td_, dialog_id, m->content.get(), false); if (content == nullptr) { - LOG(INFO) << "Can't resend " << message_id; + LOG(INFO) << "Can't resend " << m->message_id; continue; } auto can_send_status = can_send_message_content(dialog_id, content.get(), false); if (can_send_status.is_error()) { - LOG(INFO) << "Can't resend " << message_id << ": " << can_send_status.message(); + LOG(INFO) << "Can't resend " << m->message_id << ": " << can_send_status.message(); continue; } if (content->get_type() == MessageContentType::Game && !get_message_content_game_bot_user_id(content.get()).is_valid()) { // must not happen - LOG(ERROR) << "Can't resend game from " << message_id; + LOG(ERROR) << "Can't resend game from " << m->message_id; continue; } @@ -18884,9 +20930,10 @@ Result> MessagesManager::resend_messages(DialogId dialog_id, v CHECK(message != nullptr); send_update_delete_messages(dialog_id, {message->message_id.get()}, true, false); - Message *m = - get_message_to_send(d, get_reply_to_message_id(d, message->reply_to_message_id), message->disable_notification, - message->from_background, std::move(new_contents[i]), &need_update_dialog_pos); + SendMessageOptions options(message->disable_notification, message->from_background, + get_message_schedule_date(message.get())); + Message *m = get_message_to_send(d, get_reply_to_message_id(d, message->reply_to_message_id), options, + std::move(new_contents[i]), &need_update_dialog_pos, nullptr, message->is_copy); m->reply_markup = std::move(message->reply_markup); m->via_bot_user_id = message->via_bot_user_id; m->disable_web_page_preview = message->disable_web_page_preview; @@ -18928,7 +20975,7 @@ Result MessagesManager::send_dialog_set_ttl_message(DialogId dialog_i TRY_STATUS(can_send_message(dialog_id)); bool need_update_dialog_pos = false; - Message *m = get_message_to_send(d, MessageId(), false, false, create_chat_set_ttl_message_content(ttl), + Message *m = get_message_to_send(d, MessageId(), SendMessageOptions(), create_chat_set_ttl_message_content(ttl), &need_update_dialog_pos); send_update_new_message(d, m); @@ -18961,8 +21008,8 @@ Status MessagesManager::send_screenshot_taken_notification_message(DialogId dial if (dialog_type == DialogType::User) { bool need_update_dialog_pos = false; - const Message *m = get_message_to_send(d, MessageId(), false, false, create_screenshot_taken_message_content(), - &need_update_dialog_pos); + const Message *m = get_message_to_send(d, MessageId(), SendMessageOptions(), + create_screenshot_taken_message_content(), &need_update_dialog_pos); do_send_screenshot_taken_notification_message(dialog_id, m, 0); @@ -18994,9 +21041,7 @@ class MessagesManager::SendScreenshotTakenNotificationMessageLogEvent { template void parse(ParserT &parser) { td::parse(dialog_id, parser); - CHECK(m_out == nullptr); - m_out = make_unique(); - td::parse(*m_out, parser); + td::parse(m_out, parser); } }; @@ -19063,17 +21108,18 @@ Result MessagesManager::add_local_message( if (dialog_type == DialogType::User && DialogId(sender_user_id) != dialog_id) { return Status::Error(400, "Wrong sender user"); } - if (dialog_type == DialogType::SecretChat && - sender_user_id != td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id())) { - return Status::Error(400, "Wrong sender user"); + if (dialog_type == DialogType::SecretChat) { + auto peer_user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (!peer_user_id.is_valid() || sender_user_id != peer_user_id) { + return Status::Error(400, "Wrong sender user"); + } } } MessageId message_id = get_next_local_message_id(d); auto m = make_unique(); - m->random_y = get_random_y(message_id); - m->message_id = message_id; + set_message_id(m, message_id); if (is_channel_post) { // sender of the post can be hidden if (td_->contacts_manager_->get_channel_sign_messages(dialog_id.get_channel_id())) { @@ -19133,7 +21179,7 @@ Result MessagesManager::add_local_message( bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_message_id, const string &source) { if (!new_message_id.is_valid()) { - LOG(ERROR) << "Receive " << new_message_id << " in update message id with random_id " << random_id << " from " + LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " from " << source; auto it = debug_being_sent_messages_.find(random_id); if (it == debug_being_sent_messages_.end()) { @@ -19149,7 +21195,7 @@ bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_messag LOG(ERROR) << "Sent message is in not found " << dialog_id; return false; } - LOG(ERROR) << "Receive " << new_message_id << " in update message id with random_id " << random_id << " in " + LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " in " << dialog_id; return false; } @@ -19166,10 +21212,35 @@ bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_messag being_sent_messages_.erase(it); + LOG(INFO) << "Save correspondence from " << new_message_id << " in " << dialog_id << " to " << old_message_id; update_message_ids_[FullMessageId(dialog_id, new_message_id)] = old_message_id; return true; } +bool MessagesManager::on_update_scheduled_message_id(int64 random_id, ScheduledServerMessageId new_message_id, + const string &source) { + if (!new_message_id.is_valid()) { + LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " from " + << source; + return false; + } + + auto it = being_sent_messages_.find(random_id); + if (it == being_sent_messages_.end()) { + LOG(ERROR) << "Receive not send outgoing " << new_message_id << " with random_id = " << random_id; + return false; + } + + auto dialog_id = it->second.get_dialog_id(); + auto old_message_id = it->second.get_message_id(); + + being_sent_messages_.erase(it); + + LOG(INFO) << "Save correspondence from " << new_message_id << " in " << dialog_id << " to " << old_message_id; + update_scheduled_message_ids_[dialog_id][new_message_id] = old_message_id; + return true; +} + bool MessagesManager::on_get_dialog_error(DialogId dialog_id, const Status &status, const string &source) { if (status.code() == 401) { // authorization is lost @@ -19259,11 +21330,11 @@ NotificationGroupId MessagesManager::get_dialog_notification_group_id(DialogId d } Result MessagesManager::get_message_push_notification_info( - DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id, int32 date, bool contains_mention, - bool is_pinned, bool is_from_binlog) { + DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id, int32 date, + bool is_from_scheduled, bool contains_mention, bool is_pinned, bool is_from_binlog) { init(); - if (dialog_id == get_my_dialog_id()) { + if (!is_from_scheduled && dialog_id == get_my_dialog_id()) { return Status::Error("Ignore notification in chat with self"); } if (td_->auth_manager_->is_bot()) { @@ -19275,18 +21346,19 @@ Result MessagesManager::get_messag return Status::Error(406, "Ignore notification in unknown chat"); } - bool is_new_pinned = is_pinned && message_id.is_valid() && message_id.get() > d->max_notification_message_id.get(); + 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()) { - if (message_id.get() <= d->last_new_message_id.get()) { + if (message_id <= d->last_new_message_id) { return Status::Error("Ignore notification about known message"); } - if (!is_from_binlog && message_id.get() == d->max_notification_message_id.get()) { + if (!is_from_binlog && message_id == d->max_notification_message_id) { return Status::Error("Ignore previously added message push notification"); } - if (!is_from_binlog && message_id.get() < d->max_notification_message_id.get()) { + if (!is_from_binlog && message_id < d->max_notification_message_id) { return Status::Error("Ignore out of order message push notification"); } - if (message_id.get() <= d->last_read_inbox_message_id.get()) { + if (message_id <= d->last_read_inbox_message_id) { return Status::Error("Ignore notification about read message"); } } @@ -19333,7 +21405,7 @@ Result MessagesManager::get_messag return Status::Error("Can't assign notification group ID"); } - if (message_id.is_valid() && message_id.get() > d->max_notification_message_id.get()) { + 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()); } @@ -19350,6 +21422,8 @@ Result MessagesManager::get_messag NotificationId MessagesManager::get_next_notification_id(Dialog *d, NotificationGroupId notification_group_id, MessageId message_id) { + CHECK(d != nullptr); + CHECK(!message_id.is_scheduled()); NotificationId notification_id; do { notification_id = td_->notification_manager_->get_next_notification_id(); @@ -19468,14 +21542,15 @@ bool MessagesManager::is_from_mention_notification_group(const Dialog *d, const } bool MessagesManager::is_message_notification_active(const Dialog *d, const Message *m) { + CHECK(!m->message_id.is_scheduled()); if (is_from_mention_notification_group(d, m)) { return m->notification_id.get() > d->mention_notification_group.max_removed_notification_id.get() && - m->message_id.get() > d->mention_notification_group.max_removed_message_id.get() && + m->message_id > d->mention_notification_group.max_removed_message_id && (m->contains_unread_mention || m->message_id == d->pinned_message_notification_message_id); } else { return m->notification_id.get() > d->message_notification_group.max_removed_notification_id.get() && - m->message_id.get() > d->message_notification_group.max_removed_message_id.get() && - m->message_id.get() > d->last_read_inbox_message_id.get(); + m->message_id > d->message_notification_group.max_removed_message_id && + m->message_id > d->last_read_inbox_message_id; } } @@ -19483,15 +21558,15 @@ void MessagesManager::try_add_pinned_message_notification(Dialog *d, vectorpinned_message_notification_message_id; - if (!message_id.is_valid() || message_id.get() > d->last_new_message_id.get()) { + if (!message_id.is_valid() || message_id > d->last_new_message_id) { + CHECK(!message_id.is_scheduled()); return; } auto m = get_message_force(d, message_id, "try_add_pinned_message_notification"); if (m != nullptr && m->notification_id.get() > d->mention_notification_group.max_removed_notification_id.get() && - m->message_id.get() > d->mention_notification_group.max_removed_message_id.get() && - m->message_id.get() > d->last_read_inbox_message_id.get() && - !is_dialog_pinned_message_notifications_disabled(d)) { + m->message_id > d->mention_notification_group.max_removed_message_id && + m->message_id > d->last_read_inbox_message_id && !is_dialog_pinned_message_notifications_disabled(d)) { if (m->notification_id.get() < max_notification_id.get()) { VLOG(notifications) << "Add " << m->notification_id << " about pinned " << message_id << " in " << d->dialog_id; auto pinned_message_id = get_message_content_pinned_message_id(m->content.get()); @@ -19502,11 +21577,11 @@ void MessagesManager::try_add_pinned_message_notification(Dialog *d, vectornotification_id, m->date, m->disable_notification, create_new_message_notification(message_id)); - while (pos > 0 && res[pos - 1].type->get_message_id().get() < message_id.get()) { + while (pos > 0 && res[pos - 1].type->get_message_id() < message_id) { std::swap(res[pos - 1], res[pos]); pos--; } - if (pos > 0 && res[pos - 1].type->get_message_id().get() == message_id.get()) { + if (pos > 0 && res[pos - 1].type->get_message_id() == message_id) { res.erase(res.begin() + pos); // notification was already there } if (res.size() > static_cast(limit)) { @@ -19530,7 +21605,7 @@ vector MessagesManager::get_message_notifications_from_database_fo auto from_notification_id = NotificationId::max(); auto from_message_id = MessageId::max(); vector res; - if (!from_mentions && from_message_id.get() <= d->last_read_inbox_message_id.get()) { + if (!from_mentions && from_message_id <= d->last_read_inbox_message_id) { return res; } while (true) { @@ -19549,7 +21624,7 @@ vector MessagesManager::get_message_notifications_from_database_fo << " messages with notifications from database in " << group_info.group_id << '/' << d->dialog_id; for (auto &message : messages) { - auto m = on_get_message_from_database(d->dialog_id, d, std::move(message), + auto m = on_get_message_from_database(d->dialog_id, d, std::move(message), false, "get_message_notifications_from_database_force"); if (m == nullptr) { VLOG(notifications) << "Receive from database a broken message"; @@ -19573,7 +21648,7 @@ vector MessagesManager::get_message_notifications_from_database_fo from_notification_id = notification_id; is_found = true; } - if (m->message_id.get() >= from_message_id.get()) { + if (m->message_id >= from_message_id) { LOG(ERROR) << "Have nonmonotoic message ids: " << d->dialog_id << " " << m->message_id << " " << notification_id << " " << from_message_id << " " << from_notification_id; is_correct = false; @@ -19583,8 +21658,8 @@ vector MessagesManager::get_message_notifications_from_database_fo } if (notification_id.get() <= group_info.max_removed_notification_id.get() || - m->message_id.get() <= group_info.max_removed_message_id.get() || - (!from_mentions && m->message_id.get() <= d->last_read_inbox_message_id.get())) { + m->message_id <= group_info.max_removed_message_id || + (!from_mentions && m->message_id <= d->last_read_inbox_message_id)) { // if message still has notification_id, but it was removed via max_removed_notification_id, // or max_removed_message_id, or last_read_inbox_message_id, // then there will be no more messages with active notifications @@ -19635,10 +21710,11 @@ vector MessagesManager::get_message_notifications_from_database_fo Result> MessagesManager::do_get_message_notifications_from_database_force( Dialog *d, bool from_mentions, NotificationId from_notification_id, MessageId from_message_id, int32 limit) { CHECK(G()->parameters().use_message_db); + CHECK(!from_message_id.is_scheduled()); auto *db = G()->td_db()->get_messages_db_sync(); if (!from_mentions) { - CHECK(from_message_id.get() > d->last_read_inbox_message_id.get()); + CHECK(from_message_id > d->last_read_inbox_message_id); VLOG(notifications) << "Trying to load " << limit << " messages with notifications in " << d->message_notification_group.group_id << '/' << d->dialog_id << " from " << from_notification_id; @@ -19704,6 +21780,7 @@ void MessagesManager::get_message_notifications_from_database(DialogId dialog_id CHECK(dialog_id.is_valid()); CHECK(group_id.is_valid()); + CHECK(!from_message_id.is_scheduled()); CHECK(limit > 0); auto d = get_dialog(dialog_id); @@ -19736,11 +21813,12 @@ void MessagesManager::do_get_message_notifications_from_database(Dialog *d, bool MessageId from_message_id, int32 limit, Promise> promise) { CHECK(G()->parameters().use_message_db); + CHECK(!from_message_id.is_scheduled()); auto &group_info = from_mentions ? d->mention_notification_group : d->message_notification_group; if (from_notification_id.get() <= group_info.max_removed_notification_id.get() || - from_message_id.get() <= group_info.max_removed_message_id.get() || - (!from_mentions && from_message_id.get() <= d->last_read_inbox_message_id.get())) { + from_message_id <= group_info.max_removed_message_id || + (!from_mentions && from_message_id <= d->last_read_inbox_message_id)) { return promise.set_value(vector()); } @@ -19796,8 +21874,8 @@ void MessagesManager::on_get_message_notifications_from_database(DialogId dialog VLOG(notifications) << "Loaded " << messages.size() << " messages with notifications in " << group_info.group_id << '/' << dialog_id << " from database"; for (auto &message : messages) { - auto m = - on_get_message_from_database(dialog_id, d, std::move(message), "on_get_message_notifications_from_database"); + auto m = on_get_message_from_database(dialog_id, d, std::move(message), false, + "on_get_message_notifications_from_database"); if (m == nullptr) { VLOG(notifications) << "Receive from database a broken message"; continue; @@ -19818,7 +21896,7 @@ void MessagesManager::on_get_message_notifications_from_database(DialogId dialog } else { from_notification_id = notification_id; } - if (from_message_id.is_valid() && m->message_id.get() >= from_message_id.get()) { + if (from_message_id.is_valid() && m->message_id >= from_message_id) { LOG(ERROR) << "Receive " << m->message_id << "/" << notification_id << " after " << from_message_id << "/" << from_notification_id; is_correct = false; @@ -19827,8 +21905,8 @@ void MessagesManager::on_get_message_notifications_from_database(DialogId dialog } if (notification_id.get() <= group_info.max_removed_notification_id.get() || - m->message_id.get() <= group_info.max_removed_message_id.get() || - (!from_mentions && m->message_id.get() <= d->last_read_inbox_message_id.get())) { + m->message_id <= group_info.max_removed_message_id || + (!from_mentions && m->message_id <= d->last_read_inbox_message_id)) { // if message still has notification_id, but it was removed via max_removed_notification_id, // or max_removed_message_id, or last_read_inbox_message_id, // then there will be no more messages with active notifications @@ -19908,6 +21986,7 @@ void MessagesManager::remove_message_notification(DialogId dialog_id, Notificati auto m = get_message(d, it->second); CHECK(m != nullptr); CHECK(m->notification_id == notification_id); + CHECK(!m->message_id.is_scheduled()); if (is_from_mention_notification_group(d, m) == from_mentions && is_message_notification_active(d, m)) { remove_message_notification_id(d, m, false, false); } @@ -19936,13 +22015,9 @@ void MessagesManager::remove_message_notifications_by_message_ids(DialogId dialo bool need_update_dialog_pos = false; vector deleted_message_ids; for (auto message_id : message_ids) { + CHECK(!message_id.is_scheduled()); // can't remove just notification_id, because total_count will stay wrong after restart - // auto m = get_message_force(d, message_id, "remove_message_notifications_by_message_ids"); - // if (m != nullptr) { - // remove_message_notification_id(d, m, true, false); - // on_message_changed(d, m, false, "remove_message_notifications_by_message_ids"); - // } - + // delete whole message auto message = delete_message(d, message_id, true, &need_update_dialog_pos, "remove_message_notifications_by_message_ids"); if (message == nullptr) { @@ -19954,7 +22029,7 @@ void MessagesManager::remove_message_notifications_by_message_ids(DialogId dialo d->mention_notification_group.group_id, message_id, true, "remove_message_notifications_by_message_ids"); continue; } - deleted_message_ids.push_back(message_id.get()); + deleted_message_ids.push_back(message->message_id.get()); } if (need_update_dialog_pos) { @@ -19973,7 +22048,7 @@ void MessagesManager::do_remove_message_notification(DialogId dialog_id, bool fr Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); - auto m = on_get_message_from_database(dialog_id, d, std::move(result[0]), "do_remove_message_notification"); + auto m = on_get_message_from_database(dialog_id, d, std::move(result[0]), false, "do_remove_message_notification"); if (m != nullptr && m->notification_id == notification_id && is_from_mention_notification_group(d, m) == from_mentions && is_message_notification_active(d, m)) { remove_message_notification_id(d, m, false, false); @@ -19994,6 +22069,7 @@ void MessagesManager::remove_message_notifications(DialogId dialog_id, Notificat if (!max_notification_id.is_valid()) { return; } + CHECK(!max_message_id.is_scheduled()); bool from_mentions = d->mention_notification_group.group_id == group_id; if (d->new_secret_chat_notification_id.is_valid()) { @@ -20006,7 +22082,7 @@ void MessagesManager::remove_message_notifications(DialogId dialog_id, Notificat if (max_notification_id.get() <= group_info.max_removed_notification_id.get()) { return; } - if (max_message_id.get() > group_info.max_removed_message_id.get()) { + if (max_message_id > group_info.max_removed_message_id) { VLOG(notifications) << "Set max_removed_message_id in " << group_info.group_id << '/' << dialog_id << " to " << max_message_id; group_info.max_removed_message_id = max_message_id.get_prev_server_message_id(); @@ -20029,7 +22105,7 @@ int32 MessagesManager::get_dialog_pending_notification_count(const Dialog *d, bo CHECK(d != nullptr); if (from_mentions) { bool has_pinned_message = d->pinned_message_notification_message_id.is_valid() && - d->pinned_message_notification_message_id.get() <= d->last_new_message_id.get(); + d->pinned_message_notification_message_id <= d->last_new_message_id; return d->unread_mention_count + static_cast(has_pinned_message); } else { if (d->new_secret_chat_notification_id.is_valid()) { @@ -20063,7 +22139,7 @@ bool MessagesManager::is_message_notification_disabled(const Dialog *d, const Me CHECK(d != nullptr); CHECK(m != nullptr); - if (m->is_outgoing || d->dialog_id == get_my_dialog_id() || td_->auth_manager_->is_bot()) { + if (!has_incoming_notification(d->dialog_id, m) || td_->auth_manager_->is_bot()) { return true; } @@ -20121,6 +22197,7 @@ bool MessagesManager::is_dialog_message_notification_disabled(DialogId dialog_id bool MessagesManager::may_need_message_notification(const Dialog *d, const Message *m) const { CHECK(d != nullptr); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); if (is_message_notification_disabled(d, m)) { return false; @@ -20139,6 +22216,7 @@ bool MessagesManager::may_need_message_notification(const Dialog *d, const Messa bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool force) { CHECK(d != nullptr); CHECK(m != nullptr); + CHECK(m->message_id.is_valid()); if (!force) { if (d->message_notification_group.group_id.is_valid()) { @@ -20158,11 +22236,11 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f auto from_mentions = is_from_mention_notification_group(d, m); bool is_pinned = m->content->get_type() == MessageContentType::PinMessage; - bool is_active = from_mentions ? m->contains_unread_mention || is_pinned - : m->message_id.get() > d->last_read_inbox_message_id.get(); + bool is_active = + from_mentions ? m->contains_unread_mention || is_pinned : m->message_id > d->last_read_inbox_message_id; if (is_active) { auto &group = from_mentions ? d->mention_notification_group : d->message_notification_group; - if (group.max_removed_message_id.get() >= m->message_id.get()) { + if (group.max_removed_message_id >= m->message_id) { is_active = false; } } @@ -20201,8 +22279,8 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f if (is_pinned) { auto message_id = get_message_content_pinned_message_id(m->content.get()); if (message_id.is_valid() && - !have_message({d->dialog_id, message_id}, - force ? "add_new_message_notification force" : "add_new_message_notification not force")) { + !have_message_force({d->dialog_id, message_id}, + force ? "add_new_message_notification force" : "add_new_message_notification not force")) { missing_pinned_message_id = message_id; } } @@ -20286,7 +22364,7 @@ bool MessagesManager::add_new_message_notification(Dialog *d, Message *m, bool f } else if (td_->is_online() && d->is_opened) { min_delay_ms = 1000; // 1 second } - bool is_silent = m->disable_notification || m->message_id.get() <= d->max_notification_message_id.get(); + bool is_silent = m->disable_notification || m->message_id <= d->max_notification_message_id; send_closure_later(G()->notification_manager(), &NotificationManager::add_notification, notification_group_id, from_mentions ? NotificationGroupType::Mentions : NotificationGroupType::Messages, d->dialog_id, m->date, settings_dialog_id, m->disable_notification, is_silent, min_delay_ms, m->notification_id, @@ -20338,7 +22416,7 @@ void MessagesManager::remove_all_dialog_notifications(Dialog *d, bool from_menti VLOG(notifications) << "Set max_removed_notification_id in " << group_info.group_id << '/' << d->dialog_id << " to " << group_info.last_notification_id << " from " << source; group_info.max_removed_notification_id = group_info.last_notification_id; - if (d->max_notification_message_id.get() > group_info.max_removed_message_id.get()) { + if (d->max_notification_message_id > group_info.max_removed_message_id) { group_info.max_removed_message_id = d->max_notification_message_id.get_prev_server_message_id(); } if (!d->pending_new_message_notifications.empty()) { @@ -20362,6 +22440,7 @@ void MessagesManager::remove_all_dialog_notifications(Dialog *d, bool from_menti void MessagesManager::remove_message_dialog_notifications(Dialog *d, MessageId max_message_id, bool from_mentions, const char *source) { // removes up to max_message_id + CHECK(!max_message_id.is_scheduled()); NotificationGroupInfo &group_info = from_mentions ? d->mention_notification_group : d->message_notification_group; if (!group_info.group_id.is_valid()) { return; @@ -20372,7 +22451,7 @@ void MessagesManager::remove_message_dialog_notifications(Dialog *d, MessageId m if (!d->pending_new_message_notifications.empty()) { for (auto &it : d->pending_new_message_notifications) { - if (it.second.get() <= max_message_id.get()) { + if (it.second <= max_message_id) { it.first = DialogId(); } } @@ -20380,7 +22459,7 @@ void MessagesManager::remove_message_dialog_notifications(Dialog *d, MessageId m } auto max_notification_message_id = max_message_id; - if (d->last_message_id.is_valid() && max_notification_message_id.get() >= d->last_message_id.get()) { + if (d->last_message_id.is_valid() && max_notification_message_id >= d->last_message_id) { max_notification_message_id = d->last_message_id; set_dialog_last_notification(d->dialog_id, group_info, 0, NotificationId(), "remove_message_dialog_notifications 1"); @@ -20419,11 +22498,19 @@ void MessagesManager::send_update_message_content(DialogId dialog_id, MessageId void MessagesManager::send_update_message_edited(DialogId dialog_id, const Message *m) { CHECK(m != nullptr); cancel_user_dialog_action(dialog_id, m); + auto edit_date = m->hide_edit_date ? 0 : m->edit_date; send_closure(G()->td(), &Td::send_update, - make_tl_object(dialog_id.get(), m->message_id.get(), m->edit_date, + make_tl_object(dialog_id.get(), m->message_id.get(), edit_date, get_reply_markup_object(m->reply_markup))); } +void MessagesManager::send_update_message_live_location_viewed(FullMessageId full_message_id) { + CHECK(get_message(full_message_id) != nullptr); + send_closure(G()->td(), &Td::send_update, + td_api::make_object(full_message_id.get_dialog_id().get(), + full_message_id.get_message_id().get())); +} + void MessagesManager::send_update_delete_messages(DialogId dialog_id, vector &&message_ids, bool is_permanent, bool from_cache) const { if (message_ids.empty()) { @@ -20439,8 +22526,20 @@ void MessagesManager::send_update_delete_messages(DialogId dialog_id, vectormessages == nullptr); - send_closure(G()->td(), &Td::send_update, make_tl_object(get_chat_object(d))); + auto chat_object = get_chat_object(d); + bool has_action_bar = chat_object->action_bar_ != nullptr; + d->last_sent_has_scheduled_messages = chat_object->has_scheduled_messages_; + send_closure(G()->td(), &Td::send_update, make_tl_object(std::move(chat_object))); d->is_update_new_chat_sent = true; + + if (has_action_bar && d->dialog_id.get_type() == DialogType::User) { + td_->contacts_manager_->for_each_secret_chat_with_user( + d->dialog_id.get_user_id(), [this, d](SecretChatId secret_chat_id) { + send_closure(G()->td(), &Td::send_update, + td_api::make_object(DialogId(secret_chat_id).get(), + get_chat_action_bar_object(d))); + }); + } } void MessagesManager::send_update_chat_draft_message(const Dialog *d) { @@ -20449,8 +22548,7 @@ void MessagesManager::send_update_chat_draft_message(const Dialog *d) { 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), - DialogDate(d->order, d->dialog_id) <= last_dialog_date_ ? d->order : 0)); + 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) { @@ -20465,85 +22563,96 @@ void MessagesManager::send_update_chat_last_message_impl(const Dialog *d, const LOG(INFO) << "Send updateChatLastMessage in " << d->dialog_id << " to " << d->last_message_id << " from " << source; auto update = make_tl_object( d->dialog_id.get(), get_message_object(d->dialog_id, get_message(d, d->last_message_id)), - DialogDate(d->order, d->dialog_id) <= last_dialog_date_ ? d->order : 0); + get_dialog_public_order(d)); send_closure(G()->td(), &Td::send_update, std::move(update)); } -void MessagesManager::send_update_unread_message_count(DialogId dialog_id, bool force, const char *source) { +void MessagesManager::send_update_unread_message_count(FolderId folder_id, DialogId dialog_id, bool force, + const char *source) { if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { return; } - CHECK(is_message_unread_count_inited_); - if (unread_message_muted_count_ < 0 || unread_message_muted_count_ > unread_message_total_count_) { - LOG(ERROR) << "Unread message count became invalid: " << unread_message_total_count_ << '/' - << unread_message_total_count_ - unread_message_muted_count_ << " from " << source << " and " + auto &list = get_dialog_list(folder_id); + CHECK(list.is_message_unread_count_inited_); + if (list.unread_message_muted_count_ < 0 || list.unread_message_muted_count_ > list.unread_message_total_count_) { + LOG(ERROR) << "Unread message count became invalid: " << list.unread_message_total_count_ << '/' + << list.unread_message_total_count_ - list.unread_message_muted_count_ << " from " << source << " and " << dialog_id; - if (unread_message_muted_count_ < 0) { - unread_message_muted_count_ = 0; + if (list.unread_message_muted_count_ < 0) { + list.unread_message_muted_count_ = 0; } - if (unread_message_muted_count_ > unread_message_total_count_) { - unread_message_total_count_ = unread_message_muted_count_; - } - } - G()->td_db()->get_binlog_pmc()->set("unread_message_count", - PSTRING() << unread_message_total_count_ << ' ' << unread_message_muted_count_); - int32 unread_unmuted_count = unread_message_total_count_ - unread_message_muted_count_; - if (!force && running_get_difference_) { - LOG(INFO) << "Postpone updateUnreadMessageCount to " << unread_message_total_count_ << '/' << unread_unmuted_count - << " from " << source << " and " << dialog_id; - have_postponed_unread_message_count_update_ = true; - } else { - have_postponed_unread_message_count_update_ = false; - LOG(INFO) << "Send updateUnreadMessageCount to " << 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()); - } -} - -void MessagesManager::send_update_unread_chat_count(DialogId dialog_id, bool force, const char *source) { - if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { - return; - } - - CHECK(is_dialog_unread_count_inited_); - if (unread_dialog_muted_marked_count_ < 0 || unread_dialog_marked_count_ < unread_dialog_muted_marked_count_ || - unread_dialog_muted_count_ < unread_dialog_muted_marked_count_ || - unread_dialog_total_count_ + unread_dialog_muted_marked_count_ < - unread_dialog_muted_count_ + unread_dialog_marked_count_) { - LOG(ERROR) << "Unread chat count became invalid: " << unread_dialog_total_count_ << '/' - << unread_dialog_total_count_ - unread_dialog_muted_count_ << '/' << unread_dialog_marked_count_ << '/' - << unread_dialog_marked_count_ - unread_dialog_muted_marked_count_ << " from " << source << " and " - << dialog_id; - if (unread_dialog_muted_marked_count_ < 0) { - unread_dialog_muted_marked_count_ = 0; - } - if (unread_dialog_marked_count_ < unread_dialog_muted_marked_count_) { - unread_dialog_marked_count_ = unread_dialog_muted_marked_count_; - } - if (unread_dialog_muted_count_ < unread_dialog_muted_marked_count_) { - unread_dialog_muted_count_ = unread_dialog_muted_marked_count_; - } - if (unread_dialog_total_count_ + unread_dialog_muted_marked_count_ < - unread_dialog_muted_count_ + unread_dialog_marked_count_) { - unread_dialog_total_count_ = - unread_dialog_muted_count_ + unread_dialog_marked_count_ - unread_dialog_muted_marked_count_; + if (list.unread_message_muted_count_ > list.unread_message_total_count_) { + list.unread_message_total_count_ = list.unread_message_muted_count_; } } G()->td_db()->get_binlog_pmc()->set( - "unread_dialog_count", PSTRING() << unread_dialog_total_count_ << ' ' << unread_dialog_muted_count_ << ' ' - << unread_dialog_marked_count_ << ' ' << unread_dialog_muted_marked_count_); - bool need_postpone = !force && running_get_difference_; - int32 unread_unmuted_count = unread_dialog_total_count_ - unread_dialog_muted_count_; - int32 unread_unmuted_marked_count = unread_dialog_marked_count_ - unread_dialog_muted_marked_count_; - LOG(INFO) << (need_postpone ? "Postpone" : "Send") << " updateUnreadChatCount to " << unread_dialog_total_count_ - << '/' << unread_unmuted_count << '/' << unread_dialog_marked_count_ << '/' << unread_unmuted_marked_count - << " from " << source << " and " << dialog_id; - if (need_postpone) { - have_postponed_unread_chat_count_update_ = true; + PSTRING() << "unread_message_count" << folder_id.get(), + PSTRING() << list.unread_message_total_count_ << ' ' << list.unread_message_muted_count_); + int32 unread_unmuted_count = list.unread_message_total_count_ - list.unread_message_muted_count_; + if (!force && running_get_difference_) { + LOG(INFO) << "Postpone updateUnreadMessageCount in " << folder_id << " to " << list.unread_message_total_count_ + << '/' << unread_unmuted_count << " from " << source << " and " << dialog_id; + postponed_unread_message_count_updates_.insert(folder_id); } else { - have_postponed_unread_chat_count_update_ = false; - send_closure(G()->td(), &Td::send_update, get_update_unread_chat_count_object()); + 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)); + } +} + +void MessagesManager::send_update_unread_chat_count(FolderId folder_id, DialogId dialog_id, bool force, + const char *source) { + if (td_->auth_manager_->is_bot() || !G()->parameters().use_message_db) { + return; + } + + auto &list = get_dialog_list(folder_id); + CHECK(list.is_dialog_unread_count_inited_); + if (list.unread_dialog_muted_marked_count_ < 0 || + list.unread_dialog_marked_count_ < list.unread_dialog_muted_marked_count_ || + list.unread_dialog_muted_count_ < list.unread_dialog_muted_marked_count_ || + list.unread_dialog_total_count_ + list.unread_dialog_muted_marked_count_ < + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_) { + LOG(ERROR) << "Unread chat count became invalid: " << list.unread_dialog_total_count_ << '/' + << list.unread_dialog_total_count_ - list.unread_dialog_muted_count_ << '/' + << list.unread_dialog_marked_count_ << '/' + << list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_ << " from " << source + << " and " << dialog_id; + if (list.unread_dialog_muted_marked_count_ < 0) { + list.unread_dialog_muted_marked_count_ = 0; + } + if (list.unread_dialog_marked_count_ < list.unread_dialog_muted_marked_count_) { + list.unread_dialog_marked_count_ = list.unread_dialog_muted_marked_count_; + } + if (list.unread_dialog_muted_count_ < list.unread_dialog_muted_marked_count_) { + list.unread_dialog_muted_count_ = list.unread_dialog_muted_marked_count_; + } + if (list.unread_dialog_total_count_ + list.unread_dialog_muted_marked_count_ < + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_) { + list.unread_dialog_total_count_ = + list.unread_dialog_muted_count_ + list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_; + } + } + G()->td_db()->get_binlog_pmc()->set( + PSTRING() << "unread_dialog_count" << folder_id.get(), + PSTRING() << list.unread_dialog_total_count_ << ' ' << list.unread_dialog_muted_count_ << ' ' + << list.unread_dialog_marked_count_ << ' ' << list.unread_dialog_muted_marked_count_ << ' ' + << list.server_dialog_total_count_ << ' ' << list.secret_chat_total_count_); + bool need_postpone = !force && running_get_difference_; + int32 unread_unmuted_count = list.unread_dialog_total_count_ - list.unread_dialog_muted_count_; + int32 unread_unmuted_marked_count = list.unread_dialog_marked_count_ - list.unread_dialog_muted_marked_count_; + LOG(INFO) << (need_postpone ? "Postpone" : "Send") << " updateUnreadChatCount in " << folder_id << " to " + << list.in_memory_dialog_total_count_ << '/' << list.server_dialog_total_count_ << '+' + << list.secret_chat_total_count_ << '/' << list.unread_dialog_total_count_ << '/' << unread_unmuted_count + << '/' << list.unread_dialog_marked_count_ << '/' << unread_unmuted_marked_count << " from " << source + << " and " << dialog_id; + if (need_postpone) { + 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)); } } @@ -20605,9 +22714,9 @@ void MessagesManager::send_update_chat_is_sponsored(const Dialog *d) const { 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; - auto order = DialogDate(d->order, d->dialog_id) <= last_dialog_date_ ? d->order : 0; - send_closure(G()->td(), &Td::send_update, - make_tl_object(d->dialog_id.get(), is_sponsored, order)); + send_closure( + G()->td(), &Td::send_update, + make_tl_object(d->dialog_id.get(), is_sponsored, get_dialog_public_order(d))); } void MessagesManager::send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const { @@ -20619,6 +22728,46 @@ void MessagesManager::send_update_chat_online_member_count(DialogId dialog_id, i make_tl_object(dialog_id.get(), online_member_count)); } +void MessagesManager::send_update_chat_chat_list(const Dialog *d) const { + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_chat_list"; + send_closure(G()->td(), &Td::send_update, + make_tl_object(d->dialog_id.get(), get_chat_list_object(d))); +} + +void MessagesManager::send_update_chat_action_bar(const Dialog *d) { + 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"); + send_closure(G()->td(), &Td::send_update, + td_api::make_object(d->dialog_id.get(), get_chat_action_bar_object(d))); + + if (d->dialog_id.get_type() == DialogType::User) { + td_->contacts_manager_->for_each_secret_chat_with_user( + d->dialog_id.get_user_id(), [this, d](SecretChatId secret_chat_id) { + send_closure(G()->td(), &Td::send_update, + td_api::make_object(DialogId(secret_chat_id).get(), + get_chat_action_bar_object(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); + } + + bool has_scheduled_messages = + d->has_scheduled_server_messages || d->has_scheduled_database_messages || d->scheduled_messages != nullptr; + if (has_scheduled_messages == d->last_sent_has_scheduled_messages) { + return; + } + d->last_sent_has_scheduled_messages = has_scheduled_messages; + + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_has_scheduled_messages"; + send_closure(G()->td(), &Td::send_update, + td_api::make_object(d->dialog_id.get(), has_scheduled_messages)); +} + void MessagesManager::on_send_message_get_quick_ack(int64 random_id) { auto it = being_sent_messages_.find(random_id); if (it == being_sent_messages_.end()) { @@ -20650,6 +22799,7 @@ void MessagesManager::check_send_message_result(int64 random_id, DialogId dialog } else { td_->updates_manager_->schedule_get_difference("check_send_message_result"); } + repair_dialog_scheduled_messages(dialog_id); } } @@ -20662,8 +22812,9 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI } if (!new_message_id.is_valid()) { LOG(ERROR) << "Receive " << new_message_id << " as sent message from " << source; - on_send_message_fail(random_id, - Status::Error(500, "Internal server error: receive invalid message id as sent message id")); + on_send_message_fail( + random_id, + Status::Error(500, "Internal server error: receive invalid message identifier as sent message identifier")); return {}; } if (new_message_id.is_yet_unsent()) { @@ -20708,6 +22859,12 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI auto dialog_id = it->second.get_dialog_id(); auto old_message_id = it->second.get_message_id(); + if (new_message_id.is_local() && dialog_id.get_type() != DialogType::SecretChat) { + LOG(ERROR) << "Receive " << new_message_id << " as sent message from " << source; + on_send_message_fail(random_id, Status::Error(500, "Internal server error: receive local as sent message")); + return {}; + } + being_sent_messages_.erase(it); Dialog *d = get_dialog(dialog_id); @@ -20719,7 +22876,7 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI // 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_messages_from_server(dialog_id, {new_message_id}, true, 0, Auto()); + delete_message_from_server(dialog_id, new_message_id, true); return {}; } @@ -20728,7 +22885,7 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI // dump_debug_message_op(d, 5); } - // imitation of update_message(d, sent_message, std::move(new_message), &need_update_dialog_pos); + // 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; } else { @@ -20749,9 +22906,9 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI sent_message->is_content_secret, source); } - sent_message->message_id = new_message_id; - sent_message->random_y = get_random_y(sent_message->message_id); + set_message_id(sent_message, new_message_id); + sent_message->from_database = false; sent_message->have_previous = true; sent_message->have_next = true; @@ -20801,6 +22958,7 @@ void MessagesManager::on_send_message_file_part_missing(int64 random_id, int bad } if (dialog_id.get_type() == DialogType::SecretChat) { + CHECK(!m->message_id.is_scheduled()); Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -20852,6 +23010,7 @@ void MessagesManager::on_send_message_file_reference_error(int64 random_id) { } if (dialog_id.get_type() == DialogType::SecretChat) { + CHECK(!m->message_id.is_scheduled()); Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -20905,7 +23064,7 @@ void MessagesManager::on_send_media_group_file_reference_error(DialogId dialog_i media_album_id = m->media_album_id; CHECK(dialog_id == full_message_id.get_dialog_id()); - message_ids.push_back(full_message_id.get_message_id()); + message_ids.push_back(m->message_id); messages.push_back(m); } @@ -21099,24 +23258,23 @@ void MessagesManager::on_send_message_fail(int64 random_id, Status error) { fail_send_message(full_message_id, error_code, error_message); } -MessageId MessagesManager::get_next_message_id(Dialog *d, int32 type) { +MessageId MessagesManager::get_next_message_id(Dialog *d, MessageType type) { CHECK(d != nullptr); - int64 last = - std::max({d->last_message_id.get(), d->last_new_message_id.get(), d->last_database_message_id.get(), - d->last_assigned_message_id.get(), d->last_clear_history_message_id.get(), - d->deleted_last_message_id.get(), d->max_unavailable_message_id.get(), d->max_added_message_id.get()}); - if (last < d->last_read_inbox_message_id.get() && - d->last_read_inbox_message_id.get() <= (d->last_new_message_id.get() | MessageId::FULL_TYPE_MASK)) { - last = d->last_read_inbox_message_id.get(); + MessageId last_message_id = + std::max({d->last_message_id, d->last_new_message_id, d->last_database_message_id, d->last_assigned_message_id, + d->last_clear_history_message_id, d->deleted_last_message_id, d->max_unavailable_message_id, + d->max_added_message_id}); + if (last_message_id < d->last_read_inbox_message_id && + d->last_read_inbox_message_id < d->last_new_message_id.get_next_server_message_id()) { + last_message_id = d->last_read_inbox_message_id; } - if (last < d->last_read_outbox_message_id.get() && - d->last_read_outbox_message_id.get() <= (d->last_new_message_id.get() | MessageId::FULL_TYPE_MASK)) { - last = d->last_read_outbox_message_id.get(); + if (last_message_id < d->last_read_outbox_message_id && + d->last_read_outbox_message_id < d->last_new_message_id.get_next_server_message_id()) { + last_message_id = d->last_read_outbox_message_id; } - int64 base = (last + MessageId::TYPE_MASK + 1) & ~MessageId::TYPE_MASK; - d->last_assigned_message_id = MessageId(base + type); - if (d->last_assigned_message_id.get() > MessageId::max().get()) { + d->last_assigned_message_id = last_message_id.get_next_message_id(type); + if (d->last_assigned_message_id > MessageId::max()) { LOG(FATAL) << "Force restart because of message_id overflow: " << d->last_assigned_message_id; } CHECK(d->last_assigned_message_id.is_valid()); @@ -21124,11 +23282,25 @@ MessageId MessagesManager::get_next_message_id(Dialog *d, int32 type) { } MessageId MessagesManager::get_next_yet_unsent_message_id(Dialog *d) { - return get_next_message_id(d, MessageId::TYPE_YET_UNSENT); + return get_next_message_id(d, MessageType::YetUnsent); } MessageId MessagesManager::get_next_local_message_id(Dialog *d) { - return get_next_message_id(d, MessageId::TYPE_LOCAL); + return get_next_message_id(d, MessageType::Local); +} + +MessageId MessagesManager::get_next_yet_unsent_scheduled_message_id(const Dialog *d, int32 date) { + CHECK(date > 0); + 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 (prev_date < date) { + return MessageId(ScheduledServerMessageId(1), date).get_next_message_id(MessageType::YetUnsent); + } + CHECK(*it != nullptr); + return (*it)->message_id.get_next_message_id(MessageType::YetUnsent); } void MessagesManager::fail_send_message(FullMessageId full_message_id, int error_code, const string &error_message) { @@ -21136,7 +23308,7 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); MessageId old_message_id = full_message_id.get_message_id(); - CHECK(old_message_id.is_valid()); + CHECK(old_message_id.is_valid() || old_message_id.is_valid_scheduled()); CHECK(old_message_id.is_yet_unsent()); bool need_update_dialog_pos = false; @@ -21153,15 +23325,28 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error // dump_debug_message_op(d, 5); } - auto new_message_id = MessageId(old_message_id.get() - MessageId::TYPE_YET_UNSENT + MessageId::TYPE_LOCAL); - if (get_message_force(d, new_message_id, "fail_send_message") != nullptr || - d->deleted_message_ids.count(new_message_id) || new_message_id.get() <= d->last_clear_history_message_id.get()) { - new_message_id = get_next_local_message_id(d); + MessageId new_message_id = + old_message_id.get_next_message_id(MessageType::Local); // trying to not change message place + if (!old_message_id.is_scheduled()) { + if (get_message_force(d, new_message_id, "fail_send_message") != nullptr || + d->deleted_message_ids.count(new_message_id) || new_message_id <= d->last_clear_history_message_id) { + new_message_id = get_next_local_message_id(d); + } else if (new_message_id > d->last_assigned_message_id) { + d->last_assigned_message_id = new_message_id; + } + } else { + while (get_message_force(d, new_message_id, "fail_send_message") != nullptr || + d->deleted_message_ids.count(new_message_id)) { + new_message_id = new_message_id.get_next_message_id(MessageType::Local); + } } - message->message_id = new_message_id; - CHECK(message->message_id.is_valid()); - message->random_y = get_random_y(message->message_id); + set_message_id(message, new_message_id); + if (old_message_id.is_scheduled()) { + CHECK(message->message_id.is_valid_scheduled()); + } else { + CHECK(message->message_id.is_valid()); + } message->is_failed_to_send = true; message->send_error_code = error_code; message->send_error_message = error_message; @@ -21175,6 +23360,7 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error } update_failed_to_send_message_content(td_, message->content); + message->from_database = false; message->have_previous = true; message->have_next = true; @@ -21204,7 +23390,7 @@ void MessagesManager::fail_edit_message_media(FullMessageId full_message_id, Sta Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); MessageId message_id = full_message_id.get_message_id(); - CHECK(message_id.is_server()); + CHECK(message_id.is_any_server()); auto m = get_message(d, message_id); if (m == nullptr) { @@ -21225,6 +23411,11 @@ void MessagesManager::on_update_dialog_draft_message(DialogId dialog_id, auto d = get_dialog_force(dialog_id); if (d == nullptr) { LOG(INFO) << "Ignore update chat draft in unknown " << dialog_id; + if (!have_input_peer(dialog_id, AccessRights::Read)) { + LOG(ERROR) << "Have no read access to " << dialog_id << " to repair chat draft message"; + } else { + send_get_dialog_query(dialog_id, Promise()); + } return; } update_dialog_draft_message(d, get_draft_message(td_->contacts_manager_.get(), std::move(draft_message)), true, true); @@ -21270,7 +23461,7 @@ bool MessagesManager::update_dialog_draft_message(Dialog *d, unique_ptrpinned_order == DEFAULT_ORDER) { return; } @@ -21289,18 +23482,20 @@ void MessagesManager::on_update_dialog_is_pinned(DialogId dialog_id, bool is_pin update_dialog_pos(d, false, "on_update_dialog_is_pinned"); } -void MessagesManager::on_update_pinned_dialogs() { +void MessagesManager::on_update_pinned_dialogs(FolderId folder_id) { // TODO logevent + delete_logevent_promise - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this)](Unit /* ignore result */) { - send_closure(actor_id, &MessagesManager::reload_pinned_dialogs, Promise()); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), folder_id](Unit /* ignore result */) { + 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() + 1; - if (ordered_dialogs_.size() >= needed_dialogs) { + size_t needed_dialogs = 2 * get_pinned_dialogs_limit(folder_id) + (folder_id == FolderId::main() ? 1 : 0); + auto &list = get_dialog_list(folder_id); + if (list.ordered_dialogs_.size() >= needed_dialogs) { query_promise.set_value(Unit()); } else { - load_dialog_list(narrow_cast(needed_dialogs - ordered_dialogs_.size()), true, std::move(query_promise)); + load_dialog_list(folder_id, narrow_cast(needed_dialogs - list.ordered_dialogs_.size()), true, + std::move(query_promise)); } } @@ -21339,16 +23534,17 @@ void MessagesManager::set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_a send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), is_marked_as_unread)); + auto &list = get_dialog_list(d->folder_id); if (d->server_unread_count + d->local_unread_count == 0 && need_unread_counter(d->order) && - is_dialog_unread_count_inited_) { + list.is_dialog_unread_count_inited_) { int32 delta = d->is_marked_as_unread ? 1 : -1; - unread_dialog_total_count_ += delta; - unread_dialog_marked_count_ += delta; + list.unread_dialog_total_count_ += delta; + list.unread_dialog_marked_count_ += delta; if (is_dialog_muted(d)) { - unread_dialog_muted_count_ += delta; - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } - send_update_unread_chat_count(d->dialog_id, true, "set_dialog_is_marked_as_unread"); + send_update_unread_chat_count(d->folder_id, d->dialog_id, true, "set_dialog_is_marked_as_unread"); } } @@ -21372,7 +23568,7 @@ void MessagesManager::on_update_dialog_pinned_message_id(DialogId dialog_id, Mes LOG(INFO) << "Pinned message in " << d->dialog_id << " is still " << pinned_message_id; if (!d->is_pinned_message_id_inited) { d->is_pinned_message_id_inited = true; - on_dialog_updated(d->dialog_id, "set_dialog_is_pinned_message_id_inited"); + on_dialog_updated(dialog_id, "on_update_dialog_pinned_message_id"); } return; } @@ -21393,6 +23589,117 @@ 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) { + return; + } + + // TODO create logevent + get_dialog_scheduled_messages(dialog_id, PromiseCreator::lambda([actor_id = actor_id(this), dialog_id](Unit) { + send_closure(G()->messages_manager(), &MessagesManager::get_dialog_scheduled_messages, + dialog_id, Promise()); + })); +} + +void MessagesManager::on_update_dialog_has_scheduled_server_messages(DialogId dialog_id, + bool has_scheduled_server_messages) { + if (!dialog_id.is_valid()) { + LOG(ERROR) << "Receive has_scheduled_server_messages in invalid " << dialog_id; + return; + } + if (td_->auth_manager_->is_bot() || dialog_id.get_type() == DialogType::SecretChat) { + return; + } + + auto d = get_dialog_force(dialog_id); + if (d == nullptr) { + // nothing to do + return; + } + + 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); + } +} + +void MessagesManager::set_dialog_has_scheduled_server_messages(Dialog *d, bool has_scheduled_server_messages) { + 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); + 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); +} + +void MessagesManager::set_dialog_has_scheduled_database_messages(DialogId dialog_id, + bool has_scheduled_database_messages) { + return set_dialog_has_scheduled_database_messages_impl(get_dialog(dialog_id), has_scheduled_database_messages); +} + +void MessagesManager::set_dialog_has_scheduled_database_messages_impl(Dialog *d, bool has_scheduled_database_messages) { + CHECK(d != nullptr); + if (d->has_scheduled_database_messages == has_scheduled_database_messages) { + return; + } + + if (d->has_scheduled_database_messages && d->scheduled_messages != nullptr && + !d->scheduled_messages->message_id.is_yet_unsent()) { + // to prevent race between add_message_to_database and check of has_scheduled_database_messages + return; + } + + CHECK(G()->parameters().use_message_db); + + d->has_scheduled_database_messages = has_scheduled_database_messages; + on_dialog_updated(d->dialog_id, "set_dialog_has_scheduled_database_messages"); +} + +void MessagesManager::on_update_dialog_folder_id(DialogId dialog_id, FolderId folder_id) { + auto d = get_dialog_force(dialog_id); + if (d == nullptr) { + // nothing to do + return; + } + + set_dialog_folder_id(d, folder_id); +} + +void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) { + CHECK(d != nullptr); + + if (d->folder_id == folder_id) { + if (!d->is_folder_id_inited) { + LOG(INFO) << "Folder of " << d->dialog_id << " is still " << folder_id; + d->is_folder_id_inited = true; + on_dialog_updated(d->dialog_id, "set_dialog_folder_id"); + } + return; + } + + LOG(INFO) << "Change " << d->dialog_id << " folder from " << d->folder_id << " to " << 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); + } + set_dialog_order(d, DEFAULT_ORDER, true, false, "set_dialog_folder_id old"); + + // change the folder + d->folder_id = folder_id; + d->is_folder_id_inited = true; + + // 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); + + on_dialog_updated(d->dialog_id, "set_dialog_folder_id"); +} + void MessagesManager::on_create_new_dialog_success(int64 random_id, tl_object_ptr &&updates, DialogType expected_type, Promise &&promise) { auto sent_messages = UpdatesManager::get_new_messages(updates.get()); @@ -21460,8 +23767,7 @@ void MessagesManager::on_dialog_bots_updated(DialogId dialog_id, vector return; } const Message *m = get_message_force(d, d->reply_markup_message_id, "on_dialog_bots_updated"); - if (m == nullptr || (m->sender_user_id.is_valid() && - std::find(bot_user_ids.begin(), bot_user_ids.end(), m->sender_user_id) == bot_user_ids.end())) { + if (m == nullptr || (m->sender_user_id.is_valid() && !td::contains(bot_user_ids, m->sender_user_id))) { LOG(INFO) << "Remove reply markup in " << dialog_id << ", because bot " << (m == nullptr ? UserId() : m->sender_user_id) << " isn't a member of the chat"; set_dialog_reply_markup(d, MessageId()); @@ -21497,6 +23803,78 @@ void MessagesManager::on_dialog_permissions_updated(DialogId dialog_id) { } } +void MessagesManager::on_dialog_user_is_contact_updated(DialogId dialog_id, bool is_contact) { + CHECK(dialog_id.get_type() == DialogType::User); + auto d = get_dialog(dialog_id); // called from update_user, must not create the dialog + if (d != nullptr && d->is_update_new_chat_sent) { + if (d->know_action_bar) { + if (is_contact) { + if (d->can_block_user || d->can_add_contact) { + d->can_block_user = false; + d->can_add_contact = false; + 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"); + } + } + } +} + +void MessagesManager::on_dialog_user_is_blocked_updated(DialogId dialog_id, bool is_blocked) { + CHECK(dialog_id.get_type() == DialogType::User); + auto d = get_dialog(dialog_id); // called from update_user_full, must not create the dialog + if (d != nullptr && d->is_update_new_chat_sent) { + if (d->know_action_bar) { + if (is_blocked) { + if (d->can_report_spam || d->can_share_phone_number || d->can_block_user || d->can_add_contact) { + d->can_report_spam = false; + d->can_share_phone_number = false; + d->can_block_user = false; + d->can_add_contact = false; + 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"); + } + } + } +} + +void MessagesManager::on_dialog_user_is_deleted_updated(DialogId dialog_id, bool is_deleted) { + CHECK(dialog_id.get_type() == DialogType::User); + auto d = get_dialog(dialog_id); // called from update_user, must not create the dialog + if (d != nullptr && d->is_update_new_chat_sent) { + if (d->know_action_bar) { + if (is_deleted) { + if (d->can_share_phone_number || d->can_block_user || d->can_add_contact) { + d->can_share_phone_number = false; + d->can_block_user = false; + d->can_add_contact = false; + 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"); + } + } + } +} + DialogId MessagesManager::resolve_dialog_username(const string &username) const { auto cleaned_username = clean_username(username); auto it = resolved_usernames_.find(cleaned_username); @@ -21539,7 +23917,7 @@ DialogId MessagesManager::search_public_dialog(const string &username_to_search, if (dialog_id.is_valid()) { if (have_input_peer(dialog_id, AccessRights::Read)) { if (td_->auth_manager_->is_bot()) { - force_create_dialog(dialog_id, "search public dialog"); + force_create_dialog(dialog_id, "search public dialog", true); } else { const Dialog *d = get_dialog_force(dialog_id); if (!is_dialog_inited(d)) { @@ -21553,7 +23931,7 @@ DialogId MessagesManager::search_public_dialog(const string &username_to_search, } else { // bot username maybe known despite there is no access_hash if (force || dialog_id.get_type() != DialogType::User) { - force_create_dialog(dialog_id, "search public dialog"); + force_create_dialog(dialog_id, "search public dialog", true); promise.set_value(Unit()); return dialog_id; } @@ -21855,7 +24233,21 @@ void MessagesManager::send_dialog_action(DialogId dialog_id, const tl_object_ptr return promise.set_value(Unit()); } - if (dialog_id.get_type() == DialogType::SecretChat) { + 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) { tl_object_ptr send_action; switch (action->get_id()) { case td_api::chatActionCancel::ID: @@ -21993,7 +24385,7 @@ void MessagesManager::on_send_dialog_action_timeout(DialogId dialog_id) { if (m == nullptr) { return; } - if (m->forward_info != nullptr || m->had_forward_info) { + if (m->forward_info != nullptr || m->had_forward_info || message_id.is_scheduled()) { return; } @@ -22161,6 +24553,97 @@ SearchMessagesFilter MessagesManager::get_search_messages_filter( } } +void MessagesManager::set_dialog_folder_id(DialogId dialog_id, FolderId folder_id, Promise &&promise) { + LOG(INFO) << "Receive setChatChatList request to change folder of " << dialog_id << " to " << folder_id; + + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr) { + return promise.set_error(Status::Error(3, "Chat not found")); + } + + if (d->order == DEFAULT_ORDER) { + return promise.set_error(Status::Error(400, "Chat is not in a chat list")); + } + + if (d->folder_id == folder_id) { + return promise.set_value(Unit()); + } + + if (!have_input_peer(dialog_id, AccessRights::Read)) { + return promise.set_error(Status::Error(6, "Can't access the chat")); + } + + set_dialog_folder_id(d, folder_id); + + if (dialog_id.get_type() != DialogType::SecretChat) { + set_dialog_folder_id_on_server(dialog_id, false); + } + promise.set_value(Unit()); +} + +class MessagesManager::SetDialogFolderIdOnServerLogEvent { + public: + DialogId dialog_id_; + FolderId folder_id_; + + template + void store(StorerT &storer) const { + td::store(dialog_id_, storer); + td::store(folder_id_, storer); + } + + template + void parse(ParserT &parser) { + td::parse(dialog_id_, parser); + td::parse(folder_id_, parser); + } +}; + +void MessagesManager::set_dialog_folder_id_on_server(DialogId dialog_id, bool from_binlog) { + auto d = get_dialog(dialog_id); + CHECK(d != nullptr); + + if (!from_binlog && G()->parameters().use_message_db) { + SetDialogFolderIdOnServerLogEvent logevent; + logevent.dialog_id_ = dialog_id; + logevent.folder_id_ = d->folder_id; + auto storer = LogEventStorerImpl(logevent); + if (d->set_folder_id_logevent_id == 0) { + d->set_folder_id_logevent_id = + binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::SetDialogFolderIdOnServer, storer); + } else { + binlog_rewrite(G()->td_db()->get_binlog(), d->set_folder_id_logevent_id, + LogEvent::HandlerType::SetDialogFolderIdOnServer, storer); + } + d->set_folder_id_logevent_id_generation++; + } + + Promise<> promise; + if (d->set_folder_id_logevent_id != 0) { + promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, + generation = d->set_folder_id_logevent_id_generation](Result result) { + if (!G()->close_flag()) { + send_closure(actor_id, &MessagesManager::on_updated_dialog_folder_id, dialog_id, generation); + } + }); + } + + // TODO do not send two queries simultaneously or use SequenceDispatcher + td_->create_handler(std::move(promise))->send(dialog_id, d->folder_id); +} + +void MessagesManager::on_updated_dialog_folder_id(DialogId dialog_id, uint64 generation) { + auto d = get_dialog(dialog_id); + CHECK(d != nullptr); + LOG(INFO) << "Saved folder_id of " << dialog_id << " with logevent " << d->set_folder_id_logevent_id; + if (d->set_folder_id_logevent_id_generation == generation) { + CHECK(d->set_folder_id_logevent_id != 0); + LOG(INFO) << "Delete set folder_id logevent " << d->set_folder_id_logevent_id; + binlog_erase(G()->td_db()->get_binlog(), d->set_folder_id_logevent_id); + d->set_folder_id_logevent_id = 0; + } +} + void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptr &photo, Promise &&promise) { LOG(INFO) << "Receive setChatPhoto request to change photo of " << dialog_id; @@ -22209,9 +24692,9 @@ void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptrfile_manager_->get_file_view(file_id); CHECK(!file_view.is_encrypted()); - if (file_view.has_remote_location() && !file_view.remote_location().is_web()) { + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web()) { // file has already been uploaded, just send change photo request - auto input_photo = file_view.remote_location().as_input_photo(); + auto input_photo = file_view.main_remote_location().as_input_photo(); send_edit_dialog_photo_query( dialog_id, file_id, make_tl_object(std::move(input_photo)), std::move(promise)); return; @@ -22330,7 +24813,7 @@ void MessagesManager::set_dialog_permissions(DialogId dialog_id, auto new_permissions = get_restricted_rights(permissions); - // TODO this can be wrong if there was previous change title requests + // TODO this can be wrong if there was previous change permissions requests if (get_dialog_permissions(dialog_id) == new_permissions) { return promise.set_value(Unit()); } @@ -22403,10 +24886,13 @@ void MessagesManager::pin_dialog_message(DialogId dialog_id, MessageId message_i if (is_unpin) { CHECK(message_id == MessageId()); } else { - if (!have_message({dialog_id, message_id}, "pin_dialog_message")) { + if (!have_message_force({dialog_id, message_id}, "pin_dialog_message")) { return promise.set_error(Status::Error(6, "Message not found")); } + if (message_id.is_scheduled()) { + return promise.set_error(Status::Error(6, "Scheduled message can't be pinned")); + } if (!message_id.is_server()) { return promise.set_error(Status::Error(6, "Message can't be pinned")); } @@ -22494,7 +24980,8 @@ void MessagesManager::set_dialog_participant_status(DialogId dialog_id, UserId u DialogParticipant MessagesManager::get_dialog_participant(DialogId dialog_id, UserId user_id, int64 &random_id, bool force, Promise &&promise) { - LOG(INFO) << "Receive GetChatMember request to get " << user_id << " in " << dialog_id; + LOG(INFO) << "Receive GetChatMember request to get " << user_id << " in " << dialog_id << " with random_id " + << random_id; if (!have_dialog_force(dialog_id)) { promise.set_error(Status::Error(3, "Chat not found")); return DialogParticipant(); @@ -22524,9 +25011,9 @@ DialogParticipant MessagesManager::get_dialog_participant(DialogId dialog_id, Us auto peer_user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); if (user_id == td_->contacts_manager_->get_my_id()) { promise.set_value(Unit()); - return {user_id, peer_user_id, 0, DialogParticipantStatus::Member()}; + return {user_id, peer_user_id.is_valid() ? peer_user_id : user_id, 0, DialogParticipantStatus::Member()}; } - if (user_id == peer_user_id) { + if (peer_user_id.is_valid() && user_id == peer_user_id) { promise.set_value(Unit()); return {peer_user_id, user_id, 0, DialogParticipantStatus::Member()}; } @@ -22547,7 +25034,7 @@ std::pair> MessagesManager::search_private_chat vector user_ids; switch (filter) { case DialogParticipantsFilter::Contacts: - if (td_->contacts_manager_->is_user_contact(peer_user_id)) { + if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_contact(peer_user_id)) { user_ids.push_back(peer_user_id); } break; @@ -22555,7 +25042,9 @@ std::pair> MessagesManager::search_private_chat break; case DialogParticipantsFilter::Members: user_ids.push_back(my_user_id); - user_ids.push_back(peer_user_id); + if (peer_user_id.is_valid()) { + user_ids.push_back(peer_user_id); + } break; case DialogParticipantsFilter::Restricted: break; @@ -22565,7 +25054,7 @@ std::pair> MessagesManager::search_private_chat if (td_->auth_manager_->is_bot()) { user_ids.push_back(my_user_id); } - if (td_->contacts_manager_->is_user_bot(peer_user_id)) { + if (peer_user_id.is_valid() && td_->contacts_manager_->is_user_bot(peer_user_id)) { user_ids.push_back(peer_user_id); } break; @@ -22575,7 +25064,8 @@ std::pair> MessagesManager::search_private_chat auto result = td_->contacts_manager_->search_among_users(user_ids, query, limit); return {result.first, transform(result.second, [&](UserId user_id) { - return DialogParticipant(user_id, user_id == my_user_id ? peer_user_id : my_user_id, 0, + return DialogParticipant(user_id, + user_id == my_user_id && peer_user_id.is_valid() ? peer_user_id : my_user_id, 0, DialogParticipantStatus::Member()); })}; } @@ -22661,7 +25151,8 @@ std::pair> MessagesManager::search_dialog_parti return {}; } -vector MessagesManager::get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise) { +vector MessagesManager::get_dialog_administrators(DialogId dialog_id, int left_tries, + Promise &&promise) { LOG(INFO) << "Receive GetChatAdministrators request in " << dialog_id; if (!have_dialog_force(dialog_id)) { promise.set_error(Status::Error(3, "Chat not found")); @@ -22903,7 +25394,8 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob case telegram_api::channelAdminLogEventActionUpdatePinned::ID: { auto action = move_tl_object_as(action_ptr); auto message = create_message( - parse_telegram_api_message(std::move(action->message_), "channelAdminLogEventActionUpdatePinned"), true); + parse_telegram_api_message(std::move(action->message_), false, "channelAdminLogEventActionUpdatePinned"), + true); if (message.second == nullptr) { return make_tl_object(); } @@ -22912,12 +25404,12 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob } case telegram_api::channelAdminLogEventActionEditMessage::ID: { auto action = move_tl_object_as(action_ptr); - auto old_message = create_message( - parse_telegram_api_message(std::move(action->prev_message_), "prev channelAdminLogEventActionEditMessage"), - true); - auto new_message = create_message( - parse_telegram_api_message(std::move(action->new_message_), "new channelAdminLogEventActionEditMessage"), - true); + auto old_message = create_message(parse_telegram_api_message(std::move(action->prev_message_), false, + "prev channelAdminLogEventActionEditMessage"), + true); + auto new_message = create_message(parse_telegram_api_message(std::move(action->new_message_), false, + "new channelAdminLogEventActionEditMessage"), + true); if (old_message.second == nullptr || new_message.second == nullptr || old_message.first != new_message.first) { LOG(ERROR) << "Failed to get edited message"; return nullptr; @@ -22929,7 +25421,7 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob case telegram_api::channelAdminLogEventActionStopPoll::ID: { auto action = move_tl_object_as(action_ptr); auto message = create_message( - parse_telegram_api_message(std::move(action->message_), "channelAdminLogEventActionStopPoll"), true); + parse_telegram_api_message(std::move(action->message_), false, "channelAdminLogEventActionStopPoll"), true); if (message.second == nullptr) { LOG(ERROR) << "Failed to get stopped poll message"; return nullptr; @@ -22944,7 +25436,8 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob case telegram_api::channelAdminLogEventActionDeleteMessage::ID: { auto action = move_tl_object_as(action_ptr); auto message = create_message( - parse_telegram_api_message(std::move(action->message_), "channelAdminLogEventActionDeleteMessage"), true); + parse_telegram_api_message(std::move(action->message_), false, "channelAdminLogEventActionDeleteMessage"), + true); if (message.second == nullptr) { LOG(ERROR) << "Failed to get deleted message"; return nullptr; @@ -22956,12 +25449,47 @@ tl_object_ptr MessagesManager::get_chat_event_action_ob auto action = move_tl_object_as(action_ptr); auto old_sticker_set_id = add_sticker_set(td_, std::move(action->prev_stickerset_)); auto new_sticker_set_id = add_sticker_set(td_, std::move(action->new_stickerset_)); - return make_tl_object(old_sticker_set_id, new_sticker_set_id); + return make_tl_object(old_sticker_set_id.get(), new_sticker_set_id.get()); } case telegram_api::channelAdminLogEventActionTogglePreHistoryHidden::ID: { auto action = move_tl_object_as(action_ptr); return make_tl_object(!action->new_value_); } + case telegram_api::channelAdminLogEventActionChangeLinkedChat::ID: { + auto action = move_tl_object_as(action_ptr); + + auto get_dialog_from_channel_id = [this](int32 channel_id_int) { + ChannelId channel_id(channel_id_int); + if (!channel_id.is_valid()) { + return DialogId(); + } + + DialogId dialog_id(channel_id); + force_create_dialog(dialog_id, "get_dialog_from_channel_id"); + return dialog_id; + }; + + auto old_linked_dialog_id = get_dialog_from_channel_id(action->prev_value_); + auto new_linked_dialog_id = get_dialog_from_channel_id(action->new_value_); + if (old_linked_dialog_id == new_linked_dialog_id) { + LOG(ERROR) << "Receive the same linked " << new_linked_dialog_id; + return nullptr; + } + return make_tl_object(old_linked_dialog_id.get(), new_linked_dialog_id.get()); + } + case telegram_api::channelAdminLogEventActionChangeLocation::ID: { + auto action = move_tl_object_as(action_ptr); + auto old_location = DialogLocation(std::move(action->prev_value_)); + auto new_location = DialogLocation(std::move(action->new_value_)); + return make_tl_object(old_location.get_chat_location_object(), + new_location.get_chat_location_object()); + } + case telegram_api::channelAdminLogEventActionToggleSlowMode::ID: { + auto action = move_tl_object_as(action_ptr); + auto old_slow_mode_delay = clamp(action->prev_value_, 0, 86400 * 366); + auto new_slow_mode_delay = clamp(action->new_value_, 0, 86400 * 366); + return make_tl_object(old_slow_mode_delay, new_slow_mode_delay); + } default: UNREACHABLE(); return nullptr; @@ -23017,20 +25545,18 @@ tl_object_ptr MessagesManager::get_chat_events_object(int64 return result; } -unique_ptr *MessagesManager::find_message(unique_ptr *v, MessageId message_id) { - return const_cast *>(find_message(static_cast *>(v), message_id)); +unique_ptr *MessagesManager::treap_find_message(unique_ptr *v, + MessageId message_id) { + return const_cast *>(treap_find_message(static_cast *>(v), message_id)); } -const unique_ptr *MessagesManager::find_message(const unique_ptr *v, - MessageId message_id) { +const unique_ptr *MessagesManager::treap_find_message(const unique_ptr *v, + MessageId message_id) { LOG(DEBUG) << "Searching for " << message_id << " in " << static_cast(v->get()); while (*v != nullptr) { - // LOG(DEBUG) << "Pass " << (*v)->message_id; - if ((*v)->message_id.get() < message_id.get()) { - // LOG(DEBUG) << "Go right"; + if ((*v)->message_id < message_id) { v = &(*v)->right; - } else if ((*v)->message_id.get() > message_id.get()) { - // LOG(DEBUG) << "Go left"; + } else if ((*v)->message_id > message_id) { v = &(*v)->left; } else { LOG(DEBUG) << "Message found"; @@ -23040,10 +25566,10 @@ const unique_ptr *MessagesManager::find_message(const return v; } -MessagesManager::Message *MessagesManager::insert_message(unique_ptr *v, unique_ptr message) { +MessagesManager::Message *MessagesManager::treap_insert_message(unique_ptr *v, unique_ptr message) { auto message_id = message->message_id; while (*v != nullptr && (*v)->random_y >= message->random_y) { - if ((*v)->message_id.get() < message_id.get()) { + if ((*v)->message_id < message_id) { v = &(*v)->right; } else if ((*v)->message_id == message_id) { UNREACHABLE(); @@ -23057,7 +25583,7 @@ MessagesManager::Message *MessagesManager::insert_message(unique_ptr *v unique_ptr cur = std::move(*v); while (cur != nullptr) { - if (cur->message_id.get() < message_id.get()) { + if (cur->message_id < message_id) { *left = std::move(cur); left = &((*left)->right); cur = std::move(*left); @@ -23073,26 +25599,57 @@ MessagesManager::Message *MessagesManager::insert_message(unique_ptr *v return v->get(); } +unique_ptr MessagesManager::treap_delete_message(unique_ptr *v) { + unique_ptr result = std::move(*v); + unique_ptr left = std::move(result->left); + unique_ptr right = std::move(result->right); + + while (left != nullptr || right != nullptr) { + if (left == nullptr || (right != nullptr && right->random_y > left->random_y)) { + *v = std::move(right); + v = &((*v)->left); + right = std::move(*v); + } else { + *v = std::move(left); + v = &((*v)->right); + left = std::move(*v); + } + } + CHECK(*v == nullptr); + + return result; +} + MessagesManager::Message *MessagesManager::get_message(Dialog *d, MessageId message_id) { return const_cast(get_message(static_cast(d), message_id)); } const MessagesManager::Message *MessagesManager::get_message(const Dialog *d, MessageId message_id) { - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { return nullptr; } CHECK(d != nullptr); LOG(DEBUG) << "Search for " << message_id << " in " << d->dialog_id; - auto result = find_message(&d->messages, message_id)->get(); - if (result != nullptr) { + bool is_scheduled = message_id.is_scheduled(); + if (is_scheduled && message_id.is_scheduled_server()) { + auto server_message_id = message_id.get_scheduled_server_message_id(); + auto it = d->scheduled_message_date.find(server_message_id); + if (it != d->scheduled_message_date.end()) { + int32 date = it->second; + message_id = MessageId(server_message_id, date); + CHECK(message_id.is_scheduled_server()); + } + } + auto result = treap_find_message(is_scheduled ? &d->scheduled_messages : &d->messages, message_id)->get(); + if (result != nullptr && !is_scheduled) { result->last_access_date = G()->unix_time_cached(); } return result; } MessagesManager::Message *MessagesManager::get_message_force(Dialog *d, MessageId message_id, const char *source) { - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { return nullptr; } @@ -23109,21 +25666,33 @@ MessagesManager::Message *MessagesManager::get_message_force(Dialog *d, MessageI return nullptr; } + if (message_id.is_scheduled()) { + if (d->has_loaded_scheduled_messages_from_database) { + return nullptr; + } + if (message_id.is_scheduled_server() && + d->deleted_scheduled_server_message_ids.count(message_id.get_scheduled_server_message_id())) { + return nullptr; + } + } + LOG(INFO) << "Trying to load " << FullMessageId{d->dialog_id, message_id} << " from database from " << source; + auto r_value = G()->td_db()->get_messages_db_sync()->get_message({d->dialog_id, message_id}); if (r_value.is_error()) { return nullptr; } - return on_get_message_from_database(d->dialog_id, d, r_value.ok(), source); + return on_get_message_from_database(d->dialog_id, d, r_value.ok(), message_id.is_scheduled(), source); } MessagesManager::Message *MessagesManager::on_get_message_from_database(DialogId dialog_id, Dialog *d, - const BufferSlice &value, const char *source) { + const BufferSlice &value, bool is_scheduled, + const char *source) { if (value.empty()) { return nullptr; } - auto m = parse_message(dialog_id, std::move(value)); + auto m = parse_message(dialog_id, std::move(value), is_scheduled); if (m == nullptr) { return nullptr; } @@ -23149,16 +25718,16 @@ MessagesManager::Message *MessagesManager::on_get_message_from_database(DialogId auto old_message = get_message(d, m->message_id); if (old_message != nullptr) { - CHECK(m->message_id == old_message->message_id); // data in the database is always outdated, so return a message from the memory if (dialog_id.get_type() == DialogType::SecretChat) { + CHECK(!is_scheduled); // just in case restore random_id to message_id corespondence // can be needed if there was newer unloaded message with the same random_id - add_random_id_to_message_id_correspondence(d, old_message->random_id, m->message_id); + add_random_id_to_message_id_correspondence(d, old_message->random_id, old_message->message_id); } - if (old_message->notification_id.is_valid()) { - add_notification_id_to_message_id_correspondence(d, old_message->notification_id, m->message_id); + if (old_message->notification_id.is_valid() && !is_scheduled) { + add_notification_id_to_message_id_correspondence(d, old_message->notification_id, old_message->message_id); } return old_message; @@ -23186,6 +25755,11 @@ int32 MessagesManager::get_random_y(MessageId message_id) { return static_cast(static_cast(message_id.get() * 2101234567u)); } +void MessagesManager::set_message_id(unique_ptr &message, MessageId message_id) { + message->message_id = message_id; + message->random_y = get_random_y(message_id); +} + MessagesManager::Message *MessagesManager::add_message_to_dialog(DialogId dialog_id, unique_ptr message, bool from_update, bool *need_update, bool *need_update_dialog_pos, const char *source) { @@ -23194,7 +25768,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(DialogId dialog CHECK(need_update_dialog_pos != nullptr); MessageId message_id = message->message_id; - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " from " << source; debug_add_message_to_dialog_fail_reason_ = "invalid message id"; return nullptr; @@ -23227,12 +25801,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq DialogId dialog_id = d->dialog_id; MessageId message_id = message->message_id; - if (d->deleted_message_ids.count(message_id)) { - LOG(INFO) << "Skip adding deleted " << message_id << " to " << dialog_id << " from " << source; - debug_add_message_to_dialog_fail_reason_ = "adding deleted message"; - return nullptr; - } - if (message_id.get() <= d->last_clear_history_message_id.get()) { + if (!message_id.is_scheduled() && message_id <= d->last_clear_history_message_id) { LOG(INFO) << "Skip adding cleared " << message_id << " to " << dialog_id << " from " << source; debug_add_message_to_dialog_fail_reason_ = "cleared full history"; return nullptr; @@ -23249,12 +25818,25 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq << ", have_next = " << message->have_next; if (!message_id.is_valid()) { + if (message_id.is_valid_scheduled()) { + return add_scheduled_message_to_dialog(d, std::move(message), from_update, need_update, source); + } LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " from " << source; CHECK(!message->from_database); debug_add_message_to_dialog_fail_reason_ = "invalid message id"; return nullptr; } + if (*need_update) { + CHECK(from_update); + } + + if (d->deleted_message_ids.count(message_id)) { + LOG(INFO) << "Skip adding deleted " << message_id << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "adding deleted message"; + return nullptr; + } + auto message_content_type = message->content->get_type(); if (is_debug_message_op_enabled()) { d->debug_message_op.emplace_back(Dialog::MessageOp::Add, message_id, message_content_type, from_update, @@ -23263,18 +25845,14 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq message->last_access_date = G()->unix_time_cached(); - if (*need_update) { - CHECK(from_update); - } if (from_update) { CHECK(message->have_next); CHECK(message->have_previous); - if (message_id.get() <= d->last_new_message_id.get() && dialog_id.get_type() != DialogType::Channel) { + if (message_id <= d->last_new_message_id && dialog_id.get_type() != DialogType::Channel) { if (!G()->parameters().use_message_db) { if (td_->auth_manager_->is_bot() && Time::now() > start_time_ + 300 && - MessageId(ServerMessageId(100)).get() <= message_id.get() && - message_id.get() <= MessageId(ServerMessageId(1000)).get() && - d->last_new_message_id.get() >= MessageId(ServerMessageId(2147483000)).get()) { + MessageId(ServerMessageId(100)) <= message_id && message_id <= MessageId(ServerMessageId(1000)) && + d->last_new_message_id >= MessageId(ServerMessageId(2147483000))) { LOG(FATAL) << "Force restart because of message_id overflow in " << dialog_id << " from " << d->last_new_message_id << " to " << message_id; } @@ -23302,7 +25880,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq max_message_id = d->last_database_message_id; } } - if (max_message_id != MessageId() && message_id.get() > max_message_id.get()) { + if (max_message_id != MessageId() && message_id > max_message_id) { if (!message->from_database) { if (message_id != d->pinned_message_id) { LOG(ERROR) << "Ignore " << message_id << " in " << dialog_id << " received not through update from " << source @@ -23323,7 +25901,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } if ((message_id.is_server() || (message_id.is_local() && dialog_id.get_type() == DialogType::SecretChat)) && - message_id.get() <= d->max_unavailable_message_id.get()) { + message_id <= d->max_unavailable_message_id) { LOG(INFO) << "Can't add an unavailable " << message_id << " to " << dialog_id << " from " << source; if (message->from_database) { delete_message_from_database(d, message_id, message.get(), true); @@ -23332,23 +25910,12 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq return nullptr; } - auto web_page_id = get_message_content_web_page_id(message->content.get()); - if (web_page_id.is_valid() && !td_->web_pages_manager_->have_web_page(web_page_id)) { - waiting_for_web_page_messages_.emplace(dialog_id, message_id); - send_closure(G()->web_pages_manager(), &WebPagesManager::wait_for_pending_web_page, dialog_id, message_id, - web_page_id); - } - - if (*need_update && message_id.get() <= d->last_new_message_id.get()) { - *need_update = false; - } - - bool auto_attach = message->have_previous && message->have_next && - (from_update || message_id.is_local() || message_id.is_yet_unsent()); if (message_content_type == MessageContentType::ChatDeleteHistory) { - auto m = delete_message(d, message_id, true, need_update_dialog_pos, "message chat delete history"); - if (m != nullptr) { - send_update_delete_messages(dialog_id, {m->message_id.get()}, true, false); + { + auto m = delete_message(d, message_id, true, need_update_dialog_pos, "message chat delete history"); + if (m != nullptr) { + send_update_delete_messages(dialog_id, {m->message_id.get()}, true, false); + } } int32 last_message_date = 0; if (d->last_message_id != MessageId()) { @@ -23369,6 +25936,17 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq return nullptr; } + auto web_page_id = get_message_content_web_page_id(message->content.get()); + if (web_page_id.is_valid() && !td_->web_pages_manager_->have_web_page(web_page_id)) { + waiting_for_web_page_messages_.emplace(dialog_id, message_id); + send_closure(G()->web_pages_manager(), &WebPagesManager::wait_for_pending_web_page, + FullMessageId{dialog_id, message_id}, web_page_id); + } + + if (*need_update && message_id <= d->last_new_message_id) { + *need_update = false; + } + if (message->reply_markup != nullptr && (message->reply_markup->type == ReplyMarkup::Type::RemoveKeyboard || (message->reply_markup->type == ReplyMarkup::Type::ForceReply && !message->reply_markup->is_personal)) && @@ -23386,21 +25964,13 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq message->reply_markup = nullptr; } - if (from_update) { - cancel_user_dialog_action(dialog_id, message.get()); - } + bool auto_attach = message->have_previous && message->have_next && + (from_update || message_id.is_local() || message_id.is_yet_unsent()); { - unique_ptr *v = find_message(&d->messages, message_id); - if (*v == nullptr && !message->from_database) { - // load message from database before updating it - if (G()->parameters().use_message_db && get_message_force(d, message_id, "add_message_to_dialog 2") != nullptr) { - v = find_message(&d->messages, message_id); - CHECK(*v != nullptr); - } - } - - if (*v != nullptr) { + Message *m = message->from_database ? get_message(d, message_id) + : get_message_force(d, message_id, "add_message_to_dialog 2"); + if (m != nullptr) { LOG(INFO) << "Adding already existing " << message_id << " in " << dialog_id << " from " << source; if (*need_update) { *need_update = false; @@ -23424,36 +25994,18 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (!message->from_database) { const int32 INDEX_MASK_MASK = ~search_messages_filter_index_mask(SearchMessagesFilter::UnreadMention); - auto old_index_mask = get_message_index_mask(dialog_id, v->get()) & INDEX_MASK_MASK; - bool was_deleted = delete_active_live_location(dialog_id, v->get()); - auto old_file_ids = get_message_content_file_ids((*v)->content.get(), td_); - bool need_send_update = update_message(d, *v, std::move(message), need_update_dialog_pos); + auto old_index_mask = get_message_index_mask(dialog_id, m) & INDEX_MASK_MASK; + bool was_deleted = delete_active_live_location(dialog_id, m); + auto old_file_ids = get_message_content_file_ids(m->content.get(), td_); + bool need_send_update = update_message(d, m, std::move(message), need_update_dialog_pos); if (!need_send_update) { LOG(INFO) << message_id << " in " << dialog_id << " is not changed"; } - const Message *m = v->get(); auto new_index_mask = get_message_index_mask(dialog_id, m) & INDEX_MASK_MASK; if (was_deleted) { try_add_active_live_location(dialog_id, m); } - auto new_file_ids = get_message_content_file_ids(m->content.get(), td_); - if (new_file_ids != old_file_ids) { - if (need_delete_message_files(d, m)) { - FullMessageId full_message_id{dialog_id, message_id}; - for (auto file_id : old_file_ids) { - if (std::find(new_file_ids.begin(), new_file_ids.end(), file_id) == new_file_ids.end() && - need_delete_file(full_message_id, file_id)) { - send_closure(G()->file_manager(), &FileManager::delete_file, file_id, Promise<>(), - "edit message in add_message_to_dialog"); - } - } - } - - auto file_source_id = get_message_file_source_id(FullMessageId(dialog_id, message_id)); - if (file_source_id.is_valid()) { - td_->file_manager_->change_files_source(file_source_id, old_file_ids, new_file_ids); - } - } + change_message_files(dialog_id, m, old_file_ids); if (need_send_update && m->notification_id.is_valid() && is_message_notification_active(d, m)) { auto &group_info = get_notification_group_info(d, m); if (group_info.group_id.is_valid()) { @@ -23474,7 +26026,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq update_message_count_by_index(d, -1, old_index_mask & ~new_index_mask); update_message_count_by_index(d, +1, new_index_mask & ~old_index_mask); } - return v->get(); + return m; } } @@ -23491,7 +26043,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } - if (message->contains_unread_mention && message_id.get() <= d->last_read_all_mentions_message_id.get()) { + if (message->contains_unread_mention && message_id <= d->last_read_all_mentions_message_id) { message->contains_unread_mention = false; if (message->from_database) { on_message_changed(d, message.get(), false, "add already read mention message to dialog"); @@ -23509,14 +26061,15 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq if (*need_update) { 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({dialog_id, pinned_message_id}, "preload pinned message")) { + if (pinned_message_id.is_valid() && have_message_force({dialog_id, pinned_message_id}, "preload pinned message")) { preloaded_pinned_message_id = pinned_message_id; LOG(INFO) << "Preloaded pinned " << pinned_message_id << " from database"; } add_error_reason = debug_add_message_to_dialog_fail_reason_; if (d->pinned_message_notification_message_id.is_valid() && - have_message({dialog_id, d->pinned_message_notification_message_id}, "preload previously pinned message")) { + 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"; } } @@ -23542,7 +26095,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq remove_new_secret_chat_notification(d, true); } - if (message->message_id.get() > d->max_added_message_id.get()) { + if (message->message_id > d->max_added_message_id) { d->max_added_message_id = message->message_id; } @@ -23600,13 +26153,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } - if (message_id.is_yet_unsent() && message->reply_to_message_id.is_valid() && - !message->reply_to_message_id.is_yet_unsent()) { - replied_by_yet_unsent_messages_[FullMessageId{dialog_id, message->reply_to_message_id}]++; - } - if (G()->parameters().use_file_db && message_id.is_yet_unsent() && !message->via_bot_user_id.is_valid() && - !message->hide_via_bot) { + !message->hide_via_bot && !message->is_copy) { auto queue_id = get_sequence_dispatcher_id(dialog_id, message_content_type); if (queue_id & 1) { LOG(INFO) << "Add " << message_id << " from " << source << " to queue " << queue_id; @@ -23618,8 +26166,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (!(d->have_full_history && auto_attach) && d->last_message_id.is_valid() && - d->last_message_id.get() < MessageId(ServerMessageId(1)).get() && - message_id.get() >= MessageId(ServerMessageId(1)).get()) { + d->last_message_id < MessageId(ServerMessageId(1)) && message_id >= MessageId(ServerMessageId(1))) { set_dialog_last_message_id(d, MessageId(), "add_message_to_dialog"); set_dialog_first_database_message_id(d, MessageId(), "add_message_to_dialog"); @@ -23637,7 +26184,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq *need_update_dialog_pos = false; } - if (from_update && message_id.get() > d->last_new_message_id.get() && !message_id.is_yet_unsent()) { + if (from_update && message_id > d->last_new_message_id && !message_id.is_yet_unsent()) { if (dialog_id.get_type() == DialogType::SecretChat || message_id.is_server()) { // can delete messages, therefore must be called before message attaching/adding set_dialog_last_new_message_id(d, message_id, "add_message_to_dialog"); @@ -23651,9 +26198,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq Message *previous_message = *it; if (previous_message != nullptr) { auto previous_message_id = previous_message->message_id; - CHECK(previous_message_id.get() < message_id.get()); - if (previous_message->have_next || - (d->last_message_id.is_valid() && previous_message_id.get() >= d->last_message_id.get())) { + CHECK(previous_message_id < message_id); + if (previous_message->have_next || (d->last_message_id.is_valid() && previous_message_id >= d->last_message_id)) { if (message_id.is_server() && previous_message_id.is_server() && previous_message->have_next) { ++it; auto next_message = *it; @@ -23682,7 +26228,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq Message *cur = d->messages.get(); Message *next_message = nullptr; while (cur != nullptr) { - if (cur->message_id.get() < message_id.get()) { + if (cur->message_id < message_id) { cur = cur->right.get(); } else { next_message = cur; @@ -23731,10 +26277,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } - UserId my_user_id(td_->contacts_manager_->get_my_id()); - DialogId my_dialog_id(my_user_id); - if (*need_update && message_id.get() > d->last_read_inbox_message_id.get() && !td_->auth_manager_->is_bot()) { - if (!message->is_outgoing && dialog_id != my_dialog_id) { + if (*need_update && message_id > d->last_read_inbox_message_id && !td_->auth_manager_->is_bot()) { + if (has_incoming_notification(dialog_id, message.get())) { int32 server_unread_count = d->server_unread_count; int32 local_unread_count = d->local_unread_count; if (message_id.is_server()) { @@ -23745,8 +26289,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq set_dialog_last_read_inbox_message_id(d, MessageId::min(), server_unread_count, local_unread_count, false, source); } else { - // if outgoing message has id one greater than last_read_inbox_message_id than definitely there is no - // unread incoming message before it + // if non-scheduled outgoing message has id one greater than last_read_inbox_message_id then definitely there are no + // unread incoming messages before it if (message_id.is_server() && d->last_read_inbox_message_id.is_valid() && d->last_read_inbox_message_id.is_server() && message_id.get_server_message_id().get() == d->last_read_inbox_message_id.get_server_message_id().get() + 1) { @@ -23763,17 +26307,17 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq if (*need_update) { update_message_count_by_index(d, +1, message.get()); } - if (auto_attach && message_id.get() > d->last_message_id.get() && message_id.get() >= d->last_new_message_id.get()) { + if (auto_attach && message_id > d->last_message_id && message_id >= d->last_new_message_id) { set_dialog_last_message_id(d, message_id, "add_message_to_dialog"); *need_update_dialog_pos = true; } - if (auto_attach && !message_id.is_yet_unsent() && message_id.get() >= d->last_new_message_id.get() && + if (auto_attach && !message_id.is_yet_unsent() && message_id >= d->last_new_message_id && (d->last_new_message_id.is_valid() || (message_id.is_local() && d->last_message_id.is_valid() && - (message_id.get() >= d->last_message_id.get() || - (d->last_database_message_id.is_valid() && message_id.get() > d->last_database_message_id.get()))))) { - CHECK(message_id.get() <= d->last_message_id.get()); - if (message_id.get() > d->last_database_message_id.get()) { + (message_id >= d->last_message_id || + (d->last_database_message_id.is_valid() && message_id > d->last_database_message_id))))) { + CHECK(message_id <= d->last_message_id); + if (message_id > d->last_database_message_id) { set_dialog_last_database_message_id(d, message_id, "add_message_to_dialog"); if (!d->first_database_message_id.is_valid()) { set_dialog_first_database_message_id(d, message_id, "add_message_to_dialog"); @@ -23781,19 +26325,37 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } } - if (*need_update && dialog_id.get_type() == DialogType::Channel && - message->date < G()->unix_time_cached() - 2 * 86400 && Slice(source) == Slice("updateNewChannelMessage")) { - // if the message is pretty old, we might have missed the update that the message has already been read - repair_channel_server_unread_count(d); - } const Message *m = message.get(); - if (!m->from_database && !message_id.is_yet_unsent()) { + if (m->message_id.is_yet_unsent() && m->reply_to_message_id.is_valid() && !m->reply_to_message_id.is_yet_unsent()) { + replied_by_yet_unsent_messages_[FullMessageId{dialog_id, m->reply_to_message_id}]++; + } + + if (!m->from_database && !m->message_id.is_yet_unsent()) { add_message_to_database(d, m, "add_message_to_dialog"); } + if (from_update && dialog_id.get_type() == DialogType::Channel) { + auto now = max(G()->unix_time_cached(), m->date); + if (m->date < now - 2 * 86400 && Slice(source) == Slice("updateNewChannelMessage")) { + // if the message is pretty old, we might have missed the update that the message has already been read + repair_channel_server_unread_count(d); + } + if (m->date + 3600 >= now && m->is_outgoing) { + auto channel_id = dialog_id.get_channel_id(); + auto slow_mode_delay = td_->contacts_manager_->get_channel_slow_mode_delay(channel_id); + auto status = td_->contacts_manager_->get_channel_status(dialog_id.get_channel_id()); + if (m->date + slow_mode_delay > now && !status.is_administrator()) { + td_->contacts_manager_->on_update_channel_slow_mode_next_send_date(channel_id, m->date + slow_mode_delay); + } + } + if (m->date > now - 14 * 86400) { + td_->contacts_manager_->remove_inactive_channel(dialog_id.get_channel_id()); + } + } + if (!is_attached && !m->have_next && !m->have_previous) { - MessagesIterator it(d, message_id); + MessagesIterator it(d, m->message_id); if (*it != nullptr && (*it)->have_next) { // need to drop a connection between messages auto previous_message = *it; @@ -23802,7 +26364,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq if (next_message != nullptr) { if (next_message->message_id.is_server() && !(td_->auth_manager_->is_bot() && Slice(source) == Slice("GetChannelMessagesQuery"))) { - LOG(ERROR) << "Can't attach " << message_id << " from " << source << " before " << next_message->message_id + LOG(ERROR) << "Can't attach " << m->message_id << " from " << source << " before " << next_message->message_id << " and after " << previous_message->message_id << " in " << dialog_id; dump_debug_message_op(d); } @@ -23822,122 +26384,55 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq on_dialog_updated(dialog_id, "update_has_contact_registered_message"); } - if (message_id.is_server() && dialog_id.get_type() != DialogType::SecretChat && - (need_reget_message_content(m->content.get()) || (m->legacy_layer != 0 && m->legacy_layer < MTPROTO_LAYER))) { - FullMessageId full_message_id{dialog_id, message_id}; - LOG(INFO) << "Reget from server " << full_message_id; - get_message_from_server(full_message_id, Auto()); - } + reget_message_from_server_if_needed(dialog_id, m); add_message_file_sources(dialog_id, m); - register_message_content(td_, m->content.get(), {dialog_id, message_id}); - if (from_update && message_id.is_server() && dialog_id.get_type() == DialogType::Channel && - m->sender_user_id.is_valid()) { - switch (message_content_type) { - case MessageContentType::ChatAddUsers: - td_->contacts_manager_->speculative_add_channel_participants( - dialog_id.get_channel_id(), get_message_content_added_user_ids(m->content.get()), m->sender_user_id, - m->date, m->sender_user_id == my_user_id); - break; - case MessageContentType::ChatJoinedByLink: - td_->contacts_manager_->speculative_add_channel_participants(dialog_id.get_channel_id(), {m->sender_user_id}, - m->sender_user_id, m->date, - m->sender_user_id == my_user_id); - break; - case MessageContentType::ChatDeleteUser: - td_->contacts_manager_->speculative_delete_channel_participant( - dialog_id.get_channel_id(), get_message_content_deleted_user_id(m->content.get()), - m->sender_user_id == my_user_id); - break; - default: - break; - } - } - if (from_update && message_id.is_server() && message_content_type == MessageContentType::PinMessage) { + register_message_content(td_, m->content.get(), {dialog_id, m->message_id}); + + 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 auto pinned_message_id = get_message_content_pinned_message_id(m->content.get()); on_update_dialog_pinned_message_id(dialog_id, pinned_message_id); } - if (!td_->auth_manager_->is_bot() && from_update && m->forward_info == nullptr && !m->had_forward_info && - (m->is_outgoing || dialog_id == my_dialog_id)) { - if (dialog_id.get_type() != DialogType::SecretChat && !message_id.is_local()) { - on_sent_message_content(td_, m->content.get()); - } + + if (from_update) { + speculatively_update_channel_participants(dialog_id, m); + update_sent_message_contents(dialog_id, m); update_used_hashtags(dialog_id, m); - } - if (!td_->auth_manager_->is_bot() && from_update && message_id.is_server() && - (m->is_outgoing || dialog_id == my_dialog_id) && dialog_id.get_type() != DialogType::SecretChat) { - if (m->via_bot_user_id.is_valid() && m->forward_info == nullptr && !m->had_forward_info) { - // forwarded game messages can't be distinguished from sent via bot game messages, so increase rating anyway - send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, TopDialogCategory::BotInline, - DialogId(m->via_bot_user_id), m->date); - } - - TopDialogCategory category = TopDialogCategory::Size; - switch (dialog_id.get_type()) { - case DialogType::User: { - if (td_->contacts_manager_->is_user_bot(dialog_id.get_user_id())) { - category = TopDialogCategory::BotPM; - } else { - category = TopDialogCategory::Correspondent; - } - break; - } - case DialogType::Chat: - category = TopDialogCategory::Group; - break; - case DialogType::Channel: - switch (td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id())) { - case ChannelType::Broadcast: - category = TopDialogCategory::Channel; - break; - case ChannelType::Megagroup: - category = TopDialogCategory::Group; - break; - case ChannelType::Unknown: - break; - default: - UNREACHABLE(); - break; - } - break; - case DialogType::SecretChat: - case DialogType::None: - default: - UNREACHABLE(); - } - if (category != TopDialogCategory::Size) { - send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, category, dialog_id, m->date); - } + update_top_dialogs(dialog_id, m); } - Message *result_message = insert_message(&d->messages, std::move(message)); + Message *result_message = treap_insert_message(&d->messages, std::move(message)); CHECK(result_message != nullptr); + CHECK(result_message == m); CHECK(d->messages != nullptr); if (!is_attached) { if (m->have_next) { - LOG_CHECK(!m->have_previous) << auto_attach << " " << dialog_id << " " << message_id << " " << from_update << " " - << *need_update << " " << d->being_updated_last_new_message_id << " " + LOG_CHECK(!m->have_previous) << auto_attach << " " << dialog_id << " " << m->message_id << " " << from_update + << " " << *need_update << " " << d->being_updated_last_new_message_id << " " << d->last_new_message_id << " " << d->being_updated_last_database_message_id << " " << d->last_database_message_id << " " << debug_have_previous << " " << debug_have_next << " " << source; - attach_message_to_next(d, message_id, source); + attach_message_to_next(d, m->message_id, source); } else if (m->have_previous) { - attach_message_to_previous(d, message_id, source); + attach_message_to_previous(d, m->message_id, source); } } switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: - message_id_to_dialog_id_[message_id] = dialog_id; + if (m->message_id.is_server()) { + message_id_to_dialog_id_[m->message_id] = dialog_id; + } break; case DialogType::Channel: // nothing to do break; case DialogType::SecretChat: - add_random_id_to_message_id_correspondence(d, m->random_id, message_id); + add_random_id_to_message_id_correspondence(d, m->random_id, m->message_id); break; case DialogType::None: default: @@ -23945,7 +26440,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (m->notification_id.is_valid()) { - add_notification_id_to_message_id_correspondence(d, m->notification_id, message_id); + add_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id); } d->being_added_message_id = MessageId(); @@ -23965,6 +26460,126 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq return result_message; } +MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialog *d, unique_ptr message, + bool from_update, bool *need_update, + const char *source) { + CHECK(message != nullptr); + CHECK(d != nullptr); + CHECK(need_update != nullptr); + CHECK(source != nullptr); + + DialogId dialog_id = d->dialog_id; + MessageId message_id = message->message_id; + CHECK(message_id.is_valid_scheduled()); + CHECK(!message->notification_id.is_valid()); + CHECK(!message->removed_notification_id.is_valid()); + + if (d->deleted_message_ids.count(message_id)) { + LOG(INFO) << "Skip adding deleted " << message_id << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "adding deleted scheduled message"; + return nullptr; + } + + if (message_id.is_scheduled_server() && + d->deleted_scheduled_server_message_ids.count(message_id.get_scheduled_server_message_id())) { + LOG(INFO) << "Skip adding deleted " << message_id << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "adding deleted scheduled server message"; + return nullptr; + } + + if (dialog_id.get_type() == DialogType::SecretChat) { + LOG(ERROR) << "Tried to add " << message_id << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "skip adding scheduled message to secret chat"; + return nullptr; + } + if (message->ttl != 0 || message->ttl_expires_at != 0) { + LOG(ERROR) << "Tried to add " << message_id << " with ttl " << message->ttl << "/" << message->ttl_expires_at + << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "skip adding secret scheduled message"; + return nullptr; + } + if (td_->auth_manager_->is_bot()) { + LOG(ERROR) << "Bot tried to add " << message_id << " to " << dialog_id << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "skip adding scheduled message by bot"; + return nullptr; + } + + auto message_content_type = message->content->get_type(); + if (is_service_message_content(message_content_type) || message_content_type == MessageContentType::LiveLocation || + message_content_type == MessageContentType::ExpiredPhoto || + message_content_type == MessageContentType::ExpiredVideo) { + LOG(ERROR) << "Tried to add " << message_id << " of type " << message_content_type << " to " << dialog_id + << " from " << source; + debug_add_message_to_dialog_fail_reason_ = "skip adding message of unexpected type"; + return nullptr; + } + + auto web_page_id = get_message_content_web_page_id(message->content.get()); + if (web_page_id.is_valid() && !td_->web_pages_manager_->have_web_page(web_page_id)) { + waiting_for_web_page_messages_.emplace(dialog_id, message_id); + send_closure(G()->web_pages_manager(), &WebPagesManager::wait_for_pending_web_page, + FullMessageId{dialog_id, message_id}, web_page_id); + } + + { + Message *m = message->from_database ? get_message(d, message_id) + : get_message_force(d, message_id, "add_scheduled_message_to_dialog"); + if (m != nullptr) { + auto old_message_id = m->message_id; + LOG(INFO) << "Adding already existing " << old_message_id << " in " << dialog_id << " from " << source; + set_message_id(message, old_message_id); + if (!message->from_database) { + auto old_file_ids = get_message_content_file_ids(m->content.get(), td_); + bool need_update_dialog_pos = false; + update_message(d, m, std::move(message), &need_update_dialog_pos); + CHECK(need_update_dialog_pos == false); + change_message_files(dialog_id, m, old_file_ids); + } + if (old_message_id != 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); + set_message_id(message, message_id); + message->from_database = false; + } else { + *need_update = false; + return m; + } + } + } + + LOG(INFO) << "Adding not found " << message_id << " to " << dialog_id << " from " << source; + + const Message *m = message.get(); + if (m->message_id.is_yet_unsent() && m->reply_to_message_id.is_valid() && !m->reply_to_message_id.is_yet_unsent()) { + replied_by_yet_unsent_messages_[FullMessageId{dialog_id, m->reply_to_message_id}]++; + } + + if (!m->from_database && !m->message_id.is_yet_unsent()) { + add_message_to_database(d, m, "add_scheduled_message_to_dialog"); + } + + reget_message_from_server_if_needed(dialog_id, m); + + add_message_file_sources(dialog_id, m); + + if (from_update) { + update_sent_message_contents(dialog_id, m); + update_used_hashtags(dialog_id, m); + } + + if (m->message_id.is_scheduled_server()) { + int32 &date = d->scheduled_message_date[m->message_id.get_scheduled_server_message_id()]; + CHECK(date == 0); + date = m->date; + } + + Message *result_message = treap_insert_message(&d->scheduled_messages, std::move(message)); + CHECK(result_message != nullptr); + CHECK(d->scheduled_messages != nullptr); + return result_message; +} + void MessagesManager::on_message_changed(const Dialog *d, const Message *m, bool need_send_update, const char *source) { CHECK(d != nullptr); CHECK(m != nullptr); @@ -23989,10 +26604,17 @@ void MessagesManager::add_message_to_database(const Dialog *d, const Message *m, CHECK(d != nullptr); CHECK(m != nullptr); MessageId message_id = m->message_id; - LOG_CHECK(message_id.is_server() || message_id.is_local()) << source; LOG(INFO) << "Add " << FullMessageId(d->dialog_id, message_id) << " to database from " << source; + if (message_id.is_scheduled()) { + set_dialog_has_scheduled_database_messages(d->dialog_id, true); + G()->td_db()->get_messages_db_async()->add_scheduled_message({d->dialog_id, message_id}, log_event_store(*m), + Auto()); // TODO Promise + return; + } + LOG_CHECK(message_id.is_server() || message_id.is_local()) << source; + ServerMessageId unique_message_id; int64 random_id = 0; int64 search_id = 0; @@ -24003,11 +26625,11 @@ void MessagesManager::add_message_to_database(const Dialog *d, const Message *m, if (message_id.is_server()) { unique_message_id = message_id.get_server_message_id(); } - //FOR DEBUG - //text = get_search_text(m); - //if (!text.empty()) { - //search_id = (static_cast(m->date) << 32) | static_cast(Random::secure_int32()); - //} + // FOR DEBUG + // text = get_search_text(m); + // if (!text.empty()) { + // search_id = (static_cast(m->date) << 32) | static_cast(Random::secure_int32()); + // } break; case DialogType::Channel: break; @@ -24036,11 +26658,12 @@ void MessagesManager::add_message_to_database(const Dialog *d, const Message *m, void MessagesManager::delete_all_dialog_messages_from_database(Dialog *d, MessageId max_message_id, const char *source) { CHECK(d != nullptr); + CHECK(max_message_id.is_valid()); if (d->new_secret_chat_notification_id.is_valid()) { remove_new_secret_chat_notification(d, true); } if (d->pinned_message_notification_message_id.is_valid() && - d->pinned_message_notification_message_id.get() <= max_message_id.get()) { + d->pinned_message_notification_message_id <= max_message_id) { remove_dialog_pinned_message_notification(d); } remove_message_dialog_notifications(d, max_message_id, false, source); @@ -24051,10 +26674,6 @@ void MessagesManager::delete_all_dialog_messages_from_database(Dialog *d, Messag } auto dialog_id = d->dialog_id; - if (!max_message_id.is_valid()) { - return; - } - LOG(INFO) << "Delete all messages in " << dialog_id << " from database up to " << max_message_id << " from " << source; /* @@ -24062,7 +26681,7 @@ void MessagesManager::delete_all_dialog_messages_from_database(Dialog *d, Messag bool need_save = false; int i = 0; for (auto &first_message_id : calls_db_state_.first_calls_database_message_id_by_index) { - if (first_message_id.get() <= max_message_id.get()) { + if (first_message_id <= max_message_id) { first_message_id = max_message_id.get_next_server_message_id(); calls_db_state_.message_count_by_index[i] = -1; need_save = true; @@ -24130,21 +26749,20 @@ bool MessagesManager::need_delete_file(FullMessageId full_message_id, FileId fil return true; } -bool MessagesManager::need_delete_message_files(Dialog *d, const Message *m) const { +bool MessagesManager::need_delete_message_files(DialogId dialog_id, const Message *m) const { if (m == nullptr) { return false; } - CHECK(d != nullptr); - auto dialog_type = d->dialog_id.get_type(); - if (!m->message_id.is_server() && dialog_type != DialogType::SecretChat) { + auto dialog_type = dialog_id.get_type(); + if (!m->message_id.is_scheduled() && !m->message_id.is_server() && dialog_type != DialogType::SecretChat) { return false; } if (m->forward_info != nullptr && m->forward_info->from_dialog_id.is_valid() && m->forward_info->from_message_id.is_valid()) { // this function must not try to load the message, because it can be called from - // do_delete_message or add_message_to_dialog + // do_delete_message or add_scheduled_message_to_dialog const Message *old_m = get_message({m->forward_info->from_dialog_id, m->forward_info->from_message_id}); if (old_m != nullptr && get_message_file_ids(old_m) == get_message_file_ids(m)) { return false; @@ -24157,12 +26775,16 @@ bool MessagesManager::need_delete_message_files(Dialog *d, const Message *m) con void MessagesManager::delete_message_from_database(Dialog *d, MessageId message_id, const Message *m, bool is_permanently_deleted) { CHECK(d != nullptr); - if (!message_id.is_valid()) { + if (!message_id.is_valid() && !message_id.is_valid_scheduled()) { return; } if (is_permanently_deleted) { - d->deleted_message_ids.insert(message_id); + if (message_id.is_scheduled() && message_id.is_scheduled_server()) { + d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id()); + } else { + d->deleted_message_ids.insert(message_id); + } } if (message_id.is_yet_unsent()) { @@ -24170,6 +26792,7 @@ void MessagesManager::delete_message_from_database(Dialog *d, MessageId message_ } if (m != nullptr && m->notification_id.is_valid()) { + CHECK(!message_id.is_scheduled()); auto from_mentions = is_from_mention_notification_group(d, m); auto &group_info = from_mentions ? d->mention_notification_group : d->message_notification_group; @@ -24183,14 +26806,14 @@ void MessagesManager::delete_message_from_database(Dialog *d, MessageId message_ m->notification_id, true, false, Promise(), "delete_message_from_database"); } } - } else if (message_id.get() > d->last_new_message_id.get()) { + } else if (!message_id.is_scheduled() && message_id > d->last_new_message_id) { send_closure_later(G()->notification_manager(), &NotificationManager::remove_temporary_notification_by_message_id, d->message_notification_group.group_id, message_id, false, "delete_message_from_database"); send_closure_later(G()->notification_manager(), &NotificationManager::remove_temporary_notification_by_message_id, d->mention_notification_group.group_id, message_id, false, "delete_message_from_database"); } - auto need_delete_files = need_delete_message_files(d, m); + auto need_delete_files = need_delete_message_files(d->dialog_id, m); if (need_delete_files) { delete_message_files(d->dialog_id, m); } @@ -24247,6 +26870,7 @@ void MessagesManager::do_delete_message_logevent(const DeleteMessageLogEvent &lo void MessagesManager::attach_message_to_previous(Dialog *d, MessageId message_id, const char *source) { CHECK(d != nullptr); + CHECK(message_id.is_valid()); MessagesIterator it(d, message_id); Message *m = *it; CHECK(m != nullptr); @@ -24264,6 +26888,7 @@ void MessagesManager::attach_message_to_previous(Dialog *d, MessageId message_id void MessagesManager::attach_message_to_next(Dialog *d, MessageId message_id, const char *source) { CHECK(d != nullptr); + CHECK(message_id.is_valid()); MessagesIterator it(d, message_id); Message *m = *it; CHECK(m != nullptr); @@ -24279,7 +26904,7 @@ void MessagesManager::attach_message_to_next(Dialog *d, MessageId message_id, co } } -bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message, unique_ptr new_message, +bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr new_message, bool *need_update_dialog_pos) { CHECK(d != nullptr); CHECK(old_message != nullptr); @@ -24290,18 +26915,21 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message DialogId dialog_id = d->dialog_id; MessageId message_id = old_message->message_id; + bool is_scheduled = message_id.is_scheduled(); bool need_send_update = false; bool is_new_available = new_message->content->get_type() != MessageContentType::ChatDeleteHistory; + bool replace_legacy = (old_message->legacy_layer != 0 && + (new_message->legacy_layer == 0 || old_message->legacy_layer < new_message->legacy_layer)); if (old_message->date != new_message->date) { if (new_message->date > 0) { - LOG_IF(ERROR, !new_message->is_outgoing && dialog_id != get_my_dialog_id()) + LOG_IF(ERROR, !is_scheduled && !new_message->is_outgoing && dialog_id != get_my_dialog_id()) << "Date has changed for incoming " << message_id << " in " << dialog_id << " from " << old_message->date << " to " << new_message->date << ", message content type is " << old_message->content->get_type() << '/' << new_message->content->get_type(); CHECK(old_message->date > 0); LOG(DEBUG) << "Message date has changed from " << old_message->date << " to " << new_message->date; old_message->date = new_message->date; - if (d->last_message_id == message_id) { + if (!is_scheduled && d->last_message_id == message_id) { *need_update_dialog_pos = true; } need_send_update = true; @@ -24311,14 +26939,18 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message << new_message->content->get_type(); } } + if (old_message->date == old_message->edited_schedule_date) { + old_message->edited_schedule_date = 0; + } bool is_edited = false; + int32 old_shown_edit_date = old_message->hide_edit_date ? 0 : old_message->edit_date; if (old_message->edit_date != new_message->edit_date) { if (new_message->edit_date > 0) { if (new_message->edit_date > old_message->edit_date) { LOG(DEBUG) << "Message edit date has changed from " << old_message->edit_date << " to " << new_message->edit_date; + old_message->edit_date = new_message->edit_date; - is_edited = true; need_send_update = true; } } else { @@ -24351,10 +26983,12 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message } if (old_message->forward_info == nullptr) { if (new_message->forward_info != nullptr) { - LOG(ERROR) << message_id << " in " << dialog_id << " has received forward info " << *new_message->forward_info - << ", really forwarded from " << old_message->real_forward_from_dialog_id - << ", message content type is " << old_message->content->get_type() << '/' - << new_message->content->get_type(); + if (!replace_legacy) { + LOG(ERROR) << message_id << " in " << dialog_id << " has received forward info " << *new_message->forward_info + << ", really forwarded from " << old_message->real_forward_from_dialog_id + << ", message content type is " << old_message->content->get_type() << '/' + << new_message->content->get_type(); + } old_message->forward_info = std::move(new_message->forward_info); need_send_update = true; } @@ -24366,7 +27000,7 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message need_send_update = true; } if (*old_message->forward_info != *new_message->forward_info) { - if (!is_forward_info_sender_hidden(new_message->forward_info.get())) { + if (!is_forward_info_sender_hidden(new_message->forward_info.get()) && !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed forward info from " << *old_message->forward_info << " to " << *new_message->forward_info << ", really forwarded from " << old_message->real_forward_from_dialog_id << ", message content type is " @@ -24389,6 +27023,7 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message need_send_update = true; } if (old_message->notification_id != new_message->notification_id) { + CHECK(!is_scheduled); if (old_message->notification_id.is_valid()) { if (new_message->notification_id.is_valid()) { LOG(ERROR) << "Notification id for " << message_id << " in " << dialog_id << " has tried to change from " @@ -24409,7 +27044,7 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message if (old_message->reply_to_message_id != new_message->reply_to_message_id) { // Can't check "&& get_message_force(d, old_message->reply_to_message_id, "update_message") == nullptr", because it // can change message tree and invalidate reference to old_message - if (new_message->reply_to_message_id == MessageId()) { + if (new_message->reply_to_message_id == MessageId() || replace_legacy) { LOG(DEBUG) << "Drop message reply_to_message_id"; old_message->reply_to_message_id = MessageId(); need_send_update = true; @@ -24422,7 +27057,8 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message } } if (old_message->via_bot_user_id != new_message->via_bot_user_id) { - if ((!message_id.is_yet_unsent() || old_message->via_bot_user_id.is_valid()) && is_new_available) { + if ((!message_id.is_yet_unsent() || old_message->via_bot_user_id.is_valid()) && is_new_available && + !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed bot via it is sent from " << old_message->via_bot_user_id << " to " << new_message->via_bot_user_id << ", message content type is " << old_message->content->get_type() << '/' @@ -24439,9 +27075,11 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message } } if (old_message->is_outgoing != new_message->is_outgoing && is_new_available) { - LOG(ERROR) << message_id << " in " << dialog_id << " has changed is_outgoing from " << old_message->is_outgoing - << " to " << new_message->is_outgoing << ", message content type is " << old_message->content->get_type() - << '/' << new_message->content->get_type(); + if (!replace_legacy && !(message_id.is_scheduled() && dialog_id == get_my_dialog_id())) { + LOG(ERROR) << message_id << " in " << dialog_id << " has changed is_outgoing from " << old_message->is_outgoing + << " to " << new_message->is_outgoing << ", message content type is " + << old_message->content->get_type() << '/' << new_message->content->get_type(); + } old_message->is_outgoing = new_message->is_outgoing; need_send_update = true; } @@ -24452,7 +27090,8 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message if (old_message->contains_mention != new_message->contains_mention) { auto old_type = old_message->content->get_type(); if (old_message->edit_date == 0 && is_new_available && old_type != MessageContentType::PinMessage && - old_type != MessageContentType::ExpiredPhoto && old_type != MessageContentType::ExpiredVideo) { + old_type != MessageContentType::ExpiredPhoto && old_type != MessageContentType::ExpiredVideo && + !replace_legacy) { LOG(ERROR) << message_id << " in " << dialog_id << " has changed contains_mention from " << old_message->contains_mention << " to " << new_message->contains_mention << ", is_outgoing = " << old_message->is_outgoing << ", message content type is " << old_type << '/' @@ -24464,23 +27103,26 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message // need_send_update = true; } if (old_message->disable_notification != new_message->disable_notification) { - LOG_IF(ERROR, old_message->edit_date == 0 && is_new_available) + LOG_IF(ERROR, old_message->edit_date == 0 && is_new_available && !replace_legacy) << "Disable_notification has changed from " << old_message->disable_notification << " to " << new_message->disable_notification - << ". Old message: " << to_string(get_message_object(dialog_id, old_message.get())) + << ". Old message: " << to_string(get_message_object(dialog_id, old_message)) << ". New message: " << to_string(get_message_object(dialog_id, new_message.get())); // disable_notification flag shouldn't be changed, because we are unable to show/hide message notification // old_message->disable_notification = new_message->disable_notification; // need_send_update = true; } - if (update_message_contains_unread_mention(d, old_message.get(), new_message->contains_unread_mention, - "update_message")) { + if (!is_scheduled && + update_message_contains_unread_mention(d, old_message, new_message->contains_unread_mention, "update_message")) { need_send_update = true; } - if (update_message_views(dialog_id, old_message.get(), new_message->views)) { + if (update_message_views(dialog_id, old_message, new_message->views)) { need_send_update = true; } + if (old_message->restriction_reasons != new_message->restriction_reasons) { + old_message->restriction_reasons = std::move(new_message->restriction_reasons); + } if (old_message->legacy_layer != new_message->legacy_layer) { old_message->legacy_layer = new_message->legacy_layer; } @@ -24489,6 +27131,20 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message LOG(DEBUG) << "Update message media_album_id"; need_send_update = true; } + if (old_message->hide_edit_date != new_message->hide_edit_date) { + old_message->hide_edit_date = new_message->hide_edit_date; + if (old_message->edit_date > 0) { + need_send_update = true; + } + } + int32 new_shown_edit_date = old_message->hide_edit_date ? 0 : old_message->edit_date; + if (new_shown_edit_date != old_shown_edit_date) { + is_edited = true; + } + + if (old_message->is_from_scheduled != new_message->is_from_scheduled) { + old_message->is_from_scheduled = new_message->is_from_scheduled; + } if (old_message->edit_date > 0) { // inline keyboard can be edited @@ -24527,8 +27183,9 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message } } else { if (new_message->reply_markup != nullptr) { - if (message_id.is_yet_unsent() && old_message->reply_markup->type == ReplyMarkup::Type::InlineKeyboard && - new_message->reply_markup->type == ReplyMarkup::Type::InlineKeyboard) { + if (replace_legacy || + (message_id.is_yet_unsent() && old_message->reply_markup->type == ReplyMarkup::Type::InlineKeyboard && + new_message->reply_markup->type == ReplyMarkup::Type::InlineKeyboard)) { // allow the server to update inline keyboard for sent messages // this is needed to get correct button_id for UrlAuth buttons old_message->had_reply_markup = false; @@ -24542,7 +27199,7 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message } 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.get())) + << ". Old message: " << to_string(get_message_object(dialog_id, old_message)) << ". New message: " << to_string(get_message_object(dialog_id, new_message.get())); } } @@ -24552,26 +27209,28 @@ bool MessagesManager::update_message(Dialog *d, unique_ptr &old_message old_message->last_access_date = new_message->last_access_date; } - CHECK(!new_message->have_previous || !new_message->have_next); - if (new_message->have_previous && !old_message->have_previous) { - old_message->have_previous = true; - attach_message_to_previous(d, message_id, "update_message"); - } else if (new_message->have_next && !old_message->have_next) { - old_message->have_next = true; - attach_message_to_next(d, message_id, "update_message"); + if (!is_scheduled) { + CHECK(!new_message->have_previous || !new_message->have_next); + if (new_message->have_previous && !old_message->have_previous) { + old_message->have_previous = true; + attach_message_to_previous(d, message_id, "update_message"); + } else if (new_message->have_next && !old_message->have_next) { + old_message->have_next = true; + attach_message_to_next(d, message_id, "update_message"); + } } - if (update_message_content(dialog_id, old_message.get(), std::move(new_message->content), true, + if (update_message_content(dialog_id, old_message, std::move(new_message->content), true, message_id.is_yet_unsent() && new_message->edit_date == 0, - get_message(d, message_id) != nullptr)) { + !is_scheduled && get_message(d, message_id) != nullptr)) { need_send_update = true; } - // TODO update can be send only if the message has already been returned to the user + if (is_edited && !td_->auth_manager_->is_bot()) { - send_update_message_edited(dialog_id, old_message.get()); + send_update_message_edited(dialog_id, old_message); } - on_message_changed(d, old_message.get(), need_send_update, "update_message"); + on_message_changed(d, old_message, need_send_update, "update_message"); return need_send_update; } @@ -24592,6 +27251,10 @@ 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; @@ -24686,7 +27349,7 @@ MessagesManager::Dialog *MessagesManager::get_dialog_by_message_id(MessageId mes G()->td_db()->get_messages_db_sync()->get_message_by_unique_message_id(message_id.get_server_message_id()); if (r_value.is_ok()) { DialogId dialog_id(r_value.ok().first); - Message *m = on_get_message_from_database(dialog_id, get_dialog_force(dialog_id), r_value.ok().second, + Message *m = on_get_message_from_database(dialog_id, get_dialog_force(dialog_id), r_value.ok().second, false, "get_dialog_by_message_id"); if (m != nullptr) { CHECK(m->message_id == message_id); @@ -24717,7 +27380,7 @@ MessageId MessagesManager::get_message_id_by_random_id(Dialog *d, int64 random_i auto r_value = G()->td_db()->get_messages_db_sync()->get_message_by_random_id(d->dialog_id, random_id); if (r_value.is_ok()) { debug_add_message_to_dialog_fail_reason_ = "not called"; - Message *m = on_get_message_from_database(d->dialog_id, d, r_value.ok(), "get_message_id_by_random_id"); + Message *m = on_get_message_from_database(d->dialog_id, d, r_value.ok(), false, "get_message_id_by_random_id"); if (m != nullptr) { LOG_CHECK(m->random_id == random_id) << random_id << " " << m->random_id << " " << d->random_id_to_message_id[random_id] << " " @@ -24743,8 +27406,9 @@ MessageId MessagesManager::get_message_id_by_random_id(Dialog *d, int64 random_i return it->second; } -void MessagesManager::force_create_dialog(DialogId dialog_id, const char *source, bool force_update_dialog_pos) { - CHECK(dialog_id.is_valid()); +void MessagesManager::force_create_dialog(DialogId dialog_id, const char *source, bool expect_no_access, + bool force_update_dialog_pos) { + LOG_CHECK(dialog_id.is_valid()) << source; Dialog *d = get_dialog_force(dialog_id); if (d == nullptr) { LOG(INFO) << "Force create " << dialog_id << " from " << source; @@ -24803,12 +27467,8 @@ void MessagesManager::force_create_dialog(DialogId dialog_id, const char *source if (!have_input_peer(dialog_id, AccessRights::Read)) { if (!have_dialog_info(dialog_id)) { LOG(ERROR) << "Have no info about " << dialog_id << " received from " << source << ", but forced to create it"; - } else { - LOG_IF(ERROR, - Slice(source) != Slice("message forward info") && Slice(source) != Slice("message forward from info") && - Slice(source) != Slice("on_new_callback_query") && Slice(source) != Slice("search public dialog") && - Slice(source) != Slice("create new secret chat") && !force_update_dialog_pos) - << "Have no access to " << dialog_id << " received from " << source << ", but forced to create it"; + } else if (!expect_no_access) { + LOG(ERROR) << "Have no access to " << dialog_id << " received from " << source << ", but forced to create it"; } } } else if (force_update_dialog_pos) { @@ -24840,11 +27500,10 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, auto dialog_id = d->dialog_id; switch (dialog_id.get_type()) { case DialogType::User: - if (dialog_id == get_my_dialog_id()) { - d->last_read_inbox_message_id = MessageId::max(); - d->is_last_read_inbox_message_id_inited = true; - d->last_read_outbox_message_id = MessageId::max(); - d->is_last_read_outbox_message_id_inited = true; + if (dialog_id == get_my_dialog_id() && d->last_read_inbox_message_id == MessageId::max() && + d->last_read_outbox_message_id == MessageId::max()) { + d->last_read_inbox_message_id = d->last_new_message_id; + d->last_read_outbox_message_id = d->last_new_message_id; } break; case DialogType::Chat: @@ -24867,8 +27526,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, } case DialogType::SecretChat: if (!d->last_new_message_id.is_valid()) { - LOG(INFO) << "Set " << d->dialog_id << " last new message id in add_new_dialog"; - // TODO use date << MessageId::SERVER_ID_SHIFT; + LOG(INFO) << "Set " << d->dialog_id << " last new message in add_new_dialog"; d->last_new_message_id = MessageId::min(); } for (auto &first_message_id : d->first_database_message_id_by_index) { @@ -24892,12 +27550,8 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, d->need_restore_reply_markup = false; d->is_last_read_inbox_message_id_inited = true; d->is_last_read_outbox_message_id_inited = true; - d->know_can_report_spam = true; d->is_pinned_message_id_inited = true; - if (!is_loaded_from_database) { - d->can_report_spam = - td_->contacts_manager_->default_can_report_spam_in_secret_chat(dialog_id.get_secret_chat_id()); - } + d->is_folder_id_inited = true; break; case DialogType::None: default: @@ -24934,6 +27588,9 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr &&d, loaded_dialogs_.erase(dialog_id); Dialog *dialog = dialog_it->second.get(); + + fix_dialog_action_bar(dialog); + send_update_new_chat(dialog); fix_new_dialog(dialog, std::move(last_database_message), last_database_message_id, order, last_clear_history_date, @@ -24947,24 +27604,41 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab MessageId last_clear_history_message_id, bool is_loaded_from_database) { CHECK(d != nullptr); auto dialog_id = d->dialog_id; + auto dialog_type = dialog_id.get_type(); if (being_added_dialog_id_ != dialog_id && !td_->auth_manager_->is_bot() && !is_dialog_inited(d) && - dialog_id.get_type() != DialogType::SecretChat && have_input_peer(dialog_id, AccessRights::Read)) { + dialog_type != DialogType::SecretChat && have_input_peer(dialog_id, AccessRights::Read)) { // asynchronously get dialog from the server send_get_dialog_query(dialog_id, Auto()); } if (being_added_dialog_id_ != dialog_id && !d->is_pinned_message_id_inited && - (d->dialog_id == get_my_dialog_id() || d->dialog_id.get_type() != DialogType::User) && - !td_->auth_manager_->is_bot()) { + (dialog_id == get_my_dialog_id() || dialog_type != DialogType::User) && !td_->auth_manager_->is_bot()) { // asynchronously get dialog pinned message from the server get_dialog_pinned_message(dialog_id, Auto()); } + if (being_added_dialog_id_ != dialog_id && !d->is_folder_id_inited && !td_->auth_manager_->is_bot() && + order != DEFAULT_ORDER) { + // asynchronously get dialog folder id from the server + get_dialog_info_full(dialog_id, Auto()); + } + if (!d->know_action_bar && !td_->auth_manager_->is_bot() && dialog_id != get_my_dialog_id() && + have_input_peer(dialog_id, AccessRights::Read)) { + // asynchronously get action bar from the server + if (dialog_type == DialogType::SecretChat) { + auto user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id()); + if (user_id.is_valid()) { + 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"); + } + } if (d->notification_settings.is_synchronized && !d->notification_settings.is_use_default_fixed && have_input_peer(dialog_id, AccessRights::Read) && !td_->auth_manager_->is_bot()) { LOG(INFO) << "Reget notification settings of " << dialog_id; - if (d->dialog_id.get_type() == DialogType::SecretChat) { + if (dialog_type == DialogType::SecretChat) { if (d->notification_settings.mute_until == 0 && users_notification_settings_.mute_until == 0) { d->notification_settings.use_default_mute_until = true; } @@ -24975,7 +27649,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab d->notification_settings.use_default_show_preview = true; } d->notification_settings.is_use_default_fixed = true; - on_dialog_updated(d->dialog_id, "reget notification settings"); + on_dialog_updated(dialog_id, "reget notification settings"); } else { send_get_dialog_notification_settings_query(dialog_id, Promise<>()); } @@ -24991,17 +27665,17 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab LOG(ERROR) << "Have pinned message notification in " << pinned_message_id << " in " << dialog_id << ", but there is no mention notification group"; d->pinned_message_notification_message_id = MessageId(); - on_dialog_updated(d->dialog_id, "fix pinned message notification"); + on_dialog_updated(dialog_id, "fix pinned message notification"); } else if (is_dialog_pinned_message_notifications_disabled(d) || - pinned_message_id.get() <= d->last_read_inbox_message_id.get() || - pinned_message_id.get() <= d->mention_notification_group.max_removed_message_id.get()) { + pinned_message_id <= d->last_read_inbox_message_id || + pinned_message_id <= d->mention_notification_group.max_removed_message_id) { VLOG(notifications) << "Remove disabled pinned message notification in " << pinned_message_id << " in " << dialog_id; send_closure_later(G()->notification_manager(), &NotificationManager::remove_temporary_notification_by_message_id, d->mention_notification_group.group_id, pinned_message_id, true, "fix pinned message notification"); d->pinned_message_notification_message_id = MessageId(); - on_dialog_updated(d->dialog_id, "fix pinned message notification 2"); + on_dialog_updated(dialog_id, "fix pinned message notification 2"); } } if (d->new_secret_chat_notification_id.is_valid()) { @@ -25011,7 +27685,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab VLOG(notifications) << "Fix removing new secret chat " << d->new_secret_chat_notification_id << " in " << dialog_id; d->new_secret_chat_notification_id = NotificationId(); - on_dialog_updated(d->dialog_id, "fix new secret chat notification id"); + on_dialog_updated(dialog_id, "fix new secret chat notification id"); } } @@ -25036,10 +27710,10 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab set_dialog_order(d, order, false, is_loaded_from_database, "fix_new_dialog 9"); - if (dialog_id.get_type() != DialogType::SecretChat && d->last_new_message_id.is_valid() && + if (dialog_type != DialogType::SecretChat && d->last_new_message_id.is_valid() && !d->last_new_message_id.is_server()) { // fix wrong last_new_message_id - d->last_new_message_id = MessageId(d->last_new_message_id.get() & ~MessageId::FULL_TYPE_MASK); + d->last_new_message_id = d->last_new_message_id.get_prev_server_message_id(); on_dialog_updated(dialog_id, "fix_new_dialog 10"); } @@ -25065,13 +27739,12 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab set_dialog_last_new_message_id(d, d->max_unavailable_message_id, "fix_new_dialog 11"); } if (last_message_id.is_valid()) { - if ((last_message_id.is_server() || dialog_id.get_type() == DialogType::SecretChat) && - !d->last_new_message_id.is_valid()) { + if ((last_message_id.is_server() || dialog_type == DialogType::SecretChat) && !d->last_new_message_id.is_valid()) { LOG(ERROR) << "Bugfixing wrong last_new_message_id to " << last_message_id << " in " << dialog_id; // must be called before set_dialog_first_database_message_id and set_dialog_last_database_message_id set_dialog_last_new_message_id(d, last_message_id, "fix_new_dialog 1"); } - if (!d->first_database_message_id.is_valid() || d->first_database_message_id.get() > last_message_id.get()) { + if (!d->first_database_message_id.is_valid() || d->first_database_message_id > last_message_id) { LOG(ERROR) << "Bugfixing wrong first_database_message_id from " << d->first_database_message_id << " to " << last_message_id << " in " << dialog_id; set_dialog_first_database_message_id(d, last_message_id, "fix_new_dialog 2"); @@ -25079,7 +27752,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab set_dialog_last_database_message_id(d, last_message_id, "fix_new_dialog 3", is_loaded_from_database); } else if (d->first_database_message_id.is_valid()) { // ensure that first_database_message_id <= last_database_message_id - if (d->first_database_message_id.get() <= d->last_new_message_id.get()) { + if (d->first_database_message_id <= d->last_new_message_id) { set_dialog_last_database_message_id(d, d->last_new_message_id, "fix_new_dialog 4"); } else { // can't fix last_database_message_id, drop first_database_message_id; it shouldn't happen anyway @@ -25125,16 +27798,16 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab } } - switch (dialog_id.get_type()) { + switch (dialog_type) { case DialogType::User: break; case DialogType::Chat: - if (d->last_read_inbox_message_id.get() < d->last_read_outbox_message_id.get()) { + 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 << ", 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 id not less than last read outgoing message and less than first unread outgoing message, so - // it may not point to the outgoing message + // just a message identifier not less than an identifier of last read outgoing message and less than + // an identifier of first unread outgoing message, so it may not point to the outgoing message // read_history_inbox(dialog_id, d->last_read_outbox_message_id, d->server_unread_count, "fix_new_dialog 6"); } break; @@ -25168,7 +27841,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr &&last_datab } if (d->need_repair_server_unread_count && have_input_peer(dialog_id, AccessRights::Read)) { - CHECK(dialog_id.get_type() != DialogType::SecretChat); + CHECK(dialog_type != DialogType::SecretChat); repair_server_unread_count(dialog_id, d->server_unread_count); } @@ -25207,6 +27880,7 @@ void MessagesManager::add_dialog_last_database_message(Dialog *d, unique_ptrright == nullptr); auto message_id = last_database_message->message_id; + CHECK(message_id.is_valid()); LOG_CHECK(d->last_database_message_id == message_id) << message_id << " " << d->last_database_message_id << " " << d->debug_set_dialog_last_database_message_id; @@ -25264,7 +27938,15 @@ void MessagesManager::update_dialogs_hints_rating(const Dialog *d) { } int64 MessagesManager::get_dialog_order(MessageId message_id, int32 message_date) { - return (static_cast(message_date) << 32) + narrow_cast(message_id.get() >> MessageId::SERVER_ID_SHIFT); + CHECK(!message_id.is_scheduled()); + return (static_cast(message_date) << 32) + + message_id.get_prev_server_message_id().get_server_message_id().get(); +} + +int64 MessagesManager::get_dialog_public_order(const Dialog *d) const { + DialogDate dialog_date(d->order, d->dialog_id); + auto *list = get_dialog_list(d->folder_id); + return list != nullptr && dialog_date <= list->last_dialog_date_ ? d->order : 0; } int64 MessagesManager::get_next_pinned_dialog_order() { @@ -25383,7 +28065,7 @@ void MessagesManager::update_dialog_pos(Dialog *d, bool remove_from_dialog_list, return; } } - if (new_order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_) { + if (new_order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_ && d->folder_id == FolderId::main()) { new_order = SPONSORED_DIALOG_ORDER; } @@ -25399,13 +28081,14 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen DialogDate old_date(d->order, dialog_id); 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 = &ordered_server_dialogs_; + ordered_dialogs_set = &list.ordered_server_dialogs_; break; case DialogType::None: default: @@ -25423,8 +28106,8 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen LOG(INFO) << "Update order of " << dialog_id << " from " << d->order << " to " << new_order << " from " << source; bool need_update = false; - if (old_date <= last_dialog_date_) { - if (ordered_dialogs_.erase(old_date) == 0) { + if (old_date <= list.last_dialog_date_) { + if (list.ordered_dialogs_.erase(old_date) == 0) { UNREACHABLE(); } need_update = true; @@ -25436,8 +28119,8 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen << dialog_id << " has non-default order after loading from database from " << source; int64 updated_to = 0; - if (new_date <= last_dialog_date_) { - ordered_dialogs_.insert(new_date); + if (new_date <= list.last_dialog_date_) { + list.ordered_dialogs_.insert(new_date); need_update = true; updated_to = new_order; } @@ -25448,24 +28131,46 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen bool is_sponsored = (new_order == SPONSORED_DIALOG_ORDER); 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; + bool is_added_to_folder = d->order == DEFAULT_ORDER; + + auto old_dialog_total_count = get_dialog_total_count(list); + if (is_removed_from_folder || is_added_to_folder) { + int32 delta = is_removed_from_folder ? -1 : 1; + list.in_memory_dialog_total_count_ += delta; + if (!is_loaded_from_database) { + int32 &total_count = dialog_id.get_type() == DialogType::SecretChat ? list.secret_chat_total_count_ + : list.server_dialog_total_count_; + if (total_count != -1) { + total_count += delta; + if (total_count < 0) { + LOG(ERROR) << "Total chat count became negative after leaving " << dialog_id; + total_count = 0; + } + } + } + } + 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()); if (!is_loaded_from_database && had_unread_counter != has_unread_counter && !td_->auth_manager_->is_bot()) { auto unread_count = d->server_unread_count + d->local_unread_count; const char *change_source = had_unread_counter ? "on_dialog_leave" : "on_dialog_join"; - if (unread_count != 0 && is_message_unread_count_inited_) { + if (unread_count != 0 && list.is_message_unread_count_inited_) { if (had_unread_counter) { unread_count = -unread_count; } else { CHECK(has_unread_counter); } - unread_message_total_count_ += unread_count; + list.unread_message_total_count_ += unread_count; if (is_dialog_muted(d)) { - unread_message_muted_count_ += unread_count; + list.unread_message_muted_count_ += unread_count; } - send_update_unread_message_count(dialog_id, true, change_source); + send_update_unread_message_count(d->folder_id, dialog_id, true, change_source); } - if ((unread_count != 0 || d->is_marked_as_unread) && is_dialog_unread_count_inited_) { + if ((unread_count != 0 || d->is_marked_as_unread) && list.is_dialog_unread_count_inited_) { int delta = 1; if (had_unread_counter) { delta = -1; @@ -25473,17 +28178,18 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen CHECK(has_unread_counter); } - unread_dialog_total_count_ += delta; + list.unread_dialog_total_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_marked_count_ += delta; + list.unread_dialog_marked_count_ += delta; } if (is_dialog_muted(d)) { - unread_dialog_muted_count_ += delta; + list.unread_dialog_muted_count_ += delta; if (unread_count == 0 && d->is_marked_as_unread) { - unread_dialog_muted_marked_count_ += delta; + list.unread_dialog_muted_marked_count_ += delta; } } - send_update_unread_chat_count(dialog_id, true, change_source); + need_update_unread_chat_count = false; + send_update_unread_chat_count(d->folder_id, dialog_id, true, change_source); } auto dialog_type = dialog_id.get_type(); @@ -25498,6 +28204,9 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen clear_active_dialog_actions(dialog_id); } } + if (list.is_dialog_unread_count_inited_ && need_update_unread_chat_count) { + send_update_unread_chat_count(d->folder_id, dialog_id, true, "changed total_count"); + } d->order = new_order; @@ -25506,6 +28215,9 @@ bool MessagesManager::set_dialog_order(Dialog *d, int64 new_order, bool need_sen } update_dialogs_hints_rating(d); + if (is_added_to_folder) { + send_update_chat_chat_list(d); + } if (was_sponsored != is_sponsored) { send_update_chat_is_sponsored(d); if (!is_loaded_from_database && is_sponsored) { @@ -25518,39 +28230,48 @@ 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) { + send_update_chat_chat_list(d); + } return true; } -void MessagesManager::update_last_dialog_date() { - auto old_last_dialog_date = last_dialog_date_; - last_dialog_date_ = last_server_dialog_date_; // std::min - CHECK(old_last_dialog_date <= last_dialog_date_); +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 + CHECK(old_last_dialog_date <= list.last_dialog_date_); - LOG(INFO) << "Update last dialog date from " << old_last_dialog_date << " to " << last_dialog_date_; - LOG(INFO) << "Know about " << ordered_server_dialogs_.size() << " chats"; + LOG(INFO) << "Update last dialog date from " << old_last_dialog_date << " to " << list.last_dialog_date_; + LOG(INFO) << "Know about " << list.ordered_server_dialogs_.size() << " chats"; - if (old_last_dialog_date != last_dialog_date_) { - for (auto it = ordered_server_dialogs_.upper_bound(old_last_dialog_date); - it != ordered_server_dialogs_.end() && *it <= last_dialog_date_; ++it) { + if (old_last_dialog_date != list.last_dialog_date_) { + for (auto it = list.ordered_server_dialogs_.upper_bound(old_last_dialog_date); + it != list.ordered_server_dialogs_.end() && *it <= list.last_dialog_date_; ++it) { auto dialog_id = it->get_dialog_id(); auto d = get_dialog(dialog_id); CHECK(d != nullptr); - ordered_dialogs_.insert(DialogDate(d->order, d->dialog_id)); + list.ordered_dialogs_.insert(DialogDate(d->order, d->dialog_id)); send_closure(G()->td(), &Td::send_update, make_tl_object(d->dialog_id.get(), d->order)); } - if (last_dialog_date_ == MAX_DIALOG_DATE) { - recalc_unread_count(); + if (list.last_dialog_date_ == MAX_DIALOG_DATE) { + bool need_update_unread_chat_count = list.server_dialog_total_count_ == -1 || list.secret_chat_total_count_ == -1; + recalc_unread_count(folder_id); + if (list.is_dialog_unread_count_inited_ && need_update_unread_chat_count) { + send_update_unread_chat_count(folder_id, DialogId(), true, "update_last_dialog_date"); + } } } - if (G()->parameters().use_message_db && last_database_server_dialog_date_ < last_server_dialog_date_) { - auto last_server_dialog_date_string = PSTRING() << last_server_dialog_date_.get_order() << ' ' - << last_server_dialog_date_.get_dialog_id().get(); - G()->td_db()->get_binlog_pmc()->set("last_server_dialog_date", last_server_dialog_date_string); + if (G()->parameters().use_message_db && list.last_database_server_dialog_date_ < list.last_server_dialog_date_) { + auto last_server_dialog_date_string = PSTRING() << list.last_server_dialog_date_.get_order() << ' ' + << list.last_server_dialog_date_.get_dialog_id().get(); + G()->td_db()->get_binlog_pmc()->set(PSTRING() << "last_server_dialog_date" << folder_id.get(), + last_server_dialog_date_string); LOG(INFO) << "Save last server dialog date " << last_server_dialog_date_string; - last_database_server_dialog_date_ = last_server_dialog_date_; - last_loaded_database_dialog_date_ = last_server_dialog_date_; + list.last_database_server_dialog_date_ = list.last_server_dialog_date_; + list.last_loaded_database_dialog_date_ = list.last_server_dialog_date_; } } @@ -25674,9 +28395,30 @@ MessagesManager::Dialog *MessagesManager::on_load_dialog_from_database(DialogId return old_d; } + LOG(INFO) << "Add new " << dialog_id << " from database"; return add_new_dialog(parse_dialog(dialog_id, value), true); } +MessagesManager::DialogList &MessagesManager::get_dialog_list(FolderId folder_id) { + if (folder_id != FolderId::archive()) { + folder_id = FolderId::main(); + } + auto &list = dialog_lists_[folder_id]; + list.folder_id = folder_id; + return list; +} + +const MessagesManager::DialogList *MessagesManager::get_dialog_list(FolderId folder_id) const { + if (folder_id != FolderId::archive()) { + folder_id = FolderId::main(); + } + auto it = dialog_lists_.find(folder_id); + if (it == dialog_lists_.end()) { + return nullptr; + } + return &it->second; +} + void MessagesManager::load_notification_settings() { if (td_->auth_manager_->is_bot()) { return; @@ -25708,7 +28450,7 @@ int32 MessagesManager::load_channel_pts(DialogId dialog_id) const { return pts; } -void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *source) const { +void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *source) { CHECK(d != nullptr); CHECK(d->dialog_id.get_type() == DialogType::Channel); @@ -25724,6 +28466,9 @@ void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *sour LOG(ERROR) << "Update " << d->dialog_id << " pts to -1"; G()->td_db()->get_binlog_pmc()->erase(get_channel_pts_key(d->dialog_id)); d->pts = std::numeric_limits::max(); + if (d->pending_read_channel_inbox_pts != 0) { + d->pending_read_channel_inbox_pts = 0; + } return; } if (new_pts > d->pts || (0 < new_pts && new_pts < d->pts - 99999)) { // pts can only go up or drop cardinally @@ -25734,6 +28479,17 @@ void MessagesManager::set_channel_pts(Dialog *d, int32 new_pts, const char *sour } d->pts = new_pts; + if (d->pending_read_channel_inbox_pts != 0 && d->pending_read_channel_inbox_pts <= d->pts) { + auto pts = d->pending_read_channel_inbox_pts; + d->pending_read_channel_inbox_pts = 0; + on_dialog_updated(d->dialog_id, "set_channel_pts"); + if (d->pts == pts) { + read_history_inbox(d->dialog_id, d->pending_read_channel_inbox_max_message_id, + d->pending_read_channel_inbox_server_unread_count, "set_channel_pts"); + } else if (d->pts > pts) { + repair_channel_server_unread_count(d); + } + } if (!G()->ignore_backgrond_updates()) { G()->td_db()->get_binlog_pmc()->set(get_channel_pts_key(d->dialog_id), to_string(new_pts)); } @@ -25862,6 +28618,7 @@ void MessagesManager::process_get_channel_difference_updates( debug_channel_difference_dialog_ = dialog_id; for (auto &update : other_updates) { if (update->get_id() == telegram_api::updateMessageID::ID) { + // in channels.getDifference updateMessageID can't be received for scheduled messages auto sent_message_update = move_tl_object_as(update); on_update_message_id(sent_message_update->random_id_, MessageId(ServerMessageId(sent_message_update->id_)), "get_channel_difference"); @@ -25874,7 +28631,7 @@ void MessagesManager::process_get_channel_difference_updates( !new_messages.empty() && get_message_date(new_messages[0]) < G()->unix_time() - 2 * 86400; for (auto &message : new_messages) { - on_get_message(std::move(message), true, true, true, true, "get channel difference"); + on_get_message(std::move(message), true, true, false, true, true, "get channel difference"); } for (auto &update : other_updates) { @@ -25908,7 +28665,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m vector> &&messages) { std::unordered_map, FullMessageIdHash> full_message_id_to_message; for (auto &message : messages) { - auto message_id = get_message_id(message); + auto message_id = get_message_id(message, false); auto message_dialog_id = get_message_dialog_id(message); if (!message_dialog_id.is_valid()) { message_dialog_id = dialog_id; @@ -25927,6 +28684,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m return; } } + CHECK(!last_message_id.is_scheduled()); Dialog *d = get_dialog(dialog_id); CHECK(d != nullptr); @@ -25950,8 +28708,8 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m // offline. It is the best way for gaps support, but it is pretty hard to implement correctly. // It should be also noted that some messages like live location messages shouldn't be deleted. - if (last_message_id.get() > d->last_new_message_id.get()) { - // TODO properly support last_message_id.get() <= d->last_new_message_id.get() + if (last_message_id > d->last_new_message_id) { + // TODO properly support last_message_id <= d->last_new_message_id set_dialog_first_database_message_id(d, MessageId(), "on_get_channel_dialog 6"); set_dialog_last_database_message_id(d, MessageId(), "on_get_channel_dialog 7"); d->have_full_history = false; @@ -25963,9 +28721,9 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m on_dialog_updated(dialog_id, "on_get_channel_dialog 10"); - // TODO properly support last_message_id.get() <= d->last_new_message_id.get() - if (last_message_id.get() > d->last_new_message_id.get()) { // if last message is really a new message - if (!d->last_new_message_id.is_valid() && last_message_id.get() <= d->max_added_message_id.get()) { + // TODO properly support last_message_id <= d->last_new_message_id + if (last_message_id > d->last_new_message_id) { // if last message is really a new message + if (!d->last_new_message_id.is_valid() && last_message_id <= d->max_added_message_id) { set_dialog_last_new_message_id(d, last_message_id, "on_get_channel_dialog 15"); // remove too new messages set_dialog_first_database_message_id(d, MessageId(), "on_get_channel_dialog 16"); set_dialog_last_database_message_id(d, MessageId(), "on_get_channel_dialog 17"); @@ -25974,7 +28732,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m set_dialog_last_message_id(d, MessageId(), "on_get_channel_dialog 20"); send_update_chat_last_message(d, "on_get_channel_dialog 30"); auto added_full_message_id = on_get_message(std::move(full_message_id_to_message[last_full_message_id]), true, true, - true, true, "channel difference too long"); + false, true, true, "channel difference too long"); if (added_full_message_id.get_message_id().is_valid()) { if (added_full_message_id.get_message_id() == d->last_new_message_id) { CHECK(last_full_message_id == added_full_message_id); @@ -25983,7 +28741,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m LOG(ERROR) << added_full_message_id << " doesn't became last new message, which is " << d->last_new_message_id; dump_debug_message_op(d, 2); } - } else if (last_message_id.get() > d->last_new_message_id.get()) { + } else if (last_message_id > d->last_new_message_id) { set_dialog_last_new_message_id(d, last_message_id, "on_get_channel_dialog 40"); // skip updates about some messages } @@ -26094,13 +28852,31 @@ void MessagesManager::on_get_channel_difference( case telegram_api::updates_channelDifferenceTooLong::ID: { auto difference = move_tl_object_as(difference_ptr); + tl_object_ptr dialog; + switch (difference->dialog_->get_id()) { + case telegram_api::dialog::ID: + dialog = telegram_api::move_object_as(difference->dialog_); + break; + case telegram_api::dialogFolder::ID: + return after_get_channel_difference(dialog_id, false); + default: + UNREACHABLE(); + return; + } + + CHECK(dialog != nullptr); + if ((dialog->flags_ & telegram_api::dialog::PTS_MASK) == 0) { + LOG(ERROR) << "Receive " << dialog_id << " without pts"; + return after_get_channel_difference(dialog_id, false); + } + int32 flags = difference->flags_; is_final = (flags & CHANNEL_DIFFERENCE_FLAG_IS_FINAL) != 0; if (flags & CHANNEL_DIFFERENCE_FLAG_HAS_TIMEOUT) { timeout = difference->timeout_; } - auto new_pts = difference->pts_; + auto new_pts = dialog->pts_; if (request_pts + request_limit > new_pts) { LOG(ERROR) << "Receive channelDifferenceTooLong as result of getChannelDifference with pts = " << request_pts << " and limit = " << request_limit << " in " << dialog_id << ", but pts has changed from " << d->pts @@ -26113,10 +28889,27 @@ void MessagesManager::on_get_channel_difference( td_->contacts_manager_->on_get_users(std::move(difference->users_), "updates.channelDifferenceTooLong"); td_->contacts_manager_->on_get_chats(std::move(difference->chats_), "updates.channelDifferenceTooLong"); - on_get_channel_dialog(dialog_id, MessageId(ServerMessageId(difference->top_message_)), - MessageId(ServerMessageId(difference->read_inbox_max_id_)), difference->unread_count_, - difference->unread_mentions_count_, - MessageId(ServerMessageId(difference->read_outbox_max_id_)), + set_dialog_folder_id(d, FolderId((dialog->flags_ & DIALOG_FLAG_HAS_FOLDER_ID) != 0 ? dialog->folder_id_ : 0)); + + 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 was_pinned = d->pinned_order != DEFAULT_ORDER; + if (is_pinned != was_pinned) { + set_dialog_is_pinned(d, is_pinned); + } + + bool is_marked_as_unread = (dialog->flags_ & telegram_api::dialog::UNREAD_MARK_MASK) != 0; + if (is_marked_as_unread != d->is_marked_as_unread) { + set_dialog_is_marked_as_unread(d, is_marked_as_unread); + } + + update_dialog_draft_message(d, get_draft_message(td_->contacts_manager_.get(), std::move(dialog->draft_)), true, + false); + + on_get_channel_dialog(dialog_id, MessageId(ServerMessageId(dialog->top_message_)), + MessageId(ServerMessageId(dialog->read_inbox_max_id_)), dialog->unread_count_, + dialog->unread_mentions_count_, MessageId(ServerMessageId(dialog->read_outbox_max_id_)), std::move(difference->messages_)); need_update_dialog_pos = true; @@ -26211,7 +29004,8 @@ void MessagesManager::after_get_channel_difference(DialogId dialog_id, bool succ for (auto &request : it_get_message_requests->second) { auto message_id = request.message_id; LOG(INFO) << "Run postponed getMessage request for " << message_id << " in " << dialog_id; - if (d->last_new_message_id != MessageId() && message_id.get() > d->last_new_message_id.get()) { + CHECK(message_id.is_valid()); + if (d->last_new_message_id != MessageId() && message_id > d->last_new_message_id) { // message will not be added to the dialog anyway, get channel difference didn't help request.promise.set_value(Unit()); } else { @@ -26227,15 +29021,62 @@ void MessagesManager::after_get_channel_difference(DialogId dialog_id, bool succ PendingOnGetDialogs res = std::move(it->second); pending_channel_on_get_dialogs_.erase(it); - on_get_dialogs(std::move(res.dialogs), res.total_count, std::move(res.messages), std::move(res.promise)); + on_get_dialogs(res.folder_id, std::move(res.dialogs), res.total_count, std::move(res.messages), + std::move(res.promise)); + } +} + +void MessagesManager::reget_message_from_server_if_needed(DialogId dialog_id, const Message *m) { + if (m->message_id.is_any_server() && dialog_id.get_type() != DialogType::SecretChat && + (need_reget_message_content(m->content.get()) || (m->legacy_layer != 0 && m->legacy_layer < MTPROTO_LAYER))) { + FullMessageId full_message_id{dialog_id, m->message_id}; + LOG(INFO) << "Reget from server " << full_message_id; + get_message_from_server(full_message_id, Auto()); + } +} + +void MessagesManager::speculatively_update_channel_participants(DialogId dialog_id, const Message *m) { + CHECK(m != nullptr); + if (!m->message_id.is_any_server() || dialog_id.get_type() != DialogType::Channel || !m->sender_user_id.is_valid()) { + return; } - // TODO resend some messages + auto channel_id = dialog_id.get_channel_id(); + UserId my_user_id(td_->contacts_manager_->get_my_id()); + bool by_me = m->sender_user_id == my_user_id; + switch (m->content->get_type()) { + case MessageContentType::ChatAddUsers: + td_->contacts_manager_->speculative_add_channel_participants( + channel_id, get_message_content_added_user_ids(m->content.get()), m->sender_user_id, m->date, by_me); + break; + case MessageContentType::ChatJoinedByLink: + td_->contacts_manager_->speculative_add_channel_participants(channel_id, {m->sender_user_id}, m->sender_user_id, + m->date, by_me); + break; + case MessageContentType::ChatDeleteUser: + td_->contacts_manager_->speculative_delete_channel_participant( + channel_id, get_message_content_deleted_user_id(m->content.get()), by_me); + break; + default: + break; + } +} + +void MessagesManager::update_sent_message_contents(DialogId dialog_id, const Message *m) { + CHECK(m != nullptr); + if (td_->auth_manager_->is_bot() || (!m->is_outgoing && dialog_id != get_my_dialog_id()) || + dialog_id.get_type() == DialogType::SecretChat || !m->message_id.is_local() || m->forward_info != nullptr || + m->had_forward_info) { + return; + } + + on_sent_message_content(td_, m->content.get()); } void MessagesManager::update_used_hashtags(DialogId dialog_id, const Message *m) { CHECK(m != nullptr); - if (m->via_bot_user_id.is_valid() || m->hide_via_bot) { + if (td_->auth_manager_->is_bot() || (!m->is_outgoing && dialog_id != get_my_dialog_id()) || + m->via_bot_user_id.is_valid() || m->hide_via_bot || m->forward_info != nullptr || m->had_forward_info) { return; } const FormattedText *text = get_message_content_text(m->content.get()); @@ -26267,6 +29108,69 @@ void MessagesManager::update_used_hashtags(DialogId dialog_id, const Message *m) } } +void MessagesManager::update_top_dialogs(DialogId dialog_id, const Message *m) { + CHECK(m != nullptr); + auto dialog_type = dialog_id.get_type(); + if (td_->auth_manager_->is_bot() || (!m->is_outgoing && dialog_id != get_my_dialog_id()) || + dialog_type == DialogType::SecretChat || !m->message_id.is_any_server()) { + return; + } + + bool is_forward = m->forward_info != nullptr || m->had_forward_info; + if (m->via_bot_user_id.is_valid() && !is_forward) { + // forwarded game messages can't be distinguished from sent via bot game messages, so increase rating anyway + send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, TopDialogCategory::BotInline, + DialogId(m->via_bot_user_id), m->date); + } + + if (is_forward) { + auto &last_forward_date = last_outgoing_forwarded_message_date_[dialog_id]; + if (last_forward_date < m->date) { + TopDialogCategory category = + dialog_type == DialogType::User ? TopDialogCategory::ForwardUsers : TopDialogCategory::ForwardChats; + send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, category, dialog_id, m->date); + last_forward_date = m->date; + } + } + + TopDialogCategory category = TopDialogCategory::Size; + switch (dialog_type) { + case DialogType::User: { + if (td_->contacts_manager_->is_user_bot(dialog_id.get_user_id())) { + category = TopDialogCategory::BotPM; + } else { + category = TopDialogCategory::Correspondent; + } + break; + } + case DialogType::Chat: + category = TopDialogCategory::Group; + break; + case DialogType::Channel: + switch (td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id())) { + case ChannelType::Broadcast: + category = TopDialogCategory::Channel; + break; + case ChannelType::Megagroup: + category = TopDialogCategory::Group; + break; + case ChannelType::Unknown: + break; + default: + UNREACHABLE(); + break; + } + break; + case DialogType::SecretChat: + case DialogType::None: + default: + UNREACHABLE(); + } + if (category != TopDialogCategory::Size) { + send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, category, dialog_id, m->date); + } +} + MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog_id, unique_ptr &&m, uint64 logevent_id) { CHECK(logevent_id != 0); @@ -26276,23 +29180,24 @@ MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog binlog_erase(G()->td_db()->get_binlog(), logevent_id); return nullptr; } - - auto now = G()->unix_time(); - - m->message_id = get_next_yet_unsent_message_id(d); - m->random_y = get_random_y(m->message_id); - m->date = now; - m->have_previous = true; - m->have_next = true; - - LOG(INFO) << "Continue to send " << m->message_id << " to " << dialog_id << " initially sent at " << m->send_date - << " from binlog"; - if (!have_input_peer(dialog_id, AccessRights::Read)) { binlog_erase(G()->td_db()->get_binlog(), logevent_id); return nullptr; } + LOG(INFO) << "Continue to send " << m->message_id << " to " << dialog_id << " initially sent at " << m->send_date + << " from binlog"; + + auto now = G()->unix_time(); + if (m->message_id.is_scheduled()) { + set_message_id(m, get_next_yet_unsent_scheduled_message_id(d, m->date)); + } else { + set_message_id(m, get_next_yet_unsent_message_id(d)); + m->date = now; + } + m->have_previous = true; + m->have_next = true; + message_random_ids_.insert(m->random_id); bool need_update = false; @@ -26300,7 +29205,8 @@ MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog auto result_message = add_message_to_dialog(d, std::move(m), true, &need_update, &need_update_dialog_pos, "resend message"); CHECK(result_message != nullptr); - // CHECK(need_update_dialog_pos == true); + + send_update_chat_has_scheduled_messages(d); send_update_new_message(d, result_message); if (need_update_dialog_pos) { @@ -26452,7 +29358,7 @@ void MessagesManager::on_binlog_events(vector &&events) { case LogEvent::HandlerType::ForwardMessages: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); - break; + continue; } ForwardMessagesLogEvent log_event; @@ -26482,22 +29388,14 @@ void MessagesManager::on_binlog_events(vector &&events) { binlog_erase(G()->td_db()->get_binlog(), event.id_); continue; } - auto now = G()->unix_time(); - for (auto &m : messages) { - m->message_id = get_next_yet_unsent_message_id(to_dialog); - m->random_y = get_random_y(m->message_id); - m->date = now; - m->content = dup_message_content(td_, to_dialog_id, m->content.get(), true); - m->have_previous = true; - m->have_next = true; - } + auto now = G()->unix_time(); if (!have_input_peer(from_dialog_id, AccessRights::Read) || can_send_message(to_dialog_id).is_error() || messages.empty() || (messages[0]->send_date < now - MAX_RESEND_DELAY && to_dialog_id != get_my_dialog_id())) { LOG(WARNING) << "Can't continue forwarding " << messages.size() << " message(s) to " << to_dialog_id; binlog_erase(G()->td_db()->get_binlog(), event.id_); - break; + continue; } LOG(INFO) << "Continue to forward " << messages.size() << " message(s) to " << to_dialog_id << " from binlog"; @@ -26506,11 +29404,24 @@ void MessagesManager::on_binlog_events(vector &&events) { bool need_update_dialog_pos = false; vector forwarded_messages; for (auto &m : messages) { + if (m->message_id.is_scheduled()) { + set_message_id(m, get_next_yet_unsent_scheduled_message_id(to_dialog, m->date)); + } else { + set_message_id(m, get_next_yet_unsent_message_id(to_dialog)); + m->date = now; + } + m->content = dup_message_content(td_, to_dialog_id, m->content.get(), true); + m->have_previous = true; + m->have_next = true; + message_random_ids_.insert(m->random_id); forwarded_messages.push_back(add_message_to_dialog(to_dialog, std::move(m), true, &need_update, &need_update_dialog_pos, "forward message again")); send_update_new_message(to_dialog, forwarded_messages.back()); } + + send_update_chat_has_scheduled_messages(to_dialog); + if (need_update_dialog_pos) { send_update_chat_last_message(to_dialog, "on_reforward_message"); } @@ -26530,7 +29441,12 @@ void MessagesManager::on_binlog_events(vector &&events) { Dialog *d = get_dialog_force(log_event.full_message_id_.get_dialog_id()); if (d != nullptr) { - d->deleted_message_ids.insert(log_event.full_message_id_.get_message_id()); + auto message_id = log_event.full_message_id_.get_message_id(); + if (message_id.is_valid_scheduled() && message_id.is_scheduled_server()) { + d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id()); + } else { + d->deleted_message_ids.insert(message_id); + } } do_delete_message_logevent(log_event); @@ -26557,6 +29473,29 @@ void MessagesManager::on_binlog_events(vector &&events) { delete_messages_from_server(dialog_id, std::move(log_event.message_ids_), log_event.revoke_, event.id_, Auto()); break; } + case LogEvent::HandlerType::DeleteScheduledMessagesFromServer: { + if (!G()->parameters().use_message_db) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + DeleteScheduledMessagesFromServerLogEvent log_event; + log_event_parse(log_event, event.data_).ensure(); + + auto dialog_id = log_event.dialog_id_; + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + for (auto message_id : log_event.message_ids_) { + d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id()); + } + + delete_scheduled_messages_from_server(dialog_id, std::move(log_event.message_ids_), event.id_, Auto()); + break; + } case LogEvent::HandlerType::DeleteDialogHistoryFromServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); @@ -26670,7 +29609,7 @@ void MessagesManager::on_binlog_events(vector &&events) { break; } - read_message_contents_on_server(dialog_id, std::move(log_event.message_ids_), event.id_); + read_message_contents_on_server(dialog_id, std::move(log_event.message_ids_), event.id_, Auto()); break; } case LogEvent::HandlerType::ReadAllDialogMentionsOnServer: { @@ -26732,7 +29671,7 @@ void MessagesManager::on_binlog_events(vector &&events) { break; } - reorder_pinned_dialogs_on_server(dialog_ids, event.id_); + reorder_pinned_dialogs_on_server(log_event.folder_id_, dialog_ids, event.id_); break; } case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer: { @@ -26827,6 +29766,29 @@ void MessagesManager::on_binlog_events(vector &&events) { change_dialog_report_spam_state_on_server(dialog_id, log_event.is_spam_dialog_, event.id_, Promise()); break; } + case LogEvent::HandlerType::SetDialogFolderIdOnServer: { + if (!G()->parameters().use_message_db) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + + SetDialogFolderIdOnServerLogEvent log_event; + log_event_parse(log_event, event.data_).ensure(); + + auto dialog_id = log_event.dialog_id_; + Dialog *d = get_dialog_force(dialog_id); + if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) { + binlog_erase(G()->td_db()->get_binlog(), event.id_); + break; + } + d->set_folder_id_logevent_id = event.id_; + d->set_folder_id_logevent_id_generation++; + + set_dialog_folder_id(d, log_event.folder_id_); + + set_dialog_folder_id_on_server(dialog_id, true); + break; + } case LogEvent::HandlerType::GetDialogFromServer: { if (!G()->parameters().use_message_db) { binlog_erase(G()->td_db()->get_binlog(), event.id_); @@ -26968,7 +29930,8 @@ bool MessagesManager::load_recently_found_dialogs(Promise &promise) { } resolve_recently_found_dialogs_multipromise_.get_promise().set_value(Unit()); } else { - get_dialogs(MIN_DIALOG_DATE, MAX_GET_DIALOGS, false, resolve_recently_found_dialogs_multipromise_.get_promise()); + get_dialogs(FolderId::main(), MIN_DIALOG_DATE, MAX_GET_DIALOGS, false, + resolve_recently_found_dialogs_multipromise_.get_promise()); td_->contacts_manager_->search_contacts("", 1, resolve_recently_found_dialogs_multipromise_.get_promise()); } } @@ -27031,13 +29994,7 @@ bool MessagesManager::add_recently_found_dialog_internal(DialogId dialog_id) { bool MessagesManager::remove_recently_found_dialog_internal(DialogId dialog_id) { CHECK(have_dialog(dialog_id)); - - auto it = std::find(recently_found_dialog_ids_.begin(), recently_found_dialog_ids_.end(), dialog_id); - if (it == recently_found_dialog_ids_.end()) { - return false; - } - recently_found_dialog_ids_.erase(it); - return true; + return td::remove(recently_found_dialog_ids_, dialog_id); } void MessagesManager::suffix_load_loop(Dialog *d) { @@ -27061,6 +30018,7 @@ void MessagesManager::suffix_load_loop(Dialog *d) { if (from_message_id.is_valid()) { get_history(dialog_id, from_message_id, -1, 100, true, true, std::move(promise)); } else { + CHECK(from_message_id == MessageId()); get_history_from_the_end(dialog_id, true, true, std::move(promise)); } } @@ -27129,7 +30087,7 @@ void MessagesManager::suffix_load_till_date(Dialog *d, int32 date, Promise<> pro void MessagesManager::suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<> promise) { LOG(INFO) << "Load suffix of " << d->dialog_id << " till " << message_id; auto condition = [message_id](const Message *m) { - return m != nullptr && m->message_id.get() < message_id.get(); + return m != nullptr && m->message_id < message_id; }; suffix_load_add_query(d, std::make_pair(std::move(promise), std::move(condition))); } @@ -27146,7 +30104,10 @@ void MessagesManager::set_poll_answer(FullMessageId full_message_id, vectorcontent->get_type() != MessageContentType::Poll) { return promise.set_error(Status::Error(5, "Message is not a poll")); } - if (!full_message_id.get_message_id().is_server()) { + if (m->message_id.is_scheduled()) { + return promise.set_error(Status::Error(5, "Can't answer polls from scheduled messages")); + } + if (!m->message_id.is_server()) { return promise.set_error(Status::Error(5, "Poll can't be answered")); } @@ -27171,7 +30132,10 @@ void MessagesManager::stop_poll(FullMessageId full_message_id, td_api::object_pt if (!can_edit_message(full_message_id.get_dialog_id(), m, true)) { return promise.set_error(Status::Error(5, "Poll can't be stopped")); } - if (!full_message_id.get_message_id().is_server()) { + if (m->message_id.is_scheduled()) { + return promise.set_error(Status::Error(5, "Can't stop polls from scheduled messages")); + } + if (!m->message_id.is_server()) { return promise.set_error(Status::Error(5, "Poll can't be stopped")); } @@ -27193,13 +30157,15 @@ Result MessagesManager::get_invoice_message_id(FullMessageId fu if (m->content->get_type() != MessageContentType::Invoice) { return Status::Error(5, "Message has no invoice"); } - auto message_id = full_message_id.get_message_id(); - if (!message_id.is_server()) { + if (m->message_id.is_scheduled()) { + return Status::Error(5, "Wrong scheduled message identifier"); + } + if (!m->message_id.is_server()) { return Status::Error(5, "Wrong message identifier"); } // TODO need to check that message is not forwarded - return message_id.get_server_message_id(); + return m->message_id.get_server_message_id(); } void MessagesManager::get_payment_form(FullMessageId full_message_id, @@ -27244,12 +30210,14 @@ void MessagesManager::get_payment_receipt(FullMessageId full_message_id, if (m->content->get_type() != MessageContentType::PaymentSuccessful) { return promise.set_error(Status::Error(5, "Message has wrong type")); } - auto message_id = full_message_id.get_message_id(); - if (!message_id.is_server()) { + if (m->message_id.is_scheduled()) { + return promise.set_error(Status::Error(5, "Can't get payment receipt from scheduled messages")); + } + if (!m->message_id.is_server()) { return promise.set_error(Status::Error(5, "Wrong message identifier")); } - ::td::get_payment_receipt(message_id.get_server_message_id(), std::move(promise)); + ::td::get_payment_receipt(m->message_id.get_server_message_id(), std::move(promise)); } void MessagesManager::on_get_sponsored_dialog_id(tl_object_ptr peer, @@ -27297,12 +30265,13 @@ void MessagesManager::set_sponsored_dialog_id(DialogId dialog_id) { } } -td_api::object_ptr MessagesManager::get_update_unread_message_count_object() const { - CHECK(is_message_unread_count_inited_); - int32 unread_count = unread_message_total_count_; - int32 unread_unmuted_count = unread_message_total_count_ - unread_message_muted_count_; +td_api::object_ptr MessagesManager::get_update_unread_message_count_object( + FolderId folder_id, 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()) { + if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && 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; @@ -27322,21 +30291,23 @@ td_api::object_ptr MessagesManager::get_update CHECK(unread_count >= 0); CHECK(unread_unmuted_count >= 0); - return td_api::make_object(unread_count, unread_unmuted_count); + return td_api::make_object(get_chat_list_object(folder_id), unread_count, + unread_unmuted_count); } -td_api::object_ptr MessagesManager::get_update_unread_chat_count_object() const { - CHECK(is_dialog_unread_count_inited_); - int32 unread_count = unread_dialog_total_count_; - int32 unread_unmuted_count = unread_count - unread_dialog_muted_count_; - int32 unread_marked_count = unread_dialog_marked_count_; - int32 unread_unmuted_marked_count = unread_marked_count - unread_dialog_muted_marked_count_; +td_api::object_ptr MessagesManager::get_update_unread_chat_count_object( + FolderId folder_id, 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_; + int32 unread_marked_count = list.unread_dialog_marked_count_; + int32 unread_unmuted_marked_count = unread_marked_count - list.unread_dialog_muted_marked_count_; CHECK(unread_count >= 0); CHECK(unread_unmuted_count >= 0); CHECK(unread_marked_count >= 0); CHECK(unread_unmuted_marked_count >= 0); - if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid()) { + if (!include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() && 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; @@ -27354,18 +30325,22 @@ td_api::object_ptr MessagesManager::get_update_un } } - return td_api::make_object(unread_count, unread_unmuted_count, unread_marked_count, - 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, + unread_marked_count, unread_unmuted_marked_count); } void MessagesManager::get_current_state(vector> &updates) const { if (!td_->auth_manager_->is_bot()) { if (G()->parameters().use_message_db) { - if (is_message_unread_count_inited_) { - updates.push_back(get_update_unread_message_count_object()); - } - if (is_dialog_unread_count_inited_) { - updates.push_back(get_update_unread_chat_count_object()); + for (auto &it : dialog_lists_) { + auto &list = it.second; + if (list.is_message_unread_count_inited_) { + updates.push_back(get_update_unread_message_count_object(it.first, list)); + } + if (list.is_dialog_unread_count_inited_) { + updates.push_back(get_update_unread_chat_count_object(it.first, list)); + } } } @@ -27385,10 +30360,8 @@ void MessagesManager::get_current_state(vector(get_chat_object(d)); if (update->chat_->last_message_ != nullptr && update->chat_->last_message_->forward_info_ != nullptr) { - DialogDate dialog_date(d->order, d->dialog_id); last_message_updates.push_back(td_api::make_object( - d->dialog_id.get(), std::move(update->chat_->last_message_), - dialog_date <= last_dialog_date_ ? d->order : 0)); + d->dialog_id.get(), std::move(update->chat_->last_message_), get_dialog_public_order(d))); } updates.push_back(std::move(update)); diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 786c24ca..ccdd353c 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,11 +13,16 @@ #include "td/telegram/AccessRights.h" #include "td/telegram/ChannelId.h" #include "td/telegram/Dependencies.h" +#include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogDate.h" +#include "td/telegram/DialogDb.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" +#include "td/telegram/FolderId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/Global.h" #include "td/telegram/MessageContent.h" #include "td/telegram/MessageEntity.h" @@ -31,8 +36,11 @@ #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationSettings.h" #include "td/telegram/ReplyMarkup.h" +#include "td/telegram/RestrictionReason.h" +#include "td/telegram/ScheduledServerMessageId.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/UserId.h" #include "td/actor/actor.h" @@ -140,7 +148,10 @@ class MessagesManager : public Actor { static constexpr int32 MESSAGE_FLAG_HAS_EDIT_DATE = 1 << 15; static constexpr int32 MESSAGE_FLAG_HAS_AUTHOR_SIGNATURE = 1 << 16; static constexpr int32 MESSAGE_FLAG_HAS_MEDIA_ALBUM_ID = 1 << 17; + static constexpr int32 MESSAGE_FLAG_IS_FROM_SCHEDULED = 1 << 18; static constexpr int32 MESSAGE_FLAG_IS_LEGACY = 1 << 19; + static constexpr int32 MESSAGE_FLAG_HIDE_EDIT_DATE = 1 << 21; + static constexpr int32 MESSAGE_FLAG_IS_RESTRICTED = 1 << 22; static constexpr int32 SEND_MESSAGE_FLAG_IS_REPLY = 1 << 0; static constexpr int32 SEND_MESSAGE_FLAG_DISABLE_WEB_PAGE_PREVIEW = 1 << 1; @@ -152,6 +163,7 @@ class MessagesManager : public Actor { static constexpr int32 SEND_MESSAGE_FLAG_CLEAR_DRAFT = 1 << 7; static constexpr int32 SEND_MESSAGE_FLAG_WITH_MY_SCORE = 1 << 8; static constexpr int32 SEND_MESSAGE_FLAG_GROUP_MEDIA = 1 << 9; + static constexpr int32 SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE = 1 << 10; static constexpr int32 SEND_MESSAGE_FLAG_HAS_MESSAGE = 1 << 11; static constexpr int32 ONLINE_MEMBER_COUNT_CACHE_EXPIRE_TIME = 30 * 60; @@ -167,25 +179,19 @@ class MessagesManager : public Actor { static vector get_server_message_ids(const vector &message_ids); - static tl_object_ptr get_input_message(MessageId message_id); - - static MessageId get_message_id(const tl_object_ptr &message_ptr); + static vector get_scheduled_server_message_ids(const vector &message_ids); DialogId get_message_dialog_id(const tl_object_ptr &message_ptr) const; - FullMessageId get_full_message_id(const tl_object_ptr &message_ptr) const; - - static int32 get_message_date(const tl_object_ptr &message_ptr); - tl_object_ptr get_input_peer(DialogId dialog_id, AccessRights access_rights) const; vector> get_input_peers(const vector &dialog_ids, AccessRights access_rights) const; - tl_object_ptr get_input_dialog_peer(DialogId dialog_id, + tl_object_ptr get_input_dialog_peer(DialogId dialog_id, AccessRights access_rights) const; - vector> get_input_dialog_peers(const vector &dialog_ids, + vector> get_input_dialog_peers(const vector &dialog_ids, AccessRights access_rights) const; tl_object_ptr get_input_encrypted_chat(DialogId dialog_id, @@ -201,7 +207,7 @@ class MessagesManager : public Actor { MessagesInfo on_get_messages(tl_object_ptr &&messages_ptr, const char *source); void on_get_messages(vector> &&messages, bool is_channel_message, - const char *source); + bool is_scheduled, const char *source); void on_get_history(DialogId dialog_id, MessageId from_message_id, int32 offset, int32 limit, bool from_the_end, vector> &&messages); @@ -221,13 +227,17 @@ class MessagesManager : public Actor { vector> &&messages); void on_failed_messages_search(int64 random_id); + void on_get_scheduled_server_messages(DialogId dialog_id, uint32 generation, + vector> &&messages, bool is_not_modified); + void on_get_recent_locations(DialogId dialog_id, int32 limit, int64 random_id, int32 total_count, vector> &&messages); void on_get_recent_locations_failed(int64 random_id); // if message is from_update, flags have_previous and have_next are ignored and should be both true FullMessageId on_get_message(tl_object_ptr message_ptr, bool from_update, - bool is_channel_message, bool have_previous, bool have_next, const char *source); + bool is_channel_message, bool is_scheduled, bool have_previous, bool have_next, + const char *source); void open_secret_message(SecretChatId secret_chat_id, int64 random_id, Promise<>); @@ -258,24 +268,31 @@ class MessagesManager : public Actor { void on_update_message_web_page(FullMessageId full_message_id, bool have_web_page); - void on_get_dialogs(vector> &&dialogs, int32 total_count, - vector> &&messages, Promise &&promise); + void on_get_dialogs(FolderId folder_id, vector> &&dialog_folders, + int32 total_count, vector> &&messages, + Promise &&promise); void on_get_common_dialogs(UserId user_id, int32 offset_chat_id, vector> &&chats, int32 total_count); bool on_update_message_id(int64 random_id, MessageId new_message_id, const string &source); + bool on_update_scheduled_message_id(int64 random_id, ScheduledServerMessageId new_message_id, const string &source); + void on_update_dialog_draft_message(DialogId dialog_id, tl_object_ptr &&draft_message); - void on_update_dialog_is_pinned(DialogId dialog_id, bool is_pinned); + void on_update_dialog_is_pinned(FolderId folder_id, DialogId dialog_id, bool is_pinned); - void on_update_pinned_dialogs(); + void on_update_pinned_dialogs(FolderId folder_id); void on_update_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread); void on_update_dialog_pinned_message_id(DialogId dialog_id, MessageId pinned_message_id); + void on_update_dialog_has_scheduled_server_messages(DialogId dialog_id, bool has_scheduled_server_messages); + + void on_update_dialog_folder_id(DialogId dialog_id, FolderId folder_id); + void on_update_service_notification(tl_object_ptr &&update, bool skip_new_entities, Promise &&promise); @@ -294,9 +311,14 @@ class MessagesManager : public Actor { void on_update_message_views(FullMessageId full_message_id, int32 views); + void on_update_live_location_viewed(FullMessageId full_message_id); + + void on_update_some_live_location_viewed(Promise &&promise); + void on_update_message_content(FullMessageId full_message_id); - void on_read_channel_inbox(ChannelId channel_id, MessageId max_message_id, int32 server_unread_count); + void on_read_channel_inbox(ChannelId channel_id, MessageId max_message_id, int32 server_unread_count, int32 pts, + const char *source); void on_read_channel_outbox(ChannelId channel_id, MessageId max_message_id); @@ -304,6 +326,8 @@ class MessagesManager : public Actor { void on_update_dialog_online_member_count(DialogId dialog_id, int32 online_member_count, bool is_from_server); + void on_update_delete_scheduled_messages(DialogId dialog_id, vector &&server_message_ids); + void on_update_include_sponsored_dialog_to_unread_count(); void on_user_dialog_action(DialogId dialog_id, UserId user_id, tl_object_ptr &&action, int32 date, @@ -331,26 +355,27 @@ class MessagesManager : public Actor { DialogId search_public_dialog(const string &username_to_search, bool force, Promise &&promise); - Result send_message(DialogId dialog_id, MessageId reply_to_message_id, bool disable_notification, - bool from_background, tl_object_ptr &&reply_markup, - tl_object_ptr &&input_message_content) - TD_WARN_UNUSED_RESULT; + Result send_message( + DialogId dialog_id, MessageId reply_to_message_id, tl_object_ptr &&options, + tl_object_ptr &&reply_markup, + tl_object_ptr &&input_message_content) TD_WARN_UNUSED_RESULT; Result> send_message_group( - DialogId dialog_id, MessageId reply_to_message_id, bool disable_notification, bool from_background, + DialogId dialog_id, MessageId reply_to_message_id, tl_object_ptr &&options, vector> &&input_message_contents) TD_WARN_UNUSED_RESULT; Result send_bot_start_message(UserId bot_user_id, DialogId dialog_id, const string ¶meter) TD_WARN_UNUSED_RESULT; Result send_inline_query_result_message(DialogId dialog_id, MessageId reply_to_message_id, - bool disable_notification, bool from_background, int64 query_id, - const string &result_id, bool hide_via_bot) TD_WARN_UNUSED_RESULT; + tl_object_ptr &&options, + int64 query_id, const string &result_id, + bool hide_via_bot) TD_WARN_UNUSED_RESULT; Result> forward_messages(DialogId to_dialog_id, DialogId from_dialog_id, - vector message_ids, bool disable_notification, - bool from_background, bool in_game_share, bool as_album, bool send_copy, - bool remove_caption) TD_WARN_UNUSED_RESULT; + vector message_ids, + tl_object_ptr &&options, bool in_game_share, + bool as_album, bool send_copy, bool remove_caption) TD_WARN_UNUSED_RESULT; Result> resend_messages(DialogId dialog_id, vector message_ids) TD_WARN_UNUSED_RESULT; @@ -395,6 +420,10 @@ class MessagesManager : public Actor { void edit_inline_message_reply_markup(const string &inline_message_id, tl_object_ptr &&reply_markup, Promise &&promise); + void edit_message_scheduling_state(FullMessageId full_message_id, + td_api::object_ptr &&scheduling_state, + Promise &&promise); + void set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score, bool force, Promise &&promise); @@ -411,6 +440,8 @@ class MessagesManager : public Actor { void send_dialog_action(DialogId dialog_id, const tl_object_ptr &action, Promise &&promise); + void set_dialog_folder_id(DialogId dialog_id, FolderId folder_id, Promise &&promise); + void set_dialog_photo(DialogId dialog_id, const tl_object_ptr &photo, Promise &&promise); void set_dialog_title(DialogId dialog_id, const string &title, Promise &&promise); @@ -439,12 +470,14 @@ class MessagesManager : public Actor { int64 &random_id, bool force, Promise &&promise); - vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); + vector get_dialog_administrators(DialogId dialog_id, int left_tries, Promise &&promise); void export_dialog_invite_link(DialogId dialog_id, Promise &&promise); string get_dialog_invite_link(DialogId dialog_id); + void get_dialog_info_full(DialogId dialog_id, Promise &&promise); + int64 get_dialog_event_log(DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, const tl_object_ptr &filters, const vector &user_ids, Promise &&promise); @@ -461,7 +494,7 @@ class MessagesManager : public Actor { void load_dialogs(vector dialog_ids, Promise &&promise); - vector get_dialogs(DialogDate offset, int32 limit, bool force, Promise &&promise); + vector get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, Promise &&promise); vector search_public_dialogs(const string &query, Promise &&promise); @@ -474,7 +507,7 @@ class MessagesManager : public Actor { vector get_common_dialogs(UserId user_id, DialogId offset_dialog_id, int32 limit, bool force, Promise &&promise); - bool have_message(FullMessageId full_message_id, const char *source); + bool have_message_force(FullMessageId full_message_id, const char *source); void get_message(FullMessageId full_message_id, Promise &&promise); @@ -518,13 +551,15 @@ class MessagesManager : public Actor { void clear_all_draft_messages(bool exclude_secret_chats, Promise &&promise); + void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned); + Status toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinned) TD_WARN_UNUSED_RESULT; Status toggle_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread) TD_WARN_UNUSED_RESULT; Status toggle_dialog_silent_send_message(DialogId dialog_id, bool silent_send_message) TD_WARN_UNUSED_RESULT; - Status set_pinned_dialogs(vector dialog_ids) TD_WARN_UNUSED_RESULT; + Status set_pinned_dialogs(FolderId folder_id, vector dialog_ids) TD_WARN_UNUSED_RESULT; Status set_dialog_client_data(DialogId dialog_id, string &&client_data) TD_WARN_UNUSED_RESULT; @@ -533,8 +568,8 @@ class MessagesManager : public Actor { DialogId create_new_group_chat(const vector &user_ids, const string &title, int64 &random_id, Promise &&promise); - DialogId create_new_channel_chat(const string &title, bool is_megagroup, const string &description, int64 &random_id, - Promise &&promise); + DialogId create_new_channel_chat(const string &title, bool is_megagroup, const string &description, + const DialogLocation &location, int64 &random_id, Promise &&promise); void create_new_secret_chat(UserId user_id, Promise &&promise); @@ -585,7 +620,8 @@ class MessagesManager : public Actor { DialogId dialog_id, const string &query, int64 from_search_id, int32 limit, const tl_object_ptr &filter, int64 &random_id, Promise<> &&promise); - std::pair> search_messages(const string &query, int32 offset_date, + std::pair> search_messages(FolderId folder_id, bool ignore_folder_id, + const string &query, int32 offset_date, DialogId offset_dialog_id, MessageId offset_message_id, int32 limit, int64 &random_id, Promise &&promise); @@ -607,6 +643,8 @@ class MessagesManager : public Actor { int32 get_dialog_message_count(DialogId dialog_id, const tl_object_ptr &filter, bool return_local, int64 &random_id, Promise &&promise); + vector get_dialog_scheduled_messages(DialogId dialog_id, Promise &&promise); + tl_object_ptr get_dialog_message_by_date_object(int64 random_id); tl_object_ptr get_message_object(FullMessageId full_message_id); @@ -631,6 +669,10 @@ class MessagesManager : public Actor { void on_dialog_username_updated(DialogId dialog_id, const string &old_username, const string &new_username); void on_dialog_permissions_updated(DialogId dialog_id); + void on_dialog_user_is_contact_updated(DialogId dialog_id, bool is_contact); + void on_dialog_user_is_blocked_updated(DialogId dialog_id, bool is_blocked); + void on_dialog_user_is_deleted_updated(DialogId dialog_id, bool is_deleted); + void on_resolved_username(const string &username, DialogId dialog_id); void drop_username(const string &username); @@ -647,17 +689,26 @@ class MessagesManager : public Actor { void on_update_scope_notify_settings(NotificationSettingsScope scope, tl_object_ptr &&peer_notify_settings); - bool get_dialog_report_spam_state(DialogId dialog_id, Promise &&promise); + void hide_dialog_action_bar(DialogId dialog_id); - void change_dialog_report_spam_state(DialogId dialog_id, bool is_spam_dialog, Promise &&promise); + void remove_dialog_action_bar(DialogId dialog_id, Promise &&promise); + + void repair_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); + void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings, + bool ignore_privacy_exception = false); + void get_dialog_statistics_url(DialogId dialog_id, const string ¶meters, bool is_dark, Promise> &&promise); - void on_get_peer_settings(DialogId dialog_id, tl_object_ptr &&peer_settings); + void get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id, + Promise> &&promise); + + void get_login_url(DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access, + Promise> &&promise); void before_get_difference(); @@ -696,7 +747,8 @@ class MessagesManager : public Actor { void on_get_channel_difference(DialogId dialog_id, int32 request_pts, int32 request_limit, tl_object_ptr &&difference_ptr); - void force_create_dialog(DialogId dialog_id, const char *source, bool force_update_dialog_pos = false); + void force_create_dialog(DialogId dialog_id, const char *source, bool expect_no_access = false, + bool force_update_dialog_pos = false); void send_get_dialog_notification_settings_query(DialogId dialog_id, Promise &&promise); @@ -719,8 +771,9 @@ class MessagesManager : public Actor { }; Result get_message_push_notification_info(DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id, - int32 date, bool contains_mention, - bool is_pinned, bool is_from_binlog); + int32 date, bool is_from_scheduled, + bool contains_mention, bool is_pinned, + bool is_from_binlog); struct MessageNotificationGroup { DialogId dialog_id; @@ -797,6 +850,7 @@ class MessagesManager : public Actor { int32 views = 0; int32 flags = 0; int32 edit_date = 0; + vector restriction_reasons; string author_signature; int64 media_album_id = 0; @@ -848,7 +902,7 @@ class MessagesManager : public Actor { } }; - // Do not forget to update MessagesManager::update_message when this class is changed + // Do not forget to update MessagesManager::update_message and all make_unique when this class is changed struct Message { int32 random_y; @@ -867,6 +921,8 @@ class MessagesManager : public Actor { UserId via_bot_user_id; + vector restriction_reasons; + string author_signature; bool is_channel_post = false; @@ -875,11 +931,14 @@ class MessagesManager : public Actor { bool disable_notification = false; bool contains_mention = false; bool contains_unread_mention = false; + bool hide_edit_date = false; bool had_reply_markup = false; // had non-inline reply markup? bool had_forward_info = false; bool is_content_secret = false; // should be shown only while tapped bool is_mention_notification_disabled = false; + bool is_from_scheduled = false; + bool is_copy = false; // for send_message bool from_background = false; // for send_message bool disable_web_page_preview = false; // for send_message bool clear_draft = false; // for send_message @@ -913,6 +972,7 @@ class MessagesManager : public Actor { unique_ptr reply_markup; + int32 edited_schedule_date = 0; unique_ptr edited_content; unique_ptr edited_reply_markup; uint64 edit_generation = 0; @@ -982,11 +1042,14 @@ class MessagesManager : public Actor { uint64 save_notification_settings_logevent_id_generation = 0; uint64 read_history_logevent_id = 0; uint64 read_history_logevent_id_generation = 0; + uint64 set_folder_id_logevent_id = 0; + uint64 set_folder_id_logevent_id_generation = 0; + FolderId folder_id; MessageId - last_read_all_mentions_message_id; // all mentions with a message id not greater than it are implicitly read + last_read_all_mentions_message_id; // all mentions with a message identifier not greater than it are implicitly read MessageId - max_unavailable_message_id; // maximal unavailable message id for dialogs with cleared/unavailable history + max_unavailable_message_id; // maximum unavailable message identifier for dialogs with cleared/unavailable history int32 last_clear_history_date = 0; MessageId last_clear_history_message_id; @@ -997,6 +1060,7 @@ class MessagesManager : public Actor { int32 pending_last_message_date = 0; MessageId pending_last_message_id; MessageId max_notification_message_id; + uint32 scheduled_messages_sync_generation = 0; MessageId max_added_message_id; MessageId being_added_message_id; @@ -1020,6 +1084,11 @@ class MessagesManager : public Actor { bool know_can_report_spam = false; bool can_report_spam = false; + bool know_action_bar = false; + bool can_add_contact = false; + bool can_block_user = false; + bool can_share_phone_number = false; + bool can_report_location = false; bool is_opened = false; @@ -1031,8 +1100,14 @@ class MessagesManager : public Actor { bool is_last_read_inbox_message_id_inited = false; bool is_last_read_outbox_message_id_inited = false; bool is_pinned_message_id_inited = false; + bool is_folder_id_inited = false; bool need_repair_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 increment_view_counter = false; @@ -1041,15 +1116,21 @@ class MessagesManager : public Actor { int32 pts = 0; // for channels only std::multimap postponed_channel_updates; // for channels only int32 retry_get_difference_timeout = 1; // for channels only + int32 pending_read_channel_inbox_pts = 0; // for channels only + MessageId pending_read_channel_inbox_max_message_id; // for channels only + int32 pending_read_channel_inbox_server_unread_count = 0; // for channels only std::unordered_map random_id_to_message_id; // for secret chats only MessageId last_assigned_message_id; // identifier of the last local or yet unsent message, assigned after // application start, used to guarantee that all assigned message identifiers // are different + std::unordered_map scheduled_message_date; + std::unordered_map yet_unsent_message_id_to_persistent_message_id; std::unordered_set deleted_message_ids; + std::unordered_set deleted_scheduled_server_message_ids; std::vector> pending_new_message_notifications; std::vector> pending_new_mention_notifications; @@ -1066,9 +1147,11 @@ class MessagesManager : public Actor { bool suffix_load_done_ = false; bool suffix_load_has_query_ = false; + std::unordered_map pending_viewed_live_locations; // message_id -> task_id std::unordered_set pending_viewed_message_ids; - unique_ptr messages = nullptr; + unique_ptr messages; + unique_ptr scheduled_messages; struct MessageOp { enum : int8 { Add, SetPts, Delete, DeleteAll } type; @@ -1120,6 +1203,38 @@ class MessagesManager : public Actor { void parse(ParserT &parser); }; + struct DialogList { + FolderId folder_id; + bool is_message_unread_count_inited_ = false; + bool is_dialog_unread_count_inited_ = false; + bool need_unread_count_recalc_ = true; + int32 unread_message_total_count_ = 0; + int32 unread_message_muted_count_ = 0; + int32 unread_dialog_total_count_ = 0; + int32 unread_dialog_muted_count_ = 0; + int32 unread_dialog_marked_count_ = 0; + int32 unread_dialog_muted_marked_count_ = 0; + int32 in_memory_dialog_total_count_ = 0; + 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_) + DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory + + // date of last known user/group/channel dialog in the right order + DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE; + DialogDate last_loaded_database_dialog_date_ = MIN_DIALOG_DATE; + DialogDate last_database_server_dialog_date_ = MIN_DIALOG_DATE; + + MultiPromiseActor load_dialog_list_multipromise_{ + "LoadDialogListMultiPromiseActor"}; // should be defined before pending_on_get_dialogs_ + int32 load_dialog_list_limit_max_ = 0; + }; + class MessagesIteratorBase { vector stack_; @@ -1132,7 +1247,7 @@ class MessagesManager : public Actor { while (root != nullptr) { // LOG(DEBUG) << "Have root->message_id = " << root->message_id; stack_.push_back(root); - if (root->message_id.get() <= message_id.get()) { + if (root->message_id <= message_id) { // LOG(DEBUG) << "Go right"; last_right_pos = stack_.size(); root = root->right.get(); @@ -1223,7 +1338,9 @@ class MessagesManager : public Actor { public: MessagesIterator() = default; - MessagesIterator(Dialog *d, MessageId message_id) : MessagesIteratorBase(d->messages.get(), message_id) { + MessagesIterator(Dialog *d, MessageId message_id) + : MessagesIteratorBase(message_id.is_scheduled() ? d->scheduled_messages.get() : d->messages.get(), + message_id) { } Message *operator*() const { @@ -1235,7 +1352,9 @@ class MessagesManager : public Actor { public: MessagesConstIterator() = default; - MessagesConstIterator(const Dialog *d, MessageId message_id) : MessagesIteratorBase(d->messages.get(), message_id) { + MessagesConstIterator(const Dialog *d, MessageId message_id) + : MessagesIteratorBase(message_id.is_scheduled() ? d->scheduled_messages.get() : d->messages.get(), + message_id) { } const Message *operator*() const { @@ -1259,29 +1378,42 @@ class MessagesManager : public Actor { Promise<> success_promise; }; + struct SendMessageOptions { + bool disable_notification = false; + bool from_background = false; + int32 schedule_date = 0; + + SendMessageOptions() = default; + SendMessageOptions(bool disable_notification, bool from_background, int32 schedule_date) + : disable_notification(disable_notification), from_background(from_background), schedule_date(schedule_date) { + } + }; + + class ChangeDialogReportSpamStateOnServerLogEvent; class DeleteAllChannelMessagesFromUserOnServerLogEvent; class DeleteDialogHistoryFromServerLogEvent; class DeleteMessageLogEvent; class DeleteMessagesFromServerLogEvent; + class DeleteScheduledMessagesFromServerLogEvent; class ForwardMessagesLogEvent; class GetChannelDifferenceLogEvent; + class GetDialogFromServerLogEvent; class ReadAllDialogMentionsOnServerLogEvent; class ReadHistoryOnServerLogEvent; class ReadHistoryInSecretChatLogEvent; class ReadMessageContentsOnServerLogEvent; class ReorderPinnedDialogsOnServerLogEvent; - class SaveDialogDraftMessageOnServerLogEvent; - class UpdateDialogNotificationSettingsOnServerLogEvent; - class UpdateScopeNotificationSettingsOnServerLogEvent; class ResetAllNotificationSettingsOnServerLogEvent; - class ChangeDialogReportSpamStateOnServerLogEvent; + class SaveDialogDraftMessageOnServerLogEvent; class SendBotStartMessageLogEvent; class SendInlineQueryResultMessageLogEvent; class SendMessageLogEvent; class SendScreenshotTakenNotificationMessageLogEvent; + class SetDialogFolderIdOnServerLogEvent; class ToggleDialogIsPinnedOnServerLogEvent; class ToggleDialogIsMarkedAsUnreadOnServerLogEvent; - class GetDialogFromServerLogEvent; + class UpdateDialogNotificationSettingsOnServerLogEvent; + class UpdateScopeNotificationSettingsOnServerLogEvent; static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server side limit static constexpr int32 MAX_GET_DIALOGS = 100; // server side limit @@ -1306,12 +1438,15 @@ class MessagesManager : public Actor { static constexpr int32 DIALOG_FLAG_HAS_PTS = 1 << 0; static constexpr int32 DIALOG_FLAG_HAS_DRAFT = 1 << 1; static constexpr int32 DIALOG_FLAG_IS_PINNED = 1 << 2; + static constexpr int32 DIALOG_FLAG_HAS_FOLDER_ID = 1 << 4; static constexpr int32 MAX_MESSAGE_VIEW_DELAY = 1; // seconds static constexpr int32 MIN_SAVE_DRAFT_DELAY = 1; // seconds static constexpr int32 MIN_READ_HISTORY_DELAY = 3; // seconds static constexpr int32 MAX_SAVE_DIALOG_DELAY = 0; // seconds + static constexpr int32 LIVE_LOCATION_VIEW_PERIOD = 60; // seconds, server-side limit + static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 3 * 86400; static constexpr int32 USERNAME_CACHE_EXPIRE_TIME_SHORT = 900; @@ -1321,12 +1456,22 @@ class MessagesManager : public Actor { static constexpr int32 MAX_PRELOADED_DIALOGS = 1000; + static constexpr int32 SCHEDULE_WHEN_ONLINE_DATE = 2147483646; + static constexpr double DIALOG_ACTION_TIMEOUT = 5.5; static constexpr const char *DELETE_MESSAGE_USER_REQUEST_SOURCE = "user request"; static constexpr bool DROP_UPDATES = false; + static MessageId get_message_id(const tl_object_ptr &message_ptr, bool is_scheduled); + + FullMessageId get_full_message_id(const tl_object_ptr &message_ptr, bool is_scheduled) const; + + static int32 get_message_date(const tl_object_ptr &message_ptr); + + static tl_object_ptr get_input_message(MessageId message_id); + static bool is_dialog_inited(const Dialog *d); int32 get_dialog_mute_until(const Dialog *d) const; @@ -1353,19 +1498,29 @@ class MessagesManager : public Actor { void fix_message_info_dialog_id(MessageInfo &message_info) const; - MessageInfo parse_telegram_api_message(tl_object_ptr message_ptr, const char *source) const; + MessageInfo parse_telegram_api_message(tl_object_ptr message_ptr, bool is_scheduled, + const char *source) const; std::pair> create_message(MessageInfo &&message_info, bool is_channel_message); + MessageId find_old_message_id(DialogId dialog_id, MessageId message_id) const; + FullMessageId on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message, bool have_previous, bool have_next, const char *source); Result process_input_message_content( DialogId dialog_id, tl_object_ptr &&input_message_content); - Message *get_message_to_send(Dialog *d, MessageId reply_to_message_id, bool disable_notification, - bool from_background, unique_ptr &&content, bool *need_update_dialog_pos, - unique_ptr forward_info = nullptr); + Result process_send_message_options(DialogId dialog_id, + tl_object_ptr &&options) const; + + static Status can_use_send_message_options(const SendMessageOptions &options, + const unique_ptr &content, int32 ttl); + static Status can_use_send_message_options(const SendMessageOptions &options, const InputMessageContent &content); + + Message *get_message_to_send(Dialog *d, MessageId reply_to_message_id, const SendMessageOptions &options, + unique_ptr &&content, bool *need_update_dialog_pos, + unique_ptr forward_info = nullptr, bool is_copy = false); int64 begin_send_message(DialogId dialog_id, const Message *m); @@ -1383,8 +1538,8 @@ class MessagesManager : public Actor { void cancel_edit_message_media(DialogId dialog_id, Message *m, Slice error_message); void on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id, - bool was_uploaded, bool was_thumbnail_uploaded, string file_reference, uint64 generation, - Result &&result); + bool was_uploaded, bool was_thumbnail_uploaded, string file_reference, + int32 scheduled_date, uint64 generation, Result &&result); MessageId get_persistent_message_id(const Dialog *d, MessageId message_id) const; @@ -1410,8 +1565,8 @@ class MessagesManager : public Actor { const vector &message_ids, uint64 logevent_id); Result forward_message(DialogId to_dialog_id, DialogId from_dialog_id, MessageId message_id, - bool disable_notification, bool from_background, bool in_game_share, bool send_copy, - bool remove_caption) TD_WARN_UNUSED_RESULT; + tl_object_ptr &&options, bool in_game_share, + bool send_copy, bool remove_caption) TD_WARN_UNUSED_RESULT; void do_send_media(DialogId dialog_id, Message *m, FileId file_id, FileId thumbnail_file_id, tl_object_ptr input_file, @@ -1475,19 +1630,28 @@ class MessagesManager : public Actor { unique_ptr do_delete_message(Dialog *d, MessageId message_id, bool is_permanently_deleted, bool only_from_memory, bool *need_update_dialog_pos, const char *source); - void on_message_deleted(Dialog *d, Message *m, const char *source); + unique_ptr do_delete_scheduled_message(Dialog *d, MessageId message_id, bool is_permanently_deleted, + const char *source); + + void on_message_deleted(Dialog *d, Message *m, bool is_permanently_deleted, const char *source); int32 get_unload_dialog_delay() const; void unload_dialog(DialogId dialog_id); - void delete_all_dialog_messages(Dialog *d, bool remove_from_dialog_list, bool is_permanent); + void delete_all_dialog_messages(Dialog *d, bool remove_from_dialog_list, bool is_permanently_deleted); - void do_delete_all_dialog_messages(Dialog *d, unique_ptr &m, vector &deleted_message_ids); + void do_delete_all_dialog_messages(Dialog *d, unique_ptr &message, bool is_permanently_deleted, + vector &deleted_message_ids); + + void delete_message_from_server(DialogId dialog_id, MessageId message_ids, bool revoke); void delete_messages_from_server(DialogId dialog_id, vector message_ids, bool revoke, uint64 logevent_id, Promise &&promise); + void delete_scheduled_messages_from_server(DialogId dialog_id, vector message_ids, uint64 logevent_id, + Promise &&promise); + void delete_dialog_history_from_server(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, bool revoke, bool allow_error, uint64 logevent_id, Promise &&promise); @@ -1496,15 +1660,15 @@ class MessagesManager : public Actor { void read_all_dialog_mentions_on_server(DialogId dialog_id, uint64 logevent_id, Promise &&promise); - static MessageId find_message_by_date(const unique_ptr &m, int32 date); + static MessageId find_message_by_date(const Message *m, int32 date); - static void find_messages_from_user(const unique_ptr &m, UserId user_id, vector &message_ids); + static void find_messages_from_user(const Message *m, UserId user_id, vector &message_ids); - static void find_unread_mentions(const unique_ptr &m, vector &message_ids); + static void find_unread_mentions(const Message *m, vector &message_ids); - static void find_old_messages(const unique_ptr &m, MessageId max_message_id, vector &message_ids); + static void find_old_messages(const Message *m, MessageId max_message_id, vector &message_ids); - void find_unloadable_messages(const Dialog *d, int32 unload_before_date, const unique_ptr &m, + void find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m, vector &message_ids, int32 &left_to_unload) const; void on_pending_message_views_timeout(DialogId dialog_id); @@ -1519,14 +1683,17 @@ class MessagesManager : public Actor { bool read_message_content(Dialog *d, Message *m, bool is_local_read, const char *source); - void read_message_contents_on_server(DialogId dialog_id, vector message_ids, uint64 logevent_id); + void read_message_contents_on_server(DialogId dialog_id, vector message_ids, uint64 logevent_id, + Promise &&promise, bool skip_logevent = false); - static int32 calc_new_unread_count_from_last_unread(Dialog *d, MessageId max_message_id, MessageType type); + bool has_incoming_notification(DialogId dialog_id, const Message *m) const; - static int32 calc_new_unread_count_from_the_end(Dialog *d, MessageId max_message_id, MessageType type, - int32 hint_unread_count); + int32 calc_new_unread_count_from_last_unread(Dialog *d, MessageId max_message_id, MessageType type) const; - static int32 calc_new_unread_count(Dialog *d, MessageId max_message_id, MessageType type, int32 hint_unread_count); + int32 calc_new_unread_count_from_the_end(Dialog *d, MessageId max_message_id, MessageType type, + int32 hint_unread_count) const; + + int32 calc_new_unread_count(Dialog *d, MessageId max_message_id, MessageType type, int32 hint_unread_count) const; void repair_server_unread_count(DialogId dialog_id, int32 unread_count); @@ -1566,8 +1733,14 @@ class MessagesManager : public Actor { void load_messages(DialogId dialog_id, MessageId from_message_id, int32 offset, int32 limit, int left_tries, bool only_local, Promise &&promise); + void load_dialog_scheduled_messages(DialogId dialog_id, bool from_database, int32 hash, Promise &&promise); + + void on_get_scheduled_messages_from_database(DialogId dialog_id, vector &&messages); + static int32 get_random_y(MessageId message_id); + static void set_message_id(unique_ptr &message, MessageId message_id); + bool is_allowed_useless_update(const tl_object_ptr &update) const; bool is_message_auto_read(DialogId dialog_id, bool is_outgoing) const; @@ -1588,11 +1761,11 @@ class MessagesManager : public Actor { void try_reuse_notification_group(NotificationGroupInfo &group_info); - void load_dialog_list(int32 limit, bool only_local, Promise &&promise); + void load_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise &&promise); - void load_dialog_list_from_database(int32 limit, Promise &&promise); + void load_dialog_list_from_database(FolderId folder_id, int32 limit, Promise &&promise); - static void preload_dialog_list(void *messages_manager_void); + void preload_dialog_list(FolderId folderId); void update_message_count_by_index(Dialog *d, int diff, const Message *m); @@ -1606,11 +1779,14 @@ class MessagesManager : public Actor { Message *add_message_to_dialog(Dialog *d, unique_ptr message, bool from_update, bool *need_update, bool *need_update_dialog_pos, const char *source); + Message *add_scheduled_message_to_dialog(Dialog *d, unique_ptr message, bool from_update, bool *need_update, + const char *source); + void on_message_changed(const Dialog *d, const Message *m, bool need_send_update, const char *source); bool need_delete_file(FullMessageId full_message_id, FileId file_id) const; - bool need_delete_message_files(Dialog *d, const Message *m) const; + bool need_delete_message_files(DialogId dialog_id, const Message *m) const; void add_message_to_database(const Dialog *d, const Message *m, const char *source); @@ -1647,8 +1823,7 @@ class MessagesManager : public Actor { void attach_message_to_next(Dialog *d, MessageId message_id, const char *source); - bool update_message(Dialog *d, unique_ptr &old_message, unique_ptr new_message, - bool *need_update_dialog_pos); + bool update_message(Dialog *d, Message *old_message, unique_ptr new_message, bool *need_update_dialog_pos); static bool need_message_changed_warning(const Message *old_message); @@ -1714,6 +1889,8 @@ class MessagesManager : public Actor { void send_update_message_edited(DialogId dialog_id, const Message *m); + void send_update_message_live_location_viewed(FullMessageId full_message_id); + void send_update_delete_messages(DialogId dialog_id, vector &&message_ids, bool is_permanent, bool from_cache) const; @@ -1725,9 +1902,9 @@ class MessagesManager : public Actor { void send_update_chat_last_message_impl(const Dialog *d, const char *source) const; - void send_update_unread_message_count(DialogId dialog_id, bool force, const char *source); + void send_update_unread_message_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source); - void send_update_unread_chat_count(DialogId dialog_id, bool force, const char *source); + void send_update_unread_chat_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source); void send_update_chat_read_inbox(const Dialog *d, bool force, const char *source); @@ -1739,6 +1916,20 @@ class MessagesManager : public Actor { void send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const; + void send_update_chat_chat_list(const Dialog *d) const; + + void send_update_chat_action_bar(const Dialog *d); + + void send_update_chat_has_scheduled_messages(Dialog *d); + + void hide_dialog_action_bar(Dialog *d); + + static Result get_message_schedule_date(td_api::object_ptr &&scheduling_state); + + static tl_object_ptr get_message_sending_state_object(const Message *m); + + static tl_object_ptr get_message_scheduling_state_object(int32 send_date); + tl_object_ptr get_message_object(DialogId dialog_id, const Message *m, bool for_event_log = false) const; @@ -1751,11 +1942,21 @@ class MessagesManager : public Actor { static bool need_unread_counter(int64 dialog_order); - void recalc_unread_count(); + static int32 get_dialog_total_count(const DialogList &list); - td_api::object_ptr get_update_unread_message_count_object() const; + void repair_server_dialog_total_count(FolderId folder_id); - td_api::object_ptr get_update_unread_chat_count_object() const; + void repair_secret_chat_total_count(FolderId folder_id); + + void on_get_secret_chat_total_count(FolderId folder_id, int32 total_count); + + void recalc_unread_count(FolderId folder_id); + + td_api::object_ptr get_update_unread_message_count_object( + FolderId folder_id, const DialogList &list) const; + + td_api::object_ptr get_update_unread_chat_count_object(FolderId folder_id, + 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); @@ -1776,23 +1977,31 @@ class MessagesManager : public Actor { void set_dialog_is_empty(Dialog *d, const char *source); - static int32 get_pinned_dialogs_limit(); + static int32 get_pinned_dialogs_limit(FolderId folder_id); static vector remove_secret_chat_dialog_ids(vector dialog_ids); - void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned); - void set_dialog_is_pinned(Dialog *d, bool is_pinned); void set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_as_unread); void set_dialog_pinned_message_id(Dialog *d, MessageId pinned_message_id); + void repair_dialog_scheduled_messages(DialogId dialog_id); + + void set_dialog_has_scheduled_server_messages(Dialog *d, bool has_scheduled_server_messages); + + void set_dialog_has_scheduled_database_messages(DialogId dialog_id, bool has_scheduled_database_messages); + + void set_dialog_has_scheduled_database_messages_impl(Dialog *d, bool has_scheduled_database_messages); + + void set_dialog_folder_id(Dialog *d, FolderId folder_id); + void toggle_dialog_is_pinned_on_server(DialogId dialog_id, bool is_pinned, uint64 logevent_id); void toggle_dialog_is_marked_as_unread_on_server(DialogId dialog_id, bool is_marked_as_unread, uint64 logevent_id); - void reorder_pinned_dialogs_on_server(const vector &dialog_ids, uint64 logevent_id); + void reorder_pinned_dialogs_on_server(FolderId folder_id, const vector &dialog_ids, uint64 logevent_id); void set_dialog_reply_markup(Dialog *d, MessageId message_id); @@ -1852,9 +2061,17 @@ class MessagesManager : public Actor { void add_dialog_last_database_message(Dialog *d, unique_ptr &&last_database_message); - tl_object_ptr get_chat_type_object(DialogId dialog_id) const; + void fix_dialog_action_bar(Dialog *d); - tl_object_ptr get_chat_object(const Dialog *d) const; + td_api::object_ptr get_chat_type_object(DialogId dialog_id) const; + + static td_api::object_ptr get_chat_list_object(const Dialog *d); + + static td_api::object_ptr get_chat_list_object(FolderId folder_id); + + td_api::object_ptr get_chat_action_bar_object(const Dialog *d) const; + + td_api::object_ptr get_chat_object(const Dialog *d) const; bool have_dialog_info(DialogId dialog_id) const; bool have_dialog_info_force(DialogId dialog_id) const; @@ -1866,27 +2083,32 @@ class MessagesManager : public Actor { Dialog *on_load_dialog_from_database(DialogId dialog_id, const BufferSlice &value); - void on_get_dialogs_from_database(int32 limit, vector &&dialogs, Promise &&promise); + void on_get_dialogs_from_database(FolderId folder_id, int32 limit, DialogDbGetDialogsResult &&dialogs, + Promise &&promise); void send_get_dialog_query(DialogId dialog_id, Promise &&promise, uint64 logevent_id = 0); void send_search_public_dialogs_query(const string &query, Promise &&promise); - vector get_pinned_dialogs() const; + vector get_pinned_dialogs(FolderId folder_id) const; - void reload_pinned_dialogs(Promise &&promise); + void reload_pinned_dialogs(FolderId folder_id, Promise &&promise); void update_dialogs_hints(const Dialog *d); void update_dialogs_hints_rating(const Dialog *d); + DialogList &get_dialog_list(FolderId folder_id); + const DialogList *get_dialog_list(FolderId folder_id) const; + std::pair> search_private_chat_participants(UserId my_user_id, UserId peer_user_id, const string &query, int32 limit, DialogParticipantsFilter filter) const; - static unique_ptr *find_message(unique_ptr *v, MessageId message_id); - static const unique_ptr *find_message(const unique_ptr *v, MessageId message_id); + static unique_ptr *treap_find_message(unique_ptr *v, MessageId message_id); + static const unique_ptr *treap_find_message(const unique_ptr *v, MessageId message_id); - static Message *insert_message(unique_ptr *v, unique_ptr message); + static Message *treap_insert_message(unique_ptr *v, unique_ptr message); + static unique_ptr treap_delete_message(unique_ptr *v); static Message *get_message(Dialog *d, MessageId message_id); static const Message *get_message(const Dialog *d, MessageId message_id); @@ -1901,7 +2123,8 @@ class MessagesManager : public Actor { void get_message_force_from_server(Dialog *d, MessageId message_id, Promise &&promise, tl_object_ptr input_message = nullptr); - Message *on_get_message_from_database(DialogId dialog_id, Dialog *d, const BufferSlice &value, const char *source); + Message *on_get_message_from_database(DialogId dialog_id, Dialog *d, const BufferSlice &value, bool is_scheduled, + const char *source); void get_dialog_message_by_date_from_server(const Dialog *d, int32 date, int64 random_id, bool after_database_search, Promise &&promise); @@ -1927,7 +2150,9 @@ class MessagesManager : public Actor { static void cancel_upload_file(FileId file_id); - void cancel_send_message_query(DialogId dialog_id, unique_ptr &m); + void cancel_send_message_query(DialogId dialog_id, Message *m); + + void cancel_send_deleted_message(DialogId dialog_id, Message *m, bool is_permanently_deleted); static int32 get_message_flags(const Message *m); @@ -1991,10 +2216,20 @@ class MessagesManager : public Actor { void save_active_live_locations(); + void on_message_live_location_viewed(Dialog *d, const Message *m); + + void view_message_live_location_on_server(int64 task_id); + + void view_message_live_location_on_server_impl(int64 task_id, FullMessageId full_message_id); + + void on_message_live_location_viewed_on_server(int64 task_id); + void add_message_file_sources(DialogId dialog_id, const Message *m); void remove_message_file_sources(DialogId dialog_id, const Message *m); + void change_message_files(DialogId dialog_id, const Message *m, const vector &old_file_ids); + Result> get_dialog_reply_markup( DialogId dialog_id, tl_object_ptr &&reply_markup_ptr) const TD_WARN_UNUSED_RESULT; @@ -2008,6 +2243,8 @@ class MessagesManager : public Actor { static int64 get_dialog_order(MessageId message_id, int32 message_date); + int64 get_dialog_public_order(const Dialog *d) const; + bool update_dialog_draft_message(Dialog *d, unique_ptr &&draft_message, bool from_update, bool need_update_dialog_pos); @@ -2028,6 +2265,10 @@ class MessagesManager : public Actor { void change_dialog_report_spam_state_on_server(DialogId dialog_id, bool is_spam_dialog, uint64 logevent_id, Promise &&promise); + void set_dialog_folder_id_on_server(DialogId dialog_id, bool from_binlog); + + void on_updated_dialog_folder_id(DialogId dialog_id, uint64 generation); + int64 get_next_pinned_dialog_order(); void update_dialog_pos(Dialog *d, bool remove_from_dialog_list, const char *source, @@ -2036,7 +2277,7 @@ class MessagesManager : public Actor { bool set_dialog_order(Dialog *d, int64 new_order, bool need_send_update_chat_order, bool is_loaded_from_database, const char *source); - void update_last_dialog_date(); + void update_last_dialog_date(FolderId folder_id); void load_notification_settings(); @@ -2053,7 +2294,7 @@ class MessagesManager : public Actor { int32 load_channel_pts(DialogId dialog_id) const; - void set_channel_pts(Dialog *d, int32 new_pts, const char *source) const; + void set_channel_pts(Dialog *d, int32 new_pts, const char *source); bool running_get_channel_difference(DialogId dialog_id) const; @@ -2079,6 +2320,8 @@ class MessagesManager : public Actor { static void on_pending_message_views_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int); + static void on_pending_message_live_location_view_timeout_callback(void *messages_manager_ptr, int64 task_id); + static void on_pending_draft_message_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int); static void on_pending_read_history_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int); @@ -2095,6 +2338,8 @@ class MessagesManager : public Actor { static void on_update_dialog_online_member_count_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int); + static void on_preload_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int); + void load_secret_thumbnail(FileId thumbnail_file_id); static tl_object_ptr get_channel_admin_log_events_filter( @@ -2123,12 +2368,14 @@ class MessagesManager : public Actor { Dialog *get_service_notifications_dialog(); - static MessageId get_next_message_id(Dialog *d, int32 type); + 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); + bool add_recently_found_dialog_internal(DialogId dialog_id); bool remove_recently_found_dialog_internal(DialogId dialog_id); @@ -2136,11 +2383,19 @@ class MessagesManager : public Actor { void save_recently_found_dialogs(); bool load_recently_found_dialogs(Promise &promise); + void reget_message_from_server_if_needed(DialogId dialog_id, const Message *m); + + void speculatively_update_channel_participants(DialogId dialog_id, const Message *m); + + void update_sent_message_contents(DialogId dialog_id, const Message *m); + void update_used_hashtags(DialogId dialog_id, const Message *m); + void update_top_dialogs(DialogId dialog_id, const Message *m); + string get_search_text(const Message *m) const; - unique_ptr parse_message(DialogId dialog_id, const BufferSlice &value); + unique_ptr parse_message(DialogId dialog_id, const BufferSlice &value, bool is_scheduled); unique_ptr parse_dialog(DialogId dialog_id, const BufferSlice &value); @@ -2162,6 +2417,8 @@ class MessagesManager : public Actor { uint64 save_delete_messages_from_server_logevent(DialogId dialog_id, const vector &message_ids, bool revoke); + uint64 save_delete_scheduled_messages_from_server_logevent(DialogId dialog_id, const vector &message_ids); + uint64 save_delete_dialog_history_from_server_logevent(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list, bool revoke); @@ -2171,7 +2428,7 @@ class MessagesManager : public Actor { uint64 save_toggle_dialog_is_pinned_on_server_logevent(DialogId dialog_id, bool is_pinned); - uint64 save_reorder_pinned_dialogs_on_server_logevent(const vector &dialog_ids); + uint64 save_reorder_pinned_dialogs_on_server_logevent(FolderId folder_id, const vector &dialog_ids); uint64 save_toggle_dialog_is_marked_as_unread_on_server_logevent(DialogId dialog_id, bool is_marked_as_unread); @@ -2193,10 +2450,14 @@ class MessagesManager : public Actor { void suffix_load_till_date(Dialog *d, int32 date, Promise<> promise); void suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<> promise); + Result get_login_button_url(DialogId dialog_id, MessageId message_id, int32 button_id); + Result get_invoice_message_id(FullMessageId full_message_id); bool is_broadcast_channel(DialogId dialog_id) const; + static int32 get_message_schedule_date(const Message *m); + int32 recently_found_dialogs_loaded_ = 0; // 0 - not loaded, 1 - load request was sent, 2 - loaded MultiPromiseActor resolve_recently_found_dialogs_multipromise_{"ResolveRecentlyFoundDialogsMultiPromiseActor"}; @@ -2268,7 +2529,10 @@ class MessagesManager : public Actor { std::unordered_map being_sent_messages_; // message_random_id -> message std::unordered_map - update_message_ids_; // full_message_id -> temporary_id + update_message_ids_; // new_message_id -> temporary_id + std::unordered_map, + DialogIdHash> + update_scheduled_message_ids_; // new_message_id -> temporary_id std::unordered_map debug_being_sent_messages_; // message_random_id -> dialog_id const char *debug_add_message_to_dialog_fail_reason_ = ""; @@ -2377,39 +2641,16 @@ class MessagesManager : public Actor { std::unordered_map notification_group_id_to_dialog_id_; + uint64 current_message_edit_generation_ = 0; + bool include_sponsored_dialog_to_unread_count_ = false; - bool have_postponed_unread_message_count_update_ = false; - bool have_postponed_unread_chat_count_update_ = false; - bool is_message_unread_count_inited_ = false; - bool is_dialog_unread_count_inited_ = false; - bool need_unread_count_recalc_ = true; - int32 unread_message_total_count_ = 0; - int32 unread_message_muted_count_ = 0; - int32 unread_dialog_total_count_ = 0; - int32 unread_dialog_muted_count_ = 0; - int32 unread_dialog_marked_count_ = 0; - int32 unread_dialog_muted_marked_count_ = 0; + + std::unordered_set postponed_unread_message_count_updates_; + std::unordered_set postponed_unread_chat_count_updates_; int64 current_pinned_dialog_order_ = DEFAULT_ORDER; - uint64 current_message_edit_generation_ = 0; - - std::set ordered_dialogs_; - std::set ordered_server_dialogs_; - - // date of last dialog in the dialog list - // last_dialog_date_ == min(last_server_dialog_date_, last_secret_chat_dialog_date_) - DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory - - // date of last known user/group/channel dialog in the right order - DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE; - DialogDate last_loaded_database_dialog_date_ = MIN_DIALOG_DATE; - DialogDate last_database_server_dialog_date_ = MIN_DIALOG_DATE; - - MultiPromiseActor load_dialog_list_multipromise_{ - "LoadDialogListMultiPromiseActor"}; // should be defined before pending_on_get_dialogs_ - int32 load_dialog_list_limit_max_ = 0; - Timeout preload_dialog_list_timeout_; + std::unordered_map dialog_lists_; std::unordered_map active_get_channel_differencies_; std::unordered_map get_channel_difference_to_logevent_id_; @@ -2417,6 +2658,7 @@ class MessagesManager : public Actor { MultiTimeout channel_get_difference_timeout_{"ChannelGetDifferenceTimeout"}; MultiTimeout channel_get_difference_retry_timeout_{"ChannelGetDifferenceRetryTimeout"}; MultiTimeout pending_message_views_timeout_{"PendingMessageViewsTimeout"}; + MultiTimeout pending_message_live_location_view_timeout_{"PendingMessageLiveLocationViewTimeout"}; MultiTimeout pending_draft_message_timeout_{"PendingDraftMessageTimeout"}; MultiTimeout pending_read_history_timeout_{"PendingReadHistoryTimeout"}; MultiTimeout pending_updated_dialog_timeout_{"PendingUpdatedDialogTimeout"}; @@ -2425,6 +2667,7 @@ class MessagesManager : public Actor { MultiTimeout pending_send_dialog_action_timeout_{"PendingSendDialogActionTimeout"}; MultiTimeout active_dialog_action_timeout_{"ActiveDialogActionTimeout"}; MultiTimeout update_dialog_online_member_count_timeout_{"UpdateDialogOnlineMemberCountTimeout"}; + MultiTimeout preload_dialog_list_timeout_{"PreloadDialogListTimeout"}; Hints dialogs_hints_; // search dialogs by title and username @@ -2432,6 +2675,8 @@ class MessagesManager : public Actor { bool are_active_live_location_messages_loaded_ = false; vector> load_active_live_location_messages_queries_; + std::unordered_map>, DialogIdHash> load_scheduled_messages_from_database_queries_; + struct ResolvedUsername { DialogId dialog_id; double expires_at; @@ -2441,7 +2686,8 @@ class MessagesManager : public Actor { std::unordered_map inaccessible_resolved_usernames_; struct PendingOnGetDialogs { - vector> dialogs; + FolderId folder_id; + vector> dialogs; int32 total_count; vector> messages; Promise promise; @@ -2470,12 +2716,17 @@ class MessagesManager : public Actor { CallsDbState calls_db_state_; + int64 viewed_live_location_task_id_ = 0; + std::unordered_map viewed_live_location_tasks_; // task_id -> task + std::unordered_map>> yet_unsent_media_queues_; std::unordered_map set_typing_query_; std::unordered_map full_message_id_to_file_source_id_; + std::unordered_map last_outgoing_forwarded_message_date_; + struct OnlineMemberCountInfo { int32 online_member_count = 0; double updated_time = 0; @@ -2484,6 +2735,8 @@ class MessagesManager : public Actor { std::unordered_map dialog_online_member_counts_; + uint32 scheduled_messages_sync_generation_ = 1; + DialogId sponsored_dialog_id_; DialogId being_added_dialog_id_; diff --git a/td/telegram/Notification.h b/td/telegram/Notification.h index f6b9e60a..2a5f8715 100644 --- a/td/telegram/Notification.h +++ b/td/telegram/Notification.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationGroupId.h b/td/telegram/NotificationGroupId.h index 56881e8d..5a95578a 100644 --- a/td/telegram/NotificationGroupId.h +++ b/td/telegram/NotificationGroupId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationGroupKey.h b/td/telegram/NotificationGroupKey.h index fa828908..fc22d543 100644 --- a/td/telegram/NotificationGroupKey.h +++ b/td/telegram/NotificationGroupKey.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationGroupType.h b/td/telegram/NotificationGroupType.h index b1a93281..bfe83165 100644 --- a/td/telegram/NotificationGroupType.h +++ b/td/telegram/NotificationGroupType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationId.h b/td/telegram/NotificationId.h index e0cd7834..60b4008d 100644 --- a/td/telegram/NotificationId.h +++ b/td/telegram/NotificationId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index a8d14342..252f01d8 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -24,6 +24,7 @@ #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" #include "td/telegram/SecretChatId.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/StateManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" @@ -362,7 +363,11 @@ void NotificationManager::tear_down() { } NotificationManager::NotificationGroups::iterator NotificationManager::add_group(NotificationGroupKey &&group_key, - NotificationGroup &&group) { + NotificationGroup &&group, + const char *source) { + if (group.notifications.empty()) { + LOG_CHECK(group_key.last_notification_date == 0) << "Trying to add empty " << group_key << " from " << source; + } bool is_inserted = group_keys_.emplace(group_key.group_id, group_key).second; CHECK(is_inserted); return groups_.emplace(std::move(group_key), std::move(group)).first; @@ -392,8 +397,7 @@ NotificationManager::NotificationGroups::iterator NotificationManager::get_group return group_it; } - if (std::find(call_notification_group_ids_.begin(), call_notification_group_ids_.end(), group_id) != - call_notification_group_ids_.end()) { + if (td::contains(call_notification_group_ids_, group_id)) { return groups_.end(); } @@ -438,7 +442,7 @@ NotificationManager::NotificationGroups::iterator NotificationManager::get_group send_add_group_update(group_key, group); } } - return add_group(std::move(group_key), std::move(group)); + return add_group(std::move(group_key), std::move(group), "get_group_force"); } void NotificationManager::delete_group(NotificationGroups::iterator &&group_it) { @@ -598,7 +602,7 @@ void NotificationManager::on_get_message_notifications_from_database(Notificatio } auto first_message_id = get_first_message_id(group); if (first_message_id.is_valid()) { - while (!notifications.empty() && notifications.back().type->get_message_id().get() >= first_message_id.get()) { + while (!notifications.empty() && notifications.back().type->get_message_id() >= first_message_id) { // possible if notifications was added after the database request was sent notifications.pop_back(); } @@ -617,6 +621,10 @@ void NotificationManager::add_notifications_to_group_begin(NotificationGroups::i vector notifications) { CHECK(group_it != groups_.end()); + td::remove_if(notifications, [dialog_id = group_it->first.dialog_id](const Notification ¬ification) { + return notification.type->get_notification_type_object(dialog_id) == nullptr; + }); + if (notifications.empty()) { return; } @@ -672,11 +680,8 @@ void NotificationManager::add_notifications_to_group_begin(NotificationGroups::i added_notifications.reserve(notifications.size()); for (auto ¬ification : notifications) { added_notifications.push_back(get_notification_object(group_key.dialog_id, notification)); - if (added_notifications.back()->type_ == nullptr) { - added_notifications.pop_back(); - } else { - new_notifications.push_back(std::move(notification)); - } + CHECK(added_notifications.back()->type_ != nullptr); + new_notifications.push_back(std::move(notification)); } notifications = std::move(new_notifications); @@ -709,8 +714,9 @@ void NotificationManager::add_notifications_to_group_begin(NotificationGroups::i } if (is_position_changed) { - add_group(std::move(final_group_key), std::move(group)); + add_group(std::move(final_group_key), std::move(group), "add_notifications_to_group_begin"); } else { + CHECK(group_it->first.last_notification_date == 0 || !group.notifications.empty()); group_it->second = std::move(group); } } @@ -865,7 +871,7 @@ void NotificationManager::add_notification(NotificationGroupId group_id, Notific auto group_it = get_group_force(group_id); if (group_it == groups_.end()) { - group_it = add_group(NotificationGroupKey(group_id, dialog_id, 0), NotificationGroup()); + group_it = add_group(NotificationGroupKey(group_id, dialog_id, 0), NotificationGroup(), "add_notification"); } if (group_it->second.notifications.empty() && group_it->second.pending_notifications.empty()) { group_it->second.type = group_type; @@ -880,7 +886,7 @@ void NotificationManager::add_notification(NotificationGroupId group_id, Notific return; } auto message_id = type->get_message_id(); - if (message_id.is_valid() && message_id.get() <= get_last_message_id(group).get()) { + if (message_id.is_valid() && message_id <= get_last_message_id(group)) { LOG(ERROR) << "Failed to add " << notification_id << " of type " << *type << " to " << group_id << " of type " << group_type << " in " << dialog_id << ", because have already added notification about " << get_last_message_id(group); @@ -1263,7 +1269,7 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour auto has_common_notifications = [](const vector> ¬ifications, const vector ¬ification_ids) { for (auto ¬ification : notifications) { - if (std::find(notification_ids.begin(), notification_ids.end(), notification->id_) != notification_ids.end()) { + if (td::contains(notification_ids, notification->id_)) { return true; } } @@ -1379,14 +1385,12 @@ bool NotificationManager::do_flush_pending_notifications(NotificationGroupKey &g Notification notification(pending_notification.notification_id, pending_notification.date, pending_notification.initial_is_silent, std::move(pending_notification.type)); added_notifications.push_back(get_notification_object(group_key.dialog_id, notification)); - if (added_notifications.back()->type_ == nullptr) { - added_notifications.pop_back(); - } else { - if (!notification.type->can_be_delayed()) { - force_update = true; - } - group.notifications.push_back(std::move(notification)); + CHECK(added_notifications.back()->type_ != nullptr); + + if (!notification.type->can_be_delayed()) { + force_update = true; } + group.notifications.push_back(std::move(notification)); } group.total_count += narrow_cast(added_notifications.size()); if (added_notifications.size() > max_notification_group_size_) { @@ -1472,6 +1476,11 @@ void NotificationManager::flush_pending_notifications(NotificationGroupId group_ return; } + td::remove_if(group_it->second.pending_notifications, + [dialog_id = group_it->first.dialog_id](const PendingNotification &pending_notification) { + return pending_notification.type->get_notification_type_object(dialog_id) == nullptr; + }); + if (group_it->second.pending_notifications.empty()) { return; } @@ -1552,7 +1561,7 @@ void NotificationManager::flush_pending_notifications(NotificationGroupId group_ group.is_loaded_from_database = false; } - add_group(std::move(final_group_key), std::move(group)); + add_group(std::move(final_group_key), std::move(group), "flush_pending_notifications"); if (force_update) { if (removed_group_id.is_valid()) { @@ -1733,10 +1742,11 @@ void NotificationManager::on_notifications_removed( } if (is_position_changed) { - add_group(std::move(final_group_key), std::move(group)); + add_group(std::move(final_group_key), std::move(group), "on_notifications_removed"); last_group_key = get_last_updated_group_key(); } else { + CHECK(group_it->first.last_notification_date == 0 || !group.notifications.empty()); group_it->second = std::move(group); } @@ -1961,7 +1971,7 @@ void NotificationManager::remove_notification_group(NotificationGroupId group_id for (auto it = group_it->second.pending_notifications.begin(); it != group_it->second.pending_notifications.end(); ++it) { if (it->notification_id.get() <= max_notification_id.get() || - (max_message_id.is_valid() && it->type->get_message_id().get() <= max_message_id.get())) { + (max_message_id.is_valid() && it->type->get_message_id() <= max_message_id)) { pending_delete_end = it + 1; on_notification_removed(it->notification_id); } @@ -1988,7 +1998,7 @@ void NotificationManager::remove_notification_group(NotificationGroupId group_id for (size_t pos = 0; pos < notification_delete_end; pos++) { auto ¬ification = group_it->second.notifications[pos]; if (notification.notification_id.get() > max_notification_id.get() && - (!max_message_id.is_valid() || notification.type->get_message_id().get() > max_message_id.get())) { + (!max_message_id.is_valid() || notification.type->get_message_id() > max_message_id)) { notification_delete_end = pos; } else { on_notification_removed(notification.notification_id); @@ -2635,12 +2645,13 @@ void NotificationManager::process_push_notification(string payload, Promisemessages_manager_->on_update_some_live_location_viewed(std::move(promise)); + return Status::OK(); + } + if (loc_key == "AUTH_REGION" || loc_key == "AUTH_UNKNOWN") { // TODO return Status::Error(200, "Immediate success"); @@ -3050,7 +3069,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo return Status::Error("Can't find dialog_id"); } - if (loc_key.empty()) { + if (loc_key == "READ_HISTORY") { if (dialog_id.get_type() == DialogType::SecretChat) { return Status::Error("Receive read history in a secret chat"); } @@ -3087,6 +3106,10 @@ Status NotificationManager::process_push_notification_payload(string payload, bo return Status::OK(); } + if (loc_key == "MESSAGE_MUTED") { + return Status::Error(406, "Notifications about muted messages force loading data from the server"); + } + TRY_RESULT(msg_id, get_json_object_int_field(custom, "msg_id")); ServerMessageId server_message_id(msg_id); if (server_message_id != ServerMessageId() && !server_message_id.is_valid()) { @@ -3143,12 +3166,12 @@ Status NotificationManager::process_push_notification_payload(string payload, bo } if (begins_with(loc_key, "ENCRYPTION_")) { - // TODO new secret chat notifications + // TODO ENCRYPTION_REQUEST/ENCRYPTION_ACCEPT notifications return Status::Error(406, "New secret chat notification is not supported"); } if (begins_with(loc_key, "PHONE_CALL_")) { - // TODO phone call request/missed notification + // TODO PHONE_CALL_REQUEST/PHONE_CALL_DECLINE/PHONE_CALL_MISSED notification return Status::Error(406, "Phone call notification is not supported"); } @@ -3199,8 +3222,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo if (mtpeer.type() != JsonValue::Type::Null) { TRY_RESULT(ah, get_json_object_string_field(mtpeer.get_object(), "ah")); if (!ah.empty()) { - TRY_RESULT(sender_access_hash_safe, to_integer_safe(ah)); - sender_access_hash = sender_access_hash_safe; + TRY_RESULT_ASSIGN(sender_access_hash, to_integer_safe(ah)); } TRY_RESULT(ph, get_json_object_field(mtpeer.get_object(), "ph", JsonValue::Type::Object)); if (ph.type() != JsonValue::Type::Null) { @@ -3220,7 +3242,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, sender_name, - string(), string(), string(), std::move(sender_photo), nullptr, 0, string(), string(), string()); + 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"); } @@ -3359,10 +3381,11 @@ Status NotificationManager::process_push_notification_payload(string payload, bo std::move(arg), std::move(attached_photo), std::move(attached_document), 0, std::move(promise)); } else { + bool is_from_scheduled = has_json_object_field(custom, "schedule"); bool is_silent = has_json_object_field(custom, "silent"); add_message_push_notification(dialog_id, MessageId(server_message_id), random_id, sender_user_id, - std::move(sender_name), sent_date, contains_mention, is_silent, is_silent, - std::move(loc_key), std::move(arg), std::move(attached_photo), + std::move(sender_name), sent_date, is_from_scheduled, contains_mention, is_silent, + is_silent, std::move(loc_key), std::move(arg), std::move(attached_photo), std::move(attached_document), NotificationId(), 0, std::move(promise)); } return Status::OK(); @@ -3376,6 +3399,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent { UserId sender_user_id_; string sender_name_; int32 date_; + bool is_from_scheduled_; bool contains_mention_; bool is_silent_; string loc_key_; @@ -3403,6 +3427,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent { STORE_FLAG(has_arg); STORE_FLAG(has_photo); STORE_FLAG(has_document); + STORE_FLAG(is_from_scheduled_); END_STORE_FLAGS(); td::store(dialog_id_, storer); if (has_message_id) { @@ -3450,6 +3475,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent { PARSE_FLAG(has_arg); PARSE_FLAG(has_photo); PARSE_FLAG(has_document); + PARSE_FLAG(is_from_scheduled_); END_PARSE_FLAGS(); td::parse(dialog_id_, parser); if (has_message_id) { @@ -3483,15 +3509,14 @@ class NotificationManager::AddMessagePushNotificationLogEvent { } }; -void NotificationManager::add_message_push_notification(DialogId dialog_id, MessageId message_id, int64 random_id, - UserId sender_user_id, string sender_name, int32 date, - bool contains_mention, bool initial_is_silent, bool is_silent, - string loc_key, string arg, Photo photo, Document document, - NotificationId notification_id, uint64 logevent_id, - Promise promise) { +void NotificationManager::add_message_push_notification( + DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id, string sender_name, int32 date, + bool is_from_scheduled, bool contains_mention, bool initial_is_silent, bool is_silent, string loc_key, string arg, + Photo photo, Document document, NotificationId notification_id, uint64 logevent_id, Promise promise) { auto is_pinned = begins_with(loc_key, "PINNED_"); auto r_info = td_->messages_manager_->get_message_push_notification_info( - dialog_id, message_id, random_id, sender_user_id, date, contains_mention, is_pinned, logevent_id != 0); + dialog_id, message_id, random_id, sender_user_id, date, is_from_scheduled, contains_mention, is_pinned, + logevent_id != 0); if (r_info.is_error()) { VLOG(notifications) << "Don't need message push notification for " << message_id << "/" << random_id << " from " << dialog_id << " sent by " << sender_user_id << " at " << date << ": " << r_info.error(); @@ -3540,14 +3565,15 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, sender_name, string(), - string(), string(), nullptr, nullptr, 0, string(), string(), string()); + string(), string(), nullptr, nullptr, 0, Auto(), string(), string()); td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification"); } if (logevent_id == 0 && G()->parameters().use_message_db) { AddMessagePushNotificationLogEvent logevent{ - dialog_id, message_id, random_id, sender_user_id, sender_name, date, contains_mention, - initial_is_silent, loc_key, arg, photo, document, notification_id}; + dialog_id, message_id, random_id, sender_user_id, sender_name, date, + is_from_scheduled, contains_mention, initial_is_silent, loc_key, arg, photo, + document, notification_id}; auto storer = LogEventStorerImpl(logevent); logevent_id = binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::AddMessagePushNotification, storer); } @@ -4017,9 +4043,9 @@ void NotificationManager::on_binlog_events(vector &&events) { add_message_push_notification( log_event.dialog_id_, log_event.message_id_, log_event.random_id_, log_event.sender_user_id_, - log_event.sender_name_, log_event.date_, log_event.contains_mention_, log_event.is_silent_, true, - log_event.loc_key_, log_event.arg_, log_event.photo_, log_event.document_, log_event.notification_id_, - event.id_, PromiseCreator::lambda([](Result result) { + log_event.sender_name_, log_event.date_, log_event.is_from_scheduled_, log_event.contains_mention_, + log_event.is_silent_, true, log_event.loc_key_, log_event.arg_, log_event.photo_, log_event.document_, + log_event.notification_id_, event.id_, PromiseCreator::lambda([](Result result) { if (result.is_error() && result.error().code() != 200 && result.error().code() != 406) { LOG(ERROR) << "Receive error " << result.error() << ", while processing message push notification"; } diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index d0f59079..7ed05aa7 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,6 +9,7 @@ #include "td/telegram/CallId.h" #include "td/telegram/DialogId.h" #include "td/telegram/Document.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/MessageId.h" #include "td/telegram/Notification.h" #include "td/telegram/NotificationGroupId.h" @@ -218,7 +219,8 @@ class NotificationManager : public Actor { void add_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id, const Notification ¬ification); - NotificationGroups::iterator add_group(NotificationGroupKey &&group_key, NotificationGroup &&group); + NotificationGroups::iterator add_group(NotificationGroupKey &&group_key, NotificationGroup &&group, + const char *source); NotificationGroups::iterator get_group(NotificationGroupId group_id); @@ -305,9 +307,10 @@ class NotificationManager : public Actor { Status process_push_notification_payload(string payload, bool was_encrypted, Promise &promise); void add_message_push_notification(DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id, - string sender_name, int32 date, bool contains_mention, bool initial_is_silent, - bool is_silent, string loc_key, string arg, Photo photo, Document document, - NotificationId notification_id, uint64 logevent_id, Promise promise); + string sender_name, int32 date, bool is_from_scheduled, bool contains_mention, + bool initial_is_silent, bool is_silent, string loc_key, string arg, Photo photo, + Document document, NotificationId notification_id, uint64 logevent_id, + Promise promise); void edit_message_push_notification(DialogId dialog_id, MessageId message_id, int32 edit_date, string loc_key, string arg, Photo photo, Document document, uint64 logevent_id, diff --git a/td/telegram/NotificationSettings.cpp b/td/telegram/NotificationSettings.cpp index 000cb1e6..af94e435 100644 --- a/td/telegram/NotificationSettings.cpp +++ b/td/telegram/NotificationSettings.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -115,6 +115,19 @@ NotificationSettingsScope get_notification_settings_scope( } } +static int32 get_mute_until(int32 mute_for) { + if (mute_for <= 0) { + return 0; + } + + const int32 MAX_PRECISE_MUTE_FOR = 7 * 86400; + int32 current_time = G()->unix_time(); + if (mute_for > MAX_PRECISE_MUTE_FOR || mute_for >= std::numeric_limits::max() - current_time) { + return std::numeric_limits::max(); + } + return mute_for + current_time; +} + Result get_dialog_notification_settings( td_api::object_ptr &¬ification_settings, bool old_silent_send_message) { if (notification_settings == nullptr) { @@ -127,18 +140,8 @@ Result get_dialog_notification_settings( notification_settings->sound_ = "default"; } - int32 current_time = G()->unix_time(); - if (notification_settings->mute_for_ > std::numeric_limits::max() - current_time) { - notification_settings->mute_for_ = std::numeric_limits::max() - current_time; - } - - int32 mute_until; - if (notification_settings->use_default_mute_for_ || notification_settings->mute_for_ <= 0) { - mute_until = 0; - } else { - mute_until = notification_settings->mute_for_ + current_time; - } - + int32 mute_until = + notification_settings->use_default_mute_for_ ? 0 : get_mute_until(notification_settings->mute_for_); return DialogNotificationSettings(notification_settings->use_default_mute_for_, mute_until, notification_settings->use_default_sound_, std::move(notification_settings->sound_), notification_settings->use_default_show_preview_, @@ -161,18 +164,7 @@ Result get_scope_notification_settings( notification_settings->sound_ = "default"; } - int32 current_time = G()->unix_time(); - if (notification_settings->mute_for_ > std::numeric_limits::max() - current_time) { - notification_settings->mute_for_ = std::numeric_limits::max() - current_time; - } - - int32 mute_until; - if (notification_settings->mute_for_ <= 0) { - mute_until = 0; - } else { - mute_until = notification_settings->mute_for_ + current_time; - } - + auto mute_until = get_mute_until(notification_settings->mute_for_); return ScopeNotificationSettings(mute_until, std::move(notification_settings->sound_), notification_settings->show_preview_, notification_settings->disable_pinned_message_notifications_, diff --git a/td/telegram/NotificationSettings.h b/td/telegram/NotificationSettings.h index 8fd9c1cd..93053544 100644 --- a/td/telegram/NotificationSettings.h +++ b/td/telegram/NotificationSettings.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationSettings.hpp b/td/telegram/NotificationSettings.hpp index 7f05ebe1..235852fa 100644 --- a/td/telegram/NotificationSettings.hpp +++ b/td/telegram/NotificationSettings.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationType.cpp b/td/telegram/NotificationType.cpp index 8f36907d..03569310 100644 --- a/td/telegram/NotificationType.cpp +++ b/td/telegram/NotificationType.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/NotificationType.h b/td/telegram/NotificationType.h index abbef2e4..a33b071c 100644 --- a/td/telegram/NotificationType.h +++ b/td/telegram/NotificationType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index 2252f60f..ece74208 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,6 @@ #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/net/NetQueryDispatcher.h" -#include "td/telegram/SecureStorage.h" #include "td/telegram/TdDb.h" #include "td/mtproto/DhHandshake.h" @@ -25,9 +24,6 @@ #include "td/utils/Slice.h" #include "td/utils/Time.h" -#include "td/telegram/td_api.h" -#include "td/telegram/telegram_api.h" // TODO: this file is already included. Why? - namespace td { tl_object_ptr TempPasswordState::as_td_api() const { @@ -144,11 +140,22 @@ tl_object_ptr PasswordManager::get_input_ch } tl_object_ptr PasswordManager::get_input_check_password( - Slice password, const PasswordState &state) const { + Slice password, const PasswordState &state) { return get_input_check_password(password, state.current_client_salt, state.current_server_salt, state.current_srp_g, state.current_srp_p, state.current_srp_B, state.current_srp_id); } +void PasswordManager::get_input_check_password_srp( + string password, Promise> &&promise) { + do_get_state(PromiseCreator::lambda( + [promise = std::move(promise), password = std::move(password)](Result r_state) mutable { + if (r_state.is_error()) { + return promise.set_error(r_state.move_as_error()); + } + promise.set_value(PasswordManager::get_input_check_password(password, r_state.move_as_ok())); + })); +} + void PasswordManager::set_password(string current_password, string new_password, string new_hint, bool set_recovery_email_address, string recovery_email_address, Promise promise) { @@ -454,6 +461,7 @@ void PasswordManager::check_email_address_verification_code(string code, Promise 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())), PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { @@ -467,6 +475,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)))), PromiseCreator::lambda( [actor_id = actor_id(this), promise = std::move(promise)](Result r_query) mutable { @@ -733,7 +742,7 @@ void PasswordManager::get_ton_wallet_password_salt(Promisenet_query_creator().create(create_storer(telegram_api::wallet_getKeySecretSalt())), + send_with_promise(G()->net_query_creator().create(create_storer(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/PasswordManager.h b/td/telegram/PasswordManager.h index 436b60c9..f238b92b 100644 --- a/td/telegram/PasswordManager.h +++ b/td/telegram/PasswordManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -79,6 +79,8 @@ class PasswordManager : public NetQueryCallback { void recover_password(string code, Promise promise); void get_secure_secret(string password, Promise promise); + void get_input_check_password_srp(string password, + Promise> &&promise); void get_temp_password_state(Promise promise) /*const*/; void create_temp_password(string password, int32 timeout, Promise promise); @@ -171,8 +173,8 @@ class PasswordManager : public NetQueryCallback { static Result calc_password_srp_hash(Slice password, Slice client_salt, Slice server_salt, int32 g, Slice p); - tl_object_ptr get_input_check_password(Slice password, - const PasswordState &state) const; + static tl_object_ptr get_input_check_password(Slice password, + const PasswordState &state); void update_password_settings(UpdateSettings update_settings, Promise promise); void do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state, Promise promise); diff --git a/td/telegram/Payments.cpp b/td/telegram/Payments.cpp index 74fd75ec..eeb50af6 100644 --- a/td/telegram/Payments.cpp +++ b/td/telegram/Payments.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,8 +11,8 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/Global.h" -#include "td/telegram/MessageId.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" @@ -367,8 +367,8 @@ class SendPaymentFormQuery : public Td::ResultHandler { promise_.set_value(make_tl_object(true, string())); return; } - case telegram_api::payments_paymentVerficationNeeded::ID: { - auto result = move_tl_object_as(payment_result); + case telegram_api::payments_paymentVerificationNeeded::ID: { + auto result = move_tl_object_as(payment_result); promise_.set_value(make_tl_object(false, std::move(result->url_))); return; } @@ -496,8 +496,8 @@ 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))))); + send_query(G()->net_query_creator().create(create_storer(telegram_api::wallet_sendLiteRequest(std::move(request))), + DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); } void on_result(uint64 id, BufferSlice packet) override { diff --git a/td/telegram/Payments.h b/td/telegram/Payments.h index 416e31af..0bd4544b 100644 --- a/td/telegram/Payments.h +++ b/td/telegram/Payments.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,8 +8,8 @@ #include "td/actor/PromiseFuture.h" -#include "td/telegram/MessageId.h" #include "td/telegram/Photo.h" +#include "td/telegram/ServerMessageId.h" #include "td/utils/common.h" #include "td/utils/Slice.h" diff --git a/td/telegram/Payments.hpp b/td/telegram/Payments.hpp index 4bc1f76b..bcb23072 100644 --- a/td/telegram/Payments.hpp +++ b/td/telegram/Payments.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -100,7 +100,7 @@ void store(const OrderInfo &order_info, StorerT &storer) { store(order_info.email_address, storer); } if (has_shipping_address) { - store(*order_info.shipping_address, storer); + store(order_info.shipping_address, storer); } } @@ -126,8 +126,7 @@ void parse(OrderInfo &order_info, ParserT &parser) { parse(order_info.email_address, parser); } if (has_shipping_address) { - order_info.shipping_address = make_unique
(); - parse(*order_info.shipping_address, parser); + parse(order_info.shipping_address, parser); } } diff --git a/td/telegram/PhoneNumberManager.cpp b/td/telegram/PhoneNumberManager.cpp index d47ae6b6..8c387c18 100644 --- a/td/telegram/PhoneNumberManager.cpp +++ b/td/telegram/PhoneNumberManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PhoneNumberManager.h b/td/telegram/PhoneNumberManager.h index 49b37fcc..d7ae1c5e 100644 --- a/td/telegram/PhoneNumberManager.h +++ b/td/telegram/PhoneNumberManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index 79d19bfb..3f7c6eda 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -86,8 +86,8 @@ td_api::object_ptr get_minithumbnail_object(const string .move_as_ok(); static const string footer = base64_decode("/9k=").move_as_ok(); auto result = td_api::make_object(); - result->width_ = static_cast(packed[1]); - result->height_ = static_cast(packed[2]); + result->height_ = static_cast(packed[1]); + result->width_ = static_cast(packed[2]); result->data_ = PSTRING() << header.substr(0, 164) << packed[1] << header[165] << packed[2] << header.substr(167) << packed.substr(3) << footer; return result; @@ -627,12 +627,13 @@ tl_object_ptr photo_get_input_media(FileManager *file_ if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { int32 flags = 0; if (ttl != 0) { flags |= telegram_api::inputMediaPhoto::TTL_SECONDS_MASK; } - return make_tl_object(flags, file_view.remote_location().as_input_photo(), ttl); + return make_tl_object(flags, file_view.main_remote_location().as_input_photo(), + ttl); } if (file_view.has_url()) { int32 flags = 0; @@ -695,8 +696,8 @@ SecretInputMedia photo_get_secret_input_media(FileManager *file_manager, const P return {}; } if (file_view.has_remote_location()) { - LOG(INFO) << "HAS REMOTE LOCATION"; - input_file = file_view.remote_location().as_input_encrypted_file(); + LOG(INFO) << "Photo has remote location"; + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (input_file == nullptr) { return {}; diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index 9c27726f..23cee3b8 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Photo.hpp b/td/telegram/Photo.hpp index 0c683c9c..86f890e4 100644 --- a/td/telegram/Photo.hpp +++ b/td/telegram/Photo.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PhotoSizeSource.cpp b/td/telegram/PhotoSizeSource.cpp index ce584ff8..bbcc0d7c 100644 --- a/td/telegram/PhotoSizeSource.cpp +++ b/td/telegram/PhotoSizeSource.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PhotoSizeSource.h b/td/telegram/PhotoSizeSource.h index f6dacfb1..c74b1dbe 100644 --- a/td/telegram/PhotoSizeSource.h +++ b/td/telegram/PhotoSizeSource.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PhotoSizeSource.hpp b/td/telegram/PhotoSizeSource.hpp index 6d2fad7f..81fd5fcf 100644 --- a/td/telegram/PhotoSizeSource.hpp +++ b/td/telegram/PhotoSizeSource.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PollId.h b/td/telegram/PollId.h index 25a64eed..1078d5d9 100644 --- a/td/telegram/PollId.h +++ b/td/telegram/PollId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PollId.hpp b/td/telegram/PollId.hpp index 93021407..fc13a45a 100644 --- a/td/telegram/PollId.hpp +++ b/td/telegram/PollId.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index c9354770..ef45444f 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -154,7 +154,7 @@ class StopPollActor : public NetActorOnce { auto input_media = telegram_api::make_object(std::move(poll)); auto query = G()->net_query_creator().create(create_storer(telegram_api::messages_editMessage( flags, false /*ignored*/, std::move(input_peer), message_id, string(), std::move(input_media), - std::move(input_reply_markup), vector>()))); + 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); @@ -429,8 +429,7 @@ td_api::object_ptr PollManager::get_poll_object(PollId poll_id, co } else { auto &chosen_options = it->second.options_; for (auto &poll_option : poll->options) { - auto is_being_chosen = - std::find(chosen_options.begin(), chosen_options.end(), poll_option.data) != chosen_options.end(); + auto is_being_chosen = td::contains(chosen_options, poll_option.data); if (poll_option.is_chosen) { voter_count_diff = -1; } @@ -503,6 +502,7 @@ PollId PollManager::create_poll(string &&question, vector &&options) { void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id) { CHECK(have_poll(poll_id)); + CHECK(!full_message_id.get_message_id().is_scheduled()); if (!full_message_id.get_message_id().is_server()) { return; } @@ -516,6 +516,7 @@ void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id) { void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id) { CHECK(have_poll(poll_id)); + CHECK(!full_message_id.get_message_id().is_scheduled()); if (!full_message_id.get_message_id().is_server()) { return; } diff --git a/td/telegram/PollManager.h b/td/telegram/PollManager.h index bfb37b6c..3a8a00a4 100644 --- a/td/telegram/PollManager.h +++ b/td/telegram/PollManager.h @@ -1,12 +1,12 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once -#include "td/telegram/MessageId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/PollId.h" #include "td/telegram/ReplyMarkup.h" diff --git a/td/telegram/PollManager.hpp b/td/telegram/PollManager.hpp index 12834ac4..37462933 100644 --- a/td/telegram/PollManager.hpp +++ b/td/telegram/PollManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PrivacyManager.cpp b/td/telegram/PrivacyManager.cpp index d36cb3fe..322f6bf5 100644 --- a/td/telegram/PrivacyManager.cpp +++ b/td/telegram/PrivacyManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,8 +9,12 @@ #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" +#include "td/telegram/ChannelId.h" +#include "td/telegram/ChatId.h" #include "td/telegram/ContactsManager.h" +#include "td/telegram/DialogId.h" #include "td/telegram/Global.h" +#include "td/telegram/MessagesManager.h" #include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/Td.h" @@ -48,6 +52,12 @@ PrivacyManager::UserPrivacySetting::UserPrivacySetting(const telegram_api::Priva case telegram_api::privacyKeyProfilePhoto::ID: type_ = Type::UserProfilePhoto; break; + case telegram_api::privacyKeyPhoneNumber::ID: + type_ = Type::UserPhoneNumber; + break; + case telegram_api::privacyKeyAddedByPhone::ID: + type_ = Type::FindByPhoneNumber; + break; default: UNREACHABLE(); type_ = Type::UserStatus; @@ -68,6 +78,10 @@ tl_object_ptr PrivacyManager::UserPrivacySetting::as return make_tl_object(); case Type::UserProfilePhoto: return make_tl_object(); + case Type::UserPhoneNumber: + return make_tl_object(); + case Type::FindByPhoneNumber: + return make_tl_object(); default: UNREACHABLE(); return nullptr; @@ -87,6 +101,10 @@ tl_object_ptr PrivacyManager::UserPrivacySetting: return make_tl_object(); case Type::UserProfilePhoto: return make_tl_object(); + case Type::UserPhoneNumber: + return make_tl_object(); + case Type::FindByPhoneNumber: + return make_tl_object(); default: UNREACHABLE(); return nullptr; @@ -113,12 +131,47 @@ PrivacyManager::UserPrivacySetting::UserPrivacySetting(const td_api::UserPrivacy case td_api::userPrivacySettingShowProfilePhoto::ID: type_ = Type::UserProfilePhoto; break; + case td_api::userPrivacySettingShowPhoneNumber::ID: + type_ = Type::UserPhoneNumber; + break; + case td_api::userPrivacySettingAllowFindingByPhoneNumber::ID: + type_ = Type::FindByPhoneNumber; + break; default: UNREACHABLE(); type_ = Type::UserStatus; } } +void PrivacyManager::UserPrivacySettingRule::set_chat_ids(const vector &dialog_ids) { + chat_ids_.clear(); + auto td = G()->td().get_actor_unsafe(); + for (auto dialog_id_int : dialog_ids) { + DialogId dialog_id(dialog_id_int); + if (!td->messages_manager_->have_dialog_force(dialog_id)) { + LOG(ERROR) << "Ignore not found " << dialog_id; + continue; + } + + switch (dialog_id.get_type()) { + case DialogType::Chat: + chat_ids_.push_back(dialog_id.get_chat_id().get()); + break; + case DialogType::Channel: { + auto channel_id = dialog_id.get_channel_id(); + if (td->contacts_manager_->get_channel_type(channel_id) != ChannelType::Megagroup) { + LOG(ERROR) << "Ignore broadcast " << channel_id; + break; + } + chat_ids_.push_back(channel_id.get()); + break; + } + default: + LOG(ERROR) << "Ignore " << dialog_id; + } + } +} + PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const td_api::UserPrivacySettingRule &rule) { switch (rule.get_id()) { case td_api::userPrivacySettingRuleAllowContacts::ID: @@ -131,6 +184,10 @@ PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const td_api::Use type_ = Type::AllowUsers; user_ids_ = static_cast(rule).user_ids_; break; + case td_api::userPrivacySettingRuleAllowChatMembers::ID: + type_ = Type::AllowChatParticipants; + set_chat_ids(static_cast(rule).chat_ids_); + break; case td_api::userPrivacySettingRuleRestrictContacts::ID: type_ = Type::RestrictContacts; break; @@ -141,6 +198,10 @@ PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const td_api::Use type_ = Type::RestrictUsers; user_ids_ = static_cast(rule).user_ids_; break; + case td_api::userPrivacySettingRuleRestrictChatMembers::ID: + type_ = Type::RestrictChatParticipants; + set_chat_ids(static_cast(rule).chat_ids_); + break; default: UNREACHABLE(); } @@ -158,6 +219,10 @@ PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const telegram_ap type_ = Type::AllowUsers; user_ids_ = static_cast(rule).users_; break; + case telegram_api::privacyValueAllowChatParticipants::ID: + type_ = Type::AllowChatParticipants; + chat_ids_ = static_cast(rule).chats_; + break; case telegram_api::privacyValueDisallowContacts::ID: type_ = Type::RestrictContacts; break; @@ -168,6 +233,10 @@ PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const telegram_ap type_ = Type::RestrictUsers; user_ids_ = static_cast(rule).users_; break; + case telegram_api::privacyValueDisallowChatParticipants::ID: + type_ = Type::RestrictChatParticipants; + chat_ids_ = static_cast(rule).chats_; + break; default: UNREACHABLE(); } @@ -180,13 +249,17 @@ tl_object_ptr PrivacyManager::UserPrivacySetting case Type::AllowAll: return make_tl_object(); case Type::AllowUsers: - return make_tl_object(user_ids_as_td_api()); + return make_tl_object(vector{user_ids_}); + case Type::AllowChatParticipants: + return make_tl_object(chat_ids_as_dialog_ids()); case Type::RestrictContacts: return make_tl_object(); case Type::RestrictAll: return make_tl_object(); case Type::RestrictUsers: - return make_tl_object(user_ids_as_td_api()); + return make_tl_object(vector{user_ids_}); + case Type::RestrictChatParticipants: + return make_tl_object(chat_ids_as_dialog_ids()); default: UNREACHABLE(); } @@ -200,12 +273,16 @@ tl_object_ptr PrivacyManager::UserPrivacySetting return make_tl_object(); case Type::AllowUsers: return make_tl_object(user_ids_as_telegram_api()); + case Type::AllowChatParticipants: + return make_tl_object(vector{chat_ids_}); case Type::RestrictContacts: return make_tl_object(); case Type::RestrictAll: return make_tl_object(); case Type::RestrictUsers: return make_tl_object(user_ids_as_telegram_api()); + case Type::RestrictChatParticipants: + return make_tl_object(vector{chat_ids_}); default: UNREACHABLE(); } @@ -214,47 +291,78 @@ tl_object_ptr PrivacyManager::UserPrivacySetting Result PrivacyManager::UserPrivacySettingRule::from_telegram_api( tl_object_ptr rule) { CHECK(rule != nullptr); - UserPrivacySettingRule res(*rule); - for (auto user_id : res.user_ids_) { - if (!G()->td().get_actor_unsafe()->contacts_manager_->have_user(UserId(user_id))) { + UserPrivacySettingRule result(*rule); + auto td = G()->td().get_actor_unsafe(); + for (auto user_id : result.user_ids_) { + if (!td->contacts_manager_->have_user(UserId(user_id))) { return Status::Error(500, "Got inaccessible user from the server"); } } - return res; + for (auto chat_id_int : result.chat_ids_) { + ChatId chat_id(chat_id_int); + DialogId dialog_id(chat_id); + if (!td->contacts_manager_->have_chat(chat_id)) { + ChannelId channel_id(chat_id_int); + dialog_id = DialogId(channel_id); + if (!td->contacts_manager_->have_channel(channel_id)) { + return Status::Error(500, "Got inaccessible chat from the server"); + } + } + td->messages_manager_->force_create_dialog(dialog_id, "UserPrivacySettingRule"); + } + return result; } vector> PrivacyManager::UserPrivacySettingRule::user_ids_as_telegram_api() const { - vector> res; + vector> result; for (auto user_id : user_ids_) { auto input_user = G()->td().get_actor_unsafe()->contacts_manager_->get_input_user(UserId(user_id)); if (input_user != nullptr) { - res.push_back(std::move(input_user)); + result.push_back(std::move(input_user)); } else { LOG(ERROR) << "Have no access to " << user_id; } } - return res; + return result; } -vector PrivacyManager::UserPrivacySettingRule::user_ids_as_td_api() const { - return user_ids_; +vector PrivacyManager::UserPrivacySettingRule::chat_ids_as_dialog_ids() const { + vector result; + auto td = G()->td().get_actor_unsafe(); + for (auto chat_id_int : chat_ids_) { + ChatId chat_id(chat_id_int); + DialogId dialog_id(chat_id); + if (!td->contacts_manager_->have_chat(chat_id)) { + ChannelId channel_id(chat_id_int); + dialog_id = DialogId(channel_id); + CHECK(td->contacts_manager_->have_channel(channel_id)); + } + CHECK(td->messages_manager_->have_dialog(dialog_id)); + result.push_back(dialog_id.get()); + } + return result; } Result PrivacyManager::UserPrivacySettingRules::from_telegram_api( tl_object_ptr rules) { G()->td().get_actor_unsafe()->contacts_manager_->on_get_users(std::move(rules->users_), "on get privacy rules"); + G()->td().get_actor_unsafe()->contacts_manager_->on_get_chats(std::move(rules->chats_), "on get privacy rules"); return from_telegram_api(std::move(rules->rules_)); } Result PrivacyManager::UserPrivacySettingRules::from_telegram_api( vector> rules) { - UserPrivacySettingRules res; + UserPrivacySettingRules result; for (auto &rule : rules) { TRY_RESULT(new_rule, UserPrivacySettingRule::from_telegram_api(std::move(rule))); - res.rules_.push_back(new_rule); + result.rules_.push_back(new_rule); } - return res; + if (!result.rules_.empty() && + result.rules_.back().as_td_api()->get_id() == td_api::userPrivacySettingRuleRestrictAll::ID) { + result.rules_.pop_back(); + } + return result; } Result PrivacyManager::UserPrivacySettingRules::from_td_api( @@ -262,14 +370,14 @@ Result PrivacyManager::UserPrivacySetti if (!rules) { return Status::Error(5, "UserPrivacySettingRules should not be empty"); } - UserPrivacySettingRules res; + UserPrivacySettingRules result; for (auto &rule : rules->rules_) { if (!rule) { return Status::Error(5, "UserPrivacySettingRule should not be empty"); } - res.rules_.emplace_back(*rule); + result.rules_.emplace_back(*rule); } - return res; + return result; } tl_object_ptr PrivacyManager::UserPrivacySettingRules::as_td_api() const { @@ -278,7 +386,11 @@ tl_object_ptr PrivacyManager::UserPrivacySettin } vector> PrivacyManager::UserPrivacySettingRules::as_telegram_api() const { - return transform(rules_, [](const auto &rule) { return rule.as_telegram_api(); }); + auto result = transform(rules_, [](const auto &rule) { return rule.as_telegram_api(); }); + if (!result.empty() && result.back()->get_id() == telegram_api::inputPrivacyValueDisallowAll::ID) { + result.pop_back(); + } + return result; } void PrivacyManager::get_privacy(tl_object_ptr key, @@ -305,6 +417,7 @@ void PrivacyManager::get_privacy(tl_object_ptr key, on_get_result(user_privacy_setting, [&]() -> Result { TRY_RESULT(net_query, std::move(x_net_query)); TRY_RESULT(rules, fetch_result(std::move(net_query))); + LOG(INFO) << "Receive " << to_string(rules); return UserPrivacySettingRules::from_telegram_api(std::move(rules)); }()); })); @@ -337,10 +450,11 @@ void PrivacyManager::set_privacy(tl_object_ptr key, PromiseCreator::lambda([this, user_privacy_setting, promise = std::move(promise)](Result x_net_query) mutable { promise.set_result([&]() -> Result { + get_info(user_privacy_setting).has_set_query = false; TRY_RESULT(net_query, std::move(x_net_query)); TRY_RESULT(rules, fetch_result(std::move(net_query))); + LOG(INFO) << "Receive " << to_string(rules); TRY_RESULT(privacy_rules, UserPrivacySettingRules::from_telegram_api(std::move(rules))); - get_info(user_privacy_setting).has_set_query = false; do_update_privacy(user_privacy_setting, std::move(privacy_rules), true); return Unit(); }()); diff --git a/td/telegram/PrivacyManager.h b/td/telegram/PrivacyManager.h index 0aedf70a..26b57e4d 100644 --- a/td/telegram/PrivacyManager.h +++ b/td/telegram/PrivacyManager.h @@ -1,19 +1,18 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once +#include "td/telegram/net/NetQuery.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/actor/actor.h" #include "td/actor/PromiseFuture.h" -#include "td/telegram/net/NetQuery.h" - #include "td/utils/common.h" #include "td/utils/Container.h" #include "td/utils/Status.h" @@ -45,6 +44,8 @@ class PrivacyManager : public NetQueryCallback { PeerToPeerCall, LinkInForwardedMessages, UserProfilePhoto, + UserPhoneNumber, + FindByPhoneNumber, Size }; @@ -72,7 +73,7 @@ class PrivacyManager : public NetQueryCallback { tl_object_ptr as_telegram_api() const; bool operator==(const UserPrivacySettingRule &other) const { - return type_ == other.type_ && user_ids_ == other.user_ids_; + return type_ == other.type_ && user_ids_ == other.user_ids_ && chat_ids_ == other.chat_ids_; } private: @@ -80,17 +81,22 @@ class PrivacyManager : public NetQueryCallback { AllowContacts, AllowAll, AllowUsers, + AllowChatParticipants, RestrictContacts, RestrictAll, - RestrictUsers + RestrictUsers, + RestrictChatParticipants } type_ = Type::RestrictAll; vector user_ids_; - - vector user_ids_as_td_api() const; + vector chat_ids_; vector> user_ids_as_telegram_api() const; + void set_chat_ids(const vector &dialog_ids); + + vector chat_ids_as_dialog_ids() const; + explicit UserPrivacySettingRule(const telegram_api::PrivacyRule &rule); }; diff --git a/td/telegram/PtsManager.h b/td/telegram/PtsManager.h index 8d6a3d6c..3adbb259 100644 --- a/td/telegram/PtsManager.h +++ b/td/telegram/PtsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/PublicDialogType.h b/td/telegram/PublicDialogType.h new file mode 100644 index 00000000..01649d4c --- /dev/null +++ b/td/telegram/PublicDialogType.h @@ -0,0 +1,22 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/td_api.h" + +namespace td { + +enum class PublicDialogType : int32 { HasUsername, IsLocationBased }; + +inline PublicDialogType get_public_dialog_type(const td_api::object_ptr &type) { + if (type == nullptr || type->get_id() == td_api::publicChatTypeHasUsername::ID) { + return PublicDialogType::HasUsername; + } + return PublicDialogType::IsLocationBased; +} + +} // namespace td diff --git a/td/telegram/QueryCombiner.cpp b/td/telegram/QueryCombiner.cpp index 89dcc057..8e799d5c 100644 --- a/td/telegram/QueryCombiner.cpp +++ b/td/telegram/QueryCombiner.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/QueryCombiner.h b/td/telegram/QueryCombiner.h index 222b7fb7..79933cde 100644 --- a/td/telegram/QueryCombiner.h +++ b/td/telegram/QueryCombiner.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ReplyMarkup.cpp b/td/telegram/ReplyMarkup.cpp index 95c6576b..3fddb1b8 100644 --- a/td/telegram/ReplyMarkup.cpp +++ b/td/telegram/ReplyMarkup.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -380,8 +380,8 @@ static Result get_inline_keyboard_button(tl_object_ptr(button->type_.get())->url_)); - current_button.data = std::move(url); + TRY_RESULT_ASSIGN(current_button.data, + check_url(static_cast(button->type_.get())->url_)); if (!clean_input_string(current_button.data)) { return Status::Error(400, "Inline keyboard button url must be encoded in UTF-8"); } @@ -420,8 +420,7 @@ static Result get_inline_keyboard_button(tl_object_ptr(button->type_); - TRY_RESULT(url, check_url(login_url->url_)); - current_button.data = std::move(url); + TRY_RESULT_ASSIGN(current_button.data, check_url(login_url->url_)); current_button.forward_text = std::move(login_url->forward_text_); if (!clean_input_string(current_button.data)) { return Status::Error(400, "Inline keyboard button login url must be encoded in UTF-8"); diff --git a/td/telegram/ReplyMarkup.h b/td/telegram/ReplyMarkup.h index 5236c331..5f999aad 100644 --- a/td/telegram/ReplyMarkup.h +++ b/td/telegram/ReplyMarkup.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ReplyMarkup.hpp b/td/telegram/ReplyMarkup.hpp index afa2bd8f..897036b7 100644 --- a/td/telegram/ReplyMarkup.hpp +++ b/td/telegram/ReplyMarkup.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/RequestActor.h b/td/telegram/RequestActor.h index 179d1438..9e1e8f82 100644 --- a/td/telegram/RequestActor.h +++ b/td/telegram/RequestActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/RestrictionReason.cpp b/td/telegram/RestrictionReason.cpp new file mode 100644 index 00000000..f5a9da13 --- /dev/null +++ b/td/telegram/RestrictionReason.cpp @@ -0,0 +1,88 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/RestrictionReason.h" + +#include "td/telegram/ConfigShared.h" +#include "td/telegram/Global.h" + +#include "td/utils/common.h" +#include "td/utils/misc.h" + +#include + +namespace td { + +string get_restriction_reason_description(const vector &restriction_reasons) { + if (restriction_reasons.empty()) { + return string(); + } + + auto ignored_restriction_reasons = + full_split(G()->shared_config().get_option_string("ignored_restriction_reasons"), ','); + auto platform = [] { + if (G()->shared_config().get_option_boolean("ignore_platform_restrictions")) { + return Slice(); + } + +#if TD_ANDROID + return Slice("android"); +#elif TD_WINDOWS + return Slice("ms"); +#elif TD_DARWIN + return Slice("ios"); +#else + return Slice(); +#endif + }(); + + if (!platform.empty()) { + for (auto &restriction_reason : restriction_reasons) { + if (restriction_reason.platform_ == platform && + !td::contains(ignored_restriction_reasons, restriction_reason.reason_)) { + return restriction_reason.description_; + } + } + } + + for (auto &restriction_reason : restriction_reasons) { + if (restriction_reason.platform_ == "all" && + !td::contains(ignored_restriction_reasons, restriction_reason.reason_)) { + return restriction_reason.description_; + } + } + + return string(); +} + +vector get_restriction_reasons(Slice legacy_restriction_reason) { + Slice type; + Slice description; + std::tie(type, description) = split(legacy_restriction_reason, ':'); + auto parts = full_split(type, '-'); + description = trim(description); + + vector result; + if (parts.size() <= 1) { + return result; + } + for (size_t i = 1; i < parts.size(); i++) { + result.emplace_back(parts[i].str(), parts[0].str(), description.str()); + } + return result; +} + +vector get_restriction_reasons( + vector> &&restriction_reasons) { + return transform(std::move(restriction_reasons), + [](telegram_api::object_ptr &&restriction_reason) { + return RestrictionReason(std::move(restriction_reason->platform_), + std::move(restriction_reason->reason_), + std::move(restriction_reason->text_)); + }); +} + +} // namespace td diff --git a/td/telegram/RestrictionReason.h b/td/telegram/RestrictionReason.h new file mode 100644 index 00000000..56f24b53 --- /dev/null +++ b/td/telegram/RestrictionReason.h @@ -0,0 +1,70 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/telegram_api.h" + +#include "td/utils/common.h" +#include "td/utils/Slice.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class RestrictionReason { + string platform_; + string reason_; + string description_; + + friend StringBuilder &operator<<(StringBuilder &string_builder, const RestrictionReason &reason) { + return string_builder << "RestrictionReason[" << reason.platform_ << ", " << reason.reason_ << ", " + << reason.description_ << "]"; + } + + friend bool operator==(const RestrictionReason &lhs, const RestrictionReason &rhs) { + return lhs.platform_ == rhs.platform_ && lhs.reason_ == rhs.reason_ && lhs.description_ == rhs.description_; + } + + friend string get_restriction_reason_description(const vector &restriction_reasons); + + public: + RestrictionReason() = default; + + RestrictionReason(string &&platform, string &&reason, string &&description) + : platform_(std::move(platform)), reason_(std::move(reason)), description_(std::move(description)) { + if (description_.empty()) { + description_ = reason_; + } + } + + template + void store(StorerT &storer) const { + td::store(platform_, storer); + td::store(reason_, storer); + td::store(description_, storer); + } + + template + void parse(ParserT &parser) { + td::parse(platform_, parser); + td::parse(reason_, parser); + td::parse(description_, parser); + } +}; + +inline bool operator!=(const RestrictionReason &lhs, const RestrictionReason &rhs) { + return !(lhs == rhs); +} + +string get_restriction_reason_description(const vector &restriction_reasons); + +vector get_restriction_reasons(Slice legacy_restriction_reason); + +vector get_restriction_reasons( + vector> &&restriction_reasons); + +} // namespace td diff --git a/td/telegram/ScheduledServerMessageId.h b/td/telegram/ScheduledServerMessageId.h new file mode 100644 index 00000000..0ccc80fa --- /dev/null +++ b/td/telegram/ScheduledServerMessageId.h @@ -0,0 +1,65 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +#include +#include + +namespace td { + +class ScheduledServerMessageId { + int32 id = 0; + + public: + ScheduledServerMessageId() = default; + + explicit ScheduledServerMessageId(int32 message_id) : id(message_id) { + } + template ::value>> + ScheduledServerMessageId(T message_id) = delete; + + bool is_valid() const { + return id > 0 && id < (1 << 18); + } + + int32 get() const { + return id; + } + + bool operator==(const ScheduledServerMessageId &other) const { + return id == other.id; + } + + bool operator!=(const ScheduledServerMessageId &other) const { + return id != other.id; + } + + template + void store(StorerT &storer) const { + storer.store_int(id); + } + + template + void parse(ParserT &parser) { + id = parser.fetch_int(); + } +}; + +struct ScheduledServerMessageIdHash { + std::size_t operator()(ScheduledServerMessageId message_id) const { + return std::hash()(message_id.get()); + } +}; + +inline StringBuilder &operator<<(StringBuilder &string_builder, ScheduledServerMessageId message_id) { + return string_builder << "scheduled server message " << message_id.get(); +} + +} // namespace td diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index 8d2ed44e..0c85c8e3 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/telegram/net/NetQueryCreator.h" #include "td/telegram/SecretChatId.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/UniqueId.h" #include "td/telegram/secret_api.hpp" @@ -470,7 +471,7 @@ void SecretChatActor::notify_screenshot_taken(Promise<> promise) { promise.set_error(Status::Error(400, "Can't access the chat")); return; } - send_action(make_tl_object(), SendFlag::Push, + send_action(make_tl_object(vector()), SendFlag::Push, std::move(promise)); } @@ -565,10 +566,8 @@ 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; - tl_query.user_id_ = get_input_user(); - tl_query.random_id_ = auth_state_.random_id; - tl_query.g_a_ = BufferSlice(auth_state_.handshake.get_g_b()); + 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)); @@ -584,12 +583,9 @@ Status SecretChatActor::run_auth() { auto id_and_key = auth_state_.handshake.gen_key(); 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; - tl_query.peer_ = get_input_chat(); - tl_query.g_b_ = BufferSlice(auth_state_.handshake.get_g_b()); - tl_query.key_fingerprint_ = pfs_state_.auth_key.id(); + // 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)); @@ -910,7 +906,7 @@ Status SecretChatActor::do_inbound_message_encrypted(unique_ptr= MTPROTO_2_LAYER && mtproto_version < 2) { - return Status::Error(PSLICE() << "Mtproto 1.0 encryption is forbidden for this layer"); + return Status::Error(PSLICE() << "MTProto 1.0 encryption is forbidden for this layer"); } if (message_with_layer->in_seq_no_ < 0) { return Status::Error(PSLICE() << "Invalid seq_no: " << to_string(message_with_layer)); diff --git a/td/telegram/SecretChatActor.h b/td/telegram/SecretChatActor.h index 614e0a84..10b93853 100644 --- a/td/telegram/SecretChatActor.h +++ b/td/telegram/SecretChatActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -48,7 +48,13 @@ class NetQueryCreator; class SecretChatActor : public NetQueryCallback { public: // do not change DEFAULT_LAYER, unless all it's usages are fixed - enum : int32 { DEFAULT_LAYER = 46, VIDEO_NOTES_LAYER = 66, MTPROTO_2_LAYER = 73, MY_LAYER = MTPROTO_2_LAYER }; + enum : int32 { + DEFAULT_LAYER = 46, + VIDEO_NOTES_LAYER = 66, + MTPROTO_2_LAYER = 73, + NEW_ENTITIES_LAYER = 101, + MY_LAYER = NEW_ENTITIES_LAYER + }; class Context { public: diff --git a/td/telegram/SecretChatDb.cpp b/td/telegram/SecretChatDb.cpp index 3f01bc30..192fd636 100644 --- a/td/telegram/SecretChatDb.cpp +++ b/td/telegram/SecretChatDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecretChatDb.h b/td/telegram/SecretChatDb.h index b312fd1f..bb019151 100644 --- a/td/telegram/SecretChatDb.h +++ b/td/telegram/SecretChatDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecretChatId.h b/td/telegram/SecretChatId.h index 4301d4a7..6f85e912 100644 --- a/td/telegram/SecretChatId.h +++ b/td/telegram/SecretChatId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecretChatsManager.cpp b/td/telegram/SecretChatsManager.cpp index 9e403682..ce8981c6 100644 --- a/td/telegram/SecretChatsManager.cpp +++ b/td/telegram/SecretChatsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecretChatsManager.h b/td/telegram/SecretChatsManager.h index dee976da..2b9660f3 100644 --- a/td/telegram/SecretChatsManager.h +++ b/td/telegram/SecretChatsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecretInputMedia.h b/td/telegram/SecretInputMedia.h index a7eefadb..de1f3c29 100644 --- a/td/telegram/SecretInputMedia.h +++ b/td/telegram/SecretInputMedia.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index da77da26..857037ba 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecureManager.h b/td/telegram/SecureManager.h index c2d92e75..f8300012 100644 --- a/td/telegram/SecureManager.h +++ b/td/telegram/SecureManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecureStorage.cpp b/td/telegram/SecureStorage.cpp index 0dd0ac37..c2c43aaa 100644 --- a/td/telegram/SecureStorage.cpp +++ b/td/telegram/SecureStorage.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,6 +10,7 @@ #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" +#include "td/utils/port/FileFd.h" #include "td/utils/Random.h" #include "td/utils/SharedSlice.h" @@ -88,6 +89,18 @@ BufferSlice gen_random_prefix(int64 data_size) { return buff; } +class FileDataView : public DataView { + public: + FileDataView(FileFd &fd, int64 size); + + int64 size() const override; + Result pread(int64 offset, int64 size) const override; + + private: + FileFd &fd_; + int64 size_; +}; + FileDataView::FileDataView(FileFd &fd, int64 size) : fd_(fd), size_(size) { } diff --git a/td/telegram/SecureStorage.h b/td/telegram/SecureStorage.h index 7e68b0d1..8b6e2b8c 100644 --- a/td/telegram/SecureStorage.h +++ b/td/telegram/SecureStorage.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,7 +9,6 @@ #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/crypto.h" -#include "td/utils/port/FileFd.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/UInt.h" @@ -75,18 +74,6 @@ class DataView { virtual ~DataView() = default; }; -class FileDataView : public DataView { - public: - FileDataView(FileFd &fd, int64 size); - - int64 size() const override; - Result pread(int64 offset, int64 size) const override; - - private: - FileFd &fd_; - int64 size_; -}; - class BufferSliceDataView : public DataView { public: explicit BufferSliceDataView(BufferSlice buffer_slice); diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index 528b5286..c85a50f3 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -892,19 +892,15 @@ static Result get_identity_document(SecureValueType type, FileManag } } - TRY_RESULT(front_side, get_secure_file(file_manager, std::move(identity_document->front_side_))); - res.front_side = std::move(front_side); + TRY_RESULT_ASSIGN(res.front_side, get_secure_file(file_manager, std::move(identity_document->front_side_))); if (identity_document->reverse_side_ != nullptr) { - TRY_RESULT(reverse_side, get_secure_file(file_manager, std::move(identity_document->reverse_side_))); - res.reverse_side = std::move(reverse_side); + TRY_RESULT_ASSIGN(res.reverse_side, get_secure_file(file_manager, std::move(identity_document->reverse_side_))); } if (identity_document->selfie_ != nullptr) { - TRY_RESULT(selfie, get_secure_file(file_manager, std::move(identity_document->selfie_))); - res.selfie = std::move(selfie); + TRY_RESULT_ASSIGN(res.selfie, get_secure_file(file_manager, std::move(identity_document->selfie_))); } if (!identity_document->translation_.empty()) { - TRY_RESULT(translations, get_secure_files(file_manager, std::move(identity_document->translation_))); - res.translations = std::move(translations); + TRY_RESULT_ASSIGN(res.translations, get_secure_files(file_manager, std::move(identity_document->translation_))); } return res; } @@ -962,11 +958,9 @@ static Result get_personal_document( if (personal_document->files_.empty()) { return Status::Error(400, "Document's files are required"); } - TRY_RESULT(files, get_secure_files(file_manager, std::move(personal_document->files_))); - res.files = std::move(files); + TRY_RESULT_ASSIGN(res.files, get_secure_files(file_manager, std::move(personal_document->files_))); if (!personal_document->translation_.empty()) { - TRY_RESULT(translations, get_secure_files(file_manager, std::move(personal_document->translation_))); - res.translations = std::move(translations); + TRY_RESULT_ASSIGN(res.translations, get_secure_files(file_manager, std::move(personal_document->translation_))); } return res; } @@ -1002,8 +996,7 @@ Result get_secure_value(FileManager *file_manager, case td_api::inputPassportElementPersonalDetails::ID: { auto input = td_api::move_object_as(input_passport_element); res.type = SecureValueType::PersonalDetails; - TRY_RESULT(personal_details, get_personal_details(std::move(input->personal_details_))); - res.data = std::move(personal_details); + TRY_RESULT_ASSIGN(res.data, get_personal_details(std::move(input->personal_details_))); break; } case td_api::inputPassportElementPassport::ID: { @@ -1455,7 +1448,7 @@ static auto credentials_as_jsonable(const std::vector &c })); } })); - o(rename_payload_to_nonce ? "nonce" : "payload", nonce); + o(rename_payload_to_nonce ? Slice("nonce") : Slice("payload"), nonce); }); } diff --git a/td/telegram/SecureValue.h b/td/telegram/SecureValue.h index 8e64b2d9..be05fb21 100644 --- a/td/telegram/SecureValue.h +++ b/td/telegram/SecureValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SecureValue.hpp b/td/telegram/SecureValue.hpp index 1f85d7ae..712fc038 100644 --- a/td/telegram/SecureValue.hpp +++ b/td/telegram/SecureValue.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SendCodeHelper.cpp b/td/telegram/SendCodeHelper.cpp index 1f198f15..d6ffd47e 100644 --- a/td/telegram/SendCodeHelper.cpp +++ b/td/telegram/SendCodeHelper.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -38,7 +38,6 @@ Result SendCodeHelper::resend_code() { telegram_api::object_ptr SendCodeHelper::get_input_code_settings(const Settings &settings) { int32 flags = 0; - string app_hash; if (settings != nullptr) { if (settings->allow_flash_call_) { flags |= telegram_api::codeSettings::ALLOW_FLASHCALL_MASK; @@ -47,20 +46,15 @@ telegram_api::object_ptr SendCodeHelper::get_input_c flags |= telegram_api::codeSettings::CURRENT_NUMBER_MASK; } if (settings->allow_sms_retriever_api_) { - flags |= telegram_api::codeSettings::APP_HASH_PERSISTENT_MASK; - flags |= telegram_api::codeSettings::APP_HASH_MASK; - app_hash = "ignored1234"; + flags |= telegram_api::codeSettings::ALLOW_APP_HASH_MASK; } } return telegram_api::make_object(flags, false /*ignored*/, false /*ignored*/, - false /*ignored*/, app_hash); + false /*ignored*/); } -Result SendCodeHelper::send_code(Slice phone_number, const Settings &settings, - int32 api_id, const string &api_hash) { - if (!phone_number_.empty()) { - return Status::Error(8, "Can't change phone"); - } +telegram_api::auth_sendCode SendCodeHelper::send_code(Slice phone_number, const Settings &settings, int32 api_id, + const string &api_hash) { phone_number_ = phone_number.str(); return telegram_api::auth_sendCode(phone_number_, api_id, api_hash, get_input_code_settings(settings)); } diff --git a/td/telegram/SendCodeHelper.h b/td/telegram/SendCodeHelper.h index 9a403572..85d44496 100644 --- a/td/telegram/SendCodeHelper.h +++ b/td/telegram/SendCodeHelper.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -25,8 +25,8 @@ class SendCodeHelper { using Settings = td_api::object_ptr; - Result send_code(Slice phone_number, const Settings &settings, int32 api_id, - const string &api_hash); + telegram_api::auth_sendCode send_code(Slice phone_number, const Settings &settings, int32 api_id, + const string &api_hash); telegram_api::account_sendChangePhoneCode send_change_phone_code(Slice phone_number, const Settings &settings); diff --git a/td/telegram/SendCodeHelper.hpp b/td/telegram/SendCodeHelper.hpp index 55abfdeb..381eb249 100644 --- a/td/telegram/SendCodeHelper.hpp +++ b/td/telegram/SendCodeHelper.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SequenceDispatcher.cpp b/td/telegram/SequenceDispatcher.cpp index dd0bd0ee..706c846c 100644 --- a/td/telegram/SequenceDispatcher.cpp +++ b/td/telegram/SequenceDispatcher.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/SequenceDispatcher.h b/td/telegram/SequenceDispatcher.h index a1efba6d..6e600827 100644 --- a/td/telegram/SequenceDispatcher.h +++ b/td/telegram/SequenceDispatcher.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/ServerMessageId.h b/td/telegram/ServerMessageId.h new file mode 100644 index 00000000..e94f626e --- /dev/null +++ b/td/telegram/ServerMessageId.h @@ -0,0 +1,43 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/utils/common.h" + +#include + +namespace td { + +class ServerMessageId { + int32 id = 0; + + public: + ServerMessageId() = default; + + explicit ServerMessageId(int32 message_id) : id(message_id) { + } + template ::value>> + ServerMessageId(T message_id) = delete; + + bool is_valid() const { + return id > 0; + } + + int32 get() const { + return id; + } + + bool operator==(const ServerMessageId &other) const { + return id == other.id; + } + + bool operator!=(const ServerMessageId &other) const { + return id != other.id; + } +}; + +} // namespace td diff --git a/td/telegram/SetWithPosition.h b/td/telegram/SetWithPosition.h index 30211bcf..526f8eea 100644 --- a/td/telegram/SetWithPosition.h +++ b/td/telegram/SetWithPosition.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/StateManager.cpp b/td/telegram/StateManager.cpp index cdb28eb7..42b41179 100644 --- a/td/telegram/StateManager.cpp +++ b/td/telegram/StateManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/StateManager.h b/td/telegram/StateManager.h index 0b1f7ab7..6d917943 100644 --- a/td/telegram/StateManager.h +++ b/td/telegram/StateManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/StickerSetId.h b/td/telegram/StickerSetId.h new file mode 100644 index 00000000..e1133692 --- /dev/null +++ b/td/telegram/StickerSetId.h @@ -0,0 +1,55 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" + +#include +#include + +namespace td { + +class StickerSetId { + int64 id = 0; + + public: + StickerSetId() = default; + + explicit constexpr StickerSetId(int64 sticker_set_id) : id(sticker_set_id) { + } + template ::value>> + StickerSetId(T sticker_set_id) = delete; + + bool is_valid() const { + return id != 0; + } + + int64 get() const { + return id; + } + + bool operator==(const StickerSetId &other) const { + return id == other.id; + } + + bool operator!=(const StickerSetId &other) const { + return id != other.id; + } +}; + +struct StickerSetIdHash { + std::size_t operator()(StickerSetId sticker_set_id) const { + return std::hash()(sticker_set_id.get()); + } +}; + +inline StringBuilder &operator<<(StringBuilder &string_builder, StickerSetId sticker_set_id) { + return string_builder << "sticker set " << sticker_set_id.get(); +} + +} // namespace td diff --git a/td/telegram/StickerSetId.hpp b/td/telegram/StickerSetId.hpp new file mode 100644 index 00000000..9558dc3c --- /dev/null +++ b/td/telegram/StickerSetId.hpp @@ -0,0 +1,27 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/StickerSetId.h" + +#include "td/telegram/StickersManager.h" +#include "td/telegram/StickersManager.hpp" +#include "td/telegram/Td.h" + +namespace td { + +template +void store(const StickerSetId &sticker_set_id, StorerT &storer) { + storer.context()->td().get_actor_unsafe()->stickers_manager_->store_sticker_set_id(sticker_set_id, storer); +} + +template +void parse(StickerSetId &sticker_set_id, ParserT &parser) { + parser.context()->td().get_actor_unsafe()->stickers_manager_->parse_sticker_set_id(sticker_set_id, parser); +} + +} // namespace td diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 4767b7ed..9c02eaa4 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -28,6 +28,7 @@ #include "td/telegram/misc.h" #include "td/telegram/net/DcId.h" #include "td/telegram/net/MtprotoHeader.h" +#include "td/telegram/StickerSetId.hpp" #include "td/telegram/StickersManager.hpp" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" @@ -225,14 +226,14 @@ class GetEmojiUrlQuery : public Td::ResultHandler { class GetArchivedStickerSetsQuery : public Td::ResultHandler { Promise promise_; - int64 offset_sticker_set_id_; + StickerSetId offset_sticker_set_id_; bool is_masks_; public: explicit GetArchivedStickerSetsQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(bool is_masks, int64 offset_sticker_set_id, int32 limit) { + void send(bool is_masks, StickerSetId offset_sticker_set_id, int32 limit) { offset_sticker_set_id_ = offset_sticker_set_id; is_masks_ = is_masks; LOG(INFO) << "Get archived " << (is_masks ? "mask" : "sticker") << " sets from " << offset_sticker_set_id @@ -245,7 +246,7 @@ 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, limit)))); + telegram_api::messages_getArchivedStickers(flags, is_masks /*ignored*/, offset_sticker_set_id.get(), limit)))); } void on_result(uint64 id, BufferSlice packet) override { @@ -583,14 +584,14 @@ class ReorderStickerSetsQuery : public Td::ResultHandler { bool is_masks_; public: - void send(bool is_masks, vector sticker_set_ids) { + void send(bool is_masks, vector sticker_set_ids) { is_masks_ = is_masks; int32 flags = 0; 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*/, std::move(sticker_set_ids))))); + 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))))); } void on_result(uint64 id, BufferSlice packet) override { @@ -615,20 +616,20 @@ class ReorderStickerSetsQuery : public Td::ResultHandler { class GetStickerSetQuery : public Td::ResultHandler { Promise promise_; - int64 sticker_set_id_; + StickerSetId sticker_set_id_; string sticker_set_name_; public: explicit GetStickerSetQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(int64 sticker_set_id, tl_object_ptr &&input_sticker_set) { + void send(StickerSetId sticker_set_id, tl_object_ptr &&input_sticker_set) { sticker_set_id_ = sticker_set_id; if (input_sticker_set->get_id() == telegram_api::inputStickerSetShortName::ID) { sticker_set_name_ = static_cast(input_sticker_set.get())->short_name_; } - LOG(INFO) << "Load sticker set " << sticker_set_id << " from server: " << to_string(input_sticker_set); + 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))))); } @@ -644,14 +645,14 @@ class GetStickerSetQuery : public Td::ResultHandler { constexpr int64 GREAT_MINDS_COLOR_SET_ID = 151353307481243663; if (set->set_->id_ == GREAT_MINDS_COLOR_SET_ID) { string great_minds_name = "TelegramGreatMinds"; - if (sticker_set_id_ == StickersManager::GREAT_MINDS_SET_ID || + if (sticker_set_id_.get() == StickersManager::GREAT_MINDS_SET_ID || trim(to_lower(sticker_set_name_)) == to_lower(great_minds_name)) { set->set_->id_ = StickersManager::GREAT_MINDS_SET_ID; set->set_->short_name_ = std::move(great_minds_name); } } - td->stickers_manager_->on_get_messages_sticker_set(sticker_set_id_, std::move(set), true); + td->stickers_manager_->on_get_messages_sticker_set(sticker_set_id_, std::move(set), true, "GetStickerSetQuery"); promise_.set_value(Unit()); } @@ -663,6 +664,31 @@ class GetStickerSetQuery : public Td::ResultHandler { } }; +class ReloadAnimatedEmojiStickerSetQuery : public Td::ResultHandler { + public: + void send() { + send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_getStickerSet( + telegram_api::make_object())))); + } + + 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 sticker_set_id = td->stickers_manager_->on_get_messages_sticker_set( + StickerSetId(), result_ptr.move_as_ok(), true, "ReloadAnimatedEmojiStickerSetQuery"); + if (sticker_set_id.is_valid()) { + td->stickers_manager_->on_get_animated_emoji_sticker_set(sticker_set_id); + } + } + + void on_error(uint64 id, Status status) override { + LOG(WARNING) << "Receive error for ReloadAnimatedEmojiStickerSetQuery: " << status; + } +}; + class SearchStickerSetsQuery : public Td::ResultHandler { string query_; @@ -694,14 +720,14 @@ class SearchStickerSetsQuery : public Td::ResultHandler { class InstallStickerSetQuery : public Td::ResultHandler { Promise promise_; - int64 set_id_; + StickerSetId set_id_; bool is_archived_; public: explicit InstallStickerSetQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(int64 set_id, tl_object_ptr &&input_set, bool is_archived) { + 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( @@ -727,13 +753,13 @@ class InstallStickerSetQuery : public Td::ResultHandler { class UninstallStickerSetQuery : public Td::ResultHandler { Promise promise_; - int64 set_id_; + StickerSetId set_id_; public: explicit UninstallStickerSetQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(int64 set_id, tl_object_ptr &&input_set) { + 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))))); @@ -763,10 +789,10 @@ class UninstallStickerSetQuery : public Td::ResultHandler { class ReadFeaturedStickerSetsQuery : public Td::ResultHandler { public: - void send(vector sticker_set_ids) { + 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(std::move(sticker_set_ids))))); + send_query(G()->net_query_creator().create(create_storer( + telegram_api::messages_readFeaturedStickers(StickersManager::convert_sticker_set_ids(sticker_set_ids))))); } void on_result(uint64 id, BufferSlice packet) override { @@ -861,7 +887,8 @@ class CreateNewStickerSetQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->stickers_manager_->on_get_messages_sticker_set(0, result_ptr.move_as_ok(), true); + td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, + "CreateNewStickerSetQuery"); promise_.set_value(Unit()); } @@ -890,7 +917,8 @@ class AddStickerToSetQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->stickers_manager_->on_get_messages_sticker_set(0, result_ptr.move_as_ok(), true); + td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, + "AddStickerToSetQuery"); promise_.set_value(Unit()); } @@ -919,7 +947,8 @@ class SetStickerPositionQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->stickers_manager_->on_get_messages_sticker_set(0, result_ptr.move_as_ok(), true); + td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, + "SetStickerPositionQuery"); promise_.set_value(Unit()); } @@ -948,7 +977,8 @@ class DeleteStickerFromSetQuery : public Td::ResultHandler { return on_error(id, result_ptr.move_as_error()); } - td->stickers_manager_->on_get_messages_sticker_set(0, result_ptr.move_as_ok(), true); + td->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true, + "DeleteStickerFromSetQuery"); promise_.set_value(Unit()); } @@ -990,30 +1020,21 @@ class StickersManager::StickerListLogEvent { class StickersManager::StickerSetListLogEvent { public: - vector sticker_set_ids; + vector sticker_set_ids; StickerSetListLogEvent() = default; - explicit StickerSetListLogEvent(vector sticker_set_ids) : sticker_set_ids(std::move(sticker_set_ids)) { + explicit StickerSetListLogEvent(vector sticker_set_ids) : sticker_set_ids(std::move(sticker_set_ids)) { } template void store(StorerT &storer) const { - StickersManager *stickers_manager = storer.context()->td().get_actor_unsafe()->stickers_manager_.get(); - td::store(narrow_cast(sticker_set_ids.size()), storer); - for (auto sticker_set_id : sticker_set_ids) { - stickers_manager->store_sticker_set_id(sticker_set_id, storer); - } + td::store(sticker_set_ids, storer); } template void parse(ParserT &parser) { - StickersManager *stickers_manager = parser.context()->td().get_actor_unsafe()->stickers_manager_.get(); - int32 size = parser.fetch_int(); - sticker_set_ids.resize(size); - for (auto &sticker_set_id : sticker_set_ids) { - stickers_manager->parse_sticker_set_id(sticker_set_id, parser); - } + td::parse(sticker_set_ids, parser); } }; @@ -1045,6 +1066,48 @@ StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent on_update_favorite_stickers_limit(G()->shared_config().get_option_integer("favorite_stickers_limit", 5)); } +void StickersManager::start_up() { + // add animated emoji 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"; + } 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"; + } + 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); + if (parts.size() != 3) { + LOG(ERROR) << "Can't parse " << animated_emoji_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; + } 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); + } + } + } + } else { + G()->td_db()->get_binlog_pmc()->erase("animated_emoji_sticker_set"); + } + + 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_); +} + void StickersManager::tear_down() { parent_.reset(); } @@ -1082,7 +1145,7 @@ tl_object_ptr StickersManager::get_sticker_object(FileId file_i : nullptr; const PhotoSize &thumbnail = sticker->m_thumbnail.file_id.is_valid() ? sticker->m_thumbnail : sticker->s_thumbnail; - return make_tl_object(sticker->set_id, sticker->dimensions.width, sticker->dimensions.height, + return make_tl_object(sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height, sticker->alt, sticker->is_animated, sticker->is_mask, std::move(mask_position), get_photo_size_object(td_->file_manager_.get(), &thumbnail), td_->file_manager_->get_file_object(file_id)); @@ -1097,7 +1160,7 @@ tl_object_ptr StickersManager::get_stickers_object(const vecto return result; } -tl_object_ptr StickersManager::get_sticker_set_object(int64 sticker_set_id) const { +tl_object_ptr StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const { const StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); CHECK(sticker_set->was_loaded); @@ -1115,14 +1178,14 @@ tl_object_ptr StickersManager::get_sticker_set_object(int64 } } return make_tl_object( - sticker_set->id, sticker_set->title, sticker_set->short_name, + sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_photo_size_object(td_->file_manager_.get(), &sticker_set->thumbnail), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis)); } tl_object_ptr StickersManager::get_sticker_sets_object(int32 total_count, - const vector &sticker_set_ids, + const vector &sticker_set_ids, size_t covers_limit) const { vector> result; result.reserve(sticker_set_ids.size()); @@ -1143,7 +1206,7 @@ tl_object_ptr StickersManager::get_sticker_sets_object(int3 return make_tl_object(total_count, std::move(result)); } -tl_object_ptr StickersManager::get_sticker_set_info_object(int64 sticker_set_id, +tl_object_ptr StickersManager::get_sticker_set_info_object(StickerSetId sticker_set_id, size_t covers_limit) const { const StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); @@ -1158,7 +1221,7 @@ tl_object_ptr StickersManager::get_sticker_set_info_obje } return make_tl_object( - sticker_set->id, sticker_set->title, sticker_set->short_name, + sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_photo_size_object(td_->file_manager_.get(), &sticker_set->thumbnail), sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official, sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, @@ -1166,7 +1229,7 @@ tl_object_ptr StickersManager::get_sticker_set_info_obje std::move(stickers)); } -tl_object_ptr StickersManager::get_input_sticker_set(int64 sticker_set_id) const { +tl_object_ptr StickersManager::get_input_sticker_set(StickerSetId sticker_set_id) const { auto sticker_set = get_sticker_set(sticker_set_id); if (sticker_set == nullptr) { return nullptr; @@ -1189,8 +1252,8 @@ FileId StickersManager::on_get_sticker(unique_ptr new_sticker, bool rep s->dimensions = new_sticker->dimensions; s->is_changed = true; } - if (s->set_id != new_sticker->set_id && new_sticker->set_id != 0) { - LOG_IF(ERROR, s->set_id != 0) << "Sticker " << file_id << " set_id has changed"; + if (s->set_id != new_sticker->set_id && new_sticker->set_id.is_valid()) { + LOG_IF(ERROR, s->set_id.is_valid()) << "Sticker " << file_id << " set_id has changed"; s->set_id = new_sticker->set_id; s->is_changed = true; } @@ -1318,7 +1381,7 @@ const StickersManager::Sticker *StickersManager::get_sticker(FileId file_id) con return sticker->second.get(); } -StickersManager::StickerSet *StickersManager::get_sticker_set(int64 sticker_set_id) { +StickersManager::StickerSet *StickersManager::get_sticker_set(StickerSetId sticker_set_id) { auto sticker_set = sticker_sets_.find(sticker_set_id); if (sticker_set == sticker_sets_.end()) { return nullptr; @@ -1327,7 +1390,7 @@ StickersManager::StickerSet *StickersManager::get_sticker_set(int64 sticker_set_ return sticker_set->second.get(); } -const StickersManager::StickerSet *StickersManager::get_sticker_set(int64 sticker_set_id) const { +const StickersManager::StickerSet *StickersManager::get_sticker_set(StickerSetId sticker_set_id) const { auto sticker_set = sticker_sets_.find(sticker_set_id); if (sticker_set == sticker_sets_.end()) { return nullptr; @@ -1336,31 +1399,31 @@ const StickersManager::StickerSet *StickersManager::get_sticker_set(int64 sticke return sticker_set->second.get(); } -int64 StickersManager::get_sticker_set_id(const tl_object_ptr &set_ptr) { +StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptr &set_ptr) { CHECK(set_ptr != nullptr); switch (set_ptr->get_id()) { case telegram_api::inputStickerSetEmpty::ID: - return 0; + return StickerSetId(); case telegram_api::inputStickerSetID::ID: - return static_cast(set_ptr.get())->id_; + return StickerSetId(static_cast(set_ptr.get())->id_); case telegram_api::inputStickerSetShortName::ID: LOG(ERROR) << "Receive sticker set by its short name"; return search_sticker_set(static_cast(set_ptr.get())->short_name_, Auto()); default: UNREACHABLE(); - return 0; + return StickerSetId(); } } -int64 StickersManager::add_sticker_set(tl_object_ptr &&set_ptr) { +StickerSetId StickersManager::add_sticker_set(tl_object_ptr &&set_ptr) { CHECK(set_ptr != nullptr); switch (set_ptr->get_id()) { case telegram_api::inputStickerSetEmpty::ID: - return 0; + return StickerSetId(); case telegram_api::inputStickerSetID::ID: { auto set = move_tl_object_as(set_ptr); - int64 set_id = set->id_; + StickerSetId set_id{set->id_}; add_sticker_set(set_id, set->access_hash_); return set_id; } @@ -1371,11 +1434,11 @@ int64 StickersManager::add_sticker_set(tl_object_ptr(); @@ -1482,7 +1545,7 @@ bool StickersManager::merge_stickers(FileId new_id, FileId old_id, bool can_dele tl_object_ptr StickersManager::get_input_sticker_set(const StickerSet *set) { CHECK(set != nullptr); - return make_tl_object(set->id, set->access_hash); + return make_tl_object(set->id.get(), set->access_hash); } void StickersManager::reload_installed_sticker_sets(bool is_masks, bool force) { @@ -1511,18 +1574,18 @@ void StickersManager::reload_featured_sticker_sets(bool force) { } } -int64 StickersManager::on_get_input_sticker_set(FileId sticker_file_id, - tl_object_ptr &&set_ptr, - MultiPromiseActor *load_data_multipromise_ptr) { +StickerSetId StickersManager::on_get_input_sticker_set(FileId sticker_file_id, + tl_object_ptr &&set_ptr, + MultiPromiseActor *load_data_multipromise_ptr) { if (set_ptr == nullptr) { - return 0; + return StickerSetId(); } switch (set_ptr->get_id()) { case telegram_api::inputStickerSetEmpty::ID: - return 0; + return StickerSetId(); case telegram_api::inputStickerSetID::ID: { auto set = move_tl_object_as(set_ptr); - int64 set_id = set->id_; + StickerSetId set_id{set->id_}; add_sticker_set(set_id, set->access_hash_); return set_id; } @@ -1533,7 +1596,7 @@ int64 StickersManager::on_get_input_sticker_set(FileId sticker_file_id, return search_sticker_set(set->short_name_, Auto()); } auto set_id = search_sticker_set(set->short_name_, load_data_multipromise_ptr->get_promise()); - if (set_id == 0) { + if (!set_id.is_valid()) { load_data_multipromise_ptr->add_promise( PromiseCreator::lambda([td = td_, sticker_file_id, short_name = set->short_name_](Result result) { if (result.is_ok()) { @@ -1546,14 +1609,14 @@ int64 StickersManager::on_get_input_sticker_set(FileId sticker_file_id, } default: UNREACHABLE(); - return 0; + return StickerSetId(); } } void StickersManager::on_resolve_sticker_set_short_name(FileId sticker_file_id, const string &short_name) { LOG(INFO) << "Resolve sticker " << sticker_file_id << " set to " << short_name; - int64 set_id = search_sticker_set(short_name, Auto()); - if (set_id != 0) { + StickerSetId set_id = search_sticker_set(short_name, Auto()); + if (set_id.is_valid()) { auto &s = stickers_[sticker_file_id]; if (s == nullptr) { LOG(ERROR) << "Can't find sticker " << sticker_file_id; @@ -1623,7 +1686,7 @@ bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) co return true; } } else if (!file_view.is_encrypted()) { - if (sticker->set_id != 0) { + if (sticker->set_id.is_valid()) { // stickers within a set can be sent by id and access_hash return true; } @@ -1648,7 +1711,7 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id, auto file_view = td_->file_manager_->get_file_view(sticker_file_id); if (file_view.is_encrypted_secret()) { if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return {}; @@ -1657,7 +1720,7 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id, return {}; } } else if (!file_view.is_encrypted()) { - if (sticker->set_id == 0) { + if (!sticker->set_id.is_valid()) { // stickers without set can't be sent by id and access_hash return {}; } @@ -1666,7 +1729,7 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id, } tl_object_ptr input_sticker_set = make_tl_object(); - if (sticker->set_id) { + if (sticker->set_id.is_valid()) { const StickerSet *sticker_set = get_sticker_set(sticker->set_id); CHECK(sticker_set != nullptr); if (sticker_set->is_inited) { @@ -1697,13 +1760,13 @@ SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id, auto &remote_location = file_view.remote_location(); if (remote_location.is_web()) { // web stickers shouldn't have set_id - LOG(ERROR) << "Have a web sticker in set " << sticker->set_id; + LOG(ERROR) << "Have a web sticker in " << sticker->set_id; return {}; } return SecretInputMedia{nullptr, make_tl_object( remote_location.get_id(), remote_location.get_access_hash(), 0 /*date*/, get_sticker_mime_type(sticker), narrow_cast(file_view.size()), - make_tl_object(), + make_tl_object("t"), remote_location.get_dc_id().get_raw_id(), std::move(attributes))}; } } @@ -1715,8 +1778,8 @@ tl_object_ptr StickersManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); @@ -1748,8 +1811,10 @@ tl_object_ptr StickersManager::get_input_media( return nullptr; } -int64 StickersManager::on_get_sticker_set(tl_object_ptr &&set, bool is_changed) { - int64 set_id = set->id_; +StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr &&set, bool is_changed, + const char *source) { + CHECK(set != nullptr); + StickerSetId set_id{set->id_}; StickerSet *s = add_sticker_set(set_id, set->access_hash_); bool is_installed = (set->flags_ & telegram_api::stickerSet::INSTALLED_DATE_MASK) != 0; @@ -1760,12 +1825,12 @@ int64 StickersManager::on_get_sticker_set(tl_object_ptrthumb_ != nullptr) { - auto photo_size = get_photo_size(td_->file_manager_.get(), {set_id, s->access_hash}, 0, 0, "", + auto photo_size = get_photo_size(td_->file_manager_.get(), {set_id.get(), s->access_hash}, 0, 0, "", DcId::create(set->thumb_dc_id_), DialogId(), std::move(set->thumb_), true, false); if (photo_size.get_offset() == 0) { thumbnail = std::move(photo_size.get<0>()); } else { - LOG(ERROR) << "Receive minithumbnail for a sticker set " << set_id; + LOG(ERROR) << "Receive minithumbnail for a " << set_id; } } if (!s->is_inited) { @@ -1783,32 +1848,32 @@ int64 StickersManager::on_get_sticker_set(tl_object_ptrid == set_id); if (s->access_hash != set->access_hash_) { - LOG(INFO) << "Sticker set " << set_id << " access hash has changed"; + LOG(INFO) << "Access hash of " << set_id << " has changed"; s->access_hash = set->access_hash_; s->is_changed = true; } if (s->title != set->title_) { - LOG(INFO) << "Sticker set " << set_id << " title has changed"; + LOG(INFO) << "Title of " << set_id << " has changed"; s->title = std::move(set->title_); s->is_changed = true; - if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id)) { - installed_sticker_sets_hints_[s->is_masks].add(set_id, PSLICE() << s->title << ' ' << s->short_name); + if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id.get())) { + installed_sticker_sets_hints_[s->is_masks].add(set_id.get(), PSLICE() << s->title << ' ' << s->short_name); } } if (s->short_name != set->short_name_) { - LOG(ERROR) << "Sticker set " << set_id << " short name has changed from \"" << s->short_name << "\" to \"" - << set->short_name_ << "\""; + LOG(ERROR) << "Short name of " << set_id << " has changed from \"" << s->short_name << "\" to \"" + << set->short_name_ << "\" from " << source; short_name_to_sticker_set_id_.erase(clean_username(s->short_name)); s->short_name = std::move(set->short_name_); s->is_changed = true; - if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id)) { - installed_sticker_sets_hints_[s->is_masks].add(set_id, PSLICE() << s->title << ' ' << s->short_name); + if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id.get())) { + installed_sticker_sets_hints_[s->is_masks].add(set_id.get(), PSLICE() << s->title << ' ' << s->short_name); } } if (s->thumbnail != thumbnail) { - LOG(INFO) << "Sticker set " << set_id << " thumbnail has changed from " << s->thumbnail << " to " << thumbnail; + LOG(INFO) << "Thumbnail of " << set_id << " has changed from " << s->thumbnail << " to " << thumbnail; s->thumbnail = std::move(thumbnail); s->is_changed = true; } @@ -1829,8 +1894,14 @@ int64 StickersManager::on_get_sticker_set(tl_object_ptris_official = is_official; s->is_changed = true; } - LOG_IF(ERROR, s->is_animated != is_animated) << "Animated type of the sticker set " << set_id << " has changed"; - LOG_IF(ERROR, s->is_masks != is_masks) << "Masks type of the sticker set " << set_id << " has changed"; + if (s->is_animated != is_animated) { + LOG(ERROR) << "Animated type of " << set_id << "/" << s->short_name << " has changed from " << s->is_animated + << " to " << is_animated << " from " << source; + s->is_animated = is_animated; + s->is_changed = true; + } + LOG_IF(ERROR, s->is_masks != is_masks) << "Masks type of " << set_id << "/" << s->short_name << " has changed from " + << s->is_masks << " to " << is_masks << " from " << source; } short_name_to_sticker_set_id_.emplace(clean_username(s->short_name), set_id); @@ -1839,14 +1910,14 @@ int64 StickersManager::on_get_sticker_set(tl_object_ptr &&set_ptr, - bool is_changed) { - int64 set_id = 0; +StickerSetId StickersManager::on_get_sticker_set_covered(tl_object_ptr &&set_ptr, + bool is_changed, const char *source) { + StickerSetId set_id; switch (set_ptr->get_id()) { case telegram_api::stickerSetCovered::ID: { auto covered_set = move_tl_object_as(set_ptr); - set_id = on_get_sticker_set(std::move(covered_set->set_), is_changed); - if (set_id == 0) { + set_id = on_get_sticker_set(std::move(covered_set->set_), is_changed, source); + if (!set_id.is_valid()) { break; } @@ -1863,7 +1934,7 @@ int64 StickersManager::on_get_sticker_set_covered(tl_object_ptrsticker_ids; auto sticker_id = on_get_sticker_document(std::move(covered_set->cover_)).second; - if (sticker_id.is_valid() && std::find(sticker_ids.begin(), sticker_ids.end(), sticker_id) == sticker_ids.end()) { + if (sticker_id.is_valid() && !td::contains(sticker_ids, sticker_id)) { sticker_ids.push_back(sticker_id); sticker_set->is_changed = true; } @@ -1872,8 +1943,8 @@ int64 StickersManager::on_get_sticker_set_covered(tl_object_ptr(set_ptr); - set_id = on_get_sticker_set(std::move(multicovered_set->set_), is_changed); - if (set_id == 0) { + set_id = on_get_sticker_set(std::move(multicovered_set->set_), is_changed, source); + if (!set_id.is_valid()) { break; } @@ -1887,8 +1958,7 @@ int64 StickersManager::on_get_sticker_set_covered(tl_object_ptrcovers_) { auto sticker_id = on_get_sticker_document(std::move(cover)).second; - if (sticker_id.is_valid() && - std::find(sticker_ids.begin(), sticker_ids.end(), sticker_id) == sticker_ids.end()) { + if (sticker_id.is_valid() && !td::contains(sticker_ids, sticker_id)) { sticker_ids.push_back(sticker_id); sticker_set->is_changed = true; } @@ -1902,19 +1972,19 @@ int64 StickersManager::on_get_sticker_set_covered(tl_object_ptr &&set, - bool is_changed) { +StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_set_id, + tl_object_ptr &&set, + bool is_changed, const char *source) { LOG(INFO) << "Receive sticker set " << to_string(set); - auto set_id = on_get_sticker_set(std::move(set->set_), is_changed); - if (set_id == 0) { - return; + auto set_id = on_get_sticker_set(std::move(set->set_), is_changed, source); + if (!set_id.is_valid()) { + return set_id; } - if (sticker_set_id != 0 && sticker_set_id != set_id) { - LOG(ERROR) << "Expected sticker set " << sticker_set_id << ", but receive sticker set " << set_id; + if (sticker_set_id.is_valid() && sticker_set_id != set_id) { + LOG(ERROR) << "Expected " << sticker_set_id << ", but receive " << set_id << " from " << source; on_load_sticker_set_fail(sticker_set_id, Status::Error(500, "Internal server error")); - return; + return StickerSetId(); } auto s = get_sticker_set(set_id); @@ -1927,7 +1997,7 @@ void StickersManager::on_get_messages_sticker_set(int64 sticker_set_id, if (s->is_loaded) { update_sticker_set(s); send_update_installed_sticker_sets(); - return; + return set_id; } s->was_loaded = true; s->is_loaded = true; @@ -1952,7 +2022,7 @@ void StickersManager::on_get_messages_sticker_set(int64 sticker_set_id, } } if (static_cast(s->sticker_ids.size()) != s->sticker_count) { - LOG(ERROR) << "Wrong sticker set size specified"; + LOG(ERROR) << "Wrong sticker set size specified in " << set_id << " from " << source; s->sticker_count = static_cast(s->sticker_ids.size()); } @@ -1965,7 +2035,7 @@ void StickersManager::on_get_messages_sticker_set(int64 sticker_set_id, for (int64 document_id : pack->documents_) { auto it = document_id_to_sticker_id.find(document_id); if (it == document_id_to_sticker_id.end()) { - LOG(ERROR) << "Can't find document with id " << document_id; + LOG(ERROR) << "Can't find document with id " << document_id << " in " << set_id << " from " << source; continue; } @@ -1974,7 +2044,7 @@ void StickersManager::on_get_messages_sticker_set(int64 sticker_set_id, } auto &sticker_ids = s->emoji_stickers_map_[remove_emoji_modifiers(pack->emoticon_)]; for (auto sticker_id : stickers) { - if (std::find(sticker_ids.begin(), sticker_ids.end(), sticker_id) == sticker_ids.end()) { + if (!td::contains(sticker_ids, sticker_id)) { sticker_ids.push_back(sticker_id); } } @@ -1984,10 +2054,11 @@ void StickersManager::on_get_messages_sticker_set(int64 sticker_set_id, update_sticker_set(s); update_load_requests(s, true, Status::OK()); send_update_installed_sticker_sets(); + return set_id; } -void StickersManager::on_load_sticker_set_fail(int64 sticker_set_id, const Status &error) { - if (sticker_set_id == 0) { +void StickersManager::on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error) { + if (!sticker_set_id.is_valid()) { return; } update_load_requests(get_sticker_set(sticker_set_id), true, error); @@ -2034,6 +2105,28 @@ void StickersManager::update_load_request(uint32 load_request_id, const Status & } } +void StickersManager::on_get_animated_emoji_sticker_set(StickerSetId sticker_set_id) { + 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()) { + 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); + + 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_); +} + void StickersManager::on_get_installed_sticker_sets(bool is_masks, tl_object_ptr &&stickers_ptr) { next_installed_sticker_sets_load_time_[is_masks] = Time::now_cached() + Random::fast(30 * 60, 50 * 60); @@ -2047,19 +2140,19 @@ void StickersManager::on_get_installed_sticker_sets(bool is_masks, CHECK(constructor_id == telegram_api::messages_allStickers::ID); auto stickers = move_tl_object_as(stickers_ptr); - std::unordered_set uninstalled_sticker_sets(installed_sticker_set_ids_[is_masks].begin(), - installed_sticker_set_ids_[is_masks].end()); + std::unordered_set uninstalled_sticker_sets( + installed_sticker_set_ids_[is_masks].begin(), installed_sticker_set_ids_[is_masks].end()); - vector sets_to_load; - vector installed_sticker_set_ids; - vector hashes; - vector sticker_set_ids; + vector sets_to_load; + vector installed_sticker_set_ids; + vector debug_hashes; + vector debug_sticker_set_ids; std::reverse(stickers->sets_.begin(), stickers->sets_.end()); // apply installed sticker sets in reverse order for (auto &set : stickers->sets_) { - hashes.push_back(set->hash_); - sticker_set_ids.push_back(set->id_); - int64 set_id = on_get_sticker_set(std::move(set), false); - if (set_id == 0) { + debug_hashes.push_back(set->hash_); + debug_sticker_set_ids.push_back(set->id_); + StickerSetId set_id = on_get_sticker_set(std::move(set), false, "on_get_installed_sticker_sets"); + if (!set_id.is_valid()) { continue; } @@ -2080,9 +2173,9 @@ void StickersManager::on_get_installed_sticker_sets(bool is_masks, sets_to_load.push_back(set_id); } } - std::reverse(hashes.begin(), hashes.end()); + std::reverse(debug_hashes.begin(), debug_hashes.end()); std::reverse(installed_sticker_set_ids.begin(), installed_sticker_set_ids.end()); - std::reverse(sticker_set_ids.begin(), sticker_set_ids.end()); + std::reverse(debug_sticker_set_ids.begin(), debug_sticker_set_ids.end()); if (!sets_to_load.empty()) { load_sticker_sets(std::move(sets_to_load), Auto()); @@ -2099,12 +2192,12 @@ void StickersManager::on_get_installed_sticker_sets(bool is_masks, on_load_installed_sticker_sets_finished(is_masks, std::move(installed_sticker_set_ids)); if (installed_sticker_sets_hash_[is_masks] != stickers->hash_) { - LOG(ERROR) << "Sticker sets hash mismatch: server hash list = " << format::as_array(hashes) + LOG(ERROR) << "Sticker sets hash mismatch: server hash list = " << format::as_array(debug_hashes) << ", client hash list = " << format::as_array( transform(installed_sticker_set_ids_[is_masks], - [this](int64 sticker_set_id) { return get_sticker_set(sticker_set_id)->hash; })) - << ", server sticker set list = " << format::as_array(sticker_set_ids) + [this](StickerSetId sticker_set_id) { return get_sticker_set(sticker_set_id)->hash; })) + << ", server sticker set list = " << format::as_array(debug_sticker_set_ids) << ", client sticker set list = " << format::as_array(installed_sticker_set_ids_[is_masks]) << ", server hash = " << stickers->hash_ << ", client hash = " << installed_sticker_sets_hash_[is_masks]; } @@ -2152,7 +2245,7 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for */ } - vector sets_to_load; + vector sets_to_load; bool need_load = false; for (const auto &sticker_set_id : installed_sticker_set_ids_[0]) { const StickerSet *sticker_set = get_sticker_set(sticker_set_id); @@ -2172,7 +2265,7 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for prepend_sticker_ids.reserve(favorite_sticker_ids_.size() + recent_sticker_ids_[0].size()); append(prepend_sticker_ids, recent_sticker_ids_[0]); for (auto sticker_id : favorite_sticker_ids_) { - if (std::find(prepend_sticker_ids.begin(), prepend_sticker_ids.end(), sticker_id) == prepend_sticker_ids.end()) { + if (!td::contains(prepend_sticker_ids, sticker_id)) { prepend_sticker_ids.push_back(sticker_id); } } @@ -2190,8 +2283,8 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for LOG(INFO) << "Have " << recent_sticker_ids_[0] << " recent and " << favorite_sticker_ids_ << " favorite stickers"; for (const auto &sticker_id : prepend_sticker_ids) { const Sticker *s = get_sticker(sticker_id); - LOG(INFO) << "Have prepend sticker " << sticker_id << " from set " << s->set_id; - if (s->set_id != 0 && std::find(sets_to_load.begin(), sets_to_load.end(), s->set_id) == sets_to_load.end()) { + LOG(INFO) << "Have prepend sticker " << sticker_id << " from " << s->set_id; + if (s->set_id.is_valid() && !td::contains(sets_to_load, s->set_id)) { const StickerSet *sticker_set = get_sticker_set(s->set_id); if (sticker_set == nullptr || !sticker_set->is_loaded) { sets_to_load.push_back(s->set_id); @@ -2241,8 +2334,7 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for continue; } - if (std::find(examined_sticker_sets.begin(), examined_sticker_sets.end(), sticker_set) == - examined_sticker_sets.end()) { + if (!td::contains(examined_sticker_sets, sticker_set)) { examined_sticker_sets.push_back(sticker_set); } } @@ -2252,7 +2344,7 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for for (auto sticker_set : examined_sticker_sets) { auto it = sticker_set->emoji_stickers_map_.find(emoji); if (it != sticker_set->emoji_stickers_map_.end()) { - LOG(INFO) << "Add " << it->second << " stickers from set " << sticker_set->id; + LOG(INFO) << "Add " << it->second << " stickers from " << sticker_set->id; append(result, it->second); } } @@ -2280,12 +2372,12 @@ vector StickersManager::get_stickers(string emoji, int32 limit, bool for if (remove_emoji_modifiers(s->alt) == emoji) { LOG(INFO) << "Found prepend sticker " << sticker_id << " main emoji matches"; is_good = true; - } else if (s->set_id != 0) { + } else if (s->set_id.is_valid()) { const StickerSet *sticker_set = get_sticker_set(s->set_id); if (sticker_set != nullptr && sticker_set->was_loaded) { auto map_it = sticker_set->emoji_stickers_map_.find(emoji); if (map_it != sticker_set->emoji_stickers_map_.end()) { - if (std::find(map_it->second.begin(), map_it->second.end(), sticker_id) != map_it->second.end()) { + if (td::contains(map_it->second, sticker_id)) { LOG(INFO) << "Found prepend sticker " << sticker_id << " has matching emoji"; is_good = true; } @@ -2409,7 +2501,7 @@ void StickersManager::on_find_stickers_fail(const string &emoji, Status &&error) } } -vector StickersManager::get_installed_sticker_sets(bool is_masks, Promise &&promise) { +vector StickersManager::get_installed_sticker_sets(bool is_masks, Promise &&promise) { if (!are_installed_sticker_sets_loaded_[is_masks]) { load_installed_sticker_sets(is_masks, std::move(promise)); return {}; @@ -2446,47 +2538,49 @@ bool StickersManager::update_sticker_set_cache(const StickerSet *sticker_set, Pr return false; } -int64 StickersManager::get_sticker_set(int64 set_id, Promise &&promise) { +StickerSetId StickersManager::get_sticker_set(StickerSetId set_id, Promise &&promise) { const StickerSet *sticker_set = get_sticker_set(set_id); if (sticker_set == nullptr) { - if (set_id == GREAT_MINDS_SET_ID) { - do_reload_sticker_set(set_id, make_tl_object(set_id, 0), std::move(promise)); - return 0; + if (set_id.get() == GREAT_MINDS_SET_ID) { + do_reload_sticker_set(set_id, make_tl_object(set_id.get(), 0), + std::move(promise)); + return StickerSetId(); } promise.set_error(Status::Error(400, "Sticker set not found")); - return 0; + return StickerSetId(); } if (update_sticker_set_cache(sticker_set, promise)) { - return 0; + return StickerSetId(); } promise.set_value(Unit()); return set_id; } -int64 StickersManager::search_sticker_set(const string &short_name_to_search, Promise &&promise) { +StickerSetId StickersManager::search_sticker_set(const string &short_name_to_search, Promise &&promise) { string short_name = clean_username(short_name_to_search); 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) { auto set_to_load = make_tl_object(short_name); - do_reload_sticker_set(0, std::move(set_to_load), std::move(promise)); - return 0; + do_reload_sticker_set(StickerSetId(), std::move(set_to_load), std::move(promise)); + return StickerSetId(); } if (update_sticker_set_cache(sticker_set, promise)) { - return 0; + return StickerSetId(); } promise.set_value(Unit()); return sticker_set->id; } -std::pair> StickersManager::search_installed_sticker_sets(bool is_masks, const string &query, - int32 limit, Promise &&promise) { +std::pair> StickersManager::search_installed_sticker_sets(bool is_masks, + const string &query, int32 limit, + Promise &&promise) { LOG(INFO) << "Search installed " << (is_masks ? "mask " : "") << "sticker sets with query = \"" << query << "\" and limit = " << limit; @@ -2503,10 +2597,10 @@ std::pair> StickersManager::search_installed_sticker_sets(b std::pair> result = installed_sticker_sets_hints_[is_masks].search(query, limit); promise.set_value(Unit()); - return {narrow_cast(result.first), std::move(result.second)}; + return {narrow_cast(result.first), convert_sticker_set_ids(result.second)}; } -vector StickersManager::search_sticker_sets(const string &query, Promise &&promise) { +vector StickersManager::search_sticker_sets(const string &query, Promise &&promise) { auto q = clean_name(query, 1000); auto it = found_sticker_sets_.find(q); if (it != found_sticker_sets_.end()) { @@ -2531,12 +2625,12 @@ void StickersManager::on_find_sticker_sets_success( return on_find_sticker_sets_fail(query, Status::Error(500, "Receive messages.foundStickerSetsNotModified")); case telegram_api::messages_foundStickerSets::ID: { auto found_stickers_sets = move_tl_object_as(sticker_sets); - vector &sticker_set_ids = found_sticker_sets_[query]; + vector &sticker_set_ids = found_sticker_sets_[query]; CHECK(sticker_set_ids.empty()); for (auto &sticker_set : found_stickers_sets->sets_) { - int64 set_id = on_get_sticker_set_covered(std::move(sticker_set), true); - if (set_id == 0) { + StickerSetId set_id = on_get_sticker_set_covered(std::move(sticker_set), true, "on_find_sticker_sets_success"); + if (!set_id.is_valid()) { continue; } @@ -2576,7 +2670,8 @@ void StickersManager::on_find_sticker_sets_fail(const string &query, Status &&er } } -void StickersManager::change_sticker_set(int64 set_id, bool is_installed, bool is_archived, Promise &&promise) { +void StickersManager::change_sticker_set(StickerSetId set_id, bool is_installed, bool is_archived, + Promise &&promise) { if (is_installed && is_archived) { return promise.set_error(Status::Error(400, "Sticker set can't be installed and archived simultaneously")); } @@ -2615,8 +2710,8 @@ void StickersManager::change_sticker_set(int64 set_id, bool is_installed, bool i void StickersManager::on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived, bool is_changed, bool from_database) { - LOG(INFO) << "Update sticker set " << sticker_set->id << ": installed = " << is_installed - << ", archived = " << is_archived << ", changed = " << is_changed; + LOG(INFO) << "Update " << sticker_set->id << ": installed = " << is_installed << ", archived = " << is_archived + << ", changed = " << is_changed; CHECK(sticker_set->is_inited); if (is_archived) { is_installed = true; @@ -2635,28 +2730,27 @@ void StickersManager::on_update_sticker_set(StickerSet *sticker_set, bool is_ins bool is_added = sticker_set->is_installed && !sticker_set->is_archived; if (was_added != is_added) { - vector &sticker_set_ids = installed_sticker_set_ids_[sticker_set->is_masks]; + vector &sticker_set_ids = installed_sticker_set_ids_[sticker_set->is_masks]; need_update_installed_sticker_sets_[sticker_set->is_masks] = true; if (is_added) { installed_sticker_sets_hints_[sticker_set->is_masks].add( - sticker_set->id, PSLICE() << sticker_set->title << ' ' << sticker_set->short_name); + sticker_set->id.get(), PSLICE() << sticker_set->title << ' ' << sticker_set->short_name); sticker_set_ids.insert(sticker_set_ids.begin(), sticker_set->id); } else { - installed_sticker_sets_hints_[sticker_set->is_masks].remove(sticker_set->id); - sticker_set_ids.erase(std::remove(sticker_set_ids.begin(), sticker_set_ids.end(), sticker_set->id), - sticker_set_ids.end()); + installed_sticker_sets_hints_[sticker_set->is_masks].remove(sticker_set->id.get()); + td::remove(sticker_set_ids, sticker_set->id); } } if (was_archived != is_archived && is_changed) { int32 &total_count = total_archived_sticker_set_count_[sticker_set->is_masks]; - vector &sticker_set_ids = archived_sticker_set_ids_[sticker_set->is_masks]; + vector &sticker_set_ids = archived_sticker_set_ids_[sticker_set->is_masks]; if (total_count < 0) { return; } if (is_archived) { - if (std::find(sticker_set_ids.begin(), sticker_set_ids.end(), sticker_set->id) == sticker_set_ids.end()) { + if (!td::contains(sticker_set_ids, sticker_set->id)) { total_count++; sticker_set_ids.insert(sticker_set_ids.begin(), sticker_set->id); } @@ -2666,8 +2760,7 @@ void StickersManager::on_update_sticker_set(StickerSet *sticker_set, bool is_ins LOG(ERROR) << "Total count of archived sticker sets became negative"; total_count = 0; } - sticker_set_ids.erase(std::remove(sticker_set_ids.begin(), sticker_set_ids.end(), sticker_set->id), - sticker_set_ids.end()); + td::remove(sticker_set_ids, sticker_set->id); } } } @@ -2714,7 +2807,7 @@ void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks return reload_installed_sticker_sets(is_masks, true); } - vector sets_to_load; + vector sets_to_load; for (auto sticker_set_id : log_event.sticker_set_ids) { StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); @@ -2735,16 +2828,17 @@ void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks })); } -void StickersManager::on_load_installed_sticker_sets_finished(bool is_masks, vector &&installed_sticker_set_ids, +void StickersManager::on_load_installed_sticker_sets_finished(bool is_masks, + vector &&installed_sticker_set_ids, bool from_database) { bool need_reload = false; - vector old_installed_sticker_set_ids; + vector old_installed_sticker_set_ids; if (!are_installed_sticker_sets_loaded_[is_masks] && !installed_sticker_set_ids_[is_masks].empty()) { old_installed_sticker_set_ids = std::move(installed_sticker_set_ids_[is_masks]); } installed_sticker_set_ids_[is_masks].clear(); for (auto set_id : installed_sticker_set_ids) { - CHECK(set_id != 0); + CHECK(set_id.is_valid()); auto sticker_set = get_sticker_set(set_id); CHECK(sticker_set != nullptr); @@ -2779,12 +2873,12 @@ void StickersManager::on_load_installed_sticker_sets_finished(bool is_masks, vec } } -string StickersManager::get_sticker_set_database_key(int64 set_id) { - return PSTRING() << "ss" << set_id; +string StickersManager::get_sticker_set_database_key(StickerSetId set_id) { + return PSTRING() << "ss" << set_id.get(); } -string StickersManager::get_full_sticker_set_database_key(int64 set_id) { - return PSTRING() << "ssf" << set_id; +string StickersManager::get_full_sticker_set_database_key(StickerSetId set_id) { + return PSTRING() << "ssf" << set_id.get(); } string StickersManager::get_sticker_set_database_value(const StickerSet *s, bool with_stickers) { @@ -2794,7 +2888,7 @@ string StickersManager::get_sticker_set_database_value(const StickerSet *s, bool BufferSlice value_buffer{storer_calc_length.get_length()}; auto value = value_buffer.as_slice(); - LOG(DEBUG) << "Sticker set " << s->id << " serialized size is " << value.size(); + LOG(DEBUG) << "Serialized size of " << s->id << " is " << value.size(); LogEventStorerUnsafe storer_unsafe(value.ubegin()); store_sticker_set(s, with_stickers, storer_unsafe); @@ -2807,7 +2901,7 @@ void StickersManager::update_sticker_set(StickerSet *sticker_set) { if (sticker_set->is_changed) { sticker_set->is_changed = false; if (G()->parameters().use_file_db) { - LOG(INFO) << "Save sticker set " << sticker_set->id << " to database"; + LOG(INFO) << "Save " << sticker_set->id << " to database"; if (sticker_set->is_inited) { G()->td_db()->get_sqlite_pmc()->set(get_sticker_set_database_key(sticker_set->id), get_sticker_set_database_value(sticker_set, false), Auto()); @@ -2823,7 +2917,7 @@ void StickersManager::update_sticker_set(StickerSet *sticker_set) { } } -void StickersManager::load_sticker_sets(vector &&sticker_set_ids, Promise &&promise) { +void StickersManager::load_sticker_sets(vector &&sticker_set_ids, Promise &&promise) { if (sticker_set_ids.empty()) { promise.set_value(Unit()); return; @@ -2842,21 +2936,22 @@ void StickersManager::load_sticker_sets(vector &&sticker_set_ids, Promise sticker_set->load_requests.push_back(load_request_id); if (sticker_set->load_requests.size() == 1u) { if (G()->parameters().use_file_db && !sticker_set->was_loaded) { - LOG(INFO) << "Trying to load sticker set " << sticker_set_id << " with stickers from database"; + LOG(INFO) << "Trying to load " << sticker_set_id << " with stickers from database"; G()->td_db()->get_sqlite_pmc()->get( get_full_sticker_set_database_key(sticker_set_id), PromiseCreator::lambda([sticker_set_id](string value) { send_closure(G()->stickers_manager(), &StickersManager::on_load_sticker_set_from_database, sticker_set_id, true, std::move(value)); })); } else { - LOG(INFO) << "Trying to load sticker set " << sticker_set_id << " with stickers from server"; + LOG(INFO) << "Trying to load " << sticker_set_id << " with stickers from server"; do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), Auto()); } } } } -void StickersManager::load_sticker_sets_without_stickers(vector &&sticker_set_ids, Promise &&promise) { +void StickersManager::load_sticker_sets_without_stickers(vector &&sticker_set_ids, + Promise &&promise) { if (sticker_set_ids.empty()) { promise.set_value(Unit()); return; @@ -2878,14 +2973,14 @@ void StickersManager::load_sticker_sets_without_stickers(vector &&sticker sticker_set->load_without_stickers_requests.push_back(load_request_id); if (sticker_set->load_without_stickers_requests.size() == 1u) { if (G()->parameters().use_file_db) { - LOG(INFO) << "Trying to load sticker set " << sticker_set_id << " from database"; + LOG(INFO) << "Trying to load " << sticker_set_id << " from database"; G()->td_db()->get_sqlite_pmc()->get( get_sticker_set_database_key(sticker_set_id), PromiseCreator::lambda([sticker_set_id](string value) { send_closure(G()->stickers_manager(), &StickersManager::on_load_sticker_set_from_database, sticker_set_id, false, std::move(value)); })); } else { - LOG(INFO) << "Trying to load sticker set " << sticker_set_id << " from server"; + LOG(INFO) << "Trying to load " << sticker_set_id << " from server"; do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), Auto()); } } @@ -2893,15 +2988,15 @@ void StickersManager::load_sticker_sets_without_stickers(vector &&sticker } } -void StickersManager::on_load_sticker_set_from_database(int64 sticker_set_id, bool with_stickers, string value) { +void StickersManager::on_load_sticker_set_from_database(StickerSetId sticker_set_id, bool with_stickers, string value) { StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); if (sticker_set->was_loaded) { - LOG(INFO) << "Sticker set " << sticker_set_id << " was loaded"; + LOG(INFO) << "Receive from database previously loaded " << sticker_set_id; return; } if (!with_stickers && sticker_set->is_inited) { - LOG(INFO) << "Sticker set " << sticker_set_id << " was inited"; + LOG(INFO) << "Receive from database previously inited " << sticker_set_id; return; } @@ -2916,25 +3011,25 @@ void StickersManager::on_load_sticker_set_from_database(int64 sticker_set_id, bo return do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), Auto()); } - LOG(INFO) << "Successfully loaded sticker set " << sticker_set_id << " with" << (with_stickers ? "" : "out") + LOG(INFO) << "Successfully loaded " << sticker_set_id << " with" << (with_stickers ? "" : "out") << " stickers of size " << value.size() << " from database"; auto old_sticker_count = sticker_set->sticker_ids.size(); { - LOG_IF(ERROR, sticker_set->is_changed) << "Sticker set with" << (with_stickers ? "" : "out") << " stickers " - << sticker_set_id << " was changed before it is loaded from database"; + LOG_IF(ERROR, sticker_set->is_changed) << sticker_set_id << " with" << (with_stickers ? "" : "out") + << " stickers was changed before it is loaded from database"; LogEventParser parser(value); parse_sticker_set(sticker_set, parser); LOG_IF(ERROR, sticker_set->is_changed) - << "Sticker set with" << (with_stickers ? "" : "out") << " stickers " << sticker_set_id << " is changed"; + << sticker_set_id << " with" << (with_stickers ? "" : "out") << " stickers is changed"; parser.fetch_end(); auto status = parser.get_status(); if (status.is_error()) { G()->td_db()->get_sqlite_sync_pmc()->erase(with_stickers ? get_full_sticker_set_database_key(sticker_set_id) : get_sticker_set_database_key(sticker_set_id)); // need to crash, because the current StickerSet state is spoiled by parse_sticker_set - LOG(FATAL) << "Failed to parse sticker set " << sticker_set_id << ": " << status << ' ' + LOG(FATAL) << "Failed to parse " << sticker_set_id << ": " << status << ' ' << format::as_hex_dump<4>(Slice(value)); } } @@ -2950,12 +3045,13 @@ void StickersManager::on_load_sticker_set_from_database(int64 sticker_set_id, bo update_load_requests(sticker_set, with_stickers, Status::OK()); } -void StickersManager::reload_sticker_set(int64 sticker_set_id, int64 access_hash, Promise &&promise) { - do_reload_sticker_set(sticker_set_id, make_tl_object(sticker_set_id, access_hash), +void StickersManager::reload_sticker_set(StickerSetId sticker_set_id, int64 access_hash, Promise &&promise) { + do_reload_sticker_set(sticker_set_id, + make_tl_object(sticker_set_id.get(), access_hash), std::move(promise)); } -void StickersManager::do_reload_sticker_set(int64 sticker_set_id, +void StickersManager::do_reload_sticker_set(StickerSetId sticker_set_id, tl_object_ptr &&input_sticker_set, Promise &&promise) const { if (G()->close_flag()) { @@ -2964,7 +3060,7 @@ void StickersManager::do_reload_sticker_set(int64 sticker_set_id, td_->create_handler(std::move(promise))->send(sticker_set_id, std::move(input_sticker_set)); } -void StickersManager::on_install_sticker_set(int64 set_id, bool is_archived, +void StickersManager::on_install_sticker_set(StickerSetId set_id, bool is_archived, tl_object_ptr &&result) { StickerSet *sticker_set = get_sticker_set(set_id); CHECK(sticker_set != nullptr); @@ -2977,8 +3073,9 @@ void StickersManager::on_install_sticker_set(int64 set_id, bool is_archived, case telegram_api::messages_stickerSetInstallResultArchive::ID: { auto archived_sets = move_tl_object_as(result); for (auto &archived_set_ptr : archived_sets->sets_) { - int64 archived_sticker_set_id = on_get_sticker_set_covered(std::move(archived_set_ptr), true); - if (archived_sticker_set_id != 0) { + StickerSetId archived_sticker_set_id = + on_get_sticker_set_covered(std::move(archived_set_ptr), true, "on_install_sticker_set"); + if (archived_sticker_set_id.is_valid()) { auto archived_sticker_set = get_sticker_set(archived_sticker_set_id); CHECK(archived_sticker_set != nullptr); update_sticker_set(archived_sticker_set); @@ -2993,7 +3090,7 @@ void StickersManager::on_install_sticker_set(int64 set_id, bool is_archived, send_update_installed_sticker_sets(); } -void StickersManager::on_uninstall_sticker_set(int64 set_id) { +void StickersManager::on_uninstall_sticker_set(StickerSetId set_id) { StickerSet *sticker_set = get_sticker_set(set_id); CHECK(sticker_set != nullptr); on_update_sticker_set(sticker_set, false, false, true); @@ -3012,7 +3109,7 @@ void StickersManager::on_update_sticker_sets() { reload_installed_sticker_sets(true, true); } -void StickersManager::view_featured_sticker_sets(const vector &sticker_set_ids) { +void StickersManager::view_featured_sticker_sets(const vector &sticker_set_ids) { for (auto sticker_set_id : sticker_set_ids) { auto set = get_sticker_set(sticker_set_id); if (set != nullptr && !set->is_viewed) { @@ -3038,23 +3135,24 @@ void StickersManager::read_featured_sticker_sets(void *td_void) { auto td = static_cast(td_void); auto &set_ids = td->stickers_manager_->pending_viewed_featured_sticker_set_ids_; - td->create_handler()->send(vector(set_ids.begin(), set_ids.end())); + td->create_handler()->send(vector(set_ids.begin(), set_ids.end())); set_ids.clear(); } -std::pair> StickersManager::get_archived_sticker_sets(bool is_masks, int64 offset_sticker_set_id, - int32 limit, bool force, - Promise &&promise) { +std::pair> StickersManager::get_archived_sticker_sets(bool is_masks, + StickerSetId offset_sticker_set_id, + int32 limit, bool force, + Promise &&promise) { if (limit <= 0) { promise.set_error(Status::Error(3, "Parameter limit must be positive")); return {}; } - vector &sticker_set_ids = archived_sticker_set_ids_[is_masks]; + vector &sticker_set_ids = archived_sticker_set_ids_[is_masks]; int32 total_count = total_archived_sticker_set_count_[is_masks]; if (total_count >= 0) { auto offset_it = sticker_set_ids.begin(); - if (offset_sticker_set_id != 0) { + if (offset_sticker_set_id.is_valid()) { offset_it = std::find(sticker_set_ids.begin(), sticker_set_ids.end(), offset_sticker_set_id); if (offset_it == sticker_set_ids.end()) { offset_it = sticker_set_ids.begin(); @@ -3062,13 +3160,13 @@ std::pair> StickersManager::get_archived_sticker_sets(bool ++offset_it; } } - vector result; + vector result; while (result.size() < static_cast(limit)) { if (offset_it == sticker_set_ids.end()) { break; } auto sticker_set_id = *offset_it++; - if (sticker_set_id == 0) { // end of the list + if (!sticker_set_id.is_valid()) { // end of the list promise.set_value(Unit()); return {total_count, std::move(result)}; } @@ -3085,10 +3183,10 @@ std::pair> StickersManager::get_archived_sticker_sets(bool } void StickersManager::on_get_archived_sticker_sets( - bool is_masks, int64 offset_sticker_set_id, vector> &&sticker_sets, - int32 total_count) { - vector &sticker_set_ids = archived_sticker_set_ids_[is_masks]; - if (!sticker_set_ids.empty() && sticker_set_ids.back() == 0) { + bool is_masks, StickerSetId offset_sticker_set_id, + vector> &&sticker_sets, int32 total_count) { + vector &sticker_set_ids = archived_sticker_set_ids_[is_masks]; + if (!sticker_set_ids.empty() && sticker_set_ids.back() == StickerSetId()) { return; } if (total_count < 0) { @@ -3098,18 +3196,19 @@ void StickersManager::on_get_archived_sticker_sets( // if 0 sticker sets are received, then set offset_sticker_set_id was found and there is no stickers after it // or it wasn't found and there is no archived sets at all bool is_last = - sticker_sets.empty() && - (offset_sticker_set_id == 0 || (!sticker_set_ids.empty() && offset_sticker_set_id == sticker_set_ids.back())); + sticker_sets.empty() && (!offset_sticker_set_id.is_valid() || + (!sticker_set_ids.empty() && offset_sticker_set_id == sticker_set_ids.back())); total_archived_sticker_set_count_[is_masks] = total_count; for (auto &sticker_set_covered : sticker_sets) { - auto sticker_set_id = on_get_sticker_set_covered(std::move(sticker_set_covered), false); - if (sticker_set_id != 0) { + auto sticker_set_id = + on_get_sticker_set_covered(std::move(sticker_set_covered), false, "on_get_archived_sticker_sets"); + if (sticker_set_id.is_valid()) { auto sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); update_sticker_set(sticker_set); - if (std::find(sticker_set_ids.begin(), sticker_set_ids.end(), sticker_set_id) == sticker_set_ids.end()) { + if (!td::contains(sticker_set_ids, sticker_set_id)) { sticker_set_ids.push_back(sticker_set_id); } } @@ -3120,12 +3219,12 @@ void StickersManager::on_get_archived_sticker_sets( << " found"; total_archived_sticker_set_count_[is_masks] = static_cast(sticker_set_ids.size()); } - sticker_set_ids.push_back(0); + sticker_set_ids.push_back(StickerSetId()); } send_update_installed_sticker_sets(); } -vector StickersManager::get_featured_sticker_sets(Promise &&promise) { +vector StickersManager::get_featured_sticker_sets(Promise &&promise) { if (!are_featured_sticker_sets_loaded_) { load_featured_sticker_sets(std::move(promise)); return {}; @@ -3148,12 +3247,15 @@ void StickersManager::on_get_featured_sticker_sets( CHECK(constructor_id == telegram_api::messages_featuredStickers::ID); auto featured_stickers = move_tl_object_as(sticker_sets_ptr); - vector featured_sticker_set_ids; - std::unordered_set unread_sticker_set_ids(featured_stickers->unread_.begin(), - featured_stickers->unread_.end()); + std::unordered_set unread_sticker_set_ids; + for (auto &unread_sticker_set_id : featured_stickers->unread_) { + unread_sticker_set_ids.insert(StickerSetId(unread_sticker_set_id)); + } + + vector featured_sticker_set_ids; for (auto &sticker_set : featured_stickers->sets_) { - int64 set_id = on_get_sticker_set_covered(std::move(sticker_set), true); - if (set_id == 0) { + StickerSetId set_id = on_get_sticker_set_covered(std::move(sticker_set), true, "on_get_featured_sticker_sets"); + if (!set_id.is_valid()) { continue; } @@ -3236,7 +3338,7 @@ void StickersManager::on_load_featured_sticker_sets_from_database(string value) return reload_featured_sticker_sets(true); } - vector sets_to_load; + vector sets_to_load; for (auto sticker_set_id : log_event.sticker_set_ids) { StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); @@ -3255,7 +3357,7 @@ void StickersManager::on_load_featured_sticker_sets_from_database(string value) })); } -void StickersManager::on_load_featured_sticker_sets_finished(vector &&featured_sticker_set_ids) { +void StickersManager::on_load_featured_sticker_sets_finished(vector &&featured_sticker_set_ids) { featured_sticker_set_ids_ = std::move(featured_sticker_set_ids); are_featured_sticker_sets_loaded_ = true; need_update_featured_sticker_sets_ = true; @@ -3267,7 +3369,7 @@ void StickersManager::on_load_featured_sticker_sets_finished(vector &&fea } } -vector StickersManager::get_attached_sticker_sets(FileId file_id, Promise &&promise) { +vector StickersManager::get_attached_sticker_sets(FileId file_id, Promise &&promise) { if (!file_id.is_valid()) { promise.set_error(Status::Error(5, "Wrong file_id specified")); return {}; @@ -3296,12 +3398,12 @@ void StickersManager::send_get_attached_stickers_query(FileId file_id, Promise input_stickered_media; string file_reference; - if (file_view.remote_location().is_photo()) { - auto input_photo = file_view.remote_location().as_input_photo(); + if (file_view.main_remote_location().is_photo()) { + auto input_photo = file_view.main_remote_location().as_input_photo(); file_reference = input_photo->file_reference_.as_slice().str(); input_stickered_media = make_tl_object(std::move(input_photo)); } else { - auto input_document = file_view.remote_location().as_input_document(); + auto input_document = file_view.main_remote_location().as_input_document(); file_reference = input_document->file_reference_.as_slice().str(); input_stickered_media = make_tl_object(std::move(input_document)); } @@ -3312,11 +3414,12 @@ void StickersManager::send_get_attached_stickers_query(FileId file_id, Promise> &&sticker_sets) { - vector &sticker_set_ids = attached_sticker_sets_[file_id]; + vector &sticker_set_ids = attached_sticker_sets_[file_id]; sticker_set_ids.clear(); for (auto &sticker_set_covered : sticker_sets) { - auto sticker_set_id = on_get_sticker_set_covered(std::move(sticker_set_covered), true); - if (sticker_set_id != 0) { + auto sticker_set_id = + on_get_sticker_set_covered(std::move(sticker_set_covered), true, "on_get_attached_sticker_sets"); + if (sticker_set_id.is_valid()) { auto sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); update_sticker_set(sticker_set); @@ -3329,18 +3432,19 @@ void StickersManager::on_get_attached_sticker_sets( // -1 - order can't be applied, because some sticker sets aren't loaded or aren't installed, // 0 - order wasn't changed, 1 - order was partly replaced by the new order, 2 - order was replaced by the new order -int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vector &sticker_set_ids) { +int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vector &sticker_set_ids) { if (!are_installed_sticker_sets_loaded_[is_masks]) { return -1; } - vector ¤t_sticker_set_ids = installed_sticker_set_ids_[is_masks]; + vector ¤t_sticker_set_ids = installed_sticker_set_ids_[is_masks]; if (sticker_set_ids == current_sticker_set_ids) { return 0; } - std::unordered_set valid_set_ids(current_sticker_set_ids.begin(), current_sticker_set_ids.end()); - vector new_sticker_set_ids; + std::unordered_set valid_set_ids(current_sticker_set_ids.begin(), + current_sticker_set_ids.end()); + vector new_sticker_set_ids; for (auto sticker_set_id : sticker_set_ids) { auto it = valid_set_ids.find(sticker_set_id); if (it != valid_set_ids.end()) { @@ -3354,7 +3458,7 @@ int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vec return 0; } if (!valid_set_ids.empty()) { - vector missed_sticker_set_ids; + vector missed_sticker_set_ids; for (auto sticker_set_id : current_sticker_set_ids) { auto it = valid_set_ids.find(sticker_set_id); if (it != valid_set_ids.end()) { @@ -3379,7 +3483,7 @@ int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vec return 2; } -void StickersManager::on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids) { +void StickersManager::on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids) { int result = apply_installed_sticker_sets_order(is_masks, sticker_set_ids); if (result < 0) { return reload_installed_sticker_sets(is_masks, true); @@ -3389,7 +3493,7 @@ void StickersManager::on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids, +void StickersManager::reorder_installed_sticker_sets(bool is_masks, const vector &sticker_set_ids, Promise &&promise) { auto result = apply_installed_sticker_sets_order(is_masks, sticker_set_ids); if (result < 0) { @@ -3429,13 +3533,13 @@ Result> StickersManager::prepare_input_file( return Status::Error(400, "Can't use encrypted file"); } - if (file_view.has_remote_location() && file_view.remote_location().is_web()) { + if (file_view.has_remote_location() && file_view.main_remote_location().is_web()) { return Status::Error(400, "Can't use web file to create a sticker"); } bool is_url = false; bool is_local = false; if (file_view.has_remote_location()) { - CHECK(file_view.remote_location().is_document()); + CHECK(file_view.main_remote_location().is_document()); } else { if (file_view.has_url()) { is_url = true; @@ -3487,7 +3591,7 @@ tl_object_ptr StickersManager::get_input_stic FileId file_id) const { FileView file_view = td_->file_manager_->get_file_view(file_id); CHECK(file_view.has_remote_location()); - auto input_document = file_view.remote_location().as_input_document(); + 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) { @@ -3808,13 +3912,13 @@ void StickersManager::set_sticker_position_in_set(const tl_object_ptrfile_manager_->get_file_view(file_id); - if (!file_view.has_remote_location() || !file_view.remote_location().is_document() || - file_view.remote_location().is_web()) { + if (!file_view.has_remote_location() || !file_view.main_remote_location().is_document() || + file_view.main_remote_location().is_web()) { return promise.set_error(Status::Error(7, "Wrong sticker file specified")); } td_->create_handler(std::move(promise)) - ->send(file_view.remote_location().as_input_document(), position); + ->send(file_view.main_remote_location().as_input_document(), position); } void StickersManager::remove_sticker_from_set(const tl_object_ptr &sticker, @@ -3826,13 +3930,13 @@ void StickersManager::remove_sticker_from_set(const tl_object_ptrfile_manager_->get_file_view(file_id); - if (!file_view.has_remote_location() || !file_view.remote_location().is_document() || - file_view.remote_location().is_web()) { + if (!file_view.has_remote_location() || !file_view.main_remote_location().is_document() || + file_view.main_remote_location().is_web()) { return promise.set_error(Status::Error(7, "Wrong sticker file specified")); } td_->create_handler(std::move(promise)) - ->send(file_view.remote_location().as_input_document()); + ->send(file_view.main_remote_location().as_input_document()); } vector StickersManager::get_attached_sticker_file_ids(const vector &int_file_ids) { @@ -3846,7 +3950,7 @@ vector StickersManager::get_attached_sticker_file_ids(const vectorset_id == 0) { + if (!s->set_id.is_valid()) { // only stickers from sticker sets can be attached to files continue; } @@ -3875,7 +3979,7 @@ vector StickersManager::get_attached_sticker_file_ids(const vector &sticker_set_ids) const { +int32 StickersManager::get_sticker_sets_hash(const vector &sticker_set_ids) const { vector numbers; numbers.reserve(sticker_set_ids.size()); for (auto sticker_set_id : sticker_set_ids) { @@ -3895,7 +3999,7 @@ int32 StickersManager::get_featured_sticker_sets_hash() const { CHECK(sticker_set != nullptr); CHECK(sticker_set->is_inited); - uint64 pack_id = static_cast(sticker_set_id); + uint64 pack_id = static_cast(sticker_set_id.get()); numbers.push_back(static_cast(pack_id >> 32)); numbers.push_back(static_cast(pack_id & 0xFFFFFFFF)); @@ -3906,10 +4010,18 @@ int32 StickersManager::get_featured_sticker_sets_hash() const { return get_vector_hash(numbers); } +vector StickersManager::convert_sticker_set_ids(const vector &sticker_set_ids) { + return transform(sticker_set_ids, [](StickerSetId sticker_set_id) { return sticker_set_id.get(); }); +} + +vector StickersManager::convert_sticker_set_ids(const vector &sticker_set_ids) { + return transform(sticker_set_ids, [](int64 sticker_set_id) { return StickerSetId(sticker_set_id); }); +} + td_api::object_ptr StickersManager::get_update_installed_sticker_sets_object( int is_masks) const { - return td_api::make_object(is_masks != 0, - vector(installed_sticker_set_ids_[is_masks])); + return td_api::make_object( + is_masks != 0, convert_sticker_set_ids(installed_sticker_set_ids_[is_masks])); } void StickersManager::send_update_installed_sticker_sets(bool from_database) { @@ -4136,13 +4248,7 @@ void StickersManager::add_recent_sticker(bool is_attached, const tl_object_ptr &&promise) { - if (add_recent_sticker_impl(is_attached, sticker_id, promise)) { - send_save_recent_sticker_query(is_attached, sticker_id, false, std::move(promise)); - } + add_recent_sticker_impl(is_attached, r_file_id.ok(), true, std::move(promise)); } void StickersManager::send_save_recent_sticker_query(bool is_attached, FileId sticker_id, bool unsave, @@ -4162,25 +4268,25 @@ void StickersManager::send_save_recent_sticker_query(bool is_attached, FileId st void StickersManager::add_recent_sticker_by_id(bool is_attached, FileId sticker_id) { // TODO log event - Promise promise; - add_recent_sticker_impl(is_attached, sticker_id, promise); + add_recent_sticker_impl(is_attached, sticker_id, false, Auto()); } -bool StickersManager::add_recent_sticker_impl(bool is_attached, FileId sticker_id, Promise &promise) { +void StickersManager::add_recent_sticker_impl(bool is_attached, FileId sticker_id, bool add_on_server, + Promise &&promise) { CHECK(!td_->auth_manager_->is_bot()); LOG(INFO) << "Add recent " << (is_attached ? "attached " : "") << "sticker " << sticker_id; if (!are_recent_stickers_loaded_[is_attached]) { - load_recent_stickers(is_attached, PromiseCreator::lambda([is_attached, sticker_id, + load_recent_stickers(is_attached, PromiseCreator::lambda([is_attached, sticker_id, add_on_server, promise = std::move(promise)](Result<> result) mutable { if (result.is_ok()) { - send_closure(G()->stickers_manager(), &StickersManager::add_recent_sticker_inner, - is_attached, sticker_id, std::move(promise)); + send_closure(G()->stickers_manager(), &StickersManager::add_recent_sticker_impl, + is_attached, sticker_id, add_on_server, std::move(promise)); } else { promise.set_error(result.move_as_error()); } })); - return false; + return; } auto is_equal = [sticker_id](FileId file_id) { @@ -4194,32 +4300,26 @@ bool StickersManager::add_recent_sticker_impl(bool is_attached, FileId sticker_i save_recent_stickers_to_database(is_attached); } - promise.set_value(Unit()); - return false; + return promise.set_value(Unit()); } auto sticker = get_sticker(sticker_id); if (sticker == nullptr) { - promise.set_error(Status::Error(7, "Sticker not found")); - return false; + return promise.set_error(Status::Error(7, "Sticker not found")); } - if (sticker->set_id == 0) { - promise.set_error(Status::Error(7, "Stickers without sticker set can't be added to recent")); - return false; + if (!sticker->set_id.is_valid()) { + return promise.set_error(Status::Error(7, "Stickers without sticker set can't be added to recent")); } auto file_view = td_->file_manager_->get_file_view(sticker_id); if (!file_view.has_remote_location()) { - promise.set_error(Status::Error(7, "Can save only sent stickers")); - return false; + return promise.set_error(Status::Error(7, "Can save only sent stickers")); } if (file_view.remote_location().is_web()) { - promise.set_error(Status::Error(7, "Can't save web stickers")); - return false; + return promise.set_error(Status::Error(7, "Can't save web stickers")); } if (!file_view.remote_location().is_document()) { - promise.set_error(Status::Error(7, "Can't save encrypted stickers")); - return false; + return promise.set_error(Status::Error(7, "Can't save encrypted stickers")); } need_update_recent_stickers_[is_attached] = true; @@ -4239,7 +4339,9 @@ bool StickersManager::add_recent_sticker_impl(bool is_attached, FileId sticker_i } send_update_recent_stickers(); - return true; + if (add_on_server) { + send_save_recent_sticker_query(is_attached, sticker_id, false, std::move(promise)); + } } void StickersManager::remove_recent_sticker(bool is_attached, const tl_object_ptr &input_file, @@ -4259,8 +4361,7 @@ void StickersManager::remove_recent_sticker(bool is_attached, const tl_object_pt vector &sticker_ids = recent_sticker_ids_[is_attached]; FileId file_id = r_file_id.ok(); - auto it = std::find(sticker_ids.begin(), sticker_ids.end(), file_id); - if (it == sticker_ids.end()) { + if (!td::remove(sticker_ids, file_id)) { return promise.set_value(Unit()); } @@ -4271,8 +4372,6 @@ void StickersManager::remove_recent_sticker(bool is_attached, const tl_object_pt send_save_recent_sticker_query(is_attached, file_id, true, std::move(promise)); - sticker_ids.erase(it); - need_update_recent_stickers_[is_attached] = true; send_update_recent_stickers(); } @@ -4551,13 +4650,7 @@ void StickersManager::add_favorite_sticker(const tl_object_ptr &&promise) { - if (add_favorite_sticker_impl(sticker_id, promise)) { - send_fave_sticker_query(sticker_id, false, std::move(promise)); - } + add_favorite_sticker_impl(r_file_id.ok(), true, std::move(promise)); } void StickersManager::send_fave_sticker_query(FileId sticker_id, bool unsave, Promise &&promise) { @@ -4576,23 +4669,23 @@ void StickersManager::send_fave_sticker_query(FileId sticker_id, bool unsave, Pr void StickersManager::add_favorite_sticker_by_id(FileId sticker_id) { // TODO log event - Promise promise; - add_favorite_sticker_impl(sticker_id, promise); + add_favorite_sticker_impl(sticker_id, false, Auto()); } -bool StickersManager::add_favorite_sticker_impl(FileId sticker_id, Promise &promise) { +void StickersManager::add_favorite_sticker_impl(FileId sticker_id, bool add_on_server, Promise &&promise) { CHECK(!td_->auth_manager_->is_bot()); if (!are_favorite_stickers_loaded_) { - load_favorite_stickers(PromiseCreator::lambda([sticker_id, promise = std::move(promise)](Result<> result) mutable { - if (result.is_ok()) { - send_closure(G()->stickers_manager(), &StickersManager::add_favorite_sticker_inner, sticker_id, - std::move(promise)); - } else { - promise.set_error(result.move_as_error()); - } - })); - return false; + load_favorite_stickers( + PromiseCreator::lambda([sticker_id, add_on_server, promise = std::move(promise)](Result<> result) mutable { + if (result.is_ok()) { + send_closure(G()->stickers_manager(), &StickersManager::add_favorite_sticker_impl, sticker_id, + add_on_server, std::move(promise)); + } else { + promise.set_error(result.move_as_error()); + } + })); + return; } auto is_equal = [sticker_id](FileId file_id) { @@ -4605,32 +4698,26 @@ bool StickersManager::add_favorite_sticker_impl(FileId sticker_id, Promise save_favorite_stickers_to_database(); } - promise.set_value(Unit()); - return false; + return promise.set_value(Unit()); } auto sticker = get_sticker(sticker_id); if (sticker == nullptr) { - promise.set_error(Status::Error(7, "Sticker not found")); - return false; + return promise.set_error(Status::Error(7, "Sticker not found")); } - if (sticker->set_id == 0) { - promise.set_error(Status::Error(7, "Stickers without sticker set can't be favorite")); - return false; + if (!sticker->set_id.is_valid()) { + return promise.set_error(Status::Error(7, "Stickers without sticker set can't be favorite")); } auto file_view = td_->file_manager_->get_file_view(sticker_id); if (!file_view.has_remote_location()) { - promise.set_error(Status::Error(7, "Can add to favorites only sent stickers")); - return false; + return promise.set_error(Status::Error(7, "Can add to favorites only sent stickers")); } if (file_view.remote_location().is_web()) { - promise.set_error(Status::Error(7, "Can't add to favorites web stickers")); - return false; + return promise.set_error(Status::Error(7, "Can't add to favorites web stickers")); } if (!file_view.remote_location().is_document()) { - promise.set_error(Status::Error(7, "Can't add to favorites encrypted stickers")); - return false; + return promise.set_error(Status::Error(7, "Can't add to favorites encrypted stickers")); } auto it = std::find_if(favorite_sticker_ids_.begin(), favorite_sticker_ids_.end(), is_equal); @@ -4648,7 +4735,9 @@ bool StickersManager::add_favorite_sticker_impl(FileId sticker_id, Promise } send_update_favorite_stickers(); - return true; + if (add_on_server) { + send_fave_sticker_query(sticker_id, false, std::move(promise)); + } } void StickersManager::remove_favorite_sticker(const tl_object_ptr &input_file, @@ -4667,8 +4756,7 @@ void StickersManager::remove_favorite_sticker(const tl_object_ptr StickersManager::get_sticker_emojis(const tl_object_ptrset_id == 0) { + if (!sticker->set_id.is_valid()) { promise.set_value(Unit()); return {}; } @@ -4863,15 +4949,13 @@ void StickersManager::on_get_language_codes(const string &key, Result emojis = search_language_emojis(language_code, text, true); bool is_changed = false; for (auto &emoji : keyword->emoticons_) { - if (std::find(emojis.begin(), emojis.end(), emoji) == emojis.end()) { + if (!td::contains(emojis, emoji)) { emojis.push_back(emoji); is_changed = true; } @@ -5089,9 +5173,7 @@ void StickersManager::on_get_emoji_keywords_difference( vector emojis = search_language_emojis(language_code, text, true); bool is_changed = false; for (auto &emoji : keyword->emoticons_) { - auto it = std::find(emojis.begin(), emojis.end(), emoji); - if (it != emojis.end()) { - emojis.erase(it); + if (td::remove(emojis, emoji)) { is_changed = true; } } @@ -5232,6 +5314,9 @@ string StickersManager::remove_emoji_modifiers(string emoji) { } void StickersManager::after_get_difference() { + if (!td_->auth_manager_->is_bot()) { + return; + } if (td_->is_online()) { get_installed_sticker_sets(false, Auto()); get_installed_sticker_sets(true, Auto()); @@ -5239,6 +5324,7 @@ void StickersManager::after_get_difference() { get_recent_stickers(false, Auto()); get_recent_stickers(true, Auto()); get_favorite_stickers(Auto()); + td_->create_handler()->send(); } } diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index 8f5b90fb..26ae5f39 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,6 +15,7 @@ #include "td/telegram/files/FileSourceId.h" #include "td/telegram/Photo.h" #include "td/telegram/SecretInputMedia.h" +#include "td/telegram/StickerSetId.h" #include "td/utils/buffer.h" #include "td/utils/common.h" @@ -38,18 +39,22 @@ class StickersManager : public Actor { public: static constexpr int64 GREAT_MINDS_SET_ID = 1842540969984001; + static vector convert_sticker_set_ids(const vector &sticker_set_ids); + static vector convert_sticker_set_ids(const vector &sticker_set_ids); + StickersManager(Td *td, ActorShared<> parent); tl_object_ptr get_sticker_object(FileId file_id) const; tl_object_ptr get_stickers_object(const vector &sticker_ids) const; - tl_object_ptr get_sticker_set_object(int64 sticker_set_id) const; + tl_object_ptr get_sticker_set_object(StickerSetId sticker_set_id) const; - tl_object_ptr get_sticker_sets_object(int32 total_count, const vector &sticker_set_ids, + tl_object_ptr get_sticker_sets_object(int32 total_count, + const vector &sticker_set_ids, size_t covers_limit) const; - tl_object_ptr get_input_sticker_set(int64 sticker_set_id) const; + tl_object_ptr get_input_sticker_set(StickerSetId sticker_set_id) const; void create_sticker(FileId file_id, PhotoSize thumbnail, Dimensions dimensions, tl_object_ptr sticker, bool is_animated, @@ -69,68 +74,73 @@ class StickersManager : public Actor { vector search_stickers(string emoji, int32 limit, Promise &&promise); - vector get_installed_sticker_sets(bool is_masks, Promise &&promise); + vector get_installed_sticker_sets(bool is_masks, Promise &&promise); bool has_webp_thumbnail(const tl_object_ptr &sticker); - int64 get_sticker_set_id(const tl_object_ptr &set_ptr); + StickerSetId get_sticker_set_id(const tl_object_ptr &set_ptr); - int64 add_sticker_set(tl_object_ptr &&set_ptr); + StickerSetId add_sticker_set(tl_object_ptr &&set_ptr); - int64 get_sticker_set(int64 set_id, Promise &&promise); + StickerSetId get_sticker_set(StickerSetId set_id, Promise &&promise); - int64 search_sticker_set(const string &short_name_to_search, Promise &&promise); + StickerSetId search_sticker_set(const string &short_name_to_search, Promise &&promise); - std::pair> search_installed_sticker_sets(bool is_masks, const string &query, int32 limit, - Promise &&promise); + std::pair> search_installed_sticker_sets(bool is_masks, const string &query, int32 limit, + Promise &&promise); - vector search_sticker_sets(const string &query, Promise &&promise); + vector search_sticker_sets(const string &query, Promise &&promise); - void change_sticker_set(int64 set_id, bool is_installed, bool is_archived, Promise &&promise); + void change_sticker_set(StickerSetId set_id, bool is_installed, bool is_archived, Promise &&promise); - void view_featured_sticker_sets(const vector &sticker_set_ids); + void view_featured_sticker_sets(const vector &sticker_set_ids); void on_get_installed_sticker_sets(bool is_masks, tl_object_ptr &&stickers_ptr); void on_get_installed_sticker_sets_failed(bool is_masks, Status error); - void on_get_messages_sticker_set(int64 sticker_set_id, tl_object_ptr &&set, - bool is_changed); + StickerSetId on_get_messages_sticker_set(StickerSetId sticker_set_id, + tl_object_ptr &&set, bool is_changed, + const char *source); - int64 on_get_sticker_set(tl_object_ptr &&set, bool is_changed); + StickerSetId on_get_sticker_set(tl_object_ptr &&set, bool is_changed, const char *source); - int64 on_get_sticker_set_covered(tl_object_ptr &&set_ptr, bool is_changed); + StickerSetId on_get_sticker_set_covered(tl_object_ptr &&set_ptr, bool is_changed, + const char *source); - void on_load_sticker_set_fail(int64 sticker_set_id, const Status &error); + void on_get_animated_emoji_sticker_set(StickerSetId sticker_set_id); - void on_install_sticker_set(int64 set_id, bool is_archived, + void on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error); + + void on_install_sticker_set(StickerSetId set_id, bool is_archived, tl_object_ptr &&result); - void on_uninstall_sticker_set(int64 set_id); + void on_uninstall_sticker_set(StickerSetId set_id); void on_update_sticker_sets(); - void on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids); + void on_update_sticker_sets_order(bool is_masks, const vector &sticker_set_ids); - std::pair> get_archived_sticker_sets(bool is_masks, int64 offset_sticker_set_id, int32 limit, - bool force, Promise &&promise); + std::pair> get_archived_sticker_sets(bool is_masks, StickerSetId offset_sticker_set_id, + int32 limit, bool force, Promise &&promise); - void on_get_archived_sticker_sets(bool is_masks, int64 offset_sticker_set_id, + void on_get_archived_sticker_sets(bool is_masks, StickerSetId offset_sticker_set_id, vector> &&sticker_sets, int32 total_count); - vector get_featured_sticker_sets(Promise &&promise); + vector get_featured_sticker_sets(Promise &&promise); void on_get_featured_sticker_sets(tl_object_ptr &&sticker_sets_ptr); void on_get_featured_sticker_sets_failed(Status error); - vector get_attached_sticker_sets(FileId file_id, Promise &&promise); + vector get_attached_sticker_sets(FileId file_id, Promise &&promise); void on_get_attached_sticker_sets(FileId file_id, vector> &&sticker_sets); - void reorder_installed_sticker_sets(bool is_masks, const vector &sticker_set_ids, Promise &&promise); + void reorder_installed_sticker_sets(bool is_masks, const vector &sticker_set_ids, + Promise &&promise); FileId upload_sticker_file(UserId user_id, const tl_object_ptr &sticker, Promise &&promise); @@ -201,7 +211,7 @@ class StickersManager : public Actor { td_api::object_ptr get_emoji_suggestions_url_result(int64 random_id); - void reload_sticker_set(int64 sticker_set_id, int64 access_hash, Promise &&promise); + void reload_sticker_set(StickerSetId sticker_set_id, int64 access_hash, Promise &&promise); void reload_installed_sticker_sets(bool is_masks, bool force); @@ -245,6 +255,12 @@ class StickersManager : public Actor { void get_current_state(vector> &updates) const; + template + void store_sticker_set_id(StickerSetId sticker_set_id, StorerT &storer) const; + + template + void parse_sticker_set_id(StickerSetId &sticker_set_id, ParserT &parser); + private: static constexpr int32 MAX_FEATURED_STICKER_SET_VIEW_DELAY = 5; @@ -257,7 +273,7 @@ class StickersManager : public Actor { class Sticker { public: - int64 set_id = 0; + StickerSetId set_id; string alt; Dimensions dimensions; PhotoSize s_thumbnail; @@ -279,7 +295,7 @@ class StickersManager : public Actor { bool was_loaded = false; bool is_loaded = false; - int64 id = 0; + StickerSetId id; int64 access_hash = 0; string title; string short_name; @@ -331,56 +347,58 @@ class StickersManager : public Actor { static tl_object_ptr get_mask_point_object(int32 point); - tl_object_ptr get_sticker_set_info_object(int64 sticker_set_id, size_t covers_limit) const; + tl_object_ptr get_sticker_set_info_object(StickerSetId sticker_set_id, + size_t covers_limit) const; Sticker *get_sticker(FileId file_id); const Sticker *get_sticker(FileId file_id) const; FileId on_get_sticker(unique_ptr new_sticker, bool replace); - StickerSet *get_sticker_set(int64 sticker_set_id); - const StickerSet *get_sticker_set(int64 sticker_set_id) const; + StickerSet *get_sticker_set(StickerSetId sticker_set_id); + const StickerSet *get_sticker_set(StickerSetId sticker_set_id) const; - StickerSet *add_sticker_set(int64 sticker_set_id, int64 access_hash); + StickerSet *add_sticker_set(StickerSetId sticker_set_id, int64 access_hash); std::pair on_get_sticker_document(tl_object_ptr &&document_ptr); static tl_object_ptr get_input_sticker_set(const StickerSet *set); - int64 on_get_input_sticker_set(FileId sticker_file_id, tl_object_ptr &&set_ptr, - MultiPromiseActor *load_data_multipromise_ptr = nullptr); + StickerSetId on_get_input_sticker_set(FileId sticker_file_id, tl_object_ptr &&set_ptr, + MultiPromiseActor *load_data_multipromise_ptr = nullptr); void on_resolve_sticker_set_short_name(FileId sticker_file_id, const string &short_name); - int apply_installed_sticker_sets_order(bool is_masks, const vector &sticker_set_ids); + int apply_installed_sticker_sets_order(bool is_masks, const vector &sticker_set_ids); void on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived, bool is_changed, bool from_database = false); - static string get_sticker_set_database_key(int64 set_id); + static string get_sticker_set_database_key(StickerSetId set_id); - static string get_full_sticker_set_database_key(int64 set_id); + static string get_full_sticker_set_database_key(StickerSetId set_id); string get_sticker_set_database_value(const StickerSet *s, bool with_stickers); void update_sticker_set(StickerSet *sticker_set); - void load_sticker_sets(vector &&sticker_set_ids, Promise &&promise); + void load_sticker_sets(vector &&sticker_set_ids, Promise &&promise); - void load_sticker_sets_without_stickers(vector &&sticker_set_ids, Promise &&promise); + void load_sticker_sets_without_stickers(vector &&sticker_set_ids, Promise &&promise); - void on_load_sticker_set_from_database(int64 sticker_set_id, bool with_stickers, string value); + void on_load_sticker_set_from_database(StickerSetId sticker_set_id, bool with_stickers, string value); void update_load_requests(StickerSet *sticker_set, bool with_stickers, const Status &status); void update_load_request(uint32 load_request_id, const Status &status); - void do_reload_sticker_set(int64 sticker_set_id, tl_object_ptr &&input_sticker_set, + void do_reload_sticker_set(StickerSetId sticker_set_id, + tl_object_ptr &&input_sticker_set, Promise &&promise) const; static void read_featured_sticker_sets(void *td_void); - int32 get_sticker_sets_hash(const vector &sticker_set_ids) const; + int32 get_sticker_sets_hash(const vector &sticker_set_ids) const; int32 get_featured_sticker_sets_hash() const; @@ -394,12 +412,12 @@ class StickersManager : public Actor { void on_load_installed_sticker_sets_from_database(bool is_masks, string value); - void on_load_installed_sticker_sets_finished(bool is_masks, vector &&installed_sticker_set_ids, + void on_load_installed_sticker_sets_finished(bool is_masks, vector &&installed_sticker_set_ids, bool from_database = false); void on_load_featured_sticker_sets_from_database(string value); - void on_load_featured_sticker_sets_finished(vector &&featured_sticker_set_ids); + void on_load_featured_sticker_sets_finished(vector &&featured_sticker_set_ids); void on_load_recent_stickers_from_database(bool is_attached, string value); @@ -420,15 +438,11 @@ class StickersManager : public Actor { void save_recent_stickers_to_database(bool is_attached); - void add_recent_sticker_inner(bool is_attached, FileId sticker_id, Promise &&promise); - - bool add_recent_sticker_impl(bool is_attached, FileId sticker_id, Promise &promise); + void add_recent_sticker_impl(bool is_attached, FileId sticker_id, bool add_on_server, Promise &&promise); int32 get_favorite_stickers_hash() const; - void add_favorite_sticker_inner(FileId sticker_id, Promise &&promise); - - bool add_favorite_sticker_impl(FileId sticker_id, Promise &promise); + void add_favorite_sticker_impl(FileId sticker_id, bool add_on_server, Promise &&promise); void load_favorite_stickers(Promise &&promise); @@ -448,12 +462,6 @@ class StickersManager : public Actor { template void parse_sticker_set(StickerSet *sticker_set, ParserT &parser); - template - void store_sticker_set_id(int64 sticker_set_id, StorerT &storer) const; - - template - void parse_sticker_set_id(int64 &sticker_set_id, ParserT &parser); - Result> prepare_input_file(const tl_object_ptr &input_file); Result> prepare_input_sticker(td_api::inputSticker *sticker); @@ -476,6 +484,8 @@ class StickersManager : public Actor { bool update_sticker_set_cache(const StickerSet *sticker_set, Promise &promise); + void start_up() override; + void tear_down() override; static void add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail); @@ -520,12 +530,12 @@ class StickersManager : public Actor { Td *td_; ActorShared<> parent_; - std::unordered_map, FileIdHash> stickers_; // file_id -> Sticker - std::unordered_map> sticker_sets_; // id -> StickerSet - std::unordered_map short_name_to_sticker_set_id_; + std::unordered_map, FileIdHash> stickers_; // file_id -> Sticker + std::unordered_map, StickerSetIdHash> sticker_sets_; // id -> StickerSet + std::unordered_map short_name_to_sticker_set_id_; - vector installed_sticker_set_ids_[2]; - vector featured_sticker_set_ids_; + vector installed_sticker_set_ids_[2]; + vector featured_sticker_set_ids_; vector recent_sticker_ids_[2]; vector favorite_sticker_ids_; @@ -559,25 +569,29 @@ class StickersManager : public Actor { vector favorite_sticker_file_ids_; FileSourceId favorite_stickers_file_source_id_; - vector archived_sticker_set_ids_[2]; + vector archived_sticker_set_ids_[2]; int32 total_archived_sticker_set_count_[2] = {-1, -1}; - std::unordered_map, FileIdHash> attached_sticker_sets_; + std::unordered_map, FileIdHash> attached_sticker_sets_; Hints installed_sticker_sets_hints_[2]; // search installed sticker sets by their title and name std::unordered_map> found_stickers_; std::unordered_map>> search_stickers_queries_; - std::unordered_map> found_sticker_sets_; + std::unordered_map> found_sticker_sets_; std::unordered_map>> search_sticker_sets_queries_; - std::unordered_set pending_viewed_featured_sticker_set_ids_; + std::unordered_set pending_viewed_featured_sticker_set_ids_; Timeout pending_featured_sticker_set_views_timeout_; 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_; + struct StickerSetLoadRequest { Promise promise; Status error; diff --git a/td/telegram/StickersManager.hpp b/td/telegram/StickersManager.hpp index ce158c64..2070a9c1 100644 --- a/td/telegram/StickersManager.hpp +++ b/td/telegram/StickersManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -25,7 +25,7 @@ void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT auto it = stickers_.find(file_id); CHECK(it != stickers_.end()); const Sticker *sticker = it->second.get(); - bool has_sticker_set_access_hash = sticker->set_id != 0 && !in_sticker_set; + bool has_sticker_set_access_hash = sticker->set_id.is_valid() && !in_sticker_set; BEGIN_STORE_FLAGS(); STORE_FLAG(sticker->is_mask); STORE_FLAG(has_sticker_set_access_hash); @@ -33,7 +33,7 @@ void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT STORE_FLAG(sticker->is_animated); END_STORE_FLAGS(); if (!in_sticker_set) { - store(sticker->set_id, storer); + store(sticker->set_id.get(), storer); if (has_sticker_set_access_hash) { auto sticker_set = get_sticker_set(sticker->set_id); CHECK(sticker_set != nullptr); @@ -81,14 +81,16 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) { return FileId(); } if (!in_sticker_set) { - parse(sticker->set_id, parser); + int64 set_id; + parse(set_id, parser); + sticker->set_id = StickerSetId(set_id); if (has_sticker_set_access_hash) { int64 sticker_set_access_hash; parse(sticker_set_access_hash, parser); add_sticker_set(sticker->set_id, sticker_set_access_hash); } else { // backward compatibility - sticker->set_id = 0; + sticker->set_id = StickerSetId(); } } parse(sticker->alt, parser); @@ -133,7 +135,7 @@ void StickersManager::store_sticker_set(const StickerSet *sticker_set, bool with STORE_FLAG(sticker_set->is_thumbnail_reloaded); STORE_FLAG(sticker_set->is_animated); END_STORE_FLAGS(); - store(sticker_set->id, storer); + store(sticker_set->id.get(), storer); store(sticker_set->access_hash, storer); if (sticker_set->is_inited) { store(sticker_set->title, storer); @@ -195,9 +197,9 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser int64 access_hash; parse(sticker_set_id, parser); parse(access_hash, parser); - CHECK(sticker_set->id == sticker_set_id); + CHECK(sticker_set->id.get() == sticker_set_id); if (sticker_set->access_hash != access_hash) { - LOG(ERROR) << "Sticker set " << sticker_set_id << " access hash has changed from " << access_hash << " to " + LOG(ERROR) << "Access hash of " << sticker_set_id << " has changed from " << access_hash << " to " << sticker_set->access_hash; } @@ -227,25 +229,25 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser sticker_set->is_masks = is_masks; sticker_set->is_animated = is_animated; - short_name_to_sticker_set_id_.emplace(clean_username(sticker_set->short_name), sticker_set_id); + short_name_to_sticker_set_id_.emplace(clean_username(sticker_set->short_name), StickerSetId(sticker_set_id)); on_update_sticker_set(sticker_set, is_installed, is_archived, false, true); } else { if (sticker_set->title != title) { - LOG(INFO) << "Sticker set " << sticker_set_id << " title has changed"; + LOG(INFO) << "Title of " << sticker_set_id << " has changed"; } if (sticker_set->short_name != short_name) { - LOG(ERROR) << "Sticker set " << sticker_set_id << " short name has changed from \"" << short_name << "\" to \"" + LOG(ERROR) << "Short name of " << sticker_set_id << " has changed from \"" << short_name << "\" to \"" << sticker_set->short_name << "\""; } if (sticker_set->sticker_count != sticker_count || sticker_set->hash != hash) { sticker_set->is_loaded = false; } if (sticker_set->is_animated != is_animated) { - LOG(ERROR) << "Sticker set " << sticker_set_id << " is_animated has changed from \"" << is_animated - << "\" to \"" << sticker_set->is_animated << "\""; + LOG(ERROR) << "Is animated of " << sticker_set_id << " has changed from \"" << is_animated << "\" to \"" + << sticker_set->is_animated << "\""; } if (sticker_set->is_masks != is_masks) { - LOG(ERROR) << "Sticker set " << sticker_set_id << " is_masks has changed from \"" << is_masks << "\" to \"" + LOG(ERROR) << "Is masks of " << sticker_set_id << " has changed from \"" << is_masks << "\" to \"" << sticker_set->is_masks << "\""; } } @@ -270,7 +272,7 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser Sticker *sticker = get_sticker(sticker_id); CHECK(sticker != nullptr); if (sticker->set_id != sticker_set->id) { - LOG_IF(ERROR, sticker->set_id != 0) << "Sticker " << sticker_id << " set_id has changed"; + LOG_IF(ERROR, sticker->set_id.is_valid()) << "Sticker " << sticker_id << " set_id has changed"; sticker->set_id = sticker_set->id; sticker->is_changed = true; } @@ -294,17 +296,19 @@ void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser } template -void StickersManager::store_sticker_set_id(int64 sticker_set_id, StorerT &storer) const { - CHECK(sticker_set_id != 0); +void StickersManager::store_sticker_set_id(StickerSetId sticker_set_id, StorerT &storer) const { + CHECK(sticker_set_id.is_valid()); const StickerSet *sticker_set = get_sticker_set(sticker_set_id); CHECK(sticker_set != nullptr); - store(sticker_set_id, storer); + store(sticker_set_id.get(), storer); store(sticker_set->access_hash, storer); } template -void StickersManager::parse_sticker_set_id(int64 &sticker_set_id, ParserT &parser) { - parse(sticker_set_id, parser); +void StickersManager::parse_sticker_set_id(StickerSetId &sticker_set_id, ParserT &parser) { + int64 set_id; + parse(set_id, parser); + sticker_set_id = StickerSetId(set_id); int64 sticker_set_access_hash; parse(sticker_set_access_hash, parser); add_sticker_set(sticker_set_id, sticker_set_access_hash); diff --git a/td/telegram/StorageManager.cpp b/td/telegram/StorageManager.cpp index f1888d8e..373332cf 100644 --- a/td/telegram/StorageManager.cpp +++ b/td/telegram/StorageManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -170,7 +170,7 @@ int64 StorageManager::get_file_size(CSlice path) { return 0; } - auto size = r_info.ok().size_; + auto size = r_info.ok().real_size_; LOG(DEBUG) << "Add file \"" << path << "\" of size " << size << " to fast storage statistics"; return size; } diff --git a/td/telegram/StorageManager.h b/td/telegram/StorageManager.h index 80564b51..af3c4c56 100644 --- a/td/telegram/StorageManager.h +++ b/td/telegram/StorageManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index da88c3a5..7467a04d 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -23,7 +23,9 @@ #include "td/telegram/ConfigShared.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DeviceTokenManager.h" +#include "td/telegram/DialogAdministrator.h" #include "td/telegram/DialogId.h" +#include "td/telegram/DialogLocation.h" #include "td/telegram/DialogParticipant.h" #include "td/telegram/DocumentsManager.h" #include "td/telegram/FileReferenceManager.h" @@ -32,11 +34,14 @@ #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/files/FileType.h" +#include "td/telegram/FolderId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/Global.h" #include "td/telegram/HashtagHints.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/JsonValue.h" #include "td/telegram/LanguagePackManager.h" +#include "td/telegram/Location.h" #include "td/telegram/Logging.h" #include "td/telegram/MessageEntity.h" #include "td/telegram/MessageId.h" @@ -64,12 +69,14 @@ #include "td/telegram/PhotoSizeSource.h" #include "td/telegram/PollManager.h" #include "td/telegram/PrivacyManager.h" +#include "td/telegram/PublicDialogType.h" #include "td/telegram/RequestActor.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretChatsManager.h" #include "td/telegram/SecureManager.h" #include "td/telegram/SecureValue.h" #include "td/telegram/StateManager.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/StickersManager.h" #include "td/telegram/StorageManager.h" #include "td/telegram/TdDb.h" @@ -233,13 +240,14 @@ class GetRecentMeUrlsQuery : public Td::ResultHandler { case telegram_api::recentMeUrlStickerSet::ID: { auto url = move_tl_object_as(url_ptr); result->url_ = std::move(url->url_); - auto sticker_set_id = td->stickers_manager_->on_get_sticker_set_covered(std::move(url->set_), false); - if (sticker_set_id == 0) { + auto sticker_set_id = + td->stickers_manager_->on_get_sticker_set_covered(std::move(url->set_), false, "recentMeUrlStickerSet"); + if (!sticker_set_id.is_valid()) { LOG(ERROR) << "Receive invalid sticker set"; result = nullptr; break; } - result->type_ = make_tl_object(sticker_set_id); + result->type_ = make_tl_object(sticker_set_id.get()); break; } case telegram_api::recentMeUrlUnknown::ID: @@ -462,32 +470,6 @@ class GetDeepLinkInfoQuery : public Td::ResultHandler { } }; -class GetAppConfigQuery : public Td::ResultHandler { - Promise> promise_; - - public: - explicit GetAppConfigQuery(Promise> &&promise) : promise_(std::move(promise)) { - } - - void send() { - send_query(G()->net_query_creator().create(create_storer(telegram_api::help_getAppConfig()))); - } - - 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(convert_json_value_object(result)); - } - - void on_error(uint64 id, Status status) override { - promise_.set_error(std::move(status)); - } -}; - class SaveAppLogQuery : public Td::ResultHandler { Promise promise_; @@ -501,7 +483,8 @@ class SaveAppLogQuery : public Td::ResultHandler { 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))))); + G()->net_query_creator().create(create_storer(telegram_api::help_saveAppLog(std::move(input_app_events))), + DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off)); } void on_result(uint64 id, BufferSlice packet) override { @@ -898,13 +881,14 @@ class GetChatRequest : public RequestActor<> { }; class GetChatsRequest : public RequestActor<> { + FolderId folder_id_; DialogDate offset_; int32 limit_; vector dialog_ids_; void do_run(Promise &&promise) override { - dialog_ids_ = td->messages_manager_->get_dialogs(offset_, limit_, get_tries() < 2, std::move(promise)); + dialog_ids_ = td->messages_manager_->get_dialogs(folder_id_, offset_, limit_, get_tries() < 2, std::move(promise)); } void do_send_result() override { @@ -912,8 +896,12 @@ class GetChatsRequest : public RequestActor<> { } public: - GetChatsRequest(ActorShared td, uint64 request_id, int64 offset_order, int64 offset_dialog_id, int32 limit) - : RequestActor(std::move(td), request_id), offset_(offset_order, DialogId(offset_dialog_id)), limit_(limit) { + GetChatsRequest(ActorShared td, uint64 request_id, FolderId folder_id, int64 offset_order, int64 offset_dialog_id, + int32 limit) + : RequestActor(std::move(td), request_id) + , folder_id_(folder_id) + , offset_(offset_order, DialogId(offset_dialog_id)) + , limit_(limit) { // 1 for database + 1 for server request + 1 for server request at the end + 1 for return + 1 just in case set_tries(5); } @@ -1020,40 +1008,12 @@ class GetGroupsInCommonRequest : public RequestActor<> { } }; -class CheckChatUsernameRequest : public RequestActor { - DialogId dialog_id_; - string username_; - - CheckDialogUsernameResult result_ = CheckDialogUsernameResult::Ok; - - void do_run(Promise &&promise) override { - if (get_tries() < 2) { - promise.set_value(std::move(result_)); - return; - } - - td->contacts_manager_->check_dialog_username(dialog_id_, username_, std::move(promise)); - } - - void do_set_result(CheckDialogUsernameResult &&result) override { - result_ = std::move(result); - } - - void do_send_result() override { - send_result(ContactsManager::get_check_chat_username_result_object(result_)); - } - - public: - CheckChatUsernameRequest(ActorShared td, uint64 request_id, int64 dialog_id, string username) - : RequestActor(std::move(td), request_id), dialog_id_(dialog_id), username_(std::move(username)) { - } -}; - class GetCreatedPublicChatsRequest : public RequestActor<> { vector dialog_ids_; + PublicDialogType type_; void do_run(Promise &&promise) override { - dialog_ids_ = td->contacts_manager_->get_created_public_dialogs(std::move(promise)); + dialog_ids_ = td->contacts_manager_->get_created_public_dialogs(type_, std::move(promise)); } void do_send_result() override { @@ -1061,7 +1021,40 @@ class GetCreatedPublicChatsRequest : public RequestActor<> { } public: - GetCreatedPublicChatsRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { + GetCreatedPublicChatsRequest(ActorShared td, uint64 request_id, PublicDialogType type) + : RequestActor(std::move(td), request_id), type_(type) { + } +}; + +class GetSuitableDiscussionChatsRequest : public RequestActor<> { + vector dialog_ids_; + + void do_run(Promise &&promise) override { + dialog_ids_ = td->contacts_manager_->get_dialogs_for_discussion(std::move(promise)); + } + + void do_send_result() override { + send_result(MessagesManager::get_chats_object(dialog_ids_)); + } + + public: + GetSuitableDiscussionChatsRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { + } +}; + +class GetInactiveSupergroupChatsRequest : public RequestActor<> { + vector dialog_ids_; + + void do_run(Promise &&promise) override { + dialog_ids_ = td->contacts_manager_->get_inactive_channels(std::move(promise)); + } + + void do_send_result() override { + send_result(MessagesManager::get_chats_object(dialog_ids_)); + } + + public: + GetInactiveSupergroupChatsRequest(ActorShared td, uint64 request_id) : RequestActor(std::move(td), request_id) { } }; @@ -1335,6 +1328,24 @@ 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_; @@ -1531,6 +1542,8 @@ class OfflineSearchMessagesRequest : public RequestActor<> { }; class SearchMessagesRequest : public RequestActor<> { + FolderId folder_id_; + bool ignore_folder_id_; string query_; int32 offset_date_; DialogId offset_dialog_id_; @@ -1541,8 +1554,9 @@ class SearchMessagesRequest : public RequestActor<> { std::pair> messages_; void do_run(Promise &&promise) override { - messages_ = td->messages_manager_->search_messages(query_, offset_date_, offset_dialog_id_, offset_message_id_, - limit_, random_id_, std::move(promise)); + messages_ = + td->messages_manager_->search_messages(folder_id_, ignore_folder_id_, query_, offset_date_, offset_dialog_id_, + offset_message_id_, limit_, random_id_, std::move(promise)); } void do_send_result() override { @@ -1559,9 +1573,11 @@ class SearchMessagesRequest : public RequestActor<> { } public: - SearchMessagesRequest(ActorShared td, uint64 request_id, string query, int32 offset_date, int64 offset_dialog_id, - int64 offset_message_id, int32 limit) + SearchMessagesRequest(ActorShared td, uint64 request_id, FolderId folder_id, bool ignore_folder_id, string query, + int32 offset_date, int64 offset_dialog_id, int64 offset_message_id, int32 limit) : RequestActor(std::move(td), request_id) + , folder_id_(folder_id) + , ignore_folder_id_(ignore_folder_id) , query_(std::move(query)) , offset_date_(offset_date) , offset_dialog_id_(offset_dialog_id) @@ -1686,6 +1702,26 @@ class GetChatMessageCountRequest : public RequestActor<> { } }; +class GetChatScheduledMessagesRequest : public RequestActor<> { + DialogId dialog_id_; + + vector message_ids_; + + void do_run(Promise &&promise) override { + message_ids_ = td->messages_manager_->get_dialog_scheduled_messages(dialog_id_, std::move(promise)); + } + + void do_send_result() override { + send_result(td->messages_manager_->get_messages_object(-1, dialog_id_, message_ids_)); + } + + public: + GetChatScheduledMessagesRequest(ActorShared td, uint64 request_id, int64 dialog_id) + : RequestActor(std::move(td), request_id), dialog_id_(dialog_id) { + set_tries(3); + } +}; + class GetWebPagePreviewRequest : public RequestOnceActor { td_api::object_ptr text_; @@ -1795,7 +1831,7 @@ class CreateNewSecretChatRequest : public RequestActor { secret_chat_id_, 0 /* no access_hash */, user_id_, SecretChatState::Unknown, true /* it is outbound chat */, -1 /* unknown ttl */, 0 /* unknown creation date */, "" /* no key_hash */, 0); DialogId dialog_id(secret_chat_id_); - td->messages_manager_->force_create_dialog(dialog_id, "create new secret chat"); + td->messages_manager_->force_create_dialog(dialog_id, "create new secret chat", true); send_result(td->messages_manager_->get_chat_object(dialog_id)); } @@ -1809,13 +1845,14 @@ class CreateNewSupergroupChatRequest : public RequestActor<> { string title_; bool is_megagroup_; string description_; + DialogLocation location_; int64 random_id_; DialogId dialog_id_; void do_run(Promise &&promise) override { - dialog_id_ = td->messages_manager_->create_new_channel_chat(title_, is_megagroup_, description_, random_id_, - std::move(promise)); + dialog_id_ = td->messages_manager_->create_new_channel_chat(title_, is_megagroup_, description_, location_, + random_id_, std::move(promise)); } void do_send_result() override { @@ -1825,11 +1862,12 @@ class CreateNewSupergroupChatRequest : public RequestActor<> { public: CreateNewSupergroupChatRequest(ActorShared td, uint64 request_id, string title, bool is_megagroup, - string description) + string description, td_api::object_ptr &&location) : RequestActor(std::move(td), request_id) , title_(std::move(title)) , is_megagroup_(is_megagroup) , description_(std::move(description)) + , location_(std::move(location)) , random_id_(0) { } }; @@ -1925,14 +1963,18 @@ class SearchChatMembersRequest : public RequestActor<> { class GetChatAdministratorsRequest : public RequestActor<> { DialogId dialog_id_; - vector user_ids_; + vector administrators_; void do_run(Promise &&promise) override { - user_ids_ = td->messages_manager_->get_dialog_administrators(dialog_id_, get_tries(), std::move(promise)); + administrators_ = td->messages_manager_->get_dialog_administrators(dialog_id_, get_tries(), std::move(promise)); } void do_send_result() override { - send_result(td->contacts_manager_->get_users_object(-1, user_ids_)); + auto administrator_objects = transform( + administrators_, [contacts_manager = td->contacts_manager_.get()](const DialogAdministrator &administrator) { + return administrator.get_chat_administrator_object(contacts_manager); + }); + send_result(td_api::make_object(std::move(administrator_objects))); } public: @@ -2302,25 +2344,6 @@ class GetScopeNotificationSettingsRequest : public RequestActor<> { } }; -class GetChatReportSpamStateRequest : public RequestActor<> { - DialogId dialog_id_; - - bool can_report_spam_ = false; - - void do_run(Promise &&promise) override { - can_report_spam_ = td->messages_manager_->get_dialog_report_spam_state(dialog_id_, std::move(promise)); - } - - void do_send_result() override { - send_result(make_tl_object(can_report_spam_)); - } - - public: - GetChatReportSpamStateRequest(ActorShared td, uint64 request_id, int64 dialog_id) - : RequestActor(std::move(td), request_id), dialog_id_(dialog_id) { - } -}; - class GetStickersRequest : public RequestActor<> { string emoji_; int32 limit_; @@ -2365,7 +2388,7 @@ class SearchStickersRequest : public RequestActor<> { class GetInstalledStickerSetsRequest : public RequestActor<> { bool is_masks_; - vector sticker_set_ids_; + vector sticker_set_ids_; void do_run(Promise &&promise) override { sticker_set_ids_ = td->stickers_manager_->get_installed_sticker_sets(is_masks_, std::move(promise)); @@ -2383,11 +2406,11 @@ class GetInstalledStickerSetsRequest : public RequestActor<> { class GetArchivedStickerSetsRequest : public RequestActor<> { bool is_masks_; - int64 offset_sticker_set_id_; + StickerSetId offset_sticker_set_id_; int32 limit_; int32 total_count_; - vector sticker_set_ids_; + vector sticker_set_ids_; void do_run(Promise &&promise) override { std::tie(total_count_, sticker_set_ids_) = td->stickers_manager_->get_archived_sticker_sets( @@ -2409,7 +2432,7 @@ class GetArchivedStickerSetsRequest : public RequestActor<> { }; class GetTrendingStickerSetsRequest : public RequestActor<> { - vector sticker_set_ids_; + vector sticker_set_ids_; void do_run(Promise &&promise) override { sticker_set_ids_ = td->stickers_manager_->get_featured_sticker_sets(std::move(promise)); @@ -2427,7 +2450,7 @@ class GetTrendingStickerSetsRequest : public RequestActor<> { class GetAttachedStickerSetsRequest : public RequestActor<> { FileId file_id_; - vector sticker_set_ids_; + vector sticker_set_ids_; void do_run(Promise &&promise) override { sticker_set_ids_ = td->stickers_manager_->get_attached_sticker_sets(file_id_, std::move(promise)); @@ -2444,9 +2467,9 @@ class GetAttachedStickerSetsRequest : public RequestActor<> { }; class GetStickerSetRequest : public RequestActor<> { - int64 set_id_; + StickerSetId set_id_; - int64 sticker_set_id_; + StickerSetId sticker_set_id_; void do_run(Promise &&promise) override { sticker_set_id_ = td->stickers_manager_->get_sticker_set(set_id_, std::move(promise)); @@ -2466,7 +2489,7 @@ class GetStickerSetRequest : public RequestActor<> { class SearchStickerSetRequest : public RequestActor<> { string name_; - int64 sticker_set_id_; + StickerSetId sticker_set_id_; void do_run(Promise &&promise) override { sticker_set_id_ = td->stickers_manager_->search_sticker_set(name_, std::move(promise)); @@ -2488,7 +2511,7 @@ class SearchInstalledStickerSetsRequest : public RequestActor<> { string query_; int32 limit_; - std::pair> sticker_set_ids_; + std::pair> sticker_set_ids_; void do_run(Promise &&promise) override { sticker_set_ids_ = @@ -2508,7 +2531,7 @@ class SearchInstalledStickerSetsRequest : public RequestActor<> { class SearchStickerSetsRequest : public RequestActor<> { string query_; - vector sticker_set_ids_; + vector sticker_set_ids_; void do_run(Promise &&promise) override { sticker_set_ids_ = td->stickers_manager_->search_sticker_sets(query_, std::move(promise)); @@ -2525,7 +2548,7 @@ class SearchStickerSetsRequest : public RequestActor<> { }; class ChangeStickerSetRequest : public RequestOnceActor { - int64 set_id_; + StickerSetId set_id_; bool is_installed_; bool is_archived_; @@ -2578,7 +2601,7 @@ class CreateNewStickerSetRequest : public RequestOnceActor { void do_send_result() override { auto set_id = td->stickers_manager_->search_sticker_set(name_, Auto()); - if (set_id == 0) { + if (!set_id.is_valid()) { return send_error(Status::Error(500, "Created sticker set not found")); } send_result(td->stickers_manager_->get_sticker_set_object(set_id)); @@ -2607,7 +2630,7 @@ class AddStickerToSetRequest : public RequestOnceActor { void do_send_result() override { auto set_id = td->stickers_manager_->search_sticker_set(name_, Auto()); - if (set_id == 0) { + 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)); @@ -3315,7 +3338,7 @@ void Td::on_alarm_timeout(int64 alarm_id) { } void Td::on_online_updated(bool force, bool send_update) { - if (close_flag_ >= 2 || auth_manager_->is_bot() || !auth_manager_->is_authorized()) { + if (close_flag_ >= 2 || !auth_manager_->is_authorized() || auth_manager_->is_bot()) { return; } if (force || is_online_) { @@ -3360,7 +3383,7 @@ void Td::on_get_terms_of_service(Result> result pending_terms_of_service_ = std::move(result.ok().second); auto update = get_update_terms_of_service_object(); if (update == nullptr) { - expires_in = min(max(result.ok().first, G()->unix_time() + 60) - G()->unix_time(), 86400); + expires_in = min(max(result.ok().first, G()->unix_time() + 3600) - G()->unix_time(), 86400); } else { send_update(std::move(update)); } @@ -3401,6 +3424,7 @@ bool Td::is_authentication_request(int32 id) { case td_api::resendAuthenticationCode::ID: case td_api::checkAuthenticationCode::ID: case td_api::registerUser::ID: + case td_api::requestQrCodeAuthentication::ID: case td_api::checkAuthenticationPassword::ID: case td_api::requestAuthenticationPasswordRecovery::ID: case td_api::recoverAuthenticationPassword::ID: @@ -3473,6 +3497,7 @@ bool Td::is_preauthentication_request(int32 id) { case td_api::setCustomLanguagePackString::ID: case td_api::deleteLanguagePack::ID: case td_api::processPushNotification::ID: + case td_api::sendTonLiteServerRequest::ID: case td_api::getOption::ID: case td_api::setOption::ID: case td_api::getStorageStatistics::ID: @@ -3765,6 +3790,8 @@ bool Td::is_internal_config_option(Slice name) { return name == "dc_txt_domain_name"; case 'e': return name == "edit_time_limit"; + case 'i': + return name == "ignored_restriction_reasons"; case 'l': return name == "language_pack_version"; case 'm': @@ -3847,6 +3874,8 @@ void Td::on_config_option_updated(const string &name) { return send_closure(notification_manager_actor_, &NotificationManager::on_notification_cloud_delay_changed); } else if (name == "notification_default_delay_ms") { return send_closure(notification_manager_actor_, &NotificationManager::on_notification_default_delay_changed); + } else if (name == "ignored_restriction_reasons") { + return send_closure(contacts_manager_actor_, &ContactsManager::on_ignored_restriction_reasons_changed); } else if (is_internal_config_option(name)) { return; } @@ -4907,6 +4936,11 @@ void Td::on_request(uint64 id, td_api::registerUser &request) { std::move(request.last_name_)); } +void Td::on_request(uint64 id, td_api::requestQrCodeAuthentication &request) { + send_closure(auth_manager_actor_, &AuthManager::request_qr_code_authentication, id, + std::move(request.other_user_ids_)); +} + void Td::on_request(uint64 id, td_api::checkAuthenticationPassword &request) { CLEAN_INPUT_STRING(request.password_); send_closure(auth_manager_actor_, &AuthManager::check_password, id, std::move(request.password_)); @@ -4943,13 +4977,21 @@ void Td::on_request(uint64 id, td_api::checkAuthenticationBotToken &request) { send_closure(auth_manager_actor_, &AuthManager::check_bot_token, id, std::move(request.token_)); } +void Td::on_request(uint64 id, td_api::confirmQrCodeAuthentication &request) { + CLEAN_INPUT_STRING(request.link_); + CREATE_REQUEST_PROMISE(); + contacts_manager_->confirm_qr_code_authentication(std::move(request.link_), std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getCurrentState &request) { vector> updates; - updates.push_back(td_api::make_object( - "version", td_api::make_object(TDLIB_VERSION))); updates.push_back( td_api::make_object("online", make_tl_object(is_online_))); + updates.push_back(td_api::make_object( + "unix_time", make_tl_object(G()->unix_time()))); + updates.push_back(td_api::make_object( + "version", td_api::make_object(TDLIB_VERSION))); for (auto &option : G()->shared_config().get_options()) { if (!is_internal_config_option(option.first)) { updates.push_back(td_api::make_object( @@ -5442,7 +5484,8 @@ void Td::on_request(uint64 id, td_api::getTopChats &request) { } }); send_closure(top_dialog_manager_, &TopDialogManager::get_top_dialogs, - top_dialog_category_from_td_api(*request.category_), request.limit_, std::move(query_promise)); + top_dialog_category_from_td_api(*request.category_), narrow_cast(request.limit_), + std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::removeTopChat &request) { @@ -5451,15 +5494,20 @@ void Td::on_request(uint64 id, const td_api::removeTopChat &request) { return send_error_raw(id, 400, "Top chat category should not be empty"); } + DialogId dialog_id(request.chat_id_); + if (!dialog_id.is_valid()) { + return send_error_raw(id, 400, "Invalid chat identifier"); + } send_closure(top_dialog_manager_, &TopDialogManager::remove_dialog, - top_dialog_category_from_td_api(*request.category_), DialogId(request.chat_id_), - messages_manager_->get_input_peer(DialogId(request.chat_id_), AccessRights::Read)); - send_closure(actor_id(this), &Td::send_result, id, make_tl_object()); + top_dialog_category_from_td_api(*request.category_), dialog_id, + messages_manager_->get_input_peer(dialog_id, AccessRights::Read)); + send_closure(actor_id(this), &Td::send_result, id, td_api::make_object()); } void Td::on_request(uint64 id, const td_api::getChats &request) { CHECK_IS_USER(); - CREATE_REQUEST(GetChatsRequest, request.offset_order_, request.offset_chat_id_, request.limit_); + CREATE_REQUEST(GetChatsRequest, FolderId(request.chat_list_), request.offset_order_, request.offset_chat_id_, + request.limit_); } void Td::on_request(uint64 id, td_api::searchPublicChat &request) { @@ -5485,6 +5533,12 @@ void Td::on_request(uint64 id, td_api::searchChatsOnServer &request) { CREATE_REQUEST(SearchChatsOnServerRequest, request.query_, request.limit_); } +void Td::on_request(uint64 id, const td_api::searchChatsNearby &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + contacts_manager_->search_dialogs_nearby(Location(request.location_), std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getGroupsInCommon &request) { CHECK_IS_USER(); CREATE_REQUEST(GetGroupsInCommonRequest, request.user_id_, request.offset_chat_id_, request.limit_); @@ -5493,12 +5547,37 @@ void Td::on_request(uint64 id, const td_api::getGroupsInCommon &request) { void Td::on_request(uint64 id, td_api::checkChatUsername &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.username_); - CREATE_REQUEST(CheckChatUsernameRequest, request.chat_id_, std::move(request.username_)); + 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(ContactsManager::get_check_chat_username_result_object(result.ok())); + } + }); + contacts_manager_->check_dialog_username(DialogId(request.chat_id_), request.username_, std::move(query_promise)); } void Td::on_request(uint64 id, const td_api::getCreatedPublicChats &request) { CHECK_IS_USER(); - CREATE_NO_ARGS_REQUEST(GetCreatedPublicChatsRequest); + CREATE_REQUEST(GetCreatedPublicChatsRequest, get_public_dialog_type(request.type_)); +} + +void Td::on_request(uint64 id, const td_api::checkCreatedPublicChatsLimit &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->check_created_public_dialogs_limit(get_public_dialog_type(request.type_), std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::getSuitableDiscussionChats &request) { + CHECK_IS_USER(); + CREATE_NO_ARGS_REQUEST(GetSuitableDiscussionChatsRequest); +} + +void Td::on_request(uint64 id, const td_api::getInactiveSupergroupChats &request) { + CHECK_IS_USER(); + CREATE_NO_ARGS_REQUEST(GetInactiveSupergroupChatsRequest); } void Td::on_request(uint64 id, const td_api::addRecentlyFoundChat &request) { @@ -5570,8 +5649,9 @@ void Td::on_request(uint64 id, td_api::searchSecretMessages &request) { void Td::on_request(uint64 id, td_api::searchMessages &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.query_); - CREATE_REQUEST(SearchMessagesRequest, std::move(request.query_), request.offset_date_, request.offset_chat_id_, - request.offset_message_id_, request.limit_); + CREATE_REQUEST(SearchMessagesRequest, FolderId(request.chat_list_), request.chat_list_ == nullptr, + std::move(request.query_), request.offset_date_, request.offset_chat_id_, request.offset_message_id_, + request.limit_); } void Td::on_request(uint64 id, td_api::searchCallMessages &request) { @@ -5598,6 +5678,11 @@ void Td::on_request(uint64 id, td_api::getChatMessageCount &request) { CREATE_REQUEST(GetChatMessageCountRequest, request.chat_id_, std::move(request.filter_), request.return_local_); } +void Td::on_request(uint64 id, const td_api::getChatScheduledMessages &request) { + CHECK_IS_USER(); + CREATE_REQUEST(GetChatScheduledMessagesRequest, request.chat_id_); +} + void Td::on_request(uint64 id, const td_api::removeNotification &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); @@ -5635,23 +5720,23 @@ void Td::on_request(uint64 id, const td_api::readAllChatMentions &request) { void Td::on_request(uint64 id, td_api::sendMessage &request) { DialogId dialog_id(request.chat_id_); - auto r_new_message_id = messages_manager_->send_message( - dialog_id, MessageId(request.reply_to_message_id_), request.disable_notification_, request.from_background_, - std::move(request.reply_markup_), std::move(request.input_message_content_)); + auto r_new_message_id = + messages_manager_->send_message(dialog_id, MessageId(request.reply_to_message_id_), std::move(request.options_), + std::move(request.reply_markup_), std::move(request.input_message_content_)); if (r_new_message_id.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); } - CHECK(r_new_message_id.ok().is_valid()); + CHECK(r_new_message_id.ok().is_valid() || r_new_message_id.ok().is_valid_scheduled()); send_closure(actor_id(this), &Td::send_result, id, messages_manager_->get_message_object({dialog_id, r_new_message_id.ok()})); } void Td::on_request(uint64 id, td_api::sendMessageAlbum &request) { DialogId dialog_id(request.chat_id_); - auto r_message_ids = messages_manager_->send_message_group(dialog_id, MessageId(request.reply_to_message_id_), - request.disable_notification_, request.from_background_, - std::move(request.input_message_contents_)); + auto r_message_ids = + messages_manager_->send_message_group(dialog_id, MessageId(request.reply_to_message_id_), + std::move(request.options_), std::move(request.input_message_contents_)); if (r_message_ids.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_message_ids.move_as_error()); } @@ -5671,7 +5756,7 @@ void Td::on_request(uint64 id, td_api::sendBotStartMessage &request) { return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); } - CHECK(r_new_message_id.ok().is_valid()); + CHECK(r_new_message_id.ok().is_valid() || r_new_message_id.ok().is_valid_scheduled()); send_closure(actor_id(this), &Td::send_result, id, messages_manager_->get_message_object({dialog_id, r_new_message_id.ok()})); } @@ -5682,13 +5767,13 @@ void Td::on_request(uint64 id, td_api::sendInlineQueryResultMessage &request) { DialogId dialog_id(request.chat_id_); auto r_new_message_id = messages_manager_->send_inline_query_result_message( - dialog_id, MessageId(request.reply_to_message_id_), request.disable_notification_, request.from_background_, - request.query_id_, request.result_id_, request.hide_via_bot_); + dialog_id, MessageId(request.reply_to_message_id_), std::move(request.options_), request.query_id_, + request.result_id_, request.hide_via_bot_); if (r_new_message_id.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_new_message_id.move_as_error()); } - CHECK(r_new_message_id.ok().is_valid()); + CHECK(r_new_message_id.ok().is_valid() || r_new_message_id.ok().is_valid_scheduled()); send_closure(actor_id(this), &Td::send_result, id, messages_manager_->get_message_object({dialog_id, r_new_message_id.ok()})); } @@ -5789,6 +5874,12 @@ void Td::on_request(uint64 id, td_api::editInlineMessageReplyMarkup &request) { std::move(request.reply_markup_), std::move(promise)); } +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_)); +} + void Td::on_request(uint64 id, td_api::setGameScore &request) { CHECK_IS_BOT(); CREATE_REQUEST(SetGameScoreRequest, request.chat_id_, request.message_id_, request.edit_message_, request.user_id_, @@ -5831,12 +5922,11 @@ void Td::on_request(uint64 id, td_api::sendChatScreenshotTakenNotification &requ answer_ok_query(id, messages_manager_->send_screenshot_taken_notification_message(DialogId(request.chat_id_))); } -void Td::on_request(uint64 id, const td_api::forwardMessages &request) { +void Td::on_request(uint64 id, td_api::forwardMessages &request) { DialogId dialog_id(request.chat_id_); auto r_message_ids = messages_manager_->forward_messages( dialog_id, DialogId(request.from_chat_id_), MessagesManager::get_message_ids(request.message_ids_), - request.disable_notification_, request.from_background_, false, request.as_album_, request.send_copy_, - request.remove_caption_); + std::move(request.options_), false, request.as_album_, request.send_copy_, request.remove_caption_); if (r_message_ids.is_error()) { return send_closure(actor_id(this), &Td::send_error, id, r_message_ids.move_as_error()); } @@ -5895,7 +5985,7 @@ void Td::on_request(uint64 id, td_api::createNewSupergroupChat &request) { CLEAN_INPUT_STRING(request.title_); CLEAN_INPUT_STRING(request.description_); CREATE_REQUEST(CreateNewSupergroupChatRequest, std::move(request.title_), !request.is_channel_, - std::move(request.description_)); + std::move(request.description_), std::move(request.location_)); } void Td::on_request(uint64 id, td_api::createNewSecretChat &request) { CREATE_REQUEST(CreateNewSecretChatRequest, request.user_id_); @@ -5968,6 +6058,12 @@ void Td::on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupCh CREATE_REQUEST(UpgradeGroupChatToSupergroupChatRequest, request.chat_id_); } +void Td::on_request(uint64 id, const td_api::setChatChatList &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + messages_manager_->set_dialog_folder_id(DialogId(request.chat_id_), FolderId(request.chat_list_), std::move(promise)); +} + void Td::on_request(uint64 id, td_api::setChatTitle &request) { CLEAN_INPUT_STRING(request.title_); CREATE_OK_REQUEST_PROMISE(); @@ -6010,6 +6106,7 @@ void Td::on_request(uint64 id, const td_api::toggleChatDefaultDisableNotificatio void Td::on_request(uint64 id, const td_api::setPinnedChats &request) { CHECK_IS_USER(); answer_ok_query(id, messages_manager_->set_pinned_dialogs( + FolderId(request.chat_list_), transform(request.chat_ids_, [](int64 chat_id) { return DialogId(chat_id); }))); } @@ -6024,6 +6121,27 @@ void Td::on_request(uint64 id, td_api::setChatDescription &request) { messages_manager_->set_dialog_description(DialogId(request.chat_id_), request.description_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::setChatDiscussionGroup &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->set_channel_discussion_group(DialogId(request.chat_id_), DialogId(request.discussion_chat_id_), + std::move(promise)); +} + +void Td::on_request(uint64 id, td_api::setChatLocation &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->set_channel_location(DialogId(request.chat_id_), DialogLocation(std::move(request.location_)), + std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::setChatSlowModeDelay &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->set_channel_slow_mode_delay(DialogId(request.chat_id_), request.slow_mode_delay_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::pinChatMessage &request) { CREATE_OK_REQUEST_PROMISE(); messages_manager_->pin_dialog_message(DialogId(request.chat_id_), MessageId(request.message_id_), @@ -6072,6 +6190,28 @@ void Td::on_request(uint64 id, td_api::setChatMemberStatus &request) { request.status_, std::move(promise)); } +void Td::on_request(uint64 id, const td_api::canTransferOwnership &request) { + CHECK_IS_USER(); + 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(ContactsManager::get_can_transfer_ownership_result_object(result.ok())); + } + }); + contacts_manager_->can_transfer_ownership(std::move(query_promise)); +} + +void Td::on_request(uint64 id, td_api::transferChatOwnership &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + CLEAN_INPUT_STRING(request.password_); + contacts_manager_->transfer_dialog_ownership(DialogId(request.chat_id_), UserId(request.user_id_), request.password_, + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getChatMember &request) { CREATE_REQUEST(GetChatMemberRequest, request.chat_id_, request.user_id_); } @@ -6270,12 +6410,12 @@ void Td::on_request(uint64 id, const td_api::deleteFile &request) { void Td::on_request(uint64 id, const td_api::blockUser &request) { CHECK_IS_USER(); - answer_ok_query(id, contacts_manager_->block_user(UserId(request.user_id_))); + answer_ok_query(id, contacts_manager_->set_user_is_blocked(UserId(request.user_id_), true)); } void Td::on_request(uint64 id, const td_api::unblockUser &request) { CHECK_IS_USER(); - answer_ok_query(id, contacts_manager_->unblock_user(UserId(request.user_id_))); + answer_ok_query(id, contacts_manager_->set_user_is_blocked(UserId(request.user_id_), false)); } void Td::on_request(uint64 id, const td_api::getBlockedUsers &request) { @@ -6283,6 +6423,18 @@ void Td::on_request(uint64 id, const td_api::getBlockedUsers &request) { CREATE_REQUEST(GetBlockedUsersRequest, request.offset_, request.limit_); } +void Td::on_request(uint64 id, td_api::addContact &request) { + CHECK_IS_USER(); + if (request.contact_ == nullptr) { + return send_error_raw(id, 5, "Contact must not be empty"); + } + CLEAN_INPUT_STRING(request.contact_->phone_number_); + CLEAN_INPUT_STRING(request.contact_->first_name_); + CLEAN_INPUT_STRING(request.contact_->last_name_); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->add_contact(std::move(request.contact_), request.share_phone_number_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::importContacts &request) { CHECK_IS_USER(); for (auto &contact : request.contacts_) { @@ -6336,6 +6488,12 @@ void Td::on_request(uint64 id, const td_api::clearImportedContacts &request) { contacts_manager_->clear_imported_contacts(std::move(promise)); } +void Td::on_request(uint64 id, const td_api::sharePhoneNumber &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + contacts_manager_->share_phone_number(UserId(request.user_id_), std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getRecentInlineBots &request) { CHECK_IS_USER(); CREATE_NO_ARGS_REQUEST(GetRecentInlineBotsRequest); @@ -6388,7 +6546,7 @@ void Td::on_request(uint64 id, td_api::setSupergroupUsername &request) { void Td::on_request(uint64 id, const td_api::setSupergroupStickerSet &request) { CREATE_OK_REQUEST_PROMISE(); - contacts_manager_->set_channel_sticker_set(ChannelId(request.supergroup_id_), request.sticker_set_id_, + contacts_manager_->set_channel_sticker_set(ChannelId(request.supergroup_id_), StickerSetId(request.sticker_set_id_), std::move(promise)); } @@ -6488,14 +6646,15 @@ void Td::on_request(uint64 id, const td_api::changeStickerSet &request) { void Td::on_request(uint64 id, const td_api::viewTrendingStickerSets &request) { CHECK_IS_USER(); - stickers_manager_->view_featured_sticker_sets(request.sticker_set_ids_); + stickers_manager_->view_featured_sticker_sets(StickersManager::convert_sticker_set_ids(request.sticker_set_ids_)); send_closure(actor_id(this), &Td::send_result, id, make_tl_object()); } void Td::on_request(uint64 id, td_api::reorderInstalledStickerSets &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - stickers_manager_->reorder_installed_sticker_sets(request.is_masks_, request.sticker_set_ids_, std::move(promise)); + stickers_manager_->reorder_installed_sticker_sets( + request.is_masks_, StickersManager::convert_sticker_set_ids(request.sticker_set_ids_), std::move(promise)); } void Td::on_request(uint64 id, td_api::uploadStickerFile &request) { @@ -6615,16 +6774,10 @@ void Td::on_request(uint64 id, const td_api::getScopeNotificationSettings &reque CREATE_REQUEST(GetScopeNotificationSettingsRequest, get_notification_settings_scope(request.scope_)); } -void Td::on_request(uint64 id, const td_api::getChatReportSpamState &request) { - CHECK_IS_USER(); - CREATE_REQUEST(GetChatReportSpamStateRequest, request.chat_id_); -} - -void Td::on_request(uint64 id, const td_api::changeChatReportSpamState &request) { +void Td::on_request(uint64 id, const td_api::removeChatActionBar &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); - messages_manager_->change_dialog_report_spam_state(DialogId(request.chat_id_), request.is_spam_chat_, - std::move(promise)); + messages_manager_->remove_dialog_action_bar(DialogId(request.chat_id_), std::move(promise)); } void Td::on_request(uint64 id, td_api::reportChat &request) { @@ -6756,6 +6909,17 @@ void Td::on_request(uint64 id, td_api::getOption &request) { bool is_bot = auth_manager_ != nullptr && auth_manager_->is_authorized() && auth_manager_->is_bot(); switch (request.name_[0]) { // all these options should be added to getCurrentState + case 'c': + if (!is_bot && request.name_ == "can_ignore_sensitive_content_restrictions") { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result &&result) { + // the option is already updated on success, ignore errors + send_closure(actor_id, &Td::send_result, id, + G()->shared_config().get_option_value("can_ignore_sensitive_content_restrictions")); + }); + send_closure_later(config_manager_, &ConfigManager::get_content_settings, std::move(promise)); + return; + } + break; case 'd': if (!is_bot && request.name_ == "disable_contact_registered_notifications") { auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result &&result) { @@ -6763,8 +6927,19 @@ void Td::on_request(uint64 id, td_api::getOption &request) { send_closure(actor_id, &Td::send_result, id, G()->shared_config().get_option_value("disable_contact_registered_notifications")); }); - send_closure(notification_manager_actor_, &NotificationManager::get_disable_contact_registered_notifications, - std::move(promise)); + send_closure_later(notification_manager_actor_, + &NotificationManager::get_disable_contact_registered_notifications, std::move(promise)); + return; + } + break; + case 'i': + if (!is_bot && request.name_ == "ignore_sensitive_content_restrictions") { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result &&result) { + // the option is already updated on success, ignore errors + send_closure(actor_id, &Td::send_result, id, + G()->shared_config().get_option_value("ignore_sensitive_content_restrictions")); + }); + send_closure_later(config_manager_, &ConfigManager::get_content_settings, std::move(promise)); return; } break; @@ -6773,6 +6948,11 @@ void Td::on_request(uint64 id, td_api::getOption &request) { option_value = make_tl_object(is_online_); } break; + case 'u': + if (request.name_ == "unix_time") { + option_value = make_tl_object(G()->unix_time()); + } + break; case 'v': if (request.name_ == "version") { option_value = make_tl_object(TDLIB_VERSION); @@ -6891,10 +7071,13 @@ void Td::on_request(uint64 id, td_api::setOption &request) { } break; case 'i': + if (set_boolean_option("ignore_background_updates")) { + return; + } if (set_boolean_option("ignore_inline_thumbnails")) { return; } - if (set_boolean_option("ignore_background_updates")) { + if (set_boolean_option("ignore_platform_restrictions")) { return; } if (set_boolean_option("is_emulator")) { @@ -6905,6 +7088,25 @@ void Td::on_request(uint64 id, td_api::setOption &request) { if (false && !is_bot && set_boolean_option("include_sponsored_chat_to_unread_count")) { return; } + + if (!is_bot && request.name_ == "ignore_sensitive_content_restrictions") { + if (!G()->shared_config().get_option_boolean("can_ignore_sensitive_content_restrictions")) { + return send_error_raw(id, 3, "Option \"ignore_sensitive_content_restrictions\" can't be changed by the user"); + } + + if (value_constructor_id != td_api::optionValueBoolean::ID && + value_constructor_id != td_api::optionValueEmpty::ID) { + return send_error_raw(id, 3, "Option \"ignore_sensitive_content_restrictions\" must have boolean value"); + } + + auto ignore_sensitive_content_restrictions = + value_constructor_id == td_api::optionValueBoolean::ID && + static_cast(request.value_.get())->value_; + CREATE_OK_REQUEST_PROMISE(); + send_closure_later(config_manager_, &ConfigManager::set_content_settings, ignore_sensitive_content_restrictions, + std::move(promise)); + return; + } break; case 'l': if (!is_bot && set_string_option("language_pack_database_path", [](Slice value) { return true; })) { @@ -7035,6 +7237,20 @@ void Td::on_request(uint64 id, td_api::stopPoll &request) { std::move(request.reply_markup_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getLoginUrlInfo &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_login_url_info(DialogId(request.chat_id_), MessageId(request.message_id_), request.button_id_, + std::move(promise)); +} + +void Td::on_request(uint64 id, const td_api::getLoginUrl &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + messages_manager_->get_login_url(DialogId(request.chat_id_), MessageId(request.message_id_), request.button_id_, + request.allow_write_access_, std::move(promise)); +} + void Td::on_request(uint64 id, td_api::getInlineQueryResults &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.query_); @@ -7432,7 +7648,7 @@ void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) { void Td::on_request(uint64 id, const td_api::getApplicationConfig &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - create_handler(std::move(promise))->send(); + send_closure(G()->config_manager(), &ConfigManager::get_app_config, std::move(promise)); } void Td::on_request(uint64 id, td_api::saveApplicationLogEvent &request) { @@ -7593,8 +7809,16 @@ td_api::object_ptr Td::do_static_request(td_api::parseTextEntiti switch (request.parse_mode_->get_id()) { case td_api::textParseModeHTML::ID: return parse_html(request.text_); - case td_api::textParseModeMarkdown::ID: - return parse_markdown(request.text_); + case td_api::textParseModeMarkdown::ID: { + auto version = static_cast(request.parse_mode_.get())->version_; + if (version == 0 || version == 1) { + return parse_markdown(request.text_); + } + if (version == 2) { + return parse_markdown_v2(request.text_); + } + return Status::Error("Wrong Markdown version specified"); + } default: UNREACHABLE(); return Status::Error(500, "Unknown parse mode"); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 4ac6f050..1bc54434 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -224,7 +224,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.1"; + static constexpr const char *TDLIB_VERSION = "1.5.4"; static constexpr int64 ONLINE_ALARM_ID = 0; static constexpr int64 PING_SERVER_ALARM_ID = -1; static constexpr int32 PING_SERVER_TIMEOUT = 300; @@ -374,6 +374,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::registerUser &request); + void on_request(uint64 id, td_api::requestQrCodeAuthentication &request); + void on_request(uint64 id, td_api::checkAuthenticationPassword &request); void on_request(uint64 id, const td_api::requestAuthenticationPasswordRecovery &request); @@ -388,6 +390,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::checkAuthenticationBotToken &request); + void on_request(uint64 id, td_api::confirmQrCodeAuthentication &request); + void on_request(uint64 id, const td_api::getCurrentState &request); void on_request(uint64 id, td_api::getPasswordState &request); @@ -514,6 +518,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::searchChatsOnServer &request); + void on_request(uint64 id, const td_api::searchChatsNearby &request); + void on_request(uint64 id, const td_api::addRecentlyFoundChat &request); void on_request(uint64 id, const td_api::removeRecentlyFoundChat &request); @@ -526,6 +532,12 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getCreatedPublicChats &request); + void on_request(uint64 id, const td_api::checkCreatedPublicChatsLimit &request); + + void on_request(uint64 id, const td_api::getSuitableDiscussionChats &request); + + void on_request(uint64 id, const td_api::getInactiveSupergroupChats &request); + void on_request(uint64 id, const td_api::openChat &request); void on_request(uint64 id, const td_api::closeChat &request); @@ -554,6 +566,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatMessageCount &request); + void on_request(uint64 id, const td_api::getChatScheduledMessages &request); + void on_request(uint64 id, const td_api::removeNotification &request); void on_request(uint64 id, const td_api::removeNotificationGroup &request); @@ -596,6 +610,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::editInlineMessageReplyMarkup &request); + void on_request(uint64 id, td_api::editMessageSchedulingState &request); + void on_request(uint64 id, td_api::setGameScore &request); void on_request(uint64 id, td_api::setInlineGameScore &request); @@ -610,7 +626,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::sendChatScreenshotTakenNotification &request); - void on_request(uint64 id, const td_api::forwardMessages &request); + void on_request(uint64 id, td_api::forwardMessages &request); void on_request(uint64 id, const td_api::resendMessages &request); @@ -644,6 +660,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); + void on_request(uint64 id, const td_api::setChatChatList &request); + void on_request(uint64 id, td_api::setChatTitle &request); void on_request(uint64 id, const td_api::setChatPhoto &request); @@ -664,6 +682,12 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setChatDescription &request); + void on_request(uint64 id, const td_api::setChatDiscussionGroup &request); + + void on_request(uint64 id, td_api::setChatLocation &request); + + void on_request(uint64 id, const td_api::setChatSlowModeDelay &request); + void on_request(uint64 id, const td_api::pinChatMessage &request); void on_request(uint64 id, const td_api::unpinChatMessage &request); @@ -678,6 +702,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setChatMemberStatus &request); + void on_request(uint64 id, const td_api::canTransferOwnership &request); + + void on_request(uint64 id, td_api::transferChatOwnership &request); + void on_request(uint64 id, const td_api::getChatMember &request); void on_request(uint64 id, td_api::searchChatMembers &request); @@ -720,6 +748,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getBlockedUsers &request); + void on_request(uint64 id, td_api::addContact &request); + void on_request(uint64 id, td_api::importContacts &request); void on_request(uint64 id, const td_api::getContacts &request); @@ -734,6 +764,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::clearImportedContacts &request); + void on_request(uint64 id, const td_api::sharePhoneNumber &request); + void on_request(uint64 id, const td_api::getRecentInlineBots &request); void on_request(uint64 id, td_api::setName &request); @@ -836,9 +868,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::resetAllNotificationSettings &request); - void on_request(uint64 id, const td_api::getChatReportSpamState &request); - - void on_request(uint64 id, const td_api::changeChatReportSpamState &request); + void on_request(uint64 id, const td_api::removeChatActionBar &request); void on_request(uint64 id, td_api::reportChat &request); @@ -872,6 +902,10 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::stopPoll &request); + void on_request(uint64 id, const td_api::getLoginUrlInfo &request); + + void on_request(uint64 id, const td_api::getLoginUrl &request); + void on_request(uint64 id, td_api::getInlineQueryResults &request); void on_request(uint64 id, td_api::answerInlineQuery &request); diff --git a/td/telegram/TdCallback.h b/td/telegram/TdCallback.h index 216d5332..38b51d34 100644 --- a/td/telegram/TdCallback.h +++ b/td/telegram/TdCallback.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/TdDb.cpp b/td/telegram/TdDb.cpp index ca053402..0fdaa2e1 100644 --- a/td/telegram/TdDb.cpp +++ b/td/telegram/TdDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -107,6 +107,8 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue &binlog_p case LogEvent::HandlerType::GetChannelDifference: case LogEvent::HandlerType::ReadHistoryInSecretChat: case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer: + case LogEvent::HandlerType::SetDialogFolderIdOnServer: + case LogEvent::HandlerType::DeleteScheduledMessagesFromServer: events.to_messages_manager.push_back(event.clone()); break; case LogEvent::HandlerType::AddMessagePushNotification: @@ -341,9 +343,9 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters ¶meters, DbK } if (dialog_db_was_created) { - binlog_pmc.erase("unread_message_count"); - binlog_pmc.erase("unread_dialog_count"); - binlog_pmc.erase("last_server_dialog_date"); + binlog_pmc.erase_by_prefix("last_server_dialog_date"); + binlog_pmc.erase_by_prefix("unread_message_count"); + binlog_pmc.erase_by_prefix("unread_dialog_count"); binlog_pmc.erase("promoted_dialog_id"); binlog_pmc.erase("sponsored_dialog_id"); binlog_pmc.erase_by_prefix("top_dialogs"); diff --git a/td/telegram/TdDb.h b/td/telegram/TdDb.h index 879328ee..cf98a904 100644 --- a/td/telegram/TdDb.h +++ b/td/telegram/TdDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/TdParameters.h b/td/telegram/TdParameters.h index 3673c36d..bf54fa11 100644 --- a/td/telegram/TdParameters.h +++ b/td/telegram/TdParameters.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/TermsOfService.cpp b/td/telegram/TermsOfService.cpp index 687783c7..943c1b24 100644 --- a/td/telegram/TermsOfService.cpp +++ b/td/telegram/TermsOfService.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -26,6 +26,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()))); } diff --git a/td/telegram/TermsOfService.h b/td/telegram/TermsOfService.h index d228339f..34bf4c9d 100644 --- a/td/telegram/TermsOfService.h +++ b/td/telegram/TermsOfService.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/TopDialogManager.cpp b/td/telegram/TopDialogManager.cpp index c4c16266..03ce2130 100644 --- a/td/telegram/TopDialogManager.cpp +++ b/td/telegram/TopDialogManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -50,6 +50,10 @@ static CSlice top_dialog_category_name(TopDialogCategory category) { return CSlice("channel"); case TopDialogCategory::Call: return CSlice("call"); + case TopDialogCategory::ForwardUsers: + return CSlice("forward_users"); + case TopDialogCategory::ForwardChats: + return CSlice("forward_chats"); default: UNREACHABLE(); } @@ -69,6 +73,10 @@ static TopDialogCategory top_dialog_category_from_telegram_api(const telegram_ap return TopDialogCategory::Channel; case telegram_api::topPeerCategoryPhoneCalls::ID: return TopDialogCategory::Call; + case telegram_api::topPeerCategoryForwardUsers::ID: + return TopDialogCategory::ForwardUsers; + case telegram_api::topPeerCategoryForwardChats::ID: + return TopDialogCategory::ForwardChats; default: UNREACHABLE(); } @@ -88,6 +96,10 @@ static tl_object_ptr top_dialog_category_as_teleg return make_tl_object(); case TopDialogCategory::Call: return make_tl_object(); + case TopDialogCategory::ForwardUsers: + return make_tl_object(); + case TopDialogCategory::ForwardChats: + return make_tl_object(); default: UNREACHABLE(); } @@ -174,6 +186,11 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog if (!is_active_ || !is_enabled_) { return; } + CHECK(dialog_id.is_valid()); + + if (category == TopDialogCategory::ForwardUsers && dialog_id.get_type() != DialogType::User) { + category = TopDialogCategory::ForwardChats; + } auto pos = static_cast(category); CHECK(pos < by_category_.size()); @@ -276,13 +293,28 @@ void TopDialogManager::normalize_rating() { } void TopDialogManager::do_get_top_dialogs(GetTopDialogsQuery &&query) { - auto pos = static_cast(query.category); - CHECK(pos < by_category_.size()); - auto &top_dialogs = by_category_[pos]; + vector dialog_ids; + if (query.category != TopDialogCategory::ForwardUsers) { + auto pos = static_cast(query.category); + CHECK(pos < by_category_.size()); + dialog_ids = transform(by_category_[pos].dialogs, [](const auto &x) { return x.dialog_id; }); + } else { + // merge ForwardUsers and ForwardChats + auto &users = by_category_[static_cast(TopDialogCategory::ForwardUsers)]; + auto &chats = by_category_[static_cast(TopDialogCategory::ForwardChats)]; + size_t users_pos = 0; + size_t chats_pos = 0; + while (users_pos < users.dialogs.size() || chats_pos < chats.dialogs.size()) { + if (chats_pos == chats.dialogs.size() || + (users_pos < users.dialogs.size() && users.dialogs[users_pos] < chats.dialogs[chats_pos])) { + dialog_ids.push_back(users.dialogs[users_pos++].dialog_id); + } else { + dialog_ids.push_back(chats.dialogs[chats_pos++].dialog_id); + } + } + } - auto limit = std::min({query.limit, MAX_TOP_DIALOGS_LIMIT, top_dialogs.dialogs.size()}); - - vector dialog_ids = transform(top_dialogs.dialogs, [](const auto &x) { return x.dialog_id; }); + auto limit = std::min({query.limit, MAX_TOP_DIALOGS_LIMIT, dialog_ids.size()}); auto promise = PromiseCreator::lambda([query = std::move(query), dialog_ids, limit](Result) mutable { vector result; @@ -351,11 +383,21 @@ void TopDialogManager::do_get_top_peers() { int32 flags = contacts_getTopPeers::CORRESPONDENTS_MASK | contacts_getTopPeers::BOTS_PM_MASK | contacts_getTopPeers::BOTS_INLINE_MASK | contacts_getTopPeers::GROUPS_MASK | - contacts_getTopPeers::CHANNELS_MASK | contacts_getTopPeers::PHONE_CALLS_MASK; + contacts_getTopPeers::CHANNELS_MASK | contacts_getTopPeers::PHONE_CALLS_MASK | + contacts_getTopPeers::FORWARD_USERS_MASK | contacts_getTopPeers::FORWARD_CHATS_MASK; - contacts_getTopPeers query{ - flags, true /*correspondents*/, true /*bot_pm*/, true /*bot_inline */, true /*phone_calls*/, - true /*groups*/, true /*channels*/, 0 /*offset*/, 100 /*limit*/, hash}; + contacts_getTopPeers query{flags, + true /*correspondents*/, + true /*bot_pm*/, + true /*bot_inline */, + true /*phone_calls*/, + true /*groups*/, + true /*channels*/, + true /*forward_users*/, + true /*forward_chats*/, + 0 /*offset*/, + 100 /*limit*/, + hash}; auto net_query = G()->net_query_creator().create(create_storer(query)); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this)); } diff --git a/td/telegram/TopDialogManager.h b/td/telegram/TopDialogManager.h index 1117f125..e861896e 100644 --- a/td/telegram/TopDialogManager.h +++ b/td/telegram/TopDialogManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -23,7 +23,17 @@ namespace td { -enum class TopDialogCategory : int32 { Correspondent, BotPM, BotInline, Group, Channel, Call, Size }; +enum class TopDialogCategory : int32 { + Correspondent, + BotPM, + BotInline, + Group, + Channel, + Call, + ForwardUsers, + ForwardChats, + Size +}; inline TopDialogCategory top_dialog_category_from_td_api(const td_api::TopChatCategory &category) { switch (category.get_id()) { @@ -39,6 +49,8 @@ inline TopDialogCategory top_dialog_category_from_td_api(const td_api::TopChatCa return TopDialogCategory::Channel; case td_api::topChatCategoryCalls::ID: return TopDialogCategory::Call; + case td_api::topChatCategoryForwardChats::ID: + return TopDialogCategory::ForwardUsers; default: UNREACHABLE(); } diff --git a/td/telegram/UniqueId.h b/td/telegram/UniqueId.h index fd0b1fce..11a9958d 100644 --- a/td/telegram/UniqueId.h +++ b/td/telegram/UniqueId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 122993a6..c1a14d0a 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,6 +17,7 @@ #include "td/telegram/ConfigManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogId.h" +#include "td/telegram/FolderId.h" #include "td/telegram/Global.h" #include "td/telegram/InlineQueriesManager.h" #include "td/telegram/LanguagePackManager.h" @@ -30,9 +31,12 @@ #include "td/telegram/PollId.h" #include "td/telegram/PollManager.h" #include "td/telegram/PrivacyManager.h" +#include "td/telegram/ScheduledServerMessageId.h" #include "td/telegram/SecretChatId.h" #include "td/telegram/SecretChatsManager.h" +#include "td/telegram/ServerMessageId.h" #include "td/telegram/StateManager.h" +#include "td/telegram/StickerSetId.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" @@ -587,6 +591,9 @@ bool UpdatesManager::is_acceptable_update(const telegram_api::Update *update) co if (id == telegram_api::updateNewChannelMessage::ID) { message = static_cast(update)->message_.get(); } + if (id == telegram_api::updateNewScheduledMessage::ID) { + message = static_cast(update)->message_.get(); + } if (id == telegram_api::updateEditMessage::ID) { message = static_cast(update)->message_.get(); } @@ -616,7 +623,12 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat LOG(INFO) << "Receive " << to_string(updates_ptr); } if (!td_->auth_manager_->is_authorized()) { - LOG(INFO) << "Ignore updates received before authorization or after logout"; + 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(); + } + LOG(INFO) << "Ignore received before authorization or after logout " << to_string(updates_ptr); return; } @@ -639,15 +651,16 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat : update->user_id_; update->flags_ |= MessagesManager::MESSAGE_FLAG_HAS_FROM_ID; - on_pending_update(make_tl_object( - make_tl_object( - update->flags_, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_, from_id, - make_tl_object(update->user_id_), std::move(update->fwd_from_), - update->via_bot_id_, update->reply_to_msg_id_, update->date_, update->message_, nullptr, - nullptr, std::move(update->entities_), 0, 0, "", 0), - update->pts_, update->pts_count_), - 0, "telegram_api::updatesShortMessage"); + on_pending_update( + make_tl_object( + make_tl_object( + update->flags_, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_, from_id, + make_tl_object(update->user_id_), std::move(update->fwd_from_), + update->via_bot_id_, update->reply_to_msg_id_, update->date_, update->message_, nullptr, nullptr, + std::move(update->entities_), 0, 0, "", 0, Auto()), + update->pts_, update->pts_count_), + 0, "telegram_api::updatesShortMessage"); break; } case telegram_api::updateShortChatMessage::ID: { @@ -662,15 +675,16 @@ void UpdatesManager::on_get_updates(tl_object_ptr &&updat } update->flags_ |= MessagesManager::MESSAGE_FLAG_HAS_FROM_ID; - on_pending_update(make_tl_object( - make_tl_object( - update->flags_, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_, update->from_id_, - make_tl_object(update->chat_id_), std::move(update->fwd_from_), - update->via_bot_id_, update->reply_to_msg_id_, update->date_, update->message_, nullptr, - nullptr, std::move(update->entities_), 0, 0, "", 0), - update->pts_, update->pts_count_), - 0, "telegram_api::updatesShortChatMessage"); + on_pending_update( + make_tl_object( + make_tl_object( + update->flags_, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, update->id_, + update->from_id_, make_tl_object(update->chat_id_), + std::move(update->fwd_from_), update->via_bot_id_, update->reply_to_msg_id_, update->date_, + update->message_, nullptr, nullptr, std::move(update->entities_), 0, 0, "", 0, Auto()), + update->pts_, update->pts_count_), + 0, "telegram_api::updatesShortChatMessage"); break; } case telegram_api::updateShort::ID: { @@ -805,6 +819,8 @@ vector *> UpdatesManager::get_new_mes messages.emplace_back(&static_cast(update.get())->message_); } else if (constructor_id == telegram_api::updateNewChannelMessage::ID) { messages.emplace_back(&static_cast(update.get())->message_); + } else if (constructor_id == telegram_api::updateNewScheduledMessage::ID) { + messages.emplace_back(&static_cast(update.get())->message_); } } } @@ -936,6 +952,7 @@ void UpdatesManager::process_get_difference_updates( for (auto &update : other_updates) { auto constructor_id = update->get_id(); if (constructor_id == telegram_api::updateMessageID::ID) { + // in getDifference updateMessageID can't be received for scheduled messages on_update(move_tl_object_as(update), true); CHECK(!running_get_difference_); } @@ -945,6 +962,11 @@ void UpdatesManager::process_get_difference_updates( CHECK(!running_get_difference_); } + if (constructor_id == telegram_api::updateFolderPeers::ID) { + on_update(move_tl_object_as(update), true); + CHECK(!running_get_difference_); + } + /* // TODO can't apply it here, because dialog may not be created yet // process updateReadHistoryInbox before new messages @@ -957,7 +979,7 @@ void UpdatesManager::process_get_difference_updates( for (auto &message : new_messages) { // channel messages must not be received in this vector - td_->messages_manager_->on_get_message(std::move(message), true, false, true, true, "get difference"); + td_->messages_manager_->on_get_message(std::move(message), true, false, false, true, true, "get difference"); CHECK(!running_get_difference_); } @@ -1169,6 +1191,29 @@ void UpdatesManager::on_pending_updates(vectorget_id(); + if (constructor_id == telegram_api::updateNewMessage::ID || + constructor_id == telegram_api::updateNewChannelMessage::ID) { + ordinary_new_message_count++; + } else if (constructor_id == telegram_api::updateNewScheduledMessage::ID) { + scheduled_new_message_count++; + } + } + } + + if (ordinary_new_message_count != 0 && scheduled_new_message_count != 0) { + LOG(ERROR) << "Receive mixed message types in updates:"; + for (auto &update : updates) { + LOG(ERROR) << "Update: " << oneline(to_string(update)); + } + schedule_get_difference("on_get_wrong_updates"); + return; + } + for (auto &update : updates) { if (update != nullptr) { LOG(INFO) << "Receive from " << source << " pending " << to_string(update); @@ -1176,16 +1221,28 @@ void UpdatesManager::on_pending_updates(vector(update); - if (!td_->messages_manager_->on_update_message_id( - sent_message_update->random_id_, MessageId(ServerMessageId(sent_message_update->id_)), source)) { + bool success = false; + if (ordinary_new_message_count != 0) { + success = td_->messages_manager_->on_update_message_id( + sent_message_update->random_id_, MessageId(ServerMessageId(sent_message_update->id_)), source); + } else if (scheduled_new_message_count != 0) { + success = td_->messages_manager_->on_update_scheduled_message_id( + sent_message_update->random_id_, ScheduledServerMessageId(sent_message_update->id_), source); + } + if (!success) { for (auto &debug_update : updates) { LOG(ERROR) << "Update: " << oneline(to_string(debug_update)); } } processed_updates++; update = nullptr; - CHECK(!running_get_difference_); } + if (id == telegram_api::updateFolderPeers::ID) { + on_update(move_tl_object_as(update), false); + processed_updates++; + update = nullptr; + } + CHECK(!running_get_difference_); } } @@ -1272,6 +1329,11 @@ void UpdatesManager::process_updates(vector> on_update(move_tl_object_as(update), force_apply); } + // process updateNewScheduledMessage first + if (constructor_id == telegram_api::updateNewScheduledMessage::ID) { + on_update(move_tl_object_as(update), force_apply); + } + // updatePtsChanged forces get difference, so process it last if (constructor_id == telegram_api::updatePtsChanged::ID) { update_pts_changed = move_tl_object_as(update); @@ -1404,6 +1466,9 @@ void UpdatesManager::on_update(tl_object_ptrpts_; int pts_count = update->pts_count_; + if (force_apply) { + update->still_unread_count_ = -1; + } td_->messages_manager_->add_pending_update(std::move(update), new_pts, pts_count, force_apply, "on_updateReadHistoryInbox"); } @@ -1442,7 +1507,7 @@ void UpdatesManager::on_update(tl_object_ptr void UpdatesManager::on_update(tl_object_ptr update, bool force_apply) { if (!force_apply) { - td_->contacts_manager_->invalidate_channel_full(ChannelId(update->channel_id_), false); + td_->contacts_manager_->invalidate_channel_full(ChannelId(update->channel_id_), false, false); } } @@ -1521,6 +1586,14 @@ void UpdatesManager::on_update(tl_object_ptr } } +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + td_->messages_manager_->on_get_peer_settings(DialogId(update->peer_), std::move(update->settings_)); +} + +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + td_->contacts_manager_->on_update_peer_located(std::move(update->peers_), true); +} + void UpdatesManager::on_update(tl_object_ptr update, bool force_apply) { CHECK(update != nullptr); td_->web_pages_manager_->on_get_web_page(std::move(update->webpage_), DialogId()); @@ -1541,6 +1614,18 @@ void UpdatesManager::on_update(tl_object_ptr update->pts_count_, "on_updateChannelWebPage"); } +void UpdatesManager::on_update(tl_object_ptr update, bool force_apply) { + CHECK(update != nullptr); + for (auto &folder_peer : update->folder_peers_) { + DialogId dialog_id(folder_peer->peer_); + FolderId folder_id(folder_peer->folder_id_); + td_->messages_manager_->on_update_dialog_folder_id(dialog_id, folder_id); + } + + td_->messages_manager_->add_pending_update(make_tl_object(), update->pts_, update->pts_count_, + force_apply, "on_updateFolderPeers"); +} + int32 UpdatesManager::get_short_update_date() const { int32 now = G()->unix_time(); if (short_update_date_ > 0) { @@ -1671,12 +1756,7 @@ void UpdatesManager::on_update(tl_object_ptr upda } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { - td_->contacts_manager_->on_update_user_blocked(UserId(update->user_id_), update->blocked_); -} - -void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { - td_->contacts_manager_->on_update_user_links(UserId(update->user_id_), std::move(update->my_link_), - std::move(update->foreign_link_)); + td_->contacts_manager_->on_update_user_is_blocked(UserId(update->user_id_), update->blocked_); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { @@ -1724,12 +1804,14 @@ void UpdatesManager::on_update(tl_object_ptr u } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + FolderId folder_id(update->flags_ & telegram_api::updateDialogPinned::FOLDER_ID_MASK ? update->folder_id_ : 0); td_->messages_manager_->on_update_dialog_is_pinned( - DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogPinned::PINNED_MASK) != 0); + folder_id, DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogPinned::PINNED_MASK) != 0); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { - td_->messages_manager_->on_update_pinned_dialogs(); // TODO use update->order_ + FolderId folder_id(update->flags_ & telegram_api::updatePinnedDialogs::FOLDER_ID_MASK ? update->folder_id_ : 0); + td_->messages_manager_->on_update_pinned_dialogs(folder_id); // TODO use update->order_ } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { @@ -1797,7 +1879,8 @@ void UpdatesManager::on_update(tl_object_ptr update } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { - td_->stickers_manager_->on_get_messages_sticker_set(0, std::move(update->stickerset_), true); + td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), std::move(update->stickerset_), true, + "updateNewStickerSet"); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { @@ -1806,7 +1889,8 @@ void UpdatesManager::on_update(tl_object_ptr up void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { bool is_masks = (update->flags_ & telegram_api::updateStickerSetsOrder::MASKS_MASK) != 0; - td_->stickers_manager_->on_update_sticker_sets_order(is_masks, update->order_); + td_->stickers_manager_->on_update_sticker_sets_order(is_masks, + StickersManager::convert_sticker_set_ids(update->order_)); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { @@ -1874,10 +1958,36 @@ void UpdatesManager::on_update(tl_object_ptr updat std::move(update->difference_)); } +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + td_->messages_manager_->on_update_live_location_viewed( + {DialogId(update->peer_), MessageId(ServerMessageId(update->msg_id_))}); +} + void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { td_->poll_manager_->on_get_poll(PollId(update->poll_id_), std::move(update->poll_), std::move(update->results_)); } +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + td_->messages_manager_->on_get_message(std::move(update->message_), true, false, true, true, true, + "updateNewScheduledMessage"); +} + +void UpdatesManager::on_update(tl_object_ptr update, + bool /*force_apply*/) { + vector message_ids = transform(update->messages_, [](int32 scheduled_server_message_id) { + return ScheduledServerMessageId(scheduled_server_message_id); + }); + + td_->messages_manager_->on_update_delete_scheduled_messages(DialogId(update->peer_), std::move(message_ids)); +} + +void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + LOG(INFO) << "Receive updateLoginToken after authorization"; +} + // unsupported updates +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 68be2446..770c60a1 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -189,10 +189,14 @@ class UpdatesManager : public Actor { 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*/); + 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*/); + 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*/); @@ -202,7 +206,6 @@ class UpdatesManager : public Actor { 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*/); void on_update(tl_object_ptr update, bool /*force_apply*/); void on_update(tl_object_ptr update, bool /*force_apply*/); @@ -275,9 +278,18 @@ class UpdatesManager : public Actor { 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*/); + 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*/); + // unsupported updates + + void on_update(tl_object_ptr update, bool /*force_apply*/); }; } // namespace td diff --git a/td/telegram/UserId.h b/td/telegram/UserId.h index e89243de..a809545a 100644 --- a/td/telegram/UserId.h +++ b/td/telegram/UserId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/Venue.cpp b/td/telegram/Venue.cpp new file mode 100644 index 00000000..4349c7ac --- /dev/null +++ b/td/telegram/Venue.cpp @@ -0,0 +1,123 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/Venue.h" + +#include "td/telegram/misc.h" +#include "td/telegram/secret_api.h" + +namespace td { + +Venue::Venue(const tl_object_ptr &geo_point_ptr, string title, string address, string provider, + string id, string type) + : location_(geo_point_ptr) + , title_(std::move(title)) + , address_(std::move(address)) + , provider_(std::move(provider)) + , id_(std::move(id)) + , type_(std::move(type)) { +} + +Venue::Venue(Location location, string title, string address, string provider, string id, string type) + : location_(location) + , title_(std::move(title)) + , address_(std::move(address)) + , provider_(std::move(provider)) + , id_(std::move(id)) + , type_(std::move(type)) { +} + +Venue::Venue(const tl_object_ptr &venue) + : location_(venue->location_) + , title_(venue->title_) + , address_(venue->address_) + , provider_(venue->provider_) + , id_(venue->id_) + , type_(venue->type_) { +} + +bool Venue::empty() const { + return location_.empty(); +} + +Location &Venue::location() { + return location_; +} + +const Location &Venue::location() const { + return location_; +} + +tl_object_ptr Venue::get_venue_object() const { + return make_tl_object(location_.get_location_object(), title_, address_, provider_, id_, type_); +} + +tl_object_ptr Venue::get_input_media_venue() const { + return make_tl_object(location_.get_input_geo_point(), title_, address_, provider_, + id_, type_); +} + +SecretInputMedia Venue::get_secret_input_media_venue() const { + return SecretInputMedia{nullptr, + make_tl_object( + location_.get_latitude(), location_.get_longitude(), title_, address_, provider_, id_)}; +} + +tl_object_ptr Venue::get_input_bot_inline_message_media_venue( + int32 flags, tl_object_ptr &&reply_markup) const { + return make_tl_object( + flags, location_.get_input_geo_point(), title_, address_, provider_, id_, type_, std::move(reply_markup)); +} + +bool operator==(const Venue &lhs, const Venue &rhs) { + return lhs.location_ == rhs.location_ && lhs.title_ == rhs.title_ && lhs.address_ == rhs.address_ && + lhs.provider_ == rhs.provider_ && lhs.id_ == rhs.id_ && lhs.type_ == rhs.type_; +} + +bool operator!=(const Venue &lhs, const Venue &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue) { + return string_builder << "Venue[location = " << venue.location_ << ", title = " << venue.title_ + << ", address = " << venue.address_ << ", provider = " << venue.provider_ + << ", id = " << venue.id_ << ", type = " << venue.type_ << "]"; +} + +Result process_input_message_venue(tl_object_ptr &&input_message_content) { + CHECK(input_message_content != nullptr); + CHECK(input_message_content->get_id() == td_api::inputMessageVenue::ID); + auto venue = std::move(static_cast(input_message_content.get())->venue_); + + if (venue == nullptr) { + return Status::Error(400, "Venue can't be empty"); + } + + if (!clean_input_string(venue->title_)) { + return Status::Error(400, "Venue title must be encoded in UTF-8"); + } + if (!clean_input_string(venue->address_)) { + return Status::Error(400, "Venue address must be encoded in UTF-8"); + } + if (!clean_input_string(venue->provider_)) { + return Status::Error(400, "Venue provider must be encoded in UTF-8"); + } + if (!clean_input_string(venue->id_)) { + return Status::Error(400, "Venue identifier must be encoded in UTF-8"); + } + if (!clean_input_string(venue->type_)) { + return Status::Error(400, "Venue type must be encoded in UTF-8"); + } + + Venue result(venue); + if (result.empty()) { + return Status::Error(400, "Wrong venue location specified"); + } + + return result; +} + +} // namespace td diff --git a/td/telegram/Venue.h b/td/telegram/Venue.h new file mode 100644 index 00000000..69667dbb --- /dev/null +++ b/td/telegram/Venue.h @@ -0,0 +1,94 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/Location.h" +#include "td/telegram/SecretInputMedia.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" +#include "td/telegram/Version.h" + +#include "td/utils/common.h" +#include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +class Venue { + Location location_; + string title_; + string address_; + string provider_; + string id_; + string type_; + + friend bool operator==(const Venue &lhs, const Venue &rhs); + friend bool operator!=(const Venue &lhs, const Venue &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue); + + public: + Venue() = default; + + Venue(const tl_object_ptr &geo_point_ptr, string title, string address, string provider, + string id, string type); + + Venue(Location location, string title, string address, string provider, string id, string type); + + explicit Venue(const tl_object_ptr &venue); + + bool empty() const; + + Location &location(); + + const Location &location() const; + + tl_object_ptr get_venue_object() const; + + tl_object_ptr get_input_media_venue() const; + + SecretInputMedia get_secret_input_media_venue() const; + + // TODO very strange function + tl_object_ptr get_input_bot_inline_message_media_venue( + int32 flags, tl_object_ptr &&reply_markup) const; + + template + void store(StorerT &storer) const { + using td::store; + store(location_, storer); + store(title_, storer); + store(address_, storer); + store(provider_, storer); + store(id_, storer); + store(type_, storer); + } + + template + void parse(ParserT &parser) { + using td::parse; + parse(location_, parser); + parse(title_, parser); + parse(address_, parser); + parse(provider_, parser); + parse(id_, parser); + if (parser.version() >= static_cast(Version::AddVenueType)) { + parse(type_, parser); + } + } +}; + +bool operator==(const Venue &lhs, const Venue &rhs); +bool operator!=(const Venue &lhs, const Venue &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const Venue &venue); + +Result process_input_message_venue(td_api::object_ptr &&input_message_content) + TD_WARN_UNUSED_RESULT; + +} // namespace td diff --git a/td/telegram/Version.h b/td/telegram/Version.h index 3c4f988c..7e995081 100644 --- a/td/telegram/Version.h +++ b/td/telegram/Version.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 98; +constexpr int32 MTPROTO_LAYER = 107; enum class Version : int32 { Initial, @@ -34,6 +34,7 @@ enum class Version : int32 { SupportMinithumbnails, AddVideoCallsSupport, AddPhotoSizeSource, + AddFolders, Next }; @@ -45,6 +46,8 @@ enum class DbVersion : int32 { MessagesCallIndex, FixFileRemoteLocationKeyBug, AddNotificationsSupport, + AddFolders, + AddScheduledMessages, Next }; diff --git a/td/telegram/VideoNotesManager.cpp b/td/telegram/VideoNotesManager.cpp index 9002454e..8cebe213 100644 --- a/td/telegram/VideoNotesManager.cpp +++ b/td/telegram/VideoNotesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -174,7 +174,7 @@ SecretInputMedia VideoNotesManager::get_secret_input_media(FileId video_note_fil return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -202,8 +202,8 @@ tl_object_ptr VideoNotesManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); diff --git a/td/telegram/VideoNotesManager.h b/td/telegram/VideoNotesManager.h index 0933d480..8437916f 100644 --- a/td/telegram/VideoNotesManager.h +++ b/td/telegram/VideoNotesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/VideoNotesManager.hpp b/td/telegram/VideoNotesManager.hpp index 88fa86ef..7f198a84 100644 --- a/td/telegram/VideoNotesManager.hpp +++ b/td/telegram/VideoNotesManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/VideosManager.cpp b/td/telegram/VideosManager.cpp index 36c85098..6e3d6c95 100644 --- a/td/telegram/VideosManager.cpp +++ b/td/telegram/VideosManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -201,7 +201,7 @@ SecretInputMedia VideosManager::get_secret_input_media(FileId video_file_id, return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -228,12 +228,12 @@ tl_object_ptr VideosManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { int32 flags = 0; if (ttl != 0) { flags |= telegram_api::inputMediaDocument::TTL_SECONDS_MASK; } - return make_tl_object(flags, file_view.remote_location().as_input_document(), + return make_tl_object(flags, file_view.main_remote_location().as_input_document(), ttl); } if (file_view.has_url()) { diff --git a/td/telegram/VideosManager.h b/td/telegram/VideosManager.h index 75d0dacb..28fb3546 100644 --- a/td/telegram/VideosManager.h +++ b/td/telegram/VideosManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/VideosManager.hpp b/td/telegram/VideosManager.hpp index 844c3023..14613973 100644 --- a/td/telegram/VideosManager.hpp +++ b/td/telegram/VideosManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/VoiceNotesManager.cpp b/td/telegram/VoiceNotesManager.cpp index 85dfdf28..7ba0f8d7 100644 --- a/td/telegram/VoiceNotesManager.cpp +++ b/td/telegram/VoiceNotesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -147,7 +147,7 @@ SecretInputMedia VoiceNotesManager::get_secret_input_media(FileId voice_file_id, return SecretInputMedia{}; } if (file_view.has_remote_location()) { - input_file = file_view.remote_location().as_input_encrypted_file(); + input_file = file_view.main_remote_location().as_input_encrypted_file(); } if (!input_file) { return SecretInputMedia{}; @@ -169,8 +169,8 @@ tl_object_ptr VoiceNotesManager::get_input_media( if (file_view.is_encrypted()) { return nullptr; } - if (file_view.has_remote_location() && !file_view.remote_location().is_web() && input_file == nullptr) { - return make_tl_object(0, file_view.remote_location().as_input_document(), 0); + if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) { + return make_tl_object(0, file_view.main_remote_location().as_input_document(), 0); } if (file_view.has_url()) { return make_tl_object(0, file_view.url(), 0); diff --git a/td/telegram/VoiceNotesManager.h b/td/telegram/VoiceNotesManager.h index df1ae565..92943f0c 100644 --- a/td/telegram/VoiceNotesManager.h +++ b/td/telegram/VoiceNotesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/VoiceNotesManager.hpp b/td/telegram/VoiceNotesManager.hpp index 089041da..38046074 100644 --- a/td/telegram/VoiceNotesManager.hpp +++ b/td/telegram/VoiceNotesManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 75d3e631..000e4a69 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/WebPageBlock.h b/td/telegram/WebPageBlock.h index 6c491fe0..556ceb00 100644 --- a/td/telegram/WebPageBlock.h +++ b/td/telegram/WebPageBlock.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/WebPageId.h b/td/telegram/WebPageId.h index e8e4aefb..b1ce54f5 100644 --- a/td/telegram/WebPageId.h +++ b/td/telegram/WebPageId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index 0e2c6125..3cc8405e 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -198,6 +198,7 @@ class WebPagesManager::WebPage { int32 duration = 0; string author; Document document; + vector documents; WebPageInstantView instant_view; FileSourceId file_source_id; @@ -220,6 +221,7 @@ class WebPagesManager::WebPage { bool has_instant_view = !instant_view.is_empty; bool is_instant_view_v2 = instant_view.is_v2; bool has_no_hash = true; + bool has_documents = !documents.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_type); STORE_FLAG(has_site_name); @@ -234,6 +236,7 @@ class WebPagesManager::WebPage { STORE_FLAG(has_instant_view); STORE_FLAG(has_no_hash); STORE_FLAG(is_instant_view_v2); + STORE_FLAG(has_documents); END_STORE_FLAGS(); store(url, storer); @@ -269,6 +272,9 @@ class WebPagesManager::WebPage { if (has_document) { store(document, storer); } + if (has_documents) { + store(documents, storer); + } } template @@ -287,6 +293,7 @@ class WebPagesManager::WebPage { bool has_instant_view; bool is_instant_view_v2; bool has_no_hash; + bool has_documents; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_type); PARSE_FLAG(has_site_name); @@ -301,6 +308,7 @@ class WebPagesManager::WebPage { PARSE_FLAG(has_instant_view); PARSE_FLAG(has_no_hash); PARSE_FLAG(is_instant_view_v2); + PARSE_FLAG(has_documents); END_PARSE_FLAGS(); parse(url, parser); @@ -342,6 +350,9 @@ class WebPagesManager::WebPage { if (has_document) { parse(document, parser); } + if (has_documents) { + parse(documents, parser); + } if (has_instant_view) { instant_view.is_empty = false; @@ -464,7 +475,19 @@ WebPageId WebPagesManager::on_get_web_page(tl_object_ptr if (document_id == telegram_api::document::ID) { auto parsed_document = td_->documents_manager_->on_get_document( move_tl_object_as(web_page->document_), owner_dialog_id); - page->document = parsed_document; + page->document = std::move(parsed_document); + } + } + if (web_page->flags_ & WEBPAGE_FLAG_HAS_DOCUMENTS) { + for (auto &document : web_page->documents_) { + int32 document_id = document->get_id(); + if (document_id == telegram_api::document::ID) { + auto parsed_document = td_->documents_manager_->on_get_document( + move_tl_object_as(document), owner_dialog_id); + if (!parsed_document.empty()) { + page->documents.push_back(std::move(parsed_document)); + } + } } } if (web_page->flags_ & WEBPAGE_FLAG_HAS_INSTANT_VIEW) { @@ -616,9 +639,9 @@ void WebPagesManager::on_get_web_page_by_url(const string &url, WebPageId web_pa cached_web_page_id = web_page_id; } -void WebPagesManager::wait_for_pending_web_page(DialogId dialog_id, MessageId message_id, WebPageId web_page_id) { - LOG(INFO) << "Waiting for " << web_page_id << " needed in " << message_id << " in " << dialog_id; - pending_web_pages_[web_page_id].emplace(dialog_id, message_id); +void WebPagesManager::wait_for_pending_web_page(FullMessageId full_message_id, WebPageId web_page_id) { + LOG(INFO) << "Waiting for " << web_page_id << " needed in " << full_message_id; + pending_web_pages_[web_page_id].emplace(full_message_id); pending_web_pages_timeout_.add_timeout_in(web_page_id.get(), 1.0); } @@ -806,6 +829,10 @@ void WebPagesManager::reload_web_page_instant_view(WebPageId web_page_id) { true, std::move(result)); }); + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + td_->create_handler(std::move(promise)) ->send(web_page->url, web_page->instant_view.is_full ? web_page->instant_view.hash : 0); } @@ -1021,6 +1048,10 @@ void WebPagesManager::on_load_web_page_by_url_from_database(WebPageId web_page_i } void WebPagesManager::reload_web_page_by_url(const string &url, Promise &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + LOG(INFO) << "Reload url \"" << url << '"'; td_->create_handler(std::move(promise))->send(url, 0); } @@ -1201,65 +1232,52 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_ std::unordered_map documents; std::unordered_map videos; std::unordered_map voice_notes; + std::unordered_map others; + auto get_map = [&](Document::Type document_type) -> std::unordered_map & { + switch (document_type) { + case Document::Type::Animation: + return animations; + case Document::Type::Audio: + return audios; + case Document::Type::General: + return documents; + case Document::Type::Video: + return videos; + case Document::Type::VoiceNote: + return voice_notes; + default: + return others; + } + }; + for (auto &document_ptr : page->documents_) { if (document_ptr->get_id() == telegram_api::document::ID) { auto document = move_tl_object_as(document_ptr); auto document_id = document->id_; auto parsed_document = td_->documents_manager_->on_get_document(std::move(document), owner_dialog_id); - if (parsed_document.type == Document::Type::Animation) { - animations.emplace(document_id, parsed_document.file_id); - } else if (parsed_document.type == Document::Type::Audio) { - audios.emplace(document_id, parsed_document.file_id); - } else if (parsed_document.type == Document::Type::General) { - documents.emplace(document_id, parsed_document.file_id); - } else if (parsed_document.type == Document::Type::Video) { - videos.emplace(document_id, parsed_document.file_id); - } else if (parsed_document.type == Document::Type::VoiceNote) { - voice_notes.emplace(document_id, parsed_document.file_id); - } else { - LOG(ERROR) << "Receive document of the wrong type: " << parsed_document; + if (!parsed_document.empty()) { + get_map(parsed_document.type).emplace(document_id, parsed_document.file_id); } } } - if (web_page->document.type == Document::Type::Animation) { - auto file_view = td_->file_manager_->get_file_view(web_page->document.file_id); - if (file_view.has_remote_location()) { - animations.emplace(file_view.remote_location().get_id(), web_page->document.file_id); - } else { - LOG(ERROR) << "Animation has no remote location"; - } + if (!others.empty()) { + auto file_view = td_->file_manager_->get_file_view(others.begin()->second); + LOG(ERROR) << "Receive document of an unexpected type " << file_view.get_type(); } - if (web_page->document.type == Document::Type::Audio) { - auto file_view = td_->file_manager_->get_file_view(web_page->document.file_id); + + auto add_document = [&](const Document &document) { + auto file_view = td_->file_manager_->get_file_view(document.file_id); if (file_view.has_remote_location()) { - audios.emplace(file_view.remote_location().get_id(), web_page->document.file_id); + get_map(document.type).emplace(file_view.remote_location().get_id(), document.file_id); } else { - LOG(ERROR) << "Audio has no remote location"; + LOG(ERROR) << document.type << " has no remote location"; } + }; + if (!web_page->document.empty()) { + add_document(web_page->document); } - if (web_page->document.type == Document::Type::General) { - auto file_view = td_->file_manager_->get_file_view(web_page->document.file_id); - if (file_view.has_remote_location()) { - documents.emplace(file_view.remote_location().get_id(), web_page->document.file_id); - } else { - LOG(ERROR) << "Document has no remote location"; - } - } - if (web_page->document.type == Document::Type::Video) { - auto file_view = td_->file_manager_->get_file_view(web_page->document.file_id); - if (file_view.has_remote_location()) { - videos.emplace(file_view.remote_location().get_id(), web_page->document.file_id); - } else { - LOG(ERROR) << "Video has no remote location"; - } - } - if (web_page->document.type == Document::Type::VoiceNote) { - auto file_view = td_->file_manager_->get_file_view(web_page->document.file_id); - if (file_view.has_remote_location()) { - voice_notes.emplace(file_view.remote_location().get_id(), web_page->document.file_id); - } else { - LOG(ERROR) << "Voice note has no remote location"; - } + for (auto &document : web_page->documents) { + add_document(document); } LOG(INFO) << "Receive a web page instant view with " << page->blocks_.size() << " blocks, " << animations.size() @@ -1299,9 +1317,7 @@ class WebPagesManager::WebPageLogEvent { template void parse(ParserT &parser) { td::parse(web_page_id, parser); - CHECK(web_page_out == nullptr); - web_page_out = make_unique(); - td::parse(*web_page_out, parser); + td::parse(web_page_out, parser); } }; @@ -1493,6 +1509,9 @@ vector WebPagesManager::get_web_page_file_ids(const WebPage *web_page) c if (!web_page->document.empty()) { web_page->document.append_file_ids(td_, result); } + for (auto &document : web_page->documents) { + document.append_file_ids(td_, result); + } if (!web_page->instant_view.is_empty) { for (auto &page_block : web_page->instant_view.page_blocks) { page_block->append_file_ids(result); diff --git a/td/telegram/WebPagesManager.h b/td/telegram/WebPagesManager.h index 5244be7c..05d2680a 100644 --- a/td/telegram/WebPagesManager.h +++ b/td/telegram/WebPagesManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,7 +12,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" -#include "td/telegram/MessageId.h" +#include "td/telegram/FullMessageId.h" #include "td/telegram/Photo.h" #include "td/telegram/WebPageId.h" @@ -47,7 +47,7 @@ class WebPagesManager : public Actor { void on_get_web_page_by_url(const string &url, WebPageId web_page_id, bool from_database); - void wait_for_pending_web_page(DialogId dialog_id, MessageId message_id, WebPageId web_page_id); + void wait_for_pending_web_page(FullMessageId full_message_id, WebPageId web_page_id); bool have_web_page(WebPageId web_page_id) const; @@ -84,17 +84,18 @@ class WebPagesManager : public Actor { string get_web_page_search_text(WebPageId web_page_id) const; private: - static constexpr int32 WEBPAGE_FLAG_HAS_TYPE = 1; - static constexpr int32 WEBPAGE_FLAG_HAS_SITE_NAME = 2; - static constexpr int32 WEBPAGE_FLAG_HAS_TITLE = 4; - static constexpr int32 WEBPAGE_FLAG_HAS_DESCRIPTION = 8; - static constexpr int32 WEBPAGE_FLAG_HAS_PHOTO = 16; - static constexpr int32 WEBPAGE_FLAG_HAS_EMBEDDED_PREVIEW = 32; - static constexpr int32 WEBPAGE_FLAG_HAS_EMBEDDED_PREVIEW_SIZE = 64; - static constexpr int32 WEBPAGE_FLAG_HAS_DURATION = 128; - static constexpr int32 WEBPAGE_FLAG_HAS_AUTHOR = 256; - static constexpr int32 WEBPAGE_FLAG_HAS_DOCUMENT = 512; - static constexpr int32 WEBPAGE_FLAG_HAS_INSTANT_VIEW = 1024; + static constexpr int32 WEBPAGE_FLAG_HAS_TYPE = 1 << 0; + static constexpr int32 WEBPAGE_FLAG_HAS_SITE_NAME = 1 << 1; + static constexpr int32 WEBPAGE_FLAG_HAS_TITLE = 1 << 2; + static constexpr int32 WEBPAGE_FLAG_HAS_DESCRIPTION = 1 << 3; + static constexpr int32 WEBPAGE_FLAG_HAS_PHOTO = 1 << 4; + static constexpr int32 WEBPAGE_FLAG_HAS_EMBEDDED_PREVIEW = 1 << 5; + static constexpr int32 WEBPAGE_FLAG_HAS_EMBEDDED_PREVIEW_SIZE = 1 << 6; + static constexpr int32 WEBPAGE_FLAG_HAS_DURATION = 1 << 7; + static constexpr int32 WEBPAGE_FLAG_HAS_AUTHOR = 1 << 8; + static constexpr int32 WEBPAGE_FLAG_HAS_DOCUMENT = 1 << 9; + static constexpr int32 WEBPAGE_FLAG_HAS_INSTANT_VIEW = 1 << 10; + static constexpr int32 WEBPAGE_FLAG_HAS_DOCUMENTS = 1 << 11; class WebPage; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index c21e1efb..57e09bd7 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1,23 +1,20 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "td/telegram/ClientActor.h" -#include "td/telegram/Log.h" - -#include "td/telegram/td_api_json.h" - #include "td/actor/actor.h" -#include "td/tl/tl_json.h" // should be included after td_api_json? - #include "memprof/memprof.h" #include "td/net/HttpQuery.h" #include "td/net/HttpReader.h" +#include "td/telegram/ClientActor.h" +#include "td/telegram/Log.h" +#include "td/telegram/td_api_json.h" + #include "td/utils/base64.h" #include "td/utils/buffer.h" #include "td/utils/common.h" @@ -48,7 +45,7 @@ #include #include #include -#include // for strcmp +#include #include #include #include @@ -56,6 +53,7 @@ #include #include #include +#include #ifdef USE_READLINE /* Standard readline include files. */ @@ -402,6 +400,7 @@ class CliClient final : public Actor { auto parameters = td_api::make_object(); parameters->use_test_dc_ = use_test_dc_; parameters->use_message_database_ = true; + parameters->use_chat_info_database_ = true; parameters->use_secret_chats_ = true; parameters->api_id_ = api_id_; parameters->api_hash_ = api_hash_; @@ -430,6 +429,23 @@ 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') { + chars.insert(c); + } + } + if (chars.empty()) { + return ' '; + } + if (chars.size() == 1) { + return *chars.begin(); + } + LOG(ERROR) << "Failed to determine delimiter in \"" << str << '"'; + return ' '; + } + int64 as_chat_id(Slice str) const { str = trim(str); if (str[0] == '@') { @@ -451,8 +467,16 @@ class CliClient final : public Actor { return to_integer(str); } - vector as_chat_ids(Slice chat_ids, char delimiter = ' ') const { - return transform(full_split(trim(chat_ids), delimiter), [this](Slice str) { return as_chat_id(str); }); + static td_api::object_ptr as_chat_list(string chat_list) { + if (!chat_list.empty() && chat_list.back() == 'a') { + return td_api::make_object(); + } + return td_api::make_object(); + } + + vector as_chat_ids(Slice chat_ids) const { + return transform(full_split(trim(chat_ids), get_delimiter(chat_ids)), + [this](Slice str) { return as_chat_id(str); }); } static int64 as_message_id(Slice str) { @@ -463,8 +487,12 @@ class CliClient final : public Actor { return to_integer(str); } - static vector as_message_ids(Slice message_ids, char delimiter = ' ') { - return transform(full_split(trim(message_ids), delimiter), as_message_id); + static vector as_message_ids(Slice message_ids) { + return transform(full_split(trim(message_ids), get_delimiter(message_ids)), as_message_id); + } + + static int32 as_button_id(Slice str) { + return to_integer(trim(str)); } int32 as_user_id(Slice str) const { @@ -483,8 +511,8 @@ class CliClient final : public Actor { return to_integer(str); } - vector as_user_ids(Slice user_ids, char delimiter = ' ') const { - return transform(full_split(user_ids, delimiter), [this](Slice str) { return as_user_id(str); }); + vector as_user_ids(Slice user_ids) const { + return transform(full_split(user_ids, get_delimiter(user_ids)), [this](Slice str) { return as_user_id(str); }); } static int32 as_basic_group_id(Slice str) { @@ -524,20 +552,20 @@ class CliClient final : public Actor { return td_api::make_object(as_file_id(str)); } - static tl_object_ptr as_local_file(string path) { + static td_api::object_ptr as_local_file(string path) { return td_api::make_object(trim(std::move(path))); } - static tl_object_ptr as_remote_file(string id) { + static td_api::object_ptr as_remote_file(string id) { return td_api::make_object(trim(std::move(id))); } - static tl_object_ptr as_generated_file(string original_path, string conversion, - int32 expected_size = 0) { + static td_api::object_ptr as_generated_file(string original_path, string conversion, + int32 expected_size = 0) { return td_api::make_object(trim(original_path), trim(conversion), expected_size); } - static tl_object_ptr as_input_file(string str) { + static td_api::object_ptr as_input_file(string str) { str = trim(str); if ((str.size() >= 20 && is_base64url(str)) || begins_with(str, "http")) { return as_remote_file(str); @@ -553,18 +581,19 @@ class CliClient final : public Actor { return as_local_file(str); } - static tl_object_ptr as_input_thumbnail(tl_object_ptr input_file, - int32 width = 0, int32 height = 0) { + static td_api::object_ptr as_input_thumbnail(td_api::object_ptr input_file, + int32 width = 0, int32 height = 0) { return td_api::make_object(std::move(input_file), width, height); } - static tl_object_ptr as_input_thumbnail(const string &thumbnail, int32 width = 0, - int32 height = 0) { + static td_api::object_ptr as_input_thumbnail(const string &thumbnail, int32 width = 0, + int32 height = 0) { return as_input_thumbnail(as_input_file(thumbnail), width, height); } - static tl_object_ptr as_input_thumbnail(const string &original_path, const string &conversion, - int32 width = 0, int32 height = 0) { + static td_api::object_ptr as_input_thumbnail(const string &original_path, + const string &conversion, int32 width = 0, + int32 height = 0) { return as_input_thumbnail(as_generated_file(original_path, conversion), width, height); } @@ -576,7 +605,7 @@ class CliClient final : public Actor { return to_integer(trim(std::move(str))); } - static tl_object_ptr as_location(string latitude, string longitude) { + static td_api::object_ptr as_location(string latitude, string longitude) { if (trim(latitude).empty() && trim(longitude).empty()) { return nullptr; } @@ -589,11 +618,11 @@ class CliClient final : public Actor { } template - static vector to_integers(Slice ids_string, char delimiter = ' ') { - return transform(transform(full_split(ids_string, delimiter), trim), to_integer); + static vector to_integers(Slice ids_string) { + return transform(transform(full_split(ids_string, get_delimiter(ids_string)), trim), to_integer); } - void on_result(uint64 generation, uint64 id, tl_object_ptr result) { + void on_result(uint64 generation, uint64 id, td_api::object_ptr result) { if (id > 0 && GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) { LOG(ERROR) << "Receive result [" << generation << "][id=" << id << "] " << to_string(result); } @@ -701,7 +730,7 @@ class CliClient final : public Actor { } } - void on_error(uint64 generation, uint64 id, tl_object_ptr error) { + void on_error(uint64 generation, uint64 id, td_api::object_ptr error) { if (id > 0 && GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) { LOG(ERROR) << "Receive error [" << generation << "][id=" << id << "] " << to_string(error); } @@ -769,10 +798,10 @@ class CliClient final : public Actor { public: TdCallbackImpl(CliClient *client, uint64 generation) : client_(client), generation_(generation) { } - void on_result(uint64 id, tl_object_ptr result) override { + void on_result(uint64 id, td_api::object_ptr result) override { client_->on_result(generation_, id, std::move(result)); } - void on_error(uint64 id, tl_object_ptr error) override { + void on_error(uint64 id, td_api::object_ptr error) override { client_->on_error(generation_, id, std::move(error)); } TdCallbackImpl(const TdCallbackImpl &) = delete; @@ -860,7 +889,7 @@ class CliClient final : public Actor { Scheduler::subscribe(stdin_.get_poll_info().extract_pollable_fd(this), PollFlags::Read()); if (get_chat_list_) { - send_request(td_api::make_object(std::numeric_limits::max(), 0, 100)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 100)); } if (disable_network_) { send_request(td_api::make_object(td_api::make_object())); @@ -885,11 +914,11 @@ class CliClient final : public Actor { } #endif - static tl_object_ptr as_formatted_text( + static td_api::object_ptr as_formatted_text( string text, vector> entities = {}) { if (entities.empty() && !text.empty()) { auto parsed_text = execute( - td_api::make_object(text, td_api::make_object())); + td_api::make_object(text, td_api::make_object(2))); if (parsed_text->get_id() == td_api::formattedText::ID) { return td_api::move_object_as(parsed_text); } @@ -897,12 +926,12 @@ class CliClient final : public Actor { return td_api::make_object(text, std::move(entities)); } - static tl_object_ptr as_caption(string caption, - vector> entities = {}) { + static td_api::object_ptr as_caption( + string caption, vector> entities = {}) { return as_formatted_text(caption, std::move(entities)); } - static tl_object_ptr get_notification_settings_scope(Slice scope) { + static td_api::object_ptr get_notification_settings_scope(Slice scope) { if (scope.empty()) { return nullptr; } @@ -915,7 +944,7 @@ class CliClient final : public Actor { return td_api::make_object(); } - static tl_object_ptr get_user_privacy_setting(MutableSlice setting) { + static td_api::object_ptr get_user_privacy_setting(MutableSlice setting) { setting = trim(setting); to_lower_inplace(setting); if (setting == "invite") { @@ -936,10 +965,16 @@ class CliClient final : public Actor { if (setting == "photo") { return td_api::make_object(); } + if (setting == "phone_number") { + return td_api::make_object(); + } + if (setting == "find") { + return td_api::make_object(); + } return nullptr; } - static tl_object_ptr get_search_messages_filter(MutableSlice filter) { + static td_api::object_ptr get_search_messages_filter(MutableSlice filter) { filter = trim(filter); to_lower_inplace(filter); if (filter == "an" || filter == "animation") { @@ -993,7 +1028,7 @@ class CliClient final : public Actor { return nullptr; } - static tl_object_ptr get_chat_members_filter(MutableSlice filter) { + static td_api::object_ptr get_chat_members_filter(MutableSlice filter) { filter = trim(filter); to_lower_inplace(filter); if (filter == "a" || filter == "admin" || filter == "administrators") { @@ -1020,7 +1055,7 @@ class CliClient final : public Actor { return nullptr; } - static tl_object_ptr get_top_chat_category(MutableSlice category) { + static td_api::object_ptr get_top_chat_category(MutableSlice category) { category = trim(category); to_lower_inplace(category); if (!category.empty() && category.back() == 's') { @@ -1036,12 +1071,14 @@ class CliClient final : public Actor { return td_api::make_object(); } else if (category == "call") { return td_api::make_object(); + } else if (category == "forward") { + return td_api::make_object(); } else { return td_api::make_object(); } } - static tl_object_ptr get_chat_action(MutableSlice action) { + static td_api::object_ptr get_chat_action(MutableSlice action) { action = trim(action); to_lower_inplace(action); if (action == "c" || action == "cancel") { @@ -1083,7 +1120,7 @@ class CliClient final : public Actor { return td_api::make_object(); } - static tl_object_ptr get_network_type(MutableSlice type) { + static td_api::object_ptr get_network_type(MutableSlice type) { type = trim(type); to_lower_inplace(type); if (type == "none") { @@ -1104,7 +1141,7 @@ class CliClient final : public Actor { return nullptr; } - static tl_object_ptr as_passport_element_type(Slice passport_element_type) { + static td_api::object_ptr as_passport_element_type(Slice passport_element_type) { if (passport_element_type == "address" || passport_element_type == "a") { return td_api::make_object(); } @@ -1138,12 +1175,12 @@ class CliClient final : public Actor { return td_api::make_object(); } - static auto as_passport_element_types(Slice types, char delimiter = ',') { - return transform(full_split(types, delimiter), [](Slice str) { return as_passport_element_type(str); }); + static auto as_passport_element_types(Slice types) { + return transform(full_split(types, get_delimiter(types)), [](Slice str) { return as_passport_element_type(str); }); } - static tl_object_ptr as_input_passport_element(string passport_element_type, string arg, - bool with_selfie) { + static td_api::object_ptr as_input_passport_element(string passport_element_type, + string arg, bool with_selfie) { vector> input_files; td_api::object_ptr selfie; if (!arg.empty()) { @@ -1219,7 +1256,46 @@ class CliClient final : public Actor { true, true, -1, 5, 3, "abacaba"); } - static td_api::object_ptr execute(tl_object_ptr f) { + static td_api::object_ptr as_message_scheduling_state(Slice date) { + date = trim(date); + if (date.empty()) { + return nullptr; + } + auto send_date = to_integer(date); + if (send_date == -1) { + return td_api::make_object(); + } + return td_api::make_object(send_date); + } + + static td_api::object_ptr get_background_fill(int32 color) { + return td_api::make_object(color); + } + + static td_api::object_ptr get_background_fill(int32 top_color, int32 bottom_color) { + return td_api::make_object(top_color, bottom_color, Random::fast(0, 7) * 45); + } + + static td_api::object_ptr get_solid_pattern_background(int32 color, int32 intensity, + bool is_moving) { + return get_gradient_pattern_background(color, color, intensity, is_moving); + } + + static td_api::object_ptr get_gradient_pattern_background(int32 top_color, int32 bottom_color, + int32 intensity, bool is_moving) { + return td_api::make_object(get_background_fill(top_color, bottom_color), intensity, + is_moving); + } + + static td_api::object_ptr get_solid_background(int32 color) { + return td_api::make_object(get_background_fill(color)); + } + + static td_api::object_ptr get_gradient_background(int32 top_color, int32 bottom_color) { + return td_api::make_object(get_background_fill(top_color, bottom_color)); + } + + static td_api::object_ptr execute(td_api::object_ptr f) { if (GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) { LOG(ERROR) << "Execute request: " << to_string(f); } @@ -1230,7 +1306,7 @@ class CliClient final : public Actor { return res; } - uint64 send_request(tl_object_ptr f) { + uint64 send_request(td_api::object_ptr f) { static uint64 query_num = 1; if (!td_client_.empty()) { auto id = query_num++; @@ -1246,17 +1322,24 @@ class CliClient final : public Actor { bool disable_notification = false, bool from_background = false, int64 reply_to_message_id = 0) { auto chat = as_chat_id(chat_id); auto id = send_request(td_api::make_object( - chat, reply_to_message_id, disable_notification, from_background, nullptr, std::move(input_message_content))); + chat, reply_to_message_id, + td_api::make_object(disable_notification, from_background, + as_message_scheduling_state(schedule_date_)), + nullptr, std::move(input_message_content))); query_id_to_send_message_info_[id].start_time = Time::now(); } + td_api::object_ptr default_send_message_options() const { + return td_api::make_object(false, false, as_message_scheduling_state(schedule_date_)); + } + void send_get_background_url(td_api::object_ptr &&background_type) { send_request(td_api::make_object("asd", std::move(background_type))); } void on_cmd(string cmd) { // TODO: need to remove https://en.wikipedia.org/wiki/ANSI_escape_code from cmd - cmd.erase(std::remove_if(cmd.begin(), cmd.end(), [](unsigned char c) { return c < 32; }), cmd.end()); + td::remove_if(cmd, [](unsigned char c) { return c < 32; }); LOG(INFO) << "CMD:[" << cmd << "]"; string op; @@ -1287,8 +1370,12 @@ class CliClient final : public Actor { send_request(td_api::make_object(first_name, last_name)); } else if (op == "cap") { send_request(td_api::make_object(args)); - } else if (op == "cab" || op == "cabt") { + } else if (op == "cabt") { send_request(td_api::make_object(args)); + } else if (op == "qr") { + send_request(td_api::make_object(as_user_ids(args))); + } else if (op == "cqr") { + send_request(td_api::make_object(args)); } else if (op == "gcs") { send_request(td_api::make_object()); } else if (op == "rapr") { @@ -1297,7 +1384,7 @@ class CliClient final : public Actor { send_request(td_api::make_object(args)); } else if (op == "lo" || op == "LogOut" || op == "logout") { send_request(td_api::make_object()); - } else if (op == "ra" || op == "destroy") { + } else if (op == "destroy") { send_request(td_api::make_object()); } else if (op == "reset") { td_client_.reset(); @@ -1549,13 +1636,20 @@ class CliClient final : public Actor { } else if (op == "spr") { string setting; string allow; - std::tie(setting, allow) = split(args); + string ids; + std::tie(setting, args) = split(args); + std::tie(allow, ids) = split(args); - std::vector> rules; + std::vector> rules; if (allow == "c" || allow == "contacts") { rules.push_back(td_api::make_object()); + } else if (allow == "users") { + rules.push_back(td_api::make_object(as_user_ids(ids))); + } else if (allow == "chats") { + rules.push_back(td_api::make_object(as_chat_ids(ids))); } else if (as_bool(allow)) { rules.push_back(td_api::make_object()); + rules.push_back(td_api::make_object()); } else { rules.push_back(td_api::make_object()); } @@ -1574,9 +1668,22 @@ class CliClient final : public Actor { auto limit = to_integer(args); send_request(td_api::make_object("", limit)); } + } else if (op == "AddContact") { + string user_id; + string first_name; + string last_name; + std::tie(user_id, args) = split(args); + std::tie(first_name, last_name) = split(args); + + send_request(td_api::make_object( + td_api::make_object(string(), first_name, last_name, string(), as_user_id(user_id)), false)); + } else if (op == "spn") { + string user_id = args; + + send_request(td_api::make_object(as_user_id(user_id))); } else if (op == "ImportContacts" || op == "cic") { vector contacts_str = full_split(args, ';'); - vector> contacts; + vector> contacts; for (auto c : contacts_str) { string phone_number; string first_name; @@ -1601,7 +1708,7 @@ class CliClient final : public Actor { op_not_found_count++; } - if (op == "gc" || op == "GetChats") { + if (op == "gc" || op == "GetChats" || op == "gca") { string offset_order_string; string offset_chat_id; string limit; @@ -1618,12 +1725,12 @@ class CliClient final : public Actor { } else { offset_order = to_integer(offset_order_string); } - send_request( - td_api::make_object(offset_order, as_chat_id(offset_chat_id), to_integer(limit))); + send_request(td_api::make_object(as_chat_list(op), offset_order, as_chat_id(offset_chat_id), + to_integer(limit))); } else if (op == "gctest") { - send_request(td_api::make_object(std::numeric_limits::max(), 0, 1)); - send_request(td_api::make_object(std::numeric_limits::max(), 0, 10)); - send_request(td_api::make_object(std::numeric_limits::max(), 0, 5)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 1)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 10)); + send_request(td_api::make_object(nullptr, std::numeric_limits::max(), 0, 5)); } else if (op == "gcc" || op == "GetCommonChats") { string user_id; string offset_chat_id; @@ -1663,6 +1770,9 @@ class CliClient final : public Actor { to_integer(offset), to_integer(limit), op == "ghl")); } + } else if (op == "gcsm") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "ghf") { get_history_chat_id_ = as_chat_id(args); @@ -1673,7 +1783,7 @@ class CliClient final : public Actor { send_request(td_api::make_object( search_chat_id_, "", 0, 0, 0, 100, td_api::make_object())); - } else if (op == "Search") { + } else if (op == "Search" || op == "SearchA" || op == "SearchM") { string from_date; string limit; string query; @@ -1683,8 +1793,15 @@ class CliClient final : public Actor { if (from_date.empty()) { from_date = "0"; } - send_request(td_api::make_object(query, to_integer(from_date), 2147482647, 0, - to_integer(limit))); + td_api::object_ptr chat_list; + if (op == "SearchA") { + chat_list = td_api::make_object(); + } + if (op == "SearchM") { + chat_list = td_api::make_object(); + } + send_request(td_api::make_object( + std::move(chat_list), query, to_integer(from_date), 2147482647, 0, to_integer(limit))); } else if (op == "SCM") { string chat_id; string limit; @@ -1711,10 +1828,10 @@ class CliClient final : public Actor { to_integer(limit), nullptr)); } else if (op == "SM") { string chat_id; + string filter; + string limit; string offset_message_id; string offset; - string limit; - string filter; std::tie(chat_id, args) = split(args); std::tie(filter, args) = split(args); @@ -1905,7 +2022,7 @@ class CliClient final : public Actor { std::tie(name, args) = split(args); std::tie(native_name, key) = split(args); - vector> strings; + vector> strings; strings.push_back(td_api::make_object( key, td_api::make_object("Ordinary value"))); strings.push_back(td_api::make_object( @@ -1934,7 +2051,8 @@ class CliClient final : public Actor { std::tie(language_code, args) = split(args); std::tie(key, value) = split(args); - tl_object_ptr str = td_api::make_object(key, nullptr); + td_api::object_ptr str = + td_api::make_object(key, nullptr); if (op == "sclsv") { str->value_ = td_api::make_object(value); } else if (op == "sclsp") { @@ -1998,16 +2116,23 @@ class CliClient final : public Actor { send_get_background_url(td_api::make_object(false, true)); send_get_background_url(td_api::make_object(true, false)); send_get_background_url(td_api::make_object(true, true)); - send_get_background_url(td_api::make_object(false, -1, 0)); - send_get_background_url(td_api::make_object(true, 0x1000000, 0)); - send_get_background_url(td_api::make_object(false, 0, -1)); - send_get_background_url(td_api::make_object(false, 0, 101)); - send_get_background_url(td_api::make_object(false, 0, 0)); - send_get_background_url(td_api::make_object(true, 0xFFFFFF, 100)); - send_get_background_url(td_api::make_object(true, 0xABCDEF, 49)); - send_get_background_url(td_api::make_object(-1)); - send_get_background_url(td_api::make_object(0xABCDEF)); - send_get_background_url(td_api::make_object(0x1000000)); + send_get_background_url(get_solid_pattern_background(-1, 0, false)); + send_get_background_url(get_solid_pattern_background(0x1000000, 0, true)); + send_get_background_url(get_solid_pattern_background(0, -1, false)); + send_get_background_url(get_solid_pattern_background(0, 101, false)); + send_get_background_url(get_solid_pattern_background(0, 0, false)); + send_get_background_url(get_solid_pattern_background(0xFFFFFF, 100, true)); + send_get_background_url(get_solid_pattern_background(0xABCDEF, 49, true)); + send_get_background_url(get_gradient_pattern_background(0, 0, 0, false)); + send_get_background_url(get_gradient_pattern_background(0xFFFFFF, 0, 100, true)); + send_get_background_url(get_gradient_pattern_background(0xABCDEF, 0xFEDCBA, 49, true)); + send_get_background_url(get_gradient_pattern_background(0, 0x1000000, 49, true)); + send_get_background_url(get_solid_background(-1)); + send_get_background_url(get_solid_background(0xABCDEF)); + send_get_background_url(get_solid_background(0x1000000)); + send_get_background_url(get_gradient_background(0xABCDEF, 0xFEDCBA)); + send_get_background_url(get_gradient_background(0, 0)); + send_get_background_url(get_gradient_background(-1, -1)); } else if (op == "sbg") { send_request(td_api::make_object(args)); } else if (op == "sbgd") { @@ -2019,10 +2144,20 @@ class CliClient final : public Actor { } else if (op == "sbgp" || op == "sbgpd") { send_request(td_api::make_object( td_api::make_object(as_input_file(args)), - td_api::make_object(true, 0xabcdef, 49), op == "sbgpd")); - } else if (op == "sbgs" || op == "sbgsd") { + get_solid_pattern_background(0xABCDEF, 49, true), op == "sbgpd")); + } else if (op == "sbggp" || op == "sbggpd") { send_request(td_api::make_object( - nullptr, td_api::make_object(to_integer(args)), op == "sbgsd")); + td_api::make_object(as_input_file(args)), + get_gradient_pattern_background(0xABCDEF, 0xFE, 51, false), op == "sbggpd")); + } else if (op == "sbgs" || op == "sbgsd") { + send_request(td_api::make_object(nullptr, get_solid_background(to_integer(args)), + op == "sbgsd")); + } else if (op == "sbgg" || op == "sbggd") { + string top_color; + string bottom_color; + std::tie(top_color, bottom_color) = split(args); + auto background_type = get_gradient_background(to_integer(top_color), to_integer(bottom_color)); + send_request(td_api::make_object(nullptr, std::move(background_type), op == "sbggd")); } else if (op == "sbgwid" || op == "sbgwidd") { send_request(td_api::make_object( td_api::make_object(to_integer(args)), @@ -2030,7 +2165,7 @@ class CliClient final : public Actor { } else if (op == "sbgpid" || op == "sbgpidd") { send_request(td_api::make_object( td_api::make_object(to_integer(args)), - td_api::make_object(true, 0xabcdef, 49), op == "sbgpidd")); + get_solid_pattern_background(0xabcdef, 49, true), op == "sbgpidd")); } else if (op == "rbg") { send_request(td_api::make_object(to_integer(args))); } else if (op == "rbgs") { @@ -2107,17 +2242,17 @@ class CliClient final : public Actor { std::tie(chat_ids, args) = split(args); 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))); + 10000000, -1, -1, 0, std::vector>(), as_chat_ids(chat_ids), + as_chat_ids(exclude_chat_ids), 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; + 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)); } else if (op == "clean_storage") { - std::vector> types; + std::vector> types; types.push_back(td_api::make_object()); types.push_back(td_api::make_object()); types.push_back(td_api::make_object()); @@ -2131,7 +2266,7 @@ class CliClient final : public Actor { types.push_back(td_api::make_object()); 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, ','), + send_request(td_api::make_object(0, -1, -1, 0, std::move(types), as_chat_ids(args), as_chat_ids(""), 20)); } else if (op == "network") { send_request(td_api::make_object()); @@ -2467,7 +2602,7 @@ class CliClient final : public Actor { std::tie(chat_id, args) = split(args); std::tie(message_ids, revoke) = split(args); - send_request(td_api::make_object(as_chat_id(chat_id), as_message_ids(message_ids, ','), + send_request(td_api::make_object(as_chat_id(chat_id), as_message_ids(message_ids), as_bool(revoke))); } else if (op == "fm" || op == "fmg" || op == "cm" || op == "cmg") { string chat_id; @@ -2477,9 +2612,9 @@ class CliClient final : public Actor { std::tie(from_chat_id, message_ids) = split(args); auto chat = as_chat_id(chat_id); - send_request(td_api::make_object(chat, as_chat_id(from_chat_id), - as_message_ids(message_ids), false, false, op[2] == 'g', - op[0] == 'c', Random::fast(0, 1) == 1)); + send_request(td_api::make_object( + chat, as_chat_id(from_chat_id), as_message_ids(message_ids), default_send_message_options(), op[2] == 'g', + op[0] == 'c', Random::fast(0, 1) == 1)); } else if (op == "resend") { string chat_id; string message_ids; @@ -2540,13 +2675,13 @@ class CliClient final : public Actor { execute(td_api::make_object(args)); } else if (op == "pte") { send_request( - td_api::make_object(args, td_api::make_object())); + td_api::make_object(args, td_api::make_object(2))); } else if (op == "pteh") { send_request( td_api::make_object(args, td_api::make_object())); } else if (op == "ptes") { execute( - td_api::make_object(args, td_api::make_object())); + td_api::make_object(args, td_api::make_object(2))); } else if (op == "ptehs") { execute(td_api::make_object(args, td_api::make_object())); } else if (op == "gfmt") { @@ -2617,9 +2752,9 @@ class CliClient final : public Actor { string message; std::tie(chat_id, args) = split(args); std::tie(reply_to_message_id, message) = split(args); - tl_object_ptr draft_message; + td_api::object_ptr draft_message; if (!reply_to_message_id.empty() || !message.empty()) { - vector> entities; + vector> entities; entities.push_back( td_api::make_object(0, 1, td_api::make_object())); @@ -2647,13 +2782,13 @@ class CliClient final : public Actor { std::tie(chat_id, default_disable_notification) = split(args); send_request(td_api::make_object( as_chat_id(chat_id), as_bool(default_disable_notification))); - } else if (op == "spchats") { + } else if (op == "spchats" || op == "spchatsa") { vector chat_ids_str = full_split(args, ' '); vector chat_ids; for (auto &chat_id_str : chat_ids_str) { chat_ids.push_back(as_chat_id(chat_id_str)); } - send_request(td_api::make_object(std::move(chat_ids))); + send_request(td_api::make_object(as_chat_list(op), std::move(chat_ids))); } else if (op == "sca") { string chat_id; string action; @@ -2688,6 +2823,8 @@ class CliClient final : public Actor { send_request(td_api::make_object( as_chat_id(chat_id), query, to_integer(from_search_id), to_integer(limit), get_search_messages_filter(filter))); + } else if (op == "ssd") { + schedule_date_ = args; } else if (op == "sm" || op == "sms" || op == "smr" || op == "smf") { string chat_id; string reply_to_message_id; @@ -2730,9 +2867,9 @@ class CliClient final : public Actor { photos = full_split(args); send_request(td_api::make_object( - as_chat_id(chat_id), as_message_id(reply_to_message_id), false, false, + as_chat_id(chat_id), as_message_id(reply_to_message_id), default_send_message_options(), transform(photos, [](const string &photo_path) { - tl_object_ptr content = td_api::make_object( + td_api::object_ptr content = td_api::make_object( as_input_file(photo_path), nullptr, Auto(), 0, 0, as_caption(""), 0); return content; }))); @@ -2814,6 +2951,14 @@ class CliClient final : public Actor { std::tie(latitude, longitude) = split(args); send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id), nullptr, as_location(latitude, longitude))); + } else if (op == "emss") { + string chat_id; + string message_id; + string date; + std::tie(chat_id, args) = split(args); + std::tie(message_id, date) = split(args); + send_request(td_api::make_object( + as_chat_id(chat_id), as_message_id(message_id), as_message_scheduling_state(date))); } else if (op == "gallm") { send_request(td_api::make_object()); } else if (op == "sbsm") { @@ -2851,7 +2996,7 @@ class CliClient final : public Actor { auto chat = as_chat_id(chat_id); send_request(td_api::make_object( - chat, 0, false, false, to_integer(query_id), result_id, op == "siqrh")); + chat, 0, default_send_message_options(), to_integer(query_id), result_id, op == "siqrh")); } else if (op == "gcqr") { string chat_id; string message_id; @@ -3065,7 +3210,7 @@ class CliClient final : public Actor { if (trim(photo_path).empty()) { photo_path = sticker_file_ids_str; } else { - sticker_file_ids = to_integers(sticker_file_ids_str, ','); + sticker_file_ids = to_integers(sticker_file_ids_str); } send_message(chat_id, td_api::make_object( @@ -3160,7 +3305,7 @@ class CliClient final : public Actor { if (trim(video_path).empty()) { video_path = sticker_file_ids_str; } else { - sticker_file_ids = to_integers(sticker_file_ids_str, ','); + sticker_file_ids = to_integers(sticker_file_ids_str); } send_message(chat_id, td_api::make_object(as_input_file(video_path), nullptr, @@ -3224,17 +3369,33 @@ class CliClient final : public Actor { string title; std::tie(user_ids_string, title) = split(args); - send_request(td_api::make_object(as_user_ids(user_ids_string, ','), title)); + send_request(td_api::make_object(as_user_ids(user_ids_string), title)); } else if (op == "cnch") { - send_request(td_api::make_object(args, true, "Description")); + send_request(td_api::make_object(args, true, "Description", nullptr)); } else if (op == "cnsg") { - send_request(td_api::make_object(args, false, "Description")); + send_request(td_api::make_object(args, false, "Description", nullptr)); + } else if (op == "cngc") { + send_request(td_api::make_object( + args, false, "Description", + td_api::make_object(td_api::make_object(40.0, 60.0), "address"))); } else if (op == "UpgradeBasicGroupChatToSupergroupChat") { send_request(td_api::make_object(as_chat_id(args))); } else if (op == "DeleteSupergroup") { send_request(td_api::make_object(as_supergroup_id(args))); } else if (op == "gcpc") { send_request(td_api::make_object()); + } else if (op == "gcpcl") { + send_request(td_api::make_object( + td_api::make_object())); + } else if (op == "ccpcl") { + send_request(td_api::make_object()); + } else if (op == "ccpcll") { + send_request(td_api::make_object( + td_api::make_object())); + } else if (op == "gsdc") { + send_request(td_api::make_object()); + } else if (op == "gisc") { + send_request(td_api::make_object()); } else if (op == "cpc") { string user_id; string force; @@ -3254,6 +3415,9 @@ class CliClient final : public Actor { std::tie(supergroup_id, force) = split(args); send_request(td_api::make_object(as_supergroup_id(supergroup_id), as_bool(force))); + } else if (op == "sccl" || op == "sccla") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id), as_chat_list(op))); } else if (op == "sct") { string chat_id; string title; @@ -3306,7 +3470,7 @@ class CliClient final : public Actor { string user_ids; std::tie(chat_id, user_ids) = split(args); - send_request(td_api::make_object(as_chat_id(chat_id), as_user_ids(user_ids, ','))); + send_request(td_api::make_object(as_chat_id(chat_id), as_user_ids(user_ids))); } else if (op == "spolla") { string chat_id; string message_id; @@ -3330,7 +3494,7 @@ class CliClient final : public Actor { string chat_id; string user_id; string status_str; - tl_object_ptr status; + td_api::object_ptr status; std::tie(chat_id, args) = split(args); std::tie(user_id, status_str) = split(args); @@ -3341,17 +3505,20 @@ class CliClient final : public Actor { } else if (status_str == "banned") { status = td_api::make_object(std::numeric_limits::max()); } else if (status_str == "creator") { - status = td_api::make_object(true); + status = td_api::make_object("", true); } else if (status_str == "uncreator") { - status = td_api::make_object(false); + status = td_api::make_object("", false); } else if (status_str == "admin") { - status = td_api::make_object(true, true, true, true, true, true, true, - true, true); + status = td_api::make_object("", true, true, true, true, true, true, + true, true, true); + } else if (status_str == "adminq") { + status = td_api::make_object("title", true, true, true, true, true, true, + true, true, true); } else if (status_str == "minadmin") { - status = td_api::make_object(true, true, false, false, false, false, + status = td_api::make_object("", true, true, false, false, false, false, false, false, false); } else if (status_str == "unadmin") { - status = td_api::make_object(true, false, false, false, false, false, + status = td_api::make_object("", true, false, false, false, false, false, false, false, false); } else if (status_str == "rest") { status = td_api::make_object( @@ -3375,6 +3542,17 @@ class CliClient final : public Actor { } else { LOG(ERROR) << "Unknown status \"" << status_str << "\""; } + } else if (op == "cto") { + send_request(td_api::make_object()); + } else if (op == "transferChatOwnership") { + string chat_id; + string user_id; + string password; + + std::tie(chat_id, args) = split(args); + std::tie(user_id, password) = split(args); + send_request( + td_api::make_object(as_chat_id(chat_id), as_user_id(user_id), password)); } else if (op == "log") { string chat_id; string limit; @@ -3445,6 +3623,28 @@ class CliClient final : public Actor { std::tie(chat_id, description) = split(args); send_request(td_api::make_object(as_chat_id(chat_id), description)); + } else if (op == "scdg") { + string chat_id; + string group_chat_id; + + std::tie(chat_id, group_chat_id) = split(args); + send_request(td_api::make_object(as_chat_id(chat_id), as_chat_id(group_chat_id))); + } else if (op == "scl") { + string chat_id; + string latitude; + string longitude; + + std::tie(chat_id, args) = split(args); + std::tie(latitude, longitude) = split(args); + send_request(td_api::make_object( + as_chat_id(chat_id), td_api::make_object(as_location(latitude, longitude), "address"))); + } else if (op == "scsmd") { + string chat_id; + string slow_mode_delay; + + std::tie(chat_id, slow_mode_delay) = split(args); + send_request( + td_api::make_object(as_chat_id(chat_id), to_integer(slow_mode_delay))); } else if (op == "pcm" || op == "pcms") { string chat_id; string message_id; @@ -3470,6 +3670,12 @@ class CliClient final : public Actor { string query; std::tie(limit, query) = split(args); send_request(td_api::make_object(query, to_integer(limit))); + } else if (op == "scn") { + 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; @@ -3565,8 +3771,7 @@ class CliClient final : public Actor { string group_id; string notification_ids; std::tie(group_id, notification_ids) = split(args); - char delimiter = notification_ids.find(',') != string::npos ? ',' : ' '; - for (auto notification_id : to_integers(notification_ids, delimiter)) { + for (auto notification_id : to_integers(notification_ids)) { send_request(td_api::make_object(to_integer(group_id), notification_id)); } } else if (op == "rng") { @@ -3575,14 +3780,9 @@ class CliClient final : public Actor { std::tie(group_id, max_notification_id) = split(args); send_request(td_api::make_object(to_integer(group_id), to_integer(max_notification_id))); - } else if (op == "gcrss") { - send_request(td_api::make_object(as_chat_id(args))); - } else if (op == "ccrss") { - string chat_id; - string is_spam_chat; - std::tie(chat_id, is_spam_chat) = split(args); - - send_request(td_api::make_object(as_chat_id(chat_id), as_bool(is_spam_chat))); + } else if (op == "rcab") { + string chat_id = args; + send_request(td_api::make_object(as_chat_id(chat_id))); } else if (op == "rc") { string chat_id; string reason_str; @@ -3590,7 +3790,7 @@ class CliClient final : public Actor { std::tie(chat_id, args) = split(args); std::tie(reason_str, message_ids) = split(args); - tl_object_ptr reason; + td_api::object_ptr reason; if (reason_str == "spam") { reason = td_api::make_object(); } else if (reason_str == "violence") { @@ -3601,6 +3801,8 @@ class CliClient final : public Actor { reason = td_api::make_object(); } else if (reason_str == "copyright") { reason = td_api::make_object(); + } else if (reason_str == "geo" || reason_str == "location") { + reason = td_api::make_object(); } else { reason = td_api::make_object(reason_str); } @@ -3615,6 +3817,20 @@ class CliClient final : public Actor { std::tie(parameters, is_dark) = split(args); send_request(td_api::make_object(as_chat_id(args), parameters, as_bool(is_dark))); + } else if (op == "glui" || op == "glu" || op == "glua") { + string chat_id; + string message_id; + string button_id; + std::tie(chat_id, args) = split(args); + std::tie(message_id, button_id) = split(args); + + if (op == "glui") { + send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id), + as_button_id(button_id))); + } else { + send_request(td_api::make_object(as_chat_id(chat_id), as_message_id(message_id), + as_button_id(button_id), op == "glua")); + } } else if (op == "rsgs" || op == "rchs") { string supergroup_id; string user_id; @@ -3854,6 +4070,7 @@ class CliClient final : public Actor { std::unordered_map being_downloaded_files_; int32 my_id_ = 0; + string schedule_date_; ConcurrentScheduler *scheduler_{nullptr}; diff --git a/td/telegram/files/FileBitmask.cpp b/td/telegram/files/FileBitmask.cpp index 472fa0f2..fc9f3ff7 100644 --- a/td/telegram/files/FileBitmask.cpp +++ b/td/telegram/files/FileBitmask.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileBitmask.h b/td/telegram/files/FileBitmask.h index c170a000..be7a848f 100644 --- a/td/telegram/files/FileBitmask.h +++ b/td/telegram/files/FileBitmask.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileData.h b/td/telegram/files/FileData.h index d6ad4d5c..66b271ce 100644 --- a/td/telegram/files/FileData.h +++ b/td/telegram/files/FileData.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileData.hpp b/td/telegram/files/FileData.hpp index 51fe8dd9..ede1c480 100644 --- a/td/telegram/files/FileData.hpp +++ b/td/telegram/files/FileData.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileDb.cpp b/td/telegram/files/FileDb.cpp index 9a2794f7..e862f397 100644 --- a/td/telegram/files/FileDb.cpp +++ b/td/telegram/files/FileDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileDb.h b/td/telegram/files/FileDb.h index 89303466..40afdf51 100644 --- a/td/telegram/files/FileDb.h +++ b/td/telegram/files/FileDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileDbId.h b/td/telegram/files/FileDbId.h index 26b37fb7..bd4fb809 100644 --- a/td/telegram/files/FileDbId.h +++ b/td/telegram/files/FileDbId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileDownloader.cpp b/td/telegram/files/FileDownloader.cpp index 574d1d25..317b21be 100644 --- a/td/telegram/files/FileDownloader.cpp +++ b/td/telegram/files/FileDownloader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -117,6 +117,7 @@ Result FileDownloader::init() { res.limit = limit_; return res; } + Status FileDownloader::on_ok(int64 size) { auto dir = get_files_dir(remote_.file_type_); @@ -135,12 +136,12 @@ Status FileDownloader::on_ok(int64 size) { if (only_check_) { path = path_; } else { - TRY_RESULT(perm_path, create_from_temp(path_, dir, name_)); - path = std::move(perm_path); + TRY_RESULT_ASSIGN(path, create_from_temp(path_, dir, name_)); } callback_->on_ok(FullLocalFileLocation(remote_.file_type_, std::move(path), 0), size, !only_check_); return Status::OK(); } + void FileDownloader::on_error(Status status) { fd_.close(); callback_->on_error(std::move(status)); @@ -218,6 +219,7 @@ Result FileDownloader::should_restart_part(Part part, NetQueryPtr &net_que return false; } + Result> FileDownloader::start_part(Part part, int32 part_count) { if (encryption_key_.is_secret()) { part.size = (part.size + 15) & ~15; // fix for last part @@ -235,13 +237,19 @@ Result> FileDownloader::start_part(Part part, int32 NetQueryPtr net_query; if (!use_cdn_) { + int32 flags = 0; +#if !TD_EMSCRIPTEN + // CDN is supported, unless we use domains instead of IPs from a browser + flags |= telegram_api::upload_getFile::CDN_SUPPORTED_MASK; +#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(remote_.as_input_file_location(), + : 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); } else { @@ -262,8 +270,7 @@ Result> FileDownloader::start_part(Part part, int32 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, - NetQuery::AuthFlag::On); + remote_.get_dc_id(), is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download); cdn_part_reupload_token_.erase(it); } } @@ -356,12 +363,14 @@ Result FileDownloader::process_part(Part part, NetQueryPtr net_query) { TRY_STATUS(acquire_fd()); LOG(INFO) << "Got " << slice.size() << " bytes at offset " << part.offset << " for \"" << path_ << '"'; TRY_RESULT(written, fd_.pwrite(slice, part.offset)); + LOG(INFO) << "Written " << written << " bytes"; // may write less than part.size, when size of downloadable file is unknown if (written != slice.size()) { return Status::Error("Failed to save file part to the file"); } return written; } + void FileDownloader::on_progress(Progress progress) { if (progress.is_ready) { // do not send partial location. will lead to wrong local_size @@ -453,9 +462,9 @@ 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, NetQuery::AuthFlag::On); + auto net_query = + G()->net_query_creator().create(create_storer(query), remote_.get_dc_id(), + is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download); info.queries.push_back(std::move(net_query)); break; } @@ -492,11 +501,9 @@ void FileDownloader::try_release_fd() { Status FileDownloader::acquire_fd() { if (fd_.empty()) { if (path_.empty()) { - TRY_RESULT(file_path, open_temp_file(remote_.file_type_)); - std::tie(fd_, path_) = std::move(file_path); + TRY_RESULT_ASSIGN(std::tie(fd_, path_), open_temp_file(remote_.file_type_)); } else { - TRY_RESULT(fd, FileFd::open(path_, (only_check_ ? 0 : FileFd::Write) | FileFd::Read)); - fd_ = std::move(fd); + TRY_RESULT_ASSIGN(fd_, FileFd::open(path_, (only_check_ ? 0 : FileFd::Write) | FileFd::Read)); } } return Status::OK(); diff --git a/td/telegram/files/FileDownloader.h b/td/telegram/files/FileDownloader.h index cf90c3c1..b1b36579 100644 --- a/td/telegram/files/FileDownloader.h +++ b/td/telegram/files/FileDownloader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileEncryptionKey.cpp b/td/telegram/files/FileEncryptionKey.cpp index ae319b65..034b9bed 100644 --- a/td/telegram/files/FileEncryptionKey.cpp +++ b/td/telegram/files/FileEncryptionKey.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileEncryptionKey.h b/td/telegram/files/FileEncryptionKey.h index 56433fd8..922ac9eb 100644 --- a/td/telegram/files/FileEncryptionKey.h +++ b/td/telegram/files/FileEncryptionKey.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileFromBytes.cpp b/td/telegram/files/FileFromBytes.cpp index 2dda34d3..5591f2af 100644 --- a/td/telegram/files/FileFromBytes.cpp +++ b/td/telegram/files/FileFromBytes.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileFromBytes.h b/td/telegram/files/FileFromBytes.h index 1c362caa..0b828c0d 100644 --- a/td/telegram/files/FileFromBytes.h +++ b/td/telegram/files/FileFromBytes.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileGcParameters.cpp b/td/telegram/files/FileGcParameters.cpp index 67ebc789..f2d072c4 100644 --- a/td/telegram/files/FileGcParameters.cpp +++ b/td/telegram/files/FileGcParameters.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileGcParameters.h b/td/telegram/files/FileGcParameters.h index 97081511..01ff802e 100644 --- a/td/telegram/files/FileGcParameters.h +++ b/td/telegram/files/FileGcParameters.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileGcWorker.cpp b/td/telegram/files/FileGcWorker.cpp index c831dd17..6bca6b75 100644 --- a/td/telegram/files/FileGcWorker.cpp +++ b/td/telegram/files/FileGcWorker.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -94,47 +94,40 @@ void FileGcWorker::run_gc(const FileGcParameters ¶meters, std::vector now - max_time_from_last_access double now = Clocks::system(); - files.erase( - std::remove_if( - files.begin(), files.end(), - [&](const FullFileInfo &info) { - if (token_) { - return false; - } - if (immune_types[narrow_cast(info.file_type)]) { - type_immunity_ignored_cnt++; - new_stats.add(FullFileInfo(info)); - return true; - } - if (std::find(parameters.exclude_owner_dialog_ids.begin(), parameters.exclude_owner_dialog_ids.end(), - info.owner_dialog_id) != parameters.exclude_owner_dialog_ids.end()) { - exclude_owner_dialog_id_ignored_cnt++; - new_stats.add(FullFileInfo(info)); - return true; - } - if (!parameters.owner_dialog_ids.empty() && - std::find(parameters.owner_dialog_ids.begin(), parameters.owner_dialog_ids.end(), - info.owner_dialog_id) == parameters.owner_dialog_ids.end()) { - owner_dialog_id_ignored_cnt++; - new_stats.add(FullFileInfo(info)); - return true; - } - if (static_cast(info.mtime_nsec / 1000000000) > now - parameters.immunity_delay) { - // new files are immune to gc - time_immunity_ignored_cnt++; - new_stats.add(FullFileInfo(info)); - return true; - } + 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)); + 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)); + 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)); + return true; + } + if (static_cast(info.mtime_nsec / 1000000000) > now - parameters.immunity_delay) { + // new files are immune to gc + time_immunity_ignored_cnt++; + new_stats.add(FullFileInfo(info)); + return true; + } - if (static_cast(info.atime_nsec / 1000000000) < now - parameters.max_time_from_last_access) { - do_remove_file(info); - total_removed_size += info.size; - remove_by_atime_cnt++; - return true; - } - return false; - }), - files.end()); + if (static_cast(info.atime_nsec / 1000000000) < now - parameters.max_time_from_last_access) { + do_remove_file(info); + total_removed_size += info.size; + remove_by_atime_cnt++; + return true; + } + return false; + }); if (token_) { return promise.set_error(Status::Error(500, "Request aborted")); } diff --git a/td/telegram/files/FileGcWorker.h b/td/telegram/files/FileGcWorker.h index 1e99459f..d5dbb1d0 100644 --- a/td/telegram/files/FileGcWorker.h +++ b/td/telegram/files/FileGcWorker.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileGenerateManager.cpp b/td/telegram/files/FileGenerateManager.cpp index e94933aa..19e27579 100644 --- a/td/telegram/files/FileGenerateManager.cpp +++ b/td/telegram/files/FileGenerateManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileGenerateManager.h b/td/telegram/files/FileGenerateManager.h index 62ace9e8..3c6a75b3 100644 --- a/td/telegram/files/FileGenerateManager.h +++ b/td/telegram/files/FileGenerateManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileHashUploader.cpp b/td/telegram/files/FileHashUploader.cpp index 16c2280b..62063f71 100644 --- a/td/telegram/files/FileHashUploader.cpp +++ b/td/telegram/files/FileHashUploader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileHashUploader.h b/td/telegram/files/FileHashUploader.h index 2819c832..0fc9d556 100644 --- a/td/telegram/files/FileHashUploader.h +++ b/td/telegram/files/FileHashUploader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileId.h b/td/telegram/files/FileId.h index 32aa075a..43bdc56e 100644 --- a/td/telegram/files/FileId.h +++ b/td/telegram/files/FileId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileId.hpp b/td/telegram/files/FileId.hpp index 1ff2abbd..c3d08352 100644 --- a/td/telegram/files/FileId.hpp +++ b/td/telegram/files/FileId.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoadManager.cpp b/td/telegram/files/FileLoadManager.cpp index ce59fe39..59ed04db 100644 --- a/td/telegram/files/FileLoadManager.cpp +++ b/td/telegram/files/FileLoadManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoadManager.h b/td/telegram/files/FileLoadManager.h index e87908ee..0aa2b58f 100644 --- a/td/telegram/files/FileLoadManager.h +++ b/td/telegram/files/FileLoadManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoader.cpp b/td/telegram/files/FileLoader.cpp index 6df62d64..cc2bd254 100644 --- a/td/telegram/files/FileLoader.cpp +++ b/td/telegram/files/FileLoader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoader.h b/td/telegram/files/FileLoader.h index 855012d9..30fe5016 100644 --- a/td/telegram/files/FileLoader.h +++ b/td/telegram/files/FileLoader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoaderActor.h b/td/telegram/files/FileLoaderActor.h index 830504f6..a38f5f19 100644 --- a/td/telegram/files/FileLoaderActor.h +++ b/td/telegram/files/FileLoaderActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoaderUtils.cpp b/td/telegram/files/FileLoaderUtils.cpp index b39b301e..17b8c3e8 100644 --- a/td/telegram/files/FileLoaderUtils.cpp +++ b/td/telegram/files/FileLoaderUtils.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLoaderUtils.h b/td/telegram/files/FileLoaderUtils.h index a207036e..32a10cf4 100644 --- a/td/telegram/files/FileLoaderUtils.h +++ b/td/telegram/files/FileLoaderUtils.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileLocation.h b/td/telegram/files/FileLocation.h index b091a25d..69f725ce 100644 --- a/td/telegram/files/FileLocation.h +++ b/td/telegram/files/FileLocation.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,7 +18,6 @@ #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" -#include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/Variant.h" @@ -93,12 +92,13 @@ struct PhotoRemoteFileLocation { struct AsKey { const PhotoRemoteFileLocation &key; + bool is_unique; template void store(StorerT &storer) const; }; - AsKey as_key() const { - return AsKey{*this}; + AsKey as_key(bool is_unique) const { + return AsKey{*this, is_unique}; } bool operator<(const PhotoRemoteFileLocation &other) const { @@ -129,7 +129,7 @@ struct WebRemoteFileLocation { template void store(StorerT &storer) const; }; - AsKey as_key() const { + AsKey as_key(bool /*is_unique*/) const { return AsKey{*this}; } @@ -160,7 +160,7 @@ struct CommonRemoteFileLocation { template void store(StorerT &storer) const; }; - AsKey as_key() const { + AsKey as_key(bool /*is_unique*/) const { return AsKey{*this}; } @@ -183,7 +183,6 @@ class FullRemoteFileLocation { private: static constexpr int32 WEB_LOCATION_FLAG = 1 << 24; static constexpr int32 FILE_REFERENCE_FLAG = 1 << 25; - bool web_location_flag_{false}; DcId dc_id_; string file_reference_; enum class LocationType : int32 { Web, Photo, Common, None }; @@ -250,16 +249,6 @@ class FullRemoteFileLocation { } return type; } - int32 full_type() const { - auto type = static_cast(file_type_); - if (is_web()) { - type |= WEB_LOCATION_FLAG; - } - if (!file_reference_.empty()) { - type |= FILE_REFERENCE_FLAG; - } - return type; - } void check_file_reference() { if (file_reference_ == FileReferenceView::invalid_file_reference()) { @@ -284,6 +273,16 @@ class FullRemoteFileLocation { return AsKey{*this}; } + struct AsUnique { + const FullRemoteFileLocation &key; + + template + void store(StorerT &storer) const; + }; + AsUnique as_unique() const { + return AsUnique{*this}; + } + DcId get_dc_id() const { CHECK(!is_web()); return dc_id_; @@ -361,7 +360,7 @@ class FullRemoteFileLocation { } bool is_web() const { - return web_location_flag_; + return variant_.get_offset() == 0; } bool is_photo() const { return location_type() == LocationType::Photo; @@ -396,19 +395,20 @@ class FullRemoteFileLocation { case LocationType::Photo: switch (photo().source_.get_type()) { case PhotoSizeSource::Type::Legacy: - return make_tl_object( - photo().volume_id_, photo().local_id_, photo().source_.legacy().secret, BufferSlice(file_reference_)); + return make_tl_object( + photo().id_, photo().access_hash_, BufferSlice(file_reference_), photo().volume_id_, photo().local_id_, + photo().source_.legacy().secret); case PhotoSizeSource::Type::Thumbnail: { auto &thumbnail = photo().source_.thumbnail(); switch (thumbnail.file_type) { case FileType::Photo: return make_tl_object( photo().id_, photo().access_hash_, BufferSlice(file_reference_), - std::string(1, static_cast(narrow_cast(thumbnail.thumbnail_type)))); + std::string(1, static_cast(static_cast(thumbnail.thumbnail_type)))); case FileType::Thumbnail: return make_tl_object( photo().id_, photo().access_hash_, BufferSlice(file_reference_), - std::string(1, static_cast(narrow_cast(thumbnail.thumbnail_type)))); + std::string(1, static_cast(static_cast(thumbnail.thumbnail_type)))); default: UNREACHABLE(); break; @@ -501,10 +501,7 @@ class FullRemoteFileLocation { // web document FullRemoteFileLocation(FileType file_type, string url, int64 access_hash) - : file_type_(file_type) - , web_location_flag_{true} - , dc_id_() - , variant_(WebRemoteFileLocation{std::move(url), access_hash}) { + : file_type_(file_type), dc_id_(), variant_(WebRemoteFileLocation{std::move(url), access_hash}) { CHECK(is_web()); CHECK(!web().url_.empty()); } diff --git a/td/telegram/files/FileLocation.hpp b/td/telegram/files/FileLocation.hpp index 28532680..6b792694 100644 --- a/td/telegram/files/FileLocation.hpp +++ b/td/telegram/files/FileLocation.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -68,7 +68,9 @@ void PhotoRemoteFileLocation::parse(ParserT &parser) { template void PhotoRemoteFileLocation::AsKey::store(StorerT &storer) const { using td::store; - store(key.id_, storer); + if (!is_unique) { + store(key.id_, storer); + } store(key.volume_id_, storer); store(key.local_id_, storer); } @@ -114,9 +116,14 @@ void CommonRemoteFileLocation::AsKey::store(StorerT &storer) const { template void FullRemoteFileLocation::store(StorerT &storer) const { using ::td::store; - store(full_type(), storer); + bool has_file_reference = !file_reference_.empty(); + auto type = key_type(); + if (has_file_reference) { + type |= FILE_REFERENCE_FLAG; + } + store(type, storer); store(dc_id_.get_value(), storer); - if (!file_reference_.empty()) { + if (has_file_reference) { store(file_reference_, storer); } variant_.visit([&](auto &&value) { @@ -130,7 +137,7 @@ void FullRemoteFileLocation::parse(ParserT &parser) { using ::td::parse; int32 raw_type; parse(raw_type, parser); - web_location_flag_ = (raw_type & WEB_LOCATION_FLAG) != 0; + bool is_web = (raw_type & WEB_LOCATION_FLAG) != 0; raw_type &= ~WEB_LOCATION_FLAG; bool has_file_reference = (raw_type & FILE_REFERENCE_FLAG) != 0; raw_type &= ~FILE_REFERENCE_FLAG; @@ -144,13 +151,17 @@ void FullRemoteFileLocation::parse(ParserT &parser) { if (has_file_reference) { parse(file_reference_, parser); - file_reference_.clear(); + // file_reference_.clear(); + } + if (is_web) { + variant_ = WebRemoteFileLocation(); + return web().parse(parser); } switch (location_type()) { case LocationType::Web: - variant_ = WebRemoteFileLocation(); - return web().parse(parser); + UNREACHABLE(); + break; case LocationType::Photo: variant_ = PhotoRemoteFileLocation(); photo().parse(parser); @@ -198,7 +209,52 @@ void FullRemoteFileLocation::AsKey::store(StorerT &storer) const { store(key.key_type(), storer); key.variant_.visit([&](auto &&value) { using td::store; - store(value.as_key(), storer); + store(value.as_key(false), storer); + }); +} + +template +void FullRemoteFileLocation::AsUnique::store(StorerT &storer) const { + using td::store; + + int32 type = [key = &key] { + if (key->is_web()) { + return 0; + } + switch (key->file_type_) { + case FileType::Photo: + case FileType::ProfilePhoto: + case FileType::Thumbnail: + case FileType::EncryptedThumbnail: + case FileType::Wallpaper: + return 1; + case FileType::Video: + case FileType::VoiceNote: + case FileType::Document: + case FileType::Sticker: + case FileType::Audio: + case FileType::Animation: + case FileType::VideoNote: + case FileType::Background: + return 2; + case FileType::SecureRaw: + case FileType::Secure: + return 3; + case FileType::Encrypted: + return 4; + case FileType::Temp: + return 5; + case FileType::None: + case FileType::Size: + default: + UNREACHABLE(); + return -1; + } + }(); + store(type, storer); + key.variant_.visit([&](auto &&value) { + using td::store; + store(value.as_key(true), storer); }); } diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 469180a4..ab97f7d7 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -35,12 +35,14 @@ #include "td/utils/port/Stat.h" #include "td/utils/ScopeGuard.h" #include "td/utils/StringBuilder.h" +#include "td/utils/Time.h" #include "td/utils/tl_helpers.h" #include "td/utils/tl_parsers.h" #include #include #include +#include #include #include @@ -49,6 +51,8 @@ namespace { constexpr int64 MAX_FILE_SIZE = 1500 * (1 << 20) /* 1500MB */; } // namespace +int VERBOSITY_NAME(update_file) = VERBOSITY_NAME(INFO); + StringBuilder &operator<<(StringBuilder &string_builder, FileLocationSource source) { switch (source) { case FileLocationSource::None: @@ -67,6 +71,30 @@ StringBuilder &operator<<(StringBuilder &string_builder, FileLocationSource sour } } +StringBuilder &operator<<(StringBuilder &string_builder, FileManager::Query::Type type) { + switch (type) { + case FileManager::Query::Type::UploadByHash: + return string_builder << "UploadByHash"; + case FileManager::Query::Type::UploadWaitFileReference: + return string_builder << "UploadWaitFileReference"; + case FileManager::Query::Type::Upload: + return string_builder << "Upload"; + case FileManager::Query::Type::DownloadWaitFileReferece: + return string_builder << "DownloadWaitFileReferece"; + case FileManager::Query::Type::DownloadReloadDialog: + return string_builder << "DownloadReloadDialog"; + case FileManager::Query::Type::Download: + return string_builder << "Download"; + case FileManager::Query::Type::SetContent: + return string_builder << "SetContent"; + case FileManager::Query::Type::Generate: + return string_builder << "Generate"; + default: + UNREACHABLE(); + return string_builder << "Unknown"; + } +} + NewRemoteFileLocation::NewRemoteFileLocation(RemoteFileLocation remote, FileLocationSource source) { switch (remote.type()) { case RemoteFileLocation::Type::Empty: @@ -91,8 +119,6 @@ RemoteFileLocation NewRemoteFileLocation::partial_or_empty() const { return {}; } -int VERBOSITY_NAME(update_file) = VERBOSITY_NAME(INFO); - FileNode *FileNodePtr::operator->() const { return get(); } @@ -469,13 +495,16 @@ bool FileView::has_alive_remote_location() const { } bool FileView::has_active_upload_remote_location() const { + if (!has_remote_location()) { + return false; + } if (!has_alive_remote_location()) { return false; } - if (remote_location().is_encrypted_any()) { + if (main_remote_location().is_encrypted_any()) { return true; } - return remote_location().has_file_reference(); + return main_remote_location().has_file_reference(); } bool FileView::has_active_download_remote_location() const { @@ -497,6 +526,11 @@ const FullRemoteFileLocation &FileView::remote_location() const { return node_->remote_.full.value(); } +const FullRemoteFileLocation &FileView::main_remote_location() const { + CHECK(has_remote_location()); + return node_->remote_.full.value(); +} + bool FileView::has_generate_location() const { return node_->generate_ != nullptr; } @@ -510,6 +544,18 @@ int64 FileView::size() const { return node_->size_; } +int64 FileView::get_allocated_local_size() const { + auto file_path = path(); + if (file_path.empty()) { + return 0; + } + auto r_stat = stat(file_path); + if (r_stat.is_error()) { + return 0; + } + return r_stat.ok().real_size_; +} + int64 FileView::expected_size(bool may_guess) const { if (node_->size_ != 0) { return node_->size_; @@ -963,9 +1009,8 @@ void FileManager::try_forget_file_id(FileId file_id) { } LOG(DEBUG) << "Forget file " << file_id; - auto it = std::find(file_node->file_ids_.begin(), file_node->file_ids_.end(), file_id); - CHECK(it != file_node->file_ids_.end()); - file_node->file_ids_.erase(it); + bool is_removed = td::remove(file_node->file_ids_, file_id); + CHECK(is_removed); *info = FileIdInfo(); empty_file_ids_.push_back(file_id.get()); } @@ -1030,7 +1075,7 @@ Result FileManager::register_generate(FileType file_type, FileLocationSo // add #mtime# into conversion if (!original_path.empty() && conversion[0] != '#' && PathView(original_path).is_absolute()) { auto file_paths = log_interface->get_file_paths(); - if (std::find(file_paths.begin(), file_paths.end(), original_path) == file_paths.end()) { + if (!td::contains(file_paths, original_path)) { auto r_stat = stat(original_path); uint64 mtime = r_stat.is_ok() ? r_stat.ok().mtime_nsec_ : 0; conversion = PSTRING() << "#mtime#" << lpad0(to_string(mtime), 20) << '#' << conversion; @@ -1327,6 +1372,7 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy } if (!y_file_id.is_valid()) { + LOG(DEBUG) << "Old file is invalid"; return x_node->main_file_id_; } FileNodePtr y_node = get_file_node(y_file_id); @@ -1338,6 +1384,7 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy x_node->set_upload_pause(FileId()); } if (x_node.get() == y_node.get()) { + LOG(DEBUG) << "Files are already merged"; return x_node->main_file_id_; } if (y_file_id == y_node->upload_pause_) { @@ -1350,6 +1397,12 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy LOG(WARNING) << "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, + [](const auto &x, const auto &y) { return x + (y.get_remote() != 0); }); + }; + if (count_local(x_node) + count_local(y_node) > 100) { + } FileNodePtr nodes[] = {x_node, y_node, x_node}; FileNodeId node_ids[] = {get_file_id_info(x_file_id)->node_id_, get_file_id_info(y_file_id)->node_id_}; @@ -1492,6 +1545,10 @@ Result FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy node->need_load_from_pmc_ |= other_node->need_load_from_pmc_; node->can_search_locally_ &= other_node->can_search_locally_; + if (other_node->last_successful_force_reupload_time_ > node->last_successful_force_reupload_time_) { + node->last_successful_force_reupload_time_ = other_node->last_successful_force_reupload_time_; + } + if (main_file_id_i == other_node_i) { context_->on_merge_files(other_node->main_file_id_, node->main_file_id_); node->main_file_id_ = other_node->main_file_id_; @@ -1845,7 +1902,7 @@ bool FileManager::set_content(FileId file_id, BufferSlice bytes) { node->set_download_priority(FROM_BYTES_PRIORITY); - QueryId id = queries_container_.create(Query{file_id, Query::SetContent}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::SetContent}); node->download_id_ = id; node->is_download_started_ = true; send_closure(file_load_manager_, &FileLoadManager::from_bytes, id, node->remote_.full.value().file_type_, @@ -1963,8 +2020,8 @@ 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); unlink(file_view.local_location().path_).ignore(); - context_->on_new_file(-file_view.size(), -1); node->drop_local_location(); try_flush_node(node, "delete_file 1"); } @@ -2111,7 +2168,7 @@ void FileManager::run_download(FileNodePtr node) { if (node->need_reload_photo_ && file_view.may_reload_photo()) { LOG(INFO) << "Reload photo from file " << node->main_file_id_; - QueryId id = queries_container_.create(Query{file_id, Query::DownloadReloadDialog}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::DownloadReloadDialog}); node->download_id_ = id; context_->reload_photo(file_view.remote_location().get_source(), PromiseCreator::lambda([id, actor_id = actor_id(this), file_id](Result res) { @@ -2132,7 +2189,7 @@ void FileManager::run_download(FileNodePtr node) { // If file reference is needed if (!file_view.has_active_download_remote_location()) { VLOG(file_references) << "Do not have valid file_reference for file " << file_id; - QueryId id = queries_container_.create(Query{file_id, Query::DownloadWaitFileReferece}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::DownloadWaitFileReferece}); node->download_id_ = id; if (node->download_was_update_file_reference_) { on_error(id, Status::Error("Can't download file: have no valid file reference")); @@ -2154,7 +2211,7 @@ void FileManager::run_download(FileNodePtr node) { return; } - QueryId id = queries_container_.create(Query{file_id, Query::Download}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::Download}); node->download_id_ = id; node->is_download_started_ = false; LOG(INFO) << "Run download of file " << file_id << " of size " << node->size_ << " from " @@ -2167,7 +2224,7 @@ void FileManager::run_download(FileNodePtr node) { download_limit, priority); } -class ForceUploadActor : public Actor { +class FileManager::ForceUploadActor : public Actor { public: ForceUploadActor(FileManager *file_manager, FileId file_id, std::shared_ptr callback, int32 new_priority, uint64 upload_order, ActorShared<> parent) @@ -2248,6 +2305,7 @@ class ForceUploadActor : public Actor { void on_ok() { callback_.reset(); + send_closure(G()->file_manager(), &FileManager::on_force_reupload_success, file_id_); stop(); } @@ -2284,16 +2342,14 @@ class ForceUploadActor : public Actor { } }; +void FileManager::on_force_reupload_success(FileId file_id) { + auto node = get_sync_file_node(file_id); + CHECK(node); + node->last_successful_force_reupload_time_ = Time::now(); +} + void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std::shared_ptr callback, int32 new_priority, uint64 upload_order, bool force) { - if (bad_parts.size() == 1 && bad_parts[0] == -1) { - create_actor("ForceUploadActor", this, file_id, std::move(callback), new_priority, upload_order, - context_->create_reference()) - .release(); - return; - } - LOG(INFO) << "Resume upload of file " << file_id << " with priority " << new_priority << " and force = " << force; - auto node = get_sync_file_node(file_id); if (!node) { LOG(INFO) << "File " << file_id << " not found"; @@ -2302,6 +2358,23 @@ void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std: } return; } + + if (bad_parts.size() == 1 && bad_parts[0] == -1) { + if (node->last_successful_force_reupload_time_ >= Time::now() - 60) { + LOG(INFO) << "Recently reuploaded file " << file_id << ", do not try again"; + if (callback) { + callback->on_upload_error(file_id, Status::Error("Failed to reupload file")); + } + return; + } + + create_actor("ForceUploadActor", this, file_id, std::move(callback), new_priority, upload_order, + context_->create_reference()) + .release(); + return; + } + LOG(INFO) << "Resume upload of file " << file_id << " with priority " << new_priority << " and force = " << force; + if (force) { node->remote_.is_full_alive = false; } @@ -2402,6 +2475,7 @@ void FileManager::delete_file_reference(FileId file_id, string file_reference) { if (remote != nullptr) { VLOG(file_references) << "Do delete file reference of remote file " << file_id; if (remote->delete_file_reference(file_reference)) { + VLOG(file_references) << "Successfully deleted file reference of remote file " << file_id; node->upload_was_update_file_reference_ = false; node->download_was_update_file_reference_ = false; node->on_pmc_changed(); @@ -2480,7 +2554,7 @@ void FileManager::run_generate(FileNodePtr node) { return; } - QueryId id = queries_container_.create(Query{file_id, Query::Generate}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::Generate}); node->generate_id_ = id; send_closure(file_generate_manager_, &FileGenerateManager::generate_file, id, *node->generate_, node->local_, node->suggested_name(), [file_manager = this, id] { @@ -2581,7 +2655,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { if (file_view.has_alive_remote_location() && !file_view.has_active_upload_remote_location() && file_view.get_type() != FileType::Thumbnail && file_view.get_type() != FileType::EncryptedThumbnail && file_view.get_type() != FileType::Background) { - QueryId id = queries_container_.create(Query{file_id, Query::UploadWaitFileReference}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::UploadWaitFileReference}); node->upload_id_ = id; if (node->upload_was_update_file_reference_) { on_error(id, Status::Error("Can't upload file: have no valid file reference")); @@ -2598,7 +2672,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { if (!node->remote_.partial && node->get_by_hash_) { LOG(INFO) << "Get file " << node->main_file_id_ << " by hash"; - QueryId id = queries_container_.create(Query{file_id, Query::UploadByHash}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::UploadByHash}); node->upload_id_ = id; send_closure(file_load_manager_, &FileLoadManager::upload_by_hash, id, node->local_.full(), node->size_, @@ -2607,10 +2681,9 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { } auto new_priority = narrow_cast(bad_parts.empty() ? -priority : priority); - bad_parts.erase(std::remove_if(bad_parts.begin(), bad_parts.end(), [](auto part_id) { return part_id < 0; }), - bad_parts.end()); + td::remove_if(bad_parts, [](auto part_id) { return part_id < 0; }); - QueryId id = queries_container_.create(Query{file_id, Query::Upload}); + QueryId id = queries_container_.create(Query{file_id, Query::Type::Upload}); node->upload_id_ = id; send_closure(file_load_manager_, &FileLoadManager::upload, id, node->local_, node->remote_.partial_or_empty(), file_view.expected_size(true), node->encryption_key_, new_priority, std::move(bad_parts)); @@ -2636,6 +2709,14 @@ static bool is_background_type(FileType type) { return type == FileType::Wallpaper || type == FileType::Background; } +string FileManager::get_unique_id(const FullGenerateFileLocation &location) { + return base64url_encode(zero_encode('\xff' + serialize(location))); +} + +string FileManager::get_unique_id(const FullRemoteFileLocation &location) { + return base64url_encode(zero_encode(serialize(location.as_unique()))); +} + string FileManager::get_persistent_id(const FullGenerateFileLocation &location) { auto binary = serialize(location); @@ -2643,13 +2724,16 @@ string FileManager::get_persistent_id(const FullGenerateFileLocation &location) binary.push_back(PERSISTENT_ID_VERSION_MAP); return base64url_encode(binary); } + string FileManager::get_persistent_id(const FullRemoteFileLocation &location) { auto location_copy = location; location_copy.clear_file_reference(); auto binary = serialize(location_copy); binary = zero_encode(binary); - binary.push_back(static_cast(narrow_cast(Version::Next) - 1)); + binary.push_back(static_cast(narrow_cast(Version::AddFolders) - 1)); + // TODO return back correct version + // binary.push_back(static_cast(narrow_cast(Version::Next) - 1)); binary.push_back(PERSISTENT_ID_VERSION); return base64url_encode(binary); } @@ -2774,12 +2858,17 @@ td_api::object_ptr FileManager::get_file_object(FileId file_id, bo } string persistent_file_id; + string unique_file_id; if (file_view.has_alive_remote_location()) { persistent_file_id = get_persistent_id(file_view.remote_location()); + if (!file_view.remote_location().is_web()) { + unique_file_id = get_unique_id(file_view.remote_location()); + } } else if (file_view.has_url()) { persistent_file_id = file_view.url(); } else if (file_view.has_generate_location() && begins_with(file_view.generate_location().conversion_, "#map#")) { persistent_file_id = get_persistent_id(file_view.generate_location()); + unique_file_id = get_unique_id(file_view.generate_location()); } bool is_uploading_completed = !persistent_file_id.empty(); @@ -2810,8 +2899,8 @@ td_api::object_ptr FileManager::get_file_object(FileId file_id, bo td_api::make_object(std::move(path), can_be_downloaded, can_be_deleted, file_view.is_downloading(), file_view.has_local_location(), download_offset, local_prefix_size, local_total_size), - td_api::make_object(std::move(persistent_file_id), file_view.is_uploading(), - is_uploading_completed, remote_size)); + td_api::make_object(std::move(persistent_file_id), std::move(unique_file_id), + file_view.is_uploading(), is_uploading_completed, remote_size)); } vector FileManager::get_file_ids_object(const vector &file_ids, bool with_main_file_id) { @@ -3187,7 +3276,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(size, 1); + context_->on_new_file(get_file_view(r_new_file_id.ok()).get_allocated_local_size(), 1); } LOG_STATUS(merge(r_new_file_id.ok(), file_id)); } @@ -3354,7 +3443,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.size(), 1); + context_->on_new_file(file_view.get_allocated_local_size(), 1); } run_upload(file_node, {}); @@ -3381,7 +3470,7 @@ void FileManager::on_error(QueryId query_id, Status status) { return; } - if (query.type_ == Query::UploadByHash && !G()->close_flag()) { + if (query.type_ == Query::Type::UploadByHash && !G()->close_flag()) { LOG(INFO) << "Upload By Hash failed: " << status << ", restart upload"; node->get_by_hash_ = false; run_upload(node, {}); @@ -3390,7 +3479,7 @@ void FileManager::on_error(QueryId query_id, Status status) { on_error_impl(node, query.type_, was_active, std::move(status)); } -void FileManager::on_error_impl(FileNodePtr node, FileManager::Query::Type type, bool was_active, Status status) { +void FileManager::on_error_impl(FileNodePtr node, Query::Type type, bool was_active, Status status) { SCOPE_EXIT { try_flush_node(node, "on_error"); }; diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 18d127b8..cfbda8fa 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -33,6 +33,7 @@ #include "td/utils/optional.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" #include #include @@ -147,6 +148,8 @@ class FileNode { FileId main_file_id_; + double last_successful_force_reupload_time_ = -1e10; + FileId upload_pause_; int8 upload_priority_ = 0; int8 download_priority_ = 0; @@ -238,6 +241,7 @@ class FileView { bool has_active_upload_remote_location() const; bool has_active_download_remote_location() const; const FullRemoteFileLocation &remote_location() const; + const FullRemoteFileLocation &main_remote_location() const; bool has_generate_location() const; const FullGenerateFileLocation &generate_location() const; @@ -267,6 +271,8 @@ class FileView { int64 remote_size() const; string path() const; + int64 get_allocated_local_size() const; + bool can_download_from_server() const; bool can_generate() const; bool can_delete() const; @@ -482,12 +488,14 @@ class FileManager : public FileLoadManager::Callback { bool force); static constexpr int8 FROM_BYTES_PRIORITY = 10; + using FileNodeId = int32; + using QueryId = FileLoadManager::QueryId; class Query { public: FileId file_id_; - enum Type : int32 { + enum class Type : int32 { UploadByHash, UploadWaitFileReference, Upload, @@ -498,6 +506,9 @@ class FileManager : public FileLoadManager::Callback { Generate } type_; }; + + friend StringBuilder &operator<<(StringBuilder &string_builder, Query::Type type); + struct FileIdInfo { FileNodeId node_id_{0}; bool send_updates_flag_{false}; @@ -513,6 +524,8 @@ class FileManager : public FileLoadManager::Callback { std::shared_ptr upload_callback_; }; + class ForceUploadActor; + ActorShared<> parent_; unique_ptr context_; std::shared_ptr file_db_; @@ -574,6 +587,9 @@ class FileManager : public FileLoadManager::Callback { void flush_to_pmc(FileNodePtr node, bool new_remote, bool new_local, bool new_generate, const char *source); void load_from_pmc(FileNodePtr node, bool new_remote, bool new_local, bool new_generate); + string get_unique_id(const FullGenerateFileLocation &location); + string get_unique_id(const FullRemoteFileLocation &location); + string get_persistent_id(const FullGenerateFileLocation &location); string get_persistent_id(const FullRemoteFileLocation &location); @@ -595,6 +611,8 @@ class FileManager : public FileLoadManager::Callback { FileNodePtr get_sync_file_node(FileId file_id); + void on_force_reupload_success(FileId file_id); + // void release_file_node(FileNodeId id); void do_cancel_download(FileNodePtr node); void do_cancel_upload(FileNodePtr node); diff --git a/td/telegram/files/FileManager.hpp b/td/telegram/files/FileManager.hpp index 9d7ca515..f112665c 100644 --- a/td/telegram/files/FileManager.hpp +++ b/td/telegram/files/FileManager.hpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileSourceId.h b/td/telegram/files/FileSourceId.h index 3dc1512a..88d47ee7 100644 --- a/td/telegram/files/FileSourceId.h +++ b/td/telegram/files/FileSourceId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileStats.cpp b/td/telegram/files/FileStats.cpp index 7933d2c3..5dca628b 100644 --- a/td/telegram/files/FileStats.cpp +++ b/td/telegram/files/FileStats.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileStats.h b/td/telegram/files/FileStats.h index 38ca35ca..4d76d66c 100644 --- a/td/telegram/files/FileStats.h +++ b/td/telegram/files/FileStats.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileStatsWorker.cpp b/td/telegram/files/FileStatsWorker.cpp index 4ce7ee99..34d9edb8 100644 --- a/td/telegram/files/FileStatsWorker.cpp +++ b/td/telegram/files/FileStatsWorker.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -126,7 +126,7 @@ void scan_fs(CancellationToken &token, CallbackT &&callback) { FsFileInfo info; info.path = path.str(); - info.size = stat.size_; + info.size = stat.real_size_; info.file_type = file_type; info.atime_nsec = stat.atime_nsec_; info.mtime_nsec = stat.mtime_nsec_; diff --git a/td/telegram/files/FileStatsWorker.h b/td/telegram/files/FileStatsWorker.h index 65d906dd..faf59d55 100644 --- a/td/telegram/files/FileStatsWorker.h +++ b/td/telegram/files/FileStatsWorker.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileType.h b/td/telegram/files/FileType.h index 469cc786..31c0427a 100644 --- a/td/telegram/files/FileType.h +++ b/td/telegram/files/FileType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/FileUploader.cpp b/td/telegram/files/FileUploader.cpp index cd7519ed..2272e439 100644 --- a/td/telegram/files/FileUploader.cpp +++ b/td/telegram/files/FileUploader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -167,8 +167,7 @@ Result FileUploader::on_update_local_location(const Loca } if (local_is_ready) { CHECK(!fd_.empty()); - TRY_RESULT(local_file_size, fd_.get_size()); - local_size = local_file_size; + TRY_RESULT_ASSIGN(local_size, fd_.get_size()); LOG(INFO) << "Set file local_size to " << local_size; if (local_size == 0) { return Status::Error("Can't upload empty file"); @@ -338,8 +337,7 @@ void FileUploader::try_release_fd() { Status FileUploader::acquire_fd() { if (fd_.empty()) { - TRY_RESULT(fd, FileFd::open(fd_path_, FileFd::Read)); - fd_ = std::move(fd); + TRY_RESULT_ASSIGN(fd_, FileFd::open(fd_path_, FileFd::Read)); } return Status::OK(); } diff --git a/td/telegram/files/FileUploader.h b/td/telegram/files/FileUploader.h index 8d58d38d..f3834066 100644 --- a/td/telegram/files/FileUploader.h +++ b/td/telegram/files/FileUploader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/PartsManager.cpp b/td/telegram/files/PartsManager.cpp index 5706a08f..7a154c14 100644 --- a/td/telegram/files/PartsManager.cpp +++ b/td/telegram/files/PartsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/PartsManager.h b/td/telegram/files/PartsManager.h index d69f40b9..2f88265a 100644 --- a/td/telegram/files/PartsManager.h +++ b/td/telegram/files/PartsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/ResourceManager.cpp b/td/telegram/files/ResourceManager.cpp index 27ae0fe8..39c9fcf2 100644 --- a/td/telegram/files/ResourceManager.cpp +++ b/td/telegram/files/ResourceManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/ResourceManager.h b/td/telegram/files/ResourceManager.h index 9c0633a4..3eb9fe53 100644 --- a/td/telegram/files/ResourceManager.h +++ b/td/telegram/files/ResourceManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/files/ResourceState.h b/td/telegram/files/ResourceState.h index 47943e67..130bccb0 100644 --- a/td/telegram/files/ResourceState.h +++ b/td/telegram/files/ResourceState.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/logevent/LogEvent.h b/td/telegram/logevent/LogEvent.h index 0883fb49..dd0fa076 100644 --- a/td/telegram/logevent/LogEvent.h +++ b/td/telegram/logevent/LogEvent.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -94,6 +94,8 @@ class LogEvent { GetDialogFromServer = 0x113, ReadHistoryInSecretChat = 0x114, ToggleDialogIsMarkedAsUnreadOnServer = 0x115, + SetDialogFolderIdOnServer = 0x116, + DeleteScheduledMessagesFromServer = 0x117, GetChannelDifference = 0x140, AddMessagePushNotification = 0x200, EditMessagePushNotification = 0x201, diff --git a/td/telegram/logevent/LogEventHelper.h b/td/telegram/logevent/LogEventHelper.h index f3be2705..31bdc7da 100644 --- a/td/telegram/logevent/LogEventHelper.h +++ b/td/telegram/logevent/LogEventHelper.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,17 +20,14 @@ namespace td { -#define get_erase_logevent_promise(...) get_erase_logevent_promise_impl(__FILE__, __LINE__, __VA_ARGS__) - -inline Promise get_erase_logevent_promise_impl(const char *file, int line, uint64 logevent_id, - Promise promise = Promise()) { +inline Promise get_erase_logevent_promise(uint64 logevent_id, Promise promise = Promise()) { if (logevent_id == 0) { return promise; } - return PromiseCreator::lambda([file, line, logevent_id, promise = std::move(promise)](Result result) mutable { + return PromiseCreator::lambda([logevent_id, promise = std::move(promise)](Result result) mutable { if (!G()->close_flag()) { - binlog_erase(G()->get_td_db_impl(file, line)->get_binlog_impl(file, line), logevent_id); + binlog_erase(G()->td_db()->get_binlog(), logevent_id); } promise.set_result(std::move(result)); diff --git a/td/telegram/logevent/SecretChatEvent.h b/td/telegram/logevent/SecretChatEvent.h index bfd94f4f..28e79a1d 100644 --- a/td/telegram/logevent/SecretChatEvent.h +++ b/td/telegram/logevent/SecretChatEvent.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/misc.cpp b/td/telegram/misc.cpp index 9d619d5d..35aa474e 100644 --- a/td/telegram/misc.cpp +++ b/td/telegram/misc.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,7 +13,6 @@ #include "td/utils/Slice.h" #include "td/utils/utf8.h" -#include #include #include @@ -48,8 +47,9 @@ string clean_name(string str, size_t max_length) { } string clean_username(string str) { - str.resize(std::remove(str.begin(), str.end(), '.') - str.begin()); - return trim(to_lower(str)); + td::remove(str, '.'); + to_lower_inplace(str); + return trim(str); } bool clean_input_string(string &str) { diff --git a/td/telegram/misc.h b/td/telegram/misc.h index 62804318..2f033150 100644 --- a/td/telegram/misc.h +++ b/td/telegram/misc.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/AuthDataShared.cpp b/td/telegram/net/AuthDataShared.cpp index 4e2eac56..40da715a 100644 --- a/td/telegram/net/AuthDataShared.cpp +++ b/td/telegram/net/AuthDataShared.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,11 +11,10 @@ #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" #include "td/utils/port/RwMutex.h" #include "td/utils/tl_helpers.h" -#include - namespace td { class AuthDataSharedImpl : public AuthDataShared { @@ -103,9 +102,7 @@ class AuthDataSharedImpl : public AuthDataShared { void notify() { auto lock = rw_mutex_.lock_read(); - auto it = std::remove_if(auth_key_listeners_.begin(), auth_key_listeners_.end(), - [&](auto &listener) { return !listener->notify(); }); - auth_key_listeners_.erase(it, auth_key_listeners_.end()); + td::remove_if(auth_key_listeners_, [&](auto &listener) { return !listener->notify(); }); } void log_auth_key(const mtproto::AuthKey &auth_key) { diff --git a/td/telegram/net/AuthDataShared.h b/td/telegram/net/AuthDataShared.h index de09006f..e7ca5e07 100644 --- a/td/telegram/net/AuthDataShared.h +++ b/td/telegram/net/AuthDataShared.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index a213f436..199d12de 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -39,7 +39,6 @@ #include "td/utils/Time.h" #include "td/utils/tl_helpers.h" -#include #include namespace td { @@ -578,6 +577,7 @@ void ConnectionCreator::on_online(bool online_flag) { } void ConnectionCreator::on_pong(size_t hash) { + G()->save_server_time(); if (active_proxy_id_ != 0) { auto now = G()->unix_time(); int32 &last_used = proxy_last_used_date_[active_proxy_id_]; @@ -686,14 +686,13 @@ Result ConnectionCreator::find_connection(const Proxy &proxy, const IP TRY_RESULT(info, dc_options_set_.find_connection( dc_id, allow_media_only, proxy.use_proxy() && proxy.use_socks5_proxy(), prefer_ipv6, only_http)); extra.stat = info.stat; - TRY_RESULT(transport_type, get_transport_type(proxy, info)); - extra.transport_type = std::move(transport_type); + TRY_RESULT_ASSIGN(extra.transport_type, get_transport_type(proxy, info)); extra.debug_str = PSTRING() << " to " << (info.option->is_media_only() ? "MEDIA " : "") << dc_id << (info.use_http ? " over HTTP" : ""); if (proxy.use_mtproto_proxy()) { - extra.debug_str = PSTRING() << "Mtproto " << proxy_ip_address << extra.debug_str; + extra.debug_str = PSTRING() << "MTProto " << proxy_ip_address << extra.debug_str; LOG(INFO) << "Create: " << extra.debug_str; return SocketFd::open(proxy_ip_address); @@ -812,14 +811,12 @@ void ConnectionCreator::client_loop(ClientInfo &client) { VLOG(connections) << "In client_loop: " << tag("client", format::as_hex(client.hash)); // Remove expired ready connections - client.ready_connections.erase( - std::remove_if(client.ready_connections.begin(), client.ready_connections.end(), - [&, expires_at = Time::now_cached() - ClientInfo::READY_CONNECTIONS_TIMEOUT](auto &v) { - bool drop = v.second < expires_at; - VLOG_IF(connections, drop) << "Drop expired " << tag("connection", v.first.get()); - return drop; - }), - client.ready_connections.end()); + td::remove_if(client.ready_connections, + [&, expires_at = Time::now_cached() - ClientInfo::READY_CONNECTIONS_TIMEOUT](auto &v) { + bool drop = v.second < expires_at; + VLOG_IF(connections, drop) << "Drop expired " << tag("connection", v.first.get()); + return drop; + }); // Send ready connections into promises { @@ -1010,6 +1007,7 @@ void ConnectionCreator::client_add_connection(size_t hash, Resultsave_server_time(); client_loop(clients_[hash]); } diff --git a/td/telegram/net/ConnectionCreator.h b/td/telegram/net/ConnectionCreator.h index 752dfc14..ac932d33 100644 --- a/td/telegram/net/ConnectionCreator.h +++ b/td/telegram/net/ConnectionCreator.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/DcAuthManager.cpp b/td/telegram/net/DcAuthManager.cpp index c34f1919..917fb536 100644 --- a/td/telegram/net/DcAuthManager.cpp +++ b/td/telegram/net/DcAuthManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,7 +18,6 @@ #include "td/telegram/telegram_api.h" -#include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/misc.h" @@ -34,8 +33,9 @@ DcAuthManager::DcAuthManager(ActorShared<> parent) { auto main_dc_id = to_integer(s_main_dc_id); if (DcId::is_valid(main_dc_id)) { main_dc_id_ = DcId::internal(main_dc_id); + VLOG(dc) << "Init main DcId to " << main_dc_id_; } else { - LOG(ERROR) << "Receive invalid main dc id " << main_dc_id; + LOG(ERROR) << "Receive invalid main DcId " << main_dc_id; } } } @@ -64,9 +64,12 @@ void DcAuthManager::add_dc(std::shared_ptr auth_data) { info.shared_auth_data = std::move(auth_data); auto state_was_auth = info.shared_auth_data->get_auth_key_state(); info.auth_key_state = state_was_auth.first; + VLOG(dc) << "Add " << info.dc_id << " with auth key state " << info.auth_key_state + << " and was_auth = " << state_was_auth.second; was_auth_ |= state_was_auth.second; if (!main_dc_id_.is_exact()) { main_dc_id_ = info.dc_id; + VLOG(dc) << "Set main DcId to " << main_dc_id_; } info.shared_auth_data->add_auth_key_listener(make_unique(actor_shared(this, info.dc_id.get_raw_id()))); dcs_.emplace_back(std::move(info)); @@ -75,6 +78,7 @@ void DcAuthManager::add_dc(std::shared_ptr auth_data) { void DcAuthManager::update_main_dc(DcId new_main_dc_id) { main_dc_id_ = new_main_dc_id; + VLOG(dc) << "Update main DcId to " << main_dc_id_; loop(); } @@ -95,8 +99,8 @@ void DcAuthManager::update_auth_key_state() { int32 dc_id = narrow_cast(get_link_token()); auto &dc = get_dc(dc_id); auto state_was_auth = dc.shared_auth_data->get_auth_key_state(); - VLOG(dc) << "Update DC auth key state " << tag("dc_id", dc_id) << tag("old_auth_key_state", dc.auth_key_state) - << tag("new_auth_key_state", state_was_auth.first); + VLOG(dc) << "Update " << dc_id << " auth key state from " << dc.auth_key_state << " to " << state_was_auth.first + << " with was_auth = " << state_was_auth.second; dc.auth_key_state = state_was_auth.first; was_auth_ |= state_was_auth.second; @@ -213,10 +217,10 @@ void DcAuthManager::destroy_loop() { } if (is_ready) { - LOG(INFO) << "Destroy auth keys loop is ready, all keys are destroyed"; + VLOG(dc) << "Destroy auth keys loop is ready, all keys are destroyed"; destroy_promise_.set_value(Unit()); } else { - LOG(INFO) << "DC is not ready for destroying auth key"; + VLOG(dc) << "DC is not ready for destroying auth key"; } } @@ -232,11 +236,13 @@ void DcAuthManager::loop() { } auto main_dc = find_dc(main_dc_id_.get_raw_id()); if (!main_dc || main_dc->auth_key_state != AuthKeyState::OK) { + VLOG(dc) << "Main is " << main_dc_id_ << ", main auth key state is " + << (main_dc ? main_dc->auth_key_state : AuthKeyState::Empty) << ", was_auth = " << was_auth_; if (was_auth_) { G()->shared_config().set_option_boolean("auth", false); destroy_loop(); } - VLOG(dc) << "Skip loop because auth state of main dc " << main_dc_id_.get_raw_id() << " is " + VLOG(dc) << "Skip loop because auth state of main DcId " << main_dc_id_.get_raw_id() << " is " << (main_dc != nullptr ? (PSTRING() << main_dc->auth_key_state) : "unknown"); return; diff --git a/td/telegram/net/DcAuthManager.h b/td/telegram/net/DcAuthManager.h index 1d0d2800..bec44bc2 100644 --- a/td/telegram/net/DcAuthManager.h +++ b/td/telegram/net/DcAuthManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/DcId.h b/td/telegram/net/DcId.h index 01cde739..c4009907 100644 --- a/td/telegram/net/DcId.h +++ b/td/telegram/net/DcId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/DcOptions.h b/td/telegram/net/DcOptions.h index 37e7bcb8..6d3d0a58 100644 --- a/td/telegram/net/DcOptions.h +++ b/td/telegram/net/DcOptions.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/DcOptionsSet.cpp b/td/telegram/net/DcOptionsSet.cpp index c031ac93..906fee3a 100644 --- a/td/telegram/net/DcOptionsSet.cpp +++ b/td/telegram/net/DcOptionsSet.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" #include #include @@ -46,6 +47,8 @@ DcOptions DcOptionsSet::get_dc_options() const { vector DcOptionsSet::find_all_connections(DcId dc_id, bool allow_media_only, bool use_static, bool prefer_ipv6, bool only_http) { + LOG(DEBUG) << "Find all " << (allow_media_only ? "media " : "") << "connections in " << dc_id + << ". use_static = " << use_static << ", prefer_ipv6 = " << prefer_ipv6 << ", only_http = " << only_http; std::vector options; std::vector static_options; @@ -98,8 +101,7 @@ vector DcOptionsSet::find_all_connections(DcId dc_ } else { bool have_ipv4 = std::any_of(options.begin(), options.end(), [](auto &v) { return !v.option->is_ipv6(); }); if (have_ipv4) { - options.erase(std::remove_if(options.begin(), options.end(), [](auto &v) { return v.option->is_ipv6(); }), - options.end()); + td::remove_if(options, [](auto &v) { return v.option->is_ipv6(); }); } } } else { @@ -111,15 +113,13 @@ vector DcOptionsSet::find_all_connections(DcId dc_ if (prefer_ipv6) { bool have_ipv6 = std::any_of(options.begin(), options.end(), [](auto &v) { return v.option->is_ipv6(); }); if (have_ipv6) { - options.erase(std::remove_if(options.begin(), options.end(), [](auto &v) { return !v.option->is_ipv6(); }), - options.end()); + td::remove_if(options, [](auto &v) { return !v.option->is_ipv6(); }); } } bool have_media_only = std::any_of(options.begin(), options.end(), [](auto &v) { return v.option->is_media_only(); }); if (have_media_only) { - options.erase(std::remove_if(options.begin(), options.end(), [](auto &v) { return !v.option->is_media_only(); }), - options.end()); + td::remove_if(options, [](auto &v) { return !v.option->is_media_only(); }); } return options; diff --git a/td/telegram/net/DcOptionsSet.h b/td/telegram/net/DcOptionsSet.h index eb772e13..4b5fcf33 100644 --- a/td/telegram/net/DcOptionsSet.h +++ b/td/telegram/net/DcOptionsSet.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/MtprotoHeader.cpp b/td/telegram/net/MtprotoHeader.cpp index f51fd066..bd90bc4a 100644 --- a/td/telegram/net/MtprotoHeader.cpp +++ b/td/telegram/net/MtprotoHeader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/MtprotoHeader.h b/td/telegram/net/MtprotoHeader.h index 3512c39a..5397b01e 100644 --- a/td/telegram/net/MtprotoHeader.h +++ b/td/telegram/net/MtprotoHeader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetActor.cpp b/td/telegram/net/NetActor.cpp index 6dfba4c1..9464e276 100644 --- a/td/telegram/net/NetActor.cpp +++ b/td/telegram/net/NetActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetActor.h b/td/telegram/net/NetActor.h index c6bb52aa..9eb19b28 100644 --- a/td/telegram/net/NetActor.h +++ b/td/telegram/net/NetActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQuery.cpp b/td/telegram/net/NetQuery.cpp index 9692ebe1..6602ae4a 100644 --- a/td/telegram/net/NetQuery.cpp +++ b/td/telegram/net/NetQuery.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -54,7 +54,8 @@ void NetQuery::set_error(Status status, string source) { } if (status.message() == "BOT_METHOD_INVALID") { auto id = tl_constructor(); - if (id != telegram_api::help_getNearestDc::ID && id != telegram_api::help_getProxyData::ID) { + if (id != telegram_api::help_getNearestDc::ID && id != telegram_api::help_getProxyData::ID && + id != telegram_api::help_getAppConfig::ID) { LOG(ERROR) << "Receive BOT_METHOD_INVALID for query " << format::as_hex(id); } } diff --git a/td/telegram/net/NetQuery.h b/td/telegram/net/NetQuery.h index 4e355b36..444e2614 100644 --- a/td/telegram/net/NetQuery.h +++ b/td/telegram/net/NetQuery.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -293,7 +293,7 @@ class NetQuery : public ListNode { static int32 get_my_id(); movable_atomic session_id_{0}; - uint64 message_id_{}; + uint64 message_id_{0}; movable_atomic cancellation_token_{-1}; // == 0 if query is canceled ActorShared callback_; diff --git a/td/telegram/net/NetQueryCounter.cpp b/td/telegram/net/NetQueryCounter.cpp index 4459a492..eb486793 100644 --- a/td/telegram/net/NetQueryCounter.cpp +++ b/td/telegram/net/NetQueryCounter.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQueryCounter.h b/td/telegram/net/NetQueryCounter.h index 7b3187f2..79e9c799 100644 --- a/td/telegram/net/NetQueryCounter.h +++ b/td/telegram/net/NetQueryCounter.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQueryCreator.cpp b/td/telegram/net/NetQueryCreator.cpp index 950cd236..3777a6f8 100644 --- a/td/telegram/net/NetQueryCreator.cpp +++ b/td/telegram/net/NetQueryCreator.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQueryCreator.h b/td/telegram/net/NetQueryCreator.h index 41c18041..a7ca101f 100644 --- a/td/telegram/net/NetQueryCreator.h +++ b/td/telegram/net/NetQueryCreator.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQueryDelayer.cpp b/td/telegram/net/NetQueryDelayer.cpp index 452cb483..48d3847f 100644 --- a/td/telegram/net/NetQueryDelayer.cpp +++ b/td/telegram/net/NetQueryDelayer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -33,9 +33,10 @@ void NetQueryDelayer::delay(NetQueryPtr query) { } } else if (code == 420) { auto msg = query->error().message(); - for (auto prefix : {Slice("FLOOD_WAIT_"), Slice("2FA_CONFIRM_WAIT_"), Slice("TAKEOUT_INIT_DELAY_")}) { + for (auto prefix : + {Slice("FLOOD_WAIT_"), Slice("SLOWMODE_WAIT_"), Slice("2FA_CONFIRM_WAIT_"), Slice("TAKEOUT_INIT_DELAY_")}) { if (begins_with(msg, prefix)) { - timeout = clamp(to_integer(msg.substr(prefix.size())), 0, 14 * 24 * 60 * 60); + timeout = clamp(to_integer(msg.substr(prefix.size())), 1, 14 * 24 * 60 * 60); break; } } diff --git a/td/telegram/net/NetQueryDelayer.h b/td/telegram/net/NetQueryDelayer.h index 855b38ef..90bba273 100644 --- a/td/telegram/net/NetQueryDelayer.h +++ b/td/telegram/net/NetQueryDelayer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetQueryDispatcher.cpp b/td/telegram/net/NetQueryDispatcher.cpp index 3af0e41c..957bbf0a 100644 --- a/td/telegram/net/NetQueryDispatcher.cpp +++ b/td/telegram/net/NetQueryDispatcher.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,6 +18,7 @@ #include "td/telegram/Global.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" +#include "td/telegram/telegram_api.h" #include "td/utils/common.h" #include "td/utils/format.h" @@ -51,6 +52,10 @@ void NetQueryDispatcher::dispatch(NetQueryPtr net_query) { net_query->set_error(Status::Error(429, "Too Many Requests: retry after 10")); return complete_net_query(std::move(net_query)); } + if (net_query->tl_constructor() == telegram_api::account_getPassword::ID && false) { + net_query->set_error(Status::Error(429, "Too Many Requests: retry after 10")); + return complete_net_query(std::move(net_query)); + } if (net_query->is_ready()) { if (net_query->is_error()) { @@ -296,26 +301,7 @@ void NetQueryDispatcher::try_fix_migrate(NetQueryPtr &net_query) { for (auto &prefix : prefixes) { if (msg.substr(0, prefix.size()) == prefix) { int32 new_main_dc_id = to_integer(msg.substr(prefix.size())); - if (!DcId::is_valid(new_main_dc_id)) { - LOG(FATAL) << "Receive " << prefix << " to wrong dc " << new_main_dc_id; - } - if (new_main_dc_id != main_dc_id_.load(std::memory_order_relaxed)) { - // Very rare event. Mutex is ok. - std::lock_guard guard(main_dc_id_mutex_); - if (new_main_dc_id != main_dc_id_) { - LOG(INFO) << "Update: " << tag("main_dc_id", main_dc_id_.load(std::memory_order_relaxed)); - if (is_dc_inited(main_dc_id_.load(std::memory_order_relaxed))) { - send_closure_later(dcs_[main_dc_id_ - 1].main_session_, &SessionMultiProxy::update_main_flag, false); - } - main_dc_id_ = new_main_dc_id; - if (is_dc_inited(main_dc_id_.load(std::memory_order_relaxed))) { - send_closure_later(dcs_[main_dc_id_ - 1].main_session_, &SessionMultiProxy::update_main_flag, true); - } - send_closure_later(dc_auth_manager_, &DcAuthManager::update_main_dc, - DcId::internal(main_dc_id_.load(std::memory_order_relaxed))); - G()->td_db()->get_binlog_pmc()->set("main_dc_id", to_string(main_dc_id_.load(std::memory_order_relaxed))); - } - } + set_main_dc_id(new_main_dc_id); if (!net_query->dc_id().is_main()) { LOG(ERROR) << msg << " from query to non-main dc " << net_query->dc_id(); @@ -328,4 +314,32 @@ void NetQueryDispatcher::try_fix_migrate(NetQueryPtr &net_query) { } } +void NetQueryDispatcher::set_main_dc_id(int32 new_main_dc_id) { + if (!DcId::is_valid(new_main_dc_id)) { + LOG(ERROR) << "Receive wrong DC " << new_main_dc_id; + return; + } + if (new_main_dc_id == main_dc_id_.load(std::memory_order_relaxed)) { + return; + } + + // Very rare event. Mutex is ok. + std::lock_guard guard(main_dc_id_mutex_); + if (new_main_dc_id == main_dc_id_) { + return; + } + + LOG(INFO) << "Update main DcId from " << main_dc_id_.load(std::memory_order_relaxed) << " to " << new_main_dc_id; + if (is_dc_inited(main_dc_id_.load(std::memory_order_relaxed))) { + send_closure_later(dcs_[main_dc_id_ - 1].main_session_, &SessionMultiProxy::update_main_flag, false); + } + main_dc_id_ = new_main_dc_id; + if (is_dc_inited(main_dc_id_.load(std::memory_order_relaxed))) { + send_closure_later(dcs_[main_dc_id_ - 1].main_session_, &SessionMultiProxy::update_main_flag, true); + } + send_closure_later(dc_auth_manager_, &DcAuthManager::update_main_dc, + DcId::internal(main_dc_id_.load(std::memory_order_relaxed))); + G()->td_db()->get_binlog_pmc()->set("main_dc_id", to_string(main_dc_id_.load(std::memory_order_relaxed))); +} + } // namespace td diff --git a/td/telegram/net/NetQueryDispatcher.h b/td/telegram/net/NetQueryDispatcher.h index 8f78eb5c..78cb18fc 100644 --- a/td/telegram/net/NetQueryDispatcher.h +++ b/td/telegram/net/NetQueryDispatcher.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -56,6 +56,8 @@ class NetQueryDispatcher { return DcId::internal(main_dc_id_.load()); } + void set_main_dc_id(int32 new_main_dc_id); + private: std::atomic stop_flag_{false}; bool need_destroy_auth_key_{false}; diff --git a/td/telegram/net/NetStatsManager.cpp b/td/telegram/net/NetStatsManager.cpp index 22478f58..0c9179f0 100644 --- a/td/telegram/net/NetStatsManager.cpp +++ b/td/telegram/net/NetStatsManager.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetStatsManager.h b/td/telegram/net/NetStatsManager.h index 4a766558..d7a6a4cb 100644 --- a/td/telegram/net/NetStatsManager.h +++ b/td/telegram/net/NetStatsManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/NetType.h b/td/telegram/net/NetType.h index 3a2539f5..8faea2a8 100644 --- a/td/telegram/net/NetType.h +++ b/td/telegram/net/NetType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/Proxy.cpp b/td/telegram/net/Proxy.cpp index 60b4cfe5..db68882f 100644 --- a/td/telegram/net/Proxy.cpp +++ b/td/telegram/net/Proxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/Proxy.h b/td/telegram/net/Proxy.h index 497316c8..a9dca8a4 100644 --- a/td/telegram/net/Proxy.h +++ b/td/telegram/net/Proxy.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/PublicRsaKeyShared.cpp b/td/telegram/net/PublicRsaKeyShared.cpp index 56a97505..5066a8ff 100644 --- a/td/telegram/net/PublicRsaKeyShared.cpp +++ b/td/telegram/net/PublicRsaKeyShared.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" @@ -153,8 +154,7 @@ RSA *PublicRsaKeyShared::get_rsa_locked(int64 fingerprint) { void PublicRsaKeyShared::notify() { auto lock = rw_mutex_.lock_read(); - auto it = std::remove_if(listeners_.begin(), listeners_.end(), [&](auto &listener) { return !listener->notify(); }); - listeners_.erase(it, listeners_.end()); + td::remove_if(listeners_, [&](auto &listener) { return !listener->notify(); }); } } // namespace td diff --git a/td/telegram/net/PublicRsaKeyShared.h b/td/telegram/net/PublicRsaKeyShared.h index 2b5587f4..efab0853 100644 --- a/td/telegram/net/PublicRsaKeyShared.h +++ b/td/telegram/net/PublicRsaKeyShared.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/PublicRsaKeyWatchdog.cpp b/td/telegram/net/PublicRsaKeyWatchdog.cpp index 306b0947..6556647c 100644 --- a/td/telegram/net/PublicRsaKeyWatchdog.cpp +++ b/td/telegram/net/PublicRsaKeyWatchdog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -70,7 +70,7 @@ void PublicRsaKeyWatchdog::loop() { 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::Off, NetQuery::GzipFlag::On, + NetQuery::Type::Common, NetQuery::AuthFlag::On, NetQuery::GzipFlag::On, 60 * 60 * 24), actor_shared(this)); } @@ -98,6 +98,7 @@ void PublicRsaKeyWatchdog::sync(BufferSlice cdn_config_serialized) { return; } cdn_config_ = r_keys.move_as_ok(); + LOG(INFO) << "Receive " << to_string(cdn_config_); for (auto &key : keys_) { sync_key(key); } @@ -114,6 +115,7 @@ void PublicRsaKeyWatchdog::sync_key(std::shared_ptr &key) { LOG(ERROR) << r_rsa.error(); continue; } + LOG(INFO) << "Add CDN " << key->dc_id() << " key with fingerprint " << r_rsa.ok().get_fingerprint(); key->add_rsa(r_rsa.move_as_ok()); } } diff --git a/td/telegram/net/PublicRsaKeyWatchdog.h b/td/telegram/net/PublicRsaKeyWatchdog.h index b1501e49..e417bb50 100644 --- a/td/telegram/net/PublicRsaKeyWatchdog.h +++ b/td/telegram/net/PublicRsaKeyWatchdog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/Session.cpp b/td/telegram/net/Session.cpp index 6c9fde10..d314764f 100644 --- a/td/telegram/net/Session.cpp +++ b/td/telegram/net/Session.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -34,7 +34,6 @@ #include "td/utils/Timer.h" #include "td/utils/tl_parsers.h" -#include #include #include @@ -116,11 +115,11 @@ class GenAuthKeyActor : public Actor { } // namespace detail -Session::Session(unique_ptr callback, std::shared_ptr shared_auth_data, int32 dc_id, - bool is_main, bool use_pfs, bool is_cdn, bool need_destroy, const mtproto::AuthKey &tmp_auth_key, - std::vector server_salts) - : dc_id_(dc_id), is_main_(is_main), is_cdn_(is_cdn) { - VLOG(dc) << "Start connection"; +Session::Session(unique_ptr callback, std::shared_ptr shared_auth_data, int32 raw_dc_id, + int32 dc_id, bool is_main, bool use_pfs, bool is_cdn, bool need_destroy, + const mtproto::AuthKey &tmp_auth_key, std::vector server_salts) + : raw_dc_id_(raw_dc_id), dc_id_(dc_id), is_main_(is_main), is_cdn_(is_cdn) { + VLOG(dc) << "Start connection " << tag("need_destroy", need_destroy); need_destroy_ = need_destroy; if (need_destroy) { use_pfs = false; @@ -142,7 +141,8 @@ Session::Session(unique_ptr callback, std::shared_ptr } while (session_id == 0); auth_data_.set_session_id(session_id); LOG(WARNING) << "Generate new session_id " << session_id << " for " << (use_pfs ? "temp " : "") - << (is_cdn ? "CDN " : "") << "auth key " << auth_data_.get_auth_key().id() << " for DC" << dc_id; + << (is_cdn ? "CDN " : "") << "auth key " << auth_data_.get_auth_key().id() << " for " + << (is_main_ ? "main " : "") << "DC" << dc_id; callback_ = std::shared_ptr(callback.release()); @@ -467,8 +467,7 @@ void Session::on_closed(Status status) { void Session::on_session_created(uint64 unique_id, uint64 first_id) { // TODO: use unique_id // send updatesTooLong to force getDifference - LOG(INFO) << "New session " << unique_id << " created " - << " with first message_id " << 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_; @@ -510,7 +509,7 @@ void Session::on_session_failed(Status status) { } void Session::on_container_sent(uint64 container_id, vector msg_ids) { - auto erase_from = std::remove_if(msg_ids.begin(), msg_ids.end(), [&](uint64 msg_id) { + td::remove_if(msg_ids, [&](uint64 msg_id) { auto it = sent_queries_.find(msg_id); if (it == sent_queries_.end()) { return true; // remove @@ -518,7 +517,6 @@ void Session::on_container_sent(uint64 container_id, vector msg_ids) { it->second.container_id = container_id; return false; }); - msg_ids.erase(erase_from, msg_ids.end()); if (msg_ids.empty()) { return; } @@ -610,16 +608,6 @@ void Session::mark_as_unknown(uint64 id, Query *query) { Status Session::on_message_result_ok(uint64 id, BufferSlice packet, size_t original_size) { // Steal authorization information. // It is a dirty hack, yep. - TlParser parser(packet.as_slice()); - int32 ID = parser.fetch_int(); - if (!parser.get_error()) { - if (ID == telegram_api::auth_authorization::ID) { - LOG(INFO) << "GOT AUTHORIZATION!"; - auth_data_.set_auth_flag(true); - shared_auth_data_->set_auth_key(auth_data_.get_main_auth_key()); - } - } - if (id == 0) { if (is_cdn_) { return Status::Error("Got update from CDN connection"); @@ -627,9 +615,13 @@ Status Session::on_message_result_ok(uint64 id, BufferSlice packet, size_t origi return_query(G()->net_query_creator().create_result(0, std::move(packet))); return Status::OK(); } + + TlParser parser(packet.as_slice()); + int32 ID = parser.fetch_int(); + auto it = sent_queries_.find(id); if (it == sent_queries_.end()) { - LOG(DEBUG) << "DROP result to " << tag("request_id", format::as_hex(id)) << tag("tl", format::as_hex(ID)); + LOG(DEBUG) << "Drop result to " << tag("request_id", format::as_hex(id)) << tag("tl", format::as_hex(ID)); if (packet.size() > 16 * 1024) { dropped_size_ += packet.size(); @@ -642,10 +634,21 @@ Status Session::on_message_result_ok(uint64 id, BufferSlice packet, size_t origi } return Status::OK(); } + auth_data_.on_api_response(); Query *query_ptr = &it->second; VLOG(net_query) << "Return query result " << query_ptr->query; + if (!parser.get_error()) { + if (ID == telegram_api::auth_authorization::ID || ID == telegram_api::auth_loginTokenSuccess::ID) { + if (query_ptr->query->tl_constructor() != telegram_api::auth_importAuthorization::ID) { + G()->net_query_dispatcher().set_main_dc_id(raw_dc_id_); + } + auth_data_.set_auth_flag(true); + shared_auth_data_->set_auth_key(auth_data_.get_main_auth_key()); + } + } + cleanup_container(id, query_ptr); mark_as_known(id, query_ptr); query_ptr->query->on_net_read(original_size); diff --git a/td/telegram/net/Session.h b/td/telegram/net/Session.h index b1edbbce..7036e0af 100644 --- a/td/telegram/net/Session.h +++ b/td/telegram/net/Session.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -65,8 +65,8 @@ class Session final virtual void on_result(NetQueryPtr net_query) = 0; }; - Session(unique_ptr callback, std::shared_ptr shared_auth_data, int32 dc_id, bool is_main, - bool use_pfs, bool is_cdn, bool need_destroy, const mtproto::AuthKey &tmp_auth_key, + Session(unique_ptr callback, std::shared_ptr shared_auth_data, int32 raw_dc_id, int32 dc_id, + bool is_main, bool use_pfs, bool is_cdn, bool need_destroy, const mtproto::AuthKey &tmp_auth_key, std::vector server_salts); void send(NetQueryPtr &&query); void on_network(bool network_flag, uint32 network_generation); @@ -101,6 +101,7 @@ class Session final // Just re-ask answer_id each time we get information about it. // Though mtproto::Connection must ensure delivery of such query. + int32 raw_dc_id_; int32 dc_id_; enum class Mode : int8 { Tcp, Http } mode_ = Mode::Tcp; bool is_main_; diff --git a/td/telegram/net/SessionMultiProxy.cpp b/td/telegram/net/SessionMultiProxy.cpp index d3a905d2..3adb8fdb 100644 --- a/td/telegram/net/SessionMultiProxy.cpp +++ b/td/telegram/net/SessionMultiProxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -111,7 +111,7 @@ void SessionMultiProxy::start_up() { } bool SessionMultiProxy::get_pfs_flag() const { - return use_pfs_; + return use_pfs_ && !is_cdn_; } void SessionMultiProxy::init() { diff --git a/td/telegram/net/SessionMultiProxy.h b/td/telegram/net/SessionMultiProxy.h index 92f0e975..e78c5411 100644 --- a/td/telegram/net/SessionMultiProxy.h +++ b/td/telegram/net/SessionMultiProxy.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/SessionProxy.cpp b/td/telegram/net/SessionProxy.cpp index 3717cf8e..17a11052 100644 --- a/td/telegram/net/SessionProxy.cpp +++ b/td/telegram/net/SessionProxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -192,7 +192,8 @@ void SessionProxy::open_session(bool force) { string name = PSTRING() << "Session" << get_name().substr(Slice("SessionProxy").size()); string hash_string = PSTRING() << name << " " << dc_id.get_raw_id() << " " << allow_media_only_; auto hash = std::hash()(hash_string); - int32 int_dc_id = dc_id.get_raw_id(); + int32 raw_dc_id = dc_id.get_raw_id(); + int32 int_dc_id = raw_dc_id; if (G()->is_test_dc()) { int_dc_id += 10000; } @@ -202,7 +203,7 @@ void SessionProxy::open_session(bool force) { session_ = create_actor( name, make_unique(actor_shared(this, session_generation_), dc_id, allow_media_only_, is_media_, hash), - auth_data_, int_dc_id, is_main_, use_pfs_, is_cdn_, need_destroy_, tmp_auth_key_, server_salts_); + auth_data_, raw_dc_id, int_dc_id, is_main_, use_pfs_, is_cdn_, need_destroy_, tmp_auth_key_, server_salts_); } void SessionProxy::update_auth_key_state() { diff --git a/td/telegram/net/SessionProxy.h b/td/telegram/net/SessionProxy.h index 968359a0..cc9d4dfd 100644 --- a/td/telegram/net/SessionProxy.h +++ b/td/telegram/net/SessionProxy.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/net/TempAuthKeyWatchdog.h b/td/telegram/net/TempAuthKeyWatchdog.h index e7c9681e..d90766a4 100644 --- a/td/telegram/net/TempAuthKeyWatchdog.h +++ b/td/telegram/net/TempAuthKeyWatchdog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,6 +9,7 @@ #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" @@ -110,7 +111,8 @@ 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)))); + auto query = G()->net_query_creator().create(create_storer(telegram_api::auth_dropTempAuthKeys(std::move(ids))), + DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } diff --git a/td/telegram/td_c_client.cpp b/td/telegram/td_c_client.cpp index 18f11296..3aa4a14a 100644 --- a/td/telegram/td_c_client.cpp +++ b/td/telegram/td_c_client.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_c_client.h b/td/telegram/td_c_client.h index b4d69dbf..7ea1014d 100644 --- a/td/telegram/td_c_client.h +++ b/td/telegram/td_c_client.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_emscripten.cpp b/td/telegram/td_emscripten.cpp index 854e6710..0cf00401 100644 --- a/td/telegram/td_emscripten.cpp +++ b/td/telegram/td_emscripten.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_json_client.cpp b/td/telegram/td_json_client.cpp index b3da9eb9..8637ee2e 100644 --- a/td/telegram/td_json_client.cpp +++ b/td/telegram/td_json_client.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_json_client.h b/td/telegram/td_json_client.h index cb07790d..8e276c3c 100644 --- a/td/telegram/td_json_client.h +++ b/td/telegram/td_json_client.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_log.cpp b/td/telegram/td_log.cpp index 5b20bf67..b3803978 100644 --- a/td/telegram/td_log.cpp +++ b/td/telegram/td_log.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/telegram/td_log.h b/td/telegram/td_log.h index 423c5c51..ecdc61f1 100644 --- a/td/telegram/td_log.h +++ b/td/telegram/td_log.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/tl/TlObject.h b/td/tl/TlObject.h index 4871a5bb..f4ebb262 100644 --- a/td/tl/TlObject.h +++ b/td/tl/TlObject.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -186,8 +186,8 @@ using tl_object_ptr = tl::unique_ptr; * \code * auto get_authorization_state_request = td::make_tl_object(); * auto message_text = td::make_tl_object("Hello, world!!!", - * std::vector>()); - * auto send_message_request = td::make_tl_object(chat_id, 0, false, false, nullptr, + * std::vector>()); + * auto send_message_request = td::make_tl_object(chat_id, 0, nullptr, nullptr, * td::make_tl_object(std::move(message_text), false, true)); * \endcode * diff --git a/td/tl/tl_dotnet_object.h b/td/tl/tl_dotnet_object.h index 8c53f629..ea1c8366 100644 --- a/td/tl/tl_dotnet_object.h +++ b/td/tl/tl_dotnet_object.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/tl/tl_jni_object.cpp b/td/tl/tl_jni_object.cpp index 220199ed..640635c3 100644 --- a/td/tl/tl_jni_object.cpp +++ b/td/tl/tl_jni_object.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/tl/tl_jni_object.h b/td/tl/tl_jni_object.h index 15a060b3..31c7fe7c 100644 --- a/td/tl/tl_jni_object.h +++ b/td/tl/tl_jni_object.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/tl/tl_json.h b/td/tl/tl_json.h index 1ff559a3..690b3ce8 100644 --- a/td/tl/tl_json.h +++ b/td/tl/tl_json.h @@ -1,11 +1,13 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once +#include "td/tl/TlObject.h" + #include "td/utils/base64.h" #include "td/utils/common.h" #include "td/utils/format.h" @@ -15,10 +17,6 @@ #include "td/utils/Status.h" #include "td/utils/tl_storers.h" -#include "td/telegram/td_api.h" -#include "td/telegram/td_api.hpp" - -#include #include namespace td { @@ -30,6 +28,7 @@ struct JsonInt64 { inline void to_json(JsonValueScope &jv, const JsonInt64 json_int64) { jv << JsonString(PSLICE() << json_int64.value); } + struct JsonVectorInt64 { const std::vector &value; }; @@ -41,24 +40,6 @@ inline void to_json(JsonValueScope &jv, const JsonVectorInt64 &vec) { } } -template -auto lazy_to_json(JsonValueScope &jv, const T &t) -> decltype(td_api::to_json(jv, t)) { - return td_api::to_json(jv, t); -} - -template -void lazy_to_json(std::reference_wrapper, const T &t) { - UNREACHABLE(); -} - -inline void to_json(JsonValueScope &jv, const td_api::Object &object) { - td_api::downcast_call(const_cast(object), [&jv](const auto &object) { lazy_to_json(jv, object); }); -} -inline void to_json(JsonValueScope &jv, const td_api::Function &object) { - td_api::downcast_call(const_cast(object), - [&jv](const auto &object) { lazy_to_json(jv, object); }); -} - template void to_json(JsonValueScope &jv, const tl_object_ptr &value) { if (value) { @@ -76,20 +57,25 @@ void to_json(JsonValueScope &jv, const std::vector &v) { } } -inline Status from_json(int32 &to, JsonValue &from) { +inline Status from_json(int32 &to, JsonValue from) { if (from.type() != JsonValue::Type::Number && from.type() != JsonValue::Type::String) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected Number, got " << from.type()); } Slice number = from.type() == JsonValue::Type::String ? from.get_string() : from.get_number(); - TRY_RESULT(res, to_integer_safe(number)); - to = res; + TRY_RESULT_ASSIGN(to, to_integer_safe(number)); return Status::OK(); } -inline Status from_json(bool &to, JsonValue &from) { +inline Status from_json(bool &to, JsonValue from) { if (from.type() != JsonValue::Type::Boolean) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } int32 x = 0; - auto status = from_json(x, from); + auto status = from_json(x, std::move(from)); if (status.is_ok()) { to = x != 0; return Status::OK(); @@ -100,50 +86,63 @@ inline Status from_json(bool &to, JsonValue &from) { return Status::OK(); } -inline Status from_json(int64 &to, JsonValue &from) { +inline Status from_json(int64 &to, JsonValue from) { if (from.type() != JsonValue::Type::Number && from.type() != JsonValue::Type::String) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected String or Number, got " << from.type()); } Slice number = from.type() == JsonValue::Type::String ? from.get_string() : from.get_number(); - TRY_RESULT(res, to_integer_safe(number)); - to = res; + TRY_RESULT_ASSIGN(to, to_integer_safe(number)); return Status::OK(); } -inline Status from_json(double &to, JsonValue &from) { +inline Status from_json(double &to, JsonValue from) { if (from.type() != JsonValue::Type::Number) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected Number, got " << from.type()); } to = to_double(from.get_number()); return Status::OK(); } -inline Status from_json(string &to, JsonValue &from) { +inline Status from_json(string &to, JsonValue from) { if (from.type() != JsonValue::Type::String) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected String, got " << from.type()); } to = from.get_string().str(); return Status::OK(); } -inline Status from_json_bytes(string &to, JsonValue &from) { +inline Status from_json_bytes(string &to, JsonValue from) { if (from.type() != JsonValue::Type::String) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected String, got " << from.type()); } - TRY_RESULT(decoded, base64_decode(from.get_string())); - to = std::move(decoded); + TRY_RESULT_ASSIGN(to, base64_decode(from.get_string())); return Status::OK(); } template -Status from_json(std::vector &to, JsonValue &from) { +Status from_json(std::vector &to, JsonValue from) { if (from.type() != JsonValue::Type::Array) { + if (from.type() == JsonValue::Type::Null) { + return Status::OK(); + } return Status::Error(PSLICE() << "Expected Array, got " << from.type()); } to = std::vector(from.get_array().size()); size_t i = 0; for (auto &value : from.get_array()) { - TRY_STATUS(from_json(to[i], value)); + TRY_STATUS(from_json(to[i], std::move(value))); i++; } return Status::OK(); @@ -165,7 +164,7 @@ class DowncastHelper : public T { }; template -std::enable_if_t::value, Status> from_json(tl_object_ptr &to, JsonValue &from) { +std::enable_if_t::value, Status> from_json(tl_object_ptr &to, JsonValue from) { if (from.type() != JsonValue::Type::Object) { if (from.type() == JsonValue::Type::Null) { to = nullptr; @@ -180,8 +179,7 @@ std::enable_if_t::value, Status> from_json(tl_object_p if (constructor_value.type() == JsonValue::Type::Number) { constructor = to_integer(constructor_value.get_number()); } else if (constructor_value.type() == JsonValue::Type::String) { - TRY_RESULT(t_constructor, tl_constructor_from_string(to.get(), constructor_value.get_string().str())); - constructor = t_constructor; + TRY_RESULT_ASSIGN(constructor, tl_constructor_from_string(to.get(), constructor_value.get_string().str())); } else { return Status::Error(PSLICE() << "Expected String or Integer, got " << constructor_value.type()); } @@ -202,7 +200,7 @@ std::enable_if_t::value, Status> from_json(tl_object_p } template -std::enable_if_t::value, Status> from_json(tl_object_ptr &to, JsonValue &from) { +std::enable_if_t::value, Status> from_json(tl_object_ptr &to, JsonValue from) { if (from.type() != JsonValue::Type::Object) { if (from.type() == JsonValue::Type::Null) { to = nullptr; diff --git a/td/tl/tl_object_parse.h b/td/tl/tl_object_parse.h index 6b20e3c6..f61b9b96 100644 --- a/td/tl/tl_object_parse.h +++ b/td/tl/tl_object_parse.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/td/tl/tl_object_store.h b/td/tl/tl_object_store.h index cb11d4dc..40bcfcbf 100644 --- a/td/tl/tl_object_store.h +++ b/td/tl/tl_object_store.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/CMakeLists.txt b/tdactor/CMakeLists.txt index 62acc1ec..8628954b 100644 --- a/tdactor/CMakeLists.txt +++ b/tdactor/CMakeLists.txt @@ -45,8 +45,10 @@ add_library(tdactor STATIC ${TDACTOR_SOURCE}) target_include_directories(tdactor PUBLIC $) target_link_libraries(tdactor PUBLIC tdutils) -add_executable(example example/example.cpp) -target_link_libraries(example PRIVATE tdactor) +if (NOT CMAKE_CROSSCOMPILING) + add_executable(example example/example.cpp) + target_link_libraries(example PRIVATE tdactor) +endif() install(TARGETS tdactor EXPORT TdTargets LIBRARY DESTINATION lib diff --git a/tdactor/example/example.cpp b/tdactor/example/example.cpp index f2b3f024..e02f0ae3 100644 --- a/tdactor/example/example.cpp +++ b/tdactor/example/example.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/Condition.h b/tdactor/td/actor/Condition.h index b9d10f15..15ac8abd 100644 --- a/tdactor/td/actor/Condition.h +++ b/tdactor/td/actor/Condition.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/MultiPromise.cpp b/tdactor/td/actor/MultiPromise.cpp index 2c1ababc..c289e332 100644 --- a/tdactor/td/actor/MultiPromise.cpp +++ b/tdactor/td/actor/MultiPromise.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/MultiPromise.h b/tdactor/td/actor/MultiPromise.h index 86fd801c..d231cafd 100644 --- a/tdactor/td/actor/MultiPromise.h +++ b/tdactor/td/actor/MultiPromise.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/PromiseFuture.h b/tdactor/td/actor/PromiseFuture.h index 850e8814..873a8518 100644 --- a/tdactor/td/actor/PromiseFuture.h +++ b/tdactor/td/actor/PromiseFuture.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/SchedulerLocalStorage.h b/tdactor/td/actor/SchedulerLocalStorage.h index 46d15f52..16c0b6a9 100644 --- a/tdactor/td/actor/SchedulerLocalStorage.h +++ b/tdactor/td/actor/SchedulerLocalStorage.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/SignalSlot.h b/tdactor/td/actor/SignalSlot.h index 8c081ec3..e7cc7e16 100644 --- a/tdactor/td/actor/SignalSlot.h +++ b/tdactor/td/actor/SignalSlot.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/SleepActor.h b/tdactor/td/actor/SleepActor.h index 1d53d393..d0e2ea22 100644 --- a/tdactor/td/actor/SleepActor.h +++ b/tdactor/td/actor/SleepActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/Timeout.cpp b/tdactor/td/actor/Timeout.cpp index e1513edc..2f896965 100644 --- a/tdactor/td/actor/Timeout.cpp +++ b/tdactor/td/actor/Timeout.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/Timeout.h b/tdactor/td/actor/Timeout.h index 52f4e109..99360273 100644 --- a/tdactor/td/actor/Timeout.h +++ b/tdactor/td/actor/Timeout.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -38,6 +38,9 @@ class Timeout final : public Actor { void set_timeout_in(double timeout) { Actor::set_timeout_in(timeout); } + void set_timeout_at(double timeout) { + Actor::set_timeout_at(timeout); + } void cancel_timeout() { if (has_timeout()) { Actor::cancel_timeout(); @@ -52,10 +55,6 @@ class Timeout final : public Actor { Callback callback_{}; Data data_{}; - void set_timeout_at(double timeout) { - Actor::set_timeout_at(timeout); - } - void timeout_expired() override { CHECK(!has_timeout()); CHECK(callback_ != Callback()); diff --git a/tdactor/td/actor/actor.h b/tdactor/td/actor/actor.h index df8d844c..c5761d6a 100644 --- a/tdactor/td/actor/actor.h +++ b/tdactor/td/actor/actor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Actor-decl.h b/tdactor/td/actor/impl/Actor-decl.h index a46cd6e8..7086d23e 100644 --- a/tdactor/td/actor/impl/Actor-decl.h +++ b/tdactor/td/actor/impl/Actor-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Actor.h b/tdactor/td/actor/impl/Actor.h index 3c135ff4..cf7475ae 100644 --- a/tdactor/td/actor/impl/Actor.h +++ b/tdactor/td/actor/impl/Actor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ActorId-decl.h b/tdactor/td/actor/impl/ActorId-decl.h index 0a420ae9..fbfca04f 100644 --- a/tdactor/td/actor/impl/ActorId-decl.h +++ b/tdactor/td/actor/impl/ActorId-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ActorId.h b/tdactor/td/actor/impl/ActorId.h index 716d0a09..d04afb1b 100644 --- a/tdactor/td/actor/impl/ActorId.h +++ b/tdactor/td/actor/impl/ActorId.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ActorInfo-decl.h b/tdactor/td/actor/impl/ActorInfo-decl.h index c4746ef3..1c6c59e5 100644 --- a/tdactor/td/actor/impl/ActorInfo-decl.h +++ b/tdactor/td/actor/impl/ActorInfo-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ActorInfo.h b/tdactor/td/actor/impl/ActorInfo.h index 9654cbb2..ef0d5f20 100644 --- a/tdactor/td/actor/impl/ActorInfo.h +++ b/tdactor/td/actor/impl/ActorInfo.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ConcurrentScheduler.cpp b/tdactor/td/actor/impl/ConcurrentScheduler.cpp index aec2c9ec..cab7841b 100644 --- a/tdactor/td/actor/impl/ConcurrentScheduler.cpp +++ b/tdactor/td/actor/impl/ConcurrentScheduler.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/ConcurrentScheduler.h b/tdactor/td/actor/impl/ConcurrentScheduler.h index 02f8c22a..e0c14db9 100644 --- a/tdactor/td/actor/impl/ConcurrentScheduler.h +++ b/tdactor/td/actor/impl/ConcurrentScheduler.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Event.h b/tdactor/td/actor/impl/Event.h index 963815af..347b9c4c 100644 --- a/tdactor/td/actor/impl/Event.h +++ b/tdactor/td/actor/impl/Event.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/EventFull-decl.h b/tdactor/td/actor/impl/EventFull-decl.h index fc9abcea..fd8deef1 100644 --- a/tdactor/td/actor/impl/EventFull-decl.h +++ b/tdactor/td/actor/impl/EventFull-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/EventFull.h b/tdactor/td/actor/impl/EventFull.h index 4a010100..8960118a 100644 --- a/tdactor/td/actor/impl/EventFull.h +++ b/tdactor/td/actor/impl/EventFull.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Scheduler-decl.h b/tdactor/td/actor/impl/Scheduler-decl.h index f8c86318..f44ab8e8 100644 --- a/tdactor/td/actor/impl/Scheduler-decl.h +++ b/tdactor/td/actor/impl/Scheduler-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Scheduler.cpp b/tdactor/td/actor/impl/Scheduler.cpp index b84c5722..1c653507 100644 --- a/tdactor/td/actor/impl/Scheduler.cpp +++ b/tdactor/td/actor/impl/Scheduler.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/td/actor/impl/Scheduler.h b/tdactor/td/actor/impl/Scheduler.h index 0dd5ea1d..c72b158a 100644 --- a/tdactor/td/actor/impl/Scheduler.h +++ b/tdactor/td/actor/impl/Scheduler.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/test/actors_bugs.cpp b/tdactor/test/actors_bugs.cpp index 54377070..678611f3 100644 --- a/tdactor/test/actors_bugs.cpp +++ b/tdactor/test/actors_bugs.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/test/actors_main.cpp b/tdactor/test/actors_main.cpp index 5ba3b198..ed3c3185 100644 --- a/tdactor/test/actors_main.cpp +++ b/tdactor/test/actors_main.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/test/actors_simple.cpp b/tdactor/test/actors_simple.cpp index 17bc7366..4204d60e 100644 --- a/tdactor/test/actors_simple.cpp +++ b/tdactor/test/actors_simple.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdactor/test/actors_workers.cpp b/tdactor/test/actors_workers.cpp index efc9d740..3c54f369 100644 --- a/tdactor/test/actors_workers.cpp +++ b/tdactor/test/actors_workers.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/BinlogKeyValue.h b/tddb/td/db/BinlogKeyValue.h index 8649e7ba..db1ed869 100644 --- a/tddb/td/db/BinlogKeyValue.h +++ b/tddb/td/db/BinlogKeyValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,6 +10,7 @@ #include "td/db/binlog/Binlog.h" #include "td/db/binlog/BinlogEvent.h" +#include "td/db/DbKey.h" #include "td/db/KeyValueSyncInterface.h" #include "td/utils/buffer.h" diff --git a/tddb/td/db/DbKey.h b/tddb/td/db/DbKey.h index 53800834..e7a55cbf 100644 --- a/tddb/td/db/DbKey.h +++ b/tddb/td/db/DbKey.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/KeyValueSyncInterface.h b/tddb/td/db/KeyValueSyncInterface.h index 337c3da6..272adbcf 100644 --- a/tddb/td/db/KeyValueSyncInterface.h +++ b/tddb/td/db/KeyValueSyncInterface.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/Pmc.h b/tddb/td/db/Pmc.h index acfcb2f9..834fccec 100644 --- a/tddb/td/db/Pmc.h +++ b/tddb/td/db/Pmc.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SeqKeyValue.h b/tddb/td/db/SeqKeyValue.h index 1441911e..de0dd519 100644 --- a/tddb/td/db/SeqKeyValue.h +++ b/tddb/td/db/SeqKeyValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteConnectionSafe.cpp b/tddb/td/db/SqliteConnectionSafe.cpp index 547bf20c..6977d3e4 100644 --- a/tddb/td/db/SqliteConnectionSafe.cpp +++ b/tddb/td/db/SqliteConnectionSafe.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteConnectionSafe.h b/tddb/td/db/SqliteConnectionSafe.h index b53b3691..c2eae816 100644 --- a/tddb/td/db/SqliteConnectionSafe.h +++ b/tddb/td/db/SqliteConnectionSafe.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteDb.cpp b/tddb/td/db/SqliteDb.cpp index 899e74c1..f17956da 100644 --- a/tddb/td/db/SqliteDb.cpp +++ b/tddb/td/db/SqliteDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteDb.h b/tddb/td/db/SqliteDb.h index 474a5900..057a5e4a 100644 --- a/tddb/td/db/SqliteDb.h +++ b/tddb/td/db/SqliteDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteKeyValue.cpp b/tddb/td/db/SqliteKeyValue.cpp index cfe0a234..b0cf00a1 100644 --- a/tddb/td/db/SqliteKeyValue.cpp +++ b/tddb/td/db/SqliteKeyValue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -31,30 +31,21 @@ Status SqliteKeyValue::init_with_connection(SqliteDb connection, string table_na table_name_ = std::move(table_name); TRY_STATUS(init(db_, table_name_)); - TRY_RESULT(set_stmt, db_.get_statement(PSLICE() << "REPLACE INTO " << table_name_ << " (k, v) VALUES (?1, ?2)")); - set_stmt_ = std::move(set_stmt); - TRY_RESULT(get_stmt, db_.get_statement(PSLICE() << "SELECT v FROM " << table_name_ << " WHERE k = ?1")); - get_stmt_ = std::move(get_stmt); - TRY_RESULT(erase_stmt, db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE k = ?1")); - erase_stmt_ = std::move(erase_stmt); - TRY_RESULT(get_all_stmt, db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_)); - get_all_stmt_ = std::move(get_all_stmt); + TRY_RESULT_ASSIGN(set_stmt_, + db_.get_statement(PSLICE() << "REPLACE INTO " << table_name_ << " (k, v) VALUES (?1, ?2)")); + TRY_RESULT_ASSIGN(get_stmt_, db_.get_statement(PSLICE() << "SELECT v FROM " << table_name_ << " WHERE k = ?1")); + TRY_RESULT_ASSIGN(erase_stmt_, db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE k = ?1")); + TRY_RESULT_ASSIGN(get_all_stmt_, db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_)); - TRY_RESULT(erase_by_prefix_stmt, - db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); - erase_by_prefix_stmt_ = std::move(erase_by_prefix_stmt); + TRY_RESULT_ASSIGN(erase_by_prefix_stmt_, + db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); + TRY_RESULT_ASSIGN(erase_by_prefix_rare_stmt_, + db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k")); - TRY_RESULT(erase_by_prefix_rare_stmt, - db_.get_statement(PSLICE() << "DELETE FROM " << table_name_ << " WHERE ?1 <= k")); - erase_by_prefix_rare_stmt_ = std::move(erase_by_prefix_rare_stmt); - - TRY_RESULT(get_by_prefix_stmt, - db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); - get_by_prefix_stmt_ = std::move(get_by_prefix_stmt); - - TRY_RESULT(get_by_prefix_rare_stmt, - db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k")); - get_by_prefix_rare_stmt_ = std::move(get_by_prefix_rare_stmt); + TRY_RESULT_ASSIGN(get_by_prefix_stmt_, + db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k AND k < ?2")); + TRY_RESULT_ASSIGN(get_by_prefix_rare_stmt_, + db_.get_statement(PSLICE() << "SELECT k, v FROM " << table_name_ << " WHERE ?1 <= k")); init_guard.dismiss(); return Status::OK(); diff --git a/tddb/td/db/SqliteKeyValue.h b/tddb/td/db/SqliteKeyValue.h index d027d422..b12c3a59 100644 --- a/tddb/td/db/SqliteKeyValue.h +++ b/tddb/td/db/SqliteKeyValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteKeyValueAsync.cpp b/tddb/td/db/SqliteKeyValueAsync.cpp index 315ca494..15af7201 100644 --- a/tddb/td/db/SqliteKeyValueAsync.cpp +++ b/tddb/td/db/SqliteKeyValueAsync.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteKeyValueAsync.h b/tddb/td/db/SqliteKeyValueAsync.h index 0d9015a0..62293c82 100644 --- a/tddb/td/db/SqliteKeyValueAsync.h +++ b/tddb/td/db/SqliteKeyValueAsync.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteKeyValueSafe.h b/tddb/td/db/SqliteKeyValueSafe.h index 81b63af4..cf727464 100644 --- a/tddb/td/db/SqliteKeyValueSafe.h +++ b/tddb/td/db/SqliteKeyValueSafe.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteStatement.cpp b/tddb/td/db/SqliteStatement.cpp index 3873ef08..fd21752a 100644 --- a/tddb/td/db/SqliteStatement.cpp +++ b/tddb/td/db/SqliteStatement.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/SqliteStatement.h b/tddb/td/db/SqliteStatement.h index ecbac9e7..f25cda67 100644 --- a/tddb/td/db/SqliteStatement.h +++ b/tddb/td/db/SqliteStatement.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/TsSeqKeyValue.h b/tddb/td/db/TsSeqKeyValue.h index 99e8a4cd..b940702c 100644 --- a/tddb/td/db/TsSeqKeyValue.h +++ b/tddb/td/db/TsSeqKeyValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/Binlog.cpp b/tddb/td/db/binlog/Binlog.cpp index aa66df98..c717fbb3 100644 --- a/tddb/td/db/binlog/Binlog.cpp +++ b/tddb/td/db/binlog/Binlog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/Binlog.h b/tddb/td/db/binlog/Binlog.h index 99a7c35e..99b74e81 100644 --- a/tddb/td/db/binlog/Binlog.h +++ b/tddb/td/db/binlog/Binlog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/BinlogEvent.cpp b/tddb/td/db/binlog/BinlogEvent.cpp index 5336870d..c4a81993 100644 --- a/tddb/td/db/binlog/BinlogEvent.cpp +++ b/tddb/td/db/binlog/BinlogEvent.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/BinlogEvent.h b/tddb/td/db/binlog/BinlogEvent.h index 120f9e74..038c8cd9 100644 --- a/tddb/td/db/binlog/BinlogEvent.h +++ b/tddb/td/db/binlog/BinlogEvent.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/BinlogHelper.h b/tddb/td/db/binlog/BinlogHelper.h index 04399e09..480afc2a 100644 --- a/tddb/td/db/binlog/BinlogHelper.h +++ b/tddb/td/db/binlog/BinlogHelper.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/BinlogInterface.h b/tddb/td/db/binlog/BinlogInterface.h index f5555777..7c81eb68 100644 --- a/tddb/td/db/binlog/BinlogInterface.h +++ b/tddb/td/db/binlog/BinlogInterface.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/ConcurrentBinlog.cpp b/tddb/td/db/binlog/ConcurrentBinlog.cpp index 56c74f49..e4b11b45 100644 --- a/tddb/td/db/binlog/ConcurrentBinlog.cpp +++ b/tddb/td/db/binlog/ConcurrentBinlog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/ConcurrentBinlog.h b/tddb/td/db/binlog/ConcurrentBinlog.h index 6cac4139..730bfd63 100644 --- a/tddb/td/db/binlog/ConcurrentBinlog.h +++ b/tddb/td/db/binlog/ConcurrentBinlog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +11,7 @@ #include "td/db/binlog/Binlog.h" #include "td/db/binlog/BinlogInterface.h" +#include "td/db/DbKey.h" #include "td/utils/buffer.h" #include "td/utils/common.h" diff --git a/tddb/td/db/binlog/binlog_dump.cpp b/tddb/td/db/binlog/binlog_dump.cpp index 50c1407d..0eecfcca 100644 --- a/tddb/td/db/binlog/binlog_dump.cpp +++ b/tddb/td/db/binlog/binlog_dump.cpp @@ -1,11 +1,13 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/db/binlog/Binlog.h" +#include "td/db/DbKey.h" + #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" diff --git a/tddb/td/db/binlog/detail/BinlogEventsBuffer.cpp b/tddb/td/db/binlog/detail/BinlogEventsBuffer.cpp index 606f8ffc..915a612f 100644 --- a/tddb/td/db/binlog/detail/BinlogEventsBuffer.cpp +++ b/tddb/td/db/binlog/detail/BinlogEventsBuffer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,6 +10,7 @@ namespace td { namespace detail { + void BinlogEventsBuffer::add_event(BinlogEvent &&event) { total_events_++; if ((event.flags_ & BinlogEvent::Flags::Partial) == 0) { @@ -26,14 +27,17 @@ void BinlogEventsBuffer::add_event(BinlogEvent &&event) { size_ += event.size_; events_.push_back(std::move(event)); } + bool BinlogEventsBuffer::need_flush() const { return total_events_ > 5000 || ids_.size() > 100; } + void BinlogEventsBuffer::clear() { ids_.clear(); events_.clear(); total_events_ = 0; size_ = 0; } + } // namespace detail } // namespace td diff --git a/tddb/td/db/binlog/detail/BinlogEventsBuffer.h b/tddb/td/db/binlog/detail/BinlogEventsBuffer.h index 35fa1906..382622ce 100644 --- a/tddb/td/db/binlog/detail/BinlogEventsBuffer.h +++ b/tddb/td/db/binlog/detail/BinlogEventsBuffer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +12,7 @@ namespace td { namespace detail { + class BinlogEventsBuffer { public: void add_event(BinlogEvent &&event); @@ -45,5 +46,6 @@ class BinlogEventsBuffer { void do_event(BinlogEvent &&event); void clear(); }; + } // namespace detail } // namespace td diff --git a/tddb/td/db/binlog/detail/BinlogEventsProcessor.cpp b/tddb/td/db/binlog/detail/BinlogEventsProcessor.cpp index b9056d98..f8eb41f9 100644 --- a/tddb/td/db/binlog/detail/BinlogEventsProcessor.cpp +++ b/tddb/td/db/binlog/detail/BinlogEventsProcessor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/binlog/detail/BinlogEventsProcessor.h b/tddb/td/db/binlog/detail/BinlogEventsProcessor.h index 0d3417d3..c5a16dda 100644 --- a/tddb/td/db/binlog/detail/BinlogEventsProcessor.h +++ b/tddb/td/db/binlog/detail/BinlogEventsProcessor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,6 +14,7 @@ namespace td { namespace detail { + class BinlogEventsProcessor { public: Status add_event(BinlogEvent &&event) TD_WARN_UNUSED_RESULT { @@ -54,5 +55,6 @@ class BinlogEventsProcessor { Status do_event(BinlogEvent &&event); void compactify(); }; + } // namespace detail } // namespace td diff --git a/tddb/td/db/detail/RawSqliteDb.cpp b/tddb/td/db/detail/RawSqliteDb.cpp index c778a153..3199ad40 100644 --- a/tddb/td/db/detail/RawSqliteDb.cpp +++ b/tddb/td/db/detail/RawSqliteDb.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tddb/td/db/detail/RawSqliteDb.h b/tddb/td/db/detail/RawSqliteDb.h index a9b8cc4d..02941d40 100644 --- a/tddb/td/db/detail/RawSqliteDb.h +++ b/tddb/td/db/detail/RawSqliteDb.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/GetHostByNameActor.cpp b/tdnet/td/net/GetHostByNameActor.cpp index 338f98dc..8f331c7a 100644 --- a/tdnet/td/net/GetHostByNameActor.cpp +++ b/tdnet/td/net/GetHostByNameActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/GetHostByNameActor.h b/tdnet/td/net/GetHostByNameActor.h index 7faa8fd0..1eda69de 100644 --- a/tdnet/td/net/GetHostByNameActor.h +++ b/tdnet/td/net/GetHostByNameActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpChunkedByteFlow.cpp b/tdnet/td/net/HttpChunkedByteFlow.cpp index 96942b7b..a13d5df3 100644 --- a/tdnet/td/net/HttpChunkedByteFlow.cpp +++ b/tdnet/td/net/HttpChunkedByteFlow.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpChunkedByteFlow.h b/tdnet/td/net/HttpChunkedByteFlow.h index 76a6625a..aff6daa2 100644 --- a/tdnet/td/net/HttpChunkedByteFlow.h +++ b/tdnet/td/net/HttpChunkedByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpConnectionBase.cpp b/tdnet/td/net/HttpConnectionBase.cpp index f0e22242..698d8bc1 100644 --- a/tdnet/td/net/HttpConnectionBase.cpp +++ b/tdnet/td/net/HttpConnectionBase.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpConnectionBase.h b/tdnet/td/net/HttpConnectionBase.h index ccf968c8..296d342e 100644 --- a/tdnet/td/net/HttpConnectionBase.h +++ b/tdnet/td/net/HttpConnectionBase.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpContentLengthByteFlow.cpp b/tdnet/td/net/HttpContentLengthByteFlow.cpp index c36981ac..cfd31d67 100644 --- a/tdnet/td/net/HttpContentLengthByteFlow.cpp +++ b/tdnet/td/net/HttpContentLengthByteFlow.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpContentLengthByteFlow.h b/tdnet/td/net/HttpContentLengthByteFlow.h index a0514981..1c4129c7 100644 --- a/tdnet/td/net/HttpContentLengthByteFlow.h +++ b/tdnet/td/net/HttpContentLengthByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpFile.cpp b/tdnet/td/net/HttpFile.cpp index f372ca94..27ec08c5 100644 --- a/tdnet/td/net/HttpFile.cpp +++ b/tdnet/td/net/HttpFile.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpFile.h b/tdnet/td/net/HttpFile.h index ed65decc..3de6898c 100644 --- a/tdnet/td/net/HttpFile.h +++ b/tdnet/td/net/HttpFile.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpHeaderCreator.h b/tdnet/td/net/HttpHeaderCreator.h index 47fae175..3e722751 100644 --- a/tdnet/td/net/HttpHeaderCreator.h +++ b/tdnet/td/net/HttpHeaderCreator.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpInboundConnection.cpp b/tdnet/td/net/HttpInboundConnection.cpp index ea0207c1..f457e260 100644 --- a/tdnet/td/net/HttpInboundConnection.cpp +++ b/tdnet/td/net/HttpInboundConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpInboundConnection.h b/tdnet/td/net/HttpInboundConnection.h index 1c07f586..d9ccf224 100644 --- a/tdnet/td/net/HttpInboundConnection.h +++ b/tdnet/td/net/HttpInboundConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpOutboundConnection.cpp b/tdnet/td/net/HttpOutboundConnection.cpp index 0e1b1990..01fbb59e 100644 --- a/tdnet/td/net/HttpOutboundConnection.cpp +++ b/tdnet/td/net/HttpOutboundConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpOutboundConnection.h b/tdnet/td/net/HttpOutboundConnection.h index 875fb1b9..732235c2 100644 --- a/tdnet/td/net/HttpOutboundConnection.h +++ b/tdnet/td/net/HttpOutboundConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpProxy.cpp b/tdnet/td/net/HttpProxy.cpp index eb6dad31..b604e2e3 100644 --- a/tdnet/td/net/HttpProxy.cpp +++ b/tdnet/td/net/HttpProxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpProxy.h b/tdnet/td/net/HttpProxy.h index 4bc56fb5..bba52db8 100644 --- a/tdnet/td/net/HttpProxy.h +++ b/tdnet/td/net/HttpProxy.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpQuery.cpp b/tdnet/td/net/HttpQuery.cpp index 0d7a762d..d16d5ad5 100644 --- a/tdnet/td/net/HttpQuery.cpp +++ b/tdnet/td/net/HttpQuery.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpQuery.h b/tdnet/td/net/HttpQuery.h index b2483208..b037d47d 100644 --- a/tdnet/td/net/HttpQuery.h +++ b/tdnet/td/net/HttpQuery.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/HttpReader.cpp b/tdnet/td/net/HttpReader.cpp index 223bebdc..55202bd4 100644 --- a/tdnet/td/net/HttpReader.cpp +++ b/tdnet/td/net/HttpReader.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -590,6 +590,18 @@ Status HttpReader::parse_json_parameters(MutableSlice parameters) { Parser parser(parameters); parser.skip_whitespaces(); + if (parser.peek_char() == '"') { + auto r_value = json_string_decode(parser); + if (r_value.is_error()) { + return Status::Error(400, PSLICE() << "Bad Request: can't parse string content: " << r_value.error().message()); + } + if (!parser.empty()) { + return Status::Error(400, "Bad Request: extra data after string"); + } + query_->container_.emplace_back(BufferSlice("content")); + query_->args_.emplace_back(query_->container_.back().as_slice(), r_value.move_as_ok()); + return Status::OK(); + } parser.skip('{'); if (parser.status().is_error()) { return Status::Error(400, "Bad Request: JSON object expected"); diff --git a/tdnet/td/net/HttpReader.h b/tdnet/td/net/HttpReader.h index 23c877c5..7e1384d5 100644 --- a/tdnet/td/net/HttpReader.h +++ b/tdnet/td/net/HttpReader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/NetStats.h b/tdnet/td/net/NetStats.h index 84267c59..90153edd 100644 --- a/tdnet/td/net/NetStats.h +++ b/tdnet/td/net/NetStats.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/Socks5.cpp b/tdnet/td/net/Socks5.cpp index 0115dcaf..8dba5f40 100644 --- a/tdnet/td/net/Socks5.cpp +++ b/tdnet/td/net/Socks5.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -40,7 +40,7 @@ Status Socks5::wait_greeting_response() { auto buffer_slice = buf.read_as_buffer_slice(2); auto slice = buffer_slice.as_slice(); if (slice[0] != '\x05') { - return Status::Error(PSLICE() << "Unsupported socks protocol version " << int(slice[0])); + return Status::Error(PSLICE() << "Unsupported socks protocol version " << static_cast(slice[0])); } auto authentication_method = slice[1]; if (authentication_method == '\0') { @@ -83,7 +83,8 @@ Status Socks5::wait_password_response() { auto buffer_slice = buf.read_as_buffer_slice(2); auto slice = buffer_slice.as_slice(); if (slice[0] != '\x01') { - return Status::Error(PSLICE() << "Unsupported socks subnegotiation protocol version " << int(slice[0])); + return Status::Error(PSLICE() << "Unsupported socks subnegotiation protocol version " + << static_cast(slice[0])); } if (slice[1] != '\x00') { return Status::Error("Wrong username or password"); diff --git a/tdnet/td/net/Socks5.h b/tdnet/td/net/Socks5.h index d30b81b8..660261f7 100644 --- a/tdnet/td/net/Socks5.h +++ b/tdnet/td/net/Socks5.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/SslStream.cpp b/tdnet/td/net/SslStream.cpp index 388d4248..36636104 100644 --- a/tdnet/td/net/SslStream.cpp +++ b/tdnet/td/net/SslStream.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/SslStream.h b/tdnet/td/net/SslStream.h index e4432057..d5bb6752 100644 --- a/tdnet/td/net/SslStream.h +++ b/tdnet/td/net/SslStream.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/TcpListener.cpp b/tdnet/td/net/TcpListener.cpp index bca52b58..8bbf00af 100644 --- a/tdnet/td/net/TcpListener.cpp +++ b/tdnet/td/net/TcpListener.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -30,7 +30,6 @@ void TcpListener::start_up() { } void TcpListener::tear_down() { - LOG(ERROR) << "TcpListener closed"; if (!server_fd_.empty()) { Scheduler::unsubscribe_before_close(server_fd_.get_poll_info().get_pollable_fd_ref()); server_fd_.close(); @@ -53,7 +52,6 @@ void TcpListener::loop() { } if (can_close(server_fd_)) { - LOG(ERROR) << "HELLO!"; stop(); } } diff --git a/tdnet/td/net/TcpListener.h b/tdnet/td/net/TcpListener.h index bd7c5021..dfbed9a5 100644 --- a/tdnet/td/net/TcpListener.h +++ b/tdnet/td/net/TcpListener.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/TransparentProxy.cpp b/tdnet/td/net/TransparentProxy.cpp index 3a6246e5..ed38b9a5 100644 --- a/tdnet/td/net/TransparentProxy.cpp +++ b/tdnet/td/net/TransparentProxy.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/TransparentProxy.h b/tdnet/td/net/TransparentProxy.h index f3295e2f..2a745b2c 100644 --- a/tdnet/td/net/TransparentProxy.h +++ b/tdnet/td/net/TransparentProxy.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdnet/td/net/Wget.cpp b/tdnet/td/net/Wget.cpp index 2965c3b4..f7a8b876 100644 --- a/tdnet/td/net/Wget.cpp +++ b/tdnet/td/net/Wget.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -23,23 +23,33 @@ namespace td { Wget::Wget(Promise> promise, string url, std::vector> headers, - int32 timeout_in, int32 ttl, bool prefer_ipv6, SslStream::VerifyPeer verify_peer) + int32 timeout_in, int32 ttl, bool prefer_ipv6, SslStream::VerifyPeer verify_peer, string content, + string content_type) : promise_(std::move(promise)) , input_url_(std::move(url)) , headers_(std::move(headers)) , timeout_in_(timeout_in) , ttl_(ttl) , prefer_ipv6_(prefer_ipv6) - , verify_peer_(verify_peer) { + , verify_peer_(verify_peer) + , content_(std::move(content)) + , content_type_(std::move(content_type)) { } Status Wget::try_init() { TRY_RESULT(url, parse_url(input_url_)); - TRY_RESULT(ascii_host, idn_to_ascii(url.host_)); - url.host_ = std::move(ascii_host); + TRY_RESULT_ASSIGN(url.host_, idn_to_ascii(url.host_)); HttpHeaderCreator hc; - hc.init_get(url.query_); + if (content_.empty()) { + hc.init_get(url.query_); + } else { + hc.init_post(url.query_); + hc.set_content_size(content_.size()); + if (!content_type_.empty()) { + hc.set_content_type(content_type_); + } + } bool was_host = false; bool was_accept_encoding = false; for (auto &header : headers_) { @@ -58,7 +68,7 @@ Status Wget::try_init() { if (!was_accept_encoding) { hc.add_header("Accept-Encoding", "gzip, deflate"); } - TRY_RESULT(header, hc.finish()); + TRY_RESULT(header, hc.finish(content_)); IPAddress addr; TRY_STATUS(addr.init_host_port(url.host_, url.port_, prefer_ipv6_)); diff --git a/tdnet/td/net/Wget.h b/tdnet/td/net/Wget.h index edf46779..bd180675 100644 --- a/tdnet/td/net/Wget.h +++ b/tdnet/td/net/Wget.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -24,7 +24,8 @@ class Wget : public HttpOutboundConnection::Callback { public: explicit Wget(Promise> promise, string url, std::vector> headers = {}, int32 timeout_in = 10, int32 ttl = 3, bool prefer_ipv6 = false, - SslStream::VerifyPeer verify_peer = SslStream::VerifyPeer::On); + SslStream::VerifyPeer verify_peer = SslStream::VerifyPeer::On, string content = {}, + string content_type = {}); private: Status try_init(); @@ -46,6 +47,8 @@ class Wget : public HttpOutboundConnection::Callback { int32 ttl_; bool prefer_ipv6_ = false; SslStream::VerifyPeer verify_peer_; + string content_; + string content_type_; }; } // namespace td diff --git a/tdtl/td/tl/tl_config.cpp b/tdtl/td/tl/tl_config.cpp index fe4bff9c..bfe03875 100644 --- a/tdtl/td/tl/tl_config.cpp +++ b/tdtl/td/tl/tl_config.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_config.h b/tdtl/td/tl/tl_config.h index 40ab9d4d..afc3cb44 100644 --- a/tdtl/td/tl/tl_config.h +++ b/tdtl/td/tl/tl_config.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_core.cpp b/tdtl/td/tl/tl_core.cpp index b9511339..9e1c2bf6 100644 --- a/tdtl/td/tl/tl_core.cpp +++ b/tdtl/td/tl/tl_core.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_core.h b/tdtl/td/tl/tl_core.h index a6b27901..51ca49c5 100644 --- a/tdtl/td/tl/tl_core.h +++ b/tdtl/td/tl/tl_core.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_file_outputer.cpp b/tdtl/td/tl/tl_file_outputer.cpp index b83fa424..d27574a0 100644 --- a/tdtl/td/tl/tl_file_outputer.cpp +++ b/tdtl/td/tl/tl_file_outputer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_file_outputer.h b/tdtl/td/tl/tl_file_outputer.h index cfd0f9a9..d58b658a 100644 --- a/tdtl/td/tl/tl_file_outputer.h +++ b/tdtl/td/tl/tl_file_outputer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_file_utils.cpp b/tdtl/td/tl/tl_file_utils.cpp index 9c3f7344..b31a8d8b 100644 --- a/tdtl/td/tl/tl_file_utils.cpp +++ b/tdtl/td/tl/tl_file_utils.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_file_utils.h b/tdtl/td/tl/tl_file_utils.h index cdc1c38d..0a3537e8 100644 --- a/tdtl/td/tl/tl_file_utils.h +++ b/tdtl/td/tl/tl_file_utils.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_generate.cpp b/tdtl/td/tl/tl_generate.cpp index aa107f1f..71ef03c8 100644 --- a/tdtl/td/tl/tl_generate.cpp +++ b/tdtl/td/tl/tl_generate.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,26 +53,26 @@ static bool is_reachable_for_storer(int storer_type, const std::string &name, static void write_class_constructor(tl_outputer &out, const tl_combinator *t, const std::string &class_name, bool is_default, const TL_writer &w) { // std::fprintf(stderr, "Gen constructor %s\n", class_name.c_str()); - int fields_num = 0; + int field_count = 0; for (std::size_t i = 0; i < t->args.size(); i++) { - fields_num += !w.gen_constructor_parameter(0, class_name, t->args[i], is_default).empty(); + field_count += !w.gen_constructor_parameter(field_count, class_name, t->args[i], is_default).empty(); } - out.append(w.gen_constructor_begin(fields_num, class_name, is_default)); + out.append(w.gen_constructor_begin(field_count, class_name, is_default)); int field_num = 0; for (std::size_t i = 0; i < t->args.size(); i++) { std::string parameter_init = w.gen_constructor_parameter(field_num, class_name, t->args[i], is_default); - if (parameter_init.size()) { + if (!parameter_init.empty()) { out.append(parameter_init); field_num++; } } - assert(field_num == fields_num); + assert(field_num == field_count); field_num = 0; for (std::size_t i = 0; i < t->args.size(); i++) { std::string field_init = w.gen_constructor_field_init(field_num, class_name, t->args[i], is_default); - if (field_init.size()) { + if (!field_init.empty()) { out.append(field_init); field_num++; } @@ -85,19 +85,20 @@ static void write_function_fetch(tl_outputer &out, const std::string &parser_nam const std::string &class_name, const std::set &request_types, const std::set &result_types, const TL_writer &w) { // std::fprintf(stderr, "Write function fetch %s\n", class_name.c_str()); - std::vector vars(t->var_count); int parser_type = w.get_parser_type(t, parser_name); if (!is_reachable_for_parser(parser_type, t->name, request_types, result_types, w)) { return; } - out.append(w.gen_fetch_function_begin(parser_name, class_name, class_name, 0, vars, parser_type)); + std::vector vars(t->var_count); + out.append(w.gen_fetch_function_begin(parser_name, class_name, class_name, 0, static_cast(t->args.size()), vars, + parser_type)); out.append(w.gen_vars(t, NULL, vars)); int field_num = 0; for (std::size_t i = 0; i < t->args.size(); i++) { std::string field_fetch = w.gen_field_fetch(field_num, t->args[i], vars, false, parser_type); - if (field_fetch.size()) { + if (!field_fetch.empty()) { out.append(field_fetch); field_num++; } @@ -167,22 +168,22 @@ static void write_constructor_fetch(tl_outputer &out, const std::string &parser_ const tl_tree_type *result_type, bool is_flat, const std::set &request_types, const std::set &result_types, const TL_writer &w) { - std::vector vars(t->var_count); - int parser_type = w.get_parser_type(t, parser_name); if (!is_reachable_for_parser(parser_type, t->name, request_types, result_types, w)) { return; } + std::vector vars(t->var_count); out.append(w.gen_fetch_function_begin(parser_name, class_name, parent_class_name, - static_cast(result_type->children.size()), vars, parser_type)); + static_cast(result_type->children.size()), + static_cast(t->args.size()), vars, parser_type)); out.append(w.gen_vars(t, result_type, vars)); out.append(w.gen_uni(result_type, vars, true)); int field_num = 0; for (std::size_t i = 0; i < t->args.size(); i++) { std::string field_fetch = w.gen_field_fetch(field_num, t->args[i], vars, is_flat, parser_type); - if (field_fetch.size()) { + if (!field_fetch.empty()) { out.append(field_fetch); field_num++; } @@ -230,7 +231,7 @@ static int gen_field_definitions(tl_outputer &out, const tl_combinator *t, const } std::string type_name = w.gen_field_type(a); - if (type_name.size()) { + if (!type_name.empty()) { out.append(w.gen_field_definition(class_name, type_name, w.gen_field_name(a.name))); } } @@ -252,7 +253,9 @@ static void write_function(tl_outputer &out, const tl_combinator *t, const std:: std::vector vars(t->var_count); out.append(w.gen_function_vars(t, vars)); - write_class_constructor(out, t, class_name, true, w); + if (w.is_default_constructor_generated(t, true)) { + write_class_constructor(out, t, class_name, true, w); + } if (required_args) { write_class_constructor(out, t, class_name, false, w); } @@ -303,7 +306,9 @@ static void write_constructor(tl_outputer &out, const tl_combinator *t, const st int required_args = gen_field_definitions(out, t, class_name, w); out.append(w.gen_flags_definitions(t)); - write_class_constructor(out, t, class_name, true, w); + if (w.is_default_constructor_generated(t, false)) { + write_class_constructor(out, t, class_name, true, w); + } if (required_args) { write_class_constructor(out, t, class_name, false, w); } @@ -365,7 +370,7 @@ void write_class(tl_outputer &out, const tl_type *t, const std::set continue; } - out.append(w.gen_fetch_function_begin(parsers[i], class_name, class_name, t->arity, empty_vars, -1)); + out.append(w.gen_fetch_function_begin(parsers[i], class_name, class_name, t->arity, -1, empty_vars, -1)); out.append(w.gen_fetch_switch_begin()); for (std::size_t j = 0; j < t->constructors_num; j++) { if (w.is_combinator_supported(t->constructors[j])) { @@ -648,7 +653,7 @@ void write_tl(const tl_config &config, tl_outputer &out, const TL_writer &w) { } out.append(w.gen_fetch_function_begin(parsers[j], w.gen_base_type_class_name(i), w.gen_base_type_class_name(i), i, - empty_vars, -1)); + -1, empty_vars, -1)); out.append(w.gen_fetch_switch_begin()); for (std::size_t type = 0; type < types_n; type++) { tl_type *t = config.get_type_by_num(type); @@ -723,7 +728,7 @@ void write_tl(const tl_config &config, tl_outputer &out, const TL_writer &w) { } out.append(w.gen_fetch_function_begin(parsers[j], w.gen_base_function_class_name(), - w.gen_base_function_class_name(), 0, empty_vars, -1)); + w.gen_base_function_class_name(), 0, -1, empty_vars, -1)); out.append(w.gen_fetch_switch_begin()); for (std::size_t function = 0; function < functions_n; function++) { tl_combinator *t = config.get_function_by_num(function); diff --git a/tdtl/td/tl/tl_generate.h b/tdtl/td/tl/tl_generate.h index 5d4e4ae4..96e3d8c3 100644 --- a/tdtl/td/tl/tl_generate.h +++ b/tdtl/td/tl/tl_generate.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_outputer.cpp b/tdtl/td/tl/tl_outputer.cpp index bc56b271..3fae2210 100644 --- a/tdtl/td/tl/tl_outputer.cpp +++ b/tdtl/td/tl/tl_outputer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_outputer.h b/tdtl/td/tl/tl_outputer.h index 0d466f72..579d812e 100644 --- a/tdtl/td/tl/tl_outputer.h +++ b/tdtl/td/tl/tl_outputer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_simple.h b/tdtl/td/tl/tl_simple.h index ea53a062..8850bc79 100644 --- a/tdtl/td/tl/tl_simple.h +++ b/tdtl/td/tl/tl_simple.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,7 +21,6 @@ namespace td { namespace tl { namespace simple { -// TL type is std::string gen_cpp_name(std::string name) { for (std::size_t i = 0; i < name.size(); i++) { @@ -132,9 +131,11 @@ class Schema { void mark_result(const Type *type) { do_mark(type, true); } + void mark_query(const Type *type) { do_mark(type, false); } + void do_mark(const Type *type, bool is_result) { if (type->type == Type::Vector) { return do_mark(type->vector_value_type, is_result); @@ -190,6 +191,7 @@ class Schema { } return type; } + const CustomType *get_custom_type(const tl_type *from_type) { auto *type = get_type(from_type); assert(type->type == Type::Custom); @@ -213,6 +215,7 @@ class Schema { } return constructor; } + const Function *get_function(const tl_combinator *from) { auto &function = function_by_id[from->id]; if (!function) { @@ -230,6 +233,7 @@ class Schema { } return function; } + const Type *get_type(const tl_tree *tree) { assert(tree->get_type() == NODE_TYPE_TYPE); auto *type_tree = static_cast(tree); diff --git a/tdtl/td/tl/tl_simple_parser.h b/tdtl/td/tl/tl_simple_parser.h index 87d6f3b9..8a8e5fe8 100644 --- a/tdtl/td/tl/tl_simple_parser.h +++ b/tdtl/td/tl/tl_simple_parser.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_string_outputer.cpp b/tdtl/td/tl/tl_string_outputer.cpp index 7ce830d2..0894b084 100644 --- a/tdtl/td/tl/tl_string_outputer.cpp +++ b/tdtl/td/tl/tl_string_outputer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_string_outputer.h b/tdtl/td/tl/tl_string_outputer.h index 736ad325..b343f49e 100644 --- a/tdtl/td/tl/tl_string_outputer.h +++ b/tdtl/td/tl/tl_string_outputer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdtl/td/tl/tl_writer.cpp b/tdtl/td/tl/tl_writer.cpp index 5e80315a..ad918d22 100644 --- a/tdtl/td/tl/tl_writer.cpp +++ b/tdtl/td/tl/tl_writer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -135,6 +135,10 @@ bool TL_writer::is_documentation_generated() const { return false; } +bool TL_writer::is_default_constructor_generated(const tl_combinator *t, bool is_function) const { + return true; +} + std::string TL_writer::gen_main_class_name(const tl_type *t) const { if (t->simple_constructors == 1) { for (std::size_t i = 0; i < t->constructors_num; i++) { diff --git a/tdtl/td/tl/tl_writer.h b/tdtl/td/tl/tl_writer.h index 8c243dfa..e2b9a88a 100644 --- a/tdtl/td/tl/tl_writer.h +++ b/tdtl/td/tl/tl_writer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -56,6 +56,7 @@ class TL_writer { virtual bool is_type_bare(const tl_type *t) const = 0; virtual bool is_combinator_supported(const tl_combinator *constructor) const; virtual bool is_documentation_generated() const; + virtual bool is_default_constructor_generated(const tl_combinator *t, bool is_function) const; virtual int get_parser_type(const tl_combinator *t, const std::string &parser_name) const; virtual int get_storer_type(const tl_combinator *t, const std::string &storer_name) const; @@ -119,9 +120,9 @@ class TL_writer { virtual std::string gen_function_result_type(const tl_tree *result) const = 0; virtual std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, - const std::string &parent_class_name, int arity, + const std::string &parent_class_name, int arity, int field_count, std::vector &vars, int parser_type) const = 0; - virtual std::string gen_fetch_function_end(bool has_parent, int field_num, const std::vector &vars, + virtual std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector &vars, int parser_type) const = 0; virtual std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, @@ -139,12 +140,12 @@ class TL_writer { virtual std::string gen_fetch_switch_case(const tl_combinator *t, int arity) const = 0; virtual std::string gen_fetch_switch_end() const = 0; - virtual std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const = 0; + virtual std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const = 0; virtual std::string gen_constructor_parameter(int field_num, const std::string &class_name, const arg &a, bool is_default) const = 0; virtual std::string gen_constructor_field_init(int field_num, const std::string &class_name, const arg &a, bool is_default) const = 0; - virtual std::string gen_constructor_end(const tl_combinator *t, int fields_num, bool is_default) const = 0; + virtual std::string gen_constructor_end(const tl_combinator *t, int field_count, bool is_default) const = 0; virtual std::string gen_additional_function(const std::string &function_name, const tl_combinator *t, bool is_function) const; diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 7acd7821..d34b7f76 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -99,6 +99,7 @@ set(TDUTILS_SOURCE td/utils/MimeType.cpp td/utils/MpmcQueue.cpp td/utils/OptionsParser.cpp + td/utils/PathView.cpp td/utils/Random.cpp td/utils/SharedSlice.cpp td/utils/Slice.cpp diff --git a/tdutils/generate/generate_mime_types_gperf.cpp b/tdutils/generate/generate_mime_types_gperf.cpp index f446969e..9ff4504d 100644 --- a/tdutils/generate/generate_mime_types_gperf.cpp +++ b/tdutils/generate/generate_mime_types_gperf.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/generate/mime_types.txt b/tdutils/generate/mime_types.txt index cc1fb5e3..4f8c9d32 100644 --- a/tdutils/generate/mime_types.txt +++ b/tdutils/generate/mime_types.txt @@ -565,6 +565,7 @@ application/x-tex-tfm tfm application/x-texinfo texinfo texi application/x-tgif obj application/x-tgsticker tgs +application/x-tgwallpattern tgv application/x-ustar ustar application/x-wais-source src application/x-x509-ca-cert der crt diff --git a/tdutils/td/utils/AesCtrByteFlow.h b/tdutils/td/utils/AesCtrByteFlow.h index f7c6a2be..15c1dc62 100644 --- a/tdutils/td/utils/AesCtrByteFlow.h +++ b/tdutils/td/utils/AesCtrByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BigNum.cpp b/tdutils/td/utils/BigNum.cpp index c4ac3dc0..d943039b 100644 --- a/tdutils/td/utils/BigNum.cpp +++ b/tdutils/td/utils/BigNum.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BigNum.h b/tdutils/td/utils/BigNum.h index 56e5b930..4df9b49c 100644 --- a/tdutils/td/utils/BigNum.h +++ b/tdutils/td/utils/BigNum.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BufferedFd.h b/tdutils/td/utils/BufferedFd.h index 851db7c7..05c82663 100644 --- a/tdutils/td/utils/BufferedFd.h +++ b/tdutils/td/utils/BufferedFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BufferedReader.h b/tdutils/td/utils/BufferedReader.h index 55b67239..88b242d6 100644 --- a/tdutils/td/utils/BufferedReader.h +++ b/tdutils/td/utils/BufferedReader.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BufferedUdp.cpp b/tdutils/td/utils/BufferedUdp.cpp index dcdb65a7..b3c6d48e 100644 --- a/tdutils/td/utils/BufferedUdp.cpp +++ b/tdutils/td/utils/BufferedUdp.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/BufferedUdp.h b/tdutils/td/utils/BufferedUdp.h index b3c36040..92c5b763 100644 --- a/tdutils/td/utils/BufferedUdp.h +++ b/tdutils/td/utils/BufferedUdp.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ByteFlow.h b/tdutils/td/utils/ByteFlow.h index 5efb78ec..d46d1386 100644 --- a/tdutils/td/utils/ByteFlow.h +++ b/tdutils/td/utils/ByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/CancellationToken.h b/tdutils/td/utils/CancellationToken.h index cabb7768..36e88eec 100644 --- a/tdutils/td/utils/CancellationToken.h +++ b/tdutils/td/utils/CancellationToken.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ChangesProcessor.h b/tdutils/td/utils/ChangesProcessor.h index cab21e7e..8db2a7a7 100644 --- a/tdutils/td/utils/ChangesProcessor.h +++ b/tdutils/td/utils/ChangesProcessor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Closure.h b/tdutils/td/utils/Closure.h index 20bae518..851bec64 100644 --- a/tdutils/td/utils/Closure.h +++ b/tdutils/td/utils/Closure.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ConcurrentHashTable.h b/tdutils/td/utils/ConcurrentHashTable.h index 14db1d1e..30ae4dea 100644 --- a/tdutils/td/utils/ConcurrentHashTable.h +++ b/tdutils/td/utils/ConcurrentHashTable.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Container.h b/tdutils/td/utils/Container.h index 26ca00b8..e85e8691 100644 --- a/tdutils/td/utils/Container.h +++ b/tdutils/td/utils/Container.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Context.h b/tdutils/td/utils/Context.h index e542991c..27528bb9 100644 --- a/tdutils/td/utils/Context.h +++ b/tdutils/td/utils/Context.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/DecTree.h b/tdutils/td/utils/DecTree.h index ae2ae715..07e5587a 100644 --- a/tdutils/td/utils/DecTree.h +++ b/tdutils/td/utils/DecTree.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Destructor.h b/tdutils/td/utils/Destructor.h index b998bcc9..e7bf6ebd 100644 --- a/tdutils/td/utils/Destructor.h +++ b/tdutils/td/utils/Destructor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Enumerator.h b/tdutils/td/utils/Enumerator.h index aad19833..e616eb5b 100644 --- a/tdutils/td/utils/Enumerator.h +++ b/tdutils/td/utils/Enumerator.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,8 +7,8 @@ #pragma once #include "td/utils/common.h" -#include "td/utils/misc.h" +#include #include #include @@ -20,7 +20,8 @@ class Enumerator { using Key = int32; Key add(ValueT v) { - int32 next_id = narrow_cast(arr_.size() + 1); + CHECK(arr_.size() < static_cast(std::numeric_limits::max() - 1)); + int32 next_id = static_cast(arr_.size() + 1); bool was_inserted; decltype(map_.begin()) it; std::tie(it, was_inserted) = map_.emplace(std::move(v), next_id); diff --git a/tdutils/td/utils/EpochBasedMemoryReclamation.h b/tdutils/td/utils/EpochBasedMemoryReclamation.h index acd4082a..7402befc 100644 --- a/tdutils/td/utils/EpochBasedMemoryReclamation.h +++ b/tdutils/td/utils/EpochBasedMemoryReclamation.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/FileLog.cpp b/tdutils/td/utils/FileLog.cpp index 28aa9eae..aae75084 100644 --- a/tdutils/td/utils/FileLog.cpp +++ b/tdutils/td/utils/FileLog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -40,8 +40,7 @@ Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) } else { path_ = r_path.move_as_ok(); } - TRY_RESULT(size, fd_.get_size()); - size_ = size; + TRY_RESULT_ASSIGN(size_, fd_.get_size()); rotate_threshold_ = rotate_threshold; redirect_stderr_ = redirect_stderr; return Status::OK(); diff --git a/tdutils/td/utils/FileLog.h b/tdutils/td/utils/FileLog.h index c801b1cf..0dd7b4fe 100644 --- a/tdutils/td/utils/FileLog.h +++ b/tdutils/td/utils/FileLog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/FloodControlFast.h b/tdutils/td/utils/FloodControlFast.h index 326780bf..b7ee3c93 100644 --- a/tdutils/td/utils/FloodControlFast.h +++ b/tdutils/td/utils/FloodControlFast.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/FloodControlStrict.h b/tdutils/td/utils/FloodControlStrict.h index a2e794bd..fe40a2fc 100644 --- a/tdutils/td/utils/FloodControlStrict.h +++ b/tdutils/td/utils/FloodControlStrict.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/GitInfo.cpp b/tdutils/td/utils/GitInfo.cpp index 2240e926..3858757a 100644 --- a/tdutils/td/utils/GitInfo.cpp +++ b/tdutils/td/utils/GitInfo.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/GitInfo.h b/tdutils/td/utils/GitInfo.h index 8eab463b..8d628724 100644 --- a/tdutils/td/utils/GitInfo.h +++ b/tdutils/td/utils/GitInfo.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Gzip.cpp b/tdutils/td/utils/Gzip.cpp index 40e184d2..6fc36e42 100644 --- a/tdutils/td/utils/Gzip.cpp +++ b/tdutils/td/utils/Gzip.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Gzip.h b/tdutils/td/utils/Gzip.h index 889bfc56..f937a86c 100644 --- a/tdutils/td/utils/Gzip.h +++ b/tdutils/td/utils/Gzip.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/GzipByteFlow.cpp b/tdutils/td/utils/GzipByteFlow.cpp index f8d38fac..a32ae117 100644 --- a/tdutils/td/utils/GzipByteFlow.cpp +++ b/tdutils/td/utils/GzipByteFlow.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/GzipByteFlow.h b/tdutils/td/utils/GzipByteFlow.h index d7fb756a..46871c5d 100644 --- a/tdutils/td/utils/GzipByteFlow.h +++ b/tdutils/td/utils/GzipByteFlow.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Hash.h b/tdutils/td/utils/Hash.h index 72c0b837..70b0eaa3 100644 --- a/tdutils/td/utils/Hash.h +++ b/tdutils/td/utils/Hash.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/HashMap.h b/tdutils/td/utils/HashMap.h index aad6fceb..db1be7ae 100644 --- a/tdutils/td/utils/HashMap.h +++ b/tdutils/td/utils/HashMap.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/HashSet.h b/tdutils/td/utils/HashSet.h index ea7c686e..9e3a5a4c 100644 --- a/tdutils/td/utils/HashSet.h +++ b/tdutils/td/utils/HashSet.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/HazardPointers.h b/tdutils/td/utils/HazardPointers.h index 8c668b1f..954e423e 100644 --- a/tdutils/td/utils/HazardPointers.h +++ b/tdutils/td/utils/HazardPointers.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Heap.h b/tdutils/td/utils/Heap.h index 0666158d..b4449c30 100644 --- a/tdutils/td/utils/Heap.h +++ b/tdutils/td/utils/Heap.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Hints.cpp b/tdutils/td/utils/Hints.cpp index a9180e3a..e0996aeb 100644 --- a/tdutils/td/utils/Hints.cpp +++ b/tdutils/td/utils/Hints.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -68,7 +68,7 @@ vector Hints::get_words(Slice name, bool is_search) { void Hints::add_word(const string &word, KeyT key, std::map> &word_to_keys) { vector &keys = word_to_keys[word]; - CHECK(std::find(keys.begin(), keys.end(), key) == keys.end()); + CHECK(!td::contains(keys, key)); keys.push_back(key); } diff --git a/tdutils/td/utils/Hints.h b/tdutils/td/utils/Hints.h index a0af1418..dc313b67 100644 --- a/tdutils/td/utils/Hints.h +++ b/tdutils/td/utils/Hints.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/HttpUrl.cpp b/tdutils/td/utils/HttpUrl.cpp index f728a8c7..000e0e60 100644 --- a/tdutils/td/utils/HttpUrl.cpp +++ b/tdutils/td/utils/HttpUrl.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/HttpUrl.h b/tdutils/td/utils/HttpUrl.h index e3a42acd..04abc89f 100644 --- a/tdutils/td/utils/HttpUrl.h +++ b/tdutils/td/utils/HttpUrl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/JsonBuilder.cpp b/tdutils/td/utils/JsonBuilder.cpp index 94bce57b..d58b65b6 100644 --- a/tdutils/td/utils/JsonBuilder.cpp +++ b/tdutils/td/utils/JsonBuilder.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/JsonBuilder.h b/tdutils/td/utils/JsonBuilder.h index 2b3cf6e8..39c7387e 100644 --- a/tdutils/td/utils/JsonBuilder.h +++ b/tdutils/td/utils/JsonBuilder.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,20 +15,11 @@ #include "td/utils/StringBuilder.h" #include -#include #include #include namespace td { -template -std::tuple ctie(const Args &... args) TD_WARN_UNUSED_RESULT; - -template -std::tuple ctie(const Args &... args) { - return std::tie(args...); -} - class JsonTrue { public: friend StringBuilder &operator<<(StringBuilder &sb, const JsonTrue &val) { @@ -298,9 +289,7 @@ class JsonScope { *sb_ << x; return *this; } - JsonScope &operator<<(bool x) { - return *this << JsonBool(x); - } + JsonScope &operator<<(bool x) = delete; JsonScope &operator<<(int32 x) { return *this << JsonInt(x); } @@ -310,8 +299,6 @@ class JsonScope { JsonScope &operator<<(double x) { return *this << JsonFloat(x); } - template - JsonScope &operator<<(const T *x); // not implemented template JsonScope &operator<<(const char (&x)[N]) { return *this << JsonString(Slice(x)); @@ -319,9 +306,6 @@ class JsonScope { JsonScope &operator<<(const char *x) { return *this << JsonString(Slice(x)); } - JsonScope &operator<<(const string &x) { - return *this << JsonString(Slice(x)); - } JsonScope &operator<<(Slice x) { return *this << JsonString(x); } @@ -410,16 +394,8 @@ class JsonObjectScope : public JsonScope { jb_->print_offset(); *sb_ << "}"; } - template - JsonObjectScope &operator<<(std::tuple key_value) { - return (*this)(std::get<0>(key_value), std::get<1>(key_value)); - } - template - JsonObjectScope &operator<<(std::pair key_value) { - return (*this)(key_value.first, key_value.second); - } - template - JsonObjectScope &operator()(S &&key, T &&value) { + template + JsonObjectScope &operator()(Slice key, T &&value) { CHECK(is_active()); if (is_first_) { *sb_ << ","; @@ -603,7 +579,7 @@ class JsonValue : public Jsonable { case Type::Object: { auto object = scope->enter_object(); for (auto &key_value : get_object()) { - object << ctie(JsonString(key_value.first), key_value.second); + object(key_value.first, key_value.second); } break; } diff --git a/tdutils/td/utils/List.h b/tdutils/td/utils/List.h index 8faa6fac..ec7977c3 100644 --- a/tdutils/td/utils/List.h +++ b/tdutils/td/utils/List.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MemoryLog.h b/tdutils/td/utils/MemoryLog.h index 1864a0c4..94b60850 100644 --- a/tdutils/td/utils/MemoryLog.h +++ b/tdutils/td/utils/MemoryLog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MimeType.cpp b/tdutils/td/utils/MimeType.cpp index b375b3d6..053f66e4 100644 --- a/tdutils/td/utils/MimeType.cpp +++ b/tdutils/td/utils/MimeType.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MimeType.h b/tdutils/td/utils/MimeType.h index 9ed3eff5..4174be0e 100644 --- a/tdutils/td/utils/MimeType.h +++ b/tdutils/td/utils/MimeType.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MovableValue.h b/tdutils/td/utils/MovableValue.h index c109bdca..003bb3de 100644 --- a/tdutils/td/utils/MovableValue.h +++ b/tdutils/td/utils/MovableValue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MpmcQueue.cpp b/tdutils/td/utils/MpmcQueue.cpp index bded0967..93aff3a1 100644 --- a/tdutils/td/utils/MpmcQueue.cpp +++ b/tdutils/td/utils/MpmcQueue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MpmcQueue.h b/tdutils/td/utils/MpmcQueue.h index 08026842..ef16ba25 100644 --- a/tdutils/td/utils/MpmcQueue.h +++ b/tdutils/td/utils/MpmcQueue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MpmcWaiter.h b/tdutils/td/utils/MpmcWaiter.h index 1afbd147..0c3a62d9 100644 --- a/tdutils/td/utils/MpmcWaiter.h +++ b/tdutils/td/utils/MpmcWaiter.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MpscLinkQueue.h b/tdutils/td/utils/MpscLinkQueue.h index 82565e47..1a7a817d 100644 --- a/tdutils/td/utils/MpscLinkQueue.h +++ b/tdutils/td/utils/MpscLinkQueue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/MpscPollableQueue.h b/tdutils/td/utils/MpscPollableQueue.h index f73794ae..c3a7d6d2 100644 --- a/tdutils/td/utils/MpscPollableQueue.h +++ b/tdutils/td/utils/MpscPollableQueue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Named.h b/tdutils/td/utils/Named.h index f76042ac..6186aed2 100644 --- a/tdutils/td/utils/Named.h +++ b/tdutils/td/utils/Named.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ObjectPool.h b/tdutils/td/utils/ObjectPool.h index 97ed0fce..f5901709 100644 --- a/tdutils/td/utils/ObjectPool.h +++ b/tdutils/td/utils/ObjectPool.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Observer.h b/tdutils/td/utils/Observer.h index 97ef49a2..1ffead8e 100644 --- a/tdutils/td/utils/Observer.h +++ b/tdutils/td/utils/Observer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/OptionsParser.cpp b/tdutils/td/utils/OptionsParser.cpp index 474860a2..b9138ad0 100644 --- a/tdutils/td/utils/OptionsParser.cpp +++ b/tdutils/td/utils/OptionsParser.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/OptionsParser.h b/tdutils/td/utils/OptionsParser.h index cbb817a3..fd76474f 100644 --- a/tdutils/td/utils/OptionsParser.h +++ b/tdutils/td/utils/OptionsParser.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/OrderedEventsProcessor.h b/tdutils/td/utils/OrderedEventsProcessor.h index e5648603..6230708e 100644 --- a/tdutils/td/utils/OrderedEventsProcessor.h +++ b/tdutils/td/utils/OrderedEventsProcessor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Parser.h b/tdutils/td/utils/Parser.h index d783b22d..245f4504 100644 --- a/tdutils/td/utils/Parser.h +++ b/tdutils/td/utils/Parser.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/PathView.cpp b/tdutils/td/utils/PathView.cpp new file mode 100644 index 00000000..4487f867 --- /dev/null +++ b/tdutils/td/utils/PathView.cpp @@ -0,0 +1,39 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/utils/PathView.h" + +#include "td/utils/misc.h" + +namespace td { + +PathView::PathView(Slice path) : path_(path) { + last_slash_ = narrow_cast(path_.size()) - 1; + while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) { + last_slash_--; + } + + last_dot_ = static_cast(path_.size()); + for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) { + if (path_[i] == '.') { + last_dot_ = i; + break; + } + } +} + +Slice PathView::relative(Slice path, Slice dir, bool force) { + if (begins_with(path, dir)) { + path.remove_prefix(dir.size()); + return path; + } + if (force) { + return Slice(); + } + return path; +} + +} // namespace td diff --git a/tdutils/td/utils/PathView.h b/tdutils/td/utils/PathView.h index 11325f10..07241141 100644 --- a/tdutils/td/utils/PathView.h +++ b/tdutils/td/utils/PathView.h @@ -1,32 +1,18 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once -#include "td/utils/misc.h" #include "td/utils/Slice.h" namespace td { class PathView { public: - explicit PathView(Slice path) : path_(path) { - last_slash_ = narrow_cast(path_.size()) - 1; - while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) { - last_slash_--; - } - - last_dot_ = static_cast(path_.size()); - for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) { - if (path_[i] == '.') { - last_dot_ = i; - break; - } - } - } + explicit PathView(Slice path); bool empty() const { return path_.empty(); @@ -74,34 +60,7 @@ class PathView { return !is_absolute(); } - static Slice relative(Slice path, Slice dir, bool force = false) { - if (begins_with(path, dir)) { - path.remove_prefix(dir.size()); - return path; - } - if (force) { - return Slice(); - } - return path; - } - - static Slice dir_and_file(Slice path) { - auto last_slash = static_cast(path.size()) - 1; - while (last_slash >= 0 && !is_slash(path[last_slash])) { - last_slash--; - } - if (last_slash < 0) { - return Slice(); - } - last_slash--; - while (last_slash >= 0 && !is_slash(path[last_slash])) { - last_slash--; - } - if (last_slash < 0) { - return Slice(); - } - return path.substr(last_slash + 1); - } + static Slice relative(Slice path, Slice dir, bool force = false); private: static bool is_slash(char c) { diff --git a/tdutils/td/utils/Random.cpp b/tdutils/td/utils/Random.cpp index ebb24f86..234a081e 100644 --- a/tdutils/td/utils/Random.cpp +++ b/tdutils/td/utils/Random.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Random.h b/tdutils/td/utils/Random.h index 87f4904d..efe75a68 100644 --- a/tdutils/td/utils/Random.h +++ b/tdutils/td/utils/Random.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ScopeGuard.h b/tdutils/td/utils/ScopeGuard.h index 1820ad30..c4c8f9fb 100644 --- a/tdutils/td/utils/ScopeGuard.h +++ b/tdutils/td/utils/ScopeGuard.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/SharedObjectPool.h b/tdutils/td/utils/SharedObjectPool.h index f881ae18..8dd9e24a 100644 --- a/tdutils/td/utils/SharedObjectPool.h +++ b/tdutils/td/utils/SharedObjectPool.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/SharedSlice.cpp b/tdutils/td/utils/SharedSlice.cpp index 3dd76699..02b682a5 100644 --- a/tdutils/td/utils/SharedSlice.cpp +++ b/tdutils/td/utils/SharedSlice.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/SharedSlice.h b/tdutils/td/utils/SharedSlice.h index 0b140fd7..d4c20162 100644 --- a/tdutils/td/utils/SharedSlice.h +++ b/tdutils/td/utils/SharedSlice.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Slice-decl.h b/tdutils/td/utils/Slice-decl.h index 6ac42c9b..6c0e2c25 100644 --- a/tdutils/td/utils/Slice-decl.h +++ b/tdutils/td/utils/Slice-decl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Slice.cpp b/tdutils/td/utils/Slice.cpp index ed2d1630..e3ae1ace 100644 --- a/tdutils/td/utils/Slice.cpp +++ b/tdutils/td/utils/Slice.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Slice.h b/tdutils/td/utils/Slice.h index 93a50600..67b866f4 100644 --- a/tdutils/td/utils/Slice.h +++ b/tdutils/td/utils/Slice.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Span.h b/tdutils/td/utils/Span.h index 7379bfd5..c6a46808 100644 --- a/tdutils/td/utils/Span.h +++ b/tdutils/td/utils/Span.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/SpinLock.h b/tdutils/td/utils/SpinLock.h index 5a7f1704..2a5a6539 100644 --- a/tdutils/td/utils/SpinLock.h +++ b/tdutils/td/utils/SpinLock.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/StackAllocator.cpp b/tdutils/td/utils/StackAllocator.cpp index 0b01b487..78eaeac8 100644 --- a/tdutils/td/utils/StackAllocator.cpp +++ b/tdutils/td/utils/StackAllocator.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/StackAllocator.h b/tdutils/td/utils/StackAllocator.h index ba2ce272..9909805c 100644 --- a/tdutils/td/utils/StackAllocator.h +++ b/tdutils/td/utils/StackAllocator.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Status.cpp b/tdutils/td/utils/Status.cpp index bd72ab2a..81ed130b 100644 --- a/tdutils/td/utils/Status.cpp +++ b/tdutils/td/utils/Status.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index 1a818375..3a40d07c 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -38,7 +38,7 @@ #define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result) -#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result) +#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result) #define TRY_RESULT_PREFIX(name, result, prefix) \ TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix) diff --git a/tdutils/td/utils/Storer.h b/tdutils/td/utils/Storer.h index fa0b9a74..52fb4780 100644 --- a/tdutils/td/utils/Storer.h +++ b/tdutils/td/utils/Storer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/StorerBase.h b/tdutils/td/utils/StorerBase.h index 87da7136..299c5d71 100644 --- a/tdutils/td/utils/StorerBase.h +++ b/tdutils/td/utils/StorerBase.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/StringBuilder.cpp b/tdutils/td/utils/StringBuilder.cpp index c0011c25..cf516590 100644 --- a/tdutils/td/utils/StringBuilder.cpp +++ b/tdutils/td/utils/StringBuilder.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/StringBuilder.h b/tdutils/td/utils/StringBuilder.h index 12219901..d2eb461d 100644 --- a/tdutils/td/utils/StringBuilder.h +++ b/tdutils/td/utils/StringBuilder.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ThreadLocalStorage.h b/tdutils/td/utils/ThreadLocalStorage.h index fa092a5b..79dc82ed 100644 --- a/tdutils/td/utils/ThreadLocalStorage.h +++ b/tdutils/td/utils/ThreadLocalStorage.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/ThreadSafeCounter.h b/tdutils/td/utils/ThreadSafeCounter.h index e9e22f06..bfc66714 100644 --- a/tdutils/td/utils/ThreadSafeCounter.h +++ b/tdutils/td/utils/ThreadSafeCounter.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Time.cpp b/tdutils/td/utils/Time.cpp index 95c5f1a5..0180c31d 100644 --- a/tdutils/td/utils/Time.cpp +++ b/tdutils/td/utils/Time.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Time.h b/tdutils/td/utils/Time.h index d0382f9c..4652efd4 100644 --- a/tdutils/td/utils/Time.h +++ b/tdutils/td/utils/Time.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/TimedStat.h b/tdutils/td/utils/TimedStat.h index 51a57dfe..90319275 100644 --- a/tdutils/td/utils/TimedStat.h +++ b/tdutils/td/utils/TimedStat.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Timer.cpp b/tdutils/td/utils/Timer.cpp index 7528bb0e..c73b9542 100644 --- a/tdutils/td/utils/Timer.cpp +++ b/tdutils/td/utils/Timer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #include "td/utils/format.h" #include "td/utils/logging.h" -//#include "td/utils/Slice.h" // TODO move StringBuilder implementation to cpp, remove header #include "td/utils/Time.h" namespace td { diff --git a/tdutils/td/utils/Timer.h b/tdutils/td/utils/Timer.h index fbbd0597..ac32cb09 100644 --- a/tdutils/td/utils/Timer.h +++ b/tdutils/td/utils/Timer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/TsFileLog.cpp b/tdutils/td/utils/TsFileLog.cpp index b8ff11f6..d1830068 100644 --- a/tdutils/td/utils/TsFileLog.cpp +++ b/tdutils/td/utils/TsFileLog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/TsFileLog.h b/tdutils/td/utils/TsFileLog.h index 54f0ebe3..b3d23963 100644 --- a/tdutils/td/utils/TsFileLog.h +++ b/tdutils/td/utils/TsFileLog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/UInt.h b/tdutils/td/utils/UInt.h index df66e280..9e75424b 100644 --- a/tdutils/td/utils/UInt.h +++ b/tdutils/td/utils/UInt.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/Variant.h b/tdutils/td/utils/Variant.h index 2507d236..edee268a 100644 --- a/tdutils/td/utils/Variant.h +++ b/tdutils/td/utils/Variant.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/VectorQueue.h b/tdutils/td/utils/VectorQueue.h index 4a1b3d75..5264f54f 100644 --- a/tdutils/td/utils/VectorQueue.h +++ b/tdutils/td/utils/VectorQueue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/as.h b/tdutils/td/utils/as.h index 17390a24..4a6f79e3 100644 --- a/tdutils/td/utils/as.h +++ b/tdutils/td/utils/as.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/base64.cpp b/tdutils/td/utils/base64.cpp index 12ca5fcd..e4ecb5e7 100644 --- a/tdutils/td/utils/base64.cpp +++ b/tdutils/td/utils/base64.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/base64.h b/tdutils/td/utils/base64.h index bdaf8746..369c02a3 100644 --- a/tdutils/td/utils/base64.h +++ b/tdutils/td/utils/base64.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/benchmark.h b/tdutils/td/utils/benchmark.h index 2e796671..fd1678b1 100644 --- a/tdutils/td/utils/benchmark.h +++ b/tdutils/td/utils/benchmark.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/bits.h b/tdutils/td/utils/bits.h index 913b8b72..25c26864 100644 --- a/tdutils/td/utils/bits.h +++ b/tdutils/td/utils/bits.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/buffer.cpp b/tdutils/td/utils/buffer.cpp index 516d0c9c..77605ab0 100644 --- a/tdutils/td/utils/buffer.cpp +++ b/tdutils/td/utils/buffer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/buffer.h b/tdutils/td/utils/buffer.h index d540f609..d970b171 100644 --- a/tdutils/td/utils/buffer.h +++ b/tdutils/td/utils/buffer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,8 +7,6 @@ #pragma once #include "td/utils/common.h" -#include "td/utils/logging.h" -#include "td/utils/misc.h" #include "td/utils/port/thread_local.h" #include "td/utils/Slice.h" @@ -605,7 +603,7 @@ class ChainBufferReader { } ChainBufferReader cut_head(size_t offset) TD_WARN_UNUSED_RESULT { - LOG_CHECK(offset <= size()) << offset << " " << size(); + CHECK(offset <= size()); auto it = begin_.clone(); it.advance(offset); return cut_head(std::move(it)); @@ -753,8 +751,8 @@ class BufferBuilder { template void for_each(F &&f) const & { - for (auto &slice : reversed(to_prepend_)) { - f(slice.as_slice()); + for (auto i = to_prepend_.size(); i > 0; i--) { + f(to_prepend_[i - 1].as_slice()); } if (!buffer_writer_.empty()) { f(buffer_writer_.as_slice()); @@ -765,8 +763,8 @@ class BufferBuilder { } template void for_each(F &&f) && { - for (auto &slice : reversed(to_prepend_)) { - f(std::move(slice)); + for (auto i = to_prepend_.size(); i > 0; i--) { + f(std::move(to_prepend_[i - 1])); } if (!buffer_writer_.empty()) { f(buffer_writer_.as_buffer_slice()); diff --git a/tdutils/td/utils/check.cpp b/tdutils/td/utils/check.cpp index 17525d61..6d695688 100644 --- a/tdutils/td/utils/check.cpp +++ b/tdutils/td/utils/check.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/check.h b/tdutils/td/utils/check.h index 5c0b4854..1d9a85fc 100644 --- a/tdutils/td/utils/check.h +++ b/tdutils/td/utils/check.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/common.h b/tdutils/td/utils/common.h index fc79b007..ade9785e 100644 --- a/tdutils/td/utils/common.h +++ b/tdutils/td/utils/common.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/crypto.cpp b/tdutils/td/utils/crypto.cpp index 9d11e244..dba3d051 100644 --- a/tdutils/td/utils/crypto.cpp +++ b/tdutils/td/utils/crypto.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/crypto.h b/tdutils/td/utils/crypto.h index e5b79602..ae0f183b 100644 --- a/tdutils/td/utils/crypto.h +++ b/tdutils/td/utils/crypto.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/filesystem.cpp b/tdutils/td/utils/filesystem.cpp index 071c9a38..5706b4ec 100644 --- a/tdutils/td/utils/filesystem.cpp +++ b/tdutils/td/utils/filesystem.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -41,8 +41,7 @@ template Result read_file_impl(CSlice path, int64 size, int64 offset) { TRY_RESULT(from_file, FileFd::open(path, FileFd::Read)); if (size == -1) { - TRY_RESULT(file_size, from_file.get_size()); - size = file_size; + TRY_RESULT_ASSIGN(size, from_file.get_size()); } if (size < 0) { return Status::Error("Failed to read file: invalid size"); diff --git a/tdutils/td/utils/filesystem.h b/tdutils/td/utils/filesystem.h index 8522a3bb..eb8aad5a 100644 --- a/tdutils/td/utils/filesystem.h +++ b/tdutils/td/utils/filesystem.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/find_boundary.cpp b/tdutils/td/utils/find_boundary.cpp index 9c47f391..a7d0de94 100644 --- a/tdutils/td/utils/find_boundary.cpp +++ b/tdutils/td/utils/find_boundary.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/find_boundary.h b/tdutils/td/utils/find_boundary.h index d7b79ab2..69129c88 100644 --- a/tdutils/td/utils/find_boundary.h +++ b/tdutils/td/utils/find_boundary.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/format.h b/tdutils/td/utils/format.h index a3f4f260..e646a583 100644 --- a/tdutils/td/utils/format.h +++ b/tdutils/td/utils/format.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/int_types.h b/tdutils/td/utils/int_types.h index c1d7499f..03b24836 100644 --- a/tdutils/td/utils/int_types.h +++ b/tdutils/td/utils/int_types.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/invoke.h b/tdutils/td/utils/invoke.h index c83613da..a79e1e40 100644 --- a/tdutils/td/utils/invoke.h +++ b/tdutils/td/utils/invoke.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/logging.cpp b/tdutils/td/utils/logging.cpp index 3159c686..7e3f3007 100644 --- a/tdutils/td/utils/logging.cpp +++ b/tdutils/td/utils/logging.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/logging.h b/tdutils/td/utils/logging.h index 9711a862..d7174ad1 100644 --- a/tdutils/td/utils/logging.h +++ b/tdutils/td/utils/logging.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/misc.cpp b/tdutils/td/utils/misc.cpp index f4c5e4f0..968ad862 100644 --- a/tdutils/td/utils/misc.cpp +++ b/tdutils/td/utils/misc.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/misc.h b/tdutils/td/utils/misc.h index fb1d89ce..4353ed70 100644 --- a/tdutils/td/utils/misc.h +++ b/tdutils/td/utils/misc.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,10 +53,10 @@ string implode(const vector &v, char delimiter = ' '); namespace detail { -template +template struct transform_helper { template - auto transform(const T &v, const Func &f) { + auto transform(const V &v, const Func &f) { vector result; result.reserve(v.size()); for (auto &x : v) { @@ -66,7 +66,7 @@ struct transform_helper { } template - auto transform(T &&v, const Func &f) { + auto transform(V &&v, const Func &f) { vector result; result.reserve(v.size()); for (auto &x : v) { @@ -78,13 +78,13 @@ struct transform_helper { } // namespace detail -template -auto transform(T &&v, const Func &f) { - return detail::transform_helper>().transform(std::forward(v), f); +template +auto transform(V &&v, const Func &f) { + return detail::transform_helper>().transform(std::forward(v), f); } -template -void remove_if(vector &v, const Func &f) { +template +void remove_if(V &v, const Func &f) { size_t i = 0; while (i != v.size() && !f(v[i])) { i++; @@ -102,6 +102,36 @@ void remove_if(vector &v, const Func &f) { v.erase(v.begin() + j, v.end()); } +template +bool remove(V &v, const T &value) { + size_t i = 0; + while (i != v.size() && v[i] != value) { + i++; + } + if (i == v.size()) { + return false; + } + + size_t j = i; + while (++i != v.size()) { + if (v[i] != value) { + v[j++] = std::move(v[i]); + } + } + v.erase(v.begin() + j, v.end()); + return true; +} + +template +bool contains(const V &v, const T &value) { + for (auto &x : v) { + if (x == value) { + return true; + } + } + return false; +} + template void reset_to_empty(T &value) { using std::swap; diff --git a/tdutils/td/utils/optional.h b/tdutils/td/utils/optional.h index 6029d42c..31413162 100644 --- a/tdutils/td/utils/optional.h +++ b/tdutils/td/utils/optional.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/overloaded.h b/tdutils/td/utils/overloaded.h index 7a9666c7..75bb71ab 100644 --- a/tdutils/td/utils/overloaded.h +++ b/tdutils/td/utils/overloaded.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/Clocks.cpp b/tdutils/td/utils/port/Clocks.cpp index fa1d68b6..4d80568d 100644 --- a/tdutils/td/utils/port/Clocks.cpp +++ b/tdutils/td/utils/port/Clocks.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/Clocks.h b/tdutils/td/utils/port/Clocks.h index 625f9510..0ae04c20 100644 --- a/tdutils/td/utils/port/Clocks.h +++ b/tdutils/td/utils/port/Clocks.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/CxCli.h b/tdutils/td/utils/port/CxCli.h index 6a47116b..6c2691c4 100644 --- a/tdutils/td/utils/port/CxCli.h +++ b/tdutils/td/utils/port/CxCli.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/EventFd.h b/tdutils/td/utils/port/EventFd.h index c7196dff..b45856f0 100644 --- a/tdutils/td/utils/port/EventFd.h +++ b/tdutils/td/utils/port/EventFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/EventFdBase.h b/tdutils/td/utils/port/EventFdBase.h index b47fa6f2..401e545c 100644 --- a/tdutils/td/utils/port/EventFdBase.h +++ b/tdutils/td/utils/port/EventFdBase.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/FileFd.cpp b/tdutils/td/utils/port/FileFd.cpp index 16446f8c..f49184fd 100644 --- a/tdutils/td/utils/port/FileFd.cpp +++ b/tdutils/td/utils/port/FileFd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,6 +22,7 @@ #include #include #include +#include #if TD_PORT_POSIX #include @@ -206,6 +207,12 @@ Result FileFd::open(CSlice filepath, int32 flags, int32 mode) { if (handle == INVALID_HANDLE_VALUE) { return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags}); } +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + if (flags & Write) { + DWORD bytes_returned = 0; + DeviceIoControl(handle, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &bytes_returned, nullptr); + } +#endif auto native_fd = NativeFd(handle); if (flags & Append) { LARGE_INTEGER offset; @@ -469,18 +476,58 @@ NativeFd FileFd::move_as_native_fd() { return res; } -Result FileFd::get_size() const { - TRY_RESULT(s, stat()); - return s.size_; -} - #if TD_PORT_WINDOWS -static uint64 filetime_to_unix_time_nsec(LONGLONG filetime) { +namespace { + +uint64 filetime_to_unix_time_nsec(LONGLONG filetime) { const auto FILETIME_UNIX_TIME_DIFF = 116444736000000000ll; return static_cast((filetime - FILETIME_UNIX_TIME_DIFF) * 100); } + +struct FileSize { + int64 size_; + int64 real_size_; +}; + +Result get_file_size(const FileFd &file_fd) { + FILE_STANDARD_INFO standard_info; + if (!GetFileInformationByHandleEx(file_fd.get_native_fd().fd(), FileStandardInfo, &standard_info, + sizeof(standard_info))) { + return OS_ERROR("Get FileStandardInfo failed"); + } + FileSize res; + res.size_ = standard_info.EndOfFile.QuadPart; + res.real_size_ = standard_info.AllocationSize.QuadPart; + + if (res.size_ > 0 && res.real_size_ <= 0) { // just in case + LOG(ERROR) << "Fix real file size from " << res.real_size_ << " to " << res.size_; + res.real_size_ = res.size_; + } + + return res; +} + +} // namespace #endif +Result FileFd::get_size() const { +#if TD_PORT_POSIX + TRY_RESULT(s, stat()); +#elif TD_PORT_WINDOWS + TRY_RESULT(s, get_file_size(*this)); +#endif + return s.size_; +} + +Result FileFd::get_real_size() const { +#if TD_PORT_POSIX + TRY_RESULT(s, stat()); +#elif TD_PORT_WINDOWS + TRY_RESULT(s, get_file_size(*this)); +#endif + return s.real_size_; +} + Result FileFd::stat() const { CHECK(!empty()); #if TD_PORT_POSIX @@ -498,12 +545,9 @@ Result FileFd::stat() const { res.is_dir_ = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; res.is_reg_ = !res.is_dir_; // TODO this is still wrong - FILE_STANDARD_INFO standard_info; - status = GetFileInformationByHandleEx(get_native_fd().fd(), FileStandardInfo, &standard_info, sizeof(standard_info)); - if (!status) { - return OS_ERROR("Get FileStandardInfo failed"); - } - res.size_ = standard_info.EndOfFile.QuadPart; + TRY_RESULT(file_size, get_file_size(*this)); + res.size_ = file_size.size_; + res.real_size_ = file_size.real_size_; return res; #endif diff --git a/tdutils/td/utils/port/FileFd.h b/tdutils/td/utils/port/FileFd.h index 17154d35..2751af84 100644 --- a/tdutils/td/utils/port/FileFd.h +++ b/tdutils/td/utils/port/FileFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -50,11 +50,15 @@ class FileFd { PollableFdInfo &get_poll_info(); const PollableFdInfo &get_poll_info() const; + void close(); + bool empty() const; Result get_size() const; + Result get_real_size() const; + Result stat() const; Status sync() TD_WARN_UNUSED_RESULT; diff --git a/tdutils/td/utils/port/IPAddress.cpp b/tdutils/td/utils/port/IPAddress.cpp index 3cc8aad7..06d946ac 100644 --- a/tdutils/td/utils/port/IPAddress.cpp +++ b/tdutils/td/utils/port/IPAddress.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -386,7 +386,7 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) { } #endif TRY_RESULT(ascii_host, idn_to_ascii(host)); - host = ascii_host; + host = ascii_host; // assign string to CSlice // some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form, // and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses diff --git a/tdutils/td/utils/port/IPAddress.h b/tdutils/td/utils/port/IPAddress.h index c6160cbe..4d6b9bce 100644 --- a/tdutils/td/utils/port/IPAddress.h +++ b/tdutils/td/utils/port/IPAddress.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/IoSlice.h b/tdutils/td/utils/port/IoSlice.h index 07d73f9d..1c16ec6a 100644 --- a/tdutils/td/utils/port/IoSlice.h +++ b/tdutils/td/utils/port/IoSlice.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/MemoryMapping.cpp b/tdutils/td/utils/port/MemoryMapping.cpp index 722adde3..0bdf2c95 100644 --- a/tdutils/td/utils/port/MemoryMapping.cpp +++ b/tdutils/td/utils/port/MemoryMapping.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/MemoryMapping.h b/tdutils/td/utils/port/MemoryMapping.h index 6745e888..981781e6 100644 --- a/tdutils/td/utils/port/MemoryMapping.h +++ b/tdutils/td/utils/port/MemoryMapping.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/Poll.h b/tdutils/td/utils/port/Poll.h index 120e6546..926e2a19 100644 --- a/tdutils/td/utils/port/Poll.h +++ b/tdutils/td/utils/port/Poll.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/PollBase.h b/tdutils/td/utils/port/PollBase.h index af762917..bbe6a972 100644 --- a/tdutils/td/utils/port/PollBase.h +++ b/tdutils/td/utils/port/PollBase.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/PollFlags.cpp b/tdutils/td/utils/port/PollFlags.cpp index b15e648f..e4a9d1ba 100644 --- a/tdutils/td/utils/port/PollFlags.cpp +++ b/tdutils/td/utils/port/PollFlags.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/PollFlags.h b/tdutils/td/utils/port/PollFlags.h index b9b25b43..b9a677be 100644 --- a/tdutils/td/utils/port/PollFlags.h +++ b/tdutils/td/utils/port/PollFlags.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/RwMutex.h b/tdutils/td/utils/port/RwMutex.h index 76f60b40..aaf93a53 100644 --- a/tdutils/td/utils/port/RwMutex.h +++ b/tdutils/td/utils/port/RwMutex.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/ServerSocketFd.cpp b/tdutils/td/utils/port/ServerSocketFd.cpp index 17dae65d..75d68a75 100644 --- a/tdutils/td/utils/port/ServerSocketFd.cpp +++ b/tdutils/td/utils/port/ServerSocketFd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/ServerSocketFd.h b/tdutils/td/utils/port/ServerSocketFd.h index f3668e56..605fd90c 100644 --- a/tdutils/td/utils/port/ServerSocketFd.h +++ b/tdutils/td/utils/port/ServerSocketFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/SocketFd.cpp b/tdutils/td/utils/port/SocketFd.cpp index f9641be2..0641feaa 100644 --- a/tdutils/td/utils/port/SocketFd.cpp +++ b/tdutils/td/utils/port/SocketFd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/SocketFd.h b/tdutils/td/utils/port/SocketFd.h index b2d3559b..99fcde4f 100644 --- a/tdutils/td/utils/port/SocketFd.h +++ b/tdutils/td/utils/port/SocketFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/Stat.cpp b/tdutils/td/utils/port/Stat.cpp index 23959416..ff1e20d9 100644 --- a/tdutils/td/utils/port/Stat.cpp +++ b/tdutils/td/utils/port/Stat.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -106,6 +106,7 @@ Stat from_native_stat(const struct ::stat &buf) { res.atime_nsec_ = static_cast(buf.st_atime) * 1000000000 + time_nsec.first; res.mtime_nsec_ = static_cast(buf.st_mtime) * 1000000000 + time_nsec.second / 1000 * 1000; res.size_ = buf.st_size; + res.real_size_ = buf.st_blocks * 512; res.is_dir_ = (buf.st_mode & S_IFMT) == S_IFDIR; res.is_reg_ = (buf.st_mode & S_IFMT) == S_IFREG; return res; diff --git a/tdutils/td/utils/port/Stat.h b/tdutils/td/utils/port/Stat.h index 01072715..1e0fc297 100644 --- a/tdutils/td/utils/port/Stat.h +++ b/tdutils/td/utils/port/Stat.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,6 +18,7 @@ struct Stat { bool is_dir_; bool is_reg_; int64 size_; + int64 real_size_; uint64 atime_nsec_; uint64 mtime_nsec_; }; diff --git a/tdutils/td/utils/port/StdStreams.cpp b/tdutils/td/utils/port/StdStreams.cpp index 388bcf14..49983131 100644 --- a/tdutils/td/utils/port/StdStreams.cpp +++ b/tdutils/td/utils/port/StdStreams.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/StdStreams.h b/tdutils/td/utils/port/StdStreams.h index 10f77cc5..885171d6 100644 --- a/tdutils/td/utils/port/StdStreams.h +++ b/tdutils/td/utils/port/StdStreams.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/UdpSocketFd.cpp b/tdutils/td/utils/port/UdpSocketFd.cpp index c518894c..de56a02c 100644 --- a/tdutils/td/utils/port/UdpSocketFd.cpp +++ b/tdutils/td/utils/port/UdpSocketFd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/UdpSocketFd.h b/tdutils/td/utils/port/UdpSocketFd.h index b4097704..95234529 100644 --- a/tdutils/td/utils/port/UdpSocketFd.h +++ b/tdutils/td/utils/port/UdpSocketFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/config.h b/tdutils/td/utils/port/config.h index 36edd1b0..04a08ccd 100644 --- a/tdutils/td/utils/port/config.h +++ b/tdutils/td/utils/port/config.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Epoll.cpp b/tdutils/td/utils/port/detail/Epoll.cpp index 50e7c193..a14465d3 100644 --- a/tdutils/td/utils/port/detail/Epoll.cpp +++ b/tdutils/td/utils/port/detail/Epoll.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Epoll.h b/tdutils/td/utils/port/detail/Epoll.h index e99383c9..82c9e00f 100644 --- a/tdutils/td/utils/port/detail/Epoll.h +++ b/tdutils/td/utils/port/detail/Epoll.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdBsd.cpp b/tdutils/td/utils/port/detail/EventFdBsd.cpp index 64697860..7c604c55 100644 --- a/tdutils/td/utils/port/detail/EventFdBsd.cpp +++ b/tdutils/td/utils/port/detail/EventFdBsd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdBsd.h b/tdutils/td/utils/port/detail/EventFdBsd.h index 564b4f82..5aaa995d 100644 --- a/tdutils/td/utils/port/detail/EventFdBsd.h +++ b/tdutils/td/utils/port/detail/EventFdBsd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdLinux.cpp b/tdutils/td/utils/port/detail/EventFdLinux.cpp index 186db791..9b34d1e7 100644 --- a/tdutils/td/utils/port/detail/EventFdLinux.cpp +++ b/tdutils/td/utils/port/detail/EventFdLinux.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdLinux.h b/tdutils/td/utils/port/detail/EventFdLinux.h index 056504c7..10a77f0b 100644 --- a/tdutils/td/utils/port/detail/EventFdLinux.h +++ b/tdutils/td/utils/port/detail/EventFdLinux.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdWindows.cpp b/tdutils/td/utils/port/detail/EventFdWindows.cpp index edf13d51..4e133ae7 100644 --- a/tdutils/td/utils/port/detail/EventFdWindows.cpp +++ b/tdutils/td/utils/port/detail/EventFdWindows.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/EventFdWindows.h b/tdutils/td/utils/port/detail/EventFdWindows.h index 4eb84be0..adea8107 100644 --- a/tdutils/td/utils/port/detail/EventFdWindows.h +++ b/tdutils/td/utils/port/detail/EventFdWindows.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Iocp.cpp b/tdutils/td/utils/port/detail/Iocp.cpp index 70280c43..35ac2bb2 100644 --- a/tdutils/td/utils/port/detail/Iocp.cpp +++ b/tdutils/td/utils/port/detail/Iocp.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Iocp.h b/tdutils/td/utils/port/detail/Iocp.h index d8835033..9deead9b 100644 --- a/tdutils/td/utils/port/detail/Iocp.h +++ b/tdutils/td/utils/port/detail/Iocp.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/KQueue.cpp b/tdutils/td/utils/port/detail/KQueue.cpp index 52d474cf..37d756bc 100644 --- a/tdutils/td/utils/port/detail/KQueue.cpp +++ b/tdutils/td/utils/port/detail/KQueue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/KQueue.h b/tdutils/td/utils/port/detail/KQueue.h index d1e60fbf..b2a073a8 100644 --- a/tdutils/td/utils/port/detail/KQueue.h +++ b/tdutils/td/utils/port/detail/KQueue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/NativeFd.cpp b/tdutils/td/utils/port/detail/NativeFd.cpp index 0c73df34..00946516 100644 --- a/tdutils/td/utils/port/detail/NativeFd.cpp +++ b/tdutils/td/utils/port/detail/NativeFd.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/NativeFd.h b/tdutils/td/utils/port/detail/NativeFd.h index 2a2085ac..e57d8afc 100644 --- a/tdutils/td/utils/port/detail/NativeFd.h +++ b/tdutils/td/utils/port/detail/NativeFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Poll.cpp b/tdutils/td/utils/port/detail/Poll.cpp index ce916834..41cc2031 100644 --- a/tdutils/td/utils/port/detail/Poll.cpp +++ b/tdutils/td/utils/port/detail/Poll.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Poll.h b/tdutils/td/utils/port/detail/Poll.h index 4fb26442..28cb5016 100644 --- a/tdutils/td/utils/port/detail/Poll.h +++ b/tdutils/td/utils/port/detail/Poll.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/PollableFd.h b/tdutils/td/utils/port/detail/PollableFd.h index 9eebe921..4cc9fd70 100644 --- a/tdutils/td/utils/port/detail/PollableFd.h +++ b/tdutils/td/utils/port/detail/PollableFd.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Select.cpp b/tdutils/td/utils/port/detail/Select.cpp index db9fe84e..527f7b48 100644 --- a/tdutils/td/utils/port/detail/Select.cpp +++ b/tdutils/td/utils/port/detail/Select.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/Select.h b/tdutils/td/utils/port/detail/Select.h index 1dcbd20e..823b00f7 100644 --- a/tdutils/td/utils/port/detail/Select.h +++ b/tdutils/td/utils/port/detail/Select.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/ThreadIdGuard.cpp b/tdutils/td/utils/port/detail/ThreadIdGuard.cpp index 368f1511..8f9252b4 100644 --- a/tdutils/td/utils/port/detail/ThreadIdGuard.cpp +++ b/tdutils/td/utils/port/detail/ThreadIdGuard.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/ThreadIdGuard.h b/tdutils/td/utils/port/detail/ThreadIdGuard.h index cdbf90ac..816c23a3 100644 --- a/tdutils/td/utils/port/detail/ThreadIdGuard.h +++ b/tdutils/td/utils/port/detail/ThreadIdGuard.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/ThreadPthread.cpp b/tdutils/td/utils/port/detail/ThreadPthread.cpp index ca81e9e9..882d7205 100644 --- a/tdutils/td/utils/port/detail/ThreadPthread.cpp +++ b/tdutils/td/utils/port/detail/ThreadPthread.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/ThreadPthread.h b/tdutils/td/utils/port/detail/ThreadPthread.h index 6d35ae21..c3a12a6b 100644 --- a/tdutils/td/utils/port/detail/ThreadPthread.h +++ b/tdutils/td/utils/port/detail/ThreadPthread.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/ThreadStl.h b/tdutils/td/utils/port/detail/ThreadStl.h index 246e1487..ef6ff475 100644 --- a/tdutils/td/utils/port/detail/ThreadStl.h +++ b/tdutils/td/utils/port/detail/ThreadStl.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/WineventPoll.cpp b/tdutils/td/utils/port/detail/WineventPoll.cpp index 2f6a5383..86d9af2c 100644 --- a/tdutils/td/utils/port/detail/WineventPoll.cpp +++ b/tdutils/td/utils/port/detail/WineventPoll.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/detail/WineventPoll.h b/tdutils/td/utils/port/detail/WineventPoll.h index f8d2dd68..f227eb2f 100644 --- a/tdutils/td/utils/port/detail/WineventPoll.h +++ b/tdutils/td/utils/port/detail/WineventPoll.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/path.cpp b/tdutils/td/utils/port/path.cpp index 8dcbc54f..3d358ac5 100644 --- a/tdutils/td/utils/port/path.cpp +++ b/tdutils/td/utils/port/path.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -56,8 +56,7 @@ Status set_temporary_dir(CSlice dir) { input_dir += TD_DIR_SLASH; } TRY_STATUS(mkpath(input_dir, 0750)); - TRY_RESULT(real_dir, realpath(input_dir)); - temporary_dir = std::move(real_dir); + TRY_RESULT_ASSIGN(temporary_dir, realpath(input_dir)); return Status::OK(); } @@ -419,8 +418,7 @@ Result realpath(CSlice slice, bool ignore_access_denied) { return OS_ERROR(PSLICE() << "GetFullPathNameW failed for \"" << slice << '"'); } } else { - TRY_RESULT(t_res, from_wstring(buf)); - res = std::move(t_res); + TRY_RESULT_ASSIGN(res, from_wstring(buf)); } if (res.empty()) { return Status::Error("Empty path"); diff --git a/tdutils/td/utils/port/path.h b/tdutils/td/utils/port/path.h index efe2e047..fb5f10c5 100644 --- a/tdutils/td/utils/port/path.h +++ b/tdutils/td/utils/port/path.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/platform.h b/tdutils/td/utils/port/platform.h index 721f68a5..dfefd663 100644 --- a/tdutils/td/utils/port/platform.h +++ b/tdutils/td/utils/port/platform.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/signals.cpp b/tdutils/td/utils/port/signals.cpp index 03e82393..78fec8f3 100644 --- a/tdutils/td/utils/port/signals.cpp +++ b/tdutils/td/utils/port/signals.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -306,6 +306,7 @@ static void block_stdin() { } static void default_failure_signal_handler(int sig) { + Stacktrace::init(); signal_safe_write_signal_number(sig); Stacktrace::PrintOptions options; diff --git a/tdutils/td/utils/port/signals.h b/tdutils/td/utils/port/signals.h index 97e8de7a..619d35f7 100644 --- a/tdutils/td/utils/port/signals.h +++ b/tdutils/td/utils/port/signals.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/sleep.cpp b/tdutils/td/utils/port/sleep.cpp index 22f08920..9dd95d47 100644 --- a/tdutils/td/utils/port/sleep.cpp +++ b/tdutils/td/utils/port/sleep.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/sleep.h b/tdutils/td/utils/port/sleep.h index 9af65fa4..e7e7aee1 100644 --- a/tdutils/td/utils/port/sleep.h +++ b/tdutils/td/utils/port/sleep.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/stacktrace.cpp b/tdutils/td/utils/port/stacktrace.cpp index 18f0e372..3dd82355 100644 --- a/tdutils/td/utils/port/stacktrace.cpp +++ b/tdutils/td/utils/port/stacktrace.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,10 +53,12 @@ void print_backtrace_gdb(void) { name_buf[res] = 0; #if TD_LINUX +#if defined(PR_SET_DUMPABLE) if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { signal_safe_write("Can't set dumpable\n"); return; } +#endif #if defined(PR_SET_PTRACER) // We can't use event fd because we are in a signal handler int fds[2]; @@ -108,10 +110,18 @@ void print_backtrace_gdb(void) { } // namespace void Stacktrace::print_to_stderr(const PrintOptions &options) { + print_backtrace(); if (options.use_gdb) { print_backtrace_gdb(); } - print_backtrace(); +} + +void Stacktrace::init() { +#if __GLIBC__ + // backtrace needs to be called once to ensure that next calls are async-signal-safe + void *buffer[1]; + backtrace(buffer, 1); +#endif } } // namespace td diff --git a/tdutils/td/utils/port/stacktrace.h b/tdutils/td/utils/port/stacktrace.h index fbc4e78f..273d5907 100644 --- a/tdutils/td/utils/port/stacktrace.h +++ b/tdutils/td/utils/port/stacktrace.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,6 +16,8 @@ class Stacktrace { } }; static void print_to_stderr(const PrintOptions &options = PrintOptions()); + + static void init(); }; } // namespace td diff --git a/tdutils/td/utils/port/thread.h b/tdutils/td/utils/port/thread.h index 7ce89edf..ac435f5d 100644 --- a/tdutils/td/utils/port/thread.h +++ b/tdutils/td/utils/port/thread.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/thread_local.cpp b/tdutils/td/utils/port/thread_local.cpp index b7c6ca47..5eceac38 100644 --- a/tdutils/td/utils/port/thread_local.cpp +++ b/tdutils/td/utils/port/thread_local.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/thread_local.h b/tdutils/td/utils/port/thread_local.h index a1315ead..40f5d490 100644 --- a/tdutils/td/utils/port/thread_local.h +++ b/tdutils/td/utils/port/thread_local.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/wstring_convert.cpp b/tdutils/td/utils/port/wstring_convert.cpp index ffe7e15e..e98a052a 100644 --- a/tdutils/td/utils/port/wstring_convert.cpp +++ b/tdutils/td/utils/port/wstring_convert.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/port/wstring_convert.h b/tdutils/td/utils/port/wstring_convert.h index 4a71b5a2..2eb38fbc 100644 --- a/tdutils/td/utils/port/wstring_convert.h +++ b/tdutils/td/utils/port/wstring_convert.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/queue.h b/tdutils/td/utils/queue.h index bc5bed96..51b87dec 100644 --- a/tdutils/td/utils/queue.h +++ b/tdutils/td/utils/queue.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/tests.cpp b/tdutils/td/utils/tests.cpp index 31accc81..93f0aa5f 100644 --- a/tdutils/td/utils/tests.cpp +++ b/tdutils/td/utils/tests.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/tests.h b/tdutils/td/utils/tests.h index a410c228..68f9dd24 100644 --- a/tdutils/td/utils/tests.h +++ b/tdutils/td/utils/tests.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/tl_helpers.h b/tdutils/td/utils/tl_helpers.h index f04fdd30..90151fbc 100644 --- a/tdutils/td/utils/tl_helpers.h +++ b/tdutils/td/utils/tl_helpers.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,6 +19,7 @@ #include #include +#include #define BEGIN_STORE_FLAGS() \ do { \ @@ -150,6 +151,18 @@ void parse(vector &vec, ParserT &parser) { } } +template +void store(const unique_ptr &ptr, StorerT &storer) { + CHECK(ptr != nullptr); + store(*ptr, storer); +} +template +void parse(unique_ptr &ptr, ParserT &parser) { + CHECK(ptr == nullptr); + ptr = make_unique(); + parse(*ptr, parser); +} + template void store(const std::unordered_set &s, StorerT &storer) { storer.store_binary(narrow_cast(s.size())); @@ -172,6 +185,17 @@ void parse(std::unordered_set &s, ParserT &parse } } +template +void store(const std::pair &pair, StorerT &storer) { + store(pair.first, storer); + store(pair.second, storer); +} +template +void parse(std::pair &pair, ParserT &parser) { + parse(pair.first, parser); + parse(pair.second, parser); +} + template std::enable_if_t::value> store(const T &val, StorerT &storer) { store(static_cast(val), storer); diff --git a/tdutils/td/utils/tl_parsers.cpp b/tdutils/td/utils/tl_parsers.cpp index 6d9f3d8a..e5fb3e06 100644 --- a/tdutils/td/utils/tl_parsers.cpp +++ b/tdutils/td/utils/tl_parsers.cpp @@ -1,15 +1,35 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/utils/tl_parsers.h" +#include "td/utils/misc.h" + namespace td { alignas(4) const unsigned char TlParser::empty_data[sizeof(UInt256)] = {}; // static zero-initialized +TlParser::TlParser(Slice slice) { + data_len = left_len = slice.size(); + if (is_aligned_pointer<4>(slice.begin())) { + data = slice.ubegin(); + } else { + int32 *buf; + if (data_len <= small_data_array.size() * sizeof(int32)) { + buf = &small_data_array[0]; + } else { + LOG(ERROR) << "Unexpected big unaligned data pointer of length " << slice.size() << " at " << slice.begin(); + data_buf = std::make_unique(1 + data_len / sizeof(int32)); + buf = data_buf.get(); + } + std::memcpy(buf, slice.begin(), slice.size()); + data = reinterpret_cast(buf); + } +} + void TlParser::set_error(const string &error_message) { if (error.empty()) { CHECK(!error_message.empty()); @@ -27,4 +47,11 @@ void TlParser::set_error(const string &error_message) { } } +BufferSlice TlBufferParser::as_buffer_slice(Slice slice) { + if (is_aligned_pointer<4>(slice.data())) { + return parent_->from_slice(slice); + } + return BufferSlice(slice); +} + } // namespace td diff --git a/tdutils/td/utils/tl_parsers.h b/tdutils/td/utils/tl_parsers.h index e730e26e..89fa833b 100644 --- a/tdutils/td/utils/tl_parsers.h +++ b/tdutils/td/utils/tl_parsers.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,6 @@ #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" -#include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/UInt.h" @@ -38,23 +37,7 @@ class TlParser { alignas(4) static const unsigned char empty_data[sizeof(UInt256)]; public: - explicit TlParser(Slice slice) { - data_len = left_len = slice.size(); - if (is_aligned_pointer<4>(slice.begin())) { - data = slice.ubegin(); - } else { - int32 *buf; - if (data_len <= small_data_array.size() * sizeof(int32)) { - buf = &small_data_array[0]; - } else { - LOG(ERROR) << "Unexpected big unaligned data pointer of length " << slice.size() << " at " << slice.begin(); - data_buf = std::make_unique(1 + data_len / sizeof(int32)); - buf = data_buf.get(); - } - std::memcpy(buf, slice.begin(), slice.size()); - data = reinterpret_cast(buf); - } - } + explicit TlParser(Slice slice); TlParser(const TlParser &other) = delete; TlParser &operator=(const TlParser &other) = delete; @@ -143,15 +126,15 @@ class TlParser { T fetch_string() { check_len(sizeof(int32)); size_t result_len = *data; - const char *result_begin; + const unsigned char *result_begin; size_t result_aligned_len; if (result_len < 254) { - result_begin = reinterpret_cast(data + 1); + result_begin = data + 1; result_aligned_len = (result_len >> 2) << 2; data += sizeof(int32); } else if (result_len == 254) { result_len = data[1] + (data[2] << 8) + (data[3] << 16); - result_begin = reinterpret_cast(data + 4); + result_begin = data + 4; result_aligned_len = ((result_len + 3) >> 2) << 2; data += sizeof(int32); } else { @@ -165,7 +148,7 @@ class TlParser { return T(); } result_len = static_cast(result_len_uint64); - result_begin = reinterpret_cast(data + 8); + result_begin = data + 8; result_aligned_len = ((result_len + 3) >> 2) << 2; data += sizeof(int64); } @@ -174,7 +157,7 @@ class TlParser { return T(); } data += result_aligned_len; - return T(result_begin, result_len); + return T(reinterpret_cast(result_begin), result_len); } template @@ -204,6 +187,7 @@ class TlBufferParser : public TlParser { public: explicit TlBufferParser(const BufferSlice *buffer_slice) : TlParser(buffer_slice->as_slice()), parent_(buffer_slice) { } + template T fetch_string() { auto result = TlParser::fetch_string(); @@ -230,6 +214,7 @@ class TlBufferParser : public TlParser { return T(); } + template T fetch_string_raw(const size_t size) { return TlParser::fetch_string_raw(size); @@ -238,12 +223,7 @@ class TlBufferParser : public TlParser { private: const BufferSlice *parent_; - BufferSlice as_buffer_slice(Slice slice) { - if (is_aligned_pointer<4>(slice.data())) { - return parent_->from_slice(slice); - } - return BufferSlice(slice); - } + BufferSlice as_buffer_slice(Slice slice); }; template <> diff --git a/tdutils/td/utils/tl_storers.h b/tdutils/td/utils/tl_storers.h index f34c8f66..f4c6d614 100644 --- a/tdutils/td/utils/tl_storers.h +++ b/tdutils/td/utils/tl_storers.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #include "td/utils/common.h" #include "td/utils/logging.h" -#include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/StorerBase.h" #include "td/utils/UInt.h" @@ -22,7 +21,6 @@ class TlStorerUnsafe { public: explicit TlStorerUnsafe(unsigned char *buf) : buf_(buf) { - LOG_CHECK(is_aligned_pointer<4>(buf_)) << buf_; } TlStorerUnsafe(const TlStorerUnsafe &other) = delete; diff --git a/tdutils/td/utils/translit.cpp b/tdutils/td/utils/translit.cpp index 0fa9da82..2b14f036 100644 --- a/tdutils/td/utils/translit.cpp +++ b/tdutils/td/utils/translit.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/translit.h b/tdutils/td/utils/translit.h index 1db649f8..42a776f5 100644 --- a/tdutils/td/utils/translit.h +++ b/tdutils/td/utils/translit.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/type_traits.h b/tdutils/td/utils/type_traits.h index 3711427c..7f68a960 100644 --- a/tdutils/td/utils/type_traits.h +++ b/tdutils/td/utils/type_traits.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/uint128.h b/tdutils/td/utils/uint128.h index c0ab2924..c77a76b5 100644 --- a/tdutils/td/utils/uint128.h +++ b/tdutils/td/utils/uint128.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/unicode.cpp b/tdutils/td/utils/unicode.cpp index 155ac26b..20e3b2ff 100644 --- a/tdutils/td/utils/unicode.cpp +++ b/tdutils/td/utils/unicode.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,119 +15,128 @@ 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, 44065, 45312, 47617, 48480, 48641, 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, 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, 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, 119009, 119072, 119105, 119136, 119201, 119232, 119425, 119552, - 119585, 119808, 119841, 119936, 119969, 120000, 120033, 120064, 120129, 120192, 120225, 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, 200448, 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, 236833, 236960, 236993, 237120, 237217, 237280, 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, 402880, 402977, - 405984, 406083, 406208, 406529, 407392, 409089, 409600, 410627, 410944, 411907, 412160, 412195, - 412672, 413699, 414016, 415267, 415744, 425985, 636608, 638977, 1309376, 1310721, 1348000, 1350145, - 1351616, 1351681, 1360288, 1360385, 1360898, 1361217, 1361280, 1361921, 1363424, 1363937, 1364928, 1364993, - 1367235, 1367552, 1368801, 1369088, 1369153, 1372448, 1372513, 1373664, 1373697, 1373952, 1375969, 1376320, - 1376353, 1376448, 1376481, 1376608, 1376641, 1377376, 1377795, 1377984, 1378305, 1379968, 1380417, 1382016, - 1382914, 1383232, 1384001, 1384192, 1384289, 1384320, 1384353, 1384384, 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, 1404096, 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, 2123265, 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, 2180736, 2181123, - 2181376, 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, 2203648, 2214915, 2215904, 2228321, - 2230016, 2230851, 2231490, 2231808, 2232417, 2233856, 2234881, 2235680, 2235906, 2236224, 2236513, 2237664, - 2238146, 2238464, 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, 2265089, - 2266624, 2267265, 2267328, 2267361, 2267392, 2267650, 2267968, 2273281, 2274784, 2276097, 2276224, 2277377, - 2278912, 2279553, 2279584, 2279938, 2280256, 2281473, 2282848, 2283522, 2283840, 2285569, 2286400, 2287106, - 2287427, 2287488, 2298881, 2300930, 2301251, 2301536, 2301921, 2301952, 2316289, 2318112, 2326529, 2326816, - 2326849, 2328032, 2328577, 2328608, 2329090, 2329411, 2330016, 2330177, 2331136, 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, 3006465, 3008672, 3009025, 3009056, 3011169, 3011584, 3013633, 3013664, 3014657, - 3210656, 3211265, 3235424, 3538945, 3539008, 3637249, 3640672, 3640833, 3641248, 3641345, 3641632, 3641857, - 3642176, 3828739, 3829312, 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, 3997697, 4004000, 4004067, 4004352, 4005889, 4008064, 4008450, - 4008768, 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, 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, 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}; static constexpr uint32 TABLE_SIZE = 1280; @@ -212,36 +221,38 @@ static const int32 prepare_search_character_ranges[] = { 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, 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, 2260, 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, -2557, 2561, 0, + 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, -2679, 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, 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, 3076, -3077, 3134, 0, - 3141, 3141, 3142, 0, 3145, 3145, 3146, 0, 3150, -3151, 3157, 0, - 3159, -3160, 3170, 0, 3172, -3173, 3199, 32, 3200, 3200, 3201, 0, - 3204, -3205, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269, 3270, 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, 3329, 0, 3332, -3333, 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, 3770, 3770, 3771, 0, 3773, -3774, 3784, 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, @@ -267,50 +278,49 @@ static const int32 prepare_search_character_ranges[] = { 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, 7360, 32, 7368, -7369, 7376, 0, 7379, 32, - 7380, 0, 7401, -7402, 7405, 0, 7406, -7407, 7410, 0, 7413, -7414, - 7416, 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, - 7670, -7671, 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, 8383, -8384, 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, 9215, 9215, 9216, 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, - 11194, -11195, 11197, 32, 11209, 11209, 11210, 32, 11218, -11219, 11244, 32, - 11248, -11249, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549, + 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, 11845, -11846, + 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, 13056, 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, @@ -319,94 +329,111 @@ static const int32 prepare_search_character_ranges[] = { 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, 42936, -42937, 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, 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, 69632, 0, 69635, -69636, 69688, 0, - 69703, 32, 69710, -69711, 69759, 0, 69763, -69764, 69808, 0, 69819, 32, - 69821, 0, 69822, 32, 69826, -69827, 69888, 0, 69891, -69892, 69927, 0, - 69941, -69942, 69952, 32, 69956, -69957, 70003, 0, 70004, 32, 70006, -70007, - 70016, 0, 70019, -70020, 70067, 0, 70081, -70082, 70085, 32, 70090, 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, 70460, 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, -70751, 70832, 0, 70852, -70853, + 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, 71840, -71873, 71872, -71873, 72751, 0, 72759, 72759, - 72760, 0, 72768, 72768, 72769, 32, 72774, -72775, 72816, 32, 72818, -72819, - 72850, 0, 72872, 72872, 72873, 0, 72887, -72888, 74864, 32, 74869, -74870, - 92782, 32, 92784, -92785, 92912, 0, 92917, 32, 92918, -92919, 92976, 0, - 92983, 32, 92992, -92993, 92996, 32, 92998, -92999, 94033, 0, 94079, -94080, - 94095, 0, 94099, -94100, 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, 125136, 0, 125143, -125144, - 125184, -125219, 125218, -125219, 125252, 0, 125259, -125260, 125278, 32, 125280, -125281, - 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, - 127279, 127279, 127280, -98, 127306, 32, 127340, -127341, 127344, 32, 127405, -127406, - 127462, 32, 127490, -127491, 127552, 32, 127561, -127562, 127744, 32, 128723, -128724, - 128736, 32, 128749, -128750, 128752, 32, 128759, -128760, 128768, 32, 128884, -128885, - 128896, 32, 128981, -128982, 129024, 32, 129036, -129037, 129040, 32, 129096, -129097, - 129104, 32, 129114, -129115, 129120, 32, 129160, -129161, 129168, 32, 129198, -129199, - 129296, 32, 129311, 129311, 129312, 32, 129320, -129321, 129328, 32, 129329, -129330, - 129331, 32, 129343, 129343, 129344, 32, 129356, -129357, 129360, 32, 129375, -129376, - 129408, 32, 129426, -129427, 129472, 32, 129473, -129474, 131070, 32, 131072, -131073, + 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, @@ -485,29 +512,32 @@ 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, - 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, 42936, -42937, 65313, -65346, 65339, -65340, 66560, -66601, 66600, -66601, 66736, - -66777, 66772, -66773, 68736, -68801, 68787, -68788, 71840, -71873, 71872, -71873, 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, 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, @@ -580,534 +610,545 @@ static const int16 without_diacritics_table[TABLE_SIZE] = { 1099, 1274, 1275, 1276, 1277, 1278, 1279}; static const int32 without_diacritics_ranges[] = { - 1280, -1281, 1425, 0, 1470, 1470, 1471, 0, 1472, 1472, 1473, 0, - 1475, 1475, 1476, 0, 1478, 1478, 1479, 0, 1480, -1481, 1536, 0, - 1542, -1543, 1552, 0, 1563, 1563, 1564, 0, 1565, -1566, 1570, 1575, - 1572, 1608, 1573, 1575, 1574, 1610, 1575, -1576, 1611, 0, 1632, -1633, - 1648, 0, 1649, -1650, 1728, 1749, 1729, 2097154, 1732, -1733, 1747, 2097152, - 1749, 1749, 1750, 0, 1758, 1758, 1759, 0, 1765, -1766, 1767, 0, - 1769, 1769, 1770, 0, 1774, -1775, 1807, 0, 1808, 1808, 1809, 0, - 1810, -1811, 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, - 2036, -2037, 2070, 0, 2074, 2074, 2075, 0, 2084, 2084, 2085, 0, - 2088, 2088, 2089, 0, 2094, -2095, 2137, 0, 2140, -2141, 2260, 0, - 2308, -2309, 2345, 2097152, 2347, -2348, 2353, 2097152, 2355, 2097154, 2358, -2359, - 2362, 0, 2365, 2365, 2366, 0, 2384, 2384, 2385, 0, 2392, -2326, - 2395, 2332, 2396, -2338, 2398, 2347, 2399, 2351, 2400, -2401, 2402, 0, - 2404, -2405, 2433, 0, 2436, -2437, 2492, 0, 2493, 2493, 2494, 0, - 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, 2510, -2511, 2519, 0, - 2520, -2521, 2524, -2466, 2526, 2526, 2527, 2479, 2528, -2529, 2530, 0, - 2532, -2533, 2561, 0, 2564, -2565, 2611, 2097152, 2613, 2613, 2614, 2616, - 2615, -2616, 2620, 0, 2621, 2621, 2622, 0, 2627, -2628, 2631, 0, - 2633, -2634, 2635, 0, 2638, -2639, 2641, 0, 2642, -2643, 2649, -2583, - 2651, 2588, 2652, -2653, 2654, 2603, 2655, -2656, 2672, 0, 2674, -2675, - 2677, 0, 2678, -2679, 2689, 0, 2692, -2693, 2748, 0, 2749, 2749, - 2750, 0, 2758, 2758, 2759, 0, 2762, 2762, 2763, 0, 2766, -2767, - 2786, 0, 2788, -2789, 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, - 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, 3076, -3077, 3134, 0, 3141, 3141, 3142, 0, 3145, 3145, - 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0, 3172, -3173, - 3201, 0, 3204, -3205, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269, - 3270, 0, 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288, - 3298, 0, 3300, -3301, 3329, 0, 3332, -3333, 3390, 0, 3397, 3397, - 3398, 0, 3401, 3401, 3402, 0, 3406, -3407, 3415, 0, 3416, -3417, - 3426, 0, 3428, -3429, 3458, 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, 3764, 0, 3770, 3770, - 3771, 0, 3773, -3774, 3784, 0, 3790, -3791, 3852, 2097154, 3854, -3855, - 3864, 0, 3866, -3867, 3893, 0, 3894, 3894, 3895, 0, 3896, 3896, - 3897, 0, 3898, -3899, 3902, 0, 3904, -3905, 3907, 2097152, 3909, -3910, - 3917, 2097152, 3919, -3920, 3922, 2097154, 3924, -3925, 3927, 2097152, 3929, -3930, - 3932, 2097154, 3934, -3935, 3945, 3904, 3946, -3947, 3953, 0, 3973, 3973, - 3974, 0, 3976, -3977, 3981, 0, 3992, 3992, 3993, 0, 4029, -4030, - 4038, 0, 4039, -4040, 4134, 2097154, 4136, -4137, 4139, 0, 4159, -4160, - 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, -4255, 4348, 4316, 4349, -4350, - 4957, 0, 4960, -4961, 5906, 0, 5909, -5910, 5938, 0, 5941, -5942, - 5970, 0, 5972, -5973, 6002, 0, 6004, -6005, 6068, 0, 6100, -6101, - 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, - 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, - 7401, -7402, 7405, 0, 7406, -7407, 7410, 0, 7413, -7414, 7416, 0, - 7418, -7419, 7468, 65, 7469, 198, 7470, 66, 7471, 7471, 7472, -69, - 7474, 398, 7475, -72, 7483, 7483, 7484, 79, 7485, 546, 7486, 80, - 7487, 82, 7488, -85, 7490, 87, 7491, 97, 7492, -593, 7494, 7426, - 7495, 98, 7496, -101, 7498, 601, 7499, -604, 7501, 103, 7502, 7502, - 7503, 107, 7504, 109, 7505, 331, 7506, 111, 7507, 596, 7508, -7447, - 7510, 112, 7511, -117, 7513, 7453, 7514, 623, 7515, 118, 7516, 7461, - 7517, -947, 7520, -967, 7522, 105, 7523, 114, 7524, -118, 7526, -947, - 7528, 961, 7529, -967, 7531, -7532, 7544, 1085, 7545, -7546, 7579, 594, - 7580, 99, 7581, 597, 7582, 240, 7583, 604, 7584, 102, 7585, 607, - 7586, 609, 7587, 613, 7588, -617, 7591, 7547, 7592, 669, 7593, 621, - 7594, 7557, 7595, 671, 7596, 625, 7597, 624, 7598, -627, 7602, 632, - 7603, -643, 7605, 427, 7606, -650, 7608, 7452, 7609, -652, 7611, 122, - 7612, -657, 7615, 952, 7616, 0, 7670, -7671, 7675, 0, 7680, 65, - 7681, 97, 7682, 66, 7683, 98, 7684, 66, 7685, 98, 7686, 66, - 7687, 98, 7688, 67, 7689, 99, 7690, 68, 7691, 100, 7692, 68, - 7693, 100, 7694, 68, 7695, 100, 7696, 68, 7697, 100, 7698, 68, - 7699, 100, 7700, 69, 7701, 101, 7702, 69, 7703, 101, 7704, 69, - 7705, 101, 7706, 69, 7707, 101, 7708, 69, 7709, 101, 7710, 70, - 7711, 102, 7712, 71, 7713, 103, 7714, 72, 7715, 104, 7716, 72, - 7717, 104, 7718, 72, 7719, 104, 7720, 72, 7721, 104, 7722, 72, - 7723, 104, 7724, 73, 7725, 105, 7726, 73, 7727, 105, 7728, 75, - 7729, 107, 7730, 75, 7731, 107, 7732, 75, 7733, 107, 7734, 76, - 7735, 108, 7736, 76, 7737, 108, 7738, 76, 7739, 108, 7740, 76, - 7741, 108, 7742, 77, 7743, 109, 7744, 77, 7745, 109, 7746, 77, - 7747, 109, 7748, 78, 7749, 110, 7750, 78, 7751, 110, 7752, 78, - 7753, 110, 7754, 78, 7755, 110, 7756, 79, 7757, 111, 7758, 79, - 7759, 111, 7760, 79, 7761, 111, 7762, 79, 7763, 111, 7764, 80, - 7765, 112, 7766, 80, 7767, 112, 7768, 82, 7769, 114, 7770, 82, - 7771, 114, 7772, 82, 7773, 114, 7774, 82, 7775, 114, 7776, 83, - 7777, 115, 7778, 83, 7779, 115, 7780, 83, 7781, 115, 7782, 83, - 7783, 115, 7784, 83, 7785, 115, 7786, 84, 7787, 116, 7788, 84, - 7789, 116, 7790, 84, 7791, 116, 7792, 84, 7793, 116, 7794, 85, - 7795, 117, 7796, 85, 7797, 117, 7798, 85, 7799, 117, 7800, 85, - 7801, 117, 7802, 85, 7803, 117, 7804, 86, 7805, 118, 7806, 86, - 7807, 118, 7808, 87, 7809, 119, 7810, 87, 7811, 119, 7812, 87, - 7813, 119, 7814, 87, 7815, 119, 7816, 87, 7817, 119, 7818, 88, - 7819, 120, 7820, 88, 7821, 120, 7822, 89, 7823, 121, 7824, 90, - 7825, 122, 7826, 90, 7827, 122, 7828, 90, 7829, 122, 7830, 104, - 7831, 116, 7832, 119, 7833, 121, 7834, 97, 7835, 115, 7836, -7837, - 7840, 65, 7841, 97, 7842, 65, 7843, 97, 7844, 65, 7845, 97, - 7846, 65, 7847, 97, 7848, 65, 7849, 97, 7850, 65, 7851, 97, - 7852, 65, 7853, 97, 7854, 65, 7855, 97, 7856, 65, 7857, 97, - 7858, 65, 7859, 97, 7860, 65, 7861, 97, 7862, 65, 7863, 97, - 7864, 69, 7865, 101, 7866, 69, 7867, 101, 7868, 69, 7869, 101, - 7870, 69, 7871, 101, 7872, 69, 7873, 101, 7874, 69, 7875, 101, - 7876, 69, 7877, 101, 7878, 69, 7879, 101, 7880, 73, 7881, 105, - 7882, 73, 7883, 105, 7884, 79, 7885, 111, 7886, 79, 7887, 111, - 7888, 79, 7889, 111, 7890, 79, 7891, 111, 7892, 79, 7893, 111, - 7894, 79, 7895, 111, 7896, 79, 7897, 111, 7898, 79, 7899, 111, - 7900, 79, 7901, 111, 7902, 79, 7903, 111, 7904, 79, 7905, 111, - 7906, 79, 7907, 111, 7908, 85, 7909, 117, 7910, 85, 7911, 117, - 7912, 85, 7913, 117, 7914, 85, 7915, 117, 7916, 85, 7917, 117, - 7918, 85, 7919, 117, 7920, 85, 7921, 117, 7922, 89, 7923, 121, - 7924, 89, 7925, 121, 7926, 89, 7927, 121, 7928, 89, 7929, 121, - 7930, -7931, 7936, 945, 7944, 913, 7952, 949, 7958, -7959, 7960, 917, - 7966, -7967, 7968, 951, 7976, 919, 7984, 953, 7992, 921, 8000, 959, - 8006, -8007, 8008, 927, 8014, -8015, 8016, 965, 8024, 8024, 8025, 933, - 8026, 8026, 8027, 933, 8028, 8028, 8029, 933, 8030, 8030, 8031, 933, - 8032, 969, 8040, 937, 8048, 945, 8050, 949, 8052, 951, 8054, 953, - 8056, 959, 8058, 965, 8060, 969, 8062, -8063, 8064, 945, 8072, 913, - 8080, 951, 8088, 919, 8096, 969, 8104, 937, 8112, 945, 8117, 8117, - 8118, 945, 8120, 913, 8125, 32, 8126, 953, 8127, 32, 8130, 951, - 8133, 8133, 8134, 951, 8136, 917, 8138, 919, 8141, 32, 8144, 953, - 8148, -8149, 8150, 953, 8152, 921, 8156, 8156, 8157, 32, 8160, 965, - 8164, 961, 8166, 965, 8168, 933, 8172, 929, 8173, 32, 8175, 96, - 8176, -8177, 8178, 969, 8181, 8181, 8182, 969, 8184, 927, 8186, 937, - 8189, 32, 8191, 8191, 8192, 32, 8203, 0, 8208, 2097152, 8211, -8212, - 8215, 32, 8216, -8217, 8228, 46, 8229, -8230, 8234, 0, 8239, 32, - 8240, -8241, 8254, 32, 8255, -8256, 8287, 32, 8288, 0, 8293, 8293, - 8294, 0, 8304, 48, 8305, 105, 8306, -8307, 8308, -53, 8314, 43, - 8315, 8722, 8316, 61, 8317, -41, 8319, 110, 8320, -49, 8330, 43, - 8331, 8722, 8332, 61, 8333, -41, 8335, 8335, 8336, 97, 8337, 101, - 8338, 111, 8339, 120, 8340, 601, 8341, 104, 8342, -108, 8346, 112, - 8347, -116, 8349, -8350, 8400, 0, 8433, -8434, 8450, 67, 8452, -8453, - 8455, 400, 8456, 8456, 8457, 70, 8458, 103, 8459, 72, 8462, 104, - 8463, 295, 8464, 73, 8466, 76, 8467, 108, 8468, 8468, 8469, 78, - 8470, -8471, 8473, -81, 8476, 82, 8478, -8479, 8484, 90, 8485, 8485, - 8486, 937, 8487, 8487, 8488, 90, 8489, 8489, 8490, 75, 8491, -66, - 8494, 8494, 8495, 101, 8496, -70, 8498, 8498, 8499, 77, 8500, 111, - 8501, -1489, 8505, 105, 8506, -8507, 8508, 960, 8509, 947, 8510, 915, - 8511, 928, 8512, 8721, 8513, -8514, 8517, 68, 8518, -101, 8520, -106, - 8522, -8523, 8543, 49, 8544, 73, 8545, -8546, 8548, 86, 8549, -8550, - 8553, 88, 8554, -8555, 8556, 76, 8557, -68, 8559, 77, 8560, 105, - 8561, -8562, 8564, 118, 8565, -8566, 8569, 120, 8570, -8571, 8572, 108, - 8573, -100, 8575, 109, 8576, -8577, 8602, 8592, 8603, 8594, 8604, -8605, - 8622, 8596, 8623, -8624, 8653, 8656, 8654, 8660, 8655, 8658, 8656, -8657, - 8708, 2097154, 8710, -8711, 8713, 2097152, 8715, 2097154, 8718, -8719, 8740, 2097154, - 8744, -8745, 8769, 8764, 8770, -8771, 8772, 2097154, 8774, 8774, 8775, 8773, - 8776, 2097152, 8779, -8780, 8800, 61, 8801, 2097154, 8804, -8805, 8813, 8781, - 8814, 60, 8815, 62, 8816, -8805, 8818, -8819, 8820, -8819, 8822, -8823, - 8824, -8823, 8826, -8827, 8832, -8827, 8834, -8835, 8836, -8835, 8838, -8839, - 8840, -8839, 8842, -8843, 8876, 8866, 8877, -8873, 8879, 8875, 8880, -8881, - 8928, -8829, 8930, -8850, 8932, -8933, 8938, -8883, 8942, -8943, 9001, -12297, - 9003, -9004, 9312, -50, 9321, -9322, 9352, -50, 9361, -9362, 9398, -66, - 9424, -98, 9450, 48, 9451, -9452, 10972, 10973, 10974, -10975, 11388, 106, - 11389, 86, 11390, -11391, 11503, 0, 11506, -11507, 11631, 11617, 11632, -11633, - 11647, 0, 11648, -11649, 11744, 0, 11776, -11777, 11935, 27597, 11936, -11937, - 12019, 40863, 12020, -12021, 12032, 19968, 12033, 20008, 12034, 20022, 12035, 20031, - 12036, 20057, 12037, 20101, 12038, 20108, 12039, 20128, 12040, 20154, 12041, 20799, - 12042, 20837, 12043, 20843, 12044, 20866, 12045, 20886, 12046, 20907, 12047, 20960, - 12048, 20981, 12049, 20992, 12050, 21147, 12051, 21241, 12052, 21269, 12053, 21274, - 12054, 21304, 12055, 21313, 12056, 21340, 12057, 21353, 12058, 21378, 12059, 21430, - 12060, 21448, 12061, 21475, 12062, 22231, 12063, 22303, 12064, 22763, 12065, 22786, - 12066, 22794, 12067, 22805, 12068, 22823, 12069, 22899, 12070, 23376, 12071, 23424, - 12072, 23544, 12073, 23567, 12074, 23586, 12075, 23608, 12076, 23662, 12077, 23665, - 12078, 24027, 12079, 24037, 12080, 24049, 12081, 24062, 12082, 24178, 12083, 24186, - 12084, 24191, 12085, 24308, 12086, 24318, 12087, 24331, 12088, 24339, 12089, 24400, - 12090, 24417, 12091, 24435, 12092, 24515, 12093, 25096, 12094, 25142, 12095, 25163, - 12096, 25903, 12097, 25908, 12098, 25991, 12099, 26007, 12100, 26020, 12101, 26041, - 12102, 26080, 12103, 26085, 12104, 26352, 12105, 26376, 12106, 26408, 12107, 27424, - 12108, 27490, 12109, 27513, 12110, 27571, 12111, 27595, 12112, 27604, 12113, 27611, - 12114, 27663, 12115, 27668, 12116, 27700, 12117, 28779, 12118, 29226, 12119, 29238, - 12120, 29243, 12121, 29247, 12122, 29255, 12123, 29273, 12124, 29275, 12125, 29356, - 12126, 29572, 12127, 29577, 12128, 29916, 12129, 29926, 12130, 29976, 12131, 29983, - 12132, 29992, 12133, 30000, 12134, 30091, 12135, 30098, 12136, 30326, 12137, 30333, - 12138, 30382, 12139, 30399, 12140, 30446, 12141, 30683, 12142, 30690, 12143, 30707, - 12144, 31034, 12145, 31160, 12146, 31166, 12147, 31348, 12148, 31435, 12149, 31481, - 12150, 31859, 12151, 31992, 12152, 32566, 12153, 32593, 12154, 32650, 12155, 32701, - 12156, 32769, 12157, 32780, 12158, 32786, 12159, 32819, 12160, 32895, 12161, 32905, - 12162, 33251, 12163, 33258, 12164, 33267, 12165, 33276, 12166, 33292, 12167, 33307, - 12168, 33311, 12169, 33390, 12170, 33394, 12171, 33400, 12172, 34381, 12173, 34411, - 12174, 34880, 12175, 34892, 12176, 34915, 12177, 35198, 12178, 35211, 12179, 35282, - 12180, 35328, 12181, 35895, 12182, 35910, 12183, 35925, 12184, 35960, 12185, 35997, - 12186, 36196, 12187, 36208, 12188, 36275, 12189, 36523, 12190, 36554, 12191, 36763, - 12192, 36784, 12193, 36789, 12194, 37009, 12195, 37193, 12196, 37318, 12197, 37324, - 12198, 37329, 12199, 38263, 12200, 38272, 12201, 38428, 12202, 38582, 12203, 38585, - 12204, 38632, 12205, 38737, 12206, 38750, 12207, 38754, 12208, 38761, 12209, 38859, - 12210, 38893, 12211, 38899, 12212, 38913, 12213, 39080, 12214, 39131, 12215, 39135, - 12216, 39318, 12217, 39321, 12218, 39340, 12219, 39592, 12220, 39640, 12221, 39647, - 12222, 39717, 12223, 39727, 12224, 39730, 12225, 39740, 12226, 39770, 12227, 40165, - 12228, 40565, 12229, 40575, 12230, 40613, 12231, 40635, 12232, 40643, 12233, 40653, - 12234, 40657, 12235, 40697, 12236, 40701, 12237, 40718, 12238, 40723, 12239, 40736, - 12240, 40763, 12241, 40778, 12242, 40786, 12243, 40845, 12244, 40860, 12245, 40864, - 12246, -12247, 12288, 32, 12289, -12290, 12330, 0, 12336, -12337, 12342, 12306, - 12343, 12343, 12344, 21313, 12345, -21317, 12347, -12348, 12364, 2097154, 12388, 2097152, - 12395, -12396, 12400, 12399, 12402, 12402, 12405, 12405, 12408, 12408, 12411, 12411, - 12414, -12415, 12436, 12358, 12437, -12438, 12441, 0, 12443, 32, 12445, 2097154, - 12448, -12449, 12460, 2097154, 12484, 2097152, 12491, -12492, 12496, 12495, 12498, 12498, - 12501, 12501, 12504, 12504, 12507, 12507, 12510, -12511, 12532, 12454, 12533, -12534, - 12535, -12528, 12539, 12539, 12540, 0, 12541, 2097154, 12544, -12545, 12593, -4353, - 12595, 4522, 12596, 4354, 12597, -4525, 12599, -4356, 12602, -4529, 12608, 4378, - 12609, -4359, 12612, 4385, 12613, -4362, 12623, -4450, 12644, 4448, 12645, -4373, - 12647, -4552, 12649, 4556, 12650, 4558, 12651, 4563, 12652, 4567, 12653, 4569, - 12654, 4380, 12655, 4573, 12656, 4575, 12657, -4382, 12659, 4384, 12660, -4387, - 12662, 4391, 12663, 4393, 12664, -4396, 12669, 4402, 12670, 4406, 12671, 4416, - 12672, 4423, 12673, 4428, 12674, -4594, 12676, -4440, 12679, -4485, 12681, 4488, - 12682, -4498, 12684, 4500, 12685, 4510, 12686, 4513, 12687, -12688, 12690, 19968, - 12691, 20108, 12692, 19977, 12693, 22235, 12694, 19978, 12695, 20013, 12696, 19979, - 12697, 30002, 12698, 20057, 12699, 19993, 12700, 19969, 12701, 22825, 12702, 22320, - 12703, 20154, 12704, -12705, 12868, 21839, 12869, 24188, 12870, 25991, 12871, 31631, - 12872, -12873, 12896, 4352, 12897, -4355, 12899, -4358, 12902, 4361, 12903, -4364, - 12905, -4367, 12910, -12911, 12928, 19968, 12929, 20108, 12930, 19977, 12931, 22235, - 12932, 20116, 12933, 20845, 12934, 19971, 12935, 20843, 12936, 20061, 12937, 21313, - 12938, 26376, 12939, 28779, 12940, 27700, 12941, 26408, 12942, 37329, 12943, 22303, - 12944, 26085, 12945, 26666, 12946, 26377, 12947, 31038, 12948, 21517, 12949, 29305, - 12950, 36001, 12951, 31069, 12952, 21172, 12953, 31192, 12954, 30007, 12955, 22899, - 12956, 36969, 12957, 20778, 12958, 21360, 12959, 27880, 12960, 38917, 12961, 20241, - 12962, 20889, 12963, 27491, 12964, 19978, 12965, 20013, 12966, 19979, 12967, 24038, - 12968, 21491, 12969, 21307, 12970, 23447, 12971, 23398, 12972, 30435, 12973, 20225, - 12974, 36039, 12975, 21332, 12976, 22812, 12977, -12978, 13008, 12450, 13009, 12452, - 13010, 12454, 13011, 12456, 13012, -12459, 13014, 12461, 13015, 12463, 13016, 12465, - 13017, 12467, 13018, 12469, 13019, 12471, 13020, 12473, 13021, 12475, 13022, 12477, - 13023, 12479, 13024, 12481, 13025, 12484, 13026, 12486, 13027, 12488, 13028, -12491, - 13034, 12498, 13035, 12501, 13036, 12504, 13037, 12507, 13038, -12511, 13043, 12516, - 13044, 12518, 13045, -12521, 13051, -12528, 13055, -13056, 42607, 0, 42611, 42611, - 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, 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, 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, 69888, 0, - 69891, -69892, 69927, 0, 69941, -69942, 70003, 0, 70004, -70005, 70016, 0, - 70019, -70020, 70067, 0, 70081, -70082, 70090, 0, 70093, -70094, 70188, 0, - 70200, -70201, 70206, 0, 70207, -70208, 70367, 0, 70379, -70380, 70400, 0, - 70404, -70405, 70460, 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, 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, 72751, 0, 72759, 72759, 72760, 0, - 72768, -72769, 72850, 0, 72872, 72872, 72873, 0, 72887, -72888, 92912, 0, - 92917, -92918, 92976, 0, 92983, -92984, 94033, 0, 94079, -94080, 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, - 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, 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, 194579, 13497, 194580, 20839, 194581, 20877, 194582, 132427, - 194583, 20887, 194584, 20900, 194585, 20172, 194586, 20908, 194587, 20917, 194588, 168415, - 194589, 20981, 194590, 20995, 194591, 13535, 194592, 21051, 194593, 21062, 194594, 21106, - 194595, 21111, 194596, 13589, 194597, 21191, 194598, 21193, 194599, 21220, 194600, 21242, - 194601, -21254, 194603, 21271, 194604, 21321, 194605, 21329, 194606, 21338, 194607, 21363, - 194608, 21373, 194609, 21375, 194612, 133676, 194613, 28784, 194614, 21450, 194615, 21471, - 194616, 133987, 194617, 21483, 194618, 21489, 194619, 21510, 194620, 21662, 194621, 21560, - 194622, 21576, 194623, 21608, 194624, 21666, 194625, 21750, 194626, 21776, 194627, 21843, - 194628, 21859, 194629, 21892, 194631, 21913, 194632, 21931, 194633, 21939, 194634, 21954, - 194635, 22294, 194636, 22022, 194637, 22295, 194638, 22097, 194639, 22132, 194640, 20999, - 194641, 22766, 194642, 22478, 194643, 22516, 194644, 22541, 194645, 22411, 194646, 22578, - 194647, 22577, 194648, 22700, 194649, 136420, 194650, 22770, 194651, 22775, 194652, 22790, - 194653, 22810, 194654, 22818, 194655, 22882, 194656, 136872, 194657, 136938, 194658, 23020, - 194659, 23067, 194660, 23079, 194661, 23000, 194662, 23142, 194663, 14062, 194664, 14076, - 194665, 23304, 194666, 23358, 194668, 137672, 194669, 23491, 194670, 23512, 194671, 23527, - 194672, 23539, 194673, 138008, 194674, 23551, 194675, 23558, 194676, 24403, 194677, 23586, - 194678, 14209, 194679, 23648, 194680, 23662, 194681, 23744, 194682, 23693, 194683, 138724, - 194684, 23875, 194685, 138726, 194686, 23918, 194687, 23915, 194688, 23932, 194689, -24034, - 194691, 14383, 194692, 24061, 194693, 24104, 194694, 24125, 194695, 24169, 194696, 14434, - 194697, 139651, 194698, 14460, 194699, 24240, 194700, 24243, 194701, 24246, 194702, 24266, - 194703, 172946, 194704, 24318, 194705, 140081, 194707, 33281, 194708, 24354, 194710, 14535, - 194711, 144056, 194712, 156122, 194713, 24418, 194714, 24427, 194715, 14563, 194716, 24474, - 194717, 24525, 194718, 24535, 194719, 24569, 194720, 24705, 194721, 14650, 194722, 14620, - 194723, 24724, 194724, 141012, 194725, 24775, 194726, 24904, 194727, 24908, 194728, 24910, - 194729, 24908, 194730, 24954, 194731, 24974, 194732, 25010, 194733, 24996, 194734, 25007, - 194735, 25054, 194736, 25074, 194737, 25078, 194738, 25104, 194739, 25115, 194740, 25181, - 194741, 25265, 194742, 25300, 194743, 25424, 194744, 142092, 194745, 25405, 194746, 25340, - 194747, 25448, 194748, 25475, 194749, 25572, 194750, 142321, 194751, 25634, 194752, 25541, - 194753, 25513, 194754, 14894, 194755, 25705, 194756, 25726, 194757, 25757, 194758, 25719, - 194759, 14956, 194760, 25935, 194761, 25964, 194762, 143370, 194763, 26083, 194764, 26360, - 194765, 26185, 194766, 15129, 194767, 26257, 194768, 15112, 194769, 15076, 194770, 20882, - 194771, 20885, 194772, 26368, 194773, 26268, 194774, 32941, 194775, 17369, 194776, 26391, - 194777, 26395, 194778, 26401, 194779, 26462, 194780, 26451, 194781, 144323, 194782, 15177, - 194783, 26618, 194784, 26501, 194785, 26706, 194786, 26757, 194787, 144493, 194788, 26766, - 194789, 26655, 194790, 26900, 194791, 15261, 194792, 26946, 194793, 27043, 194794, 27114, - 194795, 27304, 194796, 145059, 194797, 27355, 194798, 15384, 194799, 27425, 194800, 145575, - 194801, 27476, 194802, 15438, 194803, 27506, 194804, 27551, 194805, -27579, 194807, 146061, - 194808, 138507, 194809, 146170, 194810, 27726, 194811, 146620, 194812, 27839, 194813, 27853, - 194814, 27751, 194815, 27926, 194816, 27966, 194817, 28023, 194818, 27969, 194819, 28009, - 194820, 28024, 194821, 28037, 194822, 146718, 194823, 27956, 194824, 28207, 194825, 28270, - 194826, 15667, 194827, 28363, 194828, 28359, 194829, 147153, 194830, 28153, 194831, 28526, - 194832, 147294, 194833, 147342, 194834, 28614, 194835, 28729, 194836, 28702, 194837, 28699, - 194838, 15766, 194839, 28746, 194840, 28797, 194841, 28791, 194842, 28845, 194843, 132389, - 194844, 28997, 194845, 148067, 194846, 29084, 194847, 148395, 194848, 29224, 194849, 29237, - 194850, 29264, 194851, 149000, 194852, 29312, 194853, 29333, 194854, 149301, 194855, 149524, - 194856, 29562, 194857, 29579, 194858, 16044, 194859, 29605, 194860, 16056, 194862, 29767, - 194863, 29788, 194864, 29809, 194865, 29829, 194866, 29898, 194867, 16155, 194868, 29988, - 194869, 150582, 194870, 30014, 194871, 150674, 194872, 30064, 194873, 139679, 194874, 30224, - 194875, 151457, 194876, 151480, 194877, 151620, 194878, 16380, 194879, 16392, 194880, 30452, - 194881, 151795, 194882, 151794, 194883, 151833, 194884, 151859, 194885, -30495, 194887, 30495, - 194888, 30538, 194889, 16441, 194890, 30603, 194891, 16454, 194892, 16534, 194893, 152605, - 194894, 30798, 194895, 30860, 194896, 30924, 194897, 16611, 194898, 153126, 194899, 31062, - 194900, 153242, 194901, 153285, 194902, 31119, 194903, 31211, 194904, 16687, 194905, 31296, - 194906, 31306, 194907, 31311, 194908, 153980, 194909, 154279, 194911, 31470, 194912, 16898, - 194913, 154539, 194914, 31686, 194915, 31689, 194916, 16935, 194917, 154752, 194918, 31954, - 194919, 17056, 194920, 31976, 194921, 31971, 194922, 32000, 194923, 155526, 194924, 32099, - 194925, 17153, 194926, 32199, 194927, 32258, 194928, 32325, 194929, 17204, 194930, 156200, - 194931, 156231, 194932, 17241, 194933, 156377, 194934, 32634, 194935, 156478, 194936, 32661, - 194937, 32762, 194938, 32773, 194939, 156890, 194940, 156963, 194941, 32864, 194942, 157096, - 194943, 32880, 194944, 144223, 194945, 17365, 194946, 32946, 194947, 33027, 194948, 17419, - 194949, 33086, 194950, 23221, 194951, 157607, 194952, 157621, 194953, 144275, 194954, 144284, - 194955, 33281, 194956, 33284, 194957, 36766, 194958, 17515, 194959, 33425, 194960, 33419, - 194961, 33437, 194962, 21171, 194963, 33457, 194964, 33459, 194965, 33469, 194966, 33510, - 194967, 158524, 194968, 33509, 194969, 33565, 194970, 33635, 194971, 33709, 194972, 33571, - 194973, 33725, 194974, 33767, 194975, 33879, 194976, 33619, 194977, 33738, 194978, 33740, - 194979, 33756, 194980, 158774, 194981, 159083, 194982, 158933, 194983, 17707, 194984, 34033, - 194985, 34035, 194986, 34070, 194987, 160714, 194988, 34148, 194989, 159532, 194990, 17757, - 194991, 17761, 194992, 159665, 194993, 159954, 194994, 17771, 194995, 34384, 194996, 34396, - 194997, 34407, 194998, 34409, 194999, 34473, 195000, 34440, 195001, 34574, 195002, 34530, - 195003, 34681, 195004, 34600, 195005, 34667, 195006, 34694, 195007, 17879, 195008, 34785, - 195009, 34817, 195010, 17913, 195011, 34912, 195012, 34915, 195013, 161383, 195014, 35031, - 195015, 35038, 195016, 17973, 195017, 35066, 195018, 13499, 195019, 161966, 195020, 162150, - 195021, 18110, 195022, 18119, 195023, 35488, 195024, 35565, 195025, 35722, 195026, 35925, - 195027, 162984, 195028, 36011, 195029, 36033, 195030, 36123, 195031, 36215, 195032, 163631, - 195033, 133124, 195034, 36299, 195035, 36284, 195036, 36336, 195037, 133342, 195038, 36564, - 195039, 36664, 195040, 165330, 195041, 165357, 195042, 37012, 195043, 37105, 195044, 37137, - 195045, 165678, 195046, 37147, 195047, 37432, 195048, -37592, 195050, 37500, 195051, 37881, - 195052, 37909, 195053, 166906, 195054, 38283, 195055, 18837, 195056, 38327, 195057, 167287, - 195058, 18918, 195059, 38595, 195060, 23986, 195061, 38691, 195062, 168261, 195063, 168474, - 195064, 19054, 195065, 19062, 195066, 38880, 195067, 168970, 195068, 19122, 195069, 169110, - 195070, 38923, 195072, 38953, 195073, 169398, 195074, 39138, 195075, 19251, 195076, 39209, - 195077, 39335, 195078, 39362, 195079, 39422, 195080, 19406, 195081, 170800, 195082, 39698, - 195083, 40000, 195084, 40189, 195085, 19662, 195086, 19693, 195087, 40295, 195088, 172238, - 195089, 19704, 195090, 172293, 195091, 172558, 195092, 172689, 195093, 40635, 195094, 19798, - 195095, 40697, 195096, 40702, 195097, 40709, 195098, 40719, 195099, 40726, 195100, 40763, - 195101, 173568, 195102, -195103, 917505, 0, 917506, -917507, 917536, 0, 917632, -917633, - 917760, 0, 918000, -918001, 2147483647, 0}; + 1280, -1281, 1425, 0, 1470, 1470, 1471, 0, 1472, 1472, 1473, 0, + 1475, 1475, 1476, 0, 1478, 1478, 1479, 0, 1480, -1481, 1536, 0, + 1542, -1543, 1552, 0, 1563, 1563, 1564, 0, 1565, -1566, 1570, 1575, + 1572, 1608, 1573, 1575, 1574, 1610, 1575, -1576, 1611, 0, 1632, -1633, + 1648, 0, 1649, -1650, 1728, 1749, 1729, 2097154, 1732, -1733, 1747, 2097152, + 1749, 1749, 1750, 0, 1758, 1758, 1759, 0, 1765, -1766, 1767, 0, + 1769, 1769, 1770, 0, 1774, -1775, 1807, 0, 1808, 1808, 1809, 0, + 1810, -1811, 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, + 2036, -2037, 2045, 0, 2046, -2047, 2070, 0, 2074, 2074, 2075, 0, + 2084, 2084, 2085, 0, 2088, 2088, 2089, 0, 2094, -2095, 2137, 0, + 2140, -2141, 2259, 0, 2308, -2309, 2345, 2097152, 2347, -2348, 2353, 2097152, + 2355, 2097154, 2358, -2359, 2362, 0, 2365, 2365, 2366, 0, 2384, 2384, + 2385, 0, 2392, -2326, 2395, 2332, 2396, -2338, 2398, 2347, 2399, 2351, + 2400, -2401, 2402, 0, 2404, -2405, 2433, 0, 2436, -2437, 2492, 0, + 2493, 2493, 2494, 0, 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, + 2510, -2511, 2519, 0, 2520, -2521, 2524, -2466, 2526, 2526, 2527, 2479, + 2528, -2529, 2530, 0, 2532, -2533, 2558, 0, 2559, -2560, 2561, 0, + 2564, -2565, 2611, 2097152, 2613, 2613, 2614, 2616, 2615, -2616, 2620, 0, + 2621, 2621, 2622, 0, 2627, -2628, 2631, 0, 2633, -2634, 2635, 0, + 2638, -2639, 2641, 0, 2642, -2643, 2649, -2583, 2651, 2588, 2652, -2653, + 2654, 2603, 2655, -2656, 2672, 0, 2674, -2675, 2677, 0, 2678, -2679, + 2689, 0, 2692, -2693, 2748, 0, 2749, 2749, 2750, 0, 2758, 2758, + 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, + 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, + 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0, 3172, -3173, + 3201, 0, 3204, -3205, 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, -3407, + 3415, 0, 3416, -3417, 3426, 0, 3428, -3429, 3458, 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, + 3764, 0, 3773, -3774, 3784, 0, 3790, -3791, 3852, 2097154, 3854, -3855, + 3864, 0, 3866, -3867, 3893, 0, 3894, 3894, 3895, 0, 3896, 3896, + 3897, 0, 3898, -3899, 3902, 0, 3904, -3905, 3907, 2097152, 3909, -3910, + 3917, 2097152, 3919, -3920, 3922, 2097154, 3924, -3925, 3927, 2097152, 3929, -3930, + 3932, 2097154, 3934, -3935, 3945, 3904, 3946, -3947, 3953, 0, 3973, 3973, + 3974, 0, 3976, -3977, 3981, 0, 3992, 3992, 3993, 0, 4029, -4030, + 4038, 0, 4039, -4040, 4134, 2097154, 4136, -4137, 4139, 0, 4159, -4160, + 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, -4255, 4348, 4316, 4349, -4350, + 4957, 0, 4960, -4961, 5906, 0, 5909, -5910, 5938, 0, 5941, -5942, + 5970, 0, 5972, -5973, 6002, 0, 6004, -6005, 6068, 0, 6100, -6101, + 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, + 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, + 7401, -7402, 7405, 0, 7406, -7407, 7412, 0, 7413, -7414, 7415, 0, + 7418, -7419, 7468, 65, 7469, 198, 7470, 66, 7471, 7471, 7472, -69, + 7474, 398, 7475, -72, 7483, 7483, 7484, 79, 7485, 546, 7486, 80, + 7487, 82, 7488, -85, 7490, 87, 7491, 97, 7492, -593, 7494, 7426, + 7495, 98, 7496, -101, 7498, 601, 7499, -604, 7501, 103, 7502, 7502, + 7503, 107, 7504, 109, 7505, 331, 7506, 111, 7507, 596, 7508, -7447, + 7510, 112, 7511, -117, 7513, 7453, 7514, 623, 7515, 118, 7516, 7461, + 7517, -947, 7520, -967, 7522, 105, 7523, 114, 7524, -118, 7526, -947, + 7528, 961, 7529, -967, 7531, -7532, 7544, 1085, 7545, -7546, 7579, 594, + 7580, 99, 7581, 597, 7582, 240, 7583, 604, 7584, 102, 7585, 607, + 7586, 609, 7587, 613, 7588, -617, 7591, 7547, 7592, 669, 7593, 621, + 7594, 7557, 7595, 671, 7596, 625, 7597, 624, 7598, -627, 7602, 632, + 7603, -643, 7605, 427, 7606, -650, 7608, 7452, 7609, -652, 7611, 122, + 7612, -657, 7615, 952, 7616, 0, 7674, 7674, 7675, 0, 7680, 65, + 7681, 97, 7682, 66, 7683, 98, 7684, 66, 7685, 98, 7686, 66, + 7687, 98, 7688, 67, 7689, 99, 7690, 68, 7691, 100, 7692, 68, + 7693, 100, 7694, 68, 7695, 100, 7696, 68, 7697, 100, 7698, 68, + 7699, 100, 7700, 69, 7701, 101, 7702, 69, 7703, 101, 7704, 69, + 7705, 101, 7706, 69, 7707, 101, 7708, 69, 7709, 101, 7710, 70, + 7711, 102, 7712, 71, 7713, 103, 7714, 72, 7715, 104, 7716, 72, + 7717, 104, 7718, 72, 7719, 104, 7720, 72, 7721, 104, 7722, 72, + 7723, 104, 7724, 73, 7725, 105, 7726, 73, 7727, 105, 7728, 75, + 7729, 107, 7730, 75, 7731, 107, 7732, 75, 7733, 107, 7734, 76, + 7735, 108, 7736, 76, 7737, 108, 7738, 76, 7739, 108, 7740, 76, + 7741, 108, 7742, 77, 7743, 109, 7744, 77, 7745, 109, 7746, 77, + 7747, 109, 7748, 78, 7749, 110, 7750, 78, 7751, 110, 7752, 78, + 7753, 110, 7754, 78, 7755, 110, 7756, 79, 7757, 111, 7758, 79, + 7759, 111, 7760, 79, 7761, 111, 7762, 79, 7763, 111, 7764, 80, + 7765, 112, 7766, 80, 7767, 112, 7768, 82, 7769, 114, 7770, 82, + 7771, 114, 7772, 82, 7773, 114, 7774, 82, 7775, 114, 7776, 83, + 7777, 115, 7778, 83, 7779, 115, 7780, 83, 7781, 115, 7782, 83, + 7783, 115, 7784, 83, 7785, 115, 7786, 84, 7787, 116, 7788, 84, + 7789, 116, 7790, 84, 7791, 116, 7792, 84, 7793, 116, 7794, 85, + 7795, 117, 7796, 85, 7797, 117, 7798, 85, 7799, 117, 7800, 85, + 7801, 117, 7802, 85, 7803, 117, 7804, 86, 7805, 118, 7806, 86, + 7807, 118, 7808, 87, 7809, 119, 7810, 87, 7811, 119, 7812, 87, + 7813, 119, 7814, 87, 7815, 119, 7816, 87, 7817, 119, 7818, 88, + 7819, 120, 7820, 88, 7821, 120, 7822, 89, 7823, 121, 7824, 90, + 7825, 122, 7826, 90, 7827, 122, 7828, 90, 7829, 122, 7830, 104, + 7831, 116, 7832, 119, 7833, 121, 7834, 97, 7835, 115, 7836, -7837, + 7840, 65, 7841, 97, 7842, 65, 7843, 97, 7844, 65, 7845, 97, + 7846, 65, 7847, 97, 7848, 65, 7849, 97, 7850, 65, 7851, 97, + 7852, 65, 7853, 97, 7854, 65, 7855, 97, 7856, 65, 7857, 97, + 7858, 65, 7859, 97, 7860, 65, 7861, 97, 7862, 65, 7863, 97, + 7864, 69, 7865, 101, 7866, 69, 7867, 101, 7868, 69, 7869, 101, + 7870, 69, 7871, 101, 7872, 69, 7873, 101, 7874, 69, 7875, 101, + 7876, 69, 7877, 101, 7878, 69, 7879, 101, 7880, 73, 7881, 105, + 7882, 73, 7883, 105, 7884, 79, 7885, 111, 7886, 79, 7887, 111, + 7888, 79, 7889, 111, 7890, 79, 7891, 111, 7892, 79, 7893, 111, + 7894, 79, 7895, 111, 7896, 79, 7897, 111, 7898, 79, 7899, 111, + 7900, 79, 7901, 111, 7902, 79, 7903, 111, 7904, 79, 7905, 111, + 7906, 79, 7907, 111, 7908, 85, 7909, 117, 7910, 85, 7911, 117, + 7912, 85, 7913, 117, 7914, 85, 7915, 117, 7916, 85, 7917, 117, + 7918, 85, 7919, 117, 7920, 85, 7921, 117, 7922, 89, 7923, 121, + 7924, 89, 7925, 121, 7926, 89, 7927, 121, 7928, 89, 7929, 121, + 7930, -7931, 7936, 945, 7944, 913, 7952, 949, 7958, -7959, 7960, 917, + 7966, -7967, 7968, 951, 7976, 919, 7984, 953, 7992, 921, 8000, 959, + 8006, -8007, 8008, 927, 8014, -8015, 8016, 965, 8024, 8024, 8025, 933, + 8026, 8026, 8027, 933, 8028, 8028, 8029, 933, 8030, 8030, 8031, 933, + 8032, 969, 8040, 937, 8048, 945, 8050, 949, 8052, 951, 8054, 953, + 8056, 959, 8058, 965, 8060, 969, 8062, -8063, 8064, 945, 8072, 913, + 8080, 951, 8088, 919, 8096, 969, 8104, 937, 8112, 945, 8117, 8117, + 8118, 945, 8120, 913, 8125, 32, 8126, 953, 8127, 32, 8130, 951, + 8133, 8133, 8134, 951, 8136, 917, 8138, 919, 8141, 32, 8144, 953, + 8148, -8149, 8150, 953, 8152, 921, 8156, 8156, 8157, 32, 8160, 965, + 8164, 961, 8166, 965, 8168, 933, 8172, 929, 8173, 32, 8175, 96, + 8176, -8177, 8178, 969, 8181, 8181, 8182, 969, 8184, 927, 8186, 937, + 8189, 32, 8191, 8191, 8192, 32, 8203, 0, 8208, 2097152, 8211, -8212, + 8215, 32, 8216, -8217, 8228, 46, 8229, -8230, 8234, 0, 8239, 32, + 8240, -8241, 8254, 32, 8255, -8256, 8287, 32, 8288, 0, 8293, 8293, + 8294, 0, 8304, 48, 8305, 105, 8306, -8307, 8308, -53, 8314, 43, + 8315, 8722, 8316, 61, 8317, -41, 8319, 110, 8320, -49, 8330, 43, + 8331, 8722, 8332, 61, 8333, -41, 8335, 8335, 8336, 97, 8337, 101, + 8338, 111, 8339, 120, 8340, 601, 8341, 104, 8342, -108, 8346, 112, + 8347, -116, 8349, -8350, 8400, 0, 8433, -8434, 8450, 67, 8452, -8453, + 8455, 400, 8456, 8456, 8457, 70, 8458, 103, 8459, 72, 8462, 104, + 8463, 295, 8464, 73, 8466, 76, 8467, 108, 8468, 8468, 8469, 78, + 8470, -8471, 8473, -81, 8476, 82, 8478, -8479, 8484, 90, 8485, 8485, + 8486, 937, 8487, 8487, 8488, 90, 8489, 8489, 8490, 75, 8491, -66, + 8494, 8494, 8495, 101, 8496, -70, 8498, 8498, 8499, 77, 8500, 111, + 8501, -1489, 8505, 105, 8506, -8507, 8508, 960, 8509, 947, 8510, 915, + 8511, 928, 8512, 8721, 8513, -8514, 8517, 68, 8518, -101, 8520, -106, + 8522, -8523, 8543, 49, 8544, 73, 8545, -8546, 8548, 86, 8549, -8550, + 8553, 88, 8554, -8555, 8556, 76, 8557, -68, 8559, 77, 8560, 105, + 8561, -8562, 8564, 118, 8565, -8566, 8569, 120, 8570, -8571, 8572, 108, + 8573, -100, 8575, 109, 8576, -8577, 8602, 8592, 8603, 8594, 8604, -8605, + 8622, 8596, 8623, -8624, 8653, 8656, 8654, 8660, 8655, 8658, 8656, -8657, + 8708, 2097154, 8710, -8711, 8713, 2097152, 8715, 2097154, 8718, -8719, 8740, 2097154, + 8744, -8745, 8769, 8764, 8770, -8771, 8772, 2097154, 8774, 8774, 8775, 8773, + 8776, 2097152, 8779, -8780, 8800, 61, 8801, 2097154, 8804, -8805, 8813, 8781, + 8814, 60, 8815, 62, 8816, -8805, 8818, -8819, 8820, -8819, 8822, -8823, + 8824, -8823, 8826, -8827, 8832, -8827, 8834, -8835, 8836, -8835, 8838, -8839, + 8840, -8839, 8842, -8843, 8876, 8866, 8877, -8873, 8879, 8875, 8880, -8881, + 8928, -8829, 8930, -8850, 8932, -8933, 8938, -8883, 8942, -8943, 9001, -12297, + 9003, -9004, 9312, -50, 9321, -9322, 9352, -50, 9361, -9362, 9398, -66, + 9424, -98, 9450, 48, 9451, -9452, 10972, 10973, 10974, -10975, 11388, 106, + 11389, 86, 11390, -11391, 11503, 0, 11506, -11507, 11631, 11617, 11632, -11633, + 11647, 0, 11648, -11649, 11744, 0, 11776, -11777, 11935, 27597, 11936, -11937, + 12019, 40863, 12020, -12021, 12032, 19968, 12033, 20008, 12034, 20022, 12035, 20031, + 12036, 20057, 12037, 20101, 12038, 20108, 12039, 20128, 12040, 20154, 12041, 20799, + 12042, 20837, 12043, 20843, 12044, 20866, 12045, 20886, 12046, 20907, 12047, 20960, + 12048, 20981, 12049, 20992, 12050, 21147, 12051, 21241, 12052, 21269, 12053, 21274, + 12054, 21304, 12055, 21313, 12056, 21340, 12057, 21353, 12058, 21378, 12059, 21430, + 12060, 21448, 12061, 21475, 12062, 22231, 12063, 22303, 12064, 22763, 12065, 22786, + 12066, 22794, 12067, 22805, 12068, 22823, 12069, 22899, 12070, 23376, 12071, 23424, + 12072, 23544, 12073, 23567, 12074, 23586, 12075, 23608, 12076, 23662, 12077, 23665, + 12078, 24027, 12079, 24037, 12080, 24049, 12081, 24062, 12082, 24178, 12083, 24186, + 12084, 24191, 12085, 24308, 12086, 24318, 12087, 24331, 12088, 24339, 12089, 24400, + 12090, 24417, 12091, 24435, 12092, 24515, 12093, 25096, 12094, 25142, 12095, 25163, + 12096, 25903, 12097, 25908, 12098, 25991, 12099, 26007, 12100, 26020, 12101, 26041, + 12102, 26080, 12103, 26085, 12104, 26352, 12105, 26376, 12106, 26408, 12107, 27424, + 12108, 27490, 12109, 27513, 12110, 27571, 12111, 27595, 12112, 27604, 12113, 27611, + 12114, 27663, 12115, 27668, 12116, 27700, 12117, 28779, 12118, 29226, 12119, 29238, + 12120, 29243, 12121, 29247, 12122, 29255, 12123, 29273, 12124, 29275, 12125, 29356, + 12126, 29572, 12127, 29577, 12128, 29916, 12129, 29926, 12130, 29976, 12131, 29983, + 12132, 29992, 12133, 30000, 12134, 30091, 12135, 30098, 12136, 30326, 12137, 30333, + 12138, 30382, 12139, 30399, 12140, 30446, 12141, 30683, 12142, 30690, 12143, 30707, + 12144, 31034, 12145, 31160, 12146, 31166, 12147, 31348, 12148, 31435, 12149, 31481, + 12150, 31859, 12151, 31992, 12152, 32566, 12153, 32593, 12154, 32650, 12155, 32701, + 12156, 32769, 12157, 32780, 12158, 32786, 12159, 32819, 12160, 32895, 12161, 32905, + 12162, 33251, 12163, 33258, 12164, 33267, 12165, 33276, 12166, 33292, 12167, 33307, + 12168, 33311, 12169, 33390, 12170, 33394, 12171, 33400, 12172, 34381, 12173, 34411, + 12174, 34880, 12175, 34892, 12176, 34915, 12177, 35198, 12178, 35211, 12179, 35282, + 12180, 35328, 12181, 35895, 12182, 35910, 12183, 35925, 12184, 35960, 12185, 35997, + 12186, 36196, 12187, 36208, 12188, 36275, 12189, 36523, 12190, 36554, 12191, 36763, + 12192, 36784, 12193, 36789, 12194, 37009, 12195, 37193, 12196, 37318, 12197, 37324, + 12198, 37329, 12199, 38263, 12200, 38272, 12201, 38428, 12202, 38582, 12203, 38585, + 12204, 38632, 12205, 38737, 12206, 38750, 12207, 38754, 12208, 38761, 12209, 38859, + 12210, 38893, 12211, 38899, 12212, 38913, 12213, 39080, 12214, 39131, 12215, 39135, + 12216, 39318, 12217, 39321, 12218, 39340, 12219, 39592, 12220, 39640, 12221, 39647, + 12222, 39717, 12223, 39727, 12224, 39730, 12225, 39740, 12226, 39770, 12227, 40165, + 12228, 40565, 12229, 40575, 12230, 40613, 12231, 40635, 12232, 40643, 12233, 40653, + 12234, 40657, 12235, 40697, 12236, 40701, 12237, 40718, 12238, 40723, 12239, 40736, + 12240, 40763, 12241, 40778, 12242, 40786, 12243, 40845, 12244, 40860, 12245, 40864, + 12246, -12247, 12288, 32, 12289, -12290, 12330, 0, 12336, -12337, 12342, 12306, + 12343, 12343, 12344, 21313, 12345, -21317, 12347, -12348, 12364, 2097154, 12388, 2097152, + 12395, -12396, 12400, 12399, 12402, 12402, 12405, 12405, 12408, 12408, 12411, 12411, + 12414, -12415, 12436, 12358, 12437, -12438, 12441, 0, 12443, 32, 12445, 2097154, + 12448, -12449, 12460, 2097154, 12484, 2097152, 12491, -12492, 12496, 12495, 12498, 12498, + 12501, 12501, 12504, 12504, 12507, 12507, 12510, -12511, 12532, 12454, 12533, -12534, + 12535, -12528, 12539, 12539, 12540, 0, 12541, 2097154, 12544, -12545, 12593, -4353, + 12595, 4522, 12596, 4354, 12597, -4525, 12599, -4356, 12602, -4529, 12608, 4378, + 12609, -4359, 12612, 4385, 12613, -4362, 12623, -4450, 12644, 4448, 12645, -4373, + 12647, -4552, 12649, 4556, 12650, 4558, 12651, 4563, 12652, 4567, 12653, 4569, + 12654, 4380, 12655, 4573, 12656, 4575, 12657, -4382, 12659, 4384, 12660, -4387, + 12662, 4391, 12663, 4393, 12664, -4396, 12669, 4402, 12670, 4406, 12671, 4416, + 12672, 4423, 12673, 4428, 12674, -4594, 12676, -4440, 12679, -4485, 12681, 4488, + 12682, -4498, 12684, 4500, 12685, 4510, 12686, 4513, 12687, -12688, 12690, 19968, + 12691, 20108, 12692, 19977, 12693, 22235, 12694, 19978, 12695, 20013, 12696, 19979, + 12697, 30002, 12698, 20057, 12699, 19993, 12700, 19969, 12701, 22825, 12702, 22320, + 12703, 20154, 12704, -12705, 12868, 21839, 12869, 24188, 12870, 25991, 12871, 31631, + 12872, -12873, 12896, 4352, 12897, -4355, 12899, -4358, 12902, 4361, 12903, -4364, + 12905, -4367, 12910, -12911, 12928, 19968, 12929, 20108, 12930, 19977, 12931, 22235, + 12932, 20116, 12933, 20845, 12934, 19971, 12935, 20843, 12936, 20061, 12937, 21313, + 12938, 26376, 12939, 28779, 12940, 27700, 12941, 26408, 12942, 37329, 12943, 22303, + 12944, 26085, 12945, 26666, 12946, 26377, 12947, 31038, 12948, 21517, 12949, 29305, + 12950, 36001, 12951, 31069, 12952, 21172, 12953, 31192, 12954, 30007, 12955, 22899, + 12956, 36969, 12957, 20778, 12958, 21360, 12959, 27880, 12960, 38917, 12961, 20241, + 12962, 20889, 12963, 27491, 12964, 19978, 12965, 20013, 12966, 19979, 12967, 24038, + 12968, 21491, 12969, 21307, 12970, 23447, 12971, 23398, 12972, 30435, 12973, 20225, + 12974, 36039, 12975, 21332, 12976, 22812, 12977, -12978, 13008, 12450, 13009, 12452, + 13010, 12454, 13011, 12456, 13012, -12459, 13014, 12461, 13015, 12463, 13016, 12465, + 13017, 12467, 13018, 12469, 13019, 12471, 13020, 12473, 13021, 12475, 13022, 12477, + 13023, 12479, 13024, 12481, 13025, 12484, 13026, 12486, 13027, 12488, 13028, -12491, + 13034, 12498, 13035, 12501, 13036, 12504, 13037, 12507, 13038, -12511, 13043, 12516, + 13044, 12518, 13045, -12521, 13051, -12528, 13055, -13056, 42607, 0, 42611, 42611, + 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, + 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, + 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, + 72759, 72759, 72760, 0, 72768, -72769, 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, -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, + 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, + 194579, 13497, 194580, 20839, 194581, 20877, 194582, 132427, 194583, 20887, 194584, 20900, + 194585, 20172, 194586, 20908, 194587, 20917, 194588, 168415, 194589, 20981, 194590, 20995, + 194591, 13535, 194592, 21051, 194593, 21062, 194594, 21106, 194595, 21111, 194596, 13589, + 194597, 21191, 194598, 21193, 194599, 21220, 194600, 21242, 194601, -21254, 194603, 21271, + 194604, 21321, 194605, 21329, 194606, 21338, 194607, 21363, 194608, 21373, 194609, 21375, + 194612, 133676, 194613, 28784, 194614, 21450, 194615, 21471, 194616, 133987, 194617, 21483, + 194618, 21489, 194619, 21510, 194620, 21662, 194621, 21560, 194622, 21576, 194623, 21608, + 194624, 21666, 194625, 21750, 194626, 21776, 194627, 21843, 194628, 21859, 194629, 21892, + 194631, 21913, 194632, 21931, 194633, 21939, 194634, 21954, 194635, 22294, 194636, 22022, + 194637, 22295, 194638, 22097, 194639, 22132, 194640, 20999, 194641, 22766, 194642, 22478, + 194643, 22516, 194644, 22541, 194645, 22411, 194646, 22578, 194647, 22577, 194648, 22700, + 194649, 136420, 194650, 22770, 194651, 22775, 194652, 22790, 194653, 22810, 194654, 22818, + 194655, 22882, 194656, 136872, 194657, 136938, 194658, 23020, 194659, 23067, 194660, 23079, + 194661, 23000, 194662, 23142, 194663, 14062, 194664, 14076, 194665, 23304, 194666, 23358, + 194668, 137672, 194669, 23491, 194670, 23512, 194671, 23527, 194672, 23539, 194673, 138008, + 194674, 23551, 194675, 23558, 194676, 24403, 194677, 23586, 194678, 14209, 194679, 23648, + 194680, 23662, 194681, 23744, 194682, 23693, 194683, 138724, 194684, 23875, 194685, 138726, + 194686, 23918, 194687, 23915, 194688, 23932, 194689, -24034, 194691, 14383, 194692, 24061, + 194693, 24104, 194694, 24125, 194695, 24169, 194696, 14434, 194697, 139651, 194698, 14460, + 194699, 24240, 194700, 24243, 194701, 24246, 194702, 24266, 194703, 172946, 194704, 24318, + 194705, 140081, 194707, 33281, 194708, 24354, 194710, 14535, 194711, 144056, 194712, 156122, + 194713, 24418, 194714, 24427, 194715, 14563, 194716, 24474, 194717, 24525, 194718, 24535, + 194719, 24569, 194720, 24705, 194721, 14650, 194722, 14620, 194723, 24724, 194724, 141012, + 194725, 24775, 194726, 24904, 194727, 24908, 194728, 24910, 194729, 24908, 194730, 24954, + 194731, 24974, 194732, 25010, 194733, 24996, 194734, 25007, 194735, 25054, 194736, 25074, + 194737, 25078, 194738, 25104, 194739, 25115, 194740, 25181, 194741, 25265, 194742, 25300, + 194743, 25424, 194744, 142092, 194745, 25405, 194746, 25340, 194747, 25448, 194748, 25475, + 194749, 25572, 194750, 142321, 194751, 25634, 194752, 25541, 194753, 25513, 194754, 14894, + 194755, 25705, 194756, 25726, 194757, 25757, 194758, 25719, 194759, 14956, 194760, 25935, + 194761, 25964, 194762, 143370, 194763, 26083, 194764, 26360, 194765, 26185, 194766, 15129, + 194767, 26257, 194768, 15112, 194769, 15076, 194770, 20882, 194771, 20885, 194772, 26368, + 194773, 26268, 194774, 32941, 194775, 17369, 194776, 26391, 194777, 26395, 194778, 26401, + 194779, 26462, 194780, 26451, 194781, 144323, 194782, 15177, 194783, 26618, 194784, 26501, + 194785, 26706, 194786, 26757, 194787, 144493, 194788, 26766, 194789, 26655, 194790, 26900, + 194791, 15261, 194792, 26946, 194793, 27043, 194794, 27114, 194795, 27304, 194796, 145059, + 194797, 27355, 194798, 15384, 194799, 27425, 194800, 145575, 194801, 27476, 194802, 15438, + 194803, 27506, 194804, 27551, 194805, -27579, 194807, 146061, 194808, 138507, 194809, 146170, + 194810, 27726, 194811, 146620, 194812, 27839, 194813, 27853, 194814, 27751, 194815, 27926, + 194816, 27966, 194817, 28023, 194818, 27969, 194819, 28009, 194820, 28024, 194821, 28037, + 194822, 146718, 194823, 27956, 194824, 28207, 194825, 28270, 194826, 15667, 194827, 28363, + 194828, 28359, 194829, 147153, 194830, 28153, 194831, 28526, 194832, 147294, 194833, 147342, + 194834, 28614, 194835, 28729, 194836, 28702, 194837, 28699, 194838, 15766, 194839, 28746, + 194840, 28797, 194841, 28791, 194842, 28845, 194843, 132389, 194844, 28997, 194845, 148067, + 194846, 29084, 194847, 148395, 194848, 29224, 194849, 29237, 194850, 29264, 194851, 149000, + 194852, 29312, 194853, 29333, 194854, 149301, 194855, 149524, 194856, 29562, 194857, 29579, + 194858, 16044, 194859, 29605, 194860, 16056, 194862, 29767, 194863, 29788, 194864, 29809, + 194865, 29829, 194866, 29898, 194867, 16155, 194868, 29988, 194869, 150582, 194870, 30014, + 194871, 150674, 194872, 30064, 194873, 139679, 194874, 30224, 194875, 151457, 194876, 151480, + 194877, 151620, 194878, 16380, 194879, 16392, 194880, 30452, 194881, 151795, 194882, 151794, + 194883, 151833, 194884, 151859, 194885, -30495, 194887, 30495, 194888, 30538, 194889, 16441, + 194890, 30603, 194891, 16454, 194892, 16534, 194893, 152605, 194894, 30798, 194895, 30860, + 194896, 30924, 194897, 16611, 194898, 153126, 194899, 31062, 194900, 153242, 194901, 153285, + 194902, 31119, 194903, 31211, 194904, 16687, 194905, 31296, 194906, 31306, 194907, 31311, + 194908, 153980, 194909, 154279, 194911, 31470, 194912, 16898, 194913, 154539, 194914, 31686, + 194915, 31689, 194916, 16935, 194917, 154752, 194918, 31954, 194919, 17056, 194920, 31976, + 194921, 31971, 194922, 32000, 194923, 155526, 194924, 32099, 194925, 17153, 194926, 32199, + 194927, 32258, 194928, 32325, 194929, 17204, 194930, 156200, 194931, 156231, 194932, 17241, + 194933, 156377, 194934, 32634, 194935, 156478, 194936, 32661, 194937, 32762, 194938, 32773, + 194939, 156890, 194940, 156963, 194941, 32864, 194942, 157096, 194943, 32880, 194944, 144223, + 194945, 17365, 194946, 32946, 194947, 33027, 194948, 17419, 194949, 33086, 194950, 23221, + 194951, 157607, 194952, 157621, 194953, 144275, 194954, 144284, 194955, 33281, 194956, 33284, + 194957, 36766, 194958, 17515, 194959, 33425, 194960, 33419, 194961, 33437, 194962, 21171, + 194963, 33457, 194964, 33459, 194965, 33469, 194966, 33510, 194967, 158524, 194968, 33509, + 194969, 33565, 194970, 33635, 194971, 33709, 194972, 33571, 194973, 33725, 194974, 33767, + 194975, 33879, 194976, 33619, 194977, 33738, 194978, 33740, 194979, 33756, 194980, 158774, + 194981, 159083, 194982, 158933, 194983, 17707, 194984, 34033, 194985, 34035, 194986, 34070, + 194987, 160714, 194988, 34148, 194989, 159532, 194990, 17757, 194991, 17761, 194992, 159665, + 194993, 159954, 194994, 17771, 194995, 34384, 194996, 34396, 194997, 34407, 194998, 34409, + 194999, 34473, 195000, 34440, 195001, 34574, 195002, 34530, 195003, 34681, 195004, 34600, + 195005, 34667, 195006, 34694, 195007, 17879, 195008, 34785, 195009, 34817, 195010, 17913, + 195011, 34912, 195012, 34915, 195013, 161383, 195014, 35031, 195015, 35038, 195016, 17973, + 195017, 35066, 195018, 13499, 195019, 161966, 195020, 162150, 195021, 18110, 195022, 18119, + 195023, 35488, 195024, 35565, 195025, 35722, 195026, 35925, 195027, 162984, 195028, 36011, + 195029, 36033, 195030, 36123, 195031, 36215, 195032, 163631, 195033, 133124, 195034, 36299, + 195035, 36284, 195036, 36336, 195037, 133342, 195038, 36564, 195039, 36664, 195040, 165330, + 195041, 165357, 195042, 37012, 195043, 37105, 195044, 37137, 195045, 165678, 195046, 37147, + 195047, 37432, 195048, -37592, 195050, 37500, 195051, 37881, 195052, 37909, 195053, 166906, + 195054, 38283, 195055, 18837, 195056, 38327, 195057, 167287, 195058, 18918, 195059, 38595, + 195060, 23986, 195061, 38691, 195062, 168261, 195063, 168474, 195064, 19054, 195065, 19062, + 195066, 38880, 195067, 168970, 195068, 19122, 195069, 169110, 195070, 38923, 195072, 38953, + 195073, 169398, 195074, 39138, 195075, 19251, 195076, 39209, 195077, 39335, 195078, 39362, + 195079, 39422, 195080, 19406, 195081, 170800, 195082, 39698, 195083, 40000, 195084, 40189, + 195085, 19662, 195086, 19693, 195087, 40295, 195088, 172238, 195089, 19704, 195090, 172293, + 195091, 172558, 195092, 172689, 195093, 40635, 195094, 19798, 195095, 40697, 195096, 40702, + 195097, 40709, 195098, 40719, 195099, 40726, 195100, 40763, 195101, 173568, 195102, -195103, + 917505, 0, 917506, -917507, 917536, 0, 917632, -917633, 917760, 0, 918000, -918001, + 2147483647, 0}; UnicodeSimpleCategory get_unicode_simple_category(uint32 code) { auto it = std::upper_bound(std::begin(unicode_simple_category_ranges), std::end(unicode_simple_category_ranges), diff --git a/tdutils/td/utils/unicode.h b/tdutils/td/utils/unicode.h index 7cced6d9..ba397918 100644 --- a/tdutils/td/utils/unicode.h +++ b/tdutils/td/utils/unicode.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/unique_ptr.h b/tdutils/td/utils/unique_ptr.h index 2fad9ede..f06903df 100644 --- a/tdutils/td/utils/unique_ptr.h +++ b/tdutils/td/utils/unique_ptr.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/utf8.cpp b/tdutils/td/utils/utf8.cpp index 0efcc691..9792a251 100644 --- a/tdutils/td/utils/utf8.cpp +++ b/tdutils/td/utils/utf8.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/td/utils/utf8.h b/tdutils/td/utils/utf8.h index 059416cd..b2da2d83 100644 --- a/tdutils/td/utils/utf8.h +++ b/tdutils/td/utils/utf8.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/ConcurrentHashMap.cpp b/tdutils/test/ConcurrentHashMap.cpp index 8a2d22aa..c71106c0 100644 --- a/tdutils/test/ConcurrentHashMap.cpp +++ b/tdutils/test/ConcurrentHashMap.cpp @@ -1,11 +1,12 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/utils/benchmark.h" #include "td/utils/ConcurrentHashTable.h" +#include "td/utils/misc.h" #include "td/utils/port/thread.h" #include "td/utils/SpinLock.h" #include "td/utils/tests.h" @@ -210,8 +211,8 @@ class HashMapBenchmark : public td::Benchmark { size_t r = n * (i + 1) / threads_n; threads.emplace_back([l, r, this] { for (size_t i = l; i < r; i++) { - auto x = int((i + 1) * mul_ % n_) + 3; - auto y = int(i + 2); + auto x = td::narrow_cast((i + 1) * mul_ % n_) + 3; + auto y = td::narrow_cast(i + 2); hash_map->insert(x, y); } }); @@ -223,8 +224,8 @@ class HashMapBenchmark : public td::Benchmark { void tear_down() override { for (int i = 0; i < n_; i++) { - auto x = int((i + 1) * mul_ % n_) + 3; - auto y = int(i + 2); + auto x = td::narrow_cast((i + 1) * mul_ % n_) + 3; + auto y = td::narrow_cast(i + 2); ASSERT_EQ(y, hash_map->find(x, -1)); } queries.clear(); diff --git a/tdutils/test/Enumerator.cpp b/tdutils/test/Enumerator.cpp index bf407dd8..e6fb6a08 100644 --- a/tdutils/test/Enumerator.cpp +++ b/tdutils/test/Enumerator.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/EpochBasedMemoryReclamation.cpp b/tdutils/test/EpochBasedMemoryReclamation.cpp index 021611e4..e55556ea 100644 --- a/tdutils/test/EpochBasedMemoryReclamation.cpp +++ b/tdutils/test/EpochBasedMemoryReclamation.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/HazardPointers.cpp b/tdutils/test/HazardPointers.cpp index 45b214db..4009b736 100644 --- a/tdutils/test/HazardPointers.cpp +++ b/tdutils/test/HazardPointers.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/MpmcQueue.cpp b/tdutils/test/MpmcQueue.cpp index 52de4fd5..b827ec95 100644 --- a/tdutils/test/MpmcQueue.cpp +++ b/tdutils/test/MpmcQueue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/MpmcWaiter.cpp b/tdutils/test/MpmcWaiter.cpp index 70d8ed6c..8803bafd 100644 --- a/tdutils/test/MpmcWaiter.cpp +++ b/tdutils/test/MpmcWaiter.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/MpscLinkQueue.cpp b/tdutils/test/MpscLinkQueue.cpp index 957992b4..edb40e15 100644 --- a/tdutils/test/MpscLinkQueue.cpp +++ b/tdutils/test/MpscLinkQueue.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/OrderedEventsProcessor.cpp b/tdutils/test/OrderedEventsProcessor.cpp index d627c740..26660e9c 100644 --- a/tdutils/test/OrderedEventsProcessor.cpp +++ b/tdutils/test/OrderedEventsProcessor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/SharedObjectPool.cpp b/tdutils/test/SharedObjectPool.cpp index 25f1edfd..1fd7f73b 100644 --- a/tdutils/test/SharedObjectPool.cpp +++ b/tdutils/test/SharedObjectPool.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/SharedSlice.cpp b/tdutils/test/SharedSlice.cpp index e303eda7..aa8912cc 100644 --- a/tdutils/test/SharedSlice.cpp +++ b/tdutils/test/SharedSlice.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,6 +9,9 @@ #include "td/utils/SharedSlice.h" #include "td/utils/tests.h" +char disable_linker_warning_about_empty_file_tdutils_test_shared_slice_cpp TD_UNUSED; + +#if !TD_THREAD_UNSUPPORTED TEST(SharedSlice, Hands) { { td::SharedSlice h("hello"); @@ -84,3 +87,4 @@ TEST(SharedSlice, Hands) { } } } +#endif diff --git a/tdutils/test/buffer.cpp b/tdutils/test/buffer.cpp index b4df448d..34ef876a 100644 --- a/tdutils/test/buffer.cpp +++ b/tdutils/test/buffer.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/crypto.cpp b/tdutils/test/crypto.cpp index c5afefad..d9a7676b 100644 --- a/tdutils/test/crypto.cpp +++ b/tdutils/test/crypto.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/filesystem.cpp b/tdutils/test/filesystem.cpp index a482d186..c7736c27 100644 --- a/tdutils/test/filesystem.cpp +++ b/tdutils/test/filesystem.cpp @@ -1,42 +1,47 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/utils/filesystem.h" +#include "td/utils/Slice.h" #include "td/utils/tests.h" -TEST(Misc, clean_filename) { - using td::clean_filename; - ASSERT_STREQ(clean_filename("-1234567"), "-1234567"); - ASSERT_STREQ(clean_filename(".git"), "git"); - ASSERT_STREQ(clean_filename("../../.git"), "git"); - ASSERT_STREQ(clean_filename(".././.."), ""); - ASSERT_STREQ(clean_filename("../"), ""); - ASSERT_STREQ(clean_filename(".."), ""); - ASSERT_STREQ(clean_filename("test/git/ as dsa . a"), "as dsa.a"); - ASSERT_STREQ(clean_filename(" . "), ""); - ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{;|:\"}'<>?,.`~"), "!@#$%^ ()_+-=[]{; } ,.~"); - ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{}\\|:\";'<>?,.`~"), "; ,.~"); - ASSERT_STREQ(clean_filename("عرفها بعد قد. هذا مع تاريخ اليميني واندونيسيا،, لعدم تاريخ لهيمنة الى"), - "عرفها بعد قد.هذا مع تاريخ اليميني"); - ASSERT_STREQ( - clean_filename( - "012345678901234567890123456789012345678901234567890123456789adsasdasdsaa.01234567890123456789asdasdasdasd"), - "012345678901234567890123456789012345678901234567890123456789.01234567890123456789"); - ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. " - "0123456789`<><<>><><>0123456789asdasdasdasd"), - "01234567890123456789012345678901234567890123456789.0123456789"); - ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. " - "0123456789`<><><>0123456789asdasdasdasd"), - "01234567890123456789012345678901234567890123456789.0123456789 012"); - ASSERT_STREQ(clean_filename("C:/document.tar.gz"), "document.tar.gz"); - ASSERT_STREQ(clean_filename("test...."), "test"); - ASSERT_STREQ(clean_filename("....test"), "test"); - ASSERT_STREQ(clean_filename("test.exe...."), "test.exe"); // extension has changed - ASSERT_STREQ(clean_filename("test.exe01234567890123456789...."), - "test.exe01234567890123456789"); // extension may be more than 20 characters - ASSERT_STREQ(clean_filename("....test....asdf"), "test.asdf"); - ASSERT_STREQ(clean_filename("കറുപ്പ്.txt"), "കറപപ.txt"); +static void test_clean_filename(td::CSlice name, td::Slice result) { + ASSERT_STREQ(td::clean_filename(name), result); +} + +TEST(Misc, clean_filename) { + test_clean_filename("-1234567", "-1234567"); + test_clean_filename(".git", "git"); + test_clean_filename("../../.git", "git"); + test_clean_filename(".././..", ""); + test_clean_filename("../", ""); + test_clean_filename("..", ""); + test_clean_filename("test/git/ as dsa . a", "as dsa.a"); + test_clean_filename(" . ", ""); + test_clean_filename("!@#$%^&*()_+-=[]{;|:\"}'<>?,.`~", "!@#$%^ ()_+-=[]{; } ,.~"); + test_clean_filename("!@#$%^&*()_+-=[]{}\\|:\";'<>?,.`~", "; ,.~"); + test_clean_filename("عرفها بعد قد. هذا مع تاريخ اليميني واندونيسيا،, لعدم تاريخ لهيمنة الى", + "عرفها بعد قد.هذا مع تاريخ اليميني"); + test_clean_filename( + "012345678901234567890123456789012345678901234567890123456789adsasdasdsaa.01234567890123456789asdasdasdasd", + "012345678901234567890123456789012345678901234567890123456789.01234567890123456789"); + test_clean_filename( + "01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. " + "0123456789`<><<>><><>0123456789asdasdasdasd", + "01234567890123456789012345678901234567890123456789.0123456789"); + test_clean_filename( + "01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. " + "0123456789`<><><>0123456789asdasdasdasd", + "01234567890123456789012345678901234567890123456789.0123456789 012"); + test_clean_filename("C:/document.tar.gz", "document.tar.gz"); + test_clean_filename("test....", "test"); + test_clean_filename("....test", "test"); + test_clean_filename("test.exe....", "test.exe"); // extension has changed + test_clean_filename("test.exe01234567890123456789....", + "test.exe01234567890123456789"); // extension may be more than 20 characters + test_clean_filename("....test....asdf", "test.asdf"); + test_clean_filename("കറുപ്പ്.txt", "കറപപ.txt"); } diff --git a/tdutils/test/gzip.cpp b/tdutils/test/gzip.cpp index 21262741..7edc2c49 100644 --- a/tdutils/test/gzip.cpp +++ b/tdutils/test/gzip.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/heap.cpp b/tdutils/test/heap.cpp index e78c12ca..909e6b6c 100644 --- a/tdutils/test/heap.cpp +++ b/tdutils/test/heap.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/json.cpp b/tdutils/test/json.cpp index fbb4ae06..f11962c0 100644 --- a/tdutils/test/json.cpp +++ b/tdutils/test/json.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,7 +11,6 @@ #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" -#include #include REGISTER_TESTS(json) @@ -48,8 +47,8 @@ TEST(JSON, object) { StringBuilder sb(MutableSlice{tmp, sizeof(tmp)}); JsonBuilder jb(std::move(sb)); auto c = jb.enter_object(); - c << std::tie("key", "value"); - c << std::make_pair("1", 2); + c("key", "value"); + c("1", 2); c.leave(); ASSERT_EQ(jb.string_builder().is_error(), false); auto encoded = jb.string_builder().as_cslice().str(); diff --git a/tdutils/test/log.cpp b/tdutils/test/log.cpp index 4d276221..1503aafd 100644 --- a/tdutils/test/log.cpp +++ b/tdutils/test/log.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,6 +22,9 @@ // LOG uses thread local LogInterface // void append(CSlice slice, int log_level); +char disable_linker_warning_about_empty_file_tdutils_test_log_cpp TD_UNUSED; + +#if !TD_THREAD_UNSUPPORTED template class LogBenchmark : public td::Benchmark { public: @@ -125,3 +128,4 @@ TEST(Log, TsLogger) { return td::make_unique(); }); } +#endif diff --git a/tdutils/test/misc.cpp b/tdutils/test/misc.cpp index cd10b396..acda4e38 100644 --- a/tdutils/test/misc.cpp +++ b/tdutils/test/misc.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -211,7 +211,7 @@ TEST(Misc, base64) { template static void test_remove_if(vector v, const T &func, vector expected) { - remove_if(v, func); + td::remove_if(v, func); if (expected != v) { LOG(FATAL) << "Receive " << v << ", expected " << expected << " in remove_if"; } @@ -268,6 +268,50 @@ TEST(Misc, remove_if) { test_remove_if(v, none, v); } +static void test_remove(vector v, int value, vector expected) { + bool is_found = expected != v; + ASSERT_EQ(is_found, td::remove(v, value)); + if (expected != v) { + LOG(FATAL) << "Receive " << v << ", expected " << expected << " in remove"; + } +} + +TEST(Misc, remove) { + vector v{1, 2, 3, 4, 5, 6}; + test_remove(v, 0, {1, 2, 3, 4, 5, 6}); + test_remove(v, 1, {2, 3, 4, 5, 6}); + test_remove(v, 2, {1, 3, 4, 5, 6}); + test_remove(v, 3, {1, 2, 4, 5, 6}); + test_remove(v, 4, {1, 2, 3, 5, 6}); + test_remove(v, 5, {1, 2, 3, 4, 6}); + test_remove(v, 6, {1, 2, 3, 4, 5}); + test_remove(v, 7, {1, 2, 3, 4, 5, 6}); + + v.clear(); + test_remove(v, -1, v); + test_remove(v, 0, v); + test_remove(v, 1, v); +} + +TEST(Misc, contains) { + td::vector v{1, 3, 5, 7, 4, 2}; + for (int i = -10; i < 20; i++) { + ASSERT_EQ(td::contains(v, i), (1 <= i && i <= 5) || i == 7); + } + + v.clear(); + ASSERT_TRUE(!td::contains(v, 0)); + ASSERT_TRUE(!td::contains(v, 1)); + + td::string str = "abacaba"; + ASSERT_TRUE(!td::contains(str, '0')); + ASSERT_TRUE(!td::contains(str, 0)); + ASSERT_TRUE(!td::contains(str, 'd')); + ASSERT_TRUE(td::contains(str, 'a')); + ASSERT_TRUE(td::contains(str, 'b')); + ASSERT_TRUE(td::contains(str, 'c')); +} + TEST(Misc, to_integer) { ASSERT_EQ(to_integer("-1234567"), -1234567); ASSERT_EQ(to_integer("-1234567"), -1234567); diff --git a/tdutils/test/port.cpp b/tdutils/test/port.cpp index 1055c94d..9ff5841d 100644 --- a/tdutils/test/port.cpp +++ b/tdutils/test/port.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -95,6 +95,22 @@ TEST(Port, files) { ASSERT_STREQ("Habcd world?!", buf_slice.substr(0, 13)); } +TEST(Port, SparseFiles) { + CSlice path = "sparse.txt"; + unlink(path).ignore(); + auto fd = FileFd::open(path, FileFd::Write | FileFd::CreateNew).move_as_ok(); + ASSERT_EQ(0, fd.get_size().move_as_ok()); + ASSERT_EQ(0, fd.get_real_size().move_as_ok()); + int64 offset = 100000000; + 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) { + LOG(ERROR) << "File system doesn't support sparse files, rewind during streaming can be slow"; + } + unlink(path).ensure(); +} + TEST(Port, Writev) { std::vector vec; CSlice test_file_path = "test.txt"; diff --git a/tdutils/test/pq.cpp b/tdutils/test/pq.cpp index 4b275eea..3efd4f4b 100644 --- a/tdutils/test/pq.cpp +++ b/tdutils/test/pq.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/tdutils/test/variant.cpp b/tdutils/test/variant.cpp index 442ab264..e65bc9eb 100644 --- a/tdutils/test/variant.cpp +++ b/tdutils/test/variant.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/test/data.cpp b/test/data.cpp index f6143da1..f289b534 100644 --- a/test/data.cpp +++ b/test/data.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/test/data.h b/test/data.h index acb3ebf6..4f47fc89 100644 --- a/test/data.h +++ b/test/data.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/test/db.cpp b/test/db.cpp index 69687bb0..f9f33726 100644 --- a/test/db.cpp +++ b/test/db.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,6 +7,7 @@ #include "td/db/binlog/BinlogHelper.h" #include "td/db/binlog/ConcurrentBinlog.h" #include "td/db/BinlogKeyValue.h" +#include "td/db/DbKey.h" #include "td/db/SeqKeyValue.h" #include "td/db/SqliteConnectionSafe.h" #include "td/db/SqliteDb.h" diff --git a/test/fuzz_url.cpp b/test/fuzz_url.cpp index 0a3113a7..81439bcd 100644 --- a/test/fuzz_url.cpp +++ b/test/fuzz_url.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ static td::string get_utf_string(td::Slice from) { td::string res; td::string alph = " ab@./01#"; for (auto c : from) { - res += alph[td::uint8(c) % alph.size()]; + res += alph[static_cast(c) % alph.size()]; } LOG(ERROR) << res; return res; diff --git a/test/http.cpp b/test/http.cpp index 8268ee64..f107828a 100644 --- a/test/http.cpp +++ b/test/http.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -47,7 +47,7 @@ static string make_chunked(string str) { auto v = rand_split(str); string res; for (auto &s : v) { - res += PSTRING() << format::as_hex_dump(int(s.size())); + res += PSTRING() << format::as_hex_dump(static_cast(s.size())); res += "\r\n"; res += s; res += "\r\n"; diff --git a/test/main.cpp b/test/main.cpp index 20647048..21a953ee 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -31,7 +31,8 @@ int main(int argc, char **argv) { #if TD_EMSCRIPTEN emscripten_set_main_loop( [] { - if (!runner.run_all_step()) { + td::TestsRunner &default_runner = td::TestsRunner::get_default(); + if (!default_runner.run_all_step()) { emscripten_cancel_main_loop(); } }, diff --git a/test/message_entities.cpp b/test/message_entities.cpp index 09b09deb..b43e4b5f 100644 --- a/test/message_entities.cpp +++ b/test/message_entities.cpp @@ -1,30 +1,32 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/MessageEntity.h" +#include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" +#include "td/utils/misc.h" +#include "td/utils/Slice.h" #include "td/utils/tests.h" +#include "td/utils/utf8.h" #include REGISTER_TESTS(message_entities); -using namespace td; - -static void check_mention(string str, std::vector expected) { - auto result_slice = find_mentions(str); - std::vector result; +static void check_mention(const td::string &str, const td::vector &expected) { + auto result_slice = td::find_mentions(str); + td::vector result; for (auto &it : result_slice) { result.push_back(it.str()); } if (result != expected) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result)) - << tag("expected", format::as_array(expected)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result)) + << td::tag("expected", td::format::as_array(expected)); } } @@ -45,15 +47,15 @@ TEST(MessageEntities, mention) { {"@gif", "@wiki", "@vid", "@bing", "@pic", "@bold", "@imdb", "@coub", "@like", "@vote", "@bingg"}); }; -static void check_bot_command(string str, std::vector expected) { - auto result_slice = find_bot_commands(str); - std::vector result; +static void check_bot_command(const td::string &str, const td::vector &expected) { + auto result_slice = td::find_bot_commands(str); + td::vector result; for (auto &it : result_slice) { result.push_back(it.str()); } if (result != expected) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result)) - << tag("expected", format::as_array(expected)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result)) + << td::tag("expected", td::format::as_array(expected)); } } @@ -69,15 +71,15 @@ TEST(MessageEntities, bot_command) { check_bot_command("/test/", {}); } -static void check_hashtag(string str, std::vector expected) { - auto result_slice = find_hashtags(str); - std::vector result; +static void check_hashtag(const td::string &str, const td::vector &expected) { + auto result_slice = td::find_hashtags(str); + td::vector result; for (auto &it : result_slice) { result.push_back(it.str()); } if (result != expected) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result)) - << tag("expected", format::as_array(expected)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result)) + << td::tag("expected", td::format::as_array(expected)); } } @@ -96,29 +98,29 @@ TEST(MessageEntities, hashtag) { check_hashtag(" #123a ", {"#123a"}); check_hashtag(" #a123 ", {"#a123"}); check_hashtag(" #123a# ", {}); - check_hashtag(" #" + string(300, '1'), {}); - check_hashtag(" #" + string(256, '1'), {}); - check_hashtag(" #" + string(256, '1') + "a ", {}); - check_hashtag(" #" + string(255, '1') + "a", {"#" + string(255, '1') + "a"}); - check_hashtag(" #" + string(255, '1') + "Я", {"#" + string(255, '1') + "Я"}); - check_hashtag(" #" + string(255, '1') + "a" + string(255, 'b') + "# ", {}); + check_hashtag(" #" + td::string(300, '1'), {}); + check_hashtag(" #" + td::string(256, '1'), {}); + check_hashtag(" #" + td::string(256, '1') + "a ", {}); + check_hashtag(" #" + td::string(255, '1') + "a", {"#" + td::string(255, '1') + "a"}); + check_hashtag(" #" + td::string(255, '1') + "Я", {"#" + td::string(255, '1') + "Я"}); + check_hashtag(" #" + td::string(255, '1') + "a" + td::string(255, 'b') + "# ", {}); check_hashtag("#a#b #c #d", {"#c", "#d"}); check_hashtag("#test", {"#test"}); - check_hashtag(u8"\U0001F604\U0001F604\U0001F604\U0001F604 \U0001F604\U0001F604\U0001F604#" + string(200, '1') + - "ООО" + string(200, '2'), - {"#" + string(200, '1') + "ООО" + string(53, '2')}); + check_hashtag(u8"\U0001F604\U0001F604\U0001F604\U0001F604 \U0001F604\U0001F604\U0001F604#" + td::string(200, '1') + + "ООО" + td::string(200, '2'), + {"#" + td::string(200, '1') + "ООО" + td::string(53, '2')}); check_hashtag(u8"#a\u2122", {"#a"}); } -static void check_cashtag(string str, std::vector expected) { - auto result_slice = find_cashtags(str); - std::vector result; +static void check_cashtag(const td::string &str, const td::vector &expected) { + auto result_slice = td::find_cashtags(str); + td::vector result; for (auto &it : result_slice) { result.push_back(it.str()); } if (result != expected) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result)) - << tag("expected", format::as_array(expected)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result)) + << td::tag("expected", td::format::as_array(expected)); } } @@ -162,8 +164,8 @@ TEST(MessageEntities, cashtag) { check_cashtag(u8"\u2122$ABC\u2122", {"$ABC"}); } -static void check_is_email_address(string str, bool expected) { - bool result = is_email_address(str); +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 << ")"; } @@ -184,79 +186,79 @@ TEST(MessageEntities, is_email_address) { check_is_email_address("a.ab", false); check_is_email_address("a.bc@d.ef", true); - vector bad_userdatas = {"", - "a.a.a.a.a.a.a.a.a.a.a.a", - "+.+.+.+.+.+", - "*.a.a", - "a.*.a", - "a.a.*", - "a.a.", - "a.a.abcdefghijklmnopqrstuvwxyz0123456789", - "a.abcdefghijklmnopqrstuvwxyz0.a", - "abcdefghijklmnopqrstuvwxyz0.a.a"}; - vector good_userdatas = {"a.a.a.a.a.a.a.a.a.a.a", - "a+a+a+a+a+a+a+a+a+a+a", - "+.+.+.+.+._", - "aozAQZ0-5-9_+-aozAQZ0-5-9_.aozAQZ0-5-9_.-._.+-", - "a.a.a", - "a.a.abcdefghijklmnopqrstuvwxyz012345678", - "a.abcdefghijklmnopqrstuvwxyz.a", - "a..a", - "abcdefghijklmnopqrstuvwxyz.a.a", - ".a.a"}; + td::vector bad_userdatas = {"", + "a.a.a.a.a.a.a.a.a.a.a.a", + "+.+.+.+.+.+", + "*.a.a", + "a.*.a", + "a.a.*", + "a.a.", + "a.a.abcdefghijklmnopqrstuvwxyz0123456789", + "a.abcdefghijklmnopqrstuvwxyz0.a", + "abcdefghijklmnopqrstuvwxyz0.a.a"}; + td::vector good_userdatas = {"a.a.a.a.a.a.a.a.a.a.a", + "a+a+a+a+a+a+a+a+a+a+a", + "+.+.+.+.+._", + "aozAQZ0-5-9_+-aozAQZ0-5-9_.aozAQZ0-5-9_.-._.+-", + "a.a.a", + "a.a.abcdefghijklmnopqrstuvwxyz012345678", + "a.abcdefghijklmnopqrstuvwxyz.a", + "a..a", + "abcdefghijklmnopqrstuvwxyz.a.a", + ".a.a"}; - vector bad_domains = {"", - ".", - "abc", - "localhost", - "a.a.a.a.a.a.a.ab", - ".......", - "a.a.a.a.a.a+ab", - "a+a.a.a.a.a.ab", - "a.a.a.a.a.a.a", - "a.a.a.a.a.a.abcdefg", - "a.a.a.a.a.a.ab0yz", - "a.a.a.a.a.a.ab9yz", - "a.a.a.a.a.a.ab-yz", - "a.a.a.a.a.a.ab_yz", - "a.a.a.a.a.a.ab*yz", - ".ab", - ".a.ab", - "a..ab", - "a.a.a..a.ab", - ".a.a.a.a.ab", - "abcdefghijklmnopqrstuvwxyz01234.ab", - "ab0cd.abd.aA*sd.0.9.0-9.ABOYZ", - "ab*cd.abd.aAasd.0.9.0-9.ABOYZ", - "ab0cd.abd.aAasd.0.9.0*9.ABOYZ", - "*b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0c*.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.0-*.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.*-9.ABOYZ", - "-b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0c-.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.-.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.--9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.0--.ABOYZ", - "_b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0c_.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd._.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9._-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.0-_.ABOYZ", - "-.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0cd.ab_d.-.0.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9.-.ABOYZ", - "_.ab_d.aA-sd.0.9.0-9.ABOYZ", - "ab0cd.ab_d._.0.9.0-9.ABOYZ", - "ab0cd.ab_d.aA-sd.0.9._.ABOYZ"}; - vector good_domains = {"a.a.a.a.a.a.ab", - "a.a.a.a.a.a.abcdef", - "a.a.a.a.a.a.aboyz", - "a.a.a.a.a.a.ABOYZ", - "a.a.a.a.a.a.AbOyZ", - "abcdefghijklmnopqrstuvwxyz0123.ab", - "ab0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", - "A.Z.aA-sd.a.z.0-9.ABOYZ"}; + td::vector bad_domains = {"", + ".", + "abc", + "localhost", + "a.a.a.a.a.a.a.ab", + ".......", + "a.a.a.a.a.a+ab", + "a+a.a.a.a.a.ab", + "a.a.a.a.a.a.a", + "a.a.a.a.a.a.abcdefg", + "a.a.a.a.a.a.ab0yz", + "a.a.a.a.a.a.ab9yz", + "a.a.a.a.a.a.ab-yz", + "a.a.a.a.a.a.ab_yz", + "a.a.a.a.a.a.ab*yz", + ".ab", + ".a.ab", + "a..ab", + "a.a.a..a.ab", + ".a.a.a.a.ab", + "abcdefghijklmnopqrstuvwxyz01234.ab", + "ab0cd.abd.aA*sd.0.9.0-9.ABOYZ", + "ab*cd.abd.aAasd.0.9.0-9.ABOYZ", + "ab0cd.abd.aAasd.0.9.0*9.ABOYZ", + "*b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0c*.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.0-*.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.*-9.ABOYZ", + "-b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0c-.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.-.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.--9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.0--.ABOYZ", + "_b0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0c_.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd._.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9._-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.0-_.ABOYZ", + "-.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0cd.ab_d.-.0.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9.-.ABOYZ", + "_.ab_d.aA-sd.0.9.0-9.ABOYZ", + "ab0cd.ab_d._.0.9.0-9.ABOYZ", + "ab0cd.ab_d.aA-sd.0.9._.ABOYZ"}; + td::vector good_domains = {"a.a.a.a.a.a.ab", + "a.a.a.a.a.a.abcdef", + "a.a.a.a.a.a.aboyz", + "a.a.a.a.a.a.ABOYZ", + "a.a.a.a.a.a.AbOyZ", + "abcdefghijklmnopqrstuvwxyz0123.ab", + "ab0cd.ab_d.aA-sd.0.9.0-9.ABOYZ", + "A.Z.aA-sd.a.z.0-9.ABOYZ"}; for (auto &userdata : bad_userdatas) { for (auto &domain : bad_domains) { @@ -280,11 +282,11 @@ TEST(MessageEntities, is_email_address) { } } -static void check_url(string str, std::vector expected_urls, - std::vector expected_email_addresses = {}) { - auto result_slice = find_urls(str); - std::vector result_urls; - std::vector result_email_addresses; +static void check_url(const td::string &str, const td::vector &expected_urls, + td::vector expected_email_addresses = {}) { + auto result_slice = td::find_urls(str); + td::vector result_urls; + td::vector result_email_addresses; for (auto &it : result_slice) { if (!it.second) { result_urls.push_back(it.first.str()); @@ -293,12 +295,12 @@ static void check_url(string str, std::vector expected_urls, } } if (result_urls != expected_urls) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result_urls)) - << tag("expected", format::as_array(expected_urls)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result_urls)) + << td::tag("expected", td::format::as_array(expected_urls)); } if (result_email_addresses != expected_email_addresses) { - LOG(FATAL) << tag("text", str) << tag("got", format::as_array(result_email_addresses)) - << tag("expected", format::as_array(expected_email_addresses)); + LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result_email_addresses)) + << td::tag("expected", td::format::as_array(expected_email_addresses)); } } @@ -531,23 +533,25 @@ TEST(MessageEntities, url) { check_url("...👉http://ab.com/cdefgh-1IJ", {}); // TODO } -static void check_fix_formatted_text(string str, vector entities, string expected_str, - vector expected_entities, bool allow_empty, bool skip_new_entities, - bool skip_bot_commands, bool for_draft) { - ASSERT_TRUE(fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_ok()); +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) { + 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); ASSERT_EQ(expected_entities, entities); } -static void check_fix_formatted_text(string str, vector entities, bool allow_empty, +static void check_fix_formatted_text(td::string str, td::vector entities, bool allow_empty, bool skip_new_entities, bool skip_bot_commands, bool for_draft) { ASSERT_TRUE( fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_error()); } TEST(MessageEntities, fix_formatted_text) { - string str; - string fixed_str; + td::string str; + td::string fixed_str; for (auto i = 0; i <= 32; i++) { str += static_cast(i); if (i != 13) { @@ -570,18 +574,18 @@ TEST(MessageEntities, fix_formatted_text) { str += "a \r\n "; fixed_str += "a \n "; - for (int32 i = 33; i <= 35; i++) { - vector entities; - entities.emplace_back(MessageEntity::Type::Bold, 0, i); + for (td::int32 i = 33; i <= 35; i++) { + td::vector entities; + entities.emplace_back(td::MessageEntity::Type::Bold, 0, i); - vector fixed_entities; + td::vector fixed_entities; if (i != 33) { fixed_entities = entities; fixed_entities.back().length = i - 1; } check_fix_formatted_text(str, entities, fixed_str, fixed_entities, true, false, false, true); - string expected_str; + td::string expected_str; if (i != 33) { fixed_entities = entities; fixed_entities.back().length = 33; @@ -593,35 +597,35 @@ TEST(MessageEntities, fix_formatted_text) { check_fix_formatted_text(str, entities, expected_str, fixed_entities, false, false, false, false); } - str = "👉 👉"; + str = "👉 👉 "; for (int i = 0; i < 10; i++) { - vector entities; - entities.emplace_back(MessageEntity::Type::Bold, i, 1); - if (i == 0 || i == 1 || i == 3 || i == 4) { + td::vector entities; + entities.emplace_back(td::MessageEntity::Type::Bold, i, 1); + if (i != 2 && i != 5 && i != 6) { check_fix_formatted_text(str, entities, true, true, true, true); check_fix_formatted_text(str, entities, false, false, false, false); } else { check_fix_formatted_text(str, entities, str, {}, true, true, true, true); - check_fix_formatted_text(str, entities, str, {}, false, false, false, false); + check_fix_formatted_text(str, entities, str.substr(0, str.size() - 2), {}, false, false, false, false); } } str = " /test @abaca #ORD $ABC telegram.org "; for (auto for_draft : {false, true}) { - int32 shift = for_draft ? 2 : 0; - string expected_str = for_draft ? str : str.substr(2, str.size() - 3); + td::int32 shift = for_draft ? 2 : 0; + td::string expected_str = for_draft ? str : str.substr(2, str.size() - 3); for (auto skip_new_entities : {false, true}) { for (auto skip_bot_commands : {false, true}) { - vector entities; + td::vector entities; if (!skip_new_entities) { if (!skip_bot_commands) { - entities.emplace_back(MessageEntity::Type::BotCommand, shift, 5); + entities.emplace_back(td::MessageEntity::Type::BotCommand, shift, 5); } - entities.emplace_back(MessageEntity::Type::Mention, shift + 6, 6); - entities.emplace_back(MessageEntity::Type::Hashtag, shift + 13, 4); - entities.emplace_back(MessageEntity::Type::Cashtag, shift + 18, 4); - entities.emplace_back(MessageEntity::Type::Url, shift + 24, 12); + entities.emplace_back(td::MessageEntity::Type::Mention, shift + 6, 6); + entities.emplace_back(td::MessageEntity::Type::Hashtag, shift + 13, 4); + entities.emplace_back(td::MessageEntity::Type::Cashtag, shift + 18, 4); + entities.emplace_back(td::MessageEntity::Type::Url, shift + 24, 12); } check_fix_formatted_text(str, {}, expected_str, entities, true, skip_new_entities, skip_bot_commands, @@ -633,10 +637,10 @@ TEST(MessageEntities, fix_formatted_text) { } str = "aba \r\n caba "; - for (int32 length = 1; length <= 3; length++) { - for (int32 offset = 0; static_cast(offset + length) <= str.size(); offset++) { - for (auto type : {MessageEntity::Type::Bold, MessageEntity::Type::Url, MessageEntity::Type::TextUrl, - MessageEntity::Type::MentionName}) { + for (td::int32 length = 1; length <= 3; length++) { + for (td::int32 offset = 0; static_cast(offset + length) <= str.size(); offset++) { + for (auto type : {td::MessageEntity::Type::Bold, td::MessageEntity::Type::Url, td::MessageEntity::Type::TextUrl, + td::MessageEntity::Type::MentionName}) { for (auto for_draft : {false, true}) { fixed_str = for_draft ? "aba \n caba " : "aba \n caba"; auto fixed_length = offset <= 4 && offset + length >= 5 ? length - 1 : length; @@ -648,14 +652,14 @@ TEST(MessageEntities, fix_formatted_text) { fixed_length--; } - vector entities; + td::vector entities; entities.emplace_back(type, offset, length); - vector fixed_entities; + td::vector fixed_entities; if (fixed_length > 0) { for (auto i = 0; i < length; i++) { if (str[offset + i] != '\r' && str[offset + i] != '\n' && - (str[offset + i] != ' ' || type == MessageEntity::Type::TextUrl || - type == MessageEntity::Type::MentionName)) { + (str[offset + i] != ' ' || type == td::MessageEntity::Type::TextUrl || + type == td::MessageEntity::Type::MentionName)) { fixed_entities.emplace_back(type, fixed_offset, fixed_length); break; } @@ -668,14 +672,19 @@ TEST(MessageEntities, fix_formatted_text) { } str = "aba caba"; - for (int32 length = -10; length <= 10; length++) { - for (int32 offset = -10; offset <= 10; offset++) { - vector entities; - entities.emplace_back(MessageEntity::Type::Bold, offset, length); - vector fixed_entities; - if (length > 0 && offset >= 0 && static_cast(length + offset) <= str.size() && - (length >= 2 || offset != 3)) { - fixed_entities.emplace_back(MessageEntity::Type::Bold, offset, length); + for (td::int32 length = -10; length <= 10; length++) { + for (td::int32 offset = -10; offset <= 10; offset++) { + 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()) { + 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)) { + fixed_entities.emplace_back(td::MessageEntity::Type::Bold, offset, length); } check_fix_formatted_text(str, entities, str, fixed_entities, true, false, false, false); check_fix_formatted_text(str, entities, str, fixed_entities, false, false, false, true); @@ -683,15 +692,15 @@ TEST(MessageEntities, fix_formatted_text) { } str = "aba caba"; - for (int32 length = 1; length <= 7; length++) { - for (int32 offset = 0; offset <= 8 - length; offset++) { - for (int32 length2 = 1; length2 <= 7; length2++) { - for (int32 offset2 = 0; offset2 <= 8 - length2; offset2++) { + for (td::int32 length = 1; length <= 7; length++) { + for (td::int32 offset = 0; offset <= 8 - length; offset++) { + for (td::int32 length2 = 1; length2 <= 7; length2++) { + for (td::int32 offset2 = 0; offset2 <= 8 - length2; offset2++) { if (offset != offset2) { - vector entities; - entities.emplace_back(MessageEntity::Type::TextUrl, offset, length); - entities.emplace_back(MessageEntity::Type::TextUrl, offset2, length2); - vector fixed_entities = entities; + td::vector entities; + entities.emplace_back(td::MessageEntity::Type::TextUrl, offset, length); + entities.emplace_back(td::MessageEntity::Type::TextUrl, offset2, length2); + td::vector fixed_entities = entities; std::sort(fixed_entities.begin(), fixed_entities.end()); if (fixed_entities[0].offset + fixed_entities[0].length > fixed_entities[1].offset) { fixed_entities.pop_back(); @@ -702,4 +711,285 @@ TEST(MessageEntities, fix_formatted_text) { } } } + + for (auto text : {" \n ➡️ ➡️ ➡️ ➡️ \n ", "\n\n\nab cd ef gh "}) { + str = text; + td::vector entities; + td::vector fixed_entities; + + auto length = td::narrow_cast(td::utf8_utf16_length(str)); + for (int i = 0; i < 10; i++) { + if ((i + 1) * 3 + 2 <= length) { + entities.emplace_back(td::MessageEntity::Type::Bold, (i + 1) * 3, 2); + } + if ((i + 2) * 3 <= length) { + entities.emplace_back(td::MessageEntity::Type::Italic, (i + 1) * 3 + 2, 1); + } + + if (i < 4) { + fixed_entities.emplace_back(td::MessageEntity::Type::Bold, i * 3, 2); + } + } + + check_fix_formatted_text(str, entities, td::utf8_utf16_substr(str, 3, 11), fixed_entities, false, false, false, + false); + } + + for (td::string text : {"\t", "\r", "\n", "\t ", "\r ", "\n "}) { + for (auto type : {td::MessageEntity::Type::Bold, td::MessageEntity::Type::TextUrl}) { + check_fix_formatted_text(text, {{type, 0, 1, "http://telegram.org/"}}, "", {}, true, false, false, true); + } + } +} + +static void check_parse_html(td::string text, const td::string &result, const td::vector &entities) { + auto r_entities = td::parse_html(text); + ASSERT_TRUE(r_entities.is_ok()); + ASSERT_EQ(entities, r_entities.ok()); + ASSERT_STREQ(result, text); +} + +static void check_parse_html(td::string text, td::Slice error_message) { + auto r_entities = td::parse_html(text); + ASSERT_TRUE(r_entities.is_error()); + ASSERT_EQ(400, r_entities.error().code()); + ASSERT_STREQ(error_message, r_entities.error().message()); +} + +TEST(MessageEntities, parse_html) { + td::string invalid_surrogate_pair_error_message = + "Text contains invalid Unicode characters after decoding HTML entities, check for unmatched surrogate code units"; + check_parse_html("�", invalid_surrogate_pair_error_message); + check_parse_html("�", invalid_surrogate_pair_error_message); + check_parse_html("�", invalid_surrogate_pair_error_message); + check_parse_html("🏟 🏟<", "Unsupported start tag \"abac\" at byte offset 13"); + check_parse_html("🏟 🏟<", "Unsupported start tag \"abac\" at byte offset 13"); + check_parse_html("🏟 🏟<", "Empty attribute name in the tag \"i\" at byte offset 13"); + check_parse_html("🏟 🏟<", + "Expected equal sign in declaration of an attribute of the tag \"i\" at byte offset 13"); + check_parse_html("🏟 🏟<", "Unclosed start tag at byte offset 13"); + check_parse_html("🏟 🏟<", "Unclosed start tag at byte offset 13"); + check_parse_html("🏟 🏟<aa", + "Unmatched end tag at byte offset 17, expected \"\", found \"\""); + + check_parse_html("", "", {}); + check_parse_html("➡️ ➡️", "➡️ ➡️", {}); + check_parse_html("<>&"«»�", "<>&\"«»�", {}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Italic, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Italic, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Bold, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Bold, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Underline, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Underline, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Strikethrough, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Strikethrough, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Strikethrough, 5, 5}}); + check_parse_html("➡️ ➡️➡️ ➡️➡️ ➡️", "➡️ ➡️➡️ ➡️➡️ ➡️", + {{td::MessageEntity::Type::Italic, 5, 5}, {td::MessageEntity::Type::Bold, 10, 5}}); + check_parse_html("🏟 🏟🏟 <🏟", "🏟 🏟🏟 <🏟", {{td::MessageEntity::Type::Italic, 5, 6}}); + check_parse_html("🏟 🏟🏟 ><🏟", "🏟 🏟🏟 ><🏟", + {{td::MessageEntity::Type::Italic, 5, 7}, {td::MessageEntity::Type::Bold, 9, 3}}); + check_parse_html("🏟 🏟<a", "🏟 🏟a", "🏟 🏟a", "🏟 🏟a", "🏟 🏟a", "🏟 🏟🏟 🏟<", "🏟 🏟<🏟 🏟<", + {{td::MessageEntity::Type::Italic, 6, 6}}); + check_parse_html("🏟 🏟<a", "🏟 🏟a", "🏟 🏟", "🏟 🏟<", {}); + check_parse_html("\t", "\t", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_parse_html("\r", "\r", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_parse_html("\n", "\n", {{td::MessageEntity::Type::Italic, 0, 1}}); + check_parse_html("\t", "\t", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html("\r", "\r", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html("\n", "\n", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::Code, 0, 1}, + {td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Italic, 0, 1}, + {td::MessageEntity::Type::Code, 1, 1}, + {td::MessageEntity::Type::Bold, 1, 1}, + {td::MessageEntity::Type::Italic, 1, 1}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::Italic, 0, 3}, + {td::MessageEntity::Type::Bold, 0, 1}, + {td::MessageEntity::Type::Code, 2, 1}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}}); + check_parse_html(" ", " ", + {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/?<"}}); + check_parse_html(" ", " ", {}); + check_parse_html("telegram.org ", "telegram.org ", {}); + check_parse_html("telegram.org", "telegram.org", + {{td::MessageEntity::Type::TextUrl, 0, 12, "http://telegram.org/"}}); + check_parse_html("https://telegram.org/asdsa?asdasdwe#12e3we", "https://telegram.org/asdsa?asdasdwe#12e3we", + {{td::MessageEntity::Type::TextUrl, 0, 42, "https://telegram.org/asdsa?asdasdwe#12e3we"}}); + check_parse_html("🏟 🏟<
🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::Pre, 6, 6}});
+  check_parse_html("🏟 🏟<🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::Code, 6, 6}});
+  check_parse_html("🏟 🏟<
🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::Pre, 6, 6}, {td::MessageEntity::Type::Code, 6, 6}});
+  check_parse_html("🏟 🏟<
🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::Pre, 6, 6}, {td::MessageEntity::Type::Code, 6, 6}});
+  check_parse_html("🏟 🏟<
🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::PreCode, 6, 6, "fift"}});
+  check_parse_html("🏟 🏟<
🏟 🏟<", "🏟 🏟<🏟 🏟<",
+                   {{td::MessageEntity::Type::PreCode, 6, 6, "fift"}});
+  check_parse_html("🏟 🏟<
🏟 🏟< ", "🏟 🏟<🏟 🏟< ",
+                   {{td::MessageEntity::Type::Pre, 6, 7}, {td::MessageEntity::Type::Code, 6, 6}});
+  check_parse_html("🏟 🏟<
 🏟 🏟<", "🏟 🏟< 🏟 🏟<",
+                   {{td::MessageEntity::Type::Pre, 6, 7}, {td::MessageEntity::Type::Code, 7, 6}});
+}
+
+static void check_parse_markdown(td::string text, const td::string &result,
+                                 const td::vector &entities) {
+  auto r_entities = td::parse_markdown_v2(text);
+  ASSERT_TRUE(r_entities.is_ok());
+  ASSERT_EQ(entities, r_entities.ok());
+  ASSERT_STREQ(result, text);
+}
+
+static void check_parse_markdown(td::string text, td::Slice error_message) {
+  auto r_entities = td::parse_markdown_v2(text);
+  ASSERT_TRUE(r_entities.is_error());
+  ASSERT_EQ(400, r_entities.error().code());
+  ASSERT_STREQ(error_message, r_entities.error().message());
+}
+
+TEST(MessageEntities, parse_markdown) {
+  td::Slice reserved_characters("]()>#+-=|{}.!");
+  td::Slice begin_characters("_*[~`");
+  for (char c = 1; c < 126; c++) {
+    if (begin_characters.find(c) != td::Slice::npos) {
+      continue;
+    }
+
+    td::string text(1, c);
+    if (reserved_characters.find(c) == td::Slice::npos) {
+      check_parse_markdown(text, text, {});
+    } else {
+      check_parse_markdown(
+          text, PSLICE() << "Character '" << c << "' is reserved and must be escaped with the preceding '\\'");
+
+      td::string escaped_text = "\\" + text;
+      check_parse_markdown(escaped_text, text, {});
+    }
+  }
+
+  check_parse_markdown("🏟 🏟_abacaba", "Can't find end of Italic entity at byte offset 9");
+  check_parse_markdown("🏟 🏟_abac * asd ", "Can't find end of Bold entity at byte offset 15");
+  check_parse_markdown("🏟 🏟_abac * asd _", "Can't find end of Italic entity at byte offset 21");
+  check_parse_markdown("🏟 🏟`", "Can't find end of Code entity at byte offset 9");
+  check_parse_markdown("🏟 🏟```", "Can't find end of Pre entity at byte offset 9");
+  check_parse_markdown("🏟 🏟```a", "Can't find end of Pre entity at byte offset 9");
+  check_parse_markdown("🏟 🏟```a ", "Can't find end of PreCode entity at byte offset 9");
+  check_parse_markdown("🏟 🏟__🏟 🏟_", "Can't find end of Italic entity at byte offset 20");
+  check_parse_markdown("🏟 🏟_🏟 🏟__", "Can't find end of Underline entity at byte offset 19");
+  check_parse_markdown("🏟 🏟```🏟 🏟`", "Can't find end of Code entity at byte offset 21");
+  check_parse_markdown("🏟 🏟```🏟 🏟_", "Can't find end of PreCode entity at byte offset 9");
+  check_parse_markdown("🏟 🏟```🏟 🏟\\`", "Can't find end of PreCode entity at byte offset 9");
+  check_parse_markdown("[telegram\\.org](asd\\)", "Can't find end of a URL at byte offset 16");
+  check_parse_markdown("[telegram\\.org](", "Can't find end of a URL at byte offset 16");
+  check_parse_markdown("[telegram\\.org](asd", "Can't find end of a URL at byte offset 16");
+  check_parse_markdown("🏟 🏟__🏟 _🏟___", "Can't find end of Italic entity at byte offset 23");
+  check_parse_markdown("🏟 🏟__", "Can't find end of Underline entity at byte offset 9");
+
+  check_parse_markdown("", "", {});
+  check_parse_markdown("\\\\", "\\", {});
+  check_parse_markdown("\\\\\\", "\\\\", {});
+  check_parse_markdown("\\\\\\\\\\_\\*\\`", "\\\\_*`", {});
+  check_parse_markdown("➡️ ➡️", "➡️ ➡️", {});
+  check_parse_markdown("🏟 🏟``", "🏟 🏟", {});
+  check_parse_markdown("🏟 🏟_abac \\* asd _", "🏟 🏟abac * asd ", {{td::MessageEntity::Type::Italic, 5, 11}});
+  check_parse_markdown("🏟 \\.🏟_🏟\\. 🏟_", "🏟 .🏟🏟. 🏟", {{td::MessageEntity::Type::Italic, 6, 6}});
+  check_parse_markdown("\\\\\\a\\b\\c\\d\\e\\f\\1\\2\\3\\4\\➡️\\", "\\abcdef1234\\➡️\\", {});
+  check_parse_markdown("➡️ ➡️_➡️ ➡️_", "➡️ ➡️➡️ ➡️",
+                       {{td::MessageEntity::Type::Italic, 5, 5}});
+  check_parse_markdown("➡️ ➡️_➡️ ➡️_*➡️ ➡️*", "➡️ ➡️➡️ ➡️➡️ ➡️",
+                       {{td::MessageEntity::Type::Italic, 5, 5}, {td::MessageEntity::Type::Bold, 10, 5}});
+  check_parse_markdown("🏟 🏟_🏟 \\.🏟_", "🏟 🏟🏟 .🏟", {{td::MessageEntity::Type::Italic, 5, 6}});
+  check_parse_markdown("🏟 🏟_🏟 *🏟*_", "🏟 🏟🏟 🏟",
+                       {{td::MessageEntity::Type::Italic, 5, 5}, {td::MessageEntity::Type::Bold, 8, 2}});
+  check_parse_markdown("🏟 🏟_🏟 __🏟___", "🏟 🏟🏟 🏟",
+                       {{td::MessageEntity::Type::Italic, 5, 5}, {td::MessageEntity::Type::Underline, 8, 2}});
+  check_parse_markdown("🏟 🏟__🏟 _🏟_ __", "🏟 🏟🏟 🏟 ",
+                       {{td::MessageEntity::Type::Underline, 5, 6}, {td::MessageEntity::Type::Italic, 8, 2}});
+  check_parse_markdown("🏟 🏟__🏟 _🏟_\\___", "🏟 🏟🏟 🏟_",
+                       {{td::MessageEntity::Type::Underline, 5, 6}, {td::MessageEntity::Type::Italic, 8, 2}});
+  check_parse_markdown("🏟 🏟`🏟 🏟```", "🏟 🏟🏟 🏟", {{td::MessageEntity::Type::Code, 5, 5}});
+  check_parse_markdown("🏟 🏟```🏟 🏟```", "🏟 🏟 🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 3, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\n🏟```", "🏟 🏟🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 2, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\r🏟```", "🏟 🏟🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 2, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\n\r🏟```", "🏟 🏟🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 2, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\r\n🏟```", "🏟 🏟🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 2, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\n\n🏟```", "🏟 🏟\n🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 3, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟\r\r🏟```", "🏟 🏟\r🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 3, "🏟"}});
+  check_parse_markdown("🏟 🏟```🏟 \\\\\\`🏟```", "🏟 🏟 \\`🏟",
+                       {{td::MessageEntity::Type::PreCode, 5, 5, "🏟"}});
+  check_parse_markdown("🏟 🏟**", "🏟 🏟", {});
+  check_parse_markdown("🏟 🏟``", "🏟 🏟", {});
+  check_parse_markdown("🏟 🏟``````", "🏟 🏟", {});
+  check_parse_markdown("🏟 🏟____", "🏟 🏟", {});
+  check_parse_markdown("`_* *_`__*` `*__", "_* *_ ",
+                       {{td::MessageEntity::Type::Code, 0, 5},
+                        {td::MessageEntity::Type::Code, 5, 1},
+                        {td::MessageEntity::Type::Bold, 5, 1},
+                        {td::MessageEntity::Type::Underline, 5, 1}});
+  check_parse_markdown("_* * ` `_", "   ",
+                       {{td::MessageEntity::Type::Italic, 0, 3},
+                        {td::MessageEntity::Type::Bold, 0, 1},
+                        {td::MessageEntity::Type::Code, 2, 1}});
+  check_parse_markdown("[](telegram.org)", "", {});
+  check_parse_markdown("[ ](telegram.org)", " ", {{td::MessageEntity::Type::TextUrl, 0, 1, "http://telegram.org/"}});
+  check_parse_markdown("[ ](as)", " ", {});
+  check_parse_markdown("[telegram\\.org]", "telegram.org",
+                       {{td::MessageEntity::Type::TextUrl, 0, 12, "http://telegram.org/"}});
+  check_parse_markdown("[telegram\\.org]a", "telegram.orga",
+                       {{td::MessageEntity::Type::TextUrl, 0, 12, "http://telegram.org/"}});
+  check_parse_markdown("[telegram\\.org](telegram.dog)", "telegram.org",
+                       {{td::MessageEntity::Type::TextUrl, 0, 12, "http://telegram.dog/"}});
+  check_parse_markdown("[telegram\\.org](https://telegram.dog?)", "telegram.org",
+                       {{td::MessageEntity::Type::TextUrl, 0, 12, "https://telegram.dog/?"}});
+  check_parse_markdown("[telegram\\.org](https://telegram.dog?\\\\\\()", "telegram.org",
+                       {{td::MessageEntity::Type::TextUrl, 0, 12, "https://telegram.dog/?\\("}});
+  check_parse_markdown("[telegram\\.org]()", "telegram.org", {});
+  check_parse_markdown("[telegram\\.org](asdasd)", "telegram.org", {});
+  check_parse_markdown("[telegram\\.org](tg:user?id=123456)", "telegram.org", {{0, 12, td::UserId(123456)}});
 }
diff --git a/test/mtproto.cpp b/test/mtproto.cpp
index d11f6982..8645684e 100644
--- a/test/mtproto.cpp
+++ b/test/mtproto.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -170,6 +170,9 @@ TEST(Mtproto, config) {
     run(get_simple_config_azure, true);
     run(get_simple_config_google_dns, true);
     run(get_simple_config_mozilla_dns, true);
+    run(get_simple_config_firebase_remote_config, false);
+    run(get_simple_config_firebase_realtime, false);
+    run(get_simple_config_firebase_firestore, false);
   }
   cnt--;
   sched.start();
diff --git a/test/poll.cpp b/test/poll.cpp
index 668e8ed9..ae505695 100644
--- a/test/poll.cpp
+++ b/test/poll.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/test/secret.cpp b/test/secret.cpp
index aa8c1075..b04d35cf 100644
--- a/test/secret.cpp
+++ b/test/secret.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/test/secure_storage.cpp b/test/secure_storage.cpp
index 2890d255..178947fe 100644
--- a/test/secure_storage.cpp
+++ b/test/secure_storage.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/test/set_with_position.cpp b/test/set_with_position.cpp
index f5da3961..11e94887 100644
--- a/test/set_with_position.cpp
+++ b/test/set_with_position.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,7 @@
 #include "td/telegram/SetWithPosition.h"
 
 #include "td/utils/common.h"
+#include "td/utils/misc.h"
 #include "td/utils/Random.h"
 #include "td/utils/tests.h"
 
@@ -21,8 +22,7 @@ template 
 class OldSetWithPosition {
  public:
   void add(T value) {
-    auto it = std::find(values_.begin(), values_.end(), value);
-    if (it != values_.end()) {
+    if (td::contains(values_, value)) {
       return;
     }
     values_.push_back(value);
@@ -233,7 +233,7 @@ static void test_speed() {
   std::vector> sets(total_size);
   for (size_t i = 0; i < sets.size(); i++) {
     sets[i] = make_unique();
-    sets[i]->add(int(i));
+    sets[i]->add(narrow_cast(i));
   }
   for (size_t d = 1; d < sets.size(); d *= 2) {
     for (size_t i = 0; i < sets.size(); i += 2 * d) {
diff --git a/test/string_cleaning.cpp b/test/string_cleaning.cpp
index b35cee1c..890c1a5b 100644
--- a/test/string_cleaning.cpp
+++ b/test/string_cleaning.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/test/tdclient.cpp b/test/tdclient.cpp
index d88d4a26..5841bd90 100644
--- a/test/tdclient.cpp
+++ b/test/tdclient.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
 //
 // Distributed under the Boost Software License, Version 1.0. (See accompanying
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -309,7 +309,7 @@ class SetUsername : public Task {
       auto chat = move_tl_object_as(res);
       this->send_query(
           make_tl_object(
-              chat->id_, 0, false, false, nullptr,
+              chat->id_, 0, nullptr, nullptr,
               make_tl_object(
                   make_tl_object(PSTRING() << tag_ << " INIT", Auto()), false, false)),
           [](auto res) {});
@@ -376,7 +376,7 @@ class TestA : public Task {
       auto chat = move_tl_object_as(res);
       for (int i = 0; i < 20; i++) {
         this->send_query(make_tl_object(
-                             chat->id_, 0, false, false, nullptr,
+                             chat->id_, 0, nullptr, nullptr,
                              make_tl_object(
                                  make_tl_object(PSTRING() << tag_ << " " << (1000 + i), Auto()),
                                  false, false)),
@@ -424,7 +424,7 @@ class TestSecretChat : public Task {
       LOG(INFO) << "SEND ENCRYPTED MESSAGES";
       for (int i = 0; i < 20; i++) {
         send_query(make_tl_object(
-                       chat_id_, 0, false, false, nullptr,
+                       chat_id_, 0, nullptr, nullptr,
                        make_tl_object(
                            make_tl_object(PSTRING() << tag_ << " " << (1000 + i), Auto()), false,
                            false)),
@@ -486,7 +486,7 @@ class TestFileGenerated : public Task {
     file.flush_write().ensure();  // important
     file.close();
     this->send_query(make_tl_object(
-                         chat_id_, 0, false, false, nullptr,
+                         chat_id_, 0, nullptr, nullptr,
                          make_tl_object(
                              make_tl_object(file_path, "square", 0),
                              make_tl_object(
@@ -495,7 +495,7 @@ class TestFileGenerated : public Task {
                      [](auto res) { check_td_error(res); });
 
     this->send_query(
-        make_tl_object(chat_id_, 0, false, false, nullptr,
+        make_tl_object(chat_id_, 0, nullptr, nullptr,
                                             make_tl_object(
                                                 make_tl_object(file_path, "square", 0),
                                                 nullptr, make_tl_object(tag_, Auto()))),
@@ -601,7 +601,7 @@ class CheckTestC : public Task {
   void one_file() {
     this->send_query(
         make_tl_object(
-            chat_id_, 0, false, false, nullptr,
+            chat_id_, 0, nullptr, nullptr,
             make_tl_object(
                 make_tl_object(PSTRING() << tag_ << " ONE_FILE", Auto()), false, false)),
         [](auto res) { check_td_error(res); });
@@ -874,6 +874,7 @@ TEST(Client, SimpleMulti) {
   }
 }
 
+#if !TD_THREAD_UNSUPPORTED
 TEST(Client, Multi) {
   std::vector threads;
   for (int i = 0; i < 4; i++) {
@@ -895,6 +896,7 @@ TEST(Client, Multi) {
     thread.join();
   }
 }
+#endif
 
 TEST(PartsManager, hands) {
   //Status init(int64 size, int64 expected_size, bool is_size_final, size_t part_size,