Merge commit 'ab5d35371b1fdcfb30dfa981ede54ec83db214a9'

Conflicts:
	sqlite/CMakeLists.txt
	td/telegram/ContactsManager.cpp
	td/telegram/Photo.cpp
	td/telegram/Td.cpp
	td/telegram/WebPagesManager.cpp
	td/telegram/files/FileManager.h
This commit is contained in:
Andrea Cavalli 2020-07-29 16:39:33 +02:00
commit cc087aeacd
259 changed files with 6833 additions and 2419 deletions

View File

@ -57,6 +57,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
@ -64,11 +65,13 @@ FixNamespaceComments: true
ForEachMacros:
- Q_FOREACH_THIS_LIST_MUST_BE_NON_EMPTY
IncludeBlocks: Preserve
#IndentCaseBlocks: false
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
# InsertTrailingCommas: None
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
@ -78,6 +81,7 @@ MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# ObjCBinPackProtocolList: Never
# ObjCBlockIndentWidth: 2
# ObjCBreakBeforeNestedBlockParam: true
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
@ -101,15 +105,18 @@ SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 100 # 8
UseCRLF: false
UseTab: Never
...

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TDLib VERSION 1.6.6 LANGUAGES CXX C)
project(TDLib VERSION 1.6.7 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR "lib")
@ -276,7 +276,7 @@ if (NOT CMAKE_CROSSCOMPILING)
endif()
if (NOT OPENSSL_FOUND)
message(WARNING "Not found OpenSSL: skip TDLib, tdactor, tdnet, tddb")
message(WARNING "Can't find OpenSSL: stop TDLib building")
return()
endif()
@ -284,7 +284,12 @@ if (NOT ZLIB_FOUND)
find_package(ZLIB)
endif()
if (NOT ZLIB_FOUND)
message(WARNING "Not found zlib: skip TDLib, tdactor, tdnet, tddb")
message(WARNING "Can't find zlib: stop TDLib building")
return()
endif()
if (NOT TDUTILS_MIME_TYPE)
message(WARNING "Option TDUTILS_MIME_TYPE must not be disabled: stop TDLib building")
return()
endif()
@ -419,6 +424,7 @@ set(TDLIB_SOURCE
td/telegram/files/FileManager.cpp
td/telegram/files/FileStats.cpp
td/telegram/files/FileStatsWorker.cpp
td/telegram/files/FileType.cpp
td/telegram/files/FileUploader.cpp
td/telegram/files/PartsManager.cpp
td/telegram/files/ResourceManager.cpp
@ -482,6 +488,7 @@ set(TDLIB_SOURCE
td/telegram/StateManager.cpp
td/telegram/StickersManager.cpp
td/telegram/StorageManager.cpp
td/telegram/SuggestedAction.cpp
td/telegram/Td.cpp
td/telegram/TdDb.cpp
td/telegram/TermsOfService.cpp
@ -670,6 +677,7 @@ set(TDLIB_SOURCE
td/telegram/StickerSetId.h
td/telegram/StickersManager.h
td/telegram/StorageManager.h
td/telegram/SuggestedAction.h
td/telegram/Td.h
td/telegram/TdCallback.h
td/telegram/TdDb.h
@ -732,7 +740,7 @@ set(MEMPROF_SOURCE
file(MAKE_DIRECTORY auto)
if (WIN32)
if (CMAKE_HOST_WIN32)
set(GIT_COMMIT_CMD powershell -ExecutionPolicy ByPass ./gen_git_commit_h.ps1)
else()
set(GIT_COMMIT_CMD ./gen_git_commit_h.sh)
@ -784,6 +792,13 @@ add_library(tdcore STATIC ${TDLIB_SOURCE})
target_include_directories(tdcore PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDE_DIR}>)
target_include_directories(tdcore SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(tdcore PUBLIC tdapi tdactor tdutils tdnet tddb PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
if (WIN32)
if (MINGW)
target_link_libraries(tdcore PRIVATE ws2_32 mswsock)
else()
target_link_libraries(tdcore PRIVATE ws2_32 Mswsock)
endif()
endif()
if (NOT CMAKE_CROSSCOMPILING)
add_dependencies(tdcore tl_generate_common)

View File

@ -198,7 +198,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.6.6 REQUIRED)
find_package(Td 1.6.7 REQUIRED)
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt).

View File

@ -281,7 +281,7 @@ function split_file($file, $chunks, $undo) {
'secret_chats_manager[_(-][^.]|SecretChatsManager' => 'SecretChatsManager',
'stickers_manager[_(-][^.]|StickersManager' => 'StickersManager',
'[>](td_db[(][)]|get_td_db_impl[(])|TdDb[^A-Za-z]' => 'TdDb',
'TopDialogCategory|top_dialog_category_from_td_api' => 'TopDialogCategory',
'TopDialogCategory|get_top_dialog_category' => 'TopDialogCategory',
'top_dialog_manager[_(-][^.]|TopDialogManager' => 'TopDialogManager',
'updates_manager[_(-][^.]|UpdatesManager|get_difference[)]' => 'UpdatesManager',
'WebPageId(Hash)?' => 'WebPageId',

View File

@ -8,6 +8,13 @@ endif()
#TODO: all benchmarks in one file
add_executable(bench_crypto bench_crypto.cpp)
target_link_libraries(bench_crypto PRIVATE tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
if (WIN32)
if (MINGW)
target_link_libraries(bench_crypto PRIVATE ws2_32 mswsock)
else()
target_link_libraries(bench_crypto PRIVATE ws2_32 Mswsock)
endif()
endif()
target_include_directories(bench_crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
add_executable(bench_actor bench_actor.cpp)

View File

@ -61,15 +61,16 @@ class HelloWorld : public Actor {
}
}
Status do_loop() {
sync_with_poll(socket_fd_);
TRY_STATUS(read_loop());
TRY_STATUS(write_loop());
if (can_close(socket_fd_)) {
if (can_close_local(socket_fd_)) {
return Status::Error("CLOSE");
}
return Status::OK();
}
Status write_loop() {
while (can_write(socket_fd_) && write_pos_ < write_buf_.size()) {
while (can_write_local(socket_fd_) && write_pos_ < write_buf_.size()) {
TRY_RESULT(written, socket_fd_.write(Slice(write_buf_).substr(write_pos_)));
write_pos_ += written;
if (write_pos_ == write_buf_.size()) {
@ -80,7 +81,7 @@ class HelloWorld : public Actor {
return Status::OK();
}
Status read_loop() {
while (can_read(socket_fd_)) {
while (can_read_local(socket_fd_)) {
TRY_RESULT(read_size, socket_fd_.read(MutableSlice(read_buf.data(), read_buf.size())));
for (size_t i = 0; i < read_size; i++) {
if (read_buf[i] == '\n') {

View File

@ -34,6 +34,10 @@ class HttpEchoConnection : public Actor {
Scheduler::subscribe(fd_.get_poll_info().extract_pollable_fd(this));
reader_.init(&fd_.input_buffer(), 1024 * 1024, 0);
}
void tear_down() override {
Scheduler::unsubscribe_before_close(fd_.get_poll_info().get_pollable_fd_ref());
fd_.close();
}
void handle_query() {
query_ = HttpQuery();
@ -51,19 +55,18 @@ class HttpEchoConnection : public Actor {
}
void loop() override {
sync_with_poll(fd_);
auto status = [&] {
TRY_STATUS(loop_read());
TRY_STATUS(loop_write());
return Status::OK();
}();
if (status.is_error() || can_close(fd_)) {
if (status.is_error() || can_close_local(fd_)) {
stop();
}
}
Status loop_read() {
if (can_read(fd_)) {
TRY_STATUS(fd_.flush_read());
}
TRY_STATUS(fd_.flush_read());
while (true) {
TRY_RESULT(need, reader_.read_next(&query_));
if (need == 0) {
@ -80,7 +83,7 @@ class HttpEchoConnection : public Actor {
}
};
const int N = 4;
const int N = 8;
class Server : public TcpListener::Callback {
public:
void start_up() override {

View File

@ -903,12 +903,12 @@ class RingBenchmark : public td::Benchmark {
void test_queue() {
std::vector<td::thread> threads;
constexpr size_t threads_n = 100;
std::vector<td::MpscPollableQueue<int>> queues(threads_n);
static constexpr size_t THREAD_COUNT = 100;
std::vector<td::MpscPollableQueue<int>> queues(THREAD_COUNT);
for (auto &q : queues) {
q.init();
}
for (size_t i = 0; i < threads_n; i++) {
for (size_t i = 0; i < THREAD_COUNT; i++) {
threads.emplace_back([&q = queues[i]] {
while (true) {
auto got = q.reader_wait_nonblock();
@ -923,7 +923,7 @@ void test_queue() {
while (true) {
td::usleep_for(100);
for (int i = 0; i < 5; i++) {
queues[td::Random::fast(0, threads_n - 1)].writer_put(1);
queues[td::Random::fast(0, THREAD_COUNT - 1)].writer_put(1);
}
}
}

View File

@ -113,11 +113,11 @@ td::Result<TlsInfo> test_tls(const td::string &url) {
add_random(32);
add_string("\x20");
add_random(32);
add_string("\x00\x22");
add_string("\x00\x20");
add_grease(0);
add_string(
"\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00"
"\x2f\x00\x35\x00\x0a\x01\x00\x01\x91");
"\x2f\x00\x35\x01\x00\x01\x93");
add_grease(2);
add_string("\x00\x00\x00\x00");
add_length(url.size() + 5);
@ -129,8 +129,8 @@ td::Result<TlsInfo> test_tls(const td::string &url) {
add_grease(4);
add_string(
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68"
"\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x14\x00\x12\x04\x03\x08\x04\x04"
"\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29");
"\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04"
"\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29");
add_grease(4);
add_string("\x00\x01\x00\x00\x1d\x00\x20");
add_key();

View File

@ -63,6 +63,7 @@ select.large { font-size: large; }
<option>Ubuntu 14</option>
<option>Ubuntu 16</option>
<option>Ubuntu 18</option>
<option>Ubuntu 20</option>
<option>Other</option>
</select>
<p></p>
@ -317,11 +318,12 @@ function onOsChanged() {
if (target === 'WebAssembly') {
text = 'TDLib is available in a prebuilt form as an <a href="https://www.npmjs.com/">NPM</a> package <a href="https://www.npmjs.com/package/tdweb">tdweb</a>.<br>' +
'If you want to build it manually, take a look at our <a href="https://github.com/tdlib/td/tree/master/example/web">example</a>';
'If you want to build it manually, take a look at our <a href="https://github.com/tdlib/td/tree/master/example/web">example</a>.';
target = '';
}
if (target === 'Android') {
text = 'TDLib for Android is available in a prebuilt form and can be downloaded from <a href="https://core.telegram.org/tdlib/tdlib.zip">there</a>.';
text = 'TDLib for Android is available in a prebuilt form and can be downloaded from <a href="https://core.telegram.org/tdlib/tdlib.zip">there</a>.<br>' +
'See <a href="https://github.com/tdlib/td/issues/77#issuecomment-640719893">build instructions</a> if you want to build the latest TDLib version or want to build TDLib with different interface.';
target = '';
}
if (target === 'iOS') {
@ -411,7 +413,7 @@ function onOptionsChanged() {
var use_vcpkg = os_windows;
var use_lto = false;
if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (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 === 'Ubuntu 20' || linux_distro === 'Other')))) {
document.getElementById('buildLtoDiv').style.display = 'block';
use_lto = document.getElementById('buildLtoCheckbox').checked;
} else {
@ -586,6 +588,7 @@ function onOptionsChanged() {
case 'Ubuntu 14':
case 'Ubuntu 16':
case 'Ubuntu 18':
case 'Ubuntu 20':
if (linux_distro.includes('Debian') && !use_root) {
commands.push('su -');
}
@ -609,7 +612,7 @@ function onOptionsChanged() {
packages += ' default-jdk';
}
if (use_clang) {
if (linux_distro === 'Ubuntu 18') {
if (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
packages += ' clang-6.0 libc++-dev libc++abi-dev';
} else {
if (linux_distro === 'Ubuntu 14') {
@ -783,7 +786,7 @@ function onOptionsChanged() {
var prefix = '';
if (os_linux) {
if (use_clang) {
if (linux_distro === 'Ubuntu 18') {
if (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
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');

View File

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

View File

@ -291,7 +291,6 @@ class TdExample {
parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->system_version_ = "Unknown";
parameters->application_version_ = "1.0";
parameters->enable_storage_optimizer_ = true;
send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)),

View File

@ -80,7 +80,6 @@ namespace TdExample
parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.SystemLanguageCode = "en";
parameters.DeviceModel = "Desktop";
parameters.SystemVersion = "Unknown";
parameters.ApplicationVersion = "1.0";
parameters.EnableStorageOptimizer = true;

View File

@ -107,7 +107,6 @@ public final class Example {
parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.systemLanguageCode = "en";
parameters.deviceModel = "Desktop";
parameters.systemVersion = "Unknown";
parameters.applicationVersion = "1.0";
parameters.enableStorageOptimizer = true;

View File

@ -106,7 +106,6 @@ while True:
'api_hash': 'a3406de8d171bb422bb6ddf3bbd800e2',
'system_language_code': 'en',
'device_model': 'Desktop',
'system_version': 'Linux',
'application_version': '1.0',
'enable_storage_optimizer': True}})

View File

@ -117,7 +117,6 @@ func updateAuthorizationState(authorizationState: Dictionary<String, Any>) {
"api_hash":"a3406de8d171bb422bb6ddf3bbd800e2",
"system_language_code":"en",
"device_model":"Desktop",
"system_version":"Unknown",
"application_version":"1.0",
"enable_storage_optimizer":true
]

View File

@ -43,7 +43,6 @@ namespace TdApp
parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters.SystemLanguageCode = "en";
parameters.DeviceModel = "Desktop";
parameters.SystemVersion = "Unknown";
parameters.ApplicationVersion = "1.0.0";
_client.Send(new TdApi.SetTdlibParameters(parameters), null);
_client.Send(new TdApi.CheckDatabaseEncryptionKey(), null);

View File

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

View File

@ -1,6 +1,6 @@
#!/bin/sh
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; }
emcmake true 2> /dev/null || { echo 'emcmake not found. Install emsdk and add emcmake 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 `emcmake` and `emmake` to the PATH environment variable via `emsdk/emsdk_env.sh` script.'; exit 1; }
rm -rf build/generate
rm -rf build/asmjs
@ -28,11 +28,11 @@ cmake $TD_ROOT || exit 1
cd ../..
cd build/wasm
eval emconfigure cmake -DCMAKE_BUILD_TYPE=MinSizeRel $OPENSSL_OPTIONS $TD_ROOT || exit 1
eval emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel $OPENSSL_OPTIONS $TD_ROOT || exit 1
cd ../..
cd build/asmjs
eval emconfigure cmake -DCMAKE_BUILD_TYPE=MinSizeRel $OPENSSL_OPTIONS -DASMJS=1 $TD_ROOT || exit 1
eval emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel $OPENSSL_OPTIONS -DASMJS=1 $TD_ROOT || exit 1
cd ../..
echo "Generating TDLib autogenerated source files..."

View File

@ -20,6 +20,13 @@ add_library(tdsqlite STATIC ${SQLITE_SOURCE})
target_include_directories(tdsqlite PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_include_directories(tdsqlite SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(tdsqlite PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
if (WIN32)
if (MINGW)
target_link_libraries(tdsqlite PRIVATE ws2_32 mswsock)
else()
target_link_libraries(tdsqlite PRIVATE ws2_32 Mswsock)
endif()
endif()
target_compile_definitions(tdsqlite PRIVATE
-DSQLITE_MAX_MMAP_SIZE=50331648

View File

@ -34,7 +34,7 @@ ok = Ok;
//@api_hash Application identifier hash for Telegram API access, which can be obtained at https://my.telegram.org
//@system_language_code IETF language tag of the user's operating system language; must be non-empty
//@device_model Model of the device the application is being run on; must be non-empty
//@system_version Version of the operating system the application is being run on; must be non-empty
//@system_version Version of the operating system the application is being run on. If empty, the version is automatically detected by TDLib
//@application_version Application version; must be non-empty
//@enable_storage_optimizer If set to true, old files will automatically be deleted
//@ignore_file_names If set to true, original file names will be ignored. Otherwise, downloaded files will be saved under names as close as possible to the original name
@ -43,7 +43,7 @@ tdlibParameters use_test_dc:Bool database_directory:string files_directory:strin
//@class AuthenticationCodeType @description Provides information about the method by which an authentication code is delivered to the user
//@description An authentication code is delivered via a private Telegram message, which can be viewed in another client @length Length of the code
//@description An authentication code is delivered via a private Telegram message, which can be viewed from another active session @length Length of the code
authenticationCodeTypeTelegramMessage length:int32 = AuthenticationCodeType;
//@description An authentication code is delivered via an SMS message to the specified phone number @length Length of the code
@ -63,7 +63,7 @@ authenticationCodeInfo phone_number:string type:AuthenticationCodeType next_type
emailAddressAuthenticationCodeInfo email_address_pattern:string length:int32 = EmailAddressAuthenticationCodeInfo;
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity in UTF-16 code units @length Length of the entity, in UTF-16 code units @type Type of the entity
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity, in UTF-16 code units @length Length of the entity, in UTF-16 code units @type Type of the entity
textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
//@description Contains a list of text entities @entities List of text entities
@ -78,7 +78,7 @@ formattedText text:string entities:vector<textEntity> = FormattedText;
termsOfService text:formattedText min_user_age:int32 show_popup:Bool = TermsOfService;
//@class AuthorizationState @description Represents the current authorization state of the client
//@class AuthorizationState @description Represents the current authorization state of the TDLib client
//@description TDLib needs TdlibParameters for initialization
authorizationStateWaitTdlibParameters = AuthorizationState;
@ -143,7 +143,7 @@ localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_
//@description Represents a remote file
//@id Remote file identifier; may be empty. Can be used by the current user across application restarts or even from other devices. Uniquely identifies a file, but a file can have a lot of different valid identifiers.
//-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
//-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 application with the HTTP URL in the original_path and "#url#" as the conversion string. Application 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
@ -165,14 +165,14 @@ file id:int32 size:int32 expected_size:int32 local:localFile remote:remoteFile =
inputFileId id:int32 = InputFile;
//@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 the file database is disabled, then the corresponding object with the file must be preloaded by the client
//-For example, if the file is from a message, then the message must be not deleted and accessible to the user. If the file database is disabled, then the corresponding object with the file must be preloaded by the application
//@id Remote file identifier
inputFileRemote id:string = InputFile;
//@description A file defined by a local path @path Local path to the file
inputFileLocal path:string = InputFile;
//@description A file generated by the client @original_path Local path to a file from which the file is generated; may be empty if there is no such file
//@description A file generated by the application @original_path Local path to a file from which the file is generated; may be empty if there is no such file
//@conversion String specifying the conversion applied to the original file; should be persistent across application restarts. Conversions beginning with '#' are reserved for internal TDLib usage
//@expected_size Expected size of the generated file; 0 if unknown
inputFileGenerated original_path:string conversion:string expected_size:int32 = InputFile;
@ -293,7 +293,7 @@ contact phone_number:string first_name:string last_name:string vcard:string user
//@description Describes a location on planet Earth @latitude Latitude of the location in degrees; as defined by the sender @longitude Longitude of the location, in degrees; as defined by the sender
location latitude:double longitude:double = Location;
//@description Describes a venue @location Venue location; as defined by the sender @title Venue name; as defined by the sender @address Venue address; as defined by the sender @provider Provider of the venue database; as defined by the sender. Currently only "foursquare" needs to be supported
//@description Describes a venue @location Venue location; as defined by the sender @title Venue name; as defined by the sender @address Venue address; as defined by the sender @provider Provider of the venue database; as defined by the sender. Currently only "foursquare" and "gplaces" (Google Places) needs to be supported
//@id Identifier of the venue in the provider database; as defined by the sender @type Type of the venue in the provider database; as defined by the sender
venue location:location title:string address:string provider:string id:string type:string = Venue;
@ -308,12 +308,17 @@ game id:int64 short_name:string title:string text:formattedText description:stri
poll id:int64 question:string options:vector<pollOption> total_voter_count:int32 recent_voter_user_ids:vector<int32> is_anonymous:Bool type:PollType open_period:int32 close_date:int32 is_closed:Bool = Poll;
//@description Describes a user profile photo @id Photo identifier; 0 for an empty photo. Can be used to find a photo in a list of userProfilePhotos
//@small A small (160x160) user profile photo. The file can be downloaded only before the photo is changed @big A big (640x640) user profile photo. The file can be downloaded only before the photo is changed
profilePhoto id:int64 small:file big:file = ProfilePhoto;
//@description Describes a user profile photo @id Photo identifier; 0 for an empty photo. Can be used to find a photo in a list of user profile photos
//@small A small (160x160) user profile photo. The file can be downloaded only before the photo is changed
//@big A big (640x640) user profile photo. The file can be downloaded only before the photo is changed
//@has_animation True, if the photo has animated variant
profilePhoto id:int64 small:file big:file has_animation:Bool = ProfilePhoto;
//@description Describes the photo of a chat @small A small (160x160) chat photo. The file can be downloaded only before the photo is changed @big A big (640x640) chat photo. The file can be downloaded only before the photo is changed
chatPhoto small:file big:file = ChatPhoto;
//@description Contains basic information about the photo of a chat
//@small A small (160x160) chat photo variant in JPEG format. The file can be downloaded only before the photo is changed
//@big A big (640x640) chat photo variant in JPEG format. The file can be downloaded only before the photo is changed
//@has_animation True, if the photo has animated variant
chatPhotoInfo small:file big:file has_animation:Bool = ChatPhotoInfo;
//@class UserType @description Represents the type of a user. The following types are possible: regular users, deleted users and bots
@ -326,7 +331,7 @@ 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
//@can_read_all_group_messages True, if the bot can read all messages in basic group or supergroup chats and not just those addressed to the bot. In private and channel chats a bot can always read all messages
//@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
//@is_inline True, if the bot supports inline queries @inline_query_placeholder Placeholder for inline queries (displayed on the application 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 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
@ -344,27 +349,68 @@ botInfo description:string commands:vector<botCommand> = BotInfo;
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
//@description Animated variant of a chat photo in MPEG4 format
//@length Animation width and height
//@file Information about the animation file
//@main_frame_timestamp Timestamp of the frame, used as static chat photo
animatedChatPhoto length:int32 file:file main_frame_timestamp:double = AnimatedChatPhoto;
//@description Describes a chat or user profile photo
//@id Unique photo identifier
//@added_date Point in time (Unix timestamp) when the photo has been added
//@minithumbnail Photo minithumbnail; may be null
//@sizes Available variants of the photo in JPEG format, in different size
//@animation Animated variant of the photo in MPEG4 format; may be null
chatPhoto id:int64 added_date:int32 minithumbnail:minithumbnail sizes:vector<photoSize> animation:animatedChatPhoto = ChatPhoto;
//@description Contains a list of chat or user profile photos @total_count Total number of photos @photos List of photos
chatPhotos total_count:int32 photos:vector<chatPhoto> = ChatPhotos;
//@class InputChatPhoto @description Describes a photo to be set as a user profile or chat photo
//@description A previously used profile photo of the current user @chat_photo_id Identifier of the profile photo to reuse
inputChatPhotoPrevious chat_photo_id:int64 = InputChatPhoto;
//@description A static photo in JPEG format @photo Photo to be set as profile photo. Only inputFileLocal and inputFileGenerated are allowed
inputChatPhotoStatic photo:InputFile = InputChatPhoto;
//@description An animation in MPEG4 format; must be square, shorter than 10 seconds, have width between 160 and 800 and be at most 2MB in size
//@animation Animation to be set as profile photo. Only inputFileLocal and inputFileGenerated are allowed
//@main_frame_timestamp Timestamp of the frame, which will be used as static chat photo
inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputChatPhoto;
//@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
//@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
//@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 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
//@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 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
//@photo User profile photo; may be null
//@is_blocked True, if the user is blocked 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 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<photoSize> = UserProfilePhoto;
//@description Contains part of the list of user photos @total_count Total number of user profile photos @photos A list of photos
userProfilePhotos total_count:int32 photos:vector<userProfilePhoto> = UserProfilePhotos;
//@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 photo:chatPhoto 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 Represents a list of users @total_count Approximate total count of users found @user_ids A list of user identifiers
users total_count:int32 user_ids:vector<int32> = Users;
@ -426,8 +472,11 @@ chatMemberStatusLeft = ChatMemberStatus;
chatMemberStatusBanned banned_until_date:int32 = ChatMemberStatus;
//@description A user with information about joining/leaving a chat @user_id User identifier of the chat member @inviter_user_id Identifier of a user that invited/promoted/banned this member in the chat; 0 if unknown
//@joined_chat_date Point in time (Unix timestamp) when the user joined a chat @status Status of the member in the chat @bot_info If the user is a bot, information about the bot; may be null. Can be null even for a bot if the bot is not a chat member
//@description A user with information about joining/leaving a chat @user_id User identifier of the chat member
//@inviter_user_id Identifier of a user that invited/promoted/banned this member in the chat; 0 if unknown
//@joined_chat_date Point in time (Unix timestamp) when the user joined a chat
//@status Status of the member in the chat
//@bot_info If the user is a bot, information about the bot; may be null. Can be null even for a bot if the bot is not a chat member
chatMember user_id:int32 inviter_user_id:int32 joined_chat_date:int32 status:ChatMemberStatus bot_info:botInfo = ChatMember;
//@description Contains a list of chat members @total_count Approximate total count of chat members found @members A list of chat members
@ -487,8 +536,13 @@ 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 after it has been generated at least once and only for the group creator
basicGroupFullInfo description:string creator_user_id:int32 members:vector<chatMember> invite_link:string = BasicGroupFullInfo;
//@description Contains full information about a basic group
//@photo Chat photo; may be null
//@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 photo:chatPhoto description:string creator_user_id:int32 members:vector<chatMember> invite_link:string = BasicGroupFullInfo;
//@description Represents a supergroup or channel with zero or more members (subscribers in the case of channels). From the point of view of the system, a channel is a special kind of a supergroup: only administrators can post and see the list of members, and posts from all administrators use the name and photo of the channel instead of individual names and profile photos. Unlike supergroups, channels can have an unlimited number of subscribers
@ -508,6 +562,7 @@ basicGroupFullInfo description:string creator_user_id:int32 members:vector<chatM
supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool is_slow_mode_enabled:Bool is_channel:Bool is_verified:Bool restriction_reason:string is_scam:Bool = Supergroup;
//@description Contains full information about a supergroup or channel
//@photo Chat photo; may be null
//@param_description Supergroup or channel description
//@member_count Number of members in the supergroup or channel; 0 if unknown
//@administrator_count Number of privileged users in the supergroup or channel; 0 if unknown
@ -527,7 +582,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co
//@invite_link Invite link for this chat
//@upgraded_from_basic_group_id Identifier of the basic group from which supergroup was upgraded; 0 if none
//@upgraded_from_max_message_id Identifier of the last message in the basic group from which supergroup was upgraded; 0 if none
supergroupFullInfo description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_view_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:string upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo;
supergroupFullInfo photo:chatPhoto description:string member_count:int32 administrator_count:int32 restricted_count:int32 banned_count:int32 linked_chat_id:int53 slow_mode_delay:int32 slow_mode_delay_expires_in:double can_get_members:Bool can_set_username:Bool can_set_sticker_set:Bool can_set_location:Bool can_view_statistics:Bool is_all_history_available:Bool sticker_set_id:int64 location:chatLocation invite_link:string upgraded_from_basic_group_id:int32 upgraded_from_max_message_id:int53 = SupergroupFullInfo;
//@class SecretChatState @description Describes the current secret chat state
@ -550,7 +605,7 @@ secretChatStateClosed = SecretChatState;
//@ttl Current message Time To Live setting (self-destruct timer) for the chat, in seconds
//@key_hash Hash of the currently used key for comparison with the hash of the chat partner's key. This is a string of 36 little-endian bytes, which must be split into groups of 2 bits, each denoting a pixel of one of 4 colors FFFFFF, D5E6F3, 2D5775, and 2F99C9.
//-The pixels must be used to make a 12x12 square image filled from left to right, top to bottom. Alternatively, the first 32 bytes of the hash can be converted to the hexadecimal format and printed as 32 2-digit hex numbers
//@layer Secret chat layer; determines features supported by the other client. Video notes are supported if the layer >= 66; nested text entities and underline and strikethrough entities are supported if the layer >= 101
//@layer Secret chat layer; determines features supported by the chat partner's application. 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;
@ -595,7 +650,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
//@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 application
//@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
@ -759,8 +814,8 @@ chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPo
//@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 scroll position or local chat notification settings can be stored here.) Persistent if the message database is used
chat id:int53 type:ChatType title:string photo:chatPhoto permissions:chatPermissions last_message:message positions:vector<chatPosition> is_marked_as_unread: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;
//@client_data Contains application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used
chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector<chatPosition> is_marked_as_unread: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<int53> = Chats;
@ -777,14 +832,15 @@ chatsNearby users_nearby:vector<chatNearby> supergroups_nearby:vector<chatNearby
chatInviteLink invite_link:string = ChatInviteLink;
//@description Contains information about a chat invite link
//@chat_id Chat identifier of the invite link; 0 if the user is not a member of this chat
//@chat_id Chat identifier of the invite link; 0 if the user have no access to the chat before joining
//@accessible_for If non-zero, the remaining time for which read access is granted to the chat, in seconds
//@type Contains information about the type of the chat
//@title Title of the chat
//@photo Chat photo; may be null
//@member_count Number of members in the chat
//@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 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<int32> is_public:Bool = ChatInviteLinkInfo;
chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string photo:chatPhotoInfo member_count:int32 member_user_ids:vector<int32> is_public:Bool = ChatInviteLinkInfo;
//@class PublicChatType @description Describes a type of public chats
@ -799,13 +855,16 @@ 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;
//@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings
chatActionBarReportSpam can_unarchive:Bool = 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, which can be reported using the method reportChat, or the other user can be blocked using the method blockUser, or the other user can be added to the contact list using the method addContact
//@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings
//@distance If non-negative, the current user was found by the peer through searchChatsNearby and this is the distance between the users
chatActionBarReportAddBlock can_unarchive:Bool distance:int32 = 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;
@ -860,18 +919,18 @@ inlineKeyboardButton text:string type:InlineKeyboardButtonType = InlineKeyboardB
//@class ReplyMarkup @description Contains a description of a custom keyboard and actions that can be done with it to quickly reply to bots
//@description Instructs clients to remove the keyboard once this message has been received. This kind of keyboard can't be received in an incoming message; instead, UpdateChatReplyMarkup with message_id == 0 will be sent
//@description Instructs application to remove the keyboard once this message has been received. This kind of keyboard can't be received in an incoming message; instead, UpdateChatReplyMarkup with message_id == 0 will be sent
//@is_personal True, if the keyboard is removed only for the mentioned users or the target user of a reply
replyMarkupRemoveKeyboard is_personal:Bool = ReplyMarkup;
//@description Instructs clients to force a reply to this message
//@description Instructs application to force a reply to this message
//@is_personal True, if a forced reply must automatically be shown to the current user. For outgoing messages, specify true to show the forced reply only for the mentioned users and for the target user of a reply
replyMarkupForceReply is_personal:Bool = ReplyMarkup;
//@description Contains a custom keyboard layout to quickly reply to bots
//@rows A list of rows of bot keyboard buttons
//@resize_keyboard True, if the client needs to resize the keyboard vertically
//@one_time True, if the client needs to hide the keyboard after use
//@resize_keyboard True, if the application needs to resize the keyboard vertically
//@one_time True, if the application needs to hide the keyboard after use
//@is_personal True, if the keyboard must automatically be shown to the current user. For outgoing messages, specify true to show the keyboard only for the mentioned users and for the target user of a reply
replyMarkupShowKeyboard rows:vector<vector<keyboardButton>> resize_keyboard:Bool one_time:Bool is_personal:Bool = ReplyMarkup;
@ -1059,7 +1118,7 @@ pageBlockCollage page_blocks:vector<PageBlock> caption:pageBlockCaption = PageBl
pageBlockSlideshow page_blocks:vector<PageBlock> caption:pageBlockCaption = PageBlock;
//@description A link to a chat @title Chat title @photo Chat photo; may be null @username Chat username, by which all other information about the chat should be resolved
pageBlockChatLink title:string photo:chatPhoto username:string = PageBlock;
pageBlockChatLink title:string photo:chatPhotoInfo username:string = PageBlock;
//@description A table @caption Table caption @cells Table cells @is_bordered True, if the table is bordered @is_striped True, if the table is striped
pageBlockTable caption:RichText cells:vector<vector<pageBlockTableCell>> is_bordered:Bool is_striped:Bool = PageBlock;
@ -1496,7 +1555,7 @@ messageSupergroupChatCreate title:string = MessageContent;
messageChatChangeTitle title:string = MessageContent;
//@description An updated chat photo @photo New chat photo
messageChatChangePhoto photo:photo = MessageContent;
messageChatChangePhoto photo:chatPhoto = MessageContent;
//@description A deleted chat photo
messageChatDeletePhoto = MessageContent;
@ -1551,7 +1610,7 @@ messagePassportDataSent types:vector<PassportElementType> = MessageContent;
//@description Telegram Passport data has been received; for bots only @elements List of received Telegram Passport elements @credentials Encrypted data credentials
messagePassportDataReceived elements:vector<encryptedPassportElement> credentials:encryptedCredentials = MessageContent;
//@description Message content that is not supported by the client
//@description Message content that is not supported in the current TDLib version
messageUnsupported = MessageContent;
@ -1609,7 +1668,7 @@ textEntityTypeTextUrl url:string = TextEntityType;
textEntityTypeMentionName user_id:int32 = TextEntityType;
//@description A thumbnail to be sent along with a file; should be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
//@description A thumbnail to be sent along with a file; must 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;
@ -1644,8 +1703,8 @@ inputMessageAnimation animation:InputFile thumbnail:inputThumbnail added_sticker
//@performer Performer of the audio; 0-64 characters, may be replaced by the server @caption Audio caption; 0-GetOption("message_caption_length_max") characters
inputMessageAudio audio:InputFile album_cover_thumbnail:inputThumbnail duration:int32 title:string performer:string caption:formattedText = InputMessageContent;
//@description A document message (general file) @document Document to be sent @thumbnail Document thumbnail, if available @caption Document caption; 0-GetOption("message_caption_length_max") characters
inputMessageDocument document:InputFile thumbnail:inputThumbnail caption:formattedText = InputMessageContent;
//@description A document message (general file) @document Document to be sent @thumbnail Document thumbnail, if available @force_file If true, automatic file type detection will be disabled and the document will be always sent as file. Always true for files sent to secret chats @caption Document caption; 0-GetOption("message_caption_length_max") characters
inputMessageDocument document:InputFile thumbnail:inputThumbnail force_file:Bool caption:formattedText = InputMessageContent;
//@description A photo message @photo Photo to send @thumbnail Photo thumbnail to be sent, this is sent to the other party in secret chats only @added_sticker_file_ids File identifiers of the stickers added to the photo, if applicable @width Photo width @height Photo height @caption Photo caption; 0-GetOption("message_caption_length_max") characters
//@ttl Photo TTL (Time To Live), in seconds (0-60). A non-zero TTL can be specified only in private chats
@ -1820,7 +1879,7 @@ stickerSet id:int64 title:string name:string thumbnail:thumbnail is_installed:Bo
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP or TGS format with width and height 100; may be null
//@is_installed True, if the sticker set has been installed by current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously
//@is_official True, if the sticker set is official @is_animated True, is the stickers in the set are animated @is_masks True, if the stickers in the set are masks @is_viewed True for already viewed trending sticker sets
//@size Total number of stickers in the set @covers Contains up to the first 5 stickers from the set, depending on the context. If the client needs more stickers the full set should be requested
//@size Total number of stickers in the set @covers Contains up to the first 5 stickers from the set, depending on the context. If the application needs more stickers the full set should be requested
stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool size:int32 covers:vector<sticker> = StickerSetInfo;
//@description Represents a list of sticker sets @total_count Approximate total number of sticker sets found @sets List of sticker sets
@ -2117,7 +2176,7 @@ chatEventDescriptionChanged old_description:string new_description:string = Chat
chatEventUsernameChanged old_username:string new_username:string = ChatEventAction;
//@description The chat photo was changed @old_photo Previous chat photo value; may be null @new_photo New chat photo value; may be null
chatEventPhotoChanged old_photo:photo new_photo:photo = ChatEventAction;
chatEventPhotoChanged old_photo:chatPhoto new_photo:chatPhoto = 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;
@ -2749,7 +2808,7 @@ connectionStateConnectingToProxy = ConnectionState;
//@description Currently establishing a connection to the Telegram servers
connectionStateConnecting = ConnectionState;
//@description Downloading data received while the client was offline
//@description Downloading data received while the application was offline
connectionStateUpdating = ConnectionState;
//@description There is a working connection to the Telegram servers
@ -2801,6 +2860,15 @@ tMeUrl url:string type:TMeUrlType = TMeUrl;
tMeUrls urls:vector<tMeUrl> = TMeUrls;
//@class SuggestedAction @description Describes an action suggested to the current user
//@description Suggests the user to enable "archive_and_mute_new_chats_from_unknown_users" option
suggestedActionEnableArchiveAndMuteNewChats = SuggestedAction;
//@description Suggests the user to check authorization phone number and change the phone number if it is inaccessible
suggestedActionCheckPhoneNumber = SuggestedAction;
//@description Contains a counter @count Count
count count:int32 = Count;
@ -2882,10 +2950,49 @@ statisticsGraphError error_message:string = StatisticsGraph;
//@message_id Message identifier
//@view_count Number of times the message was viewed
//@forward_count Number of times the message was forwarded
chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionCounters;
chatStatisticsMessageInteractionInfo message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionInfo;
//@description Contains statistics about messages sent by a user
//@user_id User identifier
//@sent_message_count Number of sent messages
//@average_character_count Average number of characters in sent messages
chatStatisticsMessageSenderInfo user_id:int32 sent_message_count:int32 average_character_count:int32 = ChatStatisticsMessageSenderInfo;
//@description Contains statistics about administrator actions done by a user
//@user_id Administrator user identifier
//@deleted_message_count Number of messages deleted by the administrator
//@banned_user_count Number of users banned by the administrator
//@restricted_user_count Number of users restricted by the administrator
chatStatisticsAdministratorActionsInfo user_id:int32 deleted_message_count:int32 banned_user_count:int32 restricted_user_count:int32 = ChatStatisticsAdministratorActionsInfo;
//@description Contains statistics about number of new members invited by a user
//@user_id User identifier
//@added_member_count Number of new members invited by the user
chatStatisticsInviterInfo user_id:int32 added_member_count:int32 = ChatStatisticsInviterInfo;
//@description A detailed statistics about a chat
//@class ChatStatistics @description Contains a detailed statistics about a chat
//@description A detailed statistics about a supergroup chat
//@period A period to which the statistics applies
//@member_count Number of members in the chat
//@message_count Number of messages sent to the chat
//@viewer_count Number of users who viewed messages in the chat
//@sender_count Number of users who sent messages to the chat
//@member_count_graph A graph containing number of members in the chat
//@join_graph A graph containing number of members joined and left the chat
//@join_by_source_graph A graph containing number of new member joins per source
//@language_graph A graph containing distribution of active users per language
//@message_content_graph A graph containing distribution of sent messages by content type
//@action_graph A graph containing number of different actions in the chat
//@day_graph A graph containing distribution of message views per hour
//@week_graph A graph containing distribution of message views per day of week
//@top_senders List of users sent most messages in the last week
//@top_administrators List of most active administrators in the last week
//@top_inviters List of most active inviters of new members in the last week
chatStatisticsSupergroup period:dateRange member_count:statisticsValue message_count:statisticsValue viewer_count:statisticsValue sender_count:statisticsValue member_count_graph:StatisticsGraph join_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_content_graph:StatisticsGraph action_graph:StatisticsGraph day_graph:StatisticsGraph week_graph:StatisticsGraph top_senders:vector<chatStatisticsMessageSenderInfo> top_administrators:vector<chatStatisticsAdministratorActionsInfo> top_inviters:vector<chatStatisticsInviterInfo> = ChatStatistics;
//@description A detailed statistics about a channel chat
//@period A period to which the statistics applies
//@member_count Number of members in the chat
//@mean_view_count Mean number of times the recently sent messages was viewed
@ -2901,7 +3008,7 @@ chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forwa
//@message_interaction_graph A graph containing number of chat message views and shares
//@instant_view_interaction_graph A graph containing number of views of associated with the chat instant views
//@recent_message_interactions Detailed statistics about number of views and shares of recently sent messages
chatStatistics period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector<chatStatisticsMessageInteractionCounters> = ChatStatistics;
chatStatisticsChannel period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector<chatStatisticsMessageInteractionInfo> = ChatStatistics;
//@class Update @description Contains notifications about data changes
@ -2938,18 +3045,18 @@ 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
//@description A message with a live location was viewed. When the update is received, the application 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
//@description A new chat has been loaded/created. This update is guaranteed to come before the chat identifier is returned to the application. The chat field changes will be reported through separate updates @chat The chat
updateNewChat chat:chat = 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;
//@description A chat photo was changed @chat_id Chat identifier @photo The new chat photo; may be null
updateChatPhoto chat_id:int53 photo:chatPhoto = Update;
updateChatPhoto chat_id:int53 photo:chatPhotoInfo = Update;
//@description Chat permissions was changed @chat_id Chat identifier @permissions The new chat permissions
updateChatPermissions chat_id:int53 permissions:chatPermissions = Update;
@ -3035,16 +3142,16 @@ updateUserChatAction chat_id:int53 user_id:int32 action:ChatAction = Update;
//@description The user went online or offline @user_id User identifier @status New status of the user
updateUserStatus user_id:int32 status:UserStatus = Update;
//@description Some data of a user has changed. This update is guaranteed to come before the user identifier is returned to the client @user New data about the user
//@description Some data of a user has changed. This update is guaranteed to come before the user identifier is returned to the application @user New data about the user
updateUser user:user = Update;
//@description Some data of a basic group has changed. This update is guaranteed to come before the basic group identifier is returned to the client @basic_group New data about the group
//@description Some data of a basic group has changed. This update is guaranteed to come before the basic group identifier is returned to the application @basic_group New data about the group
updateBasicGroup basic_group:basicGroup = Update;
//@description Some data of a supergroup or a channel has changed. This update is guaranteed to come before the supergroup identifier is returned to the client @supergroup New data about the supergroup
//@description Some data of a supergroup or a channel has changed. This update is guaranteed to come before the supergroup identifier is returned to the application @supergroup New data about the supergroup
updateSupergroup supergroup:supergroup = Update;
//@description Some data of a secret chat has changed. This update is guaranteed to come before the secret chat identifier is returned to the client @secret_chat New data about the secret chat
//@description Some data of a secret chat has changed. This update is guaranteed to come before the secret chat identifier is returned to the application @secret_chat New data about the secret chat
updateSecretChat secret_chat:secretChat = Update;
//@description Some data from userFullInfo has been changed @user_id User identifier @user_full_info New full information about the user
@ -3056,7 +3163,7 @@ updateBasicGroupFullInfo basic_group_id:int32 basic_group_full_info:basicGroupFu
//@description Some data from supergroupFullInfo has been changed @supergroup_id Identifier of the supergroup or channel @supergroup_full_info New full information about the supergroup
updateSupergroupFullInfo supergroup_id:int32 supergroup_full_info:supergroupFullInfo = Update;
//@description Service notification from the server. Upon receiving this the client must show a popup with the content of the notification
//@description Service notification from the server. Upon receiving this the application must show a popup with the content of the notification
//@type Notification type. If type begins with "AUTH_KEY_DROP_", then two buttons "Cancel" and "Log out" should be shown under notification; if user presses the second, all local data should be destroyed using Destroy method
//@content Notification content
updateServiceNotification type:string content:MessageContent = Update;
@ -3064,11 +3171,11 @@ updateServiceNotification type:string content:MessageContent = Update;
//@description Information about a file was updated @file New data about the file
updateFile file:file = Update;
//@description The file generation process needs to be started by the client
//@description The file generation process needs to be started by the application
//@generation_id Unique identifier for the generation process
//@original_path The path to a file from which a new file is generated; may be empty
//@destination_path The path to a file that should be created and where the new file should be generated
//@conversion String specifying the conversion applied to the original file. If conversion is "#url#" than original_path contains an HTTP/HTTPS URL of a file, which should be downloaded by the client
//@conversion String specifying the conversion applied to the original file. If conversion is "#url#" than original_path contains an HTTP/HTTPS URL of a file, which should be downloaded by the application
updateFileGenerationStart generation_id:int64 original_path:string destination_path:string conversion:string = Update;
//@description File generation is no longer needed @generation_id Unique identifier for the generation process
@ -3124,7 +3231,7 @@ 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 The 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
//@description The list of users nearby has changed. The update is guaranteed to be sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description The list of supported dice emojis has changed @emojis The new list of supported dice emojis
@ -3133,11 +3240,14 @@ updateDiceEmojis emojis:vector<string> = Update;
//@description The parameters of animation search through GetOption("animation_search_bot_username") bot has changed @provider Name of the animation search provider @emojis The new list of emojis suggested for searching
updateAnimationSearchParameters provider:string emojis:vector<string> = 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
//@description The list of suggested to the user actions has changed @added_actions Added suggested actions @removed_actions Removed suggested actions
updateSuggestedActions added_actions:vector<SuggestedAction> removed_actions:vector<SuggestedAction> = 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; 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
//@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; 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;
@ -3233,7 +3343,7 @@ 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,
//-or if there is no pending authentication query and the current authorization state is authorizationStateWaitCode, authorizationStateWaitRegistration, or authorizationStateWaitPassword
//@other_user_ids List of user identifiers of other users currently using the client
//@other_user_ids List of user identifiers of other users currently using the application
requestQrCodeAuthentication other_user_ids:vector<int32> = Ok;
//@description Finishes user registration. Works only when the current authorization state is authorizationStateWaitRegistration
@ -3353,7 +3463,7 @@ getMessages chat_id:int53 message_ids:vector<int53> = Messages;
getFile file_id:int32 = File;
//@description Returns information about a file by its remote ID; this is an offline request. Can be used to register a URL as a file for further uploading, or sending as a message. Even the request succeeds, the file can be used only if it is still accessible to the user.
//-For example, if the file is from a message, then the message must be not deleted and accessible to the user. If the file database is disabled, then the corresponding object with the file must be preloaded by the client
//-For example, if the file is from a message, then the message must be not deleted and accessible to the user. If the file database is disabled, then the corresponding object with the file must be preloaded by the application
//@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;
@ -3463,7 +3573,7 @@ searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages
//@description Returns information about the recent locations of chat members that were sent to the chat. Returns up to 1 location message per user @chat_id Chat identifier @limit The maximum number of messages to be returned
searchChatRecentLocationMessages chat_id:int53 limit:int32 = Messages;
//@description Returns all active live locations that should be updated by the client. The list is persistent across application restarts only if the message database is used
//@description Returns all active live locations that should be updated by the application. The list is persistent across application restarts only if the message database is used
getActiveLiveLocationMessages = Messages;
//@description Returns the last message sent in a chat no later than the specified date @chat_id Chat identifier @date Point in time (Unix timestamp) relative to which to search for messages
@ -3641,6 +3751,10 @@ getPollVoters chat_id:int53 message_id:int53 option_id:int32 offset:int32 limit:
stopPoll chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Ok;
//@description Hides a suggested action @action Suggested action to hide
hideSuggestedAction action:SuggestedAction = 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;
@ -3772,13 +3886,13 @@ getRecommendedChatFilters = RecommendedChatFilters;
getChatFilterDefaultIconName filter:chatFilter = Text;
//@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
//@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info rights
//@chat_id Chat identifier @title New title of the chat; 1-128 characters
setChatTitle chat_id:int53 title:string = Ok;
//@description Changes the photo of a chat. Supported only for basic groups, supergroups and channels. Requires can_change_info rights. The photo will not be changed before request to the server has been completed
//@chat_id Chat identifier @photo New chat photo. You can use a zero InputFileId to delete the chat photo. Files that are accessible only by HTTP URL are not acceptable
setChatPhoto chat_id:int53 photo:InputFile = Ok;
//@description Changes the photo of a chat. Supported only for basic groups, supergroups and channels. Requires can_change_info rights
//@chat_id Chat identifier @photo New chat photo. You can pass null to delete the chat photo
setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok;
//@description Changes the chat members permissions. Supported only for basic groups and supergroups. Requires can_restrict_members administrator right
//@chat_id Chat identifier @permissions New non-administrator members permissions in the chat
@ -3797,7 +3911,7 @@ toggleChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Ok;
//@description Changes the value of the default disable_notification parameter, used when a message is sent to a chat @chat_id Chat identifier @default_disable_notification New value of default_disable_notification
toggleChatDefaultDisableNotification chat_id:int53 default_disable_notification:Bool = Ok;
//@description Changes client data associated with a chat @chat_id Chat identifier @client_data New value of client_data
//@description Changes application-specific data associated with a chat @chat_id Chat identifier @client_data New value of client_data
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
@ -3902,7 +4016,7 @@ uploadFile file:InputFile file_type:FileType priority:int32 = File;
//@description Stops the uploading of a file. Supported only for files uploaded by using uploadFile. For other files the behavior is undefined @file_id Identifier of the file to stop uploading
cancelUploadFile file_id:int32 = Ok;
//@description Writes a part of a generated file. This method is intended to be used only if the client has no direct access to TDLib's file system, because it is usually slower than a direct write to the destination file
//@description Writes a part of a generated file. This method is intended to be used only if the application has no direct access to TDLib's file system, because it is usually slower than a direct write to the destination file
//@generation_id The identifier of the generation process @offset The offset from which to write the data to the file @data The data to write
writeGeneratedFilePart generation_id:int64 offset:int32 data:bytes = Ok;
@ -3917,7 +4031,7 @@ setFileGenerationProgress generation_id:int64 expected_size:int32 local_prefix_s
//@error If set, means that file generation has failed and should be terminated
finishFileGeneration generation_id:int64 error:error = Ok;
//@description Reads a part of a file from the TDLib file cache and returns read bytes. This method is intended to be used only if the client has no direct access to TDLib's file system, because it is usually slower than a direct read from the file
//@description Reads a part of a file from the TDLib file cache and returns read bytes. This method is intended to be used only if the application has no direct access to TDLib's file system, because it is usually slower than a direct read from the file
//@file_id Identifier of the file. The file must be located in the TDLib file cache
//@offset The offset from which to read the file
//@count Number of bytes to read. An error will be returned if there are not enough bytes available in the file from the specified position. Pass 0 to read all available data from the specified position
@ -3938,10 +4052,10 @@ checkChatInviteLink invite_link:string = ChatInviteLinkInfo;
joinChatByInviteLink invite_link:string = Chat;
//@description Creates a new call @user_id Identifier of the user to be called @protocol Description of the call protocols supported by the client
//@description Creates a new call @user_id Identifier of the user to be called @protocol Description of the call protocols supported by the application
createCall user_id:int32 protocol:callProtocol = CallId;
//@description Accepts an incoming call @call_id Call identifier @protocol Description of the call protocols supported by the client
//@description Accepts an incoming call @call_id Call identifier @protocol Description of the call protocols supported by the application
acceptCall call_id:int32 protocol:callProtocol = Ok;
//@description Discards a call @call_id Call identifier @is_disconnected True, if the user was disconnected @duration The call duration, in seconds @connection_id Identifier of the connection used during the call
@ -3955,10 +4069,10 @@ sendCallDebugInformation call_id:int32 debug_information:string = Ok;
//@description Adds a user to the blacklist @user_id User identifier
//@description Blocks a user @user_id User identifier
blockUser user_id:int32 = Ok;
//@description Removes a user from the blacklist @user_id User identifier
//@description Unblocks a user @user_id User identifier
unblockUser user_id:int32 = Ok;
//@description Returns users that were blocked by the current user @offset Number of users to skip in the result; must be non-negative @limit The maximum number of users to return; up to 100
@ -3997,7 +4111,7 @@ 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 The maximum number of photos to be returned; up to 100
getUserProfilePhotos user_id:int32 offset:int32 limit:int32 = UserProfilePhotos;
getUserProfilePhotos user_id:int32 offset:int32 limit:int32 = ChatPhotos;
//@description Returns stickers from the installed sticker sets that correspond to a given emoji. If the emoji is not empty, favorite and recently used stickers may also be returned @emoji String representation of emoji. If empty, returns all known installed stickers @limit The maximum number of stickers to be returned
@ -4103,19 +4217,19 @@ getWebPagePreview text:formattedText = WebPage;
getWebPageInstantView url:string force_full:Bool = WebPageInstantView;
//@description Uploads a new profile photo for the current user. If something changes, updateUser will be sent @photo Profile photo to set. inputFileId and inputFileRemote may still be unsupported
setProfilePhoto photo:InputFile = Ok;
//@description Changes a profile photo for the current user @photo Profile photo to set
setProfilePhoto photo:InputChatPhoto = Ok;
//@description Deletes a profile photo. If something changes, updateUser will be sent @profile_photo_id Identifier of the profile photo to delete
//@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete
deleteProfilePhoto profile_photo_id:int64 = Ok;
//@description Changes the first and last name of the current user. If something changes, updateUser will be sent @first_name The new value of the first name for the user; 1-64 characters @last_name The new value of the optional last name for the user; 0-64 characters
//@description Changes the first and last name of the current user @first_name The new value of the first name for the user; 1-64 characters @last_name The new value of the optional last name for the user; 0-64 characters
setName first_name:string last_name:string = Ok;
//@description Changes the bio of the current user @bio The new value of the user bio; 0-70 characters without line feeds
setBio bio:string = Ok;
//@description Changes the username of the current user. If something changes, updateUser will be sent @username The new value of the username. Use an empty string to remove the username
//@description Changes the username of the current user @username The new value of the username. Use an empty string to remove the username
setUsername username:string = Ok;
//@description Changes the location of the current user. Needs to be called if GetOption("is_location_visible") is true and location changes for more than 1 kilometer @location The new location of the user
@ -4265,7 +4379,7 @@ setCustomLanguagePackString language_pack_id:string new_string:languagePackStrin
deleteLanguagePack language_pack_id:string = Ok;
//@description Registers the currently used device for receiving push notifications. Returns a globally unique identifier of the push notification subscription @device_token Device token @other_user_ids List of user identifiers of other users currently using the client
//@description Registers the currently used device for receiving push notifications. Returns a globally unique identifier of the push notification subscription @device_token Device token @other_user_ids List of user identifiers of other users currently using the application
registerDevice device_token:DeviceToken other_user_ids:vector<int32> = PushReceiverId;
//@description Handles a push notification. Returns error with code 406 if the push notification is not supported and connection to the server is required to fetch new data. Can be called before authorization
@ -4316,7 +4430,7 @@ reportChat chat_id:int53 reason:ChatReportReason message_ids:vector<int53> = Ok;
//@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics is disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned
getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl;
//@description Returns detailed statistics about a chat. Currently this method can be used only for channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app
//@description Returns detailed statistics about a chat. Currently this method can be used only for supergroups and channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app
getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics;
//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifier @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise

Binary file not shown.

View File

@ -49,7 +49,7 @@ inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
@ -61,7 +61,7 @@ inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> s
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
inputChatUploadedPhoto#c642724e flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = InputChatPhoto;
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
@ -99,7 +99,7 @@ userEmpty#200250ba id:int = 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<RestrictionReason> 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;
userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
userStatusEmpty#9d05049 = UserStatus;
userStatusOnline#edb93949 expires:int = UserStatus;
@ -125,7 +125,7 @@ chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?
chatParticipants#3f460fed chat_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants;
chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
messageEmpty#83e5de54 id:int = 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<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
@ -173,7 +173,7 @@ dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer t
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<PhotoSize> dc_id:int = Photo;
photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
@ -199,7 +199,7 @@ 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 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;
peerSettings#733f2961 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 autoarchived:flags.7?true geo_distance:flags.6?int = 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;
@ -344,6 +344,7 @@ updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
updateDialogFilters#3504914f = Update;
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -381,7 +382,7 @@ help.inviteText#18cb9f78 message:string = help.InviteText;
encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat;
encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat;
encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat;
encryptedChatRequested#62718a82 flags:# folder_id:flags.0?int id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat;
encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat;
encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat;
@ -515,6 +516,7 @@ chatInviteExported#fc2e05bc link:string = ExportedChatInvite;
chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector<User> = ChatInvite;
chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
@ -1122,7 +1124,17 @@ stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueA
help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
videoSize#435bb987 type:string location:FileLocation w:int h:int size:int = VideoSize;
videoSize#e831c556 flags:# type:string location:FileLocation w:int h:int size:int video_start_ts:flags.0?double = VideoSize;
statsGroupTopPoster#18f3d0f7 user_id:int messages:int avg_chars:int = StatsGroupTopPoster;
statsGroupTopAdmin#6014f412 user_id:int deleted:int kicked:int banned:int = StatsGroupTopAdmin;
statsGroupTopInviter#31962a4c user_id:int invitations:int = StatsGroupTopInviter;
stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats;
globalPrivacySettings#bea2f424 flags:# archive_and_mute_new_noncontact_peers:flags.0?Bool = GlobalPrivacySettings;
---functions---
@ -1218,6 +1230,8 @@ 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<InputWallPaper> = Vector<WallPaper>;
account.getGlobalPrivacySettings#eb2b4cf6 = GlobalPrivacySettings;
account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = GlobalPrivacySettings;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@ -1371,8 +1385,8 @@ updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto;
photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo;
photos.updateProfilePhoto#72d4742c id:InputPhoto = photos.Photo;
photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
@ -1405,6 +1419,7 @@ help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
help.getPromoData#c0977421 = help.PromoData;
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
help.dismissSuggestion#77fa99f suggestion:string = Bool;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@ -1481,3 +1496,4 @@ folders.deleteFolder#1c295881 folder_id:int = Updates;
stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;

Binary file not shown.

View File

@ -164,7 +164,7 @@ void gen_tl_constructor_from_string(StringBuilder &sb, const tl::simple::Schema
}
Vec vec;
for (auto *constructor : custom_type->constructors) {
vec.push_back(std::make_pair(constructor->id, constructor->name));
vec.emplace_back(constructor->id, constructor->name);
vec_for_nullary.push_back(vec.back());
}
@ -179,7 +179,7 @@ void gen_tl_constructor_from_string(StringBuilder &sb, const tl::simple::Schema
}
Vec vec_for_function;
for (auto *function : schema.functions) {
vec_for_function.push_back(std::make_pair(function->id, function->name));
vec_for_function.emplace_back(function->id, function->name);
}
gen_tl_constructor_from_string(sb, "Function", vec_for_function, is_header);
}

View File

@ -158,7 +158,7 @@ std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t) co
for (auto &c : name) {
c = to_upper(c);
}
flags.push_back(std::make_pair(name, a.exist_var_bit));
flags.emplace_back(name, a.exist_var_bit);
}
}
std::string res;

View File

@ -35,7 +35,7 @@ void RawConnection::send_crypto(const Storer &storer, int64 session_id, int64 sa
bool use_quick_ack = false;
if (quick_ack_token != 0 && transport_->support_quick_ack()) {
auto tmp = quick_ack_to_token_.insert(std::make_pair(info.message_ack, quick_ack_token));
auto tmp = quick_ack_to_token_.emplace(info.message_ack, quick_ack_token);
if (tmp.second) {
use_quick_ack = true;
} else {

View File

@ -130,6 +130,7 @@ class RawConnection {
if (has_error_) {
return Status::Error("Connection has already failed");
}
sync_with_poll(socket_fd_);
// read/write
// EINVAL may be returned in linux kernel < 2.6.28. And on some new kernels too.
@ -139,7 +140,7 @@ class RawConnection {
TRY_STATUS(flush_read(auth_key, callback));
TRY_STATUS(callback.before_write());
TRY_STATUS(flush_write());
if (can_close(socket_fd_)) {
if (can_close_local(socket_fd_)) {
return Status::Error("Connection closed");
}
return Status::OK();

View File

@ -14,6 +14,7 @@
#include "td/utils/format.h"
#include "td/utils/Named.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StorerBase.h"

View File

@ -125,10 +125,10 @@ class TlsHello {
Op::zero(32),
Op::string("\x20"),
Op::random(32),
Op::string("\x00\x22"),
Op::string("\x00\x20"),
Op::grease(0),
Op::string("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c"
"\x00\x9d\x00\x2f\x00\x35\x00\x0a\x01\x00\x01\x91"),
"\x00\x9d\x00\x2f\x00\x35\x01\x00\x01\x93"),
Op::grease(2),
Op::string("\x00\x00\x00\x00"),
Op::begin_scope(),
@ -143,8 +143,8 @@ class TlsHello {
Op::grease(4),
Op::string(
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08"
"\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x14\x00\x12\x04\x03\x08"
"\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29"),
"\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08"
"\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29"),
Op::grease(4),
Op::string("\x00\x01\x00\x00\x1d\x00\x20"),
Op::key(),

View File

@ -12,30 +12,28 @@
namespace td {
namespace mtproto {
void TlsReaderByteFlow::loop() {
while (true) {
if (input_->size() < 5) {
set_need_size(5);
return;
}
auto it = input_->clone();
uint8 buf[5];
it.advance(5, MutableSlice(buf, 5));
if (Slice(buf, 3) != Slice("\x17\x03\x03")) {
close_input(Status::Error("Invalid bytes at the beginning of a packet (emulated tls)"));
return;
}
size_t len = (buf[3] << 8) | buf[4];
if (it.size() < len) {
set_need_size(5 + len);
return;
}
output_.append(it.cut_head(len));
*input_ = std::move(it);
on_output_updated();
bool TlsReaderByteFlow::loop() {
if (input_->size() < 5) {
set_need_size(5);
return false;
}
auto it = input_->clone();
uint8 buf[5];
it.advance(5, MutableSlice(buf, 5));
if (Slice(buf, 3) != Slice("\x17\x03\x03")) {
close_input(Status::Error("Invalid bytes at the beginning of a packet (emulated tls)"));
return false;
}
size_t len = (buf[3] << 8) | buf[4];
if (it.size() < len) {
set_need_size(5 + len);
return false;
}
output_.append(it.cut_head(len));
*input_ = std::move(it);
return true;
}
} // namespace mtproto

View File

@ -13,7 +13,7 @@ namespace mtproto {
class TlsReaderByteFlow final : public ByteFlowBase {
public:
void loop() override;
bool loop() override;
};
} // namespace mtproto

View File

@ -276,7 +276,7 @@ void AnimationsManager::delete_animation_thumbnail(FileId file_id) {
return;
}
animation->thumbnail = PhotoSize();
animation->animated_thumbnail = PhotoSize();
animation->animated_thumbnail = AnimationSize();
}
FileId AnimationsManager::dup_animation(FileId new_id, FileId old_id) {
@ -333,7 +333,7 @@ bool AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_
}
void AnimationsManager::create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail,
PhotoSize animated_thumbnail, bool has_stickers,
AnimationSize animated_thumbnail, bool has_stickers,
vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
int32 duration, Dimensions dimensions, bool replace) {
auto a = make_unique<Animation>();
@ -396,8 +396,8 @@ tl_object_ptr<telegram_api::InputMedia> AnimationsManager::get_input_media(
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, std::move(attributes),
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());
}

View File

@ -37,7 +37,7 @@ class AnimationsManager : public Actor {
tl_object_ptr<td_api::animation> get_animation_object(FileId file_id, const char *source);
void create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail, PhotoSize animated_thumbnail,
void create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail, AnimationSize animated_thumbnail,
bool has_stickers, vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
int32 duration, Dimensions dimensions, bool replace);
@ -106,7 +106,7 @@ class AnimationsManager : public Actor {
Dimensions dimensions;
string minithumbnail;
PhotoSize thumbnail;
PhotoSize animated_thumbnail;
AnimationSize animated_thumbnail;
bool has_stickers = false;
vector<FileId> sticker_file_ids;

View File

@ -267,8 +267,8 @@ tl_object_ptr<telegram_api::InputMedia> AudiosManager::get_input_media(
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, std::move(attributes),
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());
}

View File

@ -84,8 +84,12 @@ 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);
return is_bot_ && was_authorized();
}
bool AuthManager::was_authorized() const {
return state_ == State::Ok || state_ == State::LoggingOut || state_ == State::DestroyingKeys ||
state_ == State::Closing;
}
bool AuthManager::is_authorized() const {
@ -343,7 +347,7 @@ void AuthManager::recover_password(uint64 query_id, string code) {
G()->net_query_creator().create_unauth(telegram_api::auth_recoverPassword(code)));
}
void AuthManager::logout(uint64 query_id) {
void AuthManager::log_out(uint64 query_id) {
if (state_ == State::Closing) {
return on_query_error(query_id, Status::Error(8, "Already logged out"));
}
@ -625,7 +629,7 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
status = std::move(result->error());
}
LOG_IF(ERROR, status.is_error()) << "Receive error for auth.logOut: " << status;
// state_ will stay logout, so no queries will work.
// state_ will stay LoggingOut, so no queries will work.
destroy_auth_keys();
if (query_id_ != 0) {
on_query_ok();
@ -714,7 +718,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
if (query_id_ != 0) {
on_query_error(Status::Error(500, "Server doesn't send proper authorization"));
}
logout(0);
log_out(0);
return;
}
if ((auth->flags_ & telegram_api::auth_authorization::TMP_SESSIONS_MASK) != 0) {
@ -726,9 +730,9 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up);
td->updates_manager_->get_difference("on_get_authorization");
td->on_online_updated(false, true);
td->schedule_get_terms_of_service(0);
td->schedule_get_promo_data(0);
if (!is_bot()) {
td->schedule_get_terms_of_service(0);
td->schedule_get_promo_data(0);
G()->td_db()->get_binlog_pmc()->set("fetched_marks_as_unread", "1");
}
send_closure(G()->config_manager(), &ConfigManager::request_config);

View File

@ -30,6 +30,7 @@ class AuthManager : public NetActor {
bool is_bot() const;
bool is_authorized() const;
bool was_authorized() const;
void get_state(uint64 query_id);
void set_phone_number(uint64 query_id, string phone_number,
@ -42,7 +43,7 @@ class AuthManager : public NetActor {
void check_password(uint64 query_id, string password);
void request_password_recovery(uint64 query_id);
void recover_password(uint64 query_id, string code);
void logout(uint64 query_id);
void log_out(uint64 query_id);
void delete_account(uint64 query_id, const string &reason);
void on_update_login_token();

View File

@ -28,6 +28,7 @@
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include <tuple>
@ -35,17 +36,15 @@
namespace td {
CallProtocol CallProtocol::from_telegram_api(const telegram_api::phoneCallProtocol &protocol) {
CallProtocol res;
res.udp_p2p = protocol.udp_p2p_;
res.udp_reflector = protocol.udp_reflector_;
res.min_layer = protocol.min_layer_;
res.max_layer = protocol.max_layer_;
res.library_versions = protocol.library_versions_;
return res;
CallProtocol::CallProtocol(const telegram_api::phoneCallProtocol &protocol)
: udp_p2p(protocol.udp_p2p_)
, udp_reflector(protocol.udp_reflector_)
, min_layer(protocol.min_layer_)
, max_layer(protocol.max_layer_)
, library_versions(protocol.library_versions_) {
}
tl_object_ptr<telegram_api::phoneCallProtocol> CallProtocol::as_telegram_api() const {
tl_object_ptr<telegram_api::phoneCallProtocol> CallProtocol::get_input_phone_call_protocol() const {
int32 flags = 0;
if (udp_p2p) {
flags |= telegram_api::phoneCallProtocol::UDP_P2P_MASK;
@ -57,51 +56,45 @@ tl_object_ptr<telegram_api::phoneCallProtocol> CallProtocol::as_telegram_api() c
vector<string>(library_versions));
}
CallProtocol CallProtocol::from_td_api(const td_api::callProtocol &protocol) {
CallProtocol res;
res.udp_p2p = protocol.udp_p2p_;
res.udp_reflector = protocol.udp_reflector_;
res.min_layer = protocol.min_layer_;
res.max_layer = protocol.max_layer_;
res.library_versions = protocol.library_versions_;
return res;
CallProtocol::CallProtocol(const td_api::callProtocol &protocol)
: udp_p2p(protocol.udp_p2p_)
, udp_reflector(protocol.udp_reflector_)
, min_layer(protocol.min_layer_)
, max_layer(protocol.max_layer_)
, library_versions(protocol.library_versions_) {
}
tl_object_ptr<td_api::callProtocol> CallProtocol::as_td_api() const {
CallConnection::CallConnection(const telegram_api::phoneConnection &connection)
: id(connection.id_)
, ip(connection.ip_)
, ipv6(connection.ipv6_)
, port(connection.port_)
, peer_tag(connection.peer_tag_.as_slice().str()) {
}
tl_object_ptr<td_api::callProtocol> CallProtocol::get_call_protocol_object() const {
return make_tl_object<td_api::callProtocol>(udp_p2p, udp_reflector, min_layer, max_layer,
vector<string>(library_versions));
}
CallConnection CallConnection::from_telegram_api(const telegram_api::phoneConnection &connection) {
CallConnection res;
res.id = connection.id_;
res.ip = connection.ip_;
res.ipv6 = connection.ipv6_;
res.port = connection.port_;
res.peer_tag = connection.peer_tag_.as_slice().str();
return res;
}
tl_object_ptr<telegram_api::phoneConnection> CallConnection::as_telegram_api() const {
tl_object_ptr<telegram_api::phoneConnection> CallConnection::get_input_phone_connection() const {
return make_tl_object<telegram_api::phoneConnection>(id, ip, ipv6, port, BufferSlice(peer_tag));
}
tl_object_ptr<td_api::callConnection> CallConnection::as_td_api() const {
tl_object_ptr<td_api::callConnection> CallConnection::get_call_connection_object() const {
return make_tl_object<td_api::callConnection>(id, ip, ipv6, port, peer_tag);
}
// CallState
tl_object_ptr<td_api::CallState> CallState::as_td_api() const {
tl_object_ptr<td_api::CallState> CallState::get_call_state_object() const {
switch (type) {
case Type::Pending:
return make_tl_object<td_api::callStatePending>(is_created, is_received);
case Type::ExchangingKey:
return make_tl_object<td_api::callStateExchangingKeys>();
case Type::Ready: {
std::vector<tl_object_ptr<td_api::callConnection>> v;
for (auto &c : connections) {
v.push_back(c.as_td_api());
}
return make_tl_object<td_api::callStateReady>(protocol.as_td_api(), std::move(v), config, key,
vector<string>(emojis_fingerprint), allow_p2p);
auto call_connections = transform(connections, [](auto &c) { return c.get_call_connection_object(); });
return make_tl_object<td_api::callStateReady>(protocol.get_call_protocol_object(), std::move(call_connections),
config, key, vector<string>(emojis_fingerprint), allow_p2p);
}
case Type::HangingUp:
return make_tl_object<td_api::callStateHangingUp>();
@ -428,9 +421,9 @@ Status CallActor::do_update_call(telegram_api::phoneCall &call) {
get_emojis_fingerprint(call_state_.key, is_outgoing_ ? dh_handshake_.get_g_b() : dh_handshake_.get_g_a());
for (auto &connection : call.connections_) {
call_state_.connections.push_back(CallConnection::from_telegram_api(*connection));
call_state_.connections.push_back(CallConnection(*connection));
}
call_state_.protocol = CallProtocol::from_telegram_api(*call.protocol_);
call_state_.protocol = CallProtocol(*call.protocol_);
call_state_.allow_p2p = (call.flags_ & telegram_api::phoneCall::P2P_ALLOWED_MASK) != 0;
call_state_.type = CallState::Type::Ready;
call_state_need_flush_ = true;
@ -575,7 +568,7 @@ void CallActor::try_send_request_query() {
}
auto tl_query = telegram_api::phone_requestCall(flags, false /*ignored*/, std::move(input_user_),
Random::secure_int32(), BufferSlice(dh_handshake_.get_g_b_hash()),
call_state_.protocol.as_telegram_api());
call_state_.protocol.get_input_phone_call_protocol());
auto query = G()->net_query_creator().create(tl_query);
state_ = State::WaitRequestResult;
int32 call_receive_timeout_ms = G()->shared_config().get_option_integer("call_receive_timeout_ms", 20000);
@ -608,9 +601,9 @@ void CallActor::try_send_accept_query() {
return;
}
dh_handshake_.set_config(dh_config_->g, dh_config_->prime);
auto tl_query =
telegram_api::phone_acceptCall(get_input_phone_call("try_send_accept_query"),
BufferSlice(dh_handshake_.get_g_b()), call_state_.protocol.as_telegram_api());
auto tl_query = telegram_api::phone_acceptCall(get_input_phone_call("try_send_accept_query"),
BufferSlice(dh_handshake_.get_g_b()),
call_state_.protocol.get_input_phone_call_protocol());
auto query = G()->net_query_creator().create(tl_query);
state_ = State::WaitAcceptResult;
send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) {
@ -634,7 +627,7 @@ void CallActor::try_send_confirm_query() {
}
auto tl_query = telegram_api::phone_confirmCall(get_input_phone_call("try_send_confirm_query"),
BufferSlice(dh_handshake_.get_g_b()), call_state_.key_fingerprint,
call_state_.protocol.as_telegram_api());
call_state_.protocol.get_input_phone_call_protocol());
auto query = G()->net_query_creator().create(tl_query);
state_ = State::WaitConfirmResult;
send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) {
@ -708,7 +701,7 @@ void CallActor::flush_call_state() {
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateCall>(
make_tl_object<td_api::call>(local_call_id_.get(), is_outgoing_ ? user_id_.get() : call_admin_id_,
is_outgoing_, call_state_.as_td_api())));
is_outgoing_, call_state_.get_call_state_object())));
}
}

View File

@ -34,10 +34,15 @@ struct CallProtocol {
int32 max_layer{65};
vector<string> library_versions;
static CallProtocol from_telegram_api(const telegram_api::phoneCallProtocol &protocol);
tl_object_ptr<telegram_api::phoneCallProtocol> as_telegram_api() const;
static CallProtocol from_td_api(const td_api::callProtocol &protocol);
tl_object_ptr<td_api::callProtocol> as_td_api() const;
CallProtocol() = default;
explicit CallProtocol(const td_api::callProtocol &protocol);
explicit CallProtocol(const telegram_api::phoneCallProtocol &protocol);
tl_object_ptr<telegram_api::phoneCallProtocol> get_input_phone_call_protocol() const;
tl_object_ptr<td_api::callProtocol> get_call_protocol_object() const;
};
struct CallConnection {
@ -47,9 +52,11 @@ struct CallConnection {
int32 port;
string peer_tag;
static CallConnection from_telegram_api(const telegram_api::phoneConnection &connection);
tl_object_ptr<telegram_api::phoneConnection> as_telegram_api() const;
tl_object_ptr<td_api::callConnection> as_td_api() const;
explicit CallConnection(const telegram_api::phoneConnection &connection);
tl_object_ptr<telegram_api::phoneConnection> get_input_phone_connection() const;
tl_object_ptr<td_api::callConnection> get_call_connection_object() const;
};
struct CallState {
@ -71,7 +78,7 @@ struct CallState {
Status error;
tl_object_ptr<td_api::CallState> as_td_api() const;
tl_object_ptr<td_api::CallState> get_call_state_object() const;
};
class CallActor : public NetQueryCallback {

View File

@ -34,8 +34,8 @@ class CallId {
return id;
}
auto as_td_api() const {
return make_tl_object<td_api::callId>(id);
auto get_call_id_object() const {
return td_api::make_object<td_api::callId>(id);
}
bool operator==(const CallId &other) const {

View File

@ -82,6 +82,8 @@ class GetBotCallbackAnswerQuery : public Td::ResultHandler {
void on_error(uint64 id, Status status) override {
if (status.message() == "DATA_INVALID") {
td->messages_manager_->get_message_from_server({dialog_id_, message_id_}, Auto());
} else if (status.message() == "BOT_RESPONSE_TIMEOUT") {
status = Status::Error(502, "The bot is not responding");
}
td->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetBotCallbackAnswerQuery");
td->callback_queries_manager_->on_get_callback_query_answer(result_id_, nullptr);

View File

@ -55,6 +55,7 @@
#include "td/utils/tl_parsers.h"
#include "td/utils/UInt.h"
#include <algorithm>
#include <functional>
#include <memory>
#include <unordered_map>
@ -264,11 +265,11 @@ static ActorOwn<> get_simple_config_dns(Slice address, Slice host, Promise<Simpl
TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false));
auto &answer_array = answer.get_array();
vector<string> parts;
for (auto &v : answer_array) {
if (v.type() != JsonValue::Type::Object) {
for (auto &answer_part : answer_array) {
if (answer_part.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object");
}
auto &data_object = v.get_object();
auto &data_object = answer_part.get_object();
TRY_RESULT(part, get_json_object_string_field(data_object, "data", false));
parts.push_back(std::move(part));
}
@ -544,6 +545,7 @@ ActorOwn<> get_full_config(DcOption option, Promise<FullConfig> promise, ActorSh
class ConfigRecoverer : public Actor {
public:
explicit ConfigRecoverer(ActorShared<> parent) : parent_(std::move(parent)) {
connecting_since_ = Time::now();
}
void on_dc_options_update(DcOptions dc_options) {
@ -862,10 +864,11 @@ class ConfigRecoverer : public Actor {
}
void update_dc_options() {
auto v = simple_config_.dc_options;
v.insert(v.begin(), dc_options_update_.dc_options.begin(), dc_options_update_.dc_options.end());
if (v != dc_options_.dc_options) {
dc_options_.dc_options = std::move(v);
auto new_dc_options = simple_config_.dc_options;
new_dc_options.insert(new_dc_options.begin(), dc_options_update_.dc_options.begin(),
dc_options_update_.dc_options.end());
if (new_dc_options != dc_options_.dc_options) {
dc_options_.dc_options = std::move(new_dc_options);
dc_options_i_ = 0;
dc_options_at_ = Time::now();
}
@ -873,6 +876,7 @@ class ConfigRecoverer : public Actor {
};
ConfigManager::ConfigManager(ActorShared<> parent) : parent_(std::move(parent)) {
lazy_request_flood_countrol_.add_limit(20, 1);
}
void ConfigManager::start_up() {
@ -881,7 +885,7 @@ void ConfigManager::start_up() {
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()) {
if (expire_time.is_in_past() || true) {
request_config();
} else {
expire_time_ = expire_time;
@ -921,9 +925,24 @@ void ConfigManager::request_config() {
if (config_sent_cnt_ != 0) {
return;
}
lazy_request_flood_countrol_.add_event(static_cast<int32>(Timestamp::now().at()));
request_config_from_dc_impl(DcId::main());
}
void ConfigManager::lazy_request_config() {
if (G()->close_flag()) {
return;
}
if (config_sent_cnt_ != 0) {
return;
}
expire_time_.relax(Timestamp::at(lazy_request_flood_countrol_.get_wakeup_at()));
set_timeout_at(expire_time_.at());
}
void ConfigManager::get_app_config(Promise<td_api::object_ptr<td_api::JsonValue>> &&promise) {
if (G()->close_flag()) {
return promise.set_error(Status::Error(500, "Request aborted"));
@ -979,6 +998,44 @@ void ConfigManager::set_content_settings(bool ignore_sensitive_content_restricti
}
}
void ConfigManager::get_global_privacy_settings(Promise<Unit> &&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_global_privacy_settings_queries_.push_back(std::move(promise));
if (get_global_privacy_settings_queries_.size() == 1) {
G()->net_query_dispatcher().dispatch_with_callback(
G()->net_query_creator().create(telegram_api::account_getGlobalPrivacySettings()), actor_shared(this, 5));
}
}
void ConfigManager::set_archive_and_mute(bool archive_and_mute, Promise<Unit> &&promise) {
if (G()->close_flag()) {
return promise.set_error(Status::Error(500, "Request aborted"));
}
if (archive_and_mute) {
do_dismiss_suggested_action(SuggestedAction::EnableArchiveAndMuteNewChats);
}
last_set_archive_and_mute_ = archive_and_mute;
auto &queries = set_archive_and_mute_queries_[archive_and_mute];
queries.push_back(std::move(promise));
if (!is_set_archive_and_mute_request_sent_) {
is_set_archive_and_mute_request_sent_ = true;
int32 flags = telegram_api::globalPrivacySettings::ARCHIVE_AND_MUTE_NEW_NONCONTACT_PEERS_MASK;
auto settings = make_tl_object<telegram_api::globalPrivacySettings>(flags, archive_and_mute);
G()->net_query_dispatcher().dispatch_with_callback(
G()->net_query_creator().create(telegram_api::account_setGlobalPrivacySettings(std::move(settings))),
actor_shared(this, 6 + static_cast<uint64>(archive_and_mute)));
}
}
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));
@ -997,7 +1054,7 @@ void ConfigManager::request_config_from_dc_impl(DcId dc_id) {
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 0));
}
void ConfigManager::set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions) {
void ConfigManager::do_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");
@ -1006,8 +1063,131 @@ void ConfigManager::set_ignore_sensitive_content_restrictions(bool ignore_sensit
}
}
void ConfigManager::do_set_archive_and_mute(bool archive_and_mute) {
if (archive_and_mute) {
do_dismiss_suggested_action(SuggestedAction::EnableArchiveAndMuteNewChats);
}
G()->shared_config().set_option_boolean("archive_and_mute_new_chats_from_unknown_users", archive_and_mute);
}
td_api::object_ptr<td_api::updateSuggestedActions> ConfigManager::get_update_suggested_actions(
const vector<SuggestedAction> &added_actions, const vector<SuggestedAction> &removed_actions) {
return td_api::make_object<td_api::updateSuggestedActions>(transform(added_actions, get_suggested_action_object),
transform(removed_actions, get_suggested_action_object));
}
void ConfigManager::dismiss_suggested_action(SuggestedAction suggested_action, Promise<Unit> &&promise) {
if (suggested_action == SuggestedAction::Empty) {
return promise.set_error(Status::Error(400, "Action must be non-empty"));
}
auto action_str = get_suggested_action_str(suggested_action);
if (action_str.empty()) {
return promise.set_value(Unit());
}
if (!td::contains(suggested_actions_, suggested_action)) {
return promise.set_value(Unit());
}
dismiss_suggested_action_request_count_++;
auto &queries = dismiss_suggested_action_queries_[suggested_action];
queries.push_back(std::move(promise));
if (queries.size() == 1) {
G()->net_query_dispatcher().dispatch_with_callback(
G()->net_query_creator().create(telegram_api::help_dismissSuggestion(action_str)),
actor_shared(this, 100 + static_cast<int32>(suggested_action)));
}
}
void ConfigManager::do_dismiss_suggested_action(SuggestedAction suggested_action) {
if (td::remove(suggested_actions_, suggested_action)) {
send_closure(G()->td(), &Td::send_update, get_update_suggested_actions({}, {suggested_action}));
}
}
void ConfigManager::on_result(NetQueryPtr res) {
auto token = get_link_token();
if (token >= 100 && token <= 200) {
SuggestedAction suggested_action = static_cast<SuggestedAction>(static_cast<int32>(token - 100));
auto promises = std::move(dismiss_suggested_action_queries_[suggested_action]);
dismiss_suggested_action_queries_.erase(suggested_action);
CHECK(!promises.empty());
CHECK(dismiss_suggested_action_request_count_ >= promises.size());
dismiss_suggested_action_request_count_ -= promises.size();
auto result_ptr = fetch_result<telegram_api::help_dismissSuggestion>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
return;
}
do_dismiss_suggested_action(suggested_action);
get_app_config(Auto());
for (auto &promise : promises) {
promise.set_value(Unit());
}
return;
}
if (token == 6 || token == 7) {
is_set_archive_and_mute_request_sent_ = false;
bool archive_and_mute = (token == 7);
auto promises = std::move(set_archive_and_mute_queries_[archive_and_mute]);
set_archive_and_mute_queries_[archive_and_mute].clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_setGlobalPrivacySettings>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
} else {
if (last_set_archive_and_mute_ == archive_and_mute) {
do_set_archive_and_mute(archive_and_mute);
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
}
if (!set_archive_and_mute_queries_[!archive_and_mute].empty()) {
if (archive_and_mute == last_set_archive_and_mute_) {
promises = std::move(set_archive_and_mute_queries_[!archive_and_mute]);
set_archive_and_mute_queries_[!archive_and_mute].clear();
for (auto &promise : promises) {
promise.set_value(Unit());
}
} else {
set_archive_and_mute(!archive_and_mute, Auto());
}
}
return;
}
if (token == 5) {
auto promises = std::move(get_global_privacy_settings_queries_);
get_global_privacy_settings_queries_.clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_getGlobalPrivacySettings>(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();
if ((result->flags_ & telegram_api::globalPrivacySettings::ARCHIVE_AND_MUTE_NEW_NONCONTACT_PEERS_MASK) != 0) {
do_set_archive_and_mute(result->archive_and_mute_new_noncontact_peers_);
} else {
LOG(ERROR) << "Receive wrong response: " << to_string(result);
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
return;
}
if (token == 3 || token == 4) {
is_set_content_settings_request_sent_ = false;
bool ignore_sensitive_content_restrictions = (token == 4);
@ -1022,7 +1202,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
} 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);
do_set_ignore_sensitive_content_restrictions(ignore_sensitive_content_restrictions);
}
for (auto &promise : promises) {
@ -1056,7 +1236,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
}
auto result = result_ptr.move_as_ok();
set_ignore_sensitive_content_restrictions(result->sensitive_enabled_);
do_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) {
@ -1136,8 +1316,7 @@ Timestamp ConfigManager::load_config_expire_time() {
}
void ConfigManager::save_config_expire(Timestamp timestamp) {
G()->td_db()->get_binlog_pmc()->set("config_expire",
to_string(static_cast<int>(Clocks::system() + expire_time_.in())));
G()->td_db()->get_binlog_pmc()->set("config_expire", to_string(static_cast<int>(Clocks::system() + timestamp.in())));
}
void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
@ -1279,6 +1458,9 @@ void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
!shared_config.have_option("ignore_sensitive_content_restrictions")) {
get_content_settings(Auto());
}
if (!shared_config.have_option("archive_and_mute_new_chats_from_unknown_users")) {
get_global_privacy_settings(Auto());
}
}
}
@ -1286,6 +1468,9 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
CHECK(config != nullptr);
LOG(INFO) << "Receive app config " << to_string(config);
const bool archive_and_mute =
G()->shared_config().get_option_boolean("archive_and_mute_new_chats_from_unknown_users");
vector<tl_object_ptr<telegram_api::jsonObjectValue>> new_values;
string ignored_restriction_reasons;
vector<string> dice_emojis;
@ -1293,6 +1478,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
std::unordered_map<string, string> dice_emoji_success_value;
string animation_search_provider;
string animation_search_emojis;
vector<SuggestedAction> suggested_actions;
if (config->get_id() == telegram_api::jsonObject::ID) {
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
Slice key = key_value->key_;
@ -1414,6 +1600,32 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
}
continue;
}
if (key == "pending_suggestions") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto actions = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &action : actions) {
CHECK(action != nullptr);
if (action->get_id() == telegram_api::jsonString::ID) {
Slice action_str = static_cast<telegram_api::jsonString *>(action.get())->value_;
auto suggested_action = get_suggested_action(action_str);
if (suggested_action != SuggestedAction::Empty) {
if (archive_and_mute && suggested_action == SuggestedAction::EnableArchiveAndMuteNewChats) {
LOG(INFO) << "Skip SuggestedAction::EnableArchiveAndMuteNewChats";
} else {
suggested_actions.push_back(suggested_action);
}
} else {
LOG(ERROR) << "Receive unsupported suggested action " << action_str;
}
} else {
LOG(ERROR) << "Receive unexpected suggested action " << to_string(action);
}
}
} else {
LOG(ERROR) << "Receive unexpected pending_suggestions " << to_string(*value);
}
continue;
}
new_values.push_back(std::move(key_value));
}
@ -1464,6 +1676,39 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
shared_config.set_option_empty("default_ton_blockchain_config");
shared_config.set_option_empty("default_ton_blockchain_name");
// do not update suggested actions while changing content settings or dismissing an action
if (!is_set_content_settings_request_sent_ && dismiss_suggested_action_request_count_ == 0) {
std::sort(suggested_actions.begin(), suggested_actions.end());
suggested_actions.erase(std::unique(suggested_actions.begin(), suggested_actions.end()), suggested_actions.end());
if (suggested_actions != suggested_actions_) {
vector<SuggestedAction> added_actions;
vector<SuggestedAction> removed_actions;
auto old_it = suggested_actions_.begin();
auto new_it = suggested_actions.begin();
while (old_it != suggested_actions_.end() || new_it != suggested_actions.end()) {
if (old_it != suggested_actions_.end() &&
(new_it == suggested_actions.end() || std::less<SuggestedAction>()(*old_it, *new_it))) {
removed_actions.push_back(*old_it++);
} else if (old_it == suggested_actions_.end() || std::less<SuggestedAction>()(*new_it, *old_it)) {
added_actions.push_back(*new_it++);
} else {
old_it++;
new_it++;
}
}
CHECK(!added_actions.empty() || !removed_actions.empty());
suggested_actions_ = std::move(suggested_actions);
send_closure(G()->td(), &Td::send_update,
get_update_suggested_actions(std::move(added_actions), std::move(removed_actions)));
}
}
}
void ConfigManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
if (!suggested_actions_.empty()) {
updates.push_back(get_update_suggested_actions(suggested_actions_, {}));
}
}
} // namespace td

View File

@ -9,6 +9,7 @@
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/DcOptions.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/SuggestedAction.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -17,12 +18,15 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FloodControlStrict.h"
#include "td/utils/logging.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/Time.h"
#include <map>
namespace td {
extern int VERBOSITY_NAME(config_recoverer);
@ -84,14 +88,24 @@ class ConfigManager : public NetQueryCallback {
void request_config();
void lazy_request_config();
void get_app_config(Promise<td_api::object_ptr<td_api::JsonValue>> &&promise);
void get_content_settings(Promise<Unit> &&promise);
void set_content_settings(bool ignore_sensitive_content_restrictions, Promise<Unit> &&promise);
void get_global_privacy_settings(Promise<Unit> &&promise);
void set_archive_and_mute(bool archive_and_mute, Promise<Unit> &&promise);
void dismiss_suggested_action(SuggestedAction suggested_action, Promise<Unit> &&promise);
void on_dc_options_update(DcOptions dc_options);
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
private:
ActorShared<> parent_;
int32 config_sent_cnt_{0};
@ -99,12 +113,24 @@ class ConfigManager : public NetQueryCallback {
int ref_cnt_{1};
Timestamp expire_time_;
FloodControlStrict lazy_request_flood_countrol_;
vector<Promise<td_api::object_ptr<td_api::JsonValue>>> get_app_config_queries_;
vector<Promise<Unit>> get_content_settings_queries_;
vector<Promise<Unit>> set_content_settings_queries_[2];
bool is_set_content_settings_request_sent_ = false;
bool last_set_content_settings_ = false;
vector<Promise<Unit>> get_global_privacy_settings_queries_;
vector<Promise<Unit>> set_archive_and_mute_queries_[2];
bool is_set_archive_and_mute_request_sent_ = false;
bool last_set_archive_and_mute_ = false;
vector<SuggestedAction> suggested_actions_;
size_t dismiss_suggested_action_request_count_ = 0;
std::map<SuggestedAction, vector<Promise<Unit>>> dismiss_suggested_action_queries_;
void start_up() override;
void hangup_shared() override;
void hangup() override;
@ -115,13 +141,22 @@ class ConfigManager : public NetQueryCallback {
void request_config_from_dc_impl(DcId dc_id);
void process_config(tl_object_ptr<telegram_api::config> config);
void process_app_config(tl_object_ptr<telegram_api::JSONValue> &config);
void set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions);
Timestamp load_config_expire_time();
void save_config_expire(Timestamp timestamp);
void save_dc_options_update(DcOptions dc_options);
DcOptions load_dc_options_update();
void process_app_config(tl_object_ptr<telegram_api::JSONValue> &config);
void do_set_ignore_sensitive_content_restrictions(bool ignore_sensitive_content_restrictions);
void do_set_archive_and_mute(bool archive_and_mute);
static td_api::object_ptr<td_api::updateSuggestedActions> get_update_suggested_actions(
const vector<SuggestedAction> &added_actions, const vector<SuggestedAction> &removed_actions);
void do_dismiss_suggested_action(SuggestedAction suggested_action);
static Timestamp load_config_expire_time();
static void save_config_expire(Timestamp timestamp);
static void save_dc_options_update(DcOptions dc_options);
static DcOptions load_dc_options_update();
};
} // namespace td

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
#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/Location.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/net/DcId.h"
@ -132,6 +133,7 @@ class ContactsManager : public Actor {
bool get_secret_chat_is_outbound(SecretChatId secret_chat_id) const;
SecretChatState get_secret_chat_state(SecretChatId secret_chat_id) const;
int32 get_secret_chat_layer(SecretChatId secret_chat_id) const;
FolderId get_secret_chat_initial_folder_id(SecretChatId secret_chat_id) const;
void on_imported_contacts(int64 random_id, vector<UserId> imported_contact_user_ids,
vector<int32> unimported_contact_invites);
@ -177,6 +179,7 @@ class ContactsManager : public Actor {
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_change_profile_photo(tl_object_ptr<telegram_api::photos_photo> &&photo, int64 old_photo_id);
void on_delete_profile_photo(int64 profile_photo_id, Promise<Unit> promise);
void on_ignored_restriction_reasons_changed();
@ -229,7 +232,8 @@ class ContactsManager : public Actor {
tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link_ptr);
void on_get_dialog_invite_link_info(const string &invite_link,
tl_object_ptr<telegram_api::ChatInvite> &&chat_invite_ptr);
tl_object_ptr<telegram_api::ChatInvite> &&chat_invite_ptr,
Promise<Unit> &&promise);
void invalidate_invite_link_info(const string &invite_link);
@ -260,6 +264,8 @@ class ContactsManager : public Actor {
void on_update_phone_number_privacy();
void invalidate_user_full(UserId user_id);
void on_channel_unban_timeout(ChannelId channel_id);
void check_dialog_username(DialogId dialog_id, const string &username, Promise<CheckDialogUsernameResult> &&promise);
@ -323,12 +329,14 @@ class ContactsManager : public Actor {
void set_location_visibility();
void set_profile_photo(const tl_object_ptr<td_api::InputFile> &input_photo, Promise<Unit> &&promise);
FileId get_profile_photo_file_id(int64 photo_id) const;
void set_profile_photo(const td_api::object_ptr<td_api::InputChatPhoto> &input_photo, Promise<Unit> &&promise);
void send_update_profile_photo_query(FileId file_id, int64 old_photo_id, Promise<Unit> &&promise);
void delete_profile_photo(int64 profile_photo_id, Promise<Unit> &&promise);
void upload_profile_photo(FileId file_id, Promise<Unit> &&promise);
void set_name(const string &first_name, const string &last_name, Promise<Unit> &&promise);
void set_bio(const string &bio, Promise<Unit> &&promise);
@ -362,7 +370,7 @@ class ContactsManager : public Actor {
void delete_channel(ChannelId channel_id, Promise<Unit> &&promise);
void get_channel_statistics(DialogId dialog_id, bool is_dark,
Promise<td_api::object_ptr<td_api::chatStatistics>> &&promise);
Promise<td_api::object_ptr<td_api::ChatStatistics>> &&promise);
void load_statistics_graph(DialogId dialog_id, const string &token, int64 x,
Promise<td_api::object_ptr<td_api::StatisticsGraph>> &&promise);
@ -447,12 +455,13 @@ class ContactsManager : public Actor {
bool get_chat(ChatId chat_id, int left_tries, Promise<Unit> &&promise);
void reload_chat(ChatId chat_id, Promise<Unit> &&promise);
bool get_chat_full(ChatId chat_id, bool force, Promise<Unit> &&promise);
FileSourceId get_chat_full_file_source_id(ChatId chat_id);
void reload_chat_full(ChatId chat_id, Promise<Unit> &&promise);
bool get_chat_is_active(ChatId chat_id) const;
DialogParticipantStatus get_chat_status(ChatId chat_id) const;
DialogParticipantStatus get_chat_permissions(ChatId chat_id) const;
bool is_appointed_chat_administrator(ChatId chat_id) const;
FileSourceId get_chat_photo_file_source_id(ChatId chat_id);
bool have_channel(ChannelId channel_id) const;
bool have_min_channel(ChannelId channel_id) const;
@ -460,6 +469,8 @@ class ContactsManager : public Actor {
bool get_channel(ChannelId channel_id, int left_tries, Promise<Unit> &&promise);
void reload_channel(ChannelId chnanel_id, Promise<Unit> &&promise);
bool get_channel_full(ChannelId channel_id, bool force, Promise<Unit> &&promise);
FileSourceId get_channel_full_file_source_id(ChannelId channel_id);
void reload_channel_full(ChannelId channel_id, Promise<Unit> &&promise, const char *source);
bool is_channel_public(ChannelId channel_id) const;
@ -474,7 +485,6 @@ class ContactsManager : public Actor {
DialogParticipantStatus get_channel_permissions(ChannelId channel_id) const;
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<int32, vector<UserId>> search_among_users(const vector<UserId> &user_ids, const string &query, int32 limit);
@ -528,10 +538,8 @@ class ContactsManager : public Actor {
tl_object_ptr<td_api::secretChat> get_secret_chat_object(SecretChatId secret_chat_id);
void on_update_secret_chat(SecretChatId secret_chat_id, int64 access_hash, UserId user_id, SecretChatState state,
bool is_outbound, int32 ttl, int32 date, string key_hash, int32 layer);
void on_upload_profile_photo(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file);
void on_upload_profile_photo_error(FileId file_id, Status status);
bool is_outbound, int32 ttl, int32 date, string key_hash, int32 layer,
FolderId initial_folder_id);
tl_object_ptr<td_api::chatMember> get_chat_member_object(const DialogParticipant &dialog_participant) const;
@ -545,6 +553,9 @@ class ContactsManager : public Actor {
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
static tl_object_ptr<td_api::dateRange> convert_date_range(
const tl_object_ptr<telegram_api::statsDateRangeDays> &obj);
static tl_object_ptr<td_api::StatisticsGraph> convert_stats_graph(tl_object_ptr<telegram_api::StatsGraph> obj);
static double get_percentage_value(double new_value, double old_value);
@ -552,7 +563,9 @@ class ContactsManager : public Actor {
static tl_object_ptr<td_api::statisticsValue> convert_stats_absolute_value(
const tl_object_ptr<telegram_api::statsAbsValueAndPrev> &obj);
static tl_object_ptr<td_api::chatStatistics> convert_broadcast_stats(
tl_object_ptr<td_api::ChatStatistics> convert_megagroup_stats(tl_object_ptr<telegram_api::stats_megagroupStats> obj);
static tl_object_ptr<td_api::ChatStatistics> convert_broadcast_stats(
tl_object_ptr<telegram_api::stats_broadcastStats> obj);
private:
@ -578,7 +591,7 @@ class ContactsManager : public Actor {
std::unordered_map<DialogId, int32, DialogIdHash> online_member_dialogs; // id -> time
static constexpr uint32 CACHE_VERSION = 1;
static constexpr uint32 CACHE_VERSION = 2;
uint32 cache_version = 0;
bool is_min_access_hash = true;
@ -609,6 +622,7 @@ class ContactsManager : public Actor {
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
bool is_update_user_sent = false;
bool is_saved = false; // is current user version being saved/is saved to the database
bool is_being_saved = false; // is current user being saved to the database
@ -647,6 +661,8 @@ class ContactsManager : public Actor {
// do not forget to update drop_user_full and on_get_user_full
struct UserFull {
Photo photo;
string about;
int32 common_chat_count = 0;
@ -677,7 +693,6 @@ class ContactsManager : public Actor {
struct Chat {
string title;
DialogPhoto photo;
FileSourceId photo_source_id;
int32 participant_count = 0;
int32 date = 0;
int32 version = -1;
@ -688,7 +703,7 @@ class ContactsManager : public Actor {
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false};
static constexpr uint32 CACHE_VERSION = 1;
static constexpr uint32 CACHE_VERSION = 2;
uint32 cache_version = 0;
bool is_active = false;
@ -699,6 +714,7 @@ class ContactsManager : public Actor {
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_update_basic_group_sent = false;
bool is_repaired = false; // whether cached value is rechecked
@ -716,11 +732,16 @@ class ContactsManager : public Actor {
void parse(ParserT &parser);
};
// do not forget to update drop_chat_full and on_get_chat_full
struct ChatFull {
int32 version = -1;
UserId creator_user_id;
vector<DialogParticipant> participants;
Photo photo;
vector<FileId> registered_photo_file_ids;
FileSourceId file_source_id;
string description;
string invite_link;
@ -742,7 +763,6 @@ class ContactsManager : public Actor {
int64 access_hash = 0;
string title;
DialogPhoto photo;
FileSourceId photo_source_id;
string username;
vector<RestrictionReason> restriction_reasons;
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
@ -750,7 +770,7 @@ class ContactsManager : public Actor {
int32 date = 0;
int32 participant_count = 0;
static constexpr uint32 CACHE_VERSION = 4;
static constexpr uint32 CACHE_VERSION = 5;
uint32 cache_version = 0;
bool has_linked_channel = false;
@ -771,6 +791,7 @@ class ContactsManager : public Actor {
bool was_member = false;
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_update_supergroup_sent = false;
bool is_repaired = false; // whether cached value is rechecked
@ -788,7 +809,12 @@ class ContactsManager : public Actor {
void parse(ParserT &parser);
};
// do not forget to update invalidate_channel_full and on_get_chat_full
struct ChannelFull {
Photo photo;
vector<FileId> registered_photo_file_ids;
FileSourceId file_source_id;
string description;
int32 participant_count = 0;
int32 administrator_count = 0;
@ -844,6 +870,7 @@ class ContactsManager : public Actor {
int32 ttl = 0;
int32 date = 0;
int32 layer = 0;
FolderId initial_folder_id;
bool is_outbound = false;
@ -864,13 +891,14 @@ class ContactsManager : public Actor {
};
struct InviteLinkInfo {
ChatId chat_id; // TODO DialogId
ChannelId channel_id;
// known dialog
DialogId dialog_id;
// unknown dialog
string title;
Photo photo;
int32 participant_count = 0;
vector<UserId> participant_user_ids;
bool is_chat = false;
bool is_channel = false;
bool is_public = false;
@ -1063,8 +1091,7 @@ class ContactsManager : public Actor {
ChannelFull *add_channel_full(ChannelId channel_id);
void send_get_channel_full_query(ChannelFull *channel_full, ChannelId channel_id,
tl_object_ptr<telegram_api::InputChannel> &&input_channel, Promise<Unit> &&promise,
void send_get_channel_full_query(ChannelFull *channel_full, ChannelId channel_id, Promise<Unit> &&promise,
const char *source);
const SecretChat *get_secret_chat(SecretChatId secret_chat_id) const;
@ -1098,14 +1125,25 @@ class ContactsManager : public Actor {
void do_update_user_photo(User *u, UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo,
const char *source);
void do_update_user_photo(User *u, UserId user_id, ProfilePhoto new_photo, bool invalidate_photo_cache,
const char *source);
void add_user_photo_id(User *u, UserId user_id, int64 photo_id, const vector<FileId> &photo_file_ids);
void upload_profile_photo(FileId file_id, bool is_animation, double main_frame_timestamp, Promise<Unit> &&promise,
vector<int> bad_parts = {});
void on_upload_profile_photo(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file);
void on_upload_profile_photo_error(FileId file_id, Status status);
void register_user_photo(User *u, UserId user_id, const Photo &photo);
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);
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, const char *source);
void add_profile_photo_to_cache(UserId user_id, Photo &&photo);
bool delete_profile_photo_from_cache(UserId user_id, int64 profile_photo_id, bool send_updates);
void drop_user_photos(UserId user_id, bool is_empty, bool drop_user_full_photo, const char *source);
void drop_user_full(UserId user_id);
void on_set_user_is_blocked_failed(UserId user_id, bool is_blocked, Status error);
@ -1119,6 +1157,7 @@ class ContactsManager : public Actor {
void on_update_chat_active(Chat *c, ChatId chat_id, bool is_active);
void on_update_chat_migrated_to_channel_id(Chat *c, ChatId chat_id, ChannelId migrated_to_channel_id);
void on_update_chat_full_photo(ChatFull *chat_full, ChatId chat_id, Photo photo);
bool on_update_chat_full_participants_short(ChatFull *chat_full, ChatId chat_id, int32 version);
void on_update_chat_full_participants(ChatFull *chat_full, ChatId chat_id, vector<DialogParticipant> participants,
int32 version, bool from_update);
@ -1134,6 +1173,7 @@ class ContactsManager : public Actor {
void on_update_channel_bot_user_ids(ChannelId channel_id, vector<UserId> &&bot_user_ids);
void on_update_channel_full_photo(ChannelFull *channel_full, ChannelId channel_id, Photo photo);
void on_update_channel_full_invite_link(ChannelFull *channel_full,
tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link_ptr);
void on_update_channel_full_linked_channel_id(ChannelFull *channel_full, ChannelId channel_id,
@ -1145,6 +1185,11 @@ class ContactsManager : public Actor {
void on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id,
vector<UserId> &&bot_user_ids);
void on_channel_status_changed(Channel *c, ChannelId channel_id, const DialogParticipantStatus &old_status,
const DialogParticipantStatus &new_status);
void on_channel_username_changed(Channel *c, ChannelId channel_id, const string &old_username,
const string &new_username);
void remove_linked_channel_id(ChannelId channel_id);
ChannelId get_linked_channel_id(ChannelId channel_id) const;
@ -1155,8 +1200,11 @@ class ContactsManager : public Actor {
void speculative_add_channel_user(ChannelId channel_id, UserId user_id, DialogParticipantStatus new_status,
DialogParticipantStatus old_status);
void drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_chat_full_photo, const char *source);
void drop_chat_full(ChatId chat_id);
void drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo, const char *source);
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);
void update_channel_online_member_count(ChannelId channel_id, bool is_from_server);
@ -1288,6 +1336,8 @@ class ContactsManager : public Actor {
static bool is_channel_public(const Channel *c);
void remove_dialog_access_by_invite_link(DialogId dialog_id);
static bool is_valid_invite_link(const string &invite_link);
bool update_invite_link(string &invite_link, tl_object_ptr<telegram_api::ExportedChatInvite> &&invite_link_ptr);
@ -1307,7 +1357,9 @@ class ContactsManager : public Actor {
void reload_dialog_administrators(DialogId dialog_id, int32 hash, Promise<Unit> &&promise);
tl_object_ptr<td_api::UserStatus> get_user_status_object(UserId user_id, const User *u) const;
static td_api::object_ptr<td_api::updateUser> get_update_unknown_user_object(UserId user_id);
td_api::object_ptr<td_api::UserStatus> get_user_status_object(UserId user_id, const User *u) const;
td_api::object_ptr<td_api::botInfo> get_bot_info_object(UserId user_id) const;
@ -1315,18 +1367,24 @@ class ContactsManager : public Actor {
tl_object_ptr<td_api::userFullInfo> get_user_full_info_object(UserId user_id, const UserFull *user_full) const;
static td_api::object_ptr<td_api::updateBasicGroup> get_update_unknown_basic_group_object(ChatId chat_id);
tl_object_ptr<td_api::basicGroup> get_basic_group_object(ChatId chat_id, const Chat *c);
tl_object_ptr<td_api::basicGroup> get_basic_group_object_const(ChatId chat_id, const Chat *c) const;
tl_object_ptr<td_api::basicGroupFullInfo> get_basic_group_full_info_object(const ChatFull *chat_full) const;
static td_api::object_ptr<td_api::updateSupergroup> get_update_unknown_supergroup_object(ChannelId channel_id);
tl_object_ptr<td_api::supergroup> get_supergroup_object(ChannelId channel_id, const Channel *c) const;
tl_object_ptr<td_api::supergroupFullInfo> get_supergroup_full_info_object(const ChannelFull *channel_full) const;
static tl_object_ptr<td_api::SecretChatState> get_secret_chat_state_object(SecretChatState state);
static td_api::object_ptr<td_api::updateSecretChat> get_update_unknown_secret_chat_object(SecretChatId user_id);
tl_object_ptr<td_api::secretChat> get_secret_chat_object(SecretChatId secret_chat_id, const SecretChat *secret_chat);
tl_object_ptr<td_api::secretChat> get_secret_chat_object_const(SecretChatId secret_chat_id,
@ -1357,8 +1415,8 @@ class ContactsManager : public Actor {
void get_channel_statistics_dc_id_impl(ChannelId channel_id, Promise<DcId> &&promise);
void send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark,
Promise<td_api::object_ptr<td_api::chatStatistics>> &&promise);
void send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark,
Promise<td_api::object_ptr<td_api::ChatStatistics>> &&promise);
void send_load_async_graph_query(DcId dc_id, string token, int64 x,
Promise<td_api::object_ptr<td_api::StatisticsGraph>> &&promise);
@ -1371,12 +1429,16 @@ class ContactsManager : public Actor {
static void on_slow_mode_delay_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long);
static void on_invite_link_info_expire_timeout_callback(void *contacts_manager_ptr, int64 dialog_id_long);
void on_user_online_timeout(UserId user_id);
void on_user_nearby_timeout(UserId user_id);
void on_slow_mode_delay_timeout(ChannelId channel_id);
void on_invite_link_info_expire_timeout(DialogId dialog_id);
void tear_down() override;
Td *td_;
@ -1397,26 +1459,31 @@ class ContactsManager : public Actor {
}
};
std::unordered_map<std::pair<UserId, int64>, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_;
std::unordered_map<int64, FileId> my_photo_file_id_;
std::unordered_map<ChatId, unique_ptr<Chat>, ChatIdHash> chats_;
std::unordered_map<ChatId, unique_ptr<ChatFull>, ChatIdHash> chats_full_;
mutable std::unordered_set<ChatId, ChatIdHash> unknown_chats_;
std::unordered_map<ChatId, FileSourceId, ChatIdHash> chat_photo_file_source_ids_;
std::unordered_map<ChatId, FileSourceId, ChatIdHash> chat_full_file_source_ids_;
std::unordered_set<ChannelId, ChannelIdHash> min_channels_;
std::unordered_map<ChannelId, unique_ptr<Channel>, ChannelIdHash> channels_;
std::unordered_map<ChannelId, unique_ptr<ChannelFull>, ChannelIdHash> channels_full_;
mutable std::unordered_set<ChannelId, ChannelIdHash> unknown_channels_;
std::unordered_map<ChannelId, FileSourceId, ChannelIdHash> channel_photo_file_source_ids_;
std::unordered_map<ChannelId, FileSourceId, ChannelIdHash> channel_full_file_source_ids_;
std::unordered_map<SecretChatId, unique_ptr<SecretChat>, SecretChatIdHash> secret_chats_;
mutable std::unordered_set<SecretChatId, SecretChatIdHash> unknown_secret_chats_;
std::unordered_map<UserId, vector<SecretChatId>, UserIdHash> secret_chats_with_user_;
std::unordered_map<ChatId, string, ChatIdHash> chat_invite_links_; // in-memory cache for invite links
std::unordered_map<ChannelId, string, ChannelIdHash> channel_invite_links_; // in-memory cache for invite links
struct DialogAccessByInviteLink {
std::unordered_set<string> invite_links;
int32 accessible_before = 0;
};
std::unordered_map<DialogId, string, DialogIdHash> dialog_invite_links_; // in-memory cache for invite links
std::unordered_map<string, unique_ptr<InviteLinkInfo>> invite_link_infos_;
std::unordered_map<DialogId, DialogAccessByInviteLink, DialogIdHash> dialog_access_by_invite_link_;
bool created_public_channels_inited_[2] = {false, false};
vector<ChannelId> created_public_channels_[2];
@ -1452,7 +1519,20 @@ class ContactsManager : public Actor {
class UploadProfilePhotoCallback;
std::shared_ptr<UploadProfilePhotoCallback> upload_profile_photo_callback_;
std::unordered_map<FileId, Promise<Unit>, FileIdHash> uploaded_profile_photos_; // file_id -> promise
struct UploadedProfilePhoto {
double main_frame_timestamp;
bool is_animation;
bool is_reupload;
Promise<Unit> promise;
UploadedProfilePhoto(double main_frame_timestamp, bool is_animation, bool is_reupload, Promise<Unit> promise)
: main_frame_timestamp(main_frame_timestamp)
, is_animation(is_animation)
, is_reupload(is_reupload)
, promise(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedProfilePhoto, FileIdHash> uploaded_profile_photos_; // file_id -> promise
std::unordered_map<int64, std::pair<vector<UserId>, vector<int32>>> imported_contacts_;
@ -1506,6 +1586,7 @@ class ContactsManager : public Actor {
MultiTimeout channel_unban_timeout_{"ChannelUnbanTimeout"};
MultiTimeout user_nearby_timeout_{"UserNearbyTimeout"};
MultiTimeout slow_mode_delay_timeout_{"SlowModeDelayTimeout"};
MultiTimeout invite_link_info_expire_timeout_{"InviteLinkInfoExpireTimeout"};
};
} // namespace td

View File

@ -152,6 +152,8 @@ class DialogDbAsync : public DialogDbAsyncInterface {
}
void on_write_result(Promise<> promise, Status status) {
// We are inside a transaction and don't know how to handle the error
status.ensure();
pending_write_results_.emplace_back(std::move(promise), std::move(status));
}

View File

@ -605,6 +605,71 @@ RestrictedRights get_restricted_rights(const td_api::object_ptr<td_api::chatPerm
permissions->can_pin_messages_);
}
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) {
if (!inviter_user_id.is_valid() && inviter_user_id != UserId()) {
LOG(ERROR) << "Receive inviter " << inviter_user_id;
inviter_user_id = UserId();
}
if (joined_date < 0) {
LOG(ERROR) << "Receive date " << joined_date;
joined_date = 0;
}
}
DialogParticipant::DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr,
DialogParticipantStatus my_status) {
CHECK(participant_ptr != nullptr);
switch (participant_ptr->get_id()) {
case telegram_api::channelParticipant::ID: {
auto participant = move_tl_object_as<telegram_api::channelParticipant>(participant_ptr);
*this = {UserId(participant->user_id_), UserId(), participant->date_, DialogParticipantStatus::Member()};
break;
}
case telegram_api::channelParticipantSelf::ID: {
auto participant = move_tl_object_as<telegram_api::channelParticipantSelf>(participant_ptr);
*this = {UserId(participant->user_id_), UserId(participant->inviter_id_), participant->date_,
std::move(my_status)};
break;
}
case telegram_api::channelParticipantCreator::ID: {
auto participant = move_tl_object_as<telegram_api::channelParticipantCreator>(participant_ptr);
*this = {UserId(participant->user_id_), UserId(), 0,
DialogParticipantStatus::Creator(true, std::move(participant->rank_))};
break;
}
case telegram_api::channelParticipantAdmin::ID: {
auto participant = move_tl_object_as<telegram_api::channelParticipantAdmin>(participant_ptr);
bool can_be_edited = (participant->flags_ & telegram_api::channelParticipantAdmin::CAN_EDIT_MASK) != 0;
*this = {UserId(participant->user_id_), UserId(participant->promoted_by_), participant->date_,
get_dialog_participant_status(can_be_edited, std::move(participant->admin_rights_),
std::move(participant->rank_))};
break;
}
case telegram_api::channelParticipantBanned::ID: {
auto participant = move_tl_object_as<telegram_api::channelParticipantBanned>(participant_ptr);
auto is_member = (participant->flags_ & telegram_api::channelParticipantBanned::LEFT_MASK) == 0;
*this = {UserId(participant->user_id_), UserId(participant->kicked_by_), participant->date_,
get_dialog_participant_status(is_member, std::move(participant->banned_rights_))};
break;
}
default:
UNREACHABLE();
break;
}
}
bool DialogParticipant::is_valid() const {
if (!user_id.is_valid() || joined_date < 0) {
return false;
}
if (status.is_restricted() || status.is_banned() || (status.is_administrator() && !status.is_creator())) {
return inviter_user_id.is_valid();
}
return true;
}
StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant &dialog_participant) {
return string_builder << '[' << dialog_participant.user_id << " invited by " << dialog_participant.inviter_user_id
<< " at " << dialog_participant.joined_date << " with status " << dialog_participant.status

View File

@ -365,9 +365,12 @@ struct DialogParticipant {
DialogParticipant() = default;
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) {
}
DialogParticipant(UserId user_id, UserId inviter_user_id, int32 joined_date, DialogParticipantStatus status);
DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr,
DialogParticipantStatus my_status);
bool is_valid() const;
template <class StorerT>
void store(StorerT &storer) const {

View File

@ -217,7 +217,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
string file_reference;
string minithumbnail;
PhotoSize thumbnail;
PhotoSize animated_thumbnail;
AnimationSize animated_thumbnail;
FileEncryptionKey encryption_key;
bool is_animated_sticker = false;
bool is_web = false;
@ -276,9 +276,8 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
}
for (auto &thumb : document->video_thumbs_) {
if (thumb->type_ == "v") {
animated_thumbnail =
get_video_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, file_reference,
DcId::create(dc_id), owner_dialog_id, std::move(thumb));
animated_thumbnail = get_animation_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash,
file_reference, DcId::create(dc_id), owner_dialog_id, std::move(thumb));
if (animated_thumbnail.file_id.is_valid()) {
break;
}
@ -313,7 +312,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
dc_id = 0;
access_hash = 0;
if (remote_document.thumbnail.type == 'v') {
animated_thumbnail = std::move(remote_document.thumbnail);
static_cast<PhotoSize &>(animated_thumbnail) = std::move(remote_document.thumbnail);
} else {
thumbnail = std::move(remote_document.thumbnail);
if (remote_document.thumbnail.type == 'g') {
@ -608,9 +607,12 @@ tl_object_ptr<telegram_api::InputMedia> DocumentsManager::get_input_media(
if (input_thumbnail != nullptr) {
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
if (file_view.get_type() == FileType::DocumentAsFile) {
flags |= telegram_api::inputMediaUploadedDocument::FORCE_FILE_MASK;
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), document->mime_type,
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail),
document->mime_type, std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());
}

View File

@ -14,6 +14,7 @@
#include "td/telegram/MessagesManager.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/WebPagesManager.h"
#include "td/utils/common.h"
@ -44,14 +45,16 @@ size_t FileReferenceManager::get_file_reference_error_pos(const Status &error) {
/*
fileSourceMessage chat_id:int53 message_id:int53 = FileSource; // repaired with get_message_from_server
fileSourceUserProfilePhoto user_id:int32 photo_id:int64 = FileSource; // repaired with photos.getUserPhotos
fileSourceBasicGroupPhoto basic_group_id:int32 = FileSource; // repaired with messages.getChats
fileSourceSupergroupPhoto supergroup_id:int32 = FileSource; // repaired with channels.getChannels
fileSourceBasicGroupPhoto basic_group_id:int32 = FileSource; // no need to repair
fileSourceSupergroupPhoto supergroup_id:int32 = FileSource; // no need to repair
fileSourceWebPage url:string = FileSource; // repaired with messages.getWebPage
fileSourceWallpapers = FileSource; // can't be repaired
fileSourceSavedAnimations = FileSource; // repaired with messages.getSavedGifs
fileSourceRecentStickers is_attached:Bool = FileSource; // repaired with messages.getRecentStickers, not reliable
fileSourceFavoriteStickers = FileSource; // repaired with messages.getFavedStickers, not reliable
fileSourceBackground background_id:int64 access_hash:int64 = FileSource; // repaired with account.getWallPaper
fileSourceBasicGroupFull basic_group_id:int32 = FileSource; // repaired with messages.getFullChat
fileSourceSupergroupFull supergroup_id:int32 = FileSource; // repaired with messages.getFullChannel
*/
FileSourceId FileReferenceManager::get_current_file_source_id() const {
@ -75,16 +78,6 @@ FileSourceId FileReferenceManager::create_user_photo_file_source(UserId user_id,
return add_file_source_id(source, PSLICE() << "photo " << photo_id << " of " << user_id);
}
FileSourceId FileReferenceManager::create_chat_photo_file_source(ChatId chat_id) {
FileSourceChatPhoto source{chat_id};
return add_file_source_id(source, PSLICE() << "photo of " << chat_id);
}
FileSourceId FileReferenceManager::create_channel_photo_file_source(ChannelId channel_id) {
FileSourceChannelPhoto source{channel_id};
return add_file_source_id(source, PSLICE() << "photo of " << channel_id);
}
FileSourceId FileReferenceManager::create_web_page_file_source(string url) {
FileSourceWebPage source{std::move(url)};
auto source_str = PSTRING() << "web page of " << source.url;
@ -111,6 +104,16 @@ FileSourceId FileReferenceManager::create_background_file_source(BackgroundId ba
return add_file_source_id(source, PSLICE() << background_id);
}
FileSourceId FileReferenceManager::create_chat_full_file_source(ChatId chat_id) {
FileSourceChatFull source{chat_id};
return add_file_source_id(source, PSLICE() << "full " << chat_id);
}
FileSourceId FileReferenceManager::create_channel_full_file_source(ChannelId channel_id) {
FileSourceChannelFull source{channel_id};
return add_file_source_id(source, PSLICE() << "full " << channel_id);
}
bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_source_id) {
bool is_added = nodes_[node_id].file_source_ids.add(file_source_id);
VLOG(file_references) << "Add " << (is_added ? "new" : "old") << ' ' << file_source_id << " for file " << node_id;
@ -242,20 +245,8 @@ void FileReferenceManager::send_query(Destination dest, FileSourceId file_source
std::move(status), 0);
});
send_lambda(file_manager, [file_manager, dest, result = std::move(result), file_source_id,
new_promise = std::move(new_promise)]() mutable {
auto view = file_manager.get_actor_unsafe()->get_file_view(dest.node_id);
CHECK(!view.empty());
if (result.is_ok() &&
(!view.has_active_upload_remote_location() || !view.has_active_download_remote_location())) {
result = Status::Error("No active remote location");
}
if (result.is_error() && result.error().code() != 429 && result.error().code() < 500) {
VLOG(file_references) << "Invalid " << file_source_id << " " << result.error();
file_manager.get_actor_unsafe()->remove_file_source(dest.node_id, file_source_id);
}
new_promise.set_result(std::move(result));
});
send_closure(file_manager, &FileManager::on_file_reference_repaired, dest.node_id, file_source_id,
std::move(result), std::move(new_promise));
});
auto index = static_cast<size_t>(file_source_id.get()) - 1;
CHECK(index < file_sources_.size());
@ -293,6 +284,14 @@ void FileReferenceManager::send_query(Destination dest, FileSourceId file_source
[&](const FileSourceBackground &source) {
send_closure_later(G()->background_manager(), &BackgroundManager::reload_background, source.background_id,
source.access_hash, std::move(promise));
},
[&](const FileSourceChatFull &source) {
send_closure_later(G()->contacts_manager(), &ContactsManager::reload_chat_full, source.chat_id,
std::move(promise));
},
[&](const FileSourceChannelFull &source) {
send_closure_later(G()->contacts_manager(), &ContactsManager::reload_channel_full, source.channel_id,
std::move(promise), "repair file reference");
}));
}
@ -335,7 +334,9 @@ FileReferenceManager::Destination FileReferenceManager::on_query_result(Destinat
}
void FileReferenceManager::repair_file_reference(NodeId node_id, Promise<> promise) {
VLOG(file_references) << "Repair file reference for file " << node_id;
auto main_file_id = G()->td().get_actor_unsafe()->file_manager_->get_file_view(node_id).file_id();
VLOG(file_references) << "Repair file reference for file " << node_id << "/" << main_file_id;
node_id = main_file_id;
auto &node = nodes_[node_id];
if (!node.query) {
node.query = make_unique<Query>();

View File

@ -42,14 +42,17 @@ class FileReferenceManager : public Actor {
FileSourceId create_message_file_source(FullMessageId full_message_id);
FileSourceId create_user_photo_file_source(UserId user_id, int64 photo_id);
FileSourceId create_chat_photo_file_source(ChatId chat_id);
FileSourceId create_channel_photo_file_source(ChannelId channel_id);
// file reference aren't used for chat/channel photo download and the photos can't be reused
// FileSourceId create_chat_photo_file_source(ChatId chat_id);
// FileSourceId create_channel_photo_file_source(ChannelId channel_id);
// FileSourceId create_wallpapers_file_source(); old wallpapers can't be repaired
FileSourceId create_web_page_file_source(string url);
FileSourceId create_saved_animations_file_source();
FileSourceId create_recent_stickers_file_source(bool is_attached);
FileSourceId create_favorite_stickers_file_source();
FileSourceId create_background_file_source(BackgroundId background_id, int64 access_hash);
FileSourceId create_chat_full_file_source(ChatId chat_id);
FileSourceId create_channel_full_file_source(ChannelId channel_id);
using NodeId = FileId;
void repair_file_reference(NodeId node_id, Promise<> promise);
@ -124,11 +127,18 @@ class FileReferenceManager : public Actor {
BackgroundId background_id;
int64 access_hash;
};
struct FileSourceChatFull {
ChatId chat_id;
};
struct FileSourceChannelFull {
ChannelId channel_id;
};
// append only
using FileSource = Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto,
FileSourceWallpapers, FileSourceWebPage, FileSourceSavedAnimations,
FileSourceRecentStickers, FileSourceFavoriteStickers, FileSourceBackground>;
using FileSource =
Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto, FileSourceWallpapers,
FileSourceWebPage, FileSourceSavedAnimations, FileSourceRecentStickers, FileSourceFavoriteStickers,
FileSourceBackground, FileSourceChatFull, FileSourceChannelFull>;
vector<FileSource> file_sources_;
int64 query_generation_{0};

View File

@ -47,7 +47,9 @@ void FileReferenceManager::store_file_source(FileSourceId file_source_id, Storer
[&](const FileSourceBackground &source) {
td::store(source.background_id, storer);
td::store(source.access_hash, storer);
}));
},
[&](const FileSourceChatFull &source) { td::store(source.chat_id, storer); },
[&](const FileSourceChannelFull &source) { td::store(source.channel_id, storer); }));
}
template <class ParserT>
@ -69,12 +71,12 @@ FileSourceId FileReferenceManager::parse_file_source(Td *td, ParserT &parser) {
case 2: {
ChatId chat_id;
td::parse(chat_id, parser);
return td->contacts_manager_->get_chat_photo_file_source_id(chat_id);
return FileSourceId(); // there is no need to repair chat photos
}
case 3: {
ChannelId channel_id;
td::parse(channel_id, parser);
return td->contacts_manager_->get_channel_photo_file_source_id(channel_id);
return FileSourceId(); // there is no need to repair channel photos
}
case 4:
return FileSourceId(); // there is no way to repair old wallpapers
@ -99,6 +101,16 @@ FileSourceId FileReferenceManager::parse_file_source(Td *td, ParserT &parser) {
td::parse(access_hash, parser);
return td->background_manager_->get_background_file_source_id(background_id, access_hash);
}
case 10: {
ChatId chat_id;
td::parse(chat_id, parser);
return td->contacts_manager_->get_chat_full_file_source_id(chat_id);
}
case 11: {
ChannelId channel_id;
td::parse(channel_id, parser);
return td->contacts_manager_->get_channel_full_file_source_id(channel_id);
}
default:
parser.set_error("Invalid type in FileSource");
return FileSourceId();

View File

@ -36,7 +36,7 @@ Game::Game(Td *td, string title, string description, tl_object_ptr<telegram_api:
CHECK(td != nullptr);
CHECK(photo != nullptr);
photo_ = get_photo(td->file_manager_.get(), std::move(photo), owner_dialog_id);
if (photo_.id == -2) {
if (photo_.is_empty()) {
LOG(ERROR) << "Receive empty photo for game " << title;
photo_.id = 0; // to prevent null photo in td_api
}
@ -58,6 +58,7 @@ Game::Game(UserId bot_user_id, string short_name) : bot_user_id_(bot_user_id), s
if (!bot_user_id_.is_valid()) {
bot_user_id_ = UserId();
}
photo_.id = 0; // to prevent null photo in td_api
}
bool Game::empty() const {
@ -93,7 +94,7 @@ const FormattedText &Game::get_text() const {
tl_object_ptr<td_api::game> Game::get_game_object(Td *td) const {
return make_tl_object<td_api::game>(
id_, short_name_, title_, get_formatted_text_object(text_), description_,
get_photo_object(td->file_manager_.get(), &photo_),
get_photo_object(td->file_manager_.get(), photo_),
td->animations_manager_->get_animation_object(animation_file_id_, "get_game_object"));
}

View File

@ -161,6 +161,10 @@ void Global::save_server_time() {
}
void Global::do_save_server_time_difference() {
if (shared_config_ != nullptr && shared_config_->get_option_boolean("disable_time_adjustment_protection")) {
return;
}
// diff = server_time - Time::now
// fixed_diff = server_time - Clocks::system
double system_time = Clocks::system();

View File

@ -102,6 +102,8 @@ class GetInlineBotResultsQuery : public Td::ResultHandler {
void on_error(uint64 id, Status status) override {
if (status.code() == NetQuery::Cancelled) {
status = Status::Error(406, "Request cancelled");
} else if (status.message() == "BOT_RESPONSE_TIMEOUT") {
status = Status::Error(502, "The bot is not responding");
}
LOG(INFO) << "Inline query returned error " << status;
@ -879,6 +881,8 @@ tl_object_ptr<td_api::thumbnail> copy(const td_api::thumbnail &obj) {
return td_api::make_object<td_api::thumbnailFormatTgs>();
case td_api::thumbnailFormatMpeg4::ID:
return td_api::make_object<td_api::thumbnailFormatMpeg4>();
case td_api::thumbnailFormatGif::ID:
return td_api::make_object<td_api::thumbnailFormatGif>();
default:
UNREACHABLE();
return nullptr;
@ -1165,6 +1169,10 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
bool has_photo = (flags & BOT_INLINE_MEDIA_RESULT_FLAG_HAS_PHOTO) != 0;
bool is_photo = result->type_ == "photo";
if (result->type_ == "game") {
if (!has_photo) {
LOG(ERROR) << "Receive game without photo in the result of inline query: " << to_string(result);
break;
}
auto game = make_tl_object<td_api::inlineQueryResultGame>();
Game inline_game(td_, std::move(result->title_), std::move(result->description_), std::move(result->photo_),
std::move(result->document_), DialogId());
@ -1300,11 +1308,11 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
auto photo = make_tl_object<td_api::inlineQueryResultPhoto>();
photo->id_ = std::move(result->id_);
Photo p = get_photo(td_->file_manager_.get(), std::move(result->photo_), DialogId());
if (p.id == -2) {
if (p.is_empty()) {
LOG(ERROR) << "Receive empty cached photo in the result of inline query";
break;
}
photo->photo_ = get_photo_object(td_->file_manager_.get(), &p);
photo->photo_ = get_photo_object(td_->file_manager_.get(), p);
photo->title_ = std::move(result->title_);
photo->description_ = std::move(result->description_);
@ -1421,6 +1429,7 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
}
Photo new_photo;
new_photo.id = 0;
PhotoSize thumbnail = get_web_document_photo_size(td_->file_manager_.get(), FileType::Thumbnail, DialogId(),
std::move(result->thumb_));
if (thumbnail.file_id.is_valid() && thumbnail.type != 'v' && thumbnail.type != 'g') {
@ -1428,7 +1437,7 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
}
new_photo.photos.push_back(std::move(photo_size));
photo->photo_ = get_photo_object(td_->file_manager_.get(), &new_photo);
photo->photo_ = get_photo_object(td_->file_manager_.get(), new_photo);
photo->title_ = std::move(result->title_);
photo->description_ = std::move(result->description_);

View File

@ -403,13 +403,19 @@ void LanguagePackManager::on_update_language_pack(tl_object_ptr<telegram_api::la
<< " from version " << difference->from_version_ << " with version " << difference->version_ << " of size "
<< difference->strings_.size();
to_lower_inplace(difference->lang_code_);
if (language_code_.empty()) {
LOG(INFO) << "Ignore difference for language pack " << difference->lang_code_
<< ", because have no used language pack";
return;
}
if (language_pack_.empty()) {
LOG(WARNING) << "Ignore difference for language pack " << difference->lang_code_
<< ", because used language pack was unset";
<< ", because localization target is not set";
return;
}
if (difference->lang_code_ != language_code_ && difference->lang_code_ != base_language_code_) {
LOG(WARNING) << "Ignore difference for language pack " << difference->lang_code_;
LOG(WARNING) << "Ignore difference for language pack " << difference->lang_code_ << ", because using language pack "
<< language_code_ << " based on " << base_language_code_;
return;
}
if (is_custom_language_code(difference->lang_code_) || difference->lang_code_.empty()) {

View File

@ -652,7 +652,8 @@ class MessageDice : public MessageContent {
MessageDice() = default;
MessageDice(string emoji, int32 dice_value)
: emoji(emoji.empty() ? string(DEFAULT_EMOJI) : std::move(emoji)), dice_value(dice_value) {
: emoji(emoji.empty() ? string(DEFAULT_EMOJI) : remove_emoji_modifiers(std::move(emoji)))
, dice_value(dice_value) {
}
MessageContentType get_type() const override {
@ -1280,7 +1281,9 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
case MessageContentType::Dice: {
auto m = make_unique<MessageDice>();
if (parser.version() >= static_cast<int32>(Version::AddDiceEmoji)) {
parse(m->emoji, parser);
string emoji;
parse(emoji, parser);
m->emoji = remove_emoji_modifiers(std::move(emoji));
} else {
m->emoji = MessageDice::DEFAULT_EMOJI;
}
@ -1475,8 +1478,8 @@ static Result<InputMessageContent> create_input_message_content(
bool has_stickers = !sticker_file_ids.empty();
td->animations_manager_->create_animation(
file_id, string(), thumbnail, PhotoSize(), has_stickers, std::move(sticker_file_ids), std::move(file_name),
std::move(mime_type), input_animation->duration_,
file_id, string(), thumbnail, AnimationSize(), has_stickers, std::move(sticker_file_ids),
std::move(file_name), std::move(mime_type), input_animation->duration_,
get_dimensions(input_animation->width_, input_animation->height_), false);
content = make_unique<MessageAnimation>(file_id, std::move(caption));
@ -1529,6 +1532,8 @@ static Result<InputMessageContent> create_input_message_content(
if (file_view.has_remote_location() && !file_view.remote_location().is_web()) {
message_photo->photo.id = file_view.remote_location().get_id();
} else {
message_photo->photo.id = 0;
}
message_photo->photo.date = G()->unix_time();
int32 type = 'i';
@ -1577,9 +1582,9 @@ static Result<InputMessageContent> create_input_message_content(
bool has_stickers = !sticker_file_ids.empty();
td->videos_manager_->create_video(
file_id, string(), thumbnail, PhotoSize(), has_stickers, std::move(sticker_file_ids), std::move(file_name),
std::move(mime_type), input_video->duration_, get_dimensions(input_video->width_, input_video->height_),
input_video->supports_streaming_, false);
file_id, string(), thumbnail, AnimationSize(), has_stickers, std::move(sticker_file_ids),
std::move(file_name), std::move(mime_type), input_video->duration_,
get_dimensions(input_video->width_, input_video->height_), input_video->supports_streaming_, false);
content = make_unique<MessageVideo>(file_id, std::move(caption));
break;
@ -1673,22 +1678,21 @@ static Result<InputMessageContent> create_input_message_content(
if (!input_invoice->photo_url_.empty()) {
LOG(INFO) << "Can't register url " << input_invoice->photo_url_;
}
message_invoice->photo.id = -2;
} else {
auto url = r_http_url.ok().get_url();
auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp);
if (r_invoice_file_id.is_error()) {
LOG(INFO) << "Can't register url " << url;
message_invoice->photo.id = -2;
} else {
auto invoice_file_id = r_invoice_file_id.move_as_ok();
PhotoSize s;
s.type = 'u';
s.type = 'n';
s.dimensions = get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_);
s.size = input_invoice->photo_size_; // TODO use invoice_file_id size
s.file_id = invoice_file_id;
message_invoice->photo.id = 0;
message_invoice->photo.photos.push_back(s);
}
}
@ -1743,9 +1747,9 @@ static Result<InputMessageContent> create_input_message_content(
break;
}
case td_api::inputMessagePoll::ID: {
constexpr size_t MAX_POLL_QUESTION_LENGTH = 255; // server-side limit
constexpr size_t MAX_POLL_OPTION_LENGTH = 100; // server-side limit
constexpr size_t MAX_POLL_OPTIONS = 10; // server-side limit
const size_t MAX_POLL_QUESTION_LENGTH = is_bot ? 300 : 255; // server-side limit
constexpr size_t MAX_POLL_OPTION_LENGTH = 100; // server-side limit
constexpr size_t MAX_POLL_OPTIONS = 10; // server-side limit
auto input_poll = static_cast<td_api::inputMessagePoll *>(input_message_content.get());
if (!clean_input_string(input_poll->question_)) {
return Status::Error(400, "Poll question must be encoded in UTF-8");
@ -1855,8 +1859,9 @@ Result<InputMessageContent> get_input_message_content(
}
case td_api::inputMessageDocument::ID: {
auto input_message = static_cast<td_api::inputMessageDocument *>(input_message_content.get());
r_file_id = td->file_manager_->get_input_file_id(FileType::Document, input_message->document_, dialog_id, false,
is_secret, true);
auto file_type = input_message->force_file_ ? FileType::DocumentAsFile : FileType::Document;
r_file_id =
td->file_manager_->get_input_file_id(file_type, input_message->document_, dialog_id, false, is_secret, true);
input_thumbnail = std::move(input_message->thumbnail_);
break;
}
@ -2127,7 +2132,7 @@ static tl_object_ptr<telegram_api::invoice> get_input_invoice(const Invoice &inv
static tl_object_ptr<telegram_api::inputWebDocument> get_input_web_document(const FileManager *file_manager,
const Photo &photo) {
if (photo.id == -2) {
if (photo.is_empty()) {
return nullptr;
}
@ -2829,7 +2834,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
if (old_photo->date != new_photo->date) {
is_content_changed = true;
}
if (old_photo->id != new_photo->id || old_->caption != new_->caption) {
if (old_photo->id.get() != new_photo->id.get() || old_->caption != new_->caption) {
need_update = true;
}
if (old_photo->minithumbnail != new_photo->minithumbnail) {
@ -3783,7 +3788,7 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
}
auto photo = get_photo(td->file_manager_.get(), std::move(message_photo->photo_), owner_dialog_id);
if (photo.id == -2) {
if (photo.is_empty()) {
return make_unique<MessageExpiredPhoto>();
}
@ -4195,7 +4200,7 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
case telegram_api::messageActionChatEditPhoto::ID: {
auto chat_edit_photo = move_tl_object_as<telegram_api::messageActionChatEditPhoto>(action);
auto photo = get_photo(td->file_manager_.get(), std::move(chat_edit_photo->photo_), owner_dialog_id);
if (photo.id == -2) {
if (photo.is_empty()) {
break;
}
return make_unique<MessageChatChangePhoto>(std::move(photo));
@ -4377,7 +4382,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
case MessageContentType::Invoice: {
const MessageInvoice *m = static_cast<const MessageInvoice *>(content);
return make_tl_object<td_api::messageInvoice>(
m->title, m->description, get_photo_object(td->file_manager_.get(), &m->photo), m->invoice.currency,
m->title, m->description, get_photo_object(td->file_manager_.get(), m->photo), m->invoice.currency,
m->total_amount, m->start_parameter, m->invoice.is_test, m->invoice.need_shipping_address,
m->receipt_message_id.get());
}
@ -4393,7 +4398,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
}
case MessageContentType::Photo: {
const MessagePhoto *m = static_cast<const MessagePhoto *>(content);
return make_tl_object<td_api::messagePhoto>(get_photo_object(td->file_manager_.get(), &m->photo),
return make_tl_object<td_api::messagePhoto>(get_photo_object(td->file_manager_.get(), m->photo),
get_formatted_text_object(m->caption), is_content_secret);
}
case MessageContentType::Sticker: {
@ -4437,7 +4442,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
}
case MessageContentType::ChatChangePhoto: {
const MessageChatChangePhoto *m = static_cast<const MessageChatChangePhoto *>(content);
return make_tl_object<td_api::messageChatChangePhoto>(get_photo_object(td->file_manager_.get(), &m->photo));
return make_tl_object<td_api::messageChatChangePhoto>(get_chat_photo_object(td->file_manager_.get(), m->photo));
}
case MessageContentType::ChatDeletePhoto:
return make_tl_object<td_api::messageChatDeletePhoto>();
@ -4705,7 +4710,7 @@ FileId get_message_content_thumbnail_file_id(const MessageContent *content, cons
return FileId();
}
FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td) {
static FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td) {
switch (content->get_type()) {
case MessageContentType::Animation:
return td->animations_manager_->get_animation_animated_thumbnail_file_id(

View File

@ -199,8 +199,6 @@ void update_message_content_file_id_remote(MessageContent *content, FileId file_
FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td);
FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td);
vector<FileId> get_message_content_file_ids(const MessageContent *content, const Td *td);
string get_message_content_search_text(const Td *td, const MessageContent *content);

View File

@ -1600,7 +1600,7 @@ Result<vector<MessageEntity>> parse_markdown(string &text) {
}
if (c != '_' && c != '*' && c != '`' && c != '[') {
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
result.push_back(text[i]);
continue;
@ -1642,7 +1642,7 @@ Result<vector<MessageEntity>> parse_markdown(string &text) {
while (i < size && (text[i] != end_character || (is_pre && !(text[i + 1] == '`' && text[i + 2] == '`')))) {
auto cur_ch = static_cast<unsigned char>(text[i]);
if (is_utf8_character_first_code_unit(cur_ch)) {
utf16_offset += 1 + (cur_ch >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (cur_ch >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
result.push_back(text[i++]);
}
@ -1750,7 +1750,7 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
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
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
result.push_back(text[i]);
continue;
@ -2174,7 +2174,7 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
for (size_t i = 0; i + 1 < text.size(); i++) {
auto c = static_cast<unsigned char>(text[i]);
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
if ((c == '_' || c == '*' || c == '~') && text[i] == text[i + 1] && unallowed_boundaries.count(utf16_offset) == 0) {
auto j = i + 2;
@ -2244,7 +2244,7 @@ static FormattedText parse_markdown_v3_without_pre(Slice text, vector<MessageEnt
for (size_t i = 0; i < text.size(); i++) {
auto c = static_cast<unsigned char>(text[i]);
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
if (j < removed_pos.size() && utf16_offset == removed_pos[j]) {
i++;
@ -2284,7 +2284,7 @@ static FormattedText parse_pre_entities_v3(Slice text) {
auto c = static_cast<unsigned char>(text[i]);
if (c != '`') {
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
result.push_back(text[i]);
continue;
@ -2323,7 +2323,7 @@ static FormattedText parse_pre_entities_v3(Slice text) {
end_tag_begin = end_tag_end - 1;
}
} else if (is_utf8_character_first_code_unit(cur_c)) {
entity_length += 1 + (cur_c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
entity_length += 1 + (cur_c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
}
if (is_found) {
@ -2587,7 +2587,7 @@ FormattedText get_markdown_v3(FormattedText text) {
}
nested_entities_stack.emplace_back(&text.entities[current_entity++], utf16_added);
}
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
if (pos == text.text.size()) {
break;
@ -2688,7 +2688,7 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
}
if (c != '<') {
if (is_utf8_character_first_code_unit(c)) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
result.push_back(text[i]);
continue;
@ -3455,7 +3455,7 @@ static Result<string> clean_input_string_with_entities(const string &text, vecto
break;
default:
if (is_utf8_character_begin) {
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += 1 + (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
}
if (c == 0xe2 && pos + 2 < text_size) {
unsigned char next = static_cast<unsigned char>(text[pos + 1]);
@ -3513,11 +3513,9 @@ static std::pair<size_t, int32> remove_invalid_entities(const string &text, vect
int32 last_space_utf16_offset = -1;
int32 last_non_whitespace_utf16_offset = -1;
td::remove_if(entities, [](const auto &entity) { return entity.length == 0; });
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;
@ -3575,7 +3573,7 @@ static std::pair<size_t, int32> remove_invalid_entities(const string &text, vect
while (!is_utf8_character_first_code_unit(static_cast<unsigned char>(text[pos + 1]))) {
pos++;
}
utf16_offset += (c >= 0xf0); // >= 4 bytes in symbol => surrogaite pair
utf16_offset += (c >= 0xf0); // >= 4 bytes in symbol => surrogate pair
last_non_whitespace_pos = pos;
last_non_whitespace_utf16_offset = utf16_offset;
break;

View File

@ -578,7 +578,7 @@ class MessagesDbImpl : public MessagesDbSyncInterface {
while (get_expiring_messages_stmt_.has_row()) {
DialogId dialog_id(get_expiring_messages_stmt_.view_int64(0));
BufferSlice data(get_expiring_messages_stmt_.view_blob(1));
messages.push_back(std::make_pair(dialog_id, std::move(data)));
messages.emplace_back(dialog_id, std::move(data));
get_expiring_messages_stmt_.step().ensure();
}
}
@ -1053,6 +1053,8 @@ class MessagesDbAsync : public MessagesDbAsyncInterface {
});
}
void on_write_result(Promise<> promise, Status status) {
// We are inside a transaction and don't know how to handle the error
status.ensure();
pending_write_results_.emplace_back(std::move(promise), std::move(status));
}
void delete_all_dialog_messages(DialogId dialog_id, MessageId from_message_id, Promise<> promise) {

View File

@ -625,10 +625,8 @@ class GetDialogListActor : public NetActorOnce {
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<telegram_api::inputPeerEmpty>();
}
auto input_peer = MessagesManager::get_input_peer_force(offset_dialog_id);
CHECK(input_peer != nullptr);
int32 flags =
telegram_api::messages_getDialogs::EXCLUDE_PINNED_MASK | telegram_api::messages_getDialogs::FOLDER_ID_MASK;
@ -906,7 +904,7 @@ class EditDialogPhotoQuery : public Td::ResultHandler {
if (file_id_.is_valid() && !was_uploaded_) {
VLOG(file_references) << "Receive " << status << " for " << file_id_;
td->file_manager_->delete_file_reference(file_id_, file_reference_);
td->messages_manager_->upload_dialog_photo(dialog_id_, file_id_, std::move(promise_));
td->messages_manager_->upload_dialog_photo(dialog_id_, file_id_, false, 0.0, false, std::move(promise_), {-1});
return;
} else {
LOG(ERROR) << "Receive file reference error, but file_id = " << file_id_
@ -1676,10 +1674,8 @@ class SearchMessagesGlobalQuery : public Td::ResultHandler {
limit_ = limit;
random_id_ = random_id;
auto input_peer = td->messages_manager_->get_input_peer(offset_dialog_id, AccessRights::Read);
if (input_peer == nullptr) {
input_peer = make_tl_object<telegram_api::inputPeerEmpty>();
}
auto input_peer = MessagesManager::get_input_peer_force(offset_dialog_id);
CHECK(input_peer != nullptr);
int32 flags = 0;
if (!ignore_folder_id) {
@ -3627,7 +3623,8 @@ class UpdatePeerSettingsQuery : public Td::ResultHandler {
td->messages_manager_->on_get_peer_settings(
dialog_id_,
make_tl_object<telegram_api::peerSettings>(0, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/),
false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, 0),
true);
promise_.set_value(Unit());
@ -3668,7 +3665,8 @@ class ReportEncryptedSpamQuery : public Td::ResultHandler {
td->messages_manager_->on_get_peer_settings(
dialog_id_,
make_tl_object<telegram_api::peerSettings>(0, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/),
false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, 0),
true);
promise_.set_value(Unit());
@ -4492,6 +4490,7 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
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;
bool has_distance = distance >= 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_draft_message);
STORE_FLAG(has_last_database_message);
@ -4541,6 +4540,9 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
STORE_FLAG(has_scheduled_server_messages);
STORE_FLAG(has_scheduled_database_messages);
STORE_FLAG(need_repair_channel_server_unread_count);
STORE_FLAG(can_unarchive);
STORE_FLAG(has_distance);
STORE_FLAG(hide_distance);
END_STORE_FLAGS();
}
@ -4622,6 +4624,9 @@ void MessagesManager::Dialog::store(StorerT &storer) const {
store(pending_read_channel_inbox_max_message_id, storer);
store(pending_read_channel_inbox_server_unread_count, storer);
}
if (has_distance) {
store(distance, storer);
}
}
// do not forget to resolve dialog dependencies including dependencies of last_message
@ -4650,6 +4655,7 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
bool has_max_notification_message_id = false;
bool has_folder_id = false;
bool has_pending_read_channel_inbox = false;
bool has_distance = false;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_draft_message);
PARSE_FLAG(has_last_database_message);
@ -4699,6 +4705,9 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
PARSE_FLAG(has_scheduled_server_messages);
PARSE_FLAG(has_scheduled_database_messages);
PARSE_FLAG(need_repair_channel_server_unread_count);
PARSE_FLAG(can_unarchive);
PARSE_FLAG(has_distance);
PARSE_FLAG(hide_distance);
END_PARSE_FLAGS();
} else {
is_folder_id_inited = false;
@ -4707,6 +4716,11 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
can_block_user = false;
can_share_phone_number = false;
can_report_location = false;
has_scheduled_server_messages = false;
has_scheduled_database_messages = false;
need_repair_channel_server_unread_count = false;
can_unarchive = false;
hide_distance = false;
}
parse(last_new_message_id, parser);
@ -4820,6 +4834,9 @@ void MessagesManager::Dialog::parse(ParserT &parser) {
parse(pending_read_channel_inbox_max_message_id, parser);
parse(pending_read_channel_inbox_server_unread_count, parser);
}
if (has_distance) {
parse(distance, parser);
}
}
template <class StorerT>
@ -5275,6 +5292,29 @@ tl_object_ptr<telegram_api::InputPeer> MessagesManager::get_input_peer(DialogId
}
}
tl_object_ptr<telegram_api::InputPeer> MessagesManager::get_input_peer_force(DialogId dialog_id) {
switch (dialog_id.get_type()) {
case DialogType::User: {
UserId user_id = dialog_id.get_user_id();
return make_tl_object<telegram_api::inputPeerUser>(user_id.get(), 0);
}
case DialogType::Chat: {
ChatId chat_id = dialog_id.get_chat_id();
return make_tl_object<telegram_api::inputPeerChat>(chat_id.get());
}
case DialogType::Channel: {
ChannelId channel_id = dialog_id.get_channel_id();
return make_tl_object<telegram_api::inputPeerChannel>(channel_id.get(), 0);
}
case DialogType::SecretChat:
case DialogType::None:
return make_tl_object<telegram_api::inputPeerEmpty>();
default:
UNREACHABLE();
return nullptr;
}
}
vector<tl_object_ptr<telegram_api::InputPeer>> MessagesManager::get_input_peers(const vector<DialogId> &dialog_ids,
AccessRights access_rights) const {
vector<tl_object_ptr<telegram_api::InputPeer>> input_peers;
@ -7113,7 +7153,7 @@ void MessagesManager::hide_dialog_action_bar(Dialog *d) {
return;
}
if (!d->can_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number &&
!d->can_report_location) {
!d->can_report_location && !d->can_unarchive && d->distance < 0) {
return;
}
@ -7122,6 +7162,8 @@ void MessagesManager::hide_dialog_action_bar(Dialog *d) {
d->can_block_user = false;
d->can_share_phone_number = false;
d->can_report_location = false;
d->can_unarchive = false;
d->distance = -1;
send_update_chat_action_bar(d);
}
@ -7151,7 +7193,7 @@ void MessagesManager::remove_dialog_action_bar(DialogId dialog_id, Promise<Unit>
}
if (!d->can_report_spam && !d->can_add_contact && !d->can_block_user && !d->can_share_phone_number &&
!d->can_report_location) {
!d->can_report_location && !d->can_unarchive && d->distance < 0) {
return promise.set_value(Unit());
}
@ -7354,9 +7396,12 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id,
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;
auto can_unarchive = (peer_settings->flags_ & telegram_api::peerSettings::AUTOARCHIVED_MASK) != 0;
auto distance =
(peer_settings->flags_ & telegram_api::peerSettings::GEO_DISTANCE_MASK) != 0 ? peer_settings->geo_distance_ : -1;
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) {
d->can_report_location == can_report_location && d->can_unarchive == can_unarchive && d->distance == distance) {
if (!d->know_action_bar || !d->know_can_report_spam) {
d->know_can_report_spam = true;
d->know_action_bar = true;
@ -7372,6 +7417,8 @@ void MessagesManager::on_get_peer_settings(DialogId dialog_id,
d->can_block_user = can_block_user;
d->can_share_phone_number = can_share_phone_number;
d->can_report_location = can_report_location;
d->can_unarchive = can_unarchive;
d->distance = distance < 0 ? -1 : distance;
fix_dialog_action_bar(d);
@ -7384,20 +7431,29 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) {
return;
}
auto dialog_type = d->dialog_id.get_type();
if (d->distance >= 0 && dialog_type != DialogType::User) {
LOG(ERROR) << "Receive distance " << d->distance << " to " << d->dialog_id;
d->distance = -1;
}
if (d->can_report_location) {
if (d->dialog_id.get_type() != DialogType::Channel) {
if (dialog_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) {
} else if (d->can_report_spam || d->can_add_contact || d->can_block_user || d->can_share_phone_number ||
d->can_unarchive) {
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_share_phone_number << "/" << d->can_report_location << "/" << d->can_unarchive;
d->can_report_spam = false;
d->can_add_contact = false;
d->can_block_user = false;
d->can_share_phone_number = false;
d->can_unarchive = false;
CHECK(d->distance == -1);
}
}
if (d->dialog_id.get_type() == DialogType::User) {
if (dialog_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);
@ -7405,6 +7461,7 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) {
bool is_deleted = td_->contacts_manager_->is_user_deleted(user_id);
if (is_me || is_blocked) {
d->can_report_spam = false;
d->can_unarchive = false;
}
if (is_me || is_blocked || is_deleted) {
d->can_share_phone_number = false;
@ -7414,23 +7471,27 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) {
d->can_add_contact = false;
}
}
if (d->folder_id != FolderId::archive()) {
d->can_unarchive = false;
}
if (d->can_share_phone_number) {
CHECK(!d->can_report_location);
if (d->dialog_id.get_type() != DialogType::User) {
if (dialog_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) {
} else if (d->can_report_spam || d->can_add_contact || d->can_block_user || d->can_unarchive || d->distance >= 0) {
LOG(ERROR) << "Receive action bar " << d->can_report_spam << "/" << d->can_add_contact << "/" << d->can_block_user
<< "/" << d->can_share_phone_number;
<< "/" << d->can_share_phone_number << "/" << d->can_unarchive << "/" << d->distance;
d->can_report_spam = false;
d->can_add_contact = false;
d->can_block_user = false;
d->can_unarchive = 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) {
if (dialog_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) {
@ -7443,7 +7504,7 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) {
if (d->can_add_contact) {
CHECK(!d->can_report_location);
CHECK(!d->can_share_phone_number);
if (d->dialog_id.get_type() != DialogType::User) {
if (dialog_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) {
@ -7451,8 +7512,15 @@ void MessagesManager::fix_dialog_action_bar(Dialog *d) {
<< d->can_block_user;
d->can_report_spam = false;
d->can_block_user = false;
d->can_unarchive = false;
}
}
if (!d->can_block_user) {
d->distance = -1;
}
if (!d->can_report_spam) {
d->can_unarchive = false;
}
}
void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string &parameters, bool is_dark,
@ -7820,26 +7888,57 @@ void MessagesManager::on_upload_dialog_photo(FileId file_id, tl_object_ptr<teleg
return;
}
Promise<Unit> promise = std::move(it->second.promise);
DialogId dialog_id = it->second.dialog_id;
double main_frame_timestamp = it->second.main_frame_timestamp;
bool is_animation = it->second.is_animation;
bool is_reupload = it->second.is_reupload;
Promise<Unit> promise = std::move(it->second.promise);
being_uploaded_dialog_photos_.erase(it);
tl_object_ptr<telegram_api::InputChatPhoto> input_chat_photo;
FileView file_view = td_->file_manager_->get_file_view(file_id);
CHECK(!file_view.is_encrypted());
if (input_file == nullptr && file_view.has_remote_location()) {
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;
return promise.set_error(Status::Error(400, "Can't use web photo as profile photo"));
}
if (is_reupload) {
return promise.set_error(Status::Error(400, "Failed to reupload the file"));
}
if (is_animation) {
CHECK(file_view.get_type() == FileType::Animation);
// delete file reference and forcely reupload the file
auto file_reference = FileManager::extract_file_reference(file_view.main_remote_location().as_input_document());
td_->file_manager_->delete_file_reference(file_id, file_reference);
upload_dialog_photo(dialog_id, file_id, is_animation, main_frame_timestamp, true, std::move(promise), {-1});
} else {
CHECK(file_view.get_type() == FileType::Photo);
auto input_photo = file_view.main_remote_location().as_input_photo();
auto input_chat_photo = make_tl_object<telegram_api::inputChatPhoto>(std::move(input_photo));
send_edit_dialog_photo_query(dialog_id, file_id, std::move(input_chat_photo), std::move(promise));
}
return;
}
CHECK(input_file != nullptr);
int32 flags = 0;
tl_object_ptr<telegram_api::InputFile> photo_input_file;
tl_object_ptr<telegram_api::InputFile> video_input_file;
if (is_animation) {
flags |= telegram_api::inputChatUploadedPhoto::VIDEO_MASK;
video_input_file = std::move(input_file);
if (main_frame_timestamp != 0.0) {
flags |= telegram_api::inputChatUploadedPhoto::VIDEO_START_TS_MASK;
}
auto input_photo = file_view.main_remote_location().as_input_photo();
input_chat_photo = make_tl_object<telegram_api::inputChatPhoto>(std::move(input_photo));
} else {
input_chat_photo = make_tl_object<telegram_api::inputChatUploadedPhoto>(std::move(input_file));
flags |= telegram_api::inputChatUploadedPhoto::FILE_MASK;
photo_input_file = std::move(input_file);
}
auto input_chat_photo = make_tl_object<telegram_api::inputChatUploadedPhoto>(
flags, std::move(photo_input_file), std::move(video_input_file), main_frame_timestamp);
send_edit_dialog_photo_query(dialog_id, file_id, std::move(input_chat_photo), std::move(promise));
}
@ -9903,7 +10002,7 @@ void MessagesManager::read_history_inbox(DialogId dialog_id, MessageId max_messa
}
if (max_message_id != MessageId() && max_message_id.is_yet_unsent()) {
LOG(ERROR) << "Try to update last read inbox message in " << dialog_id << " with " << max_message_id << " from "
LOG(ERROR) << "Tried to update last read inbox message in " << dialog_id << " with " << max_message_id << " from "
<< source;
return;
}
@ -9982,7 +10081,7 @@ void MessagesManager::read_history_outbox(DialogId dialog_id, MessageId max_mess
}
if (max_message_id.is_yet_unsent()) {
LOG(ERROR) << "Try to update last read outbox message with " << max_message_id;
LOG(ERROR) << "Tried to update last read outbox message with " << max_message_id;
return;
}
@ -10354,7 +10453,7 @@ void MessagesManager::set_dialog_max_unavailable_message_id(DialogId dialog_id,
}
if (max_unavailable_message_id.is_valid() && max_unavailable_message_id.is_yet_unsent()) {
LOG(ERROR) << "Try to update " << dialog_id << " last read outbox message with " << max_unavailable_message_id
LOG(ERROR) << "Tried to update " << dialog_id << " last read outbox message with " << max_unavailable_message_id
<< " from " << source;
return;
}
@ -17350,7 +17449,8 @@ td_api::object_ptr<td_api::ChatType> MessagesManager::get_chat_type_object(Dialo
}
}
td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_object(const Dialog *d) const {
td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_object(const Dialog *d,
bool hide_unarchive) 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());
@ -17361,12 +17461,12 @@ td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_o
if (user_d == nullptr) {
return nullptr;
}
return get_chat_action_bar_object(user_d);
return get_chat_action_bar_object(user_d, d->folder_id != FolderId::archive());
}
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<td_api::chatActionBarReportSpam>();
return td_api::make_object<td_api::chatActionBarReportSpam>(false);
}
return nullptr;
}
@ -17381,10 +17481,18 @@ td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_o
CHECK(!d->can_block_user && !d->can_add_contact && !d->can_report_spam);
return td_api::make_object<td_api::chatActionBarSharePhoneNumber>();
}
if (hide_unarchive) {
if (d->can_add_contact) {
return td_api::make_object<td_api::chatActionBarAddContact>();
} else {
return nullptr;
}
}
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<td_api::chatActionBarReportAddBlock>();
auto distance = d->hide_distance ? -1 : d->distance;
return td_api::make_object<td_api::chatActionBarReportAddBlock>(d->can_unarchive, distance);
}
if (d->can_add_contact) {
CHECK(d->dialog_id.get_type() == DialogType::User);
@ -17392,7 +17500,7 @@ td_api::object_ptr<td_api::ChatActionBar> MessagesManager::get_chat_action_bar_o
return td_api::make_object<td_api::chatActionBarAddContact>();
}
if (d->can_report_spam) {
return td_api::make_object<td_api::chatActionBarReportSpam>();
return td_api::make_object<td_api::chatActionBarReportSpam>(d->can_unarchive);
}
return nullptr;
}
@ -17462,7 +17570,7 @@ td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *
return make_tl_object<td_api::chat>(
d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_dialog_title(d->dialog_id),
get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)),
get_chat_photo_info_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)),
get_dialog_permissions(d->dialog_id).get_chat_permissions_object(),
get_message_object(d->dialog_id, get_message(d, d->last_message_id)), get_chat_positions_object(d),
d->is_marked_as_unread, get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users,
@ -18318,7 +18426,7 @@ std::pair<int32, vector<FullMessageId>> MessagesManager::search_call_messages(Me
auto filter_type = only_missed ? SearchMessagesFilter::MissedCall : SearchMessagesFilter::Call;
if (use_db && G()->parameters().use_message_db) {
// Try to use database
// try to use database
MessageId first_db_message_id =
calls_db_state_.first_calls_database_message_id_by_index[search_calls_filter_index(filter_type)];
int32 message_count = calls_db_state_.message_count_by_index[search_calls_filter_index(filter_type)];
@ -23690,6 +23798,7 @@ MessagesManager::MessageNotificationGroup MessagesManager::get_message_notificat
VLOG(notifications) << "Loaded " << r_value.ok() << " from database by " << group_id;
d = get_dialog_force(r_value.ok().dialog_id);
} else {
CHECK(r_value.error().message() == "Not found");
VLOG(notifications) << "Failed to load " << group_id << " from database";
}
G()->td_db()->get_dialog_db_sync()->commit_transaction().ensure();
@ -26032,8 +26141,7 @@ void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) {
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");
do_set_dialog_folder_id(d, folder_id);
}
return;
}
@ -26052,14 +26160,44 @@ void MessagesManager::set_dialog_folder_id(Dialog *d, FolderId folder_id) {
LOG_IF(ERROR, d->order != DEFAULT_ORDER) << d->dialog_id << " not found in the chat list";
}
d->folder_id = folder_id;
d->is_folder_id_inited = true;
do_set_dialog_folder_id(d, folder_id);
get_dialog_folder(d->folder_id)->ordered_dialogs_.insert(dialog_date);
update_dialog_lists(d, std::move(dialog_positions), true, false, "set_dialog_folder_id");
}
on_dialog_updated(d->dialog_id, "set_dialog_folder_id");
void MessagesManager::do_set_dialog_folder_id(Dialog *d, FolderId folder_id) {
CHECK(!td_->auth_manager_->is_bot());
if (d->folder_id == folder_id && d->is_folder_id_inited) {
return;
}
d->folder_id = folder_id;
d->is_folder_id_inited = true;
if (d->dialog_id.get_type() == DialogType::User) {
if (d->can_unarchive && folder_id != FolderId::archive()) {
d->can_unarchive = false;
d->can_report_spam = false;
d->can_block_user = false;
// keep d->can_add_contact
send_update_chat_action_bar(d);
}
} else if (d->dialog_id.get_type() == DialogType::SecretChat) {
// need to change action bar only for the secret chat and keep unarchive for the main chat
auto user_id = td_->contacts_manager_->get_secret_chat_user_id(d->dialog_id.get_secret_chat_id());
if (d->is_update_new_chat_sent && user_id.is_valid()) {
const Dialog *user_d = get_dialog(DialogId(user_id));
if (user_d != nullptr && user_d->can_unarchive && user_d->know_action_bar) {
send_closure(
G()->td(), &Td::send_update,
td_api::make_object<td_api::updateChatActionBar>(d->dialog_id.get(), get_chat_action_bar_object(d)));
}
}
}
on_dialog_updated(d->dialog_id, "do_set_dialog_folder_id");
}
void MessagesManager::on_update_dialog_filters() {
@ -26148,9 +26286,10 @@ void MessagesManager::on_dialog_bots_updated(DialogId dialog_id, vector<UserId>
void MessagesManager::on_dialog_photo_updated(DialogId dialog_id) {
auto d = get_dialog(dialog_id); // called from update_user, must not create the dialog
if (d != nullptr && d->is_update_new_chat_sent) {
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateChatPhoto>(
dialog_id.get(), get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(dialog_id))));
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateChatPhoto>(
dialog_id.get(), get_chat_photo_info_object(td_->file_manager_.get(), get_dialog_photo(dialog_id))));
}
}
@ -26183,6 +26322,8 @@ void MessagesManager::on_dialog_user_is_contact_updated(DialogId dialog_id, bool
if (d->can_block_user || d->can_add_contact) {
d->can_block_user = false;
d->can_add_contact = false;
// keep d->can_unarchive
d->distance = -1;
send_update_chat_action_bar(d);
}
} else {
@ -26210,11 +26351,14 @@ void MessagesManager::on_dialog_user_is_blocked_updated(DialogId dialog_id, bool
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) {
if (d->can_report_spam || d->can_share_phone_number || d->can_block_user || d->can_add_contact ||
d->can_unarchive || d->distance >= 0) {
d->can_report_spam = false;
d->can_share_phone_number = false;
d->can_block_user = false;
d->can_add_contact = false;
d->can_unarchive = false;
d->distance = -1;
send_update_chat_action_bar(d);
}
} else {
@ -26230,10 +26374,11 @@ void MessagesManager::on_dialog_user_is_deleted_updated(DialogId dialog_id, bool
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) {
if (d->can_share_phone_number || d->can_block_user || d->can_add_contact || d->distance >= 0) {
d->can_share_phone_number = false;
d->can_block_user = false;
d->can_add_contact = false;
d->distance = -1;
send_update_chat_action_bar(d);
}
} else {
@ -27132,7 +27277,7 @@ void MessagesManager::on_updated_dialog_folder_id(DialogId dialog_id, uint64 gen
}
}
void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptr<td_api::InputFile> &photo,
void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptr<td_api::InputChatPhoto> &input_photo,
Promise<Unit> &&promise) {
LOG(INFO) << "Receive setChatPhoto request to change photo of " << dialog_id;
@ -27166,30 +27311,62 @@ void MessagesManager::set_dialog_photo(DialogId dialog_id, const tl_object_ptr<t
UNREACHABLE();
}
auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Photo, photo, dialog_id, true, false);
const td_api::object_ptr<td_api::InputFile> *input_file = nullptr;
double main_frame_timestamp = 0.0;
bool is_animation = false;
if (input_photo != nullptr) {
switch (input_photo->get_id()) {
case td_api::inputChatPhotoPrevious::ID: {
auto photo = static_cast<const td_api::inputChatPhotoPrevious *>(input_photo.get());
auto file_id = td_->contacts_manager_->get_profile_photo_file_id(photo->chat_photo_id_);
if (!file_id.is_valid()) {
return promise.set_error(Status::Error(400, "Unknown profile photo ID specified"));
}
auto file_view = td_->file_manager_->get_file_view(file_id);
auto input_chat_photo =
make_tl_object<telegram_api::inputChatPhoto>(file_view.main_remote_location().as_input_photo());
send_edit_dialog_photo_query(dialog_id, file_id, std::move(input_chat_photo), std::move(promise));
return;
}
case td_api::inputChatPhotoStatic::ID: {
auto photo = static_cast<const td_api::inputChatPhotoStatic *>(input_photo.get());
input_file = &photo->photo_;
break;
}
case td_api::inputChatPhotoAnimation::ID: {
auto photo = static_cast<const td_api::inputChatPhotoAnimation *>(input_photo.get());
input_file = &photo->animation_;
main_frame_timestamp = photo->main_frame_timestamp_;
is_animation = true;
break;
}
default:
UNREACHABLE();
break;
}
}
const double MAX_ANIMATION_DURATION = 10.0;
if (main_frame_timestamp < 0.0 || main_frame_timestamp > MAX_ANIMATION_DURATION) {
return promise.set_error(Status::Error(400, "Wrong main frame timestamp specified"));
}
auto file_type = is_animation ? FileType::Animation : FileType::Photo;
auto r_file_id = td_->file_manager_->get_input_file_id(file_type, *input_file, dialog_id, true, false);
if (r_file_id.is_error()) {
return promise.set_error(Status::Error(7, r_file_id.error().message()));
// TODO promise.set_error(std::move(status));
return promise.set_error(Status::Error(400, r_file_id.error().message()));
}
FileId file_id = r_file_id.ok();
if (!file_id.is_valid()) {
send_edit_dialog_photo_query(dialog_id, FileId(), make_tl_object<telegram_api::inputChatPhotoEmpty>(),
std::move(promise));
return;
}
FileView file_view = td_->file_manager_->get_file_view(file_id);
CHECK(!file_view.is_encrypted());
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.main_remote_location().as_input_photo();
send_edit_dialog_photo_query(
dialog_id, file_id, make_tl_object<telegram_api::inputChatPhoto>(std::move(input_photo)), std::move(promise));
return;
}
// need to upload file first
upload_dialog_photo(dialog_id, td_->file_manager_->dup_file_id(file_id), std::move(promise));
upload_dialog_photo(dialog_id, td_->file_manager_->dup_file_id(file_id), is_animation, main_frame_timestamp, false,
std::move(promise));
}
void MessagesManager::send_edit_dialog_photo_query(DialogId dialog_id, FileId file_id,
@ -27199,12 +27376,16 @@ void MessagesManager::send_edit_dialog_photo_query(DialogId dialog_id, FileId fi
td_->create_handler<EditDialogPhotoQuery>(std::move(promise))->send(dialog_id, file_id, std::move(input_chat_photo));
}
void MessagesManager::upload_dialog_photo(DialogId dialog_id, FileId file_id, Promise<Unit> &&promise) {
void MessagesManager::upload_dialog_photo(DialogId dialog_id, FileId file_id, bool is_animation,
double main_frame_timestamp, bool is_reupload, Promise<Unit> &&promise,
vector<int> bad_parts) {
CHECK(file_id.is_valid());
LOG(INFO) << "Ask to upload chat photo " << file_id;
CHECK(being_uploaded_dialog_photos_.find(file_id) == being_uploaded_dialog_photos_.end());
being_uploaded_dialog_photos_[file_id] = {std::move(promise), dialog_id};
td_->file_manager_->upload(file_id, upload_dialog_photo_callback_, 32, 0);
being_uploaded_dialog_photos_.emplace(
file_id, UploadedDialogPhotoInfo{dialog_id, main_frame_timestamp, is_animation, is_reupload, std::move(promise)});
// TODO use force_reupload if is_reupload
td_->file_manager_->resume_upload(file_id, std::move(bad_parts), upload_dialog_photo_callback_, 32, 0);
}
void MessagesManager::set_dialog_title(DialogId dialog_id, const string &title, Promise<Unit> &&promise) {
@ -27811,6 +27992,10 @@ tl_object_ptr<td_api::ChatEventAction> MessagesManager::get_chat_event_action_ob
case telegram_api::channelAdminLogEventActionParticipantInvite::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantInvite>(action_ptr);
auto member = td_->contacts_manager_->get_dialog_participant(channel_id, std::move(action->participant_));
if (!member.is_valid()) {
LOG(ERROR) << "Wrong invite: " << member;
return nullptr;
}
return make_tl_object<td_api::chatEventMemberInvited>(
td_->contacts_manager_->get_user_id_object(member.user_id, "chatEventMemberInvited"),
member.status.get_chat_member_status_object());
@ -27824,6 +28009,10 @@ tl_object_ptr<td_api::ChatEventAction> MessagesManager::get_chat_event_action_ob
LOG(ERROR) << old_member.user_id << " VS " << new_member.user_id;
return nullptr;
}
if (!old_member.is_valid() || !new_member.is_valid()) {
LOG(ERROR) << "Wrong restrict: " << old_member << " -> " << new_member;
return nullptr;
}
return make_tl_object<td_api::chatEventMemberRestricted>(
td_->contacts_manager_->get_user_id_object(old_member.user_id, "chatEventMemberRestricted"),
old_member.status.get_chat_member_status_object(), new_member.status.get_chat_member_status_object());
@ -27837,6 +28026,10 @@ tl_object_ptr<td_api::ChatEventAction> MessagesManager::get_chat_event_action_ob
LOG(ERROR) << old_member.user_id << " VS " << new_member.user_id;
return nullptr;
}
if (!old_member.is_valid() || !new_member.is_valid()) {
LOG(ERROR) << "Wrong edit administrator: " << old_member << " -> " << new_member;
return nullptr;
}
return make_tl_object<td_api::chatEventMemberPromoted>(
td_->contacts_manager_->get_user_id_object(old_member.user_id, "chatEventMemberPromoted"),
old_member.status.get_chat_member_status_object(), new_member.status.get_chat_member_status_object());
@ -27861,8 +28054,8 @@ tl_object_ptr<td_api::ChatEventAction> MessagesManager::get_chat_event_action_ob
auto file_manager = td_->file_manager_.get();
auto old_photo = get_photo(file_manager, std::move(action->prev_photo_), DialogId(channel_id));
auto new_photo = get_photo(file_manager, std::move(action->new_photo_), DialogId(channel_id));
return make_tl_object<td_api::chatEventPhotoChanged>(get_photo_object(file_manager, &old_photo),
get_photo_object(file_manager, &new_photo));
return make_tl_object<td_api::chatEventPhotoChanged>(get_chat_photo_object(file_manager, old_photo),
get_chat_photo_object(file_manager, new_photo));
}
case telegram_api::channelAdminLogEventActionDefaultBannedRights::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionDefaultBannedRights>(action_ptr);
@ -28873,6 +29066,31 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
update_used_hashtags(dialog_id, m);
update_top_dialogs(dialog_id, m);
cancel_user_dialog_action(dialog_id, m);
try_hide_distance(dialog_id, m);
if (d->messages == nullptr && !m->is_outgoing && dialog_id != get_my_dialog_id()) {
switch (dialog_id.get_type()) {
case DialogType::User:
td_->contacts_manager_->invalidate_user_full(dialog_id.get_user_id());
td_->contacts_manager_->reload_user_full(dialog_id.get_user_id());
break;
case DialogType::Chat:
case DialogType::Channel:
// nothing to do
break;
case DialogType::SecretChat: {
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_->invalidate_user_full(user_id);
td_->contacts_manager_->reload_user_full(user_id);
}
break;
}
case DialogType::None:
default:
UNREACHABLE();
}
}
}
Message *result_message = treap_insert_message(&d->messages, std::move(message));
@ -29034,6 +29252,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo
if (from_update) {
update_sent_message_contents(dialog_id, m);
update_used_hashtags(dialog_id, m);
try_hide_distance(dialog_id, m);
}
if (m->message_id.is_scheduled_server()) {
@ -29747,6 +29966,7 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
case FileType::Animation:
case FileType::Audio:
case FileType::Document:
case FileType::DocumentAsFile:
case FileType::Sticker:
case FileType::Video:
case FileType::VideoNote:
@ -29990,8 +30210,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
d->last_new_message_id = MessageId::min();
}
if (!d->notification_settings.is_secret_chat_show_preview_fixed &&
d->dialog_id.get_type() == DialogType::SecretChat) {
if (!d->notification_settings.is_secret_chat_show_preview_fixed) {
d->notification_settings.use_default_show_preview = true;
d->notification_settings.show_preview = false;
d->notification_settings.is_secret_chat_show_preview_fixed = true;
@ -30003,7 +30222,10 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
d->is_last_read_inbox_message_id_inited = true;
d->is_last_read_outbox_message_id_inited = true;
d->is_pinned_message_id_inited = true;
d->is_folder_id_inited = true;
if (!d->is_folder_id_inited && !td_->auth_manager_->is_bot()) {
do_set_dialog_folder_id(
d.get(), td_->contacts_manager_->get_secret_chat_initial_folder_id(dialog_id.get_secret_chat_id()));
}
break;
case DialogType::None:
default:
@ -30701,9 +30923,7 @@ void MessagesManager::update_dialog_lists(
if (d->folder_id != FolderId::main()) {
LOG(INFO) << "Change folder of " << dialog_id << " to " << FolderId::main();
d->folder_id = FolderId::main();
d->is_folder_id_inited = true;
on_dialog_updated(dialog_id, "update_dialog_lists");
do_set_dialog_folder_id(d, FolderId::main());
}
}
@ -32080,6 +32300,42 @@ void MessagesManager::update_top_dialogs(DialogId dialog_id, const Message *m) {
}
}
void MessagesManager::try_hide_distance(DialogId dialog_id, const Message *m) {
CHECK(m != nullptr);
if (!m->is_outgoing && dialog_id != get_my_dialog_id()) {
return;
}
Dialog *d = nullptr;
switch (dialog_id.get_type()) {
case DialogType::User:
d = get_dialog(dialog_id);
break;
case DialogType::Chat:
case DialogType::Channel:
break;
case DialogType::SecretChat: {
auto user_id = td_->contacts_manager_->get_secret_chat_user_id(dialog_id.get_secret_chat_id());
if (user_id.is_valid()) {
d = get_dialog_force(DialogId(user_id));
}
break;
}
default:
UNREACHABLE();
}
if (d == nullptr || d->hide_distance) {
return;
}
d->hide_distance = true;
on_dialog_updated(dialog_id, "try_hide_distance");
if (d->distance != -1) {
send_update_chat_action_bar(d);
}
}
MessagesManager::Message *MessagesManager::continue_send_message(DialogId dialog_id, unique_ptr<Message> &&m,
uint64 logevent_id) {
CHECK(logevent_id != 0);

View File

@ -194,6 +194,8 @@ class MessagesManager : public Actor {
tl_object_ptr<telegram_api::InputPeer> get_input_peer(DialogId dialog_id, AccessRights access_rights) const;
static tl_object_ptr<telegram_api::InputPeer> get_input_peer_force(DialogId dialog_id);
vector<tl_object_ptr<telegram_api::InputPeer>> get_input_peers(const vector<DialogId> &dialog_ids,
AccessRights access_rights) const;
@ -453,7 +455,8 @@ class MessagesManager : public Actor {
void add_dialog_to_list(DialogId dialog_id, DialogListId dialog_list_id, Promise<Unit> &&promise);
void set_dialog_photo(DialogId dialog_id, const tl_object_ptr<td_api::InputFile> &photo, Promise<Unit> &&promise);
void set_dialog_photo(DialogId dialog_id, const tl_object_ptr<td_api::InputChatPhoto> &input_photo,
Promise<Unit> &&promise);
void set_dialog_title(DialogId dialog_id, const string &title, Promise<Unit> &&promise);
@ -830,7 +833,8 @@ class MessagesManager : public Actor {
void remove_message_notifications(DialogId dialog_id, NotificationGroupId group_id,
NotificationId max_notification_id, MessageId max_message_id);
void upload_dialog_photo(DialogId dialog_id, FileId file_id, Promise<Unit> &&promise);
void upload_dialog_photo(DialogId dialog_id, FileId file_id, bool is_animation, double main_frame_timestamp,
bool is_reupload, Promise<Unit> &&promise, vector<int> bad_parts = {});
void on_binlog_events(vector<BinlogEvent> &&events);
@ -1113,6 +1117,8 @@ class MessagesManager : public Actor {
NotificationId new_secret_chat_notification_id; // secret chats only
MessageId pinned_message_notification_message_id;
int32 distance = -1; // distance to the peer
bool has_contact_registered_message = false;
bool is_last_message_deleted_locally = false;
@ -1124,6 +1130,8 @@ class MessagesManager : public Actor {
bool can_block_user = false;
bool can_share_phone_number = false;
bool can_report_location = false;
bool can_unarchive = false;
bool hide_distance = false;
bool is_opened = false;
@ -2148,6 +2156,8 @@ class MessagesManager : public Actor {
void set_dialog_folder_id(Dialog *d, FolderId folder_id);
void do_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);
@ -2222,7 +2232,8 @@ class MessagesManager : public Actor {
td_api::object_ptr<td_api::ChatType> get_chat_type_object(DialogId dialog_id) const;
td_api::object_ptr<td_api::ChatActionBar> get_chat_action_bar_object(const Dialog *d) const;
td_api::object_ptr<td_api::ChatActionBar> get_chat_action_bar_object(const Dialog *d,
bool hide_unarchive = false) const;
td_api::object_ptr<td_api::chat> get_chat_object(const Dialog *d, int64 real_order = DEFAULT_ORDER) const;
@ -2671,6 +2682,8 @@ class MessagesManager : public Actor {
void update_top_dialogs(DialogId dialog_id, const Message *m);
void try_hide_distance(DialogId dialog_id, const Message *m);
string get_search_text(const Message *m) const;
unique_ptr<Message> parse_message(DialogId dialog_id, const BufferSlice &value, bool is_scheduled);
@ -2815,8 +2828,20 @@ class MessagesManager : public Actor {
const char *debug_add_message_to_dialog_fail_reason_ = "";
struct UploadedDialogPhotoInfo {
Promise<Unit> promise;
DialogId dialog_id;
double main_frame_timestamp;
bool is_animation;
bool is_reupload;
Promise<Unit> promise;
UploadedDialogPhotoInfo(DialogId dialog_id, double main_frame_timestamp, bool is_animation, bool is_reupload,
Promise<Unit> promise)
: dialog_id(dialog_id)
, main_frame_timestamp(main_frame_timestamp)
, is_animation(is_animation)
, is_reupload(is_reupload)
, promise(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedDialogPhotoInfo, FileIdHash> being_uploaded_dialog_photos_; // file_id -> ...

View File

@ -3262,7 +3262,6 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
}
Photo attached_photo;
attached_photo.id = -2;
Document attached_document;
if (has_json_object_field(custom, "attachb64")) {
TRY_RESULT(attachb64, get_json_object_string_field(custom, "attachb64", false));
@ -3430,7 +3429,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
bool has_sender = sender_user_id_.is_valid();
bool has_sender_name = !sender_name_.empty();
bool has_arg = !arg_.empty();
bool has_photo = photo_.id != -2;
bool has_photo = !photo_.is_empty();
bool has_document = !document_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(contains_mention_);
@ -3514,8 +3513,6 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
}
if (has_photo) {
td::parse(photo_, parser);
} else {
photo_.id = -2;
}
if (has_document) {
td::parse(document_, parser);
@ -3637,7 +3634,7 @@ class NotificationManager::EditMessagePushNotificationLogEvent {
void store(StorerT &storer) const {
bool has_message_id = message_id_.is_valid();
bool has_arg = !arg_.empty();
bool has_photo = photo_.id != -2;
bool has_photo = !photo_.is_empty();
bool has_document = !document_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_message_id);
@ -3685,8 +3682,6 @@ class NotificationManager::EditMessagePushNotificationLogEvent {
}
if (has_photo) {
td::parse(photo_, parser);
} else {
photo_.id = -2;
}
if (has_document) {
td::parse(document_, parser);

View File

@ -255,8 +255,8 @@ class NotificationTypePushMessage : public NotificationType {
case 'P':
if (key == "MESSAGE_PHOTO") {
auto file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
return td_api::make_object<td_api::pushMessageContentPhoto>(get_photo_object(file_manager, &photo), arg,
false, is_pinned);
return td_api::make_object<td_api::pushMessageContentPhoto>(get_photo_object(file_manager, photo), arg, false,
is_pinned);
}
if (key == "MESSAGE_PHOTOS") {
return td_api::make_object<td_api::pushMessageContentMediaAlbum>(to_integer<int32>(arg), true, false);

View File

@ -26,7 +26,7 @@
namespace td {
tl_object_ptr<td_api::temporaryPasswordState> TempPasswordState::as_td_api() const {
tl_object_ptr<td_api::temporaryPasswordState> TempPasswordState::get_temporary_password_state_object() const {
if (!has_temp_password || valid_until <= G()->unix_time()) {
return make_tl_object<td_api::temporaryPasswordState>(false, 0);
}
@ -234,7 +234,7 @@ void PasswordManager::do_get_secure_secret(bool allow_recursive, string password
}
void PasswordManager::get_temp_password_state(Promise<TempState> promise) /*const*/ {
promise.set_value(temp_password_state_.as_td_api());
promise.set_value(temp_password_state_.get_temporary_password_state_object());
}
TempPasswordState PasswordManager::get_temp_password_state_sync() {
@ -298,7 +298,7 @@ void PasswordManager::on_finish_create_temp_password(Result<TempPasswordState> r
}
temp_password_state_ = result.move_as_ok();
G()->td_db()->get_binlog_pmc()->set("temp_password", log_event_store(temp_password_state_).as_slice().str());
create_temp_password_promise_.set_value(temp_password_state_.as_td_api());
create_temp_password_promise_.set_value(temp_password_state_.get_temporary_password_state_object());
}
void PasswordManager::get_full_state(string password, Promise<PasswordFullState> promise) {
@ -625,7 +625,7 @@ void PasswordManager::get_state(Promise<State> promise) {
if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error());
}
promise.set_value(r_state.move_as_ok().as_td_api());
promise.set_value(r_state.move_as_ok().get_password_state_object());
}));
}

View File

@ -30,7 +30,7 @@ struct TempPasswordState {
string temp_password;
int32 valid_until = 0; // unix_time
tl_object_ptr<td_api::temporaryPasswordState> as_td_api() const;
tl_object_ptr<td_api::temporaryPasswordState> get_temporary_password_state_object() const;
template <class StorerT>
void store(StorerT &storer) const {
@ -116,7 +116,7 @@ class PasswordManager : public NetQueryCallback {
string new_secure_salt;
State as_td_api() const {
State get_password_state_object() const {
td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo> code_info;
if (!unconfirmed_recovery_email_address_pattern.empty()) {
code_info = td_api::make_object<td_api::emailAddressAuthenticationCodeInfo>(

View File

@ -23,6 +23,7 @@
#include "td/utils/Random.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace td {
@ -164,6 +165,7 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64
auto profile_photo = move_tl_object_as<telegram_api::userProfilePhoto>(profile_photo_ptr);
auto dc_id = DcId::create(profile_photo->dc_id_);
result.has_animation = (profile_photo->flags_ & telegram_api::userProfilePhoto::HAS_VIDEO_MASK) != 0;
result.id = profile_photo->photo_id_;
result.small_file_id =
register_photo(file_manager, {DialogId(user_id), user_access_hash, false}, result.id, 0, "",
@ -182,17 +184,31 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64
}
tl_object_ptr<td_api::profilePhoto> get_profile_photo_object(FileManager *file_manager,
const ProfilePhoto *profile_photo) {
if (profile_photo == nullptr || !profile_photo->small_file_id.is_valid()) {
const ProfilePhoto &profile_photo) {
if (!profile_photo.small_file_id.is_valid()) {
return nullptr;
}
return td_api::make_object<td_api::profilePhoto>(profile_photo->id,
file_manager->get_file_object(profile_photo->small_file_id),
file_manager->get_file_object(profile_photo->big_file_id));
return td_api::make_object<td_api::profilePhoto>(
profile_photo.id, file_manager->get_file_object(profile_photo.small_file_id),
file_manager->get_file_object(profile_photo.big_file_id), profile_photo.has_animation);
}
bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs) {
return lhs.small_file_id == rhs.small_file_id || lhs.big_file_id == rhs.big_file_id;
bool location_differs = lhs.small_file_id != rhs.small_file_id || lhs.big_file_id != rhs.big_file_id;
bool id_differs;
if (lhs.id == -1 && rhs.id == -1) {
// group chat photo
id_differs = location_differs;
} else {
id_differs = lhs.id != rhs.id;
}
if (location_differs) {
LOG_IF(ERROR, !id_differs) << "Photo " << lhs.id << " location has changed. First profilePhoto: " << lhs
<< ", second profilePhoto: " << rhs;
return false;
}
return lhs.has_animation == rhs.has_animation && !id_differs;
}
bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs) {
@ -201,7 +217,8 @@ bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs) {
StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &profile_photo) {
return string_builder << "<id = " << profile_photo.id << ", small_file_id = " << profile_photo.small_file_id
<< ", big_file_id = " << profile_photo.big_file_id << ">";
<< ", big_file_id = " << profile_photo.big_file_id
<< ", has_animation = " << profile_photo.has_animation << ">";
}
DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash,
@ -216,6 +233,7 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6
auto chat_photo = move_tl_object_as<telegram_api::chatPhoto>(chat_photo_ptr);
auto dc_id = DcId::create(chat_photo->dc_id_);
result.has_animation = (chat_photo->flags_ & telegram_api::chatPhoto::HAS_VIDEO_MASK) != 0;
result.small_file_id =
register_photo(file_manager, {dialog_id, dialog_access_hash, false}, 0, 0, "",
std::move(chat_photo->photo_small_), DialogId(), 0, dc_id, PhotoFormat::Jpeg);
@ -232,12 +250,14 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6
return result;
}
tl_object_ptr<td_api::chatPhoto> get_chat_photo_object(FileManager *file_manager, const DialogPhoto *dialog_photo) {
tl_object_ptr<td_api::chatPhotoInfo> get_chat_photo_info_object(FileManager *file_manager,
const DialogPhoto *dialog_photo) {
if (dialog_photo == nullptr || !dialog_photo->small_file_id.is_valid()) {
return nullptr;
}
return td_api::make_object<td_api::chatPhoto>(file_manager->get_file_object(dialog_photo->small_file_id),
file_manager->get_file_object(dialog_photo->big_file_id));
return td_api::make_object<td_api::chatPhotoInfo>(file_manager->get_file_object(dialog_photo->small_file_id),
file_manager->get_file_object(dialog_photo->big_file_id),
dialog_photo->has_animation);
}
vector<FileId> dialog_photo_get_file_ids(const DialogPhoto &dialog_photo) {
@ -251,9 +271,9 @@ vector<FileId> dialog_photo_get_file_ids(const DialogPhoto &dialog_photo) {
return result;
}
DialogPhoto as_dialog_photo(const Photo &photo) {
DialogPhoto as_fake_dialog_photo(const Photo &photo) {
DialogPhoto result;
if (photo.id != -2) {
if (!photo.is_empty()) {
for (auto &size : photo.photos) {
if (size.type == 'a') {
result.small_file_id = size.file_id;
@ -261,6 +281,7 @@ DialogPhoto as_dialog_photo(const Photo &photo) {
result.big_file_id = size.file_id;
}
}
result.has_animation = !photo.animations.empty();
if (!result.small_file_id.is_valid() || !result.big_file_id.is_valid()) {
LOG(ERROR) << "Failed to convert " << photo << " to chat photo";
return DialogPhoto();
@ -269,8 +290,34 @@ DialogPhoto as_dialog_photo(const Photo &photo) {
return result;
}
ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo) {
ProfilePhoto result;
static_cast<DialogPhoto &>(result) = as_fake_dialog_photo(photo);
if (!result.small_file_id.is_valid()) {
return result;
}
auto reregister_photo = [&](bool is_big, FileId file_id) {
auto file_view = file_manager->get_file_view(file_id);
CHECK(file_view.has_remote_location());
auto remote = file_view.remote_location();
CHECK(remote.is_photo());
CHECK(!remote.is_web());
remote.set_source({DialogId(user_id), user_access_hash, is_big});
return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(),
file_view.size(), file_view.expected_size(), file_view.remote_name());
};
result.id = photo.id.get();
result.small_file_id = reregister_photo(false, result.small_file_id);
result.big_file_id = reregister_photo(true, result.big_file_id);
return result;
}
bool operator==(const DialogPhoto &lhs, const DialogPhoto &rhs) {
return lhs.small_file_id == rhs.small_file_id && lhs.big_file_id == rhs.big_file_id;
return lhs.small_file_id == rhs.small_file_id && lhs.big_file_id == rhs.big_file_id &&
lhs.has_animation == rhs.has_animation;
}
bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs) {
@ -279,7 +326,8 @@ bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs) {
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo) {
return string_builder << "<small_file_id = " << dialog_photo.small_file_id
<< ", big_file_id = " << dialog_photo.big_file_id << ">";
<< ", big_file_id = " << dialog_photo.big_file_id
<< ", has_animation = " << dialog_photo.has_animation << ">";
}
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
@ -372,20 +420,23 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
return std::move(res);
}
PhotoSize get_video_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
std::string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size) {
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
std::string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size) {
if (size == nullptr) {
return {};
}
CHECK(size != nullptr);
PhotoSize res;
if (size->type_ != "v") {
AnimationSize res;
if (size->type_ != "v" && size->type_ != "u") {
LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size);
}
res.type = static_cast<uint8>('v');
res.type = static_cast<uint8>(size->type_[0]);
res.dimensions = get_dimensions(size->w_, size->h_);
res.size = size->size_;
if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) {
res.main_frame_timestamp = size->video_start_ts_;
}
if (source.get_type() == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
@ -473,7 +524,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
}
PhotoSize s;
s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'u'));
s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'n'));
s.dimensions = dimensions;
s.size = size;
s.file_id = file_id;
@ -554,6 +605,31 @@ StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_
<< ", size = " << photo_size.size << ", file_id = " << photo_size.file_id << "}";
}
static tl_object_ptr<td_api::animatedChatPhoto> get_animated_chat_photo_object(FileManager *file_manager,
const AnimationSize *animation_size) {
if (animation_size == nullptr || !animation_size->file_id.is_valid()) {
return nullptr;
}
return td_api::make_object<td_api::animatedChatPhoto>(animation_size->dimensions.width,
file_manager->get_file_object(animation_size->file_id),
animation_size->main_frame_timestamp);
}
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs) {
return static_cast<const PhotoSize &>(lhs) == static_cast<const PhotoSize &>(rhs) &&
fabs(lhs.main_frame_timestamp - rhs.main_frame_timestamp) < 1e-3;
}
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size) {
return string_builder << static_cast<const PhotoSize &>(animation_size) << " from "
<< animation_size.main_frame_timestamp;
}
Photo get_encrypted_file_photo(FileManager *file_manager, tl_object_ptr<telegram_api::encryptedFile> &&file,
tl_object_ptr<secret_api::decryptedMessageMediaPhoto> &&photo,
DialogId owner_dialog_id) {
@ -564,6 +640,7 @@ Photo get_encrypted_file_photo(FileManager *file_manager, tl_object_ptr<telegram
file_manager->set_encryption_key(file_id, FileEncryptionKey{photo->key_.as_slice(), photo->iv_.as_slice()});
Photo res;
res.id = 0;
res.date = 0;
if (!photo->thumb_.empty()) {
@ -583,33 +660,33 @@ Photo get_encrypted_file_photo(FileManager *file_manager, tl_object_ptr<telegram
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::Photo> &&photo, DialogId owner_dialog_id) {
if (photo == nullptr || photo->get_id() == telegram_api::photoEmpty::ID) {
Photo result;
result.id = -2;
return result;
return Photo();
}
CHECK(photo->get_id() == telegram_api::photo::ID);
return get_photo(file_manager, move_tl_object_as<telegram_api::photo>(photo), owner_dialog_id);
}
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&photo, DialogId owner_dialog_id) {
CHECK(photo != nullptr);
Photo res;
res.id = photo->id_;
res.date = photo->date_;
res.has_stickers = (photo->flags_ & telegram_api::photo::HAS_STICKERS_MASK) != 0;
if (res.id == -2) {
LOG(ERROR) << "Receive photo with id " << res.id;
if (res.is_empty()) {
LOG(ERROR) << "Receive photo with id " << res.id.get();
res.id = -3;
}
DcId dc_id = DcId::create(photo->dc_id_);
for (auto &size_ptr : photo->sizes_) {
auto photo_size = get_photo_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_,
photo->file_reference_.as_slice().str(), DcId::create(photo->dc_id_),
owner_dialog_id, std::move(size_ptr), PhotoFormat::Jpeg);
photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id,
std::move(size_ptr), PhotoFormat::Jpeg);
if (photo_size.get_offset() == 0) {
PhotoSize &size = photo_size.get<0>();
if (size.type == 0 || size.type == 't' || size.type == 'i') {
if (size.type == 0 || size.type == 't' || size.type == 'i' || size.type == 'u' || size.type == 'v') {
LOG(ERROR) << "Skip unallowed photo size " << size;
continue;
}
@ -619,6 +696,15 @@ Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&
}
}
for (auto &size_ptr : photo->video_sizes_) {
auto animation =
get_animation_size(file_manager, {FileType::Photo, 0}, photo->id_, photo->access_hash_,
photo->file_reference_.as_slice().str(), dc_id, owner_dialog_id, std::move(size_ptr));
if (animation.type != 0 && animation.dimensions.width == animation.dimensions.height) {
res.animations.push_back(std::move(animation));
}
}
return res;
}
@ -626,31 +712,31 @@ Photo get_web_document_photo(FileManager *file_manager, tl_object_ptr<telegram_a
DialogId owner_dialog_id) {
PhotoSize s = get_web_document_photo_size(file_manager, FileType::Photo, owner_dialog_id, std::move(web_document));
Photo photo;
if (!s.file_id.is_valid() || s.type == 'v' || s.type == 'g') {
photo.id = -2;
} else {
if (s.file_id.is_valid() && s.type != 'v' && s.type != 'g') {
photo.id = 0;
photo.photos.push_back(s);
}
return photo;
}
tl_object_ptr<td_api::photo> get_photo_object(FileManager *file_manager, const Photo *photo) {
if (photo == nullptr || photo->id == -2) {
tl_object_ptr<td_api::photo> get_photo_object(FileManager *file_manager, const Photo &photo) {
if (photo.is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::photo>(photo->has_stickers, get_minithumbnail_object(photo->minithumbnail),
get_photo_sizes_object(file_manager, photo->photos));
return td_api::make_object<td_api::photo>(photo.has_stickers, get_minithumbnail_object(photo.minithumbnail),
get_photo_sizes_object(file_manager, photo.photos));
}
tl_object_ptr<td_api::userProfilePhoto> get_user_profile_photo_object(FileManager *file_manager, const Photo *photo) {
if (photo == nullptr || photo->id == -2) {
tl_object_ptr<td_api::chatPhoto> get_chat_photo_object(FileManager *file_manager, const Photo &photo) {
if (photo.is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::userProfilePhoto>(photo->id, photo->date,
get_photo_sizes_object(file_manager, photo->photos));
const AnimationSize *animation = photo.animations.empty() ? nullptr : &photo.animations.back();
return td_api::make_object<td_api::chatPhoto>(
photo.id.get(), photo.date, get_minithumbnail_object(photo.minithumbnail),
get_photo_sizes_object(file_manager, photo.photos), get_animated_chat_photo_object(file_manager, animation));
}
void photo_delete_thumbnail(Photo &photo) {
@ -788,11 +874,16 @@ SecretInputMedia photo_get_secret_input_media(FileManager *file_manager, const P
}
vector<FileId> photo_get_file_ids(const Photo &photo) {
return transform(photo.photos, [](auto &size) { return size.file_id; });
auto result = transform(photo.photos, [](auto &size) { return size.file_id; });
if (!photo.animations.empty()) {
// photo file IDs must be first
append(result, transform(photo.animations, [](auto &size) { return size.file_id; }));
}
return result;
}
bool operator==(const Photo &lhs, const Photo &rhs) {
return lhs.id == rhs.id && lhs.photos == rhs.photos;
return lhs.id.get() == rhs.id.get() && lhs.photos == rhs.photos && lhs.animations == rhs.animations;
}
bool operator!=(const Photo &lhs, const Photo &rhs) {
@ -800,7 +891,11 @@ bool operator!=(const Photo &lhs, const Photo &rhs) {
}
StringBuilder &operator<<(StringBuilder &string_builder, const Photo &photo) {
return string_builder << "[id = " << photo.id << ", photos = " << format::as_array(photo.photos) << "]";
string_builder << "[id = " << photo.id.get() << ", photos = " << format::as_array(photo.photos);
if (!photo.animations.empty()) {
string_builder << ", animations = " << format::as_array(photo.animations);
}
return string_builder << "]";
}
static tl_object_ptr<telegram_api::fileLocationToBeDeprecated> copy_location(
@ -849,8 +944,12 @@ tl_object_ptr<telegram_api::userProfilePhoto> convert_photo_to_profile_photo(
if (photo_small == nullptr || photo_big == nullptr) {
return nullptr;
}
return make_tl_object<telegram_api::userProfilePhoto>(photo->id_, std::move(photo_small), std::move(photo_big),
photo->dc_id_);
int32 flags = 0;
if (!photo->video_sizes_.empty()) {
flags |= telegram_api::userProfilePhoto::HAS_VIDEO_MASK;
}
return make_tl_object<telegram_api::userProfilePhoto>(flags, false /*ignored*/, photo->id_, std::move(photo_small),
std::move(photo_big), photo->dc_id_);
}
} // namespace td

View File

@ -20,6 +20,7 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Variant.h"
@ -35,6 +36,7 @@ struct Dimensions {
struct DialogPhoto {
FileId small_file_id;
FileId big_file_id;
bool has_animation = false;
};
struct ProfilePhoto : public DialogPhoto {
@ -48,14 +50,24 @@ struct PhotoSize {
FileId file_id;
};
struct AnimationSize : public PhotoSize {
double main_frame_timestamp = 0.0;
};
struct Photo {
int64 id = 0;
MovableValue<int64, -2> id;
int32 date = 0;
string minithumbnail;
vector<PhotoSize> photos;
vector<AnimationSize> animations;
bool has_stickers = false;
vector<FileId> sticker_file_ids;
bool is_empty() const {
return id.get() == -2;
}
};
Dimensions get_dimensions(int32 width, int32 height);
@ -70,7 +82,7 @@ td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string
ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash,
tl_object_ptr<telegram_api::UserProfilePhoto> &&profile_photo_ptr);
tl_object_ptr<td_api::profilePhoto> get_profile_photo_object(FileManager *file_manager,
const ProfilePhoto *profile_photo);
const ProfilePhoto &profile_photo);
bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs);
bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs);
@ -79,9 +91,12 @@ StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &pro
DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash,
tl_object_ptr<telegram_api::ChatPhoto> &&chat_photo_ptr);
tl_object_ptr<td_api::chatPhoto> get_chat_photo_object(FileManager *file_manager, const DialogPhoto *dialog_photo);
tl_object_ptr<td_api::chatPhotoInfo> get_chat_photo_info_object(FileManager *file_manager,
const DialogPhoto *dialog_photo);
DialogPhoto as_dialog_photo(const Photo &photo);
DialogPhoto as_fake_dialog_photo(const Photo &photo);
ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo);
vector<FileId> dialog_photo_get_file_ids(const DialogPhoto &dialog_photo);
@ -98,9 +113,9 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
int64 access_hash, string file_reference, DcId dc_id,
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
PhotoFormat format);
PhotoSize get_video_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size);
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size);
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::WebDocument> web_document_ptr);
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
@ -113,14 +128,19 @@ bool operator<(const PhotoSize &lhs, const PhotoSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size);
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs);
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size);
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::Photo> &&photo, DialogId owner_dialog_id);
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&photo, DialogId owner_dialog_id);
Photo get_encrypted_file_photo(FileManager *file_manager, tl_object_ptr<telegram_api::encryptedFile> &&file,
tl_object_ptr<secret_api::decryptedMessageMediaPhoto> &&photo, DialogId owner_dialog_id);
Photo get_web_document_photo(FileManager *file_manager, tl_object_ptr<telegram_api::WebDocument> web_document,
DialogId owner_dialog_id);
tl_object_ptr<td_api::photo> get_photo_object(FileManager *file_manager, const Photo *photo);
tl_object_ptr<td_api::userProfilePhoto> get_user_profile_photo_object(FileManager *file_manager, const Photo *photo);
tl_object_ptr<td_api::photo> get_photo_object(FileManager *file_manager, const Photo &photo);
tl_object_ptr<td_api::chatPhoto> get_chat_photo_object(FileManager *file_manager, const Photo &photo);
void photo_delete_thumbnail(Photo &photo);

View File

@ -6,9 +6,9 @@
//
#pragma once
#include "td/telegram/Photo.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Photo.h"
#include "td/telegram/Version.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
@ -30,14 +30,30 @@ void parse(Dimensions &dimensions, ParserT &parser) {
template <class StorerT>
void store(const DialogPhoto &dialog_photo, StorerT &storer) {
store(dialog_photo.small_file_id, storer);
store(dialog_photo.big_file_id, storer);
bool has_file_ids = dialog_photo.small_file_id.is_valid() || dialog_photo.big_file_id.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_file_ids);
STORE_FLAG(dialog_photo.has_animation);
END_STORE_FLAGS();
if (has_file_ids) {
store(dialog_photo.small_file_id, storer);
store(dialog_photo.big_file_id, storer);
}
}
template <class ParserT>
void parse(DialogPhoto &dialog_photo, ParserT &parser) {
parse(dialog_photo.small_file_id, parser);
parse(dialog_photo.big_file_id, parser);
bool has_file_ids = true;
if (parser.version() >= static_cast<int32>(Version::AddDialogPhotoHasAnimation)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_file_ids);
PARSE_FLAG(dialog_photo.has_animation);
END_PARSE_FLAGS();
}
if (has_file_ids) {
parse(dialog_photo.small_file_id, parser);
parse(dialog_photo.big_file_id, parser);
}
}
template <class StorerT>
@ -70,14 +86,32 @@ void parse(PhotoSize &photo_size, ParserT &parser) {
LOG(DEBUG) << "Parsed photo size " << photo_size;
}
template <class StorerT>
void store(const AnimationSize &animation_size, StorerT &storer) {
store(static_cast<const PhotoSize &>(animation_size), storer);
store(animation_size.main_frame_timestamp, storer);
}
template <class ParserT>
void parse(AnimationSize &animation_size, ParserT &parser) {
parse(static_cast<PhotoSize &>(animation_size), parser);
if (parser.version() >= static_cast<int32>(Version::AddDialogPhotoHasAnimation)) {
parse(animation_size.main_frame_timestamp, parser);
} else {
animation_size.main_frame_timestamp = 0;
}
}
template <class StorerT>
void store(const Photo &photo, StorerT &storer) {
bool has_minithumbnail = !photo.minithumbnail.empty();
bool has_animations = !photo.animations.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(photo.has_stickers);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(has_animations);
END_STORE_FLAGS();
store(photo.id, storer);
store(photo.id.get(), storer);
store(photo.date, storer);
store(photo.photos, storer);
if (photo.has_stickers) {
@ -86,16 +120,23 @@ void store(const Photo &photo, StorerT &storer) {
if (has_minithumbnail) {
store(photo.minithumbnail, storer);
}
if (has_animations) {
store(photo.animations, storer);
}
}
template <class ParserT>
void parse(Photo &photo, ParserT &parser) {
bool has_minithumbnail;
bool has_animations;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(photo.has_stickers);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(has_animations);
END_PARSE_FLAGS();
parse(photo.id, parser);
int64 id;
parse(id, parser);
photo.id = id;
parse(photo.date, parser);
parse(photo.photos, parser);
if (photo.has_stickers) {
@ -104,6 +145,9 @@ void parse(Photo &photo, ParserT &parser) {
if (has_minithumbnail) {
parse(photo.minithumbnail, parser);
}
if (has_animations) {
parse(photo.animations, parser);
}
}
} // namespace td

View File

@ -27,7 +27,7 @@
namespace td {
Result<PrivacyManager::UserPrivacySetting> PrivacyManager::UserPrivacySetting::from_td_api(
Result<PrivacyManager::UserPrivacySetting> PrivacyManager::UserPrivacySetting::get_user_privacy_setting(
tl_object_ptr<td_api::UserPrivacySetting> key) {
if (!key) {
return Status::Error(5, "UserPrivacySetting must be non-empty");
@ -67,7 +67,7 @@ PrivacyManager::UserPrivacySetting::UserPrivacySetting(const telegram_api::Priva
}
}
tl_object_ptr<td_api::UserPrivacySetting> PrivacyManager::UserPrivacySetting::as_td_api() const {
tl_object_ptr<td_api::UserPrivacySetting> PrivacyManager::UserPrivacySetting::get_user_privacy_setting_object() const {
switch (type_) {
case Type::UserStatus:
return make_tl_object<td_api::userPrivacySettingShowStatus>();
@ -90,7 +90,7 @@ tl_object_ptr<td_api::UserPrivacySetting> PrivacyManager::UserPrivacySetting::as
return nullptr;
}
}
tl_object_ptr<telegram_api::InputPrivacyKey> PrivacyManager::UserPrivacySetting::as_telegram_api() const {
tl_object_ptr<telegram_api::InputPrivacyKey> PrivacyManager::UserPrivacySetting::get_input_privacy_key() const {
switch (type_) {
case Type::UserStatus:
return make_tl_object<telegram_api::inputPrivacyKeyStatusTimestamp>();
@ -245,7 +245,8 @@ PrivacyManager::UserPrivacySettingRule::UserPrivacySettingRule(const telegram_ap
}
}
tl_object_ptr<td_api::UserPrivacySettingRule> PrivacyManager::UserPrivacySettingRule::as_td_api() const {
tl_object_ptr<td_api::UserPrivacySettingRule>
PrivacyManager::UserPrivacySettingRule::get_user_privacy_setting_rule_object() const {
switch (type_) {
case Type::AllowContacts:
return make_tl_object<td_api::userPrivacySettingRuleAllowContacts>();
@ -268,14 +269,14 @@ tl_object_ptr<td_api::UserPrivacySettingRule> PrivacyManager::UserPrivacySetting
}
}
tl_object_ptr<telegram_api::InputPrivacyRule> PrivacyManager::UserPrivacySettingRule::as_telegram_api() const {
tl_object_ptr<telegram_api::InputPrivacyRule> PrivacyManager::UserPrivacySettingRule::get_input_privacy_rule() const {
switch (type_) {
case Type::AllowContacts:
return make_tl_object<telegram_api::inputPrivacyValueAllowContacts>();
case Type::AllowAll:
return make_tl_object<telegram_api::inputPrivacyValueAllowAll>();
case Type::AllowUsers:
return make_tl_object<telegram_api::inputPrivacyValueAllowUsers>(user_ids_as_telegram_api());
return make_tl_object<telegram_api::inputPrivacyValueAllowUsers>(get_input_users());
case Type::AllowChatParticipants:
return make_tl_object<telegram_api::inputPrivacyValueAllowChatParticipants>(vector<int32>{chat_ids_});
case Type::RestrictContacts:
@ -283,7 +284,7 @@ tl_object_ptr<telegram_api::InputPrivacyRule> PrivacyManager::UserPrivacySetting
case Type::RestrictAll:
return make_tl_object<telegram_api::inputPrivacyValueDisallowAll>();
case Type::RestrictUsers:
return make_tl_object<telegram_api::inputPrivacyValueDisallowUsers>(user_ids_as_telegram_api());
return make_tl_object<telegram_api::inputPrivacyValueDisallowUsers>(get_input_users());
case Type::RestrictChatParticipants:
return make_tl_object<telegram_api::inputPrivacyValueDisallowChatParticipants>(vector<int32>{chat_ids_});
default:
@ -291,7 +292,7 @@ tl_object_ptr<telegram_api::InputPrivacyRule> PrivacyManager::UserPrivacySetting
}
}
Result<PrivacyManager::UserPrivacySettingRule> PrivacyManager::UserPrivacySettingRule::from_telegram_api(
Result<PrivacyManager::UserPrivacySettingRule> PrivacyManager::UserPrivacySettingRule::get_user_privacy_setting_rule(
tl_object_ptr<telegram_api::PrivacyRule> rule) {
CHECK(rule != nullptr);
UserPrivacySettingRule result(*rule);
@ -316,8 +317,7 @@ Result<PrivacyManager::UserPrivacySettingRule> PrivacyManager::UserPrivacySettin
return result;
}
vector<tl_object_ptr<telegram_api::InputUser>> PrivacyManager::UserPrivacySettingRule::user_ids_as_telegram_api()
const {
vector<tl_object_ptr<telegram_api::InputUser>> PrivacyManager::UserPrivacySettingRule::get_input_users() const {
vector<tl_object_ptr<telegram_api::InputUser>> result;
for (auto user_id : user_ids_) {
auto input_user = G()->td().get_actor_unsafe()->contacts_manager_->get_input_user(UserId(user_id));
@ -354,28 +354,28 @@ vector<int32> PrivacyManager::UserPrivacySettingRule::get_restricted_user_ids()
return {};
}
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::from_telegram_api(
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::get_user_privacy_setting_rules(
tl_object_ptr<telegram_api::account_privacyRules> 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_));
return get_user_privacy_setting_rules(std::move(rules->rules_));
}
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::from_telegram_api(
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::get_user_privacy_setting_rules(
vector<tl_object_ptr<telegram_api::PrivacyRule>> rules) {
UserPrivacySettingRules result;
for (auto &rule : rules) {
TRY_RESULT(new_rule, UserPrivacySettingRule::from_telegram_api(std::move(rule)));
TRY_RESULT(new_rule, UserPrivacySettingRule::get_user_privacy_setting_rule(std::move(rule)));
result.rules_.push_back(new_rule);
}
if (!result.rules_.empty() &&
result.rules_.back().as_td_api()->get_id() == td_api::userPrivacySettingRuleRestrictAll::ID) {
if (!result.rules_.empty() && result.rules_.back().get_user_privacy_setting_rule_object()->get_id() ==
td_api::userPrivacySettingRuleRestrictAll::ID) {
result.rules_.pop_back();
}
return result;
}
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::from_td_api(
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::get_user_privacy_setting_rules(
tl_object_ptr<td_api::userPrivacySettingRules> rules) {
if (!rules) {
return Status::Error(5, "UserPrivacySettingRules must be non-empty");
@ -390,13 +390,15 @@ Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySetti
return result;
}
tl_object_ptr<td_api::userPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::as_td_api() const {
tl_object_ptr<td_api::userPrivacySettingRules>
PrivacyManager::UserPrivacySettingRules::get_user_privacy_setting_rules_object() const {
return make_tl_object<td_api::userPrivacySettingRules>(
transform(rules_, [](const auto &rule) { return rule.as_td_api(); }));
transform(rules_, [](const auto &rule) { return rule.get_user_privacy_setting_rule_object(); }));
}
vector<tl_object_ptr<telegram_api::InputPrivacyRule>> PrivacyManager::UserPrivacySettingRules::as_telegram_api() const {
auto result = transform(rules_, [](const auto &rule) { return rule.as_telegram_api(); });
vector<tl_object_ptr<telegram_api::InputPrivacyRule>> PrivacyManager::UserPrivacySettingRules::get_input_privacy_rules()
const {
auto result = transform(rules_, [](const auto &rule) { return rule.get_input_privacy_rule(); });
if (!result.empty() && result.back()->get_id() == telegram_api::inputPrivacyValueDisallowAll::ID) {
result.pop_back();
}
@ -415,14 +417,14 @@ vector<int32> PrivacyManager::UserPrivacySettingRules::get_restricted_user_ids()
void PrivacyManager::get_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
Promise<tl_object_ptr<td_api::userPrivacySettingRules>> promise) {
auto r_user_privacy_setting = UserPrivacySetting::from_td_api(std::move(key));
auto r_user_privacy_setting = UserPrivacySetting::get_user_privacy_setting(std::move(key));
if (r_user_privacy_setting.is_error()) {
return promise.set_error(r_user_privacy_setting.move_as_error());
}
auto user_privacy_setting = r_user_privacy_setting.move_as_ok();
auto &info = get_info(user_privacy_setting);
if (info.is_synchronized) {
return promise.set_value(info.rules.as_td_api());
return promise.set_value(info.rules.get_user_privacy_setting_rules_object());
}
info.get_promises.push_back(std::move(promise));
if (info.get_promises.size() > 1u) {
@ -430,7 +432,7 @@ void PrivacyManager::get_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
return;
}
auto net_query =
G()->net_query_creator().create(telegram_api::account_getPrivacy(user_privacy_setting.as_telegram_api()));
G()->net_query_creator().create(telegram_api::account_getPrivacy(user_privacy_setting.get_input_privacy_key()));
send_with_promise(std::move(net_query),
PromiseCreator::lambda([this, user_privacy_setting](Result<NetQueryPtr> x_net_query) {
@ -438,20 +440,20 @@ void PrivacyManager::get_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
TRY_RESULT(net_query, std::move(x_net_query));
TRY_RESULT(rules, fetch_result<telegram_api::account_getPrivacy>(std::move(net_query)));
LOG(INFO) << "Receive " << to_string(rules);
return UserPrivacySettingRules::from_telegram_api(std::move(rules));
return UserPrivacySettingRules::get_user_privacy_setting_rules(std::move(rules));
}());
}));
}
void PrivacyManager::set_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
tl_object_ptr<td_api::userPrivacySettingRules> rules, Promise<Unit> promise) {
auto r_user_privacy_setting = UserPrivacySetting::from_td_api(std::move(key));
auto r_user_privacy_setting = UserPrivacySetting::get_user_privacy_setting(std::move(key));
if (r_user_privacy_setting.is_error()) {
return promise.set_error(r_user_privacy_setting.move_as_error());
}
auto user_privacy_setting = r_user_privacy_setting.move_as_ok();
auto r_privacy_rules = UserPrivacySettingRules::from_td_api(std::move(rules));
auto r_privacy_rules = UserPrivacySettingRules::get_user_privacy_setting_rules(std::move(rules));
if (r_privacy_rules.is_error()) {
return promise.set_error(r_privacy_rules.move_as_error());
}
@ -462,30 +464,30 @@ void PrivacyManager::set_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
// TODO cancel previous query
return promise.set_error(Status::Error(5, "Another set_privacy query is active"));
}
auto net_query = G()->net_query_creator().create(
telegram_api::account_setPrivacy(user_privacy_setting.as_telegram_api(), privacy_rules.as_telegram_api()));
auto net_query = G()->net_query_creator().create(telegram_api::account_setPrivacy(
user_privacy_setting.get_input_privacy_key(), privacy_rules.get_input_privacy_rules()));
info.has_set_query = true;
send_with_promise(std::move(net_query),
PromiseCreator::lambda([this, user_privacy_setting,
promise = std::move(promise)](Result<NetQueryPtr> x_net_query) mutable {
promise.set_result([&]() -> Result<Unit> {
get_info(user_privacy_setting).has_set_query = false;
TRY_RESULT(net_query, std::move(x_net_query));
TRY_RESULT(rules, fetch_result<telegram_api::account_setPrivacy>(std::move(net_query)));
LOG(INFO) << "Receive " << to_string(rules);
TRY_RESULT(privacy_rules, UserPrivacySettingRules::from_telegram_api(std::move(rules)));
do_update_privacy(user_privacy_setting, std::move(privacy_rules), true);
return Unit();
}());
}));
send_with_promise(
std::move(net_query), PromiseCreator::lambda([this, user_privacy_setting, promise = std::move(promise)](
Result<NetQueryPtr> x_net_query) mutable {
promise.set_result([&]() -> Result<Unit> {
get_info(user_privacy_setting).has_set_query = false;
TRY_RESULT(net_query, std::move(x_net_query));
TRY_RESULT(rules, fetch_result<telegram_api::account_setPrivacy>(std::move(net_query)));
LOG(INFO) << "Receive " << to_string(rules);
TRY_RESULT(privacy_rules, UserPrivacySettingRules::get_user_privacy_setting_rules(std::move(rules)));
do_update_privacy(user_privacy_setting, std::move(privacy_rules), true);
return Unit();
}());
}));
}
void PrivacyManager::update_privacy(tl_object_ptr<telegram_api::updatePrivacy> update) {
CHECK(update != nullptr);
CHECK(update->key_ != nullptr);
UserPrivacySetting user_privacy_setting(*update->key_);
auto r_privacy_rules = UserPrivacySettingRules::from_telegram_api(std::move(update->rules_));
auto r_privacy_rules = UserPrivacySettingRules::get_user_privacy_setting_rules(std::move(update->rules_));
if (r_privacy_rules.is_error()) {
LOG(INFO) << "Skip updatePrivacy: " << r_privacy_rules.error().message();
auto &info = get_info(user_privacy_setting);
@ -504,7 +506,7 @@ void PrivacyManager::on_get_result(UserPrivacySetting user_privacy_setting,
if (privacy_rules.is_error()) {
promise.set_error(privacy_rules.error().clone());
} else {
promise.set_value(privacy_rules.ok().as_td_api());
promise.set_value(privacy_rules.ok().get_user_privacy_setting_rules_object());
}
}
if (privacy_rules.is_ok()) {
@ -548,9 +550,10 @@ void PrivacyManager::do_update_privacy(UserPrivacySetting user_privacy_setting,
}
info.rules = std::move(privacy_rules);
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserPrivacySettingRules>(user_privacy_setting.as_td_api(),
info.rules.as_td_api()));
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserPrivacySettingRules>(user_privacy_setting.get_user_privacy_setting_object(),
info.rules.get_user_privacy_setting_rules_object()));
}
}

View File

@ -49,10 +49,13 @@ class PrivacyManager : public NetQueryCallback {
Size
};
static Result<UserPrivacySetting> from_td_api(tl_object_ptr<td_api::UserPrivacySetting> key);
explicit UserPrivacySetting(const telegram_api::PrivacyKey &key);
tl_object_ptr<td_api::UserPrivacySetting> as_td_api() const;
tl_object_ptr<telegram_api::InputPrivacyKey> as_telegram_api() const;
static Result<UserPrivacySetting> get_user_privacy_setting(tl_object_ptr<td_api::UserPrivacySetting> key);
tl_object_ptr<td_api::UserPrivacySetting> get_user_privacy_setting_object() const;
tl_object_ptr<telegram_api::InputPrivacyKey> get_input_privacy_key() const;
Type type() const {
return type_;
@ -67,10 +70,14 @@ class PrivacyManager : public NetQueryCallback {
class UserPrivacySettingRule {
public:
UserPrivacySettingRule() = default;
static Result<UserPrivacySettingRule> from_telegram_api(tl_object_ptr<telegram_api::PrivacyRule> rule);
explicit UserPrivacySettingRule(const td_api::UserPrivacySettingRule &rule);
tl_object_ptr<td_api::UserPrivacySettingRule> as_td_api() const;
tl_object_ptr<telegram_api::InputPrivacyRule> as_telegram_api() const;
static Result<UserPrivacySettingRule> get_user_privacy_setting_rule(tl_object_ptr<telegram_api::PrivacyRule> rule);
tl_object_ptr<td_api::UserPrivacySettingRule> get_user_privacy_setting_rule_object() const;
tl_object_ptr<telegram_api::InputPrivacyRule> get_input_privacy_rule() const;
bool operator==(const UserPrivacySettingRule &other) const {
return type_ == other.type_ && user_ids_ == other.user_ids_ && chat_ids_ == other.chat_ids_;
@ -93,7 +100,7 @@ class PrivacyManager : public NetQueryCallback {
vector<int32> user_ids_;
vector<int32> chat_ids_;
vector<tl_object_ptr<telegram_api::InputUser>> user_ids_as_telegram_api() const;
vector<tl_object_ptr<telegram_api::InputUser>> get_input_users() const;
void set_chat_ids(const vector<int64> &dialog_ids);
@ -105,11 +112,19 @@ class PrivacyManager : public NetQueryCallback {
class UserPrivacySettingRules {
public:
UserPrivacySettingRules() = default;
static Result<UserPrivacySettingRules> from_telegram_api(tl_object_ptr<telegram_api::account_privacyRules> rules);
static Result<UserPrivacySettingRules> from_telegram_api(vector<tl_object_ptr<telegram_api::PrivacyRule>> rules);
static Result<UserPrivacySettingRules> from_td_api(tl_object_ptr<td_api::userPrivacySettingRules> rules);
tl_object_ptr<td_api::userPrivacySettingRules> as_td_api() const;
vector<tl_object_ptr<telegram_api::InputPrivacyRule>> as_telegram_api() const;
static Result<UserPrivacySettingRules> get_user_privacy_setting_rules(
tl_object_ptr<telegram_api::account_privacyRules> rules);
static Result<UserPrivacySettingRules> get_user_privacy_setting_rules(
vector<tl_object_ptr<telegram_api::PrivacyRule>> rules);
static Result<UserPrivacySettingRules> get_user_privacy_setting_rules(
tl_object_ptr<td_api::userPrivacySettingRules> rules);
tl_object_ptr<td_api::userPrivacySettingRules> get_user_privacy_setting_rules_object() const;
vector<tl_object_ptr<telegram_api::InputPrivacyRule>> get_input_privacy_rules() const;
bool operator==(const UserPrivacySettingRules &other) const {
return rules_ == other.rules_;

View File

@ -1835,6 +1835,10 @@ Status SecretChatActor::on_update_chat(telegram_api::encryptedChatRequested &upd
auth_state_.date = context_->unix_time();
TRY_STATUS(save_common_info(update));
auth_state_.handshake.set_g_a(update.g_a_.as_slice());
if ((update.flags_ & telegram_api::encryptedChatRequested::FOLDER_ID_MASK) != 0) {
auth_state_.initial_folder_id = FolderId(update.folder_id_);
}
send_update_secret_chat();
return Status::OK();
}
@ -2014,7 +2018,8 @@ void SecretChatActor::send_update_secret_chat() {
state = SecretChatState::Waiting;
}
context_->on_update_secret_chat(auth_state_.access_hash, get_user_id(), state, auth_state_.x == 0, config_state_.ttl,
auth_state_.date, auth_state_.key_hash, current_layer());
auth_state_.date, auth_state_.key_hash, current_layer(),
auth_state_.initial_folder_id);
}
void SecretChatActor::on_outbound_action(secret_api::decryptedMessageActionSetMessageTTL &set_ttl) {
@ -2138,7 +2143,10 @@ Status SecretChatActor::on_inbound_action(secret_api::decryptedMessageActionRequ
if (pfs_state_.state != PfsState::Empty) {
return Status::Error("Unexpected RequestKey");
}
LOG_CHECK(pfs_state_.other_auth_key.empty()) << "TODO: got requestKey, before old key is dropped";
if (!pfs_state_.other_auth_key.empty()) {
LOG_CHECK(pfs_state_.can_forget_other_key) << "TODO: got requestKey, before old key is dropped";
return Status::Error("Unexpected RequestKey (old key is used)");
}
pfs_state_.state = PfsState::SendAccept;
pfs_state_.handshake = DhHandshake();
pfs_state_.exchange_id = request_key.exchange_id_;

View File

@ -16,6 +16,7 @@
#include "td/mtproto/DhHandshake.h"
#include "td/telegram/DhConfig.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/logevent/SecretChatEvent.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/net/NetQuery.h"
@ -82,7 +83,8 @@ class SecretChatActor : public NetQueryCallback {
virtual void send_net_query(NetQueryPtr query, ActorShared<NetQueryCallback> callback, bool ordered) = 0;
virtual void on_update_secret_chat(int64 access_hash, UserId user_id, SecretChatState state, bool is_outbound,
int32 ttl, int32 date, string key_hash, int32 layer) = 0;
int32 ttl, int32 date, string key_hash, int32 layer,
FolderId initial_folder_id) = 0;
// Promise must be set only after the update is processed.
//
@ -376,6 +378,8 @@ class SecretChatActor : public NetQueryCallback {
int32 date = 0;
FolderId initial_folder_id;
DhConfig dh_config;
DhHandshake handshake;
@ -385,14 +389,18 @@ class SecretChatActor : public NetQueryCallback {
template <class StorerT>
void store(StorerT &storer) const {
uint32 flags = 0;
bool date_flag = date != 0;
bool key_hash_flag = true;
if (date_flag) {
bool has_date = date != 0;
bool has_key_hash = true;
bool has_initial_folder_id = initial_folder_id != FolderId();
if (has_date) {
flags |= 1;
}
if (key_hash_flag) {
if (has_key_hash) {
flags |= 2;
}
if (has_initial_folder_id) {
flags |= 4;
}
storer.store_int((flags << 8) | static_cast<int32>(state));
storer.store_int(x);
@ -401,16 +409,19 @@ class SecretChatActor : public NetQueryCallback {
storer.store_int(user_id);
storer.store_long(user_access_hash);
storer.store_int(random_id);
if (date_flag) {
if (has_date) {
storer.store_int(date);
}
if (key_hash_flag) {
if (has_key_hash) {
storer.store_string(key_hash);
}
dh_config.store(storer);
if (state == State::SendRequest || state == State::WaitRequestResponse) {
handshake.store(storer);
}
if (has_initial_folder_id) {
initial_folder_id.store(storer);
}
}
template <class ParserT>
@ -418,8 +429,9 @@ class SecretChatActor : public NetQueryCallback {
uint32 tmp = parser.fetch_int();
state = static_cast<State>(tmp & 255);
uint32 flags = tmp >> 8;
bool date_flag = (flags & 1) != 0;
bool key_hash_flag = (flags & 2) != 0;
bool has_date = (flags & 1) != 0;
bool has_key_hash = (flags & 2) != 0;
bool has_initial_folder_id = (flags & 4) != 0;
x = parser.fetch_int();
@ -428,16 +440,19 @@ class SecretChatActor : public NetQueryCallback {
user_id = parser.fetch_int();
user_access_hash = parser.fetch_long();
random_id = parser.fetch_int();
if (date_flag) {
if (has_date) {
date = parser.fetch_int();
}
if (key_hash_flag) {
if (has_key_hash) {
key_hash = parser.template fetch_string<std::string>();
}
dh_config.parse(parser);
if (state == State::SendRequest || state == State::WaitRequestResponse) {
handshake.parse(parser);
}
if (has_initial_folder_id) {
initial_folder_id.parse(parser);
}
}
};

View File

@ -9,6 +9,7 @@
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DhCache.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/SecretChatEvent.h"
#include "td/telegram/MessageId.h"
@ -209,12 +210,6 @@ void SecretChatsManager::before_get_difference(int32 qts) {
// We will receive all updates later than qts anyway.
}
void SecretChatsManager::after_get_difference() {
if (dummy_mode_ || close_flag_) {
return;
}
}
void SecretChatsManager::on_update_chat(tl_object_ptr<telegram_api::updateEncryption> update) {
if (dummy_mode_ || close_flag_) {
return;
@ -431,9 +426,9 @@ unique_ptr<SecretChatActor::Context> SecretChatsManager::make_secret_chat_contex
}
void on_update_secret_chat(int64 access_hash, UserId user_id, SecretChatState state, bool is_outbound, int32 ttl,
int32 date, string key_hash, int32 layer) override {
int32 date, string key_hash, int32 layer, FolderId initial_folder_id) override {
send_closure(G()->contacts_manager(), &ContactsManager::on_update_secret_chat, secret_chat_id_, access_hash,
user_id, state, is_outbound, ttl, date, key_hash, layer);
user_id, state, is_outbound, ttl, date, key_hash, layer, initial_folder_id);
}
void on_inbound_message(UserId user_id, MessageId message_id, int32 date,

View File

@ -6,14 +6,14 @@
//
#pragma once
#include "td/telegram/secret_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/logevent/SecretChatEvent.h"
#include "td/telegram/PtsManager.h"
#include "td/telegram/SecretChatActor.h"
#include "td/telegram/SecretChatId.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/telegram_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
@ -34,7 +34,6 @@ class SecretChatsManager : public Actor {
// we can forget all pending_updates after start_get_difference they will be received after this point anyway
// It is not necessary, but it will help.
void before_get_difference(int32 qts);
void after_get_difference();
// Proxy query to corrensponding SecretChatActor.
// Look for more info in SecretChatActor.h

View File

@ -36,6 +36,9 @@ class StateManager final : public Actor {
}
};
explicit StateManager(ActorShared<> parent) : parent_(std::move(parent)) {
}
void on_synchronized(bool is_synchronized);
void on_network_updated();
@ -92,6 +95,7 @@ class StateManager final : public Actor {
}
private:
ActorShared<> parent_;
uint32 connect_cnt_ = 0;
uint32 connect_proxy_cnt_ = 0;
bool sync_flag_ = true;

View File

@ -1161,7 +1161,7 @@ void StickersManager::init() {
animated_emoji_sticker_set.short_name_);
}
dice_emojis_str_ = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀");
dice_emojis_str_ = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀\x01\x01⚽️");
dice_emojis_ = full_split(dice_emojis_str_, '\x01');
for (auto &dice_emoji : dice_emojis_) {
auto &animated_dice_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(dice_emoji));
@ -2126,8 +2126,8 @@ tl_object_ptr<telegram_api::InputMedia> StickersManager::get_input_media(
}
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, std::move(attributes),
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());
}
@ -3468,7 +3468,7 @@ void StickersManager::on_update_dice_emojis() {
return;
}
auto dice_emojis_str = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀");
auto dice_emojis_str = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀\x01\x01⚽️");
if (dice_emojis_str == dice_emojis_str_) {
return;
}
@ -3502,7 +3502,8 @@ void StickersManager::on_update_dice_success_values() {
return;
}
auto dice_success_values_str = G()->shared_config().get_option_string("dice_success_values", "0,0,0");
auto dice_success_values_str =
G()->shared_config().get_option_string("dice_success_values", "0,6:62,5:110,5:110,5:110");
if (dice_success_values_str == dice_success_values_str_) {
return;
}

View File

@ -26,7 +26,7 @@
namespace td {
tl_object_ptr<td_api::databaseStatistics> DatabaseStats::as_td_api() const {
tl_object_ptr<td_api::databaseStatistics> DatabaseStats::get_database_statistics_object() const {
return make_tl_object<td_api::databaseStatistics>(debug);
}

View File

@ -26,7 +26,7 @@ struct DatabaseStats {
DatabaseStats() = default;
explicit DatabaseStats(string debug) : debug(debug) {
}
tl_object_ptr<td_api::databaseStatistics> as_td_api() const;
tl_object_ptr<td_api::databaseStatistics> get_database_statistics_object() const;
};
class StorageManager : public Actor {
@ -41,9 +41,9 @@ class StorageManager : public Actor {
void on_new_file(int64 size, int64 real_size, int32 cnt);
private:
static constexpr uint32 GC_EACH = 60 * 60 * 24; // 1 day
static constexpr uint32 GC_DELAY = 60;
static constexpr uint32 GC_RAND_DELAY = 60 * 15;
static constexpr int GC_EACH = 60 * 60 * 24; // 1 day
static constexpr int GC_DELAY = 60;
static constexpr int GC_RAND_DELAY = 60 * 15;
ActorShared<> parent_;

View File

@ -0,0 +1,56 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
//
// Distributed under the 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/SuggestedAction.h"
namespace td {
SuggestedAction get_suggested_action(Slice action_str) {
if (action_str == Slice("AUTOARCHIVE_POPULAR")) {
return SuggestedAction::EnableArchiveAndMuteNewChats;
}
return SuggestedAction::Empty;
}
string get_suggested_action_str(SuggestedAction action) {
switch (action) {
case SuggestedAction::EnableArchiveAndMuteNewChats:
return "AUTOARCHIVE_POPULAR";
default:
return string();
}
}
SuggestedAction get_suggested_action(const td_api::object_ptr<td_api::SuggestedAction> &action_object) {
if (action_object == nullptr) {
return SuggestedAction::Empty;
}
switch (action_object->get_id()) {
case td_api::suggestedActionEnableArchiveAndMuteNewChats::ID:
return SuggestedAction::EnableArchiveAndMuteNewChats;
case td_api::suggestedActionCheckPhoneNumber::ID:
return SuggestedAction::CheckPhoneNumber;
default:
UNREACHABLE();
return SuggestedAction::Empty;
}
}
td_api::object_ptr<td_api::SuggestedAction> get_suggested_action_object(SuggestedAction action) {
switch (action) {
case SuggestedAction::Empty:
return nullptr;
case SuggestedAction::EnableArchiveAndMuteNewChats:
return td_api::make_object<td_api::suggestedActionEnableArchiveAndMuteNewChats>();
case SuggestedAction::CheckPhoneNumber:
return td_api::make_object<td_api::suggestedActionCheckPhoneNumber>();
default:
UNREACHABLE();
return nullptr;
}
}
} // namespace td

View File

@ -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)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
enum class SuggestedAction : int32 { Empty, EnableArchiveAndMuteNewChats, CheckPhoneNumber };
SuggestedAction get_suggested_action(Slice action_str);
string get_suggested_action_str(SuggestedAction action);
SuggestedAction get_suggested_action(const td_api::object_ptr<td_api::SuggestedAction> &action_object);
td_api::object_ptr<td_api::SuggestedAction> get_suggested_action_object(SuggestedAction action);
} // namespace td

View File

@ -83,6 +83,7 @@
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/StorageManager.h"
#include "td/telegram/SuggestedAction.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TopDialogCategory.h"
#include "td/telegram/TopDialogManager.h"
@ -118,11 +119,13 @@
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/path.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/port/uname.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/Timer.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/TsList.h"
#include "td/utils/utf8.h"
#include <cmath>
@ -265,9 +268,14 @@ class GetRecentMeUrlsQuery : public Td::ResultHandler {
case telegram_api::recentMeUrlChatInvite::ID: {
auto url = move_tl_object_as<telegram_api::recentMeUrlChatInvite>(url_ptr);
result->url_ = std::move(url->url_);
td->contacts_manager_->on_get_dialog_invite_link_info(result->url_, std::move(url->chat_invite_));
result->type_ = make_tl_object<td_api::tMeUrlTypeChatInvite>(
td->contacts_manager_->get_chat_invite_link_info_object(result->url_));
td->contacts_manager_->on_get_dialog_invite_link_info(result->url_, std::move(url->chat_invite_),
Promise<Unit>());
auto info_object = td->contacts_manager_->get_chat_invite_link_info_object(result->url_);
if (info_object == nullptr) {
result = nullptr;
break;
}
result->type_ = make_tl_object<td_api::tMeUrlTypeChatInvite>(std::move(info_object));
break;
}
case telegram_api::recentMeUrlStickerSet::ID: {
@ -1784,7 +1792,7 @@ class CreateNewSecretChatRequest : public RequestActor<SecretChatId> {
// But since the update may still be on its way, we will update essential fields here.
td->contacts_manager_->on_update_secret_chat(
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);
-1 /* unknown ttl */, 0 /* unknown creation date */, "" /* no key_hash */, 0, FolderId());
DialogId dialog_id(secret_chat_id_);
td->messages_manager_->force_create_dialog(dialog_id, "create new secret chat", true);
send_result(td->messages_manager_->get_chat_object(dialog_id));
@ -2240,10 +2248,12 @@ class GetUserProfilePhotosRequest : public RequestActor<> {
void do_send_result() override {
// TODO create function get_user_profile_photos_object
auto result = transform(photos.second, [file_manager = td->file_manager_.get()](const Photo *photo) {
return get_user_profile_photo_object(file_manager, photo);
CHECK(photo != nullptr);
CHECK(!photo->is_empty());
return get_chat_photo_object(file_manager, *photo);
});
send_result(make_tl_object<td_api::userProfilePhotos>(photos.first, std::move(result)));
send_result(make_tl_object<td_api::chatPhotos>(photos.first, std::move(result)));
}
public:
@ -3169,7 +3179,6 @@ void Td::on_get_promo_data(Result<telegram_api::object_ptr<telegram_api::help_Pr
}
void Td::schedule_get_promo_data(int32 expires_in) {
LOG(INFO) << "Schedule getPromoData in " << expires_in;
if (expires_in < 0) {
LOG(ERROR) << "Receive wrong expires_in: " << expires_in;
expires_in = 0;
@ -3181,6 +3190,7 @@ void Td::schedule_get_promo_data(int32 expires_in) {
expires_in = 86400;
}
if (!close_flag_ && !auth_manager_->is_bot()) {
LOG(INFO) << "Schedule getPromoData in " << expires_in;
alarm_timeout_.set_timeout_in(PROMO_DATA_ALARM_ID, expires_in);
}
}
@ -3726,6 +3736,8 @@ void Td::start_up() {
LOG_IF(FATAL, symbol != c) << "TDLib requires little-endian platform";
}
TsList<NetQueryDebug>::lock().unlock(); // initialize mutex before any NetQuery
VLOG(td_init) << "Create Global";
set_context(std::make_shared<Global>());
inc_request_actor_refcnt(); // guard
@ -3783,41 +3795,41 @@ void Td::dec_actor_refcnt() {
LOG(WARNING) << "ON_ACTORS_CLOSED";
Timer timer;
animations_manager_.reset();
LOG(DEBUG) << "AnimationsManager was cleared " << timer;
LOG(DEBUG) << "AnimationsManager was cleared" << timer;
audios_manager_.reset();
LOG(DEBUG) << "AudiosManager was cleared " << timer;
LOG(DEBUG) << "AudiosManager was cleared" << timer;
auth_manager_.reset();
LOG(DEBUG) << "AuthManager was cleared " << timer;
LOG(DEBUG) << "AuthManager was cleared" << timer;
background_manager_.reset();
LOG(DEBUG) << "BackgroundManager was cleared " << timer;
LOG(DEBUG) << "BackgroundManager was cleared" << timer;
contacts_manager_.reset();
LOG(DEBUG) << "ContactsManager was cleared " << timer;
LOG(DEBUG) << "ContactsManager was cleared" << timer;
documents_manager_.reset();
LOG(DEBUG) << "DocumentsManager was cleared " << timer;
LOG(DEBUG) << "DocumentsManager was cleared" << timer;
file_manager_.reset();
LOG(DEBUG) << "FileManager was cleared " << timer;
LOG(DEBUG) << "FileManager was cleared" << timer;
file_reference_manager_.reset();
LOG(DEBUG) << "FileReferenceManager was cleared " << timer;
LOG(DEBUG) << "FileReferenceManager was cleared" << timer;
inline_queries_manager_.reset();
LOG(DEBUG) << "InlineQueriesManager was cleared " << timer;
LOG(DEBUG) << "InlineQueriesManager was cleared" << timer;
messages_manager_.reset();
LOG(DEBUG) << "MessagesManager was cleared " << timer;
LOG(DEBUG) << "MessagesManager was cleared" << timer;
notification_manager_.reset();
LOG(DEBUG) << "NotificationManager was cleared " << timer;
LOG(DEBUG) << "NotificationManager was cleared" << timer;
poll_manager_.reset();
LOG(DEBUG) << "PollManager was cleared " << timer;
LOG(DEBUG) << "PollManager was cleared" << timer;
stickers_manager_.reset();
LOG(DEBUG) << "StickersManager was cleared " << timer;
LOG(DEBUG) << "StickersManager was cleared" << timer;
updates_manager_.reset();
LOG(DEBUG) << "UpdatesManager was cleared " << timer;
LOG(DEBUG) << "UpdatesManager was cleared" << timer;
video_notes_manager_.reset();
LOG(DEBUG) << "VideoNotesManager was cleared " << timer;
LOG(DEBUG) << "VideoNotesManager was cleared" << timer;
videos_manager_.reset();
LOG(DEBUG) << "VideosManager was cleared " << timer;
LOG(DEBUG) << "VideosManager was cleared" << timer;
voice_notes_manager_.reset();
LOG(DEBUG) << "VoiceNotesManager was cleared " << timer;
LOG(DEBUG) << "VoiceNotesManager was cleared" << timer;
web_pages_manager_.reset();
LOG(DEBUG) << "WebPagesManager was cleared " << timer;
LOG(DEBUG) << "WebPagesManager was cleared" << timer;
Promise<> promise = PromiseCreator::lambda([actor_id = create_reference()](Unit) mutable { actor_id.reset(); });
G()->set_shared_config(nullptr);
@ -3909,15 +3921,15 @@ void Td::clear() {
notification_manager_->flush_all_notifications();
}
}
LOG(DEBUG) << "Options was cleared " << timer;
LOG(DEBUG) << "Options was cleared" << timer;
G()->net_query_creator().stop_check();
clear_handlers();
LOG(DEBUG) << "Handlers was cleared " << timer;
LOG(DEBUG) << "Handlers was cleared" << timer;
G()->net_query_dispatcher().stop();
LOG(DEBUG) << "NetQueryDispatcher was stopped " << timer;
LOG(DEBUG) << "NetQueryDispatcher was stopped" << timer;
state_manager_.reset();
LOG(DEBUG) << "StateManager was cleared " << timer;
LOG(DEBUG) << "StateManager was cleared" << timer;
clear_requests();
if (is_online_) {
is_online_ = false;
@ -3926,70 +3938,72 @@ void Td::clear() {
alarm_timeout_.cancel_timeout(PING_SERVER_ALARM_ID);
alarm_timeout_.cancel_timeout(TERMS_OF_SERVICE_ALARM_ID);
alarm_timeout_.cancel_timeout(PROMO_DATA_ALARM_ID);
LOG(DEBUG) << "Requests was answered " << timer;
LOG(DEBUG) << "Requests was answered" << timer;
// close all pure actors
call_manager_.reset();
LOG(DEBUG) << "CallManager was cleared " << timer;
LOG(DEBUG) << "CallManager was cleared" << timer;
change_phone_number_manager_.reset();
LOG(DEBUG) << "ChangePhoneNumberManager was cleared " << timer;
LOG(DEBUG) << "ChangePhoneNumberManager was cleared" << timer;
config_manager_.reset();
LOG(DEBUG) << "ConfigManager was cleared " << timer;
LOG(DEBUG) << "ConfigManager was cleared" << timer;
confirm_phone_number_manager_.reset();
LOG(DEBUG) << "ConfirmPhoneNumberManager was cleared " << timer;
LOG(DEBUG) << "ConfirmPhoneNumberManager was cleared" << timer;
device_token_manager_.reset();
LOG(DEBUG) << "DeviceTokenManager was cleared " << timer;
LOG(DEBUG) << "DeviceTokenManager was cleared" << timer;
hashtag_hints_.reset();
LOG(DEBUG) << "HashtagHints was cleared " << timer;
LOG(DEBUG) << "HashtagHints was cleared" << timer;
language_pack_manager_.reset();
LOG(DEBUG) << "LanguagePackManager was cleared " << timer;
LOG(DEBUG) << "LanguagePackManager was cleared" << timer;
net_stats_manager_.reset();
LOG(DEBUG) << "NetStatsManager was cleared " << timer;
LOG(DEBUG) << "NetStatsManager was cleared" << timer;
password_manager_.reset();
LOG(DEBUG) << "PasswordManager was cleared " << timer;
LOG(DEBUG) << "PasswordManager was cleared" << timer;
privacy_manager_.reset();
LOG(DEBUG) << "PrivacyManager was cleared " << timer;
LOG(DEBUG) << "PrivacyManager was cleared" << timer;
secure_manager_.reset();
LOG(DEBUG) << "SecureManager was cleared " << timer;
LOG(DEBUG) << "SecureManager was cleared" << timer;
secret_chats_manager_.reset();
LOG(DEBUG) << "SecretChatsManager was cleared " << timer;
LOG(DEBUG) << "SecretChatsManager was cleared" << timer;
storage_manager_.reset();
LOG(DEBUG) << "StorageManager was cleared " << timer;
LOG(DEBUG) << "StorageManager was cleared" << timer;
top_dialog_manager_.reset();
LOG(DEBUG) << "TopDialogManager was cleared " << timer;
LOG(DEBUG) << "TopDialogManager was cleared" << timer;
verify_phone_number_manager_.reset();
LOG(DEBUG) << "VerifyPhoneNumberManager was cleared " << timer;
LOG(DEBUG) << "VerifyPhoneNumberManager was cleared" << timer;
G()->set_connection_creator(ActorOwn<ConnectionCreator>());
LOG(DEBUG) << "ConnectionCreator was cleared " << timer;
LOG(DEBUG) << "ConnectionCreator was cleared" << timer;
G()->set_temp_auth_key_watchdog(ActorOwn<TempAuthKeyWatchdog>());
LOG(DEBUG) << "TempAuthKeyWatchdog was cleared" << timer;
// clear actors which are unique pointers
animations_manager_actor_.reset();
LOG(DEBUG) << "AnimationsManager actor was cleared " << timer;
LOG(DEBUG) << "AnimationsManager actor was cleared" << timer;
auth_manager_actor_.reset();
LOG(DEBUG) << "AuthManager actor was cleared " << timer;
LOG(DEBUG) << "AuthManager actor was cleared" << timer;
background_manager_actor_.reset();
LOG(DEBUG) << "BackgroundManager actor was cleared " << timer;
LOG(DEBUG) << "BackgroundManager actor was cleared" << timer;
contacts_manager_actor_.reset();
LOG(DEBUG) << "ContactsManager actor was cleared " << timer;
LOG(DEBUG) << "ContactsManager actor was cleared" << timer;
file_manager_actor_.reset();
LOG(DEBUG) << "FileManager actor was cleared " << timer;
LOG(DEBUG) << "FileManager actor was cleared" << timer;
file_reference_manager_actor_.reset();
LOG(DEBUG) << "FileReferenceManager actor was cleared " << timer;
LOG(DEBUG) << "FileReferenceManager actor was cleared" << timer;
inline_queries_manager_actor_.reset();
LOG(DEBUG) << "InlineQueriesManager actor was cleared " << timer;
LOG(DEBUG) << "InlineQueriesManager actor was cleared" << timer;
messages_manager_actor_.reset(); // TODO: Stop silent
LOG(DEBUG) << "MessagesManager actor was cleared " << timer;
LOG(DEBUG) << "MessagesManager actor was cleared" << timer;
notification_manager_actor_.reset();
LOG(DEBUG) << "NotificationManager actor was cleared " << timer;
LOG(DEBUG) << "NotificationManager actor was cleared" << timer;
poll_manager_actor_.reset();
LOG(DEBUG) << "PollManager actor was cleared " << timer;
LOG(DEBUG) << "PollManager actor was cleared" << timer;
stickers_manager_actor_.reset();
LOG(DEBUG) << "StickersManager actor was cleared " << timer;
LOG(DEBUG) << "StickersManager actor was cleared" << timer;
updates_manager_actor_.reset();
LOG(DEBUG) << "UpdatesManager actor was cleared " << timer;
LOG(DEBUG) << "UpdatesManager actor was cleared" << timer;
web_pages_manager_actor_.reset();
LOG(DEBUG) << "WebPagesManager actor was cleared " << timer;
LOG(DEBUG) << "WebPagesManager actor was cleared" << timer;
}
void Td::close() {
@ -4233,7 +4247,7 @@ void Td::init_options_and_network() {
private:
ActorShared<Td> td_;
};
state_manager_ = create_actor<StateManager>("State manager");
state_manager_ = create_actor<StateManager>("State manager", create_reference());
send_closure(state_manager_, &StateManager::add_callback, make_unique<StateManagerCallback>(create_reference()));
G()->set_state_manager(state_manager_.get());
connection_state_ = StateManager::State::Empty;
@ -4265,7 +4279,7 @@ void Td::init_options_and_network() {
init_connection_creator();
VLOG(td_init) << "Create TempAuthKeyWatchdog";
auto temp_auth_key_watchdog = create_actor<TempAuthKeyWatchdog>("TempAuthKeyWatchdog");
auto temp_auth_key_watchdog = create_actor<TempAuthKeyWatchdog>("TempAuthKeyWatchdog", create_reference());
G()->set_temp_auth_key_watchdog(std::move(temp_auth_key_watchdog));
VLOG(td_init) << "Create ConfigManager";
@ -4680,7 +4694,8 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
return Status::Error(400, "Device model must be non-empty");
}
if (options_.system_version.empty()) {
return Status::Error(400, "System version must be non-empty");
options_.system_version = get_operating_system_version().str();
VLOG(td_init) << "Set system version to " << options_.system_version;
}
if (options_.application_version.empty()) {
return Status::Error(400, "Application version must be non-empty");
@ -4764,7 +4779,7 @@ void Td::on_request(uint64 id, td_api::recoverAuthenticationPassword &request) {
void Td::on_request(uint64 id, const td_api::logOut &request) {
// will call Td::destroy later
send_closure(auth_manager_actor_, &AuthManager::logout, id);
send_closure(auth_manager_actor_, &AuthManager::log_out, id);
}
void Td::on_request(uint64 id, const td_api::close &request) {
@ -4826,6 +4841,8 @@ void Td::on_request(uint64 id, const td_api::getCurrentState &request) {
notification_manager_->get_current_state(updates);
config_manager_->get_actor_unsafe()->get_current_state(updates);
// TODO updateFileGenerationStart generation_id:int64 original_path:string destination_path:string conversion:string = Update;
// TODO updateCall call:call = Update;
}
@ -5107,7 +5124,7 @@ void Td::on_request(uint64 id, const td_api::getFile &request) {
void Td::on_request(uint64 id, td_api::getRemoteFile &request) {
CLEAN_INPUT_STRING(request.remote_file_id_);
auto file_type = request.file_type_ == nullptr ? FileType::Temp : from_td_api(*request.file_type_);
auto file_type = request.file_type_ == nullptr ? FileType::Temp : get_file_type(*request.file_type_);
auto r_file_id = file_manager_->from_persistent_id(request.remote_file_id_, file_type);
if (r_file_id.is_error()) {
send_closure(actor_id(this), &Td::send_error, id, r_file_id.move_as_error());
@ -5122,7 +5139,7 @@ void Td::on_request(uint64 id, td_api::getStorageStatistics &request) {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_storage_statistics_object());
}
});
send_closure(storage_manager_, &StorageManager::get_storage_stats, false /*need_all_files*/, request.chat_limit_,
@ -5135,7 +5152,7 @@ void Td::on_request(uint64 id, td_api::getStorageStatisticsFast &request) {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_storage_statistics_fast_object());
}
});
send_closure(storage_manager_, &StorageManager::get_storage_stats_fast, std::move(query_promise));
@ -5146,7 +5163,7 @@ void Td::on_request(uint64 id, td_api::getDatabaseStatistics &request) {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_database_statistics_object());
}
});
send_closure(storage_manager_, &StorageManager::get_database_stats, std::move(query_promise));
@ -5171,7 +5188,7 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) {
return send_error_raw(id, 400, "File type must be non-empty");
}
file_types.push_back(from_td_api(*file_type));
file_types.push_back(get_file_type(*file_type));
}
std::vector<DialogId> owner_dialog_ids;
for (auto chat_id : request.chat_ids_) {
@ -5198,7 +5215,7 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_storage_statistics_object());
}
});
send_closure(storage_manager_, &StorageManager::run_gc, std::move(parameters),
@ -5207,11 +5224,14 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) {
void Td::on_request(uint64 id, td_api::getNetworkStatistics &request) {
CREATE_REQUEST_PROMISE();
if (!request.only_current_ && G()->shared_config().get_option_boolean("disable_persistent_network_statistics")) {
return send_error_raw(id, 400, "Persistent network statistics is disabled");
}
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<NetworkStats> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_network_statistics_object());
}
});
send_closure(net_stats_manager_, &NetStatsManager::get_network_stats, request.only_current_,
@ -5235,9 +5255,9 @@ void Td::on_request(uint64 id, td_api::addNetworkStatistics &request) {
auto file_entry = move_tl_object_as<td_api::networkStatisticsEntryFile>(request.entry_);
entry.is_call = false;
if (file_entry->file_type_ != nullptr) {
entry.file_type = from_td_api(*file_entry->file_type_);
entry.file_type = get_file_type(*file_entry->file_type_);
}
entry.net_type = from_td_api(file_entry->network_type_);
entry.net_type = get_net_type(file_entry->network_type_);
entry.rx = file_entry->received_bytes_;
entry.tx = file_entry->sent_bytes_;
break;
@ -5245,7 +5265,7 @@ void Td::on_request(uint64 id, td_api::addNetworkStatistics &request) {
case td_api::networkStatisticsEntryCall::ID: {
auto call_entry = move_tl_object_as<td_api::networkStatisticsEntryCall>(request.entry_);
entry.is_call = true;
entry.net_type = from_td_api(call_entry->network_type_);
entry.net_type = get_net_type(call_entry->network_type_);
entry.rx = call_entry->received_bytes_;
entry.tx = call_entry->sent_bytes_;
entry.duration = call_entry->duration_;
@ -5277,7 +5297,7 @@ void Td::on_request(uint64 id, td_api::addNetworkStatistics &request) {
void Td::on_request(uint64 id, const td_api::setNetworkType &request) {
CREATE_OK_REQUEST_PROMISE();
send_closure(state_manager_, &StateManager::on_network, from_td_api(request.type_));
send_closure(state_manager_, &StateManager::on_network, get_net_type(request.type_));
promise.set_value(Unit());
}
@ -5293,7 +5313,7 @@ void Td::on_request(uint64 id, const td_api::setAutoDownloadSettings &request) {
if (request.settings_ == nullptr) {
return send_error_raw(id, 400, "New settings must be non-empty");
}
set_auto_download_settings(this, from_td_api(request.type_), get_auto_download_settings(request.settings_),
set_auto_download_settings(this, get_net_type(request.type_), get_auto_download_settings(request.settings_),
std::move(promise));
}
@ -5313,9 +5333,8 @@ void Td::on_request(uint64 id, td_api::getTopChats &request) {
promise.set_value(MessagesManager::get_chats_object(result.ok()));
}
});
send_closure(top_dialog_manager_, &TopDialogManager::get_top_dialogs,
top_dialog_category_from_td_api(*request.category_), narrow_cast<size_t>(request.limit_),
std::move(query_promise));
send_closure(top_dialog_manager_, &TopDialogManager::get_top_dialogs, get_top_dialog_category(*request.category_),
narrow_cast<size_t>(request.limit_), std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::removeTopChat &request) {
@ -5328,9 +5347,8 @@ void Td::on_request(uint64 id, const td_api::removeTopChat &request) {
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_), dialog_id,
messages_manager_->get_input_peer(dialog_id, AccessRights::Read));
send_closure(top_dialog_manager_, &TopDialogManager::remove_dialog, get_top_dialog_category(*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<td_api::ok>());
}
@ -5833,7 +5851,7 @@ void Td::on_request(uint64 id, td_api::createCall &request) {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(result.ok().as_td_api());
promise.set_value(result.ok().get_call_id_object());
}
});
@ -5852,7 +5870,7 @@ void Td::on_request(uint64 id, td_api::createCall &request) {
}
send_closure(G()->call_manager(), &CallManager::create_call, user_id, std::move(input_user),
CallProtocol::from_td_api(*request.protocol_), false, std::move(query_promise));
CallProtocol(*request.protocol_), false, std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::discardCall &request) {
@ -5869,7 +5887,7 @@ void Td::on_request(uint64 id, td_api::acceptCall &request) {
return promise.set_error(Status::Error(5, "Call protocol must be non-empty"));
}
send_closure(G()->call_manager(), &CallManager::accept_call, CallId(request.call_id_),
CallProtocol::from_td_api(*request.protocol_), std::move(promise));
CallProtocol(*request.protocol_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::sendCallRating &request) {
@ -6254,7 +6272,7 @@ void Td::on_request(uint64 id, td_api::uploadFile &request) {
return send_error_raw(id, 5, "Upload priority must be in [1;32] range");
}
auto file_type = request.file_type_ == nullptr ? FileType::Temp : from_td_api(*request.file_type_);
auto file_type = request.file_type_ == nullptr ? FileType::Temp : get_file_type(*request.file_type_);
bool is_secret = file_type == FileType::Encrypted || file_type == FileType::EncryptedThumbnail;
bool is_secure = file_type == FileType::Secure;
auto r_file_id = file_manager_->get_input_file_id(file_type, request.file_, DialogId(), false, is_secret,
@ -6848,6 +6866,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 'a':
if (!is_bot && request.name_ == "archive_and_mute_new_chats_from_unknown_users") {
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result<Unit> &&result) {
// the option is already updated on success, ignore errors
send_closure(actor_id, &Td::send_result, id,
G()->shared_config().get_option_value("archive_and_mute_new_chats_from_unknown_users"));
});
send_closure_later(config_manager_, &ConfigManager::get_global_privacy_settings, std::move(promise));
return;
}
break;
case 'c':
if (!is_bot && request.name_ == "can_ignore_sensitive_content_restrictions") {
auto promise = PromiseCreator::lambda([actor_id = actor_id(this), id](Result<Unit> &&result) {
@ -6987,6 +7016,19 @@ void Td::on_request(uint64 id, td_api::setOption &request) {
if (set_boolean_option("always_parse_markdown")) {
return;
}
if (!is_bot && request.name_ == "archive_and_mute_new_chats_from_unknown_users") {
if (value_constructor_id != td_api::optionValueBoolean::ID &&
value_constructor_id != td_api::optionValueEmpty::ID) {
return send_error_raw(id, 3,
"Option \"archive_and_mute_new_chats_from_unknown_users\" must have boolean value");
}
auto archive_and_mute = value_constructor_id == td_api::optionValueBoolean::ID &&
static_cast<td_api::optionValueBoolean *>(request.value_.get())->value_;
CREATE_OK_REQUEST_PROMISE();
send_closure_later(config_manager_, &ConfigManager::set_archive_and_mute, archive_and_mute, std::move(promise));
return;
}
break;
case 'c':
if (!is_bot && set_string_option("connection_parameters", [](Slice value) {
@ -7022,7 +7064,12 @@ void Td::on_request(uint64 id, td_api::setOption &request) {
return;
}
// End custom-patches
if (set_boolean_option("disable_persistent_network_statistics")) {
return;
}
if (set_boolean_option("disable_time_adjustment_protection")) {
return;
}
if (request.name_ == "drop_notification_ids") {
G()->td_db()->get_binlog_pmc()->erase("notification_id_current");
G()->td_db()->get_binlog_pmc()->erase("notification_group_id_current");
@ -7077,7 +7124,7 @@ void Td::on_request(uint64 id, td_api::setOption &request) {
std::move(promise));
return;
}
if (set_boolean_option("is_location_visible")) {
if (!is_bot && set_boolean_option("is_location_visible")) {
contacts_manager_->set_location_visibility();
return;
}
@ -7226,6 +7273,13 @@ 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::hideSuggestedAction &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
send_closure_later(config_manager_, &ConfigManager::dismiss_suggested_action, get_suggested_action(request.action_),
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getLoginUrlInfo &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
@ -8024,7 +8078,7 @@ void Td::on_request(uint64 id, const td_api::testNetwork &request) {
}
void Td::on_request(uint64 id, td_api::testProxy &request) {
auto r_proxy = Proxy::from_td_api(std::move(request.server_), request.port_, request.type_.get());
auto r_proxy = Proxy::create_proxy(std::move(request.server_), request.port_, request.type_.get());
if (r_proxy.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, r_proxy.move_as_error());
}

View File

@ -231,7 +231,7 @@ class Td final : public NetQueryCallback {
static td_api::object_ptr<td_api::Object> static_request(td_api::object_ptr<td_api::Function> function);
private:
static constexpr const char *TDLIB_VERSION = "1.6.6";
static constexpr const char *TDLIB_VERSION = "1.6.7";
static constexpr int64 ONLINE_ALARM_ID = 0;
static constexpr int64 PING_SERVER_ALARM_ID = -1;
static constexpr int32 PING_SERVER_TIMEOUT = 300;
@ -942,6 +942,8 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::stopPoll &request);
void on_request(uint64 id, const td_api::hideSuggestedAction &request);
void on_request(uint64 id, const td_api::getLoginUrlInfo &request);
void on_request(uint64 id, const td_api::getLoginUrl &request);
@ -1123,7 +1125,7 @@ class Td final : public NetQueryCallback {
template <class T>
static td_api::object_ptr<td_api::Object> do_static_request(const T &request) {
return td_api::make_object<td_api::error>(400, "Function can't be executed synchronously");
return td_api::make_object<td_api::error>(400, "The method can't be executed synchronously");
}
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getTextEntities &request);
static td_api::object_ptr<td_api::Object> do_static_request(td_api::parseTextEntities &request);

View File

@ -24,7 +24,7 @@ enum class TopDialogCategory : int32 {
Size
};
inline TopDialogCategory top_dialog_category_from_td_api(const td_api::TopChatCategory &category) {
inline TopDialogCategory get_top_dialog_category(const td_api::TopChatCategory &category) {
switch (category.get_id()) {
case td_api::topChatCategoryUsers::ID:
return TopDialogCategory::Correspondent;

View File

@ -59,7 +59,7 @@ static CSlice top_dialog_category_name(TopDialogCategory category) {
}
}
static TopDialogCategory top_dialog_category_from_telegram_api(const telegram_api::TopPeerCategory &category) {
static TopDialogCategory get_top_dialog_category(const telegram_api::TopPeerCategory &category) {
switch (category.get_id()) {
case telegram_api::topPeerCategoryCorrespondents::ID:
return TopDialogCategory::Correspondent;
@ -82,7 +82,7 @@ static TopDialogCategory top_dialog_category_from_telegram_api(const telegram_ap
}
}
static tl_object_ptr<telegram_api::TopPeerCategory> top_dialog_category_as_telegram_api(TopDialogCategory category) {
static tl_object_ptr<telegram_api::TopPeerCategory> get_top_peer_category(TopDialogCategory category) {
switch (category) {
case TopDialogCategory::Correspondent:
return make_tl_object<telegram_api::topPeerCategoryCorrespondents>();
@ -199,8 +199,7 @@ void TopDialogManager::remove_dialog(TopDialogCategory category, DialogId dialog
LOG(INFO) << "Remove " << top_dialog_category_name(category) << " rating of " << dialog_id;
if (input_peer != nullptr) {
auto query =
telegram_api::contacts_resetTopPeerRating(top_dialog_category_as_telegram_api(category), std::move(input_peer));
auto query = telegram_api::contacts_resetTopPeerRating(get_top_peer_category(category), std::move(input_peer));
auto net_query = G()->net_query_creator().create(query);
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, 1));
}
@ -470,7 +469,7 @@ void TopDialogManager::on_result(NetQueryPtr net_query) {
send_closure(G()->contacts_manager(), &ContactsManager::on_get_chats, std::move(top_peers->chats_),
"on get top chats");
for (auto &category : top_peers->categories_) {
auto dialog_category = top_dialog_category_from_telegram_api(*category->category_);
auto dialog_category = get_top_dialog_category(*category->category_);
auto pos = static_cast<size_t>(dialog_category);
CHECK(pos < by_category_.size());
auto &top_dialogs = by_category_[pos];

View File

@ -1100,7 +1100,6 @@ void UpdatesManager::on_get_difference(tl_object_ptr<telegram_api::updates_Diffe
void UpdatesManager::after_get_difference() {
CHECK(!running_get_difference_);
send_closure(td_->secret_chats_manager_, &SecretChatsManager::after_get_difference);
retry_timeout_.cancel_timeout();
retry_time_ = 1;
@ -1442,10 +1441,9 @@ void UpdatesManager::set_seq_gap_timeout(double timeout) {
}
void UpdatesManager::on_pending_update(tl_object_ptr<telegram_api::Update> update, int32 seq, const char *source) {
vector<tl_object_ptr<telegram_api::Update>> v;
v.push_back(std::move(update));
on_pending_updates(std::move(v), seq, seq, 0, source); // TODO can be optimized
vector<tl_object_ptr<telegram_api::Update>> updates;
updates.push_back(std::move(update));
on_pending_updates(std::move(updates), seq, seq, 0, source);
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateNewMessage> update, bool force_apply) {
@ -2049,4 +2047,7 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateLoginToken> upd
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateTheme> update, bool /*force_apply*/) {
}
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateChannelParticipant> update, bool /*force_apply*/) {
}
} // namespace td

View File

@ -296,6 +296,8 @@ class UpdatesManager : public Actor {
// unsupported updates
void on_update(tl_object_ptr<telegram_api::updateTheme> update, bool /*force_apply*/);
void on_update(tl_object_ptr<telegram_api::updateChannelParticipant> update, bool /*force_apply*/);
};
} // namespace td

View File

@ -8,36 +8,37 @@
namespace td {
constexpr int32 MTPROTO_LAYER = 114;
constexpr int32 MTPROTO_LAYER = 116;
enum class Version : int32 {
Initial,
Initial, // 0
StoreFileId,
AddKeyHashToSecretChat,
AddDurationToAnimation,
FixStoreGameWithoutAnimation,
AddAccessHashToSecretChat,
AddAccessHashToSecretChat, // 5
StoreFileOwnerId,
StoreFileEncryptionKey,
NetStatsCountDuration,
FixWebPageInstantViewDatabase,
FixMinUsers,
FixMinUsers, // 10
FixPageBlockAudioEmptyFile,
AddMessageInvoiceProviderData,
AddCaptionEntities,
AddVenueType,
AddTermsOfService,
AddTermsOfService, // 15
AddContactVcard,
AddMessageUnsupportedVersion,
SupportInstantView2_0,
AddNotificationGroupInfoMaxRemovedMessageId,
SupportMinithumbnails,
SupportMinithumbnails, // 20
AddVideoCallsSupport,
AddPhotoSizeSource,
AddFolders,
SupportPolls2_0,
AddDiceEmoji,
AddDiceEmoji, // 25
AddAnimationStickers,
AddDialogPhotoHasAnimation,
Next
};

View File

@ -237,8 +237,8 @@ tl_object_ptr<telegram_api::InputMedia> VideoNotesManager::get_input_media(
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), "video/mp4", std::move(attributes),
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), "video/mp4",
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());
}

View File

@ -147,7 +147,7 @@ void VideosManager::delete_video_thumbnail(FileId file_id) {
return;
}
video->thumbnail = PhotoSize();
video->animated_thumbnail = PhotoSize();
video->animated_thumbnail = AnimationSize();
}
FileId VideosManager::dup_video(FileId new_id, FileId old_id) {
@ -206,7 +206,7 @@ bool VideosManager::merge_videos(FileId new_id, FileId old_id, bool can_delete_o
}
void VideosManager::create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail,
PhotoSize animated_thumbnail, bool has_stickers, vector<FileId> &&sticker_file_ids,
AnimationSize animated_thumbnail, bool has_stickers, vector<FileId> &&sticker_file_ids,
string file_name, string mime_type, int32 duration, Dimensions dimensions,
bool supports_streaming, bool replace) {
auto v = make_unique<Video>();
@ -311,8 +311,8 @@ tl_object_ptr<telegram_api::InputMedia> VideosManager::get_input_media(
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, std::move(attributes),
std::move(added_stickers), ttl);
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
std::move(attributes), std::move(added_stickers), ttl);
} else {
CHECK(!file_view.has_remote_location());
}

View File

@ -32,7 +32,7 @@ class VideosManager {
tl_object_ptr<td_api::video> get_video_object(FileId file_id);
void create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail, PhotoSize animated_thumbnail,
void create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail, AnimationSize animated_thumbnail,
bool has_stickers, vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
int32 duration, Dimensions dimensions, bool supports_streaming, bool replace);
@ -72,7 +72,7 @@ class VideosManager {
Dimensions dimensions;
string minithumbnail;
PhotoSize thumbnail;
PhotoSize animated_thumbnail;
AnimationSize animated_thumbnail;
bool supports_streaming = false;

View File

@ -196,7 +196,7 @@ tl_object_ptr<telegram_api::InputMedia> VoiceNotesManager::get_input_media(
mime_type = "audio/ogg";
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
0, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes),
0, false /*ignored*/, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes),
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
} else {
CHECK(!file_view.has_remote_location());

View File

@ -369,7 +369,7 @@ class RelatedArticle {
using ::td::store;
bool has_title = !title.empty();
bool has_description = !description.empty();
bool has_photo = photo.id != -2;
bool has_photo = !photo.is_empty();
bool has_author = !author.empty();
bool has_date = published_date != 0;
BEGIN_STORE_FLAGS();
@ -423,8 +423,6 @@ class RelatedArticle {
}
if (has_photo) {
parse(photo, parser);
} else {
photo.id = -2;
}
if (has_author) {
parse(author, parser);
@ -1050,7 +1048,7 @@ class WebPageBlockPhoto : public WebPageBlock {
}
td_api::object_ptr<td_api::PageBlock> get_page_block_object(Context *context) const override {
return make_tl_object<td_api::pageBlockPhoto>(get_photo_object(context->td_->file_manager_.get(), &photo),
return make_tl_object<td_api::pageBlockPhoto>(get_photo_object(context->td_->file_manager_.get(), photo),
caption.get_page_block_caption_object(context), url);
}
@ -1213,7 +1211,7 @@ class WebPageBlockEmbedded : public WebPageBlock {
td_api::object_ptr<td_api::PageBlock> get_page_block_object(Context *context) const override {
return make_tl_object<td_api::pageBlockEmbedded>(
url, html, get_photo_object(context->td_->file_manager_.get(), &poster_photo), dimensions.width,
url, html, get_photo_object(context->td_->file_manager_.get(), poster_photo), dimensions.width,
dimensions.height, caption.get_page_block_caption_object(context), is_full_width, allow_scrolling);
}
@ -1282,7 +1280,7 @@ class WebPageBlockEmbeddedPost : public WebPageBlock {
td_api::object_ptr<td_api::PageBlock> get_page_block_object(Context *context) const override {
return make_tl_object<td_api::pageBlockEmbeddedPost>(
url, author, get_photo_object(context->td_->file_manager_.get(), &author_photo), date,
url, author, get_photo_object(context->td_->file_manager_.get(), author_photo), date,
get_page_block_objects(page_blocks, context), caption.get_page_block_caption_object(context));
}
@ -1412,7 +1410,7 @@ class WebPageBlockChatLink : public WebPageBlock {
td_api::object_ptr<td_api::PageBlock> get_page_block_object(Context *context) const override {
return make_tl_object<td_api::pageBlockChatLink>(
title, get_chat_photo_object(context->td_->file_manager_.get(), &photo), username);
title, get_chat_photo_info_object(context->td_->file_manager_.get(), &photo), username);
}
template <class StorerT>
@ -1624,7 +1622,7 @@ class WebPageBlockRelatedArticles : public WebPageBlock {
void append_file_ids(const Td *td, vector<FileId> &file_ids) const override {
header.append_file_ids(td, file_ids);
for (auto &article : related_articles) {
if (article.photo.id != -2) {
if (!article.photo.is_empty()) {
append(file_ids, photo_get_file_ids(article.photo));
}
}
@ -1634,7 +1632,7 @@ class WebPageBlockRelatedArticles : public WebPageBlock {
auto related_article_objects = transform(related_articles, [context](const RelatedArticle &article) {
return td_api::make_object<td_api::pageBlockRelatedArticle>(
article.url, article.title, article.description,
get_photo_object(context->td_->file_manager_.get(), &article.photo), article.author, article.published_date);
get_photo_object(context->td_->file_manager_.get(), article.photo), article.author, article.published_date);
});
return make_tl_object<td_api::pageBlockRelatedArticles>(header.get_rich_text_object(context),
std::move(related_article_objects));
@ -2019,9 +2017,7 @@ unique_ptr<WebPageBlock> get_web_page_block(Td *td, tl_object_ptr<telegram_api::
auto page_block = move_tl_object_as<telegram_api::pageBlockPhoto>(page_block_ptr);
auto it = photos.find(page_block->photo_id_);
Photo photo;
if (it == photos.end()) {
photo.id = -2;
} else {
if (it != photos.end()) {
photo = it->second;
}
string url;
@ -2071,9 +2067,7 @@ unique_ptr<WebPageBlock> get_web_page_block(Td *td, tl_object_ptr<telegram_api::
? photos.find(page_block->poster_photo_id_)
: photos.end();
Photo poster_photo;
if (it == photos.end()) {
poster_photo.id = -2;
} else {
if (it != photos.end()) {
poster_photo = it->second;
}
Dimensions dimensions;
@ -2088,9 +2082,7 @@ unique_ptr<WebPageBlock> get_web_page_block(Td *td, tl_object_ptr<telegram_api::
auto page_block = move_tl_object_as<telegram_api::pageBlockEmbedPost>(page_block_ptr);
auto it = photos.find(page_block->author_photo_id_);
Photo author_photo;
if (it == photos.end()) {
author_photo.id = -2;
} else {
if (it != photos.end()) {
author_photo = it->second;
}
return td::make_unique<WebPageBlockEmbeddedPost>(
@ -2215,9 +2207,7 @@ unique_ptr<WebPageBlock> get_web_page_block(Td *td, tl_object_ptr<telegram_api::
auto it = (related_article->flags_ & telegram_api::pageRelatedArticle::PHOTO_ID_MASK) != 0
? photos.find(related_article->photo_id_)
: photos.end();
if (it == photos.end()) {
article.photo.id = -2;
} else {
if (it != photos.end()) {
article.photo = it->second;
}
article.author = std::move(related_article->author_);

View File

@ -239,7 +239,7 @@ class WebPagesManager::WebPage {
bool has_site_name = !site_name.empty();
bool has_title = !title.empty();
bool has_description = !description.empty();
bool has_photo = photo.id != -2;
bool has_photo = !photo.is_empty();
bool has_embed = !embed_url.empty();
bool has_embed_dimensions = has_embed && embed_dimensions != Dimensions();
bool has_duration = duration > 0;
@ -358,8 +358,6 @@ class WebPagesManager::WebPage {
}
if (has_photo) {
parse(photo, parser);
} else {
photo.id = -2;
}
if (has_embed) {
parse(embed_url, parser);
@ -1174,7 +1172,121 @@ bool WebPagesManager::have_web_page(WebPageId web_page_id) const {
}
tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId web_page_id) const {
return nullptr;
if (!web_page_id.is_valid()) {
return nullptr;
}
const WebPage *web_page = get_web_page(web_page_id);
if (web_page == nullptr) {
return nullptr;
}
int32 instant_view_version = [web_page] {
if (web_page->instant_view.is_empty) {
return 0;
}
if (web_page->instant_view.is_v2) {
return 2;
}
return 1;
}();
FormattedText description;
description.text = web_page->description;
description.entities = find_entities(web_page->description, true);
auto r_url = parse_url(web_page->display_url);
if (r_url.is_ok()) {
Slice host = r_url.ok().host_;
if (!host.empty() && host.back() == '.') {
host.truncate(host.size() - 1);
}
auto replace_entities = [](Slice text, vector<MessageEntity> &entities, auto replace_url) {
int32 current_offset = 0;
for (auto &entity : entities) {
CHECK(entity.offset >= current_offset);
text = utf8_utf16_substr(text, static_cast<size_t>(entity.offset - current_offset));
auto entity_text = utf8_utf16_substr(text, 0, static_cast<size_t>(entity.length));
text = text.substr(entity_text.size());
current_offset = entity.offset + entity.length;
auto replaced_url = replace_url(entity, entity_text);
if (!replaced_url.empty()) {
entity = MessageEntity(MessageEntity::Type::TextUrl, entity.offset, entity.length, std::move(replaced_url));
}
}
};
if (host == "instagram.com" || ends_with(host, ".instagram.com")) {
replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) {
if (entity.type == MessageEntity::Type::Mention) {
return PSTRING() << "https://www.instagram.com/" << text.substr(1) << '/';
}
if (entity.type == MessageEntity::Type::Hashtag) {
return PSTRING() << "https://www.instagram.com/explore/tags/" << url_encode(text.substr(1)) << '/';
}
return string();
});
} else if (host == "twitter.com" || ends_with(host, ".twitter.com")) {
replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) {
if (entity.type == MessageEntity::Type::Mention) {
return PSTRING() << "https://twitter.com/" << text.substr(1);
}
if (entity.type == MessageEntity::Type::Hashtag) {
return PSTRING() << "https://twitter.com/hashtag/" << url_encode(text.substr(1));
}
return string();
});
} else if (host == "t.me" || host == "telegram.me" || host == "telegram.dog" || host == "telesco.pe") {
// leave everything as is
} else {
td::remove_if(description.entities,
[](const MessageEntity &entity) { return entity.type == MessageEntity::Type::Mention; });
if (host == "youtube.com" || host == "www.youtube.com") {
replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) {
if (entity.type == MessageEntity::Type::Hashtag) {
return PSTRING() << "https://www.youtube.com/results?search_query=" << url_encode(text);
}
return string();
});
} else if (host == "music.youtube.com") {
replace_entities(description.text, description.entities, [](const MessageEntity &entity, Slice text) {
if (entity.type == MessageEntity::Type::Hashtag) {
return PSTRING() << "https://music.youtube.com/search?q=" << url_encode(text);
}
return string();
});
}
}
}
return make_tl_object<td_api::webPage>(
web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title,
get_formatted_text_object(description), get_photo_object(td_->file_manager_.get(), web_page->photo),
web_page->embed_url, web_page->embed_type, web_page->embed_dimensions.width, web_page->embed_dimensions.height,
web_page->duration, web_page->author,
web_page->document.type == Document::Type::Animation
? td_->animations_manager_->get_animation_object(web_page->document.file_id, "get_web_page_object")
: nullptr,
web_page->document.type == Document::Type::Audio
? td_->audios_manager_->get_audio_object(web_page->document.file_id)
: nullptr,
web_page->document.type == Document::Type::General
? td_->documents_manager_->get_document_object(web_page->document.file_id, PhotoFormat::Jpeg)
: nullptr,
web_page->document.type == Document::Type::Sticker
? td_->stickers_manager_->get_sticker_object(web_page->document.file_id)
: nullptr,
web_page->document.type == Document::Type::Video
? td_->videos_manager_->get_video_object(web_page->document.file_id)
: nullptr,
web_page->document.type == Document::Type::VideoNote
? td_->video_notes_manager_->get_video_note_object(web_page->document.file_id)
: nullptr,
web_page->document.type == Document::Type::VoiceNote
? td_->voice_notes_manager_->get_voice_note_object(web_page->document.file_id)
: nullptr,
instant_view_version);
}
tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_view_object(
@ -1293,15 +1405,15 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_
std::unordered_map<int64, Photo> photos;
for (auto &photo_ptr : page->photos_) {
Photo photo = get_photo(td_->file_manager_.get(), std::move(photo_ptr), owner_dialog_id);
if (photo.id == -2 || photo.id == 0) {
if (photo.is_empty() || photo.id.get() == 0) {
LOG(ERROR) << "Receive empty photo in web page instant view for " << web_page->url;
} else {
auto photo_id = photo.id;
auto photo_id = photo.id.get();
photos.emplace(photo_id, std::move(photo));
}
}
if (web_page->photo.id != -2 && web_page->photo.id != 0) {
photos.emplace(web_page->photo.id, web_page->photo);
if (!web_page->photo.is_empty() && web_page->photo.id.get() != 0) {
photos.emplace(web_page->photo.id.get(), web_page->photo);
}
std::unordered_map<int64, FileId> animations;

View File

@ -46,7 +46,6 @@
#include <clocale>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <limits>
@ -68,13 +67,14 @@ static void dump_memory_usage() {
if (is_memprof_on()) {
LOG(WARNING) << "Memory dump:";
clear_thread_locals();
std::vector<AllocInfo> v;
dump_alloc([&](const AllocInfo &info) { v.push_back(info); });
std::sort(v.begin(), v.end(), [](const AllocInfo &a, const AllocInfo &b) { return a.size > b.size; });
std::vector<AllocInfo> alloc_info;
dump_alloc([&](const AllocInfo &info) { alloc_info.push_back(info); });
std::sort(alloc_info.begin(), alloc_info.end(),
[](const AllocInfo &lhs, const AllocInfo &rhs) { return lhs.size > rhs.size; });
size_t total_size = 0;
size_t other_size = 0;
int cnt = 0;
for (auto &info : v) {
for (auto &info : alloc_info) {
if (cnt++ < 50) {
LOG(WARNING) << format::as_size(info.size) << format::as_array(info.backtrace);
} else {
@ -378,7 +378,7 @@ class CliClient final : public Actor {
auto r_stat = stat(file_generation.source);
if (r_stat.is_ok()) {
auto size = r_stat.ok().size_;
if (size <= 0 || size > 1500000000) {
if (size <= 0 || size > (2000 << 20)) {
r_stat = Status::Error(400, size == 0 ? Slice("File is empty") : Slice("File is too big"));
}
}
@ -407,7 +407,6 @@ class CliClient final : public Actor {
parameters->api_hash_ = api_hash_;
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->system_version_ = "Unknown";
parameters->application_version_ = "1.0";
send_request(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)));
break;
@ -1197,6 +1196,16 @@ class CliClient final : public Actor {
return nullptr;
}
static td_api::object_ptr<td_api::SuggestedAction> as_suggested_action(Slice action) {
if (action == "unarchive") {
return td_api::make_object<td_api::suggestedActionEnableArchiveAndMuteNewChats>();
}
if (action == "number") {
return td_api::make_object<td_api::suggestedActionCheckPhoneNumber>();
}
return nullptr;
}
static td_api::object_ptr<td_api::PassportElementType> as_passport_element_type(Slice passport_element_type) {
if (passport_element_type == "address" || passport_element_type == "a") {
return td_api::make_object<td_api::passportElementTypeAddress>();
@ -2030,7 +2039,7 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::getChatMessageCount>(
as_chat_id(chat_id), get_search_messages_filter(filter), as_bool(return_local)));
} else if (op == "gup" || op == "gupf") {
} else if (op == "gup" || op == "gupp") {
string user_id;
string offset;
string limit;
@ -2994,7 +3003,7 @@ class CliClient final : public Actor {
std::tie(message_id, document) = split(args);
send_request(td_api::make_object<td_api::editMessageMedia>(
as_chat_id(chat_id), as_message_id(message_id), nullptr,
td_api::make_object<td_api::inputMessageDocument>(as_input_file(document), nullptr, as_caption(""))));
td_api::make_object<td_api::inputMessageDocument>(as_input_file(document), nullptr, false, as_caption(""))));
} else if (op == "emp") {
string chat_id;
string message_id;
@ -3191,22 +3200,22 @@ class CliClient final : public Actor {
std::tie(chat_id, emoji) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDice>(emoji, op == "sdicecd"));
} else if (op == "sd") {
} else if (op == "sd" || op == "sdf") {
string chat_id;
string document_path;
std::tie(chat_id, document_path) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(
as_input_file(document_path), nullptr,
as_input_file(document_path), nullptr, op == "sdf",
as_caption(u8"\u1680\u180Etest \u180E\n\u180E\n\u180E\n cap\ttion\u180E\u180E")));
} else if (op == "sdt") {
} else if (op == "sdt" || op == "sdtf") {
string chat_id;
string document_path;
string thumbnail_path;
std::tie(chat_id, args) = split(args);
std::tie(document_path, thumbnail_path) = split(args);
send_message(chat_id,
td_api::make_object<td_api::inputMessageDocument>(
as_input_file(document_path), as_input_thumbnail(thumbnail_path), as_caption("test caption")));
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(
as_input_file(document_path), as_input_thumbnail(thumbnail_path), op == "sdtf",
as_caption("test caption")));
} else if (op == "sdg" || op == "sdgu") {
string chat_id;
string document_path;
@ -3217,9 +3226,9 @@ class CliClient final : public Actor {
send_request(
td_api::make_object<td_api::uploadFile>(as_generated_file(document_path, document_conversion), nullptr, 1));
}
send_message(chat_id,
td_api::make_object<td_api::inputMessageDocument>(
as_generated_file(document_path, document_conversion), nullptr, as_caption("test caption")));
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(
as_generated_file(document_path, document_conversion), nullptr, false,
as_caption("test caption")));
} else if (op == "sdtg") {
string chat_id;
string document_path;
@ -3230,7 +3239,7 @@ class CliClient final : public Actor {
std::tie(thumbnail_path, thumbnail_conversion) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(
as_input_file(document_path), as_input_thumbnail(thumbnail_path, thumbnail_conversion),
as_caption("test caption")));
false, as_caption("test caption")));
} else if (op == "sdgtg") {
string chat_id;
string document_path;
@ -3241,22 +3250,23 @@ class CliClient final : public Actor {
std::tie(document_path, args) = split(args);
std::tie(document_conversion, args) = split(args);
std::tie(thumbnail_path, thumbnail_conversion) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(
as_generated_file(document_path, document_conversion),
as_input_thumbnail(thumbnail_path, thumbnail_conversion), as_caption("test caption")));
send_message(chat_id,
td_api::make_object<td_api::inputMessageDocument>(
as_generated_file(document_path, document_conversion),
as_input_thumbnail(thumbnail_path, thumbnail_conversion), false, as_caption("test caption")));
} else if (op == "sdid") {
string chat_id;
string file_id;
std::tie(chat_id, file_id) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(as_input_file_id(file_id), nullptr,
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(as_input_file_id(file_id), nullptr, false,
as_caption("")));
} else if (op == "sdurl") {
string chat_id;
string url;
std::tie(chat_id, url) = split(args);
send_message(chat_id,
td_api::make_object<td_api::inputMessageDocument>(as_remote_file(url), nullptr, as_caption("")));
send_message(chat_id, td_api::make_object<td_api::inputMessageDocument>(as_remote_file(url), nullptr, false,
as_caption("")));
} else if (op == "sg") {
string chat_id;
string bot_user_id;
@ -3551,12 +3561,30 @@ class CliClient final : public Actor {
std::tie(chat_id, title) = split(args);
send_request(td_api::make_object<td_api::setChatTitle>(as_chat_id(chat_id), title));
} else if (op == "scpp") {
string chat_id;
string photo_id;
std::tie(chat_id, photo_id) = split(args);
send_request(td_api::make_object<td_api::setChatPhoto>(
as_chat_id(chat_id), td_api::make_object<td_api::inputChatPhotoPrevious>(to_integer<int64>(photo_id))));
} else if (op == "scp") {
string chat_id;
string photo_path;
std::tie(chat_id, photo_path) = split(args);
send_request(td_api::make_object<td_api::setChatPhoto>(as_chat_id(chat_id), as_input_file(photo_path)));
send_request(td_api::make_object<td_api::setChatPhoto>(
as_chat_id(chat_id), td_api::make_object<td_api::inputChatPhotoStatic>(as_input_file(photo_path))));
} else if (op == "scpa" || op == "scpv") {
string chat_id;
string animation;
string main_frame_timestamp;
std::tie(chat_id, args) = split(args);
std::tie(animation, main_frame_timestamp) = split(args);
send_request(td_api::make_object<td_api::setChatPhoto>(
as_chat_id(chat_id), td_api::make_object<td_api::inputChatPhotoAnimation>(as_input_file(animation),
to_double(main_frame_timestamp))));
} else if (op == "scperm") {
string chat_id;
string permissions;
@ -3571,12 +3599,6 @@ class CliClient final : public Actor {
} else {
LOG(ERROR) << "Wrong permissions size, expected 8";
}
} else if (op == "scpid") {
string chat_id;
string file_id;
std::tie(chat_id, file_id) = split(args);
send_request(td_api::make_object<td_api::setChatPhoto>(as_chat_id(chat_id), as_input_file_id(file_id)));
} else if (op == "sccd") {
string chat_id;
string client_data;
@ -3842,13 +3864,19 @@ class CliClient final : public Actor {
std::tie(url, force_full) = split(args);
send_request(td_api::make_object<td_api::getWebPageInstantView>(url, as_bool(force_full)));
} else if (op == "sppp") {
send_request(td_api::make_object<td_api::setProfilePhoto>(
td_api::make_object<td_api::inputChatPhotoPrevious>(to_integer<int64>(args))));
} else if (op == "spp") {
send_request(td_api::make_object<td_api::setProfilePhoto>(as_input_file(args)));
} else if (op == "sppg") {
string path;
string conversion;
std::tie(path, conversion) = split(args);
send_request(td_api::make_object<td_api::setProfilePhoto>(as_generated_file(path, conversion)));
send_request(td_api::make_object<td_api::setProfilePhoto>(
td_api::make_object<td_api::inputChatPhotoStatic>(as_input_file(args))));
} else if (op == "sppa" || op == "sppv") {
string animation;
string main_frame_timestamp;
std::tie(animation, main_frame_timestamp) = split(args);
send_request(td_api::make_object<td_api::setProfilePhoto>(td_api::make_object<td_api::inputChatPhotoAnimation>(
as_input_file(animation), to_double(main_frame_timestamp))));
} else if (op == "sh") {
auto prefix = std::move(args);
send_request(td_api::make_object<td_api::searchHashtags>(prefix, 10));
@ -3980,6 +4008,8 @@ class CliClient final : public Actor {
send_request(
td_api::make_object<td_api::getChatStatisticsGraph>(as_chat_id(chat_id), token, to_integer<int64>(x)));
} else if (op == "hsa" || op == "glu" || op == "glua") {
send_request(td_api::make_object<td_api::hideSuggestedAction>(as_suggested_action(args)));
} else if (op == "glui" || op == "glu" || op == "glua") {
string chat_id;
string message_id;
@ -4331,12 +4361,9 @@ void main(int argc, char **argv) {
return std::string();
}(std::getenv("TD_API_HASH"));
td::OptionParser options;
OptionParser options;
options.set_description("TDLib test client");
options.add_option('\0', "test", "Use test DC", [&] {
use_test_dc = true;
return Status::OK();
});
options.add_option('\0', "test", "Use test DC", [&] { use_test_dc = true; });
options.add_option('v', "verbosity", "Set verbosity level", [&](Slice level) {
int new_verbosity = 1;
while (begins_with(level, "v")) {
@ -4347,53 +4374,30 @@ void main(int argc, char **argv) {
new_verbosity += to_integer<int>(level) - (new_verbosity == 1);
}
new_verbosity_level = VERBOSITY_NAME(FATAL) + new_verbosity;
return Status::OK();
});
options.add_option('l', "log", "Log to file", [&](Slice file_name) {
if (file_log.init(file_name.str()).is_ok() && file_log.init(file_name.str()).is_ok() &&
file_log.init(file_name.str(), 1000 << 20).is_ok()) {
log_interface = &ts_log;
}
});
options.add_option('W', "", "Preload chat list", [&] { get_chat_list = true; });
options.add_option('n', "disable-network", "Disable network", [&] { disable_network = true; });
options.add_option('\0', "api-id", "Set Telegram API ID",
[&](Slice parameter) { api_id = to_integer<int32>(parameter); });
options.add_option('\0', "api_id", "Set Telegram API ID",
[&](Slice parameter) { api_id = to_integer<int32>(parameter); });
options.add_option('\0', "api-hash", "Set Telegram API hash", [&](Slice parameter) { api_hash = parameter.str(); });
options.add_option('\0', "api_hash", "Set Telegram API hash", [&](Slice parameter) { api_hash = parameter.str(); });
options.add_check([&] {
if (api_id == 0 || api_hash.empty()) {
return Status::Error("You must provide valid api-id and api-hash obtained at https://my.telegram.org");
}
return Status::OK();
});
options.add_option('W', "", "Preload chat list", [&] {
get_chat_list = true;
return Status::OK();
});
options.add_option('n', "disable-network", "Disable network", [&] {
disable_network = true;
return Status::OK();
});
options.add_option('\0', "api-id", "Set Telegram API ID", [&](Slice parameter) {
api_id = to_integer<int32>(parameter);
return Status::OK();
});
options.add_option('\0', "api_id", "Set Telegram API ID", [&](Slice parameter) {
api_id = to_integer<int32>(parameter);
return Status::OK();
});
options.add_option('\0', "api-hash", "Set Telegram API hash", [&](Slice parameter) {
api_hash = parameter.str();
return Status::OK();
});
options.add_option('\0', "api_hash", "Set Telegram API hash", [&](Slice parameter) {
api_hash = parameter.str();
return Status::OK();
});
auto res = options.run(argc, argv);
if (res.is_error()) {
LOG(PLAIN) << "tg_cli: " << res.error().message();
LOG(PLAIN) << options;
return;
}
if (!res.ok().empty()) {
LOG(PLAIN) << "tg_cli: " << "Have unexpected non-option parameters";
LOG(PLAIN) << options;
return;
}
if (api_id == 0 || api_hash.empty()) {
LOG(PLAIN) << "tg_cli: " << "You should provide some valid api_id and api_hash";
auto r_non_options = options.run(argc, argv, 0);
if (r_non_options.is_error()) {
LOG(PLAIN) << argv[0] << ": " << r_non_options.error().message();
LOG(PLAIN) << options;
return;
}

View File

@ -108,11 +108,12 @@ Result<FileLoader::FileInfo> FileDownloader::init() {
res.ready_parts = bitmask.as_vector();
res.use_part_count_limit = false;
res.only_check = only_check_;
auto file_type = remote_.file_type_;
res.need_delay =
!is_small_ && (remote_.file_type_ == FileType::VideoNote || remote_.file_type_ == FileType::Document ||
remote_.file_type_ == FileType::VoiceNote || remote_.file_type_ == FileType::Audio ||
remote_.file_type_ == FileType::Video || remote_.file_type_ == FileType::Animation ||
(remote_.file_type_ == FileType::Encrypted && size_ > (1 << 20)));
!is_small_ &&
(file_type == FileType::VideoNote || file_type == FileType::Document || file_type == FileType::DocumentAsFile ||
file_type == FileType::VoiceNote || file_type == FileType::Audio || file_type == FileType::Video ||
file_type == FileType::Animation || (file_type == FileType::Encrypted && size_ > (1 << 20)));
res.offset = offset_;
res.limit = limit_;
return res;

View File

@ -35,7 +35,7 @@ void FileGcWorker::run_gc(const FileGcParameters &parameters, std::vector<FullFi
// may write something more clever, but i will need at least 2 passes over the files
// TODO update atime for all files in android (?)
std::array<bool, file_type_size> immune_types{{false}};
std::array<bool, MAX_FILE_TYPE> immune_types{{false}};
if (G()->parameters().use_file_db) {
// immune by default
@ -49,13 +49,14 @@ void FileGcWorker::run_gc(const FileGcParameters &parameters, std::vector<FullFi
if (!parameters.file_types.empty()) {
std::fill(immune_types.begin(), immune_types.end(), true);
for (auto file_type : parameters.file_types) {
if (file_type == FileType::Secure) {
immune_types[narrow_cast<size_t>(FileType::SecureRaw)] = false;
} else if (file_type == FileType::Background) {
immune_types[narrow_cast<size_t>(FileType::Wallpaper)] = false;
}
immune_types[narrow_cast<size_t>(file_type)] = false;
}
for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
auto main_file_type = narrow_cast<size_t>(get_main_file_type(static_cast<FileType>(i)));
if (immune_types[main_file_type] == false) {
immune_types[i] = false;
}
}
}
if (G()->parameters().use_file_db) {

View File

@ -376,7 +376,7 @@ static Status check_mtime(std::string &conversion, CSlice original_path) {
return Status::OK();
}
ConstParser parser(conversion);
if (!parser.skip_start_with("#mtime#")) {
if (!parser.try_skip("#mtime#")) {
return Status::OK();
}
auto mtime_str = parser.read_till('#');
@ -411,7 +411,7 @@ void FileGenerateManager::generate_file(uint64 query_id, FullGenerateFileLocatio
}
CHECK(query_id != 0);
auto it_flag = query_id_to_query_.insert(std::make_pair(query_id, Query{}));
auto it_flag = query_id_to_query_.emplace(query_id, Query{});
LOG_CHECK(it_flag.second) << "Query id must be unique";
auto parent = actor_shared(this, query_id);

View File

@ -210,6 +210,7 @@ class FullRemoteFileLocation {
case FileType::SecureRaw:
case FileType::Secure:
case FileType::Background:
case FileType::DocumentAsFile:
return LocationType::Common;
case FileType::None:
case FileType::Size:
@ -331,6 +332,12 @@ class FullRemoteFileLocation {
}
}
void set_source(PhotoSizeSource source) {
CHECK(is_photo());
file_type_ = source.get_file_type();
photo().source_ = std::move(source);
}
bool delete_file_reference(Slice bad_file_reference) {
if (file_reference_ != FileReferenceView::invalid_file_reference() && file_reference_ == bad_file_reference) {
file_reference_ = FileReferenceView::invalid_file_reference().str();

View File

@ -238,6 +238,7 @@ void FullRemoteFileLocation::AsUnique::store(StorerT &storer) const {
case FileType::Animation:
case FileType::VideoNote:
case FileType::Background:
case FileType::DocumentAsFile:
return 2;
case FileType::SecureRaw:
case FileType::Secure:

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